From 48bf361b8f5c31a7e4c63dce14b6cdad013ecaa0 Mon Sep 17 00:00:00 2001 From: Clint Andrew Hall Date: Thu, 23 Feb 2023 22:25:48 -0500 Subject: [PATCH 01/58] Add 'enabled' config option to multiple plugins for projects. --- config/serverless.oblt.yml | 8 ++++++++ x-pack/plugins/apm/server/index.ts | 1 + x-pack/plugins/canvas/server/index.ts | 8 ++++++++ x-pack/plugins/enterprise_search/server/index.ts | 1 + x-pack/plugins/fleet/common/authz.ts | 2 +- x-pack/plugins/fleet/server/config.ts | 1 + x-pack/plugins/observability/server/index.ts | 1 + x-pack/plugins/security/server/config.ts | 1 + x-pack/plugins/security_solution/server/config.mock.ts | 1 + x-pack/plugins/security_solution/server/config.ts | 1 + x-pack/plugins/synthetics/common/config.ts | 1 + x-pack/plugins/synthetics/kibana.jsonc | 6 +++--- .../legacy_uptime/lib/adapters/framework/adapter_types.ts | 3 ++- .../synthetics_service/get_service_locations.test.ts | 3 +++ .../private_location/synthetics_private_location.ts | 2 +- .../server/synthetics_service/synthetics_service.test.ts | 3 ++- x-pack/plugins/watcher/server/index.ts | 8 ++++++++ 17 files changed, 44 insertions(+), 7 deletions(-) diff --git a/config/serverless.oblt.yml b/config/serverless.oblt.yml index e69de29bb2d1d..58d786b798fc9 100644 --- a/config/serverless.oblt.yml +++ b/config/serverless.oblt.yml @@ -0,0 +1,8 @@ +enterpriseSearch.enabled: false +xpack.canvas.enabled: false +xpack.cloudSecurityPosture.enabled: false +xpack.reporting.enabled: false +xpack.securitySolution.enabled: false +xpack.watcher.enabled: false + +uiSettings.overrides.defaultRoute: /app/observability/overview diff --git a/x-pack/plugins/apm/server/index.ts b/x-pack/plugins/apm/server/index.ts index 74eea568788b5..dba2d6e29ae7c 100644 --- a/x-pack/plugins/apm/server/index.ts +++ b/x-pack/plugins/apm/server/index.ts @@ -53,6 +53,7 @@ const configSchema = schema.object({ onboarding: schema.string({ defaultValue: 'apm-*' }), }), forceSyntheticSource: schema.boolean({ defaultValue: false }), + enabled: schema.boolean({ defaultValue: true }), }); // plugin config diff --git a/x-pack/plugins/canvas/server/index.ts b/x-pack/plugins/canvas/server/index.ts index d6d375b7259ac..d25ad10dd8e34 100644 --- a/x-pack/plugins/canvas/server/index.ts +++ b/x-pack/plugins/canvas/server/index.ts @@ -6,7 +6,15 @@ */ import { PluginInitializerContext } from '@kbn/core/server'; +import { schema } from '@kbn/config-schema'; + import { CanvasPlugin } from './plugin'; export const plugin = (initializerContext: PluginInitializerContext) => new CanvasPlugin(initializerContext); + +export const config = { + schema: schema.object({ + enabled: schema.boolean({ defaultValue: true }), + }), +}; diff --git a/x-pack/plugins/enterprise_search/server/index.ts b/x-pack/plugins/enterprise_search/server/index.ts index b8f65c23ab674..e1a1b33fa67ab 100644 --- a/x-pack/plugins/enterprise_search/server/index.ts +++ b/x-pack/plugins/enterprise_search/server/index.ts @@ -18,6 +18,7 @@ export const configSchema = schema.object({ accessCheckTimeout: schema.number({ defaultValue: 5000 }), accessCheckTimeoutWarning: schema.number({ defaultValue: 300 }), customHeaders: schema.maybe(schema.object({}, { unknowns: 'allow' })), + enabled: schema.boolean({ defaultValue: true }), host: schema.maybe(schema.string()), ssl: schema.object({ certificateAuthorities: schema.maybe( diff --git a/x-pack/plugins/fleet/common/authz.ts b/x-pack/plugins/fleet/common/authz.ts index fa30f2b8f7f33..a275a55a48206 100644 --- a/x-pack/plugins/fleet/common/authz.ts +++ b/x-pack/plugins/fleet/common/authz.ts @@ -99,7 +99,7 @@ export function calculatePackagePrivilegesFromCapabilities( return { ...acc, [privilege]: { - executePackageAction: capabilities.siem[privilegeName] || false, + executePackageAction: (capabilities.siem && capabilities.siem[privilegeName]) || false, }, }; }, diff --git a/x-pack/plugins/fleet/server/config.ts b/x-pack/plugins/fleet/server/config.ts index d5312bf9bc65c..41eb7e03e29c7 100644 --- a/x-pack/plugins/fleet/server/config.ts +++ b/x-pack/plugins/fleet/server/config.ts @@ -158,6 +158,7 @@ export const config: PluginConfigDescriptor = { } }, }), + enabled: schema.boolean({ defaultValue: true }), }), }; diff --git a/x-pack/plugins/observability/server/index.ts b/x-pack/plugins/observability/server/index.ts index ec930b813fb36..2ae55b5fa0b5f 100644 --- a/x-pack/plugins/observability/server/index.ts +++ b/x-pack/plugins/observability/server/index.ts @@ -47,6 +47,7 @@ const configSchema = schema.object({ }), }), }), + enabled: schema.boolean({ defaultValue: true }), }); export const config: PluginConfigDescriptor = { diff --git a/x-pack/plugins/security/server/config.ts b/x-pack/plugins/security/server/config.ts index e3584427964f3..5620b35c1fef7 100644 --- a/x-pack/plugins/security/server/config.ts +++ b/x-pack/plugins/security/server/config.ts @@ -295,6 +295,7 @@ export const ConfigSchema = schema.object({ ) ), }), + enabled: schema.boolean({ defaultValue: true }), }); export function createConfig( diff --git a/x-pack/plugins/security_solution/server/config.mock.ts b/x-pack/plugins/security_solution/server/config.mock.ts index c1faa6f401a1d..6fcaa94629643 100644 --- a/x-pack/plugins/security_solution/server/config.mock.ts +++ b/x-pack/plugins/security_solution/server/config.mock.ts @@ -31,6 +31,7 @@ export const createMockConfig = (): ConfigType => { alertIgnoreFields: [], experimentalFeatures: parseExperimentalConfigValue(enableExperimental), + enabled: true, }; }; diff --git a/x-pack/plugins/security_solution/server/config.ts b/x-pack/plugins/security_solution/server/config.ts index 5a4d18edda11c..975d53daa03f4 100644 --- a/x-pack/plugins/security_solution/server/config.ts +++ b/x-pack/plugins/security_solution/server/config.ts @@ -122,6 +122,7 @@ export const configSchema = schema.object({ * the package is not already installed. */ prebuiltRulesPackageVersion: schema.maybe(schema.string()), + enabled: schema.boolean({ defaultValue: true }), }); export type ConfigSchema = TypeOf; diff --git a/x-pack/plugins/synthetics/common/config.ts b/x-pack/plugins/synthetics/common/config.ts index c9c48e5878391..9da43f8bf9a08 100644 --- a/x-pack/plugins/synthetics/common/config.ts +++ b/x-pack/plugins/synthetics/common/config.ts @@ -23,6 +23,7 @@ const serviceConfig = schema.object({ const uptimeConfig = schema.object({ index: schema.maybe(schema.string()), service: schema.maybe(serviceConfig), + enabled: schema.boolean({ defaultValue: true }), }); export const config: PluginConfigDescriptor = { diff --git a/x-pack/plugins/synthetics/kibana.jsonc b/x-pack/plugins/synthetics/kibana.jsonc index 3236a730f1a59..5e6f1a6f30233 100644 --- a/x-pack/plugins/synthetics/kibana.jsonc +++ b/x-pack/plugins/synthetics/kibana.jsonc @@ -15,6 +15,8 @@ "actions", "alerting", "cases", + "data", + "fleet", "embeddable", "discover", "dataViews", @@ -35,8 +37,6 @@ ], "optionalPlugins": [ "cloud", - "data", - "fleet", "home", "ml", "telemetry" @@ -52,4 +52,4 @@ "indexLifecycleManagement" ] } -} +} \ No newline at end of file diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/lib/adapters/framework/adapter_types.ts b/x-pack/plugins/synthetics/server/legacy_uptime/lib/adapters/framework/adapter_types.ts index ec77b83977a09..2237fe29df0d5 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/lib/adapters/framework/adapter_types.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/lib/adapters/framework/adapter_types.ts @@ -28,7 +28,7 @@ import { MlPluginSetup as MlSetup } from '@kbn/ml-plugin/server'; import { RuleRegistryPluginSetupContract } from '@kbn/rule-registry-plugin/server'; import { SecurityPluginStart } from '@kbn/security-plugin/server'; import { CloudSetup } from '@kbn/cloud-plugin/server'; -import { SpacesPluginStart } from '@kbn/spaces-plugin/server'; +import { SpacesPluginSetup, SpacesPluginStart } from '@kbn/spaces-plugin/server'; import { FleetStartContract } from '@kbn/fleet-plugin/server'; import { BfetchServerSetup } from '@kbn/bfetch-plugin/server'; import { UptimeEsClient } from '../../lib'; @@ -76,6 +76,7 @@ export interface UptimeCorePluginsSetup { usageCollection: UsageCollectionSetup; ml: MlSetup; cloud?: CloudSetup; + spaces: SpacesPluginSetup; ruleRegistry: RuleRegistryPluginSetupContract; encryptedSavedObjects: EncryptedSavedObjectsPluginSetup; taskManager: TaskManagerSetupContract; diff --git a/x-pack/plugins/synthetics/server/synthetics_service/get_service_locations.test.ts b/x-pack/plugins/synthetics/server/synthetics_service/get_service_locations.test.ts index 1fed640bcb4e8..58faf6ba14877 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/get_service_locations.test.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/get_service_locations.test.ts @@ -50,6 +50,7 @@ describe('getServiceLocations', function () { manifestUrl: 'http://local.dev', showExperimentalLocations: false, }, + enabled: true, }, // @ts-ignore logger: { @@ -101,6 +102,7 @@ describe('getServiceLocations', function () { manifestUrl: 'http://local.dev', showExperimentalLocations: false, }, + enabled: true, }, // @ts-ignore logger: { @@ -138,6 +140,7 @@ describe('getServiceLocations', function () { manifestUrl: 'http://local.dev', showExperimentalLocations: true, }, + enabled: true, }, // @ts-ignore logger: { diff --git a/x-pack/plugins/synthetics/server/synthetics_service/private_location/synthetics_private_location.ts b/x-pack/plugins/synthetics/server/synthetics_service/private_location/synthetics_private_location.ts index 02a1828c1f56c..41410f3c0aa3a 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/private_location/synthetics_private_location.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/private_location/synthetics_private_location.ts @@ -356,7 +356,7 @@ export class SyntheticsPrivateLocation { } ); - return agentPolicies.items; + return agentPolicies.items || []; } } diff --git a/x-pack/plugins/synthetics/server/synthetics_service/synthetics_service.test.ts b/x-pack/plugins/synthetics/server/synthetics_service/synthetics_service.test.ts index 7144284e6b4f0..abb56011a14b9 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/synthetics_service.test.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/synthetics_service.test.ts @@ -42,7 +42,7 @@ describe('SyntheticsService', () => { } as unknown as UptimeServerSetup; const getMockedService = (locationsNum: number = 1) => { - serverMock.config = { service: { devUrl: 'http://localhost' } }; + serverMock.config = { service: { devUrl: 'http://localhost' }, enabled: true }; const service = new SyntheticsService(serverMock); const locations = times(locationsNum).map((n) => { @@ -116,6 +116,7 @@ describe('SyntheticsService', () => { username: 'dev', password: '12345', }, + enabled: true, }; const service = new SyntheticsService(serverMock); diff --git a/x-pack/plugins/watcher/server/index.ts b/x-pack/plugins/watcher/server/index.ts index 0aba44ed82838..36453f571f162 100644 --- a/x-pack/plugins/watcher/server/index.ts +++ b/x-pack/plugins/watcher/server/index.ts @@ -6,6 +6,14 @@ */ import { PluginInitializerContext } from '@kbn/core/server'; +import { schema } from '@kbn/config-schema'; + import { WatcherServerPlugin } from './plugin'; export const plugin = (ctx: PluginInitializerContext) => new WatcherServerPlugin(ctx); + +export const config = { + schema: schema.object({ + enabled: schema.boolean({ defaultValue: true }), + }), +}; From 023ed3a24d3ac9d1f75e2a19d225f90a6de60eaa Mon Sep 17 00:00:00 2001 From: Clint Andrew Hall Date: Thu, 23 Feb 2023 22:30:03 -0500 Subject: [PATCH 02/58] Add ability to hide Observability navigation --- .../page_template/lazy_page_template.tsx | 28 +++++++++++++------ .../page_template/page_template.test.tsx | 1 + .../shared/page_template/page_template.tsx | 6 +++- x-pack/plugins/observability/public/plugin.ts | 2 ++ .../public/services/navigation_registry.ts | 10 ++++++- 5 files changed, 37 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/observability/public/components/shared/page_template/lazy_page_template.tsx b/x-pack/plugins/observability/public/components/shared/page_template/lazy_page_template.tsx index 7c61cae4f2c73..7bbc0dbc9ea70 100644 --- a/x-pack/plugins/observability/public/components/shared/page_template/lazy_page_template.tsx +++ b/x-pack/plugins/observability/public/components/shared/page_template/lazy_page_template.tsx @@ -6,6 +6,7 @@ */ import React from 'react'; +import useObservable from 'react-use/lib/useObservable'; import type { ObservabilityPageTemplateDependencies, WrappedPageTemplateProps, @@ -15,12 +16,23 @@ export const LazyObservabilityPageTemplate = React.lazy(() => import('./page_tem export type LazyObservabilityPageTemplateProps = WrappedPageTemplateProps; -export function createLazyObservabilityPageTemplate( - injectedDeps: ObservabilityPageTemplateDependencies -) { - return (pageTemplateProps: LazyObservabilityPageTemplateProps) => ( - - - - ); +export function createLazyObservabilityPageTemplate({ + isSidebarEnabled$, + ...injectedDeps +}: ObservabilityPageTemplateDependencies) { + return (pageTemplateProps: LazyObservabilityPageTemplateProps) => { + const isSidebarEnabled = useObservable(isSidebarEnabled$, true); + const { showSolutionNav: showSolutionNavProp, ...props } = pageTemplateProps; + let showSolutionNav = showSolutionNavProp; + + if (!isSidebarEnabled) { + showSolutionNav = false; + } + + return ( + + + + ); + }; } diff --git a/x-pack/plugins/observability/public/components/shared/page_template/page_template.test.tsx b/x-pack/plugins/observability/public/components/shared/page_template/page_template.test.tsx index 2a5324ddb5397..ae221fce17319 100644 --- a/x-pack/plugins/observability/public/components/shared/page_template/page_template.test.tsx +++ b/x-pack/plugins/observability/public/components/shared/page_template/page_template.test.tsx @@ -56,6 +56,7 @@ describe('Page template', () => { navigationSections$: navigationRegistry.sections$, getPageTemplateServices, guidedOnboardingApi: guidedOnboardingMock.createStart().guidedOnboardingApi, + isSidebarEnabled$: of(true), }); const component = shallow( diff --git a/x-pack/plugins/observability/public/components/shared/page_template/page_template.tsx b/x-pack/plugins/observability/public/components/shared/page_template/page_template.tsx index cafe693b8832b..c4dae7c1dc745 100644 --- a/x-pack/plugins/observability/public/components/shared/page_template/page_template.tsx +++ b/x-pack/plugins/observability/public/components/shared/page_template/page_template.tsx @@ -53,9 +53,13 @@ export interface ObservabilityPageTemplateDependencies { navigationSections$: Observable; getPageTemplateServices: () => KibanaPageTemplateKibanaDependencies; guidedOnboardingApi: GuidedOnboardingPluginStart['guidedOnboardingApi']; + isSidebarEnabled$: Observable; } -export type ObservabilityPageTemplateProps = ObservabilityPageTemplateDependencies & +export type ObservabilityPageTemplateProps = Omit< + ObservabilityPageTemplateDependencies, + 'isSidebarEnabled$' +> & WrappedPageTemplateProps; export function ObservabilityPageTemplate({ diff --git a/x-pack/plugins/observability/public/plugin.ts b/x-pack/plugins/observability/public/plugin.ts index a170e9584c6fe..d2809048ac4d7 100644 --- a/x-pack/plugins/observability/public/plugin.ts +++ b/x-pack/plugins/observability/public/plugin.ts @@ -315,6 +315,7 @@ export class Plugin observabilityRuleTypeRegistry: this.observabilityRuleTypeRegistry, navigation: { registerSections: this.navigationRegistry.registerSections, + setIsSidebarEnabled: this.navigationRegistry.setIsSidebarEnabled, }, useRulesLink: createUseRulesLink(), }; @@ -340,6 +341,7 @@ export class Plugin navigationSections$: this.navigationRegistry.sections$, guidedOnboardingApi: pluginsStart.guidedOnboarding.guidedOnboardingApi, getPageTemplateServices: () => ({ coreStart }), + isSidebarEnabled$: this.navigationRegistry.isSidebarEnabled$, }); const getAsyncO11yAlertsTableConfiguration = async () => { diff --git a/x-pack/plugins/observability/public/services/navigation_registry.ts b/x-pack/plugins/observability/public/services/navigation_registry.ts index 5f10a6f6c6851..e18929799b5fb 100644 --- a/x-pack/plugins/observability/public/services/navigation_registry.ts +++ b/x-pack/plugins/observability/public/services/navigation_registry.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { combineLatest, Observable, ReplaySubject } from 'rxjs'; +import { combineLatest, Observable, ReplaySubject, BehaviorSubject } from 'rxjs'; import { map, scan, shareReplay, switchMap } from 'rxjs/operators'; export interface NavigationSection { @@ -45,6 +45,8 @@ export interface NavigationEntry { export interface NavigationRegistry { registerSections: (sections$: Observable) => void; sections$: Observable; + isSidebarEnabled$: Observable; + setIsSidebarEnabled: (enabled: boolean) => void; } export const createNavigationRegistry = (): NavigationRegistry => { @@ -54,6 +56,8 @@ export const createNavigationRegistry = (): NavigationRegistry => { registeredSections$.next(sections$); }; + const isSidebarEnabled$ = new BehaviorSubject(true); + const sections$: Observable = registeredSections$.pipe( scan( (accumulatedSections$, newSections) => accumulatedSections$.add(newSections), @@ -69,5 +73,9 @@ export const createNavigationRegistry = (): NavigationRegistry => { return { registerSections, sections$, + isSidebarEnabled$, + setIsSidebarEnabled: (enabled) => { + isSidebarEnabled$.next(enabled); + }, }; }; From 47f1bb6b05b8fc6ff0972f2234a14a00cbfe595e Mon Sep 17 00:00:00 2001 From: Clint Andrew Hall Date: Thu, 23 Feb 2023 23:01:20 -0500 Subject: [PATCH 03/58] Create Serverless o11y plugin; hide internal nav via API --- .github/CODEOWNERS | 1 + config/serverless.oblt.yml | 2 ++ docs/developer/plugin-list.asciidoc | 4 +++ package.json | 1 + packages/kbn-optimizer/limits.yml | 1 + tsconfig.base.json | 2 ++ .../serverless_observability/.gitignore | 2 ++ .../serverless_observability/.i18nrc.json | 7 ++++ .../serverless_observability/README.md | 3 ++ .../serverless_observability/common/index.ts | 9 +++++ .../serverless_observability/kibana.jsonc | 21 ++++++++++++ .../serverless_observability/package.json | 11 +++++++ .../serverless_observability/public/index.ts | 19 +++++++++++ .../serverless_observability/public/plugin.ts | 33 +++++++++++++++++++ .../serverless_observability/public/types.ts | 21 ++++++++++++ .../serverless_observability/server/config.ts | 23 +++++++++++++ .../serverless_observability/server/index.ts | 23 +++++++++++++ .../serverless_observability/server/plugin.ts | 26 +++++++++++++++ .../serverless_observability/server/types.ts | 11 +++++++ .../serverless_observability/tsconfig.json | 22 +++++++++++++ yarn.lock | 4 +++ 21 files changed, 246 insertions(+) create mode 100644 x-pack/plugins/serverless_observability/.gitignore create mode 100644 x-pack/plugins/serverless_observability/.i18nrc.json create mode 100755 x-pack/plugins/serverless_observability/README.md create mode 100644 x-pack/plugins/serverless_observability/common/index.ts create mode 100644 x-pack/plugins/serverless_observability/kibana.jsonc create mode 100644 x-pack/plugins/serverless_observability/package.json create mode 100644 x-pack/plugins/serverless_observability/public/index.ts create mode 100644 x-pack/plugins/serverless_observability/public/plugin.ts create mode 100644 x-pack/plugins/serverless_observability/public/types.ts create mode 100644 x-pack/plugins/serverless_observability/server/config.ts create mode 100644 x-pack/plugins/serverless_observability/server/index.ts create mode 100644 x-pack/plugins/serverless_observability/server/plugin.ts create mode 100644 x-pack/plugins/serverless_observability/server/types.ts create mode 100644 x-pack/plugins/serverless_observability/tsconfig.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 49205649178b0..f3adead0d4e33 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -543,6 +543,7 @@ packages/kbn-securitysolution-t-grid @elastic/security-solution-platform packages/kbn-securitysolution-utils @elastic/security-solution-platform packages/kbn-server-http-tools @elastic/kibana-core packages/kbn-server-route-repository @elastic/apm-ui +x-pack/plugins/serverless_observability @elastic/appex-sharedux test/plugin_functional/plugins/session_notifications @elastic/kibana-core x-pack/plugins/session_view @elastic/awp-viz packages/kbn-set-map @elastic/kibana-operations diff --git a/config/serverless.oblt.yml b/config/serverless.oblt.yml index 58d786b798fc9..b35e67c81aee7 100644 --- a/config/serverless.oblt.yml +++ b/config/serverless.oblt.yml @@ -5,4 +5,6 @@ xpack.reporting.enabled: false xpack.securitySolution.enabled: false xpack.watcher.enabled: false +xpack.serverless.observability.enabled: true + uiSettings.overrides.defaultRoute: /app/observability/overview diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index 3cdca29fcb49d..fa1c4080fe42b 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -689,6 +689,10 @@ Kibana. |Welcome to the Kibana Security Solution plugin! This README will go over getting started with development and testing. +|{kib-repo}blob/{branch}/x-pack/plugins/serverless_observability/README.md[serverlessObservability] +|A witty, fitting description to come. + + |{kib-repo}blob/{branch}/x-pack/plugins/session_view/README.md[sessionView] |Session View is meant to provide a visualization into what is going on in a particular Linux environment where the agent is running. It looks likes a terminal emulator; however, it is a tool for introspecting process activity and understanding user and service behaviour in your Linux servers and infrastructure. It is a time-ordered series of process executions displayed in a tree over time. diff --git a/package.json b/package.json index 3cce5bf6c7b47..d38535a48cd1b 100644 --- a/package.json +++ b/package.json @@ -545,6 +545,7 @@ "@kbn/securitysolution-utils": "link:packages/kbn-securitysolution-utils", "@kbn/server-http-tools": "link:packages/kbn-server-http-tools", "@kbn/server-route-repository": "link:packages/kbn-server-route-repository", + "@kbn/serverless-observability": "link:x-pack/plugins/serverless_observability", "@kbn/session-notifications-plugin": "link:test/plugin_functional/plugins/session_notifications", "@kbn/session-view-plugin": "link:x-pack/plugins/session_view", "@kbn/set-map": "link:packages/kbn-set-map", diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index ad2fd0490197e..a343faa89a9a4 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -112,6 +112,7 @@ pageLoadAssetSize: searchprofiler: 67080 security: 65433 securitySolution: 66738 + serverlessObservability: 16582 sessionView: 77750 share: 71239 snapshotRestore: 79032 diff --git a/tsconfig.base.json b/tsconfig.base.json index e4cf587ee9cb4..eb21b5cba5dd3 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1080,6 +1080,8 @@ "@kbn/server-http-tools/*": ["packages/kbn-server-http-tools/*"], "@kbn/server-route-repository": ["packages/kbn-server-route-repository"], "@kbn/server-route-repository/*": ["packages/kbn-server-route-repository/*"], + "@kbn/serverless-observability": ["x-pack/plugins/serverless_observability"], + "@kbn/serverless-observability/*": ["x-pack/plugins/serverless_observability/*"], "@kbn/session-notifications-plugin": ["test/plugin_functional/plugins/session_notifications"], "@kbn/session-notifications-plugin/*": ["test/plugin_functional/plugins/session_notifications/*"], "@kbn/session-view-plugin": ["x-pack/plugins/session_view"], diff --git a/x-pack/plugins/serverless_observability/.gitignore b/x-pack/plugins/serverless_observability/.gitignore new file mode 100644 index 0000000000000..c3dca1b96fcc2 --- /dev/null +++ b/x-pack/plugins/serverless_observability/.gitignore @@ -0,0 +1,2 @@ +/build +/target diff --git a/x-pack/plugins/serverless_observability/.i18nrc.json b/x-pack/plugins/serverless_observability/.i18nrc.json new file mode 100644 index 0000000000000..623c26e73e4da --- /dev/null +++ b/x-pack/plugins/serverless_observability/.i18nrc.json @@ -0,0 +1,7 @@ +{ + "prefix": "serverlessObservability", + "paths": { + "serverlessObservability": "." + }, + "translations": ["translations/ja-JP.json"] +} diff --git a/x-pack/plugins/serverless_observability/README.md b/x-pack/plugins/serverless_observability/README.md new file mode 100755 index 0000000000000..25d55bd95bb40 --- /dev/null +++ b/x-pack/plugins/serverless_observability/README.md @@ -0,0 +1,3 @@ +# serverlessObservability + +A witty, fitting description to come. \ No newline at end of file diff --git a/x-pack/plugins/serverless_observability/common/index.ts b/x-pack/plugins/serverless_observability/common/index.ts new file mode 100644 index 0000000000000..d6a5ea767034c --- /dev/null +++ b/x-pack/plugins/serverless_observability/common/index.ts @@ -0,0 +1,9 @@ +/* + * 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. + */ + +export const PLUGIN_ID = 'serverlessObservability'; +export const PLUGIN_NAME = 'serverlessObservability'; diff --git a/x-pack/plugins/serverless_observability/kibana.jsonc b/x-pack/plugins/serverless_observability/kibana.jsonc new file mode 100644 index 0000000000000..0f0e911e6807f --- /dev/null +++ b/x-pack/plugins/serverless_observability/kibana.jsonc @@ -0,0 +1,21 @@ +{ + "type": "plugin", + "id": "@kbn/serverless-observability", + "owner": "@elastic/appex-sharedux", + "description": "Serverless customizations for observability.", + "plugin": { + "id": "serverlessObservability", + "server": true, + "browser": true, + "configPath": [ + "xpack", + "serverless", + "observability" + ], + "requiredPlugins": [ + "observability" + ], + "optionalPlugins": [], + "requiredBundles": [] + } +} diff --git a/x-pack/plugins/serverless_observability/package.json b/x-pack/plugins/serverless_observability/package.json new file mode 100644 index 0000000000000..64b310d7eabae --- /dev/null +++ b/x-pack/plugins/serverless_observability/package.json @@ -0,0 +1,11 @@ +{ + "name": "@kbn/serverless-observability", + "version": "1.0.0", + "license": "Elastic License 2.0", + "private": true, + "scripts": { + "build": "yarn plugin-helpers build", + "plugin-helpers": "node ../../../scripts/plugin_helpers", + "kbn": "node ../../../scripts/kbn" + } +} \ No newline at end of file diff --git a/x-pack/plugins/serverless_observability/public/index.ts b/x-pack/plugins/serverless_observability/public/index.ts new file mode 100644 index 0000000000000..a785b68735375 --- /dev/null +++ b/x-pack/plugins/serverless_observability/public/index.ts @@ -0,0 +1,19 @@ +/* + * 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 { ServerlessObservabilityPlugin } from './plugin'; + +// This exports static code and TypeScript types, +// as well as, Kibana Platform `plugin()` initializer. +export function plugin() { + return new ServerlessObservabilityPlugin(); +} + +export type { + ServerlessObservabilityPluginSetup, + ServerlessObservabilityPluginStart, +} from './types'; diff --git a/x-pack/plugins/serverless_observability/public/plugin.ts b/x-pack/plugins/serverless_observability/public/plugin.ts new file mode 100644 index 0000000000000..dd1c5bfbc39c7 --- /dev/null +++ b/x-pack/plugins/serverless_observability/public/plugin.ts @@ -0,0 +1,33 @@ +/* + * 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 { CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; +import { + ServerlessObservabilityPluginSetup, + ServerlessObservabilityPluginStart, + AppPluginSetupDependencies, +} from './types'; + +export class ServerlessObservabilityPlugin + implements Plugin +{ + public setup( + _core: CoreSetup, + setupDeps: AppPluginSetupDependencies + ): ServerlessObservabilityPluginSetup { + setupDeps.observability.navigation.setIsSidebarEnabled(false); + + // Return methods that should be available to other plugins + return {}; + } + + public start(_core: CoreStart): ServerlessObservabilityPluginStart { + return {}; + } + + public stop() {} +} diff --git a/x-pack/plugins/serverless_observability/public/types.ts b/x-pack/plugins/serverless_observability/public/types.ts new file mode 100644 index 0000000000000..73ab40e9257f7 --- /dev/null +++ b/x-pack/plugins/serverless_observability/public/types.ts @@ -0,0 +1,21 @@ +/* + * 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 { ObservabilityPublicSetup } from '@kbn/observability-plugin/public'; + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ServerlessObservabilityPluginSetup {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ServerlessObservabilityPluginStart {} + +export interface AppPluginSetupDependencies { + observability: ObservabilityPublicSetup; +} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface AppPluginStartDependencies {} diff --git a/x-pack/plugins/serverless_observability/server/config.ts b/x-pack/plugins/serverless_observability/server/config.ts new file mode 100644 index 0000000000000..599a9f2bd7769 --- /dev/null +++ b/x-pack/plugins/serverless_observability/server/config.ts @@ -0,0 +1,23 @@ +/* + * 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 { schema, TypeOf } from '@kbn/config-schema'; +import { PluginConfigDescriptor } from '@kbn/core/server'; + +export * from './types'; + +const configSchema = schema.object({ + enabled: schema.boolean({ defaultValue: false }), +}); + +type ConfigType = TypeOf; + +export const config: PluginConfigDescriptor = { + schema: configSchema, +}; + +export type ServerlessObservabilityConfig = TypeOf; diff --git a/x-pack/plugins/serverless_observability/server/index.ts b/x-pack/plugins/serverless_observability/server/index.ts new file mode 100644 index 0000000000000..c45e363a429bf --- /dev/null +++ b/x-pack/plugins/serverless_observability/server/index.ts @@ -0,0 +1,23 @@ +/* + * 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 { PluginInitializerContext } from '@kbn/core/server'; + +import { ServerlessObservabilityPlugin } from './plugin'; +export { config } from './config'; + +// This exports static code and TypeScript types, +// as well as, Kibana Platform `plugin()` initializer. + +export function plugin(initializerContext: PluginInitializerContext) { + return new ServerlessObservabilityPlugin(initializerContext); +} + +export type { + ServerlessObservabilityPluginSetup, + ServerlessObservabilityPluginStart, +} from './types'; diff --git a/x-pack/plugins/serverless_observability/server/plugin.ts b/x-pack/plugins/serverless_observability/server/plugin.ts new file mode 100644 index 0000000000000..8b28ba2b0a4ac --- /dev/null +++ b/x-pack/plugins/serverless_observability/server/plugin.ts @@ -0,0 +1,26 @@ +/* + * 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 { PluginInitializerContext, Plugin } from '@kbn/core/server'; + +import { ServerlessObservabilityPluginSetup, ServerlessObservabilityPluginStart } from './types'; + +export class ServerlessObservabilityPlugin + implements Plugin +{ + constructor(_initializerContext: PluginInitializerContext) {} + + public setup() { + return {}; + } + + public start() { + return {}; + } + + public stop() {} +} diff --git a/x-pack/plugins/serverless_observability/server/types.ts b/x-pack/plugins/serverless_observability/server/types.ts new file mode 100644 index 0000000000000..f8a587103e886 --- /dev/null +++ b/x-pack/plugins/serverless_observability/server/types.ts @@ -0,0 +1,11 @@ +/* + * 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. + */ + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ServerlessObservabilityPluginSetup {} +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ServerlessObservabilityPluginStart {} diff --git a/x-pack/plugins/serverless_observability/tsconfig.json b/x-pack/plugins/serverless_observability/tsconfig.json new file mode 100644 index 0000000000000..4c9f7b818b430 --- /dev/null +++ b/x-pack/plugins/serverless_observability/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types" + }, + "include": [ + "index.ts", + "common/**/*.ts", + "public/**/*.ts", + "public/**/*.tsx", + "server/**/*.ts", + "../../../typings/**/*" + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/core", + "@kbn/config-schema", + "@kbn/observability-plugin", + ] +} diff --git a/yarn.lock b/yarn.lock index f166a66a39668..241e540cb8b65 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4885,6 +4885,10 @@ version "0.0.0" uid "" +"@kbn/serverless-observability@link:x-pack/plugins/serverless_observability": + version "0.0.0" + uid "" + "@kbn/session-notifications-plugin@link:test/plugin_functional/plugins/session_notifications": version "0.0.0" uid "" From 31d27d857c4b2f305953781218362c973eaccfd0 Mon Sep 17 00:00:00 2001 From: Clint Andrew Hall Date: Sat, 25 Feb 2023 23:16:04 -0500 Subject: [PATCH 04/58] Add ability to hide Security navigation --- .../use_primary_navigation.tsx | 10 ++++++++++ .../public/common/lib/kibana/kibana_react.mock.ts | 2 ++ x-pack/plugins/security_solution/public/index.ts | 4 ++-- x-pack/plugins/security_solution/public/plugin.tsx | 12 ++++++++++-- x-pack/plugins/security_solution/public/types.ts | 9 +++++++-- 5 files changed, 31 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/use_primary_navigation.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/use_primary_navigation.tsx index 647193357b66b..867cda2bcf4e8 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/use_primary_navigation.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/use_primary_navigation.tsx @@ -9,10 +9,12 @@ import React, { useEffect, useState, useCallback } from 'react'; import { i18n } from '@kbn/i18n'; import type { KibanaPageTemplateProps } from '@kbn/shared-ux-page-kibana-template'; +import useObservable from 'react-use/lib/useObservable'; import type { PrimaryNavigationProps } from './types'; import { usePrimaryNavigationItems } from './use_navigation_items'; import { useIsGroupedNavigationEnabled } from '../helpers'; import { SecuritySideNav } from '../security_side_nav'; +import { useKibana } from '../../../lib/kibana'; const translatedNavTitle = i18n.translate('xpack.securitySolution.navigation.mainLabel', { defaultMessage: 'Security', @@ -45,6 +47,14 @@ export const usePrimaryNavigation = ({ selectedTabId, }); + const { isSidebarEnabled$ } = useKibana().services; + + const isSidebarEnabled = useObservable(isSidebarEnabled$); + + if (!isSidebarEnabled) { + return undefined; + } + return { canBeCollapsed: true, name: translatedNavTitle, diff --git a/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts b/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts index efa9ce4831be7..01b67365b6926 100644 --- a/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts +++ b/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts @@ -45,6 +45,7 @@ import { triggersActionsUiMock } from '@kbn/triggers-actions-ui-plugin/public/mo import { mockApm } from '../apm/service.mock'; import { cloudExperimentsMock } from '@kbn/cloud-experiments-plugin/common/mocks'; import { guidedOnboardingMock } from '@kbn/guided-onboarding-plugin/public/mocks'; +import { of } from 'rxjs'; const mockUiSettings: Record = { [DEFAULT_TIME_RANGE]: { from: 'now-15m', to: 'now', mode: 'quick' }, @@ -176,6 +177,7 @@ export const createStartServicesMock = ( triggersActionsUi, cloudExperiments, guidedOnboarding, + isSidebarEnabled$: of(true), } as unknown as StartServices; }; diff --git a/x-pack/plugins/security_solution/public/index.ts b/x-pack/plugins/security_solution/public/index.ts index 1f6f121e04209..7ac596d087fca 100644 --- a/x-pack/plugins/security_solution/public/index.ts +++ b/x-pack/plugins/security_solution/public/index.ts @@ -7,10 +7,10 @@ import type { PluginInitializerContext } from '@kbn/core/public'; import { Plugin } from './plugin'; -import type { PluginSetup } from './types'; +import type { PluginSetup, PluginStart } from './types'; export type { TimelineModel } from './timelines/store/timeline/model'; export const plugin = (context: PluginInitializerContext): Plugin => new Plugin(context); -export type { PluginSetup }; +export type { PluginSetup, PluginStart }; export { Plugin }; diff --git a/x-pack/plugins/security_solution/public/plugin.tsx b/x-pack/plugins/security_solution/public/plugin.tsx index abc9c41101e25..616168b52cbba 100644 --- a/x-pack/plugins/security_solution/public/plugin.tsx +++ b/x-pack/plugins/security_solution/public/plugin.tsx @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; import type { Subscription } from 'rxjs'; -import { Subject } from 'rxjs'; +import { BehaviorSubject, Subject } from 'rxjs'; import { combineLatestWith } from 'rxjs/operators'; import type * as H from 'history'; import type { @@ -84,6 +84,7 @@ export class Plugin implements IPlugin; constructor(private readonly initializerContext: PluginInitializerContext) { this.config = this.initializerContext.config.get(); @@ -91,6 +92,7 @@ export class Plugin implements IPlugin(true); } private appUpdater$ = new Subject(); @@ -159,6 +161,7 @@ export class Plugin implements IPlugin SecuritySolutionTemplateWrapper, }, + isSidebarEnabled$: this.isSidebarEnabled$, }; return services; }; @@ -296,7 +299,12 @@ export class Plugin implements IPlugin { + this.isSidebarEnabled$.next(enabled); + }, + }; } public stop() { diff --git a/x-pack/plugins/security_solution/public/types.ts b/x-pack/plugins/security_solution/public/types.ts index 65b43d3d4635b..76349ec3c8db5 100644 --- a/x-pack/plugins/security_solution/public/types.ts +++ b/x-pack/plugins/security_solution/public/types.ts @@ -43,6 +43,7 @@ import type { ThreatIntelligencePluginStart } from '@kbn/threat-intelligence-plu import type { CloudExperimentsPluginStart } from '@kbn/cloud-experiments-plugin/common'; import type { GuidedOnboardingPluginStart } from '@kbn/guided-onboarding-plugin/public'; import type { DataViewsServicePublic } from '@kbn/data-views-plugin/public'; +import type { BehaviorSubject } from 'rxjs'; import type { ResolverPluginSetup } from './resolver/types'; import type { Inspect } from '../common/search_strategy'; import type { Detections } from './detections'; @@ -116,13 +117,17 @@ export type StartServices = CoreStart & securityLayout: { getPluginWrapper: () => typeof SecuritySolutionTemplateWrapper; }; + isSidebarEnabled$: BehaviorSubject; }; export interface PluginSetup { resolver: () => Promise; } -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface PluginStart {} + +export interface PluginStart { + isSidebarEnabled$: BehaviorSubject; + setIsSidebarEnabled: (enabled: boolean) => void; +} export interface AppObservableLibs { kibana: CoreStart; From 2cf8b52f8cbfebbe46c181f35d32fd6b26136c99 Mon Sep 17 00:00:00 2001 From: Clint Andrew Hall Date: Sat, 25 Feb 2023 23:16:11 -0500 Subject: [PATCH 05/58] Create Serverless security plugin; hide internal nav via API --- .github/CODEOWNERS | 1 + config/serverless.security.yml | 10 +++++ docs/developer/plugin-list.asciidoc | 4 ++ package.json | 1 + packages/kbn-optimizer/limits.yml | 1 + tsconfig.base.json | 2 + .../security_solution/public/plugin.tsx | 10 ++--- .../plugins/security_solution/public/types.ts | 7 ++-- x-pack/plugins/serverless_security/.gitignore | 2 + .../plugins/serverless_security/.i18nrc.json | 9 ++++ x-pack/plugins/serverless_security/README.md | 3 ++ .../serverless_security/common/index.ts | 9 ++++ .../plugins/serverless_security/kibana.jsonc | 22 ++++++++++ .../plugins/serverless_security/package.json | 11 +++++ .../serverless_security/public/index.ts | 16 ++++++++ .../serverless_security/public/plugin.ts | 41 +++++++++++++++++++ .../serverless_security/public/types.ts | 28 +++++++++++++ .../serverless_security/server/config.ts | 23 +++++++++++ .../serverless_security/server/index.ts | 20 +++++++++ .../serverless_security/server/plugin.ts | 37 +++++++++++++++++ .../serverless_security/server/types.ts | 27 ++++++++++++ .../plugins/serverless_security/tsconfig.json | 23 +++++++++++ yarn.lock | 4 ++ 23 files changed, 301 insertions(+), 10 deletions(-) create mode 100644 x-pack/plugins/serverless_security/.gitignore create mode 100644 x-pack/plugins/serverless_security/.i18nrc.json create mode 100755 x-pack/plugins/serverless_security/README.md create mode 100644 x-pack/plugins/serverless_security/common/index.ts create mode 100644 x-pack/plugins/serverless_security/kibana.jsonc create mode 100644 x-pack/plugins/serverless_security/package.json create mode 100644 x-pack/plugins/serverless_security/public/index.ts create mode 100644 x-pack/plugins/serverless_security/public/plugin.ts create mode 100644 x-pack/plugins/serverless_security/public/types.ts create mode 100644 x-pack/plugins/serverless_security/server/config.ts create mode 100644 x-pack/plugins/serverless_security/server/index.ts create mode 100644 x-pack/plugins/serverless_security/server/plugin.ts create mode 100644 x-pack/plugins/serverless_security/server/types.ts create mode 100644 x-pack/plugins/serverless_security/tsconfig.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f3adead0d4e33..f1dcd642e22d1 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -544,6 +544,7 @@ packages/kbn-securitysolution-utils @elastic/security-solution-platform packages/kbn-server-http-tools @elastic/kibana-core packages/kbn-server-route-repository @elastic/apm-ui x-pack/plugins/serverless_observability @elastic/appex-sharedux +x-pack/plugins/serverless_security @elastic/appex-sharedux test/plugin_functional/plugins/session_notifications @elastic/kibana-core x-pack/plugins/session_view @elastic/awp-viz packages/kbn-set-map @elastic/kibana-operations diff --git a/config/serverless.security.yml b/config/serverless.security.yml index e69de29bb2d1d..a5e1c170ddccd 100644 --- a/config/serverless.security.yml +++ b/config/serverless.security.yml @@ -0,0 +1,10 @@ +xpack.apm.enabled: false +xpack.canvas.enabled: false +xpack.observability.enabled: false +xpack.reporting.enabled: false +xpack.uptime.enabled: false +xpack.watcher.enabled: false + +xpack.serverless.security.enabled: true + +uiSettings.overrides.defaultRoute: /app/security/get_started diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index fa1c4080fe42b..0502017da81a4 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -693,6 +693,10 @@ Kibana. |A witty, fitting description to come. +|{kib-repo}blob/{branch}/x-pack/plugins/serverless_security/README.md[serverlessSecurity] +|A witty, fitting description to come. + + |{kib-repo}blob/{branch}/x-pack/plugins/session_view/README.md[sessionView] |Session View is meant to provide a visualization into what is going on in a particular Linux environment where the agent is running. It looks likes a terminal emulator; however, it is a tool for introspecting process activity and understanding user and service behaviour in your Linux servers and infrastructure. It is a time-ordered series of process executions displayed in a tree over time. diff --git a/package.json b/package.json index d38535a48cd1b..5db0cdbe2feb1 100644 --- a/package.json +++ b/package.json @@ -546,6 +546,7 @@ "@kbn/server-http-tools": "link:packages/kbn-server-http-tools", "@kbn/server-route-repository": "link:packages/kbn-server-route-repository", "@kbn/serverless-observability": "link:x-pack/plugins/serverless_observability", + "@kbn/serverless-security": "link:x-pack/plugins/serverless_security", "@kbn/session-notifications-plugin": "link:test/plugin_functional/plugins/session_notifications", "@kbn/session-view-plugin": "link:x-pack/plugins/session_view", "@kbn/set-map": "link:packages/kbn-set-map", diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index a343faa89a9a4..886ca3817fe0f 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -113,6 +113,7 @@ pageLoadAssetSize: security: 65433 securitySolution: 66738 serverlessObservability: 16582 + serverlessSecurity: 16556 sessionView: 77750 share: 71239 snapshotRestore: 79032 diff --git a/tsconfig.base.json b/tsconfig.base.json index eb21b5cba5dd3..2cf366080f5cd 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1082,6 +1082,8 @@ "@kbn/server-route-repository/*": ["packages/kbn-server-route-repository/*"], "@kbn/serverless-observability": ["x-pack/plugins/serverless_observability"], "@kbn/serverless-observability/*": ["x-pack/plugins/serverless_observability/*"], + "@kbn/serverless-security": ["x-pack/plugins/serverless_security"], + "@kbn/serverless-security/*": ["x-pack/plugins/serverless_security/*"], "@kbn/session-notifications-plugin": ["test/plugin_functional/plugins/session_notifications"], "@kbn/session-notifications-plugin/*": ["test/plugin_functional/plugins/session_notifications/*"], "@kbn/session-view-plugin": ["x-pack/plugins/session_view"], diff --git a/x-pack/plugins/security_solution/public/plugin.tsx b/x-pack/plugins/security_solution/public/plugin.tsx index 616168b52cbba..87bd7e72223a4 100644 --- a/x-pack/plugins/security_solution/public/plugin.tsx +++ b/x-pack/plugins/security_solution/public/plugin.tsx @@ -235,6 +235,9 @@ export class Plugin implements IPlugin { + this.isSidebarEnabled$.next(enabled); + }, }; } @@ -299,12 +302,7 @@ export class Plugin implements IPlugin { - this.isSidebarEnabled$.next(enabled); - }, - }; + return {}; } public stop() { diff --git a/x-pack/plugins/security_solution/public/types.ts b/x-pack/plugins/security_solution/public/types.ts index 76349ec3c8db5..4cdbce5c2cf47 100644 --- a/x-pack/plugins/security_solution/public/types.ts +++ b/x-pack/plugins/security_solution/public/types.ts @@ -122,13 +122,12 @@ export type StartServices = CoreStart & export interface PluginSetup { resolver: () => Promise; -} - -export interface PluginStart { - isSidebarEnabled$: BehaviorSubject; setIsSidebarEnabled: (enabled: boolean) => void; } +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface PluginStart {} + export interface AppObservableLibs { kibana: CoreStart; } diff --git a/x-pack/plugins/serverless_security/.gitignore b/x-pack/plugins/serverless_security/.gitignore new file mode 100644 index 0000000000000..c3dca1b96fcc2 --- /dev/null +++ b/x-pack/plugins/serverless_security/.gitignore @@ -0,0 +1,2 @@ +/build +/target diff --git a/x-pack/plugins/serverless_security/.i18nrc.json b/x-pack/plugins/serverless_security/.i18nrc.json new file mode 100644 index 0000000000000..e4519b9cdf9f5 --- /dev/null +++ b/x-pack/plugins/serverless_security/.i18nrc.json @@ -0,0 +1,9 @@ +{ + "prefix": "serverlessSecurity", + "paths": { + "serverlessSecurity": "." + }, + "translations": [ + "translations/ja-JP.json" + ] +} \ No newline at end of file diff --git a/x-pack/plugins/serverless_security/README.md b/x-pack/plugins/serverless_security/README.md new file mode 100755 index 0000000000000..33d229a31060d --- /dev/null +++ b/x-pack/plugins/serverless_security/README.md @@ -0,0 +1,3 @@ +# serverlessSecurity + +A witty, fitting description to come. \ No newline at end of file diff --git a/x-pack/plugins/serverless_security/common/index.ts b/x-pack/plugins/serverless_security/common/index.ts new file mode 100644 index 0000000000000..0dc5be6ddf9bb --- /dev/null +++ b/x-pack/plugins/serverless_security/common/index.ts @@ -0,0 +1,9 @@ +/* + * 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. + */ + +export const PLUGIN_ID = 'serverlessSecurity'; +export const PLUGIN_NAME = 'serverlessSecurity'; diff --git a/x-pack/plugins/serverless_security/kibana.jsonc b/x-pack/plugins/serverless_security/kibana.jsonc new file mode 100644 index 0000000000000..c94c93513209f --- /dev/null +++ b/x-pack/plugins/serverless_security/kibana.jsonc @@ -0,0 +1,22 @@ +{ + "type": "plugin", + "id": "@kbn/serverless-security", + "owner": "@elastic/appex-sharedux", + "description": "Serverless customizations for security.", + "plugin": { + "id": "serverlessSecurity", + "server": true, + "browser": true, + "configPath": [ + "xpack", + "serverless", + "security" + ], + "requiredPlugins": [ + "security", + "securitySolution" + ], + "optionalPlugins": [], + "requiredBundles": [] + } +} \ No newline at end of file diff --git a/x-pack/plugins/serverless_security/package.json b/x-pack/plugins/serverless_security/package.json new file mode 100644 index 0000000000000..0154207c22d6f --- /dev/null +++ b/x-pack/plugins/serverless_security/package.json @@ -0,0 +1,11 @@ +{ + "name": "@kbn/serverless-security", + "version": "1.0.0", + "license": "Elastic License 2.0", + "private": true, + "scripts": { + "build": "yarn plugin-helpers build", + "plugin-helpers": "node ../../../scripts/plugin_helpers", + "kbn": "node ../../../scripts/kbn" + } +} \ No newline at end of file diff --git a/x-pack/plugins/serverless_security/public/index.ts b/x-pack/plugins/serverless_security/public/index.ts new file mode 100644 index 0000000000000..ce7b6e989f4ca --- /dev/null +++ b/x-pack/plugins/serverless_security/public/index.ts @@ -0,0 +1,16 @@ +/* + * 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 { ServerlessSecurityPlugin } from './plugin'; + +// This exports static code and TypeScript types, +// as well as, Kibana Platform `plugin()` initializer. +export function plugin() { + return new ServerlessSecurityPlugin(); +} + +export type { ServerlessSecurityPluginSetup, ServerlessSecurityPluginStart } from './types'; diff --git a/x-pack/plugins/serverless_security/public/plugin.ts b/x-pack/plugins/serverless_security/public/plugin.ts new file mode 100644 index 0000000000000..4af166c8cf804 --- /dev/null +++ b/x-pack/plugins/serverless_security/public/plugin.ts @@ -0,0 +1,41 @@ +/* + * 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 { CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; +import { + ServerlessSecurityPluginSetup, + ServerlessSecurityPluginStart, + ServerlessSecurityPluginSetupDependencies, + ServerlessSecurityPluginStartDependencies, +} from './types'; + +export class ServerlessSecurityPlugin + implements + Plugin< + ServerlessSecurityPluginSetup, + ServerlessSecurityPluginStart, + ServerlessSecurityPluginSetupDependencies, + ServerlessSecurityPluginStartDependencies + > +{ + public setup( + _core: CoreSetup, + setupDeps: ServerlessSecurityPluginSetupDependencies + ): ServerlessSecurityPluginSetup { + setupDeps.securitySolution.setIsSidebarEnabled(false); + return {}; + } + + public start( + _core: CoreStart, + _startDeps: ServerlessSecurityPluginStartDependencies + ): ServerlessSecurityPluginStart { + return {}; + } + + public stop() {} +} diff --git a/x-pack/plugins/serverless_security/public/types.ts b/x-pack/plugins/serverless_security/public/types.ts new file mode 100644 index 0000000000000..58ac0b112abcf --- /dev/null +++ b/x-pack/plugins/serverless_security/public/types.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SecurityPluginSetup, SecurityPluginStart } from '@kbn/security-plugin/public'; +import { + PluginSetup as SecuritySolutionPluginSetup, + PluginStart as SecuritySolutionPluginStart, +} from '@kbn/security-solution-plugin/public'; + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ServerlessSecurityPluginSetup {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ServerlessSecurityPluginStart {} + +export interface ServerlessSecurityPluginSetupDependencies { + security: SecurityPluginSetup; + securitySolution: SecuritySolutionPluginSetup; +} + +export interface ServerlessSecurityPluginStartDependencies { + security: SecurityPluginStart; + securitySolution: SecuritySolutionPluginStart; +} diff --git a/x-pack/plugins/serverless_security/server/config.ts b/x-pack/plugins/serverless_security/server/config.ts new file mode 100644 index 0000000000000..758b22de1514e --- /dev/null +++ b/x-pack/plugins/serverless_security/server/config.ts @@ -0,0 +1,23 @@ +/* + * 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 { schema, TypeOf } from '@kbn/config-schema'; +import { PluginConfigDescriptor } from '@kbn/core/server'; + +export * from './types'; + +const configSchema = schema.object({ + enabled: schema.boolean({ defaultValue: false }), +}); + +type ConfigType = TypeOf; + +export const config: PluginConfigDescriptor = { + schema: configSchema, +}; + +export type ServerlessSecurityConfig = TypeOf; diff --git a/x-pack/plugins/serverless_security/server/index.ts b/x-pack/plugins/serverless_security/server/index.ts new file mode 100644 index 0000000000000..b2b6c8564f788 --- /dev/null +++ b/x-pack/plugins/serverless_security/server/index.ts @@ -0,0 +1,20 @@ +/* + * 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 { PluginInitializerContext } from '@kbn/core/server'; + +import { ServerlessSecurityPlugin } from './plugin'; +export { config } from './config'; + +// This exports static code and TypeScript types, +// as well as, Kibana Platform `plugin()` initializer. + +export function plugin(initializerContext: PluginInitializerContext) { + return new ServerlessSecurityPlugin(initializerContext); +} + +export type { ServerlessSecurityPluginSetup, ServerlessSecurityPluginStart } from './types'; diff --git a/x-pack/plugins/serverless_security/server/plugin.ts b/x-pack/plugins/serverless_security/server/plugin.ts new file mode 100644 index 0000000000000..7dfbae3b64a67 --- /dev/null +++ b/x-pack/plugins/serverless_security/server/plugin.ts @@ -0,0 +1,37 @@ +/* + * 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 { PluginInitializerContext, Plugin } from '@kbn/core/server'; + +import { + ServerlessSecurityPluginSetup, + ServerlessSecurityPluginStart, + ServerlessSecurityPluginSetupDependencies, + ServerlessSecurityPluginStartDependencies, +} from './types'; + +export class ServerlessSecurityPlugin + implements + Plugin< + ServerlessSecurityPluginSetup, + ServerlessSecurityPluginStart, + ServerlessSecurityPluginSetupDependencies, + ServerlessSecurityPluginStartDependencies + > +{ + constructor(_initializerContext: PluginInitializerContext) {} + + public setup() { + return {}; + } + + public start() { + return {}; + } + + public stop() {} +} diff --git a/x-pack/plugins/serverless_security/server/types.ts b/x-pack/plugins/serverless_security/server/types.ts new file mode 100644 index 0000000000000..be7167d030315 --- /dev/null +++ b/x-pack/plugins/serverless_security/server/types.ts @@ -0,0 +1,27 @@ +/* + * 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 { SecurityPluginSetup, SecurityPluginStart } from '@kbn/security-plugin/server'; +import { + PluginSetup as SecuritySolutionPluginSetup, + PluginStart as SecuritySolutionPluginStart, +} from '@kbn/security-solution-plugin/server'; + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ServerlessSecurityPluginSetup {} +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ServerlessSecurityPluginStart {} + +export interface ServerlessSecurityPluginSetupDependencies { + security: SecurityPluginSetup; + securitySolution: SecuritySolutionPluginSetup; +} + +export interface ServerlessSecurityPluginStartDependencies { + security: SecurityPluginStart; + securitySolution: SecuritySolutionPluginStart; +} diff --git a/x-pack/plugins/serverless_security/tsconfig.json b/x-pack/plugins/serverless_security/tsconfig.json new file mode 100644 index 0000000000000..7937d55df3797 --- /dev/null +++ b/x-pack/plugins/serverless_security/tsconfig.json @@ -0,0 +1,23 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types" + }, + "include": [ + "index.ts", + "common/**/*.ts", + "public/**/*.ts", + "public/**/*.tsx", + "server/**/*.ts", + "../../../typings/**/*" + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/core", + "@kbn/security-plugin", + "@kbn/security-solution-plugin", + "@kbn/config-schema", + ] +} diff --git a/yarn.lock b/yarn.lock index 241e540cb8b65..8eb67c171d178 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4889,6 +4889,10 @@ version "0.0.0" uid "" +"@kbn/serverless-security@link:x-pack/plugins/serverless_security": + version "0.0.0" + uid "" + "@kbn/session-notifications-plugin@link:test/plugin_functional/plugins/session_notifications": version "0.0.0" uid "" From e601b899437b560baf227c70c5dce2527f436ac3 Mon Sep 17 00:00:00 2001 From: Clint Andrew Hall Date: Sun, 26 Feb 2023 00:17:54 -0500 Subject: [PATCH 06/58] Create 'solution' chrome style, add API to place solution navigation --- .../src/chrome_service.tsx | 111 +++++++++++++----- .../src/ui/index.ts | 1 + .../src/ui/solution/header.tsx | 111 ++++++++++++++++++ .../src/ui/solution/index.ts | 9 ++ .../tsconfig.json | 3 +- .../src/chrome_service.mock.ts | 3 + .../core/chrome/core-chrome-browser/index.ts | 27 +++-- .../core-chrome-browser/src/contracts.ts | 6 +- .../chrome/core-chrome-browser/src/index.ts | 2 +- .../chrome/core-chrome-browser/src/types.ts | 3 + src/core/public/_variables.scss | 4 +- 11 files changed, 231 insertions(+), 49 deletions(-) create mode 100644 packages/core/chrome/core-chrome-browser-internal/src/ui/solution/header.tsx create mode 100644 packages/core/chrome/core-chrome-browser-internal/src/ui/solution/index.ts diff --git a/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx b/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx index 4e0762aee8620..4dc599e8fbbd4 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx @@ -26,6 +26,7 @@ import type { ChromeGlobalHelpExtensionMenuLink, ChromeHelpExtension, ChromeUserBanner, + ChromeStyle, } from '@kbn/core-chrome-browser'; import type { CustomBrandingStart } from '@kbn/core-custom-branding-browser'; import { KIBANA_ASK_ELASTIC_LINK } from './constants'; @@ -33,7 +34,7 @@ import { DocTitleService } from './doc_title'; import { NavControlsService } from './nav_controls'; import { NavLinksService } from './nav_links'; import { RecentlyAccessedService } from './recently_accessed'; -import { Header } from './ui'; +import { Header, SolutionHeader } from './ui'; import type { InternalChromeStart } from './types'; const IS_LOCKED_KEY = 'core.chrome.isLocked'; @@ -119,6 +120,8 @@ export class ChromeService { const customNavLink$ = new BehaviorSubject(undefined); const helpSupportUrl$ = new BehaviorSubject(KIBANA_ASK_ELASTIC_LINK); const isNavDrawerLocked$ = new BehaviorSubject(localStorage.getItem(IS_LOCKED_KEY) === 'true'); + const chromeStyle$ = new BehaviorSubject('classic'); + const solutionNavigation$ = new BehaviorSubject(undefined); const getKbnVersionClass = () => { // we assume that the version is valid and has the form 'X.X.X' @@ -163,6 +166,16 @@ export class ChromeService { const getIsNavDrawerLocked$ = isNavDrawerLocked$.pipe(takeUntil(this.stop$)); + const setChromeStyle = (style: ChromeStyle) => { + chromeStyle$.next(style); + }; + + const setSolutionNavigation = (navigation: JSX.Element) => { + solutionNavigation$.next(navigation); + }; + + const getChromeStyle$ = chromeStyle$.pipe(takeUntil(this.stop$)); + const isIE = () => { const ua = window.navigator.userAgent; const msie = ua.indexOf('MSIE '); // IE 10 or older @@ -203,41 +216,74 @@ export class ChromeService { }); } + const getHeaderComponent = () => { + const Component = ({ + style$, + navigation$, + }: { + style$: typeof chromeStyle$; + navigation$: typeof solutionNavigation$; + }) => { + if (style$.getValue() === 'solution') { + const navigation = navigation$.getValue(); + if (navigation) { + return ( + + ); + } + } + + return ( +
+ ); + }; + return ; + }; + return { navControls, navLinks, recentlyAccessed, docTitle, - - getHeaderComponent: () => ( -
- ), + getHeaderComponent, getIsVisible$: () => this.isVisible$, @@ -302,6 +348,9 @@ export class ChromeService { }, getBodyClasses$: () => bodyClasses$.pipe(takeUntil(this.stop$)), + setChromeStyle, + getChromeStyle$: () => getChromeStyle$, + setSolutionNavigation, }; } diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/index.ts b/packages/core/chrome/core-chrome-browser-internal/src/ui/index.ts index 5afd3e0f587bb..0fd5240eac29d 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/ui/index.ts +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/index.ts @@ -7,5 +7,6 @@ */ export { Header } from './header'; +export { SolutionHeader } from './solution'; export { LoadingIndicator } from './loading_indicator'; export type { NavType } from './header'; diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/solution/header.tsx b/packages/core/chrome/core-chrome-browser-internal/src/ui/solution/header.tsx new file mode 100644 index 0000000000000..0aef9755994d8 --- /dev/null +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/solution/header.tsx @@ -0,0 +1,111 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { + EuiButtonIcon, + EuiCollapsibleNav, + EuiHeader, + EuiHeaderLogo, + EuiHeaderSection, + EuiHeaderSectionItem, + EuiThemeProvider, + useEuiTheme, +} from '@elastic/eui'; +import { + ChromeBreadcrumb, + ChromeGlobalHelpExtensionMenuLink, + ChromeHelpExtension, +} from '@kbn/core-chrome-browser/src'; +import { Observable } from 'rxjs'; +import { MountPoint } from '@kbn/core-mount-utils-browser'; +import { InternalApplicationStart } from '@kbn/core-application-browser-internal'; +import { HeaderBreadcrumbs } from '../header/header_breadcrumbs'; +import { HeaderActionMenu } from '../header/header_action_menu'; +import { HeaderHelpMenu } from '../header/header_help_menu'; + +interface Props { + breadcrumbs$: Observable; + actionMenu$: Observable; + kibanaDocLink: string; + globalHelpExtensionMenuLinks$: Observable; + helpExtension$: Observable; + helpSupportUrl$: Observable; + kibanaVersion: string; + application: InternalApplicationStart; + navigation: JSX.Element; +} + +export const SolutionHeader = ({ + application, + kibanaDocLink, + kibanaVersion, + navigation, + ...observables +}: Props) => { + const { euiTheme, colorMode } = useEuiTheme(); + + const renderLogo = () => ( + e.preventDefault()} + aria-label="Go to home page" + /> + ); + + return ( + <> + + + {renderLogo()} + + + + + + + + + + + + + + + {}} + closeButtonProps={{ iconType: 'menuLeft' }} + showButtonIfDocked={true} + isDocked={true} + size={248} + hideCloseButton={false} + button={ + + + + } + > + {navigation} + + + + ); +}; diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/solution/index.ts b/packages/core/chrome/core-chrome-browser-internal/src/ui/solution/index.ts new file mode 100644 index 0000000000000..5c116bc6b30c8 --- /dev/null +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/solution/index.ts @@ -0,0 +1,9 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +export { SolutionHeader } from './header'; diff --git a/packages/core/chrome/core-chrome-browser-internal/tsconfig.json b/packages/core/chrome/core-chrome-browser-internal/tsconfig.json index 4d4d6cad3bc21..019f15681b6c1 100644 --- a/packages/core/chrome/core-chrome-browser-internal/tsconfig.json +++ b/packages/core/chrome/core-chrome-browser-internal/tsconfig.json @@ -5,7 +5,8 @@ "types": [ "jest", "node", - "react" + "react", + "@emotion/react/types/css-prop" ] }, "include": [ diff --git a/packages/core/chrome/core-chrome-browser-mocks/src/chrome_service.mock.ts b/packages/core/chrome/core-chrome-browser-mocks/src/chrome_service.mock.ts index 2f5c4deb1f38d..e33eca66322f8 100644 --- a/packages/core/chrome/core-chrome-browser-mocks/src/chrome_service.mock.ts +++ b/packages/core/chrome/core-chrome-browser-mocks/src/chrome_service.mock.ts @@ -61,6 +61,9 @@ const createStartContractMock = () => { setHeaderBanner: jest.fn(), hasHeaderBanner$: jest.fn(), getBodyClasses$: jest.fn(), + getChromeStyle$: jest.fn(), + setChromeStyle: jest.fn(), + setSolutionNavigation: jest.fn(), }; startContract.navLinks.getAll.mockReturnValue([]); startContract.getIsVisible$.mockReturnValue(new BehaviorSubject(false)); diff --git a/packages/core/chrome/core-chrome-browser/index.ts b/packages/core/chrome/core-chrome-browser/index.ts index 3fbef34126a4a..1d2dca4c957bc 100644 --- a/packages/core/chrome/core-chrome-browser/index.ts +++ b/packages/core/chrome/core-chrome-browser/index.ts @@ -7,25 +7,26 @@ */ export type { - ChromeUserBanner, + ChromeBadge, ChromeBreadcrumb, + ChromeBreadcrumbsAppendExtension, + ChromeDocTitle, + ChromeGlobalHelpExtensionMenuLink, ChromeHelpExtension, - ChromeHelpExtensionMenuLink, ChromeHelpExtensionLinkBase, + ChromeHelpExtensionMenuCustomLink, + ChromeHelpExtensionMenuDiscussLink, + ChromeHelpExtensionMenuDocumentationLink, + ChromeHelpExtensionMenuGitHubLink, + ChromeHelpExtensionMenuLink, ChromeHelpMenuActions, - ChromeNavLink, - ChromeBreadcrumbsAppendExtension, - ChromeNavLinks, ChromeNavControl, ChromeNavControls, - ChromeBadge, - ChromeHelpExtensionMenuGitHubLink, - ChromeHelpExtensionMenuDocumentationLink, - ChromeHelpExtensionMenuDiscussLink, - ChromeHelpExtensionMenuCustomLink, - ChromeGlobalHelpExtensionMenuLink, - ChromeDocTitle, - ChromeStart, + ChromeNavLink, + ChromeNavLinks, ChromeRecentlyAccessed, ChromeRecentlyAccessedHistoryItem, + ChromeStart, + ChromeStyle, + ChromeUserBanner, } from './src'; diff --git a/packages/core/chrome/core-chrome-browser/src/contracts.ts b/packages/core/chrome/core-chrome-browser/src/contracts.ts index a81d9c3c6338f..2a966e367d6e4 100644 --- a/packages/core/chrome/core-chrome-browser/src/contracts.ts +++ b/packages/core/chrome/core-chrome-browser/src/contracts.ts @@ -13,7 +13,7 @@ import type { ChromeDocTitle } from './doc_title'; import type { ChromeNavControls } from './nav_controls'; import type { ChromeHelpExtension } from './help_extension'; import type { ChromeBreadcrumb, ChromeBreadcrumbsAppendExtension } from './breadcrumb'; -import type { ChromeBadge, ChromeUserBanner } from './types'; +import type { ChromeBadge, ChromeStyle, ChromeUserBanner } from './types'; import { ChromeGlobalHelpExtensionMenuLink } from './help_extension'; /** @@ -150,4 +150,8 @@ export interface ChromeStart { * Get an observable of the current header banner presence state. */ hasHeaderBanner$(): Observable; + + setChromeStyle(style: ChromeStyle): void; + getChromeStyle$(): Observable; + setSolutionNavigation(solutionNavigation: JSX.Element): void; } diff --git a/packages/core/chrome/core-chrome-browser/src/index.ts b/packages/core/chrome/core-chrome-browser/src/index.ts index 716af097fded7..89ba12d616d0e 100644 --- a/packages/core/chrome/core-chrome-browser/src/index.ts +++ b/packages/core/chrome/core-chrome-browser/src/index.ts @@ -26,4 +26,4 @@ export type { ChromeRecentlyAccessed, ChromeRecentlyAccessedHistoryItem, } from './recently_accessed'; -export type { ChromeBadge, ChromeUserBanner } from './types'; +export type { ChromeBadge, ChromeUserBanner, ChromeStyle } from './types'; diff --git a/packages/core/chrome/core-chrome-browser/src/types.ts b/packages/core/chrome/core-chrome-browser/src/types.ts index 81b8c32a1a04c..40f29a073137d 100644 --- a/packages/core/chrome/core-chrome-browser/src/types.ts +++ b/packages/core/chrome/core-chrome-browser/src/types.ts @@ -20,3 +20,6 @@ export interface ChromeBadge { export interface ChromeUserBanner { content: MountPoint; } + +/** @public */ +export type ChromeStyle = 'classic' | 'solution'; diff --git a/src/core/public/_variables.scss b/src/core/public/_variables.scss index 6c21c9760be97..6c20ee1a4f4fd 100644 --- a/src/core/public/_variables.scss +++ b/src/core/public/_variables.scss @@ -1,8 +1,8 @@ @import '@elastic/eui/src/global_styling/variables/header'; // height of the header banner -$kbnHeaderBannerHeight: $euiSizeXL; // This value is also declared in `/x-pack/plugins/canvas/common/lib/constants.ts` +$kbnHeaderBannerHeight: $euiSizeXL; // This value is also declared in `/x-pack/plugins/canvas/common/lib/constants.ts` // total height of the header (when the banner is *not* present) -$kbnHeaderOffset: $euiHeaderHeightCompensation * 2; +$kbnHeaderOffset: $euiHeaderHeightCompensation * 2; // <- TODO: this is a problem for Projects, as the black bar is removed. // total height of the header when the banner is present $kbnHeaderOffsetWithBanner: $kbnHeaderOffset + $kbnHeaderBannerHeight; From fff494ce1f46ec440a3a4d947e72e0af3580715c Mon Sep 17 00:00:00 2001 From: Clint Andrew Hall Date: Sun, 26 Feb 2023 00:56:06 -0500 Subject: [PATCH 07/58] Create Serverless plugin; alias core navigation to local API --- .github/CODEOWNERS | 1 + docs/developer/plugin-list.asciidoc | 4 +++ package.json | 1 + packages/kbn-optimizer/limits.yml | 1 + tsconfig.base.json | 2 ++ x-pack/plugins/serverless/.i18nrc.json | 7 ++++++ x-pack/plugins/serverless/README.md | 19 ++++++++++++++ x-pack/plugins/serverless/common/index.ts | 9 +++++++ x-pack/plugins/serverless/kibana.jsonc | 19 ++++++++++++++ x-pack/plugins/serverless/package.json | 11 ++++++++ x-pack/plugins/serverless/public/index.ts | 16 ++++++++++++ x-pack/plugins/serverless/public/plugin.ts | 25 +++++++++++++++++++ x-pack/plugins/serverless/public/types.ts | 13 ++++++++++ x-pack/plugins/serverless/server/config.ts | 23 +++++++++++++++++ x-pack/plugins/serverless/server/index.ts | 19 ++++++++++++++ x-pack/plugins/serverless/server/plugin.ts | 24 ++++++++++++++++++ x-pack/plugins/serverless/server/types.ts | 12 +++++++++ x-pack/plugins/serverless/tsconfig.json | 20 +++++++++++++++ .../plugins/serverless_security/tsconfig.json | 3 +-- yarn.lock | 4 +++ 20 files changed, 231 insertions(+), 2 deletions(-) create mode 100644 x-pack/plugins/serverless/.i18nrc.json create mode 100755 x-pack/plugins/serverless/README.md create mode 100644 x-pack/plugins/serverless/common/index.ts create mode 100644 x-pack/plugins/serverless/kibana.jsonc create mode 100644 x-pack/plugins/serverless/package.json create mode 100644 x-pack/plugins/serverless/public/index.ts create mode 100644 x-pack/plugins/serverless/public/plugin.ts create mode 100644 x-pack/plugins/serverless/public/types.ts create mode 100644 x-pack/plugins/serverless/server/config.ts create mode 100644 x-pack/plugins/serverless/server/index.ts create mode 100644 x-pack/plugins/serverless/server/plugin.ts create mode 100644 x-pack/plugins/serverless/server/types.ts create mode 100644 x-pack/plugins/serverless/tsconfig.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f1dcd642e22d1..d1bada2818532 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -543,6 +543,7 @@ packages/kbn-securitysolution-t-grid @elastic/security-solution-platform packages/kbn-securitysolution-utils @elastic/security-solution-platform packages/kbn-server-http-tools @elastic/kibana-core packages/kbn-server-route-repository @elastic/apm-ui +x-pack/plugins/serverless @elastic/appex-sharedux x-pack/plugins/serverless_observability @elastic/appex-sharedux x-pack/plugins/serverless_security @elastic/appex-sharedux test/plugin_functional/plugins/session_notifications @elastic/kibana-core diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index 0502017da81a4..146300eb04bef 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -689,6 +689,10 @@ Kibana. |Welcome to the Kibana Security Solution plugin! This README will go over getting started with development and testing. +|{kib-repo}blob/{branch}/x-pack/plugins/serverless/README.md[serverless] +|A Kibana plugin + + |{kib-repo}blob/{branch}/x-pack/plugins/serverless_observability/README.md[serverlessObservability] |A witty, fitting description to come. diff --git a/package.json b/package.json index 5db0cdbe2feb1..044a514dc81e3 100644 --- a/package.json +++ b/package.json @@ -545,6 +545,7 @@ "@kbn/securitysolution-utils": "link:packages/kbn-securitysolution-utils", "@kbn/server-http-tools": "link:packages/kbn-server-http-tools", "@kbn/server-route-repository": "link:packages/kbn-server-route-repository", + "@kbn/serverless": "link:x-pack/plugins/serverless", "@kbn/serverless-observability": "link:x-pack/plugins/serverless_observability", "@kbn/serverless-security": "link:x-pack/plugins/serverless_security", "@kbn/session-notifications-plugin": "link:test/plugin_functional/plugins/session_notifications", diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 886ca3817fe0f..d979ab1ce679f 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -112,6 +112,7 @@ pageLoadAssetSize: searchprofiler: 67080 security: 65433 securitySolution: 66738 + serverless: 16573 serverlessObservability: 16582 serverlessSecurity: 16556 sessionView: 77750 diff --git a/tsconfig.base.json b/tsconfig.base.json index 2cf366080f5cd..0a8433efefdbd 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1080,6 +1080,8 @@ "@kbn/server-http-tools/*": ["packages/kbn-server-http-tools/*"], "@kbn/server-route-repository": ["packages/kbn-server-route-repository"], "@kbn/server-route-repository/*": ["packages/kbn-server-route-repository/*"], + "@kbn/serverless": ["x-pack/plugins/serverless"], + "@kbn/serverless/*": ["x-pack/plugins/serverless/*"], "@kbn/serverless-observability": ["x-pack/plugins/serverless_observability"], "@kbn/serverless-observability/*": ["x-pack/plugins/serverless_observability/*"], "@kbn/serverless-security": ["x-pack/plugins/serverless_security"], diff --git a/x-pack/plugins/serverless/.i18nrc.json b/x-pack/plugins/serverless/.i18nrc.json new file mode 100644 index 0000000000000..4e5106eb2cfca --- /dev/null +++ b/x-pack/plugins/serverless/.i18nrc.json @@ -0,0 +1,7 @@ +{ + "prefix": "serverless", + "paths": { + "serverless": "." + }, + "translations": ["translations/ja-JP.json"] +} diff --git a/x-pack/plugins/serverless/README.md b/x-pack/plugins/serverless/README.md new file mode 100755 index 0000000000000..4d279ec348054 --- /dev/null +++ b/x-pack/plugins/serverless/README.md @@ -0,0 +1,19 @@ +# serverless + +A Kibana plugin + +--- + +## Development + +See the [kibana contributing guide](https://github.com/elastic/kibana/blob/main/CONTRIBUTING.md) for instructions setting up your development environment. + +## Scripts + +
+
yarn kbn bootstrap
+
Execute this to install node_modules and setup the dependencies in your plugin and in Kibana
+ +
yarn plugin-helpers build
+
Execute this to create a distributable version of this plugin that can be installed in Kibana
+
diff --git a/x-pack/plugins/serverless/common/index.ts b/x-pack/plugins/serverless/common/index.ts new file mode 100644 index 0000000000000..f28f6aa0c3623 --- /dev/null +++ b/x-pack/plugins/serverless/common/index.ts @@ -0,0 +1,9 @@ +/* + * 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. + */ + +export const PLUGIN_ID = 'serverless'; +export const PLUGIN_NAME = 'serverless'; diff --git a/x-pack/plugins/serverless/kibana.jsonc b/x-pack/plugins/serverless/kibana.jsonc new file mode 100644 index 0000000000000..a952c98a0aba6 --- /dev/null +++ b/x-pack/plugins/serverless/kibana.jsonc @@ -0,0 +1,19 @@ +{ + "type": "plugin", + "id": "@kbn/serverless", + "owner": "@elastic/appex-sharedux", + "description": "The core Serverless plugin, providing APIs to Serverless Project plugins.", + "plugin": { + "id": "serverless", + "server": true, + "browser": true, + "configPath": [ + "xpack", + "serverless", + "plugin", + ], + "requiredPlugins": [], + "optionalPlugins": [], + "requiredBundles": [] + } +} \ No newline at end of file diff --git a/x-pack/plugins/serverless/package.json b/x-pack/plugins/serverless/package.json new file mode 100644 index 0000000000000..ec457f89fbcaa --- /dev/null +++ b/x-pack/plugins/serverless/package.json @@ -0,0 +1,11 @@ +{ + "name": "@kbn/serverless", + "version": "1.0.0", + "license": "Elastic License 2.0", + "private": true, + "scripts": { + "build": "yarn plugin-helpers build", + "plugin-helpers": "node ../../scripts/plugin_helpers", + "kbn": "node ../../scripts/kbn" + } +} \ No newline at end of file diff --git a/x-pack/plugins/serverless/public/index.ts b/x-pack/plugins/serverless/public/index.ts new file mode 100644 index 0000000000000..4e5b416edd87c --- /dev/null +++ b/x-pack/plugins/serverless/public/index.ts @@ -0,0 +1,16 @@ +/* + * 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 { ServerlessPlugin } from './plugin'; + +// This exports static code and TypeScript types, +// as well as, Kibana Platform `plugin()` initializer. +export function plugin() { + return new ServerlessPlugin(); +} + +export type { ServerlessPluginSetup, ServerlessPluginStart } from './types'; diff --git a/x-pack/plugins/serverless/public/plugin.ts b/x-pack/plugins/serverless/public/plugin.ts new file mode 100644 index 0000000000000..0c9816bd026ae --- /dev/null +++ b/x-pack/plugins/serverless/public/plugin.ts @@ -0,0 +1,25 @@ +/* + * 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 { CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; +import { ServerlessPluginSetup, ServerlessPluginStart } from './types'; + +export class ServerlessPlugin implements Plugin { + public setup(_core: CoreSetup): ServerlessPluginSetup { + return {}; + } + + public start(core: CoreStart): ServerlessPluginStart { + core.chrome.setChromeStyle('solution'); + return { + setServerlessNavigation: (navigation: JSX.Element) => + core.chrome.setSolutionNavigation(navigation), + }; + } + + public stop() {} +} diff --git a/x-pack/plugins/serverless/public/types.ts b/x-pack/plugins/serverless/public/types.ts new file mode 100644 index 0000000000000..4af0b062ede0c --- /dev/null +++ b/x-pack/plugins/serverless/public/types.ts @@ -0,0 +1,13 @@ +/* + * 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. + */ + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ServerlessPluginSetup {} + +export interface ServerlessPluginStart { + setServerlessNavigation: (navigation: JSX.Element) => void; +} diff --git a/x-pack/plugins/serverless/server/config.ts b/x-pack/plugins/serverless/server/config.ts new file mode 100644 index 0000000000000..dce4deda86049 --- /dev/null +++ b/x-pack/plugins/serverless/server/config.ts @@ -0,0 +1,23 @@ +/* + * 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 { schema, TypeOf } from '@kbn/config-schema'; +import { PluginConfigDescriptor } from '@kbn/core/server'; + +export * from './types'; + +const configSchema = schema.object({ + enabled: schema.boolean({ defaultValue: false }), +}); + +type ConfigType = TypeOf; + +export const config: PluginConfigDescriptor = { + schema: configSchema, +}; + +export type ServerlessConfig = TypeOf; diff --git a/x-pack/plugins/serverless/server/index.ts b/x-pack/plugins/serverless/server/index.ts new file mode 100644 index 0000000000000..ae805970e038e --- /dev/null +++ b/x-pack/plugins/serverless/server/index.ts @@ -0,0 +1,19 @@ +/* + * 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 { PluginInitializerContext } from '@kbn/core/server'; +import { ServerlessPlugin } from './plugin'; +export { config } from './config'; + +// This exports static code and TypeScript types, +// as well as, Kibana Platform `plugin()` initializer. + +export function plugin(initializerContext: PluginInitializerContext) { + return new ServerlessPlugin(initializerContext); +} + +export type { ServerlessPluginSetup, ServerlessPluginStart } from './types'; diff --git a/x-pack/plugins/serverless/server/plugin.ts b/x-pack/plugins/serverless/server/plugin.ts new file mode 100644 index 0000000000000..54bfb5c14dc4b --- /dev/null +++ b/x-pack/plugins/serverless/server/plugin.ts @@ -0,0 +1,24 @@ +/* + * 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 { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '@kbn/core/server'; + +import { ServerlessPluginSetup, ServerlessPluginStart } from './types'; + +export class ServerlessPlugin implements Plugin { + constructor(_initializerContext: PluginInitializerContext) {} + + public setup(_core: CoreSetup) { + return {}; + } + + public start(_core: CoreStart) { + return {}; + } + + public stop() {} +} diff --git a/x-pack/plugins/serverless/server/types.ts b/x-pack/plugins/serverless/server/types.ts new file mode 100644 index 0000000000000..92a804b34a948 --- /dev/null +++ b/x-pack/plugins/serverless/server/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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ServerlessPluginSetup {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ServerlessPluginStart {} diff --git a/x-pack/plugins/serverless/tsconfig.json b/x-pack/plugins/serverless/tsconfig.json new file mode 100644 index 0000000000000..ffdc7af796587 --- /dev/null +++ b/x-pack/plugins/serverless/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types" + }, + "include": [ + "index.ts", + "common/**/*.ts", + "public/**/*.ts", + "public/**/*.tsx", + "server/**/*.ts", + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/core", + "@kbn/config-schema", + ] +} diff --git a/x-pack/plugins/serverless_security/tsconfig.json b/x-pack/plugins/serverless_security/tsconfig.json index 7937d55df3797..86d85593b598f 100644 --- a/x-pack/plugins/serverless_security/tsconfig.json +++ b/x-pack/plugins/serverless_security/tsconfig.json @@ -7,7 +7,6 @@ "index.ts", "common/**/*.ts", "public/**/*.ts", - "public/**/*.tsx", "server/**/*.ts", "../../../typings/**/*" ], @@ -16,8 +15,8 @@ ], "kbn_references": [ "@kbn/core", + "@kbn/config-schema", "@kbn/security-plugin", "@kbn/security-solution-plugin", - "@kbn/config-schema", ] } diff --git a/yarn.lock b/yarn.lock index 8eb67c171d178..dcf878c1ae186 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4893,6 +4893,10 @@ version "0.0.0" uid "" +"@kbn/serverless@link:x-pack/plugins/serverless": + version "0.0.0" + uid "" + "@kbn/session-notifications-plugin@link:test/plugin_functional/plugins/session_notifications": version "0.0.0" uid "" From 63bc063f03a1dce1421b11780e44317aa6805b2b Mon Sep 17 00:00:00 2001 From: Clint Andrew Hall Date: Sun, 26 Feb 2023 00:56:54 -0500 Subject: [PATCH 08/58] Add Serverless as dependency to existing plugins; wire up navigation replacement --- config/serverless.yml | 2 ++ .../plugins/serverless_observability/kibana.jsonc | 1 + .../public/{plugin.ts => plugin.tsx} | 12 +++++++++--- .../serverless_observability/public/types.ts | 15 +++++++++++---- .../serverless_observability/tsconfig.json | 1 + x-pack/plugins/serverless_security/kibana.jsonc | 1 + .../public/{plugin.ts => plugin.tsx} | 4 +++- .../plugins/serverless_security/public/types.ts | 3 +++ x-pack/plugins/serverless_security/tsconfig.json | 2 ++ 9 files changed, 33 insertions(+), 8 deletions(-) rename x-pack/plugins/serverless_observability/public/{plugin.ts => plugin.tsx} (66%) rename x-pack/plugins/serverless_security/public/{plugin.ts => plugin.tsx} (85%) diff --git a/config/serverless.yml b/config/serverless.yml index e69de29bb2d1d..a4dfeaea49267 100644 --- a/config/serverless.yml +++ b/config/serverless.yml @@ -0,0 +1,2 @@ + +xpack.serverless.plugin.enabled: true diff --git a/x-pack/plugins/serverless_observability/kibana.jsonc b/x-pack/plugins/serverless_observability/kibana.jsonc index 0f0e911e6807f..abce6cd9e8350 100644 --- a/x-pack/plugins/serverless_observability/kibana.jsonc +++ b/x-pack/plugins/serverless_observability/kibana.jsonc @@ -13,6 +13,7 @@ "observability" ], "requiredPlugins": [ + "serverless", "observability" ], "optionalPlugins": [], diff --git a/x-pack/plugins/serverless_observability/public/plugin.ts b/x-pack/plugins/serverless_observability/public/plugin.tsx similarity index 66% rename from x-pack/plugins/serverless_observability/public/plugin.ts rename to x-pack/plugins/serverless_observability/public/plugin.tsx index dd1c5bfbc39c7..e6438ee8feff6 100644 --- a/x-pack/plugins/serverless_observability/public/plugin.ts +++ b/x-pack/plugins/serverless_observability/public/plugin.tsx @@ -5,11 +5,13 @@ * 2.0. */ +import React from 'react'; import { CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; import { ServerlessObservabilityPluginSetup, ServerlessObservabilityPluginStart, - AppPluginSetupDependencies, + ServerlessObservabilityPluginSetupDependencies, + ServerlessObservabilityPluginStartDependencies, } from './types'; export class ServerlessObservabilityPlugin @@ -17,7 +19,7 @@ export class ServerlessObservabilityPlugin { public setup( _core: CoreSetup, - setupDeps: AppPluginSetupDependencies + setupDeps: ServerlessObservabilityPluginSetupDependencies ): ServerlessObservabilityPluginSetup { setupDeps.observability.navigation.setIsSidebarEnabled(false); @@ -25,7 +27,11 @@ export class ServerlessObservabilityPlugin return {}; } - public start(_core: CoreStart): ServerlessObservabilityPluginStart { + public start( + _core: CoreStart, + { serverless }: ServerlessObservabilityPluginStartDependencies + ): ServerlessObservabilityPluginStart { + serverless.setServerlessNavigation(

Observability

); return {}; } diff --git a/x-pack/plugins/serverless_observability/public/types.ts b/x-pack/plugins/serverless_observability/public/types.ts index 73ab40e9257f7..e0fca61302ad7 100644 --- a/x-pack/plugins/serverless_observability/public/types.ts +++ b/x-pack/plugins/serverless_observability/public/types.ts @@ -5,7 +5,11 @@ * 2.0. */ -import { ObservabilityPublicSetup } from '@kbn/observability-plugin/public'; +import { ServerlessPluginSetup, ServerlessPluginStart } from '@kbn/serverless/public'; +import { + ObservabilityPublicSetup, + ObservabilityPublicStart, +} from '@kbn/observability-plugin/public'; // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface ServerlessObservabilityPluginSetup {} @@ -13,9 +17,12 @@ export interface ServerlessObservabilityPluginSetup {} // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface ServerlessObservabilityPluginStart {} -export interface AppPluginSetupDependencies { +export interface ServerlessObservabilityPluginSetupDependencies { observability: ObservabilityPublicSetup; + serverless: ServerlessPluginSetup; } -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface AppPluginStartDependencies {} +export interface ServerlessObservabilityPluginStartDependencies { + observability: ObservabilityPublicStart; + serverless: ServerlessPluginStart; +} diff --git a/x-pack/plugins/serverless_observability/tsconfig.json b/x-pack/plugins/serverless_observability/tsconfig.json index 4c9f7b818b430..8c9ec19e4736a 100644 --- a/x-pack/plugins/serverless_observability/tsconfig.json +++ b/x-pack/plugins/serverless_observability/tsconfig.json @@ -18,5 +18,6 @@ "@kbn/core", "@kbn/config-schema", "@kbn/observability-plugin", + "@kbn/serverless", ] } diff --git a/x-pack/plugins/serverless_security/kibana.jsonc b/x-pack/plugins/serverless_security/kibana.jsonc index c94c93513209f..71405b0e47707 100644 --- a/x-pack/plugins/serverless_security/kibana.jsonc +++ b/x-pack/plugins/serverless_security/kibana.jsonc @@ -13,6 +13,7 @@ "security" ], "requiredPlugins": [ + "serverless", "security", "securitySolution" ], diff --git a/x-pack/plugins/serverless_security/public/plugin.ts b/x-pack/plugins/serverless_security/public/plugin.tsx similarity index 85% rename from x-pack/plugins/serverless_security/public/plugin.ts rename to x-pack/plugins/serverless_security/public/plugin.tsx index 4af166c8cf804..f0f1fd2e0e9f0 100644 --- a/x-pack/plugins/serverless_security/public/plugin.ts +++ b/x-pack/plugins/serverless_security/public/plugin.tsx @@ -5,6 +5,7 @@ * 2.0. */ +import React from 'react'; import { CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; import { ServerlessSecurityPluginSetup, @@ -32,8 +33,9 @@ export class ServerlessSecurityPlugin public start( _core: CoreStart, - _startDeps: ServerlessSecurityPluginStartDependencies + startDeps: ServerlessSecurityPluginStartDependencies ): ServerlessSecurityPluginStart { + startDeps.serverless.setServerlessNavigation(

Security

); return {}; } diff --git a/x-pack/plugins/serverless_security/public/types.ts b/x-pack/plugins/serverless_security/public/types.ts index 58ac0b112abcf..1fc18893ce1fd 100644 --- a/x-pack/plugins/serverless_security/public/types.ts +++ b/x-pack/plugins/serverless_security/public/types.ts @@ -10,6 +10,7 @@ import { PluginSetup as SecuritySolutionPluginSetup, PluginStart as SecuritySolutionPluginStart, } from '@kbn/security-solution-plugin/public'; +import { ServerlessPluginSetup, ServerlessPluginStart } from '@kbn/serverless/public'; // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface ServerlessSecurityPluginSetup {} @@ -20,9 +21,11 @@ export interface ServerlessSecurityPluginStart {} export interface ServerlessSecurityPluginSetupDependencies { security: SecurityPluginSetup; securitySolution: SecuritySolutionPluginSetup; + serverless: ServerlessPluginSetup; } export interface ServerlessSecurityPluginStartDependencies { security: SecurityPluginStart; securitySolution: SecuritySolutionPluginStart; + serverless: ServerlessPluginStart; } diff --git a/x-pack/plugins/serverless_security/tsconfig.json b/x-pack/plugins/serverless_security/tsconfig.json index 86d85593b598f..ddf77b288e628 100644 --- a/x-pack/plugins/serverless_security/tsconfig.json +++ b/x-pack/plugins/serverless_security/tsconfig.json @@ -7,6 +7,7 @@ "index.ts", "common/**/*.ts", "public/**/*.ts", + "public/**/*.tsx", "server/**/*.ts", "../../../typings/**/*" ], @@ -18,5 +19,6 @@ "@kbn/config-schema", "@kbn/security-plugin", "@kbn/security-solution-plugin", + "@kbn/serverless", ] } From ec92b8a211b48e952712e7ddd5ef3f6b2cdef8bc Mon Sep 17 00:00:00 2001 From: Clint Andrew Hall Date: Sun, 26 Feb 2023 00:58:04 -0500 Subject: [PATCH 09/58] Add hack for SCSS var for demo purposes --- src/core/public/_variables.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/public/_variables.scss b/src/core/public/_variables.scss index 6c20ee1a4f4fd..048ffaa449fd5 100644 --- a/src/core/public/_variables.scss +++ b/src/core/public/_variables.scss @@ -3,6 +3,6 @@ // height of the header banner $kbnHeaderBannerHeight: $euiSizeXL; // This value is also declared in `/x-pack/plugins/canvas/common/lib/constants.ts` // total height of the header (when the banner is *not* present) -$kbnHeaderOffset: $euiHeaderHeightCompensation * 2; // <- TODO: this is a problem for Projects, as the black bar is removed. +$kbnHeaderOffset: $euiHeaderHeightCompensation; // <- TODO: this change is a hack for demo purposes. // total height of the header when the banner is present -$kbnHeaderOffsetWithBanner: $kbnHeaderOffset + $kbnHeaderBannerHeight; +$kbnHeaderOffsetWithBanner: $kbnHeaderOffset + $kbnHeaderBannerHeight; \ No newline at end of file From 3bc07467b73a57790dfb75e3ed4ce72554985e8b Mon Sep 17 00:00:00 2001 From: Clint Andrew Hall Date: Thu, 23 Feb 2023 22:25:48 -0500 Subject: [PATCH 10/58] Add 'enabled' config option to multiple plugins for projects. --- config/serverless.oblt.yml | 8 ++++++++ x-pack/plugins/apm/server/index.ts | 1 + x-pack/plugins/canvas/server/index.ts | 8 ++++++++ x-pack/plugins/enterprise_search/server/index.ts | 1 + x-pack/plugins/fleet/common/authz.ts | 2 +- x-pack/plugins/fleet/server/config.ts | 1 + x-pack/plugins/observability/server/index.ts | 1 + x-pack/plugins/security/server/config.test.ts | 3 +++ x-pack/plugins/security/server/config.ts | 1 + x-pack/plugins/security_solution/server/config.mock.ts | 1 + x-pack/plugins/security_solution/server/config.ts | 1 + x-pack/plugins/synthetics/common/config.ts | 1 + x-pack/plugins/synthetics/kibana.jsonc | 6 +++--- .../legacy_uptime/lib/adapters/framework/adapter_types.ts | 3 ++- .../synthetics_service/get_service_locations.test.ts | 3 +++ .../private_location/synthetics_private_location.ts | 2 +- .../server/synthetics_service/synthetics_service.test.ts | 3 ++- x-pack/plugins/watcher/server/index.ts | 8 ++++++++ 18 files changed, 47 insertions(+), 7 deletions(-) diff --git a/config/serverless.oblt.yml b/config/serverless.oblt.yml index e69de29bb2d1d..58d786b798fc9 100644 --- a/config/serverless.oblt.yml +++ b/config/serverless.oblt.yml @@ -0,0 +1,8 @@ +enterpriseSearch.enabled: false +xpack.canvas.enabled: false +xpack.cloudSecurityPosture.enabled: false +xpack.reporting.enabled: false +xpack.securitySolution.enabled: false +xpack.watcher.enabled: false + +uiSettings.overrides.defaultRoute: /app/observability/overview diff --git a/x-pack/plugins/apm/server/index.ts b/x-pack/plugins/apm/server/index.ts index 74eea568788b5..dba2d6e29ae7c 100644 --- a/x-pack/plugins/apm/server/index.ts +++ b/x-pack/plugins/apm/server/index.ts @@ -53,6 +53,7 @@ const configSchema = schema.object({ onboarding: schema.string({ defaultValue: 'apm-*' }), }), forceSyntheticSource: schema.boolean({ defaultValue: false }), + enabled: schema.boolean({ defaultValue: true }), }); // plugin config diff --git a/x-pack/plugins/canvas/server/index.ts b/x-pack/plugins/canvas/server/index.ts index d6d375b7259ac..d25ad10dd8e34 100644 --- a/x-pack/plugins/canvas/server/index.ts +++ b/x-pack/plugins/canvas/server/index.ts @@ -6,7 +6,15 @@ */ import { PluginInitializerContext } from '@kbn/core/server'; +import { schema } from '@kbn/config-schema'; + import { CanvasPlugin } from './plugin'; export const plugin = (initializerContext: PluginInitializerContext) => new CanvasPlugin(initializerContext); + +export const config = { + schema: schema.object({ + enabled: schema.boolean({ defaultValue: true }), + }), +}; diff --git a/x-pack/plugins/enterprise_search/server/index.ts b/x-pack/plugins/enterprise_search/server/index.ts index b8f65c23ab674..e1a1b33fa67ab 100644 --- a/x-pack/plugins/enterprise_search/server/index.ts +++ b/x-pack/plugins/enterprise_search/server/index.ts @@ -18,6 +18,7 @@ export const configSchema = schema.object({ accessCheckTimeout: schema.number({ defaultValue: 5000 }), accessCheckTimeoutWarning: schema.number({ defaultValue: 300 }), customHeaders: schema.maybe(schema.object({}, { unknowns: 'allow' })), + enabled: schema.boolean({ defaultValue: true }), host: schema.maybe(schema.string()), ssl: schema.object({ certificateAuthorities: schema.maybe( diff --git a/x-pack/plugins/fleet/common/authz.ts b/x-pack/plugins/fleet/common/authz.ts index fa30f2b8f7f33..a275a55a48206 100644 --- a/x-pack/plugins/fleet/common/authz.ts +++ b/x-pack/plugins/fleet/common/authz.ts @@ -99,7 +99,7 @@ export function calculatePackagePrivilegesFromCapabilities( return { ...acc, [privilege]: { - executePackageAction: capabilities.siem[privilegeName] || false, + executePackageAction: (capabilities.siem && capabilities.siem[privilegeName]) || false, }, }; }, diff --git a/x-pack/plugins/fleet/server/config.ts b/x-pack/plugins/fleet/server/config.ts index d5312bf9bc65c..41eb7e03e29c7 100644 --- a/x-pack/plugins/fleet/server/config.ts +++ b/x-pack/plugins/fleet/server/config.ts @@ -158,6 +158,7 @@ export const config: PluginConfigDescriptor = { } }, }), + enabled: schema.boolean({ defaultValue: true }), }), }; diff --git a/x-pack/plugins/observability/server/index.ts b/x-pack/plugins/observability/server/index.ts index ec930b813fb36..2ae55b5fa0b5f 100644 --- a/x-pack/plugins/observability/server/index.ts +++ b/x-pack/plugins/observability/server/index.ts @@ -47,6 +47,7 @@ const configSchema = schema.object({ }), }), }), + enabled: schema.boolean({ defaultValue: true }), }); export const config: PluginConfigDescriptor = { diff --git a/x-pack/plugins/security/server/config.test.ts b/x-pack/plugins/security/server/config.test.ts index 8b7324e70d646..49c1e4b951e92 100644 --- a/x-pack/plugins/security/server/config.test.ts +++ b/x-pack/plugins/security/server/config.test.ts @@ -60,6 +60,7 @@ describe('config schema', () => { "selector": Object {}, }, "cookieName": "sid", + "enabled": true, "encryptionKey": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "loginAssistanceMessage": "", "public": Object {}, @@ -113,6 +114,7 @@ describe('config schema', () => { "selector": Object {}, }, "cookieName": "sid", + "enabled": true, "encryptionKey": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "loginAssistanceMessage": "", "public": Object {}, @@ -166,6 +168,7 @@ describe('config schema', () => { "selector": Object {}, }, "cookieName": "sid", + "enabled": true, "loginAssistanceMessage": "", "public": Object {}, "secureCookies": false, diff --git a/x-pack/plugins/security/server/config.ts b/x-pack/plugins/security/server/config.ts index e3584427964f3..5620b35c1fef7 100644 --- a/x-pack/plugins/security/server/config.ts +++ b/x-pack/plugins/security/server/config.ts @@ -295,6 +295,7 @@ export const ConfigSchema = schema.object({ ) ), }), + enabled: schema.boolean({ defaultValue: true }), }); export function createConfig( diff --git a/x-pack/plugins/security_solution/server/config.mock.ts b/x-pack/plugins/security_solution/server/config.mock.ts index c1faa6f401a1d..6fcaa94629643 100644 --- a/x-pack/plugins/security_solution/server/config.mock.ts +++ b/x-pack/plugins/security_solution/server/config.mock.ts @@ -31,6 +31,7 @@ export const createMockConfig = (): ConfigType => { alertIgnoreFields: [], experimentalFeatures: parseExperimentalConfigValue(enableExperimental), + enabled: true, }; }; diff --git a/x-pack/plugins/security_solution/server/config.ts b/x-pack/plugins/security_solution/server/config.ts index 26f1be4f014b3..a0283858590cb 100644 --- a/x-pack/plugins/security_solution/server/config.ts +++ b/x-pack/plugins/security_solution/server/config.ts @@ -122,6 +122,7 @@ export const configSchema = schema.object({ * the package is not already installed. */ prebuiltRulesPackageVersion: schema.maybe(schema.string()), + enabled: schema.boolean({ defaultValue: true }), }); export type ConfigSchema = TypeOf; diff --git a/x-pack/plugins/synthetics/common/config.ts b/x-pack/plugins/synthetics/common/config.ts index c9c48e5878391..9da43f8bf9a08 100644 --- a/x-pack/plugins/synthetics/common/config.ts +++ b/x-pack/plugins/synthetics/common/config.ts @@ -23,6 +23,7 @@ const serviceConfig = schema.object({ const uptimeConfig = schema.object({ index: schema.maybe(schema.string()), service: schema.maybe(serviceConfig), + enabled: schema.boolean({ defaultValue: true }), }); export const config: PluginConfigDescriptor = { diff --git a/x-pack/plugins/synthetics/kibana.jsonc b/x-pack/plugins/synthetics/kibana.jsonc index 3236a730f1a59..5e6f1a6f30233 100644 --- a/x-pack/plugins/synthetics/kibana.jsonc +++ b/x-pack/plugins/synthetics/kibana.jsonc @@ -15,6 +15,8 @@ "actions", "alerting", "cases", + "data", + "fleet", "embeddable", "discover", "dataViews", @@ -35,8 +37,6 @@ ], "optionalPlugins": [ "cloud", - "data", - "fleet", "home", "ml", "telemetry" @@ -52,4 +52,4 @@ "indexLifecycleManagement" ] } -} +} \ No newline at end of file diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/lib/adapters/framework/adapter_types.ts b/x-pack/plugins/synthetics/server/legacy_uptime/lib/adapters/framework/adapter_types.ts index ec77b83977a09..2237fe29df0d5 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/lib/adapters/framework/adapter_types.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/lib/adapters/framework/adapter_types.ts @@ -28,7 +28,7 @@ import { MlPluginSetup as MlSetup } from '@kbn/ml-plugin/server'; import { RuleRegistryPluginSetupContract } from '@kbn/rule-registry-plugin/server'; import { SecurityPluginStart } from '@kbn/security-plugin/server'; import { CloudSetup } from '@kbn/cloud-plugin/server'; -import { SpacesPluginStart } from '@kbn/spaces-plugin/server'; +import { SpacesPluginSetup, SpacesPluginStart } from '@kbn/spaces-plugin/server'; import { FleetStartContract } from '@kbn/fleet-plugin/server'; import { BfetchServerSetup } from '@kbn/bfetch-plugin/server'; import { UptimeEsClient } from '../../lib'; @@ -76,6 +76,7 @@ export interface UptimeCorePluginsSetup { usageCollection: UsageCollectionSetup; ml: MlSetup; cloud?: CloudSetup; + spaces: SpacesPluginSetup; ruleRegistry: RuleRegistryPluginSetupContract; encryptedSavedObjects: EncryptedSavedObjectsPluginSetup; taskManager: TaskManagerSetupContract; diff --git a/x-pack/plugins/synthetics/server/synthetics_service/get_service_locations.test.ts b/x-pack/plugins/synthetics/server/synthetics_service/get_service_locations.test.ts index 1fed640bcb4e8..58faf6ba14877 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/get_service_locations.test.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/get_service_locations.test.ts @@ -50,6 +50,7 @@ describe('getServiceLocations', function () { manifestUrl: 'http://local.dev', showExperimentalLocations: false, }, + enabled: true, }, // @ts-ignore logger: { @@ -101,6 +102,7 @@ describe('getServiceLocations', function () { manifestUrl: 'http://local.dev', showExperimentalLocations: false, }, + enabled: true, }, // @ts-ignore logger: { @@ -138,6 +140,7 @@ describe('getServiceLocations', function () { manifestUrl: 'http://local.dev', showExperimentalLocations: true, }, + enabled: true, }, // @ts-ignore logger: { diff --git a/x-pack/plugins/synthetics/server/synthetics_service/private_location/synthetics_private_location.ts b/x-pack/plugins/synthetics/server/synthetics_service/private_location/synthetics_private_location.ts index 02a1828c1f56c..41410f3c0aa3a 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/private_location/synthetics_private_location.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/private_location/synthetics_private_location.ts @@ -356,7 +356,7 @@ export class SyntheticsPrivateLocation { } ); - return agentPolicies.items; + return agentPolicies.items || []; } } diff --git a/x-pack/plugins/synthetics/server/synthetics_service/synthetics_service.test.ts b/x-pack/plugins/synthetics/server/synthetics_service/synthetics_service.test.ts index 7144284e6b4f0..abb56011a14b9 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/synthetics_service.test.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/synthetics_service.test.ts @@ -42,7 +42,7 @@ describe('SyntheticsService', () => { } as unknown as UptimeServerSetup; const getMockedService = (locationsNum: number = 1) => { - serverMock.config = { service: { devUrl: 'http://localhost' } }; + serverMock.config = { service: { devUrl: 'http://localhost' }, enabled: true }; const service = new SyntheticsService(serverMock); const locations = times(locationsNum).map((n) => { @@ -116,6 +116,7 @@ describe('SyntheticsService', () => { username: 'dev', password: '12345', }, + enabled: true, }; const service = new SyntheticsService(serverMock); diff --git a/x-pack/plugins/watcher/server/index.ts b/x-pack/plugins/watcher/server/index.ts index 0aba44ed82838..36453f571f162 100644 --- a/x-pack/plugins/watcher/server/index.ts +++ b/x-pack/plugins/watcher/server/index.ts @@ -6,6 +6,14 @@ */ import { PluginInitializerContext } from '@kbn/core/server'; +import { schema } from '@kbn/config-schema'; + import { WatcherServerPlugin } from './plugin'; export const plugin = (ctx: PluginInitializerContext) => new WatcherServerPlugin(ctx); + +export const config = { + schema: schema.object({ + enabled: schema.boolean({ defaultValue: true }), + }), +}; From 9ea16a7168256da017db7fc53fe47572407f63c6 Mon Sep 17 00:00:00 2001 From: Clint Andrew Hall Date: Thu, 23 Feb 2023 22:30:03 -0500 Subject: [PATCH 11/58] Add ability to hide Observability navigation --- .../page_template/lazy_page_template.tsx | 28 +++++++++++++------ .../page_template/page_template.test.tsx | 1 + .../shared/page_template/page_template.tsx | 6 +++- x-pack/plugins/observability/public/plugin.ts | 2 ++ .../public/services/navigation_registry.ts | 10 ++++++- 5 files changed, 37 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/observability/public/components/shared/page_template/lazy_page_template.tsx b/x-pack/plugins/observability/public/components/shared/page_template/lazy_page_template.tsx index 7c61cae4f2c73..7bbc0dbc9ea70 100644 --- a/x-pack/plugins/observability/public/components/shared/page_template/lazy_page_template.tsx +++ b/x-pack/plugins/observability/public/components/shared/page_template/lazy_page_template.tsx @@ -6,6 +6,7 @@ */ import React from 'react'; +import useObservable from 'react-use/lib/useObservable'; import type { ObservabilityPageTemplateDependencies, WrappedPageTemplateProps, @@ -15,12 +16,23 @@ export const LazyObservabilityPageTemplate = React.lazy(() => import('./page_tem export type LazyObservabilityPageTemplateProps = WrappedPageTemplateProps; -export function createLazyObservabilityPageTemplate( - injectedDeps: ObservabilityPageTemplateDependencies -) { - return (pageTemplateProps: LazyObservabilityPageTemplateProps) => ( - - - - ); +export function createLazyObservabilityPageTemplate({ + isSidebarEnabled$, + ...injectedDeps +}: ObservabilityPageTemplateDependencies) { + return (pageTemplateProps: LazyObservabilityPageTemplateProps) => { + const isSidebarEnabled = useObservable(isSidebarEnabled$, true); + const { showSolutionNav: showSolutionNavProp, ...props } = pageTemplateProps; + let showSolutionNav = showSolutionNavProp; + + if (!isSidebarEnabled) { + showSolutionNav = false; + } + + return ( + + + + ); + }; } diff --git a/x-pack/plugins/observability/public/components/shared/page_template/page_template.test.tsx b/x-pack/plugins/observability/public/components/shared/page_template/page_template.test.tsx index 2a5324ddb5397..ae221fce17319 100644 --- a/x-pack/plugins/observability/public/components/shared/page_template/page_template.test.tsx +++ b/x-pack/plugins/observability/public/components/shared/page_template/page_template.test.tsx @@ -56,6 +56,7 @@ describe('Page template', () => { navigationSections$: navigationRegistry.sections$, getPageTemplateServices, guidedOnboardingApi: guidedOnboardingMock.createStart().guidedOnboardingApi, + isSidebarEnabled$: of(true), }); const component = shallow( diff --git a/x-pack/plugins/observability/public/components/shared/page_template/page_template.tsx b/x-pack/plugins/observability/public/components/shared/page_template/page_template.tsx index cafe693b8832b..c4dae7c1dc745 100644 --- a/x-pack/plugins/observability/public/components/shared/page_template/page_template.tsx +++ b/x-pack/plugins/observability/public/components/shared/page_template/page_template.tsx @@ -53,9 +53,13 @@ export interface ObservabilityPageTemplateDependencies { navigationSections$: Observable; getPageTemplateServices: () => KibanaPageTemplateKibanaDependencies; guidedOnboardingApi: GuidedOnboardingPluginStart['guidedOnboardingApi']; + isSidebarEnabled$: Observable; } -export type ObservabilityPageTemplateProps = ObservabilityPageTemplateDependencies & +export type ObservabilityPageTemplateProps = Omit< + ObservabilityPageTemplateDependencies, + 'isSidebarEnabled$' +> & WrappedPageTemplateProps; export function ObservabilityPageTemplate({ diff --git a/x-pack/plugins/observability/public/plugin.ts b/x-pack/plugins/observability/public/plugin.ts index e43725d0c5681..69c1a56084c20 100644 --- a/x-pack/plugins/observability/public/plugin.ts +++ b/x-pack/plugins/observability/public/plugin.ts @@ -316,6 +316,7 @@ export class Plugin observabilityRuleTypeRegistry: this.observabilityRuleTypeRegistry, navigation: { registerSections: this.navigationRegistry.registerSections, + setIsSidebarEnabled: this.navigationRegistry.setIsSidebarEnabled, }, useRulesLink: createUseRulesLink(), }; @@ -341,6 +342,7 @@ export class Plugin navigationSections$: this.navigationRegistry.sections$, guidedOnboardingApi: pluginsStart.guidedOnboarding.guidedOnboardingApi, getPageTemplateServices: () => ({ coreStart }), + isSidebarEnabled$: this.navigationRegistry.isSidebarEnabled$, }); const getAsyncO11yAlertsTableConfiguration = async () => { diff --git a/x-pack/plugins/observability/public/services/navigation_registry.ts b/x-pack/plugins/observability/public/services/navigation_registry.ts index 5f10a6f6c6851..e18929799b5fb 100644 --- a/x-pack/plugins/observability/public/services/navigation_registry.ts +++ b/x-pack/plugins/observability/public/services/navigation_registry.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { combineLatest, Observable, ReplaySubject } from 'rxjs'; +import { combineLatest, Observable, ReplaySubject, BehaviorSubject } from 'rxjs'; import { map, scan, shareReplay, switchMap } from 'rxjs/operators'; export interface NavigationSection { @@ -45,6 +45,8 @@ export interface NavigationEntry { export interface NavigationRegistry { registerSections: (sections$: Observable) => void; sections$: Observable; + isSidebarEnabled$: Observable; + setIsSidebarEnabled: (enabled: boolean) => void; } export const createNavigationRegistry = (): NavigationRegistry => { @@ -54,6 +56,8 @@ export const createNavigationRegistry = (): NavigationRegistry => { registeredSections$.next(sections$); }; + const isSidebarEnabled$ = new BehaviorSubject(true); + const sections$: Observable = registeredSections$.pipe( scan( (accumulatedSections$, newSections) => accumulatedSections$.add(newSections), @@ -69,5 +73,9 @@ export const createNavigationRegistry = (): NavigationRegistry => { return { registerSections, sections$, + isSidebarEnabled$, + setIsSidebarEnabled: (enabled) => { + isSidebarEnabled$.next(enabled); + }, }; }; From a5b1fa6e9f858582e8e0b4a4e088fad0e2c37692 Mon Sep 17 00:00:00 2001 From: Clint Andrew Hall Date: Thu, 23 Feb 2023 23:01:20 -0500 Subject: [PATCH 12/58] Create Serverless o11y plugin; hide internal nav via API --- .github/CODEOWNERS | 1 + config/serverless.oblt.yml | 2 ++ docs/developer/plugin-list.asciidoc | 4 +++ package.json | 1 + packages/kbn-optimizer/limits.yml | 1 + tsconfig.base.json | 2 ++ .../serverless_observability/.gitignore | 2 ++ .../serverless_observability/.i18nrc.json | 7 ++++ .../serverless_observability/README.md | 3 ++ .../serverless_observability/common/index.ts | 9 +++++ .../serverless_observability/kibana.jsonc | 21 ++++++++++++ .../serverless_observability/package.json | 11 +++++++ .../serverless_observability/public/index.ts | 19 +++++++++++ .../serverless_observability/public/plugin.ts | 33 +++++++++++++++++++ .../serverless_observability/public/types.ts | 21 ++++++++++++ .../serverless_observability/server/config.ts | 23 +++++++++++++ .../serverless_observability/server/index.ts | 23 +++++++++++++ .../serverless_observability/server/plugin.ts | 26 +++++++++++++++ .../serverless_observability/server/types.ts | 11 +++++++ .../serverless_observability/tsconfig.json | 22 +++++++++++++ yarn.lock | 4 +++ 21 files changed, 246 insertions(+) create mode 100644 x-pack/plugins/serverless_observability/.gitignore create mode 100644 x-pack/plugins/serverless_observability/.i18nrc.json create mode 100755 x-pack/plugins/serverless_observability/README.md create mode 100644 x-pack/plugins/serverless_observability/common/index.ts create mode 100644 x-pack/plugins/serverless_observability/kibana.jsonc create mode 100644 x-pack/plugins/serverless_observability/package.json create mode 100644 x-pack/plugins/serverless_observability/public/index.ts create mode 100644 x-pack/plugins/serverless_observability/public/plugin.ts create mode 100644 x-pack/plugins/serverless_observability/public/types.ts create mode 100644 x-pack/plugins/serverless_observability/server/config.ts create mode 100644 x-pack/plugins/serverless_observability/server/index.ts create mode 100644 x-pack/plugins/serverless_observability/server/plugin.ts create mode 100644 x-pack/plugins/serverless_observability/server/types.ts create mode 100644 x-pack/plugins/serverless_observability/tsconfig.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 27a7f01ec78e9..23a5df45f9951 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -547,6 +547,7 @@ packages/kbn-securitysolution-t-grid @elastic/security-solution-platform packages/kbn-securitysolution-utils @elastic/security-solution-platform packages/kbn-server-http-tools @elastic/kibana-core packages/kbn-server-route-repository @elastic/apm-ui +x-pack/plugins/serverless_observability @elastic/appex-sharedux test/plugin_functional/plugins/session_notifications @elastic/kibana-core x-pack/plugins/session_view @elastic/awp-viz packages/kbn-set-map @elastic/kibana-operations diff --git a/config/serverless.oblt.yml b/config/serverless.oblt.yml index 58d786b798fc9..b35e67c81aee7 100644 --- a/config/serverless.oblt.yml +++ b/config/serverless.oblt.yml @@ -5,4 +5,6 @@ xpack.reporting.enabled: false xpack.securitySolution.enabled: false xpack.watcher.enabled: false +xpack.serverless.observability.enabled: true + uiSettings.overrides.defaultRoute: /app/observability/overview diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index 3cdca29fcb49d..fa1c4080fe42b 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -689,6 +689,10 @@ Kibana. |Welcome to the Kibana Security Solution plugin! This README will go over getting started with development and testing. +|{kib-repo}blob/{branch}/x-pack/plugins/serverless_observability/README.md[serverlessObservability] +|A witty, fitting description to come. + + |{kib-repo}blob/{branch}/x-pack/plugins/session_view/README.md[sessionView] |Session View is meant to provide a visualization into what is going on in a particular Linux environment where the agent is running. It looks likes a terminal emulator; however, it is a tool for introspecting process activity and understanding user and service behaviour in your Linux servers and infrastructure. It is a time-ordered series of process executions displayed in a tree over time. diff --git a/package.json b/package.json index a363b2c162e56..5e34275fd563e 100644 --- a/package.json +++ b/package.json @@ -549,6 +549,7 @@ "@kbn/securitysolution-utils": "link:packages/kbn-securitysolution-utils", "@kbn/server-http-tools": "link:packages/kbn-server-http-tools", "@kbn/server-route-repository": "link:packages/kbn-server-route-repository", + "@kbn/serverless-observability": "link:x-pack/plugins/serverless_observability", "@kbn/session-notifications-plugin": "link:test/plugin_functional/plugins/session_notifications", "@kbn/session-view-plugin": "link:x-pack/plugins/session_view", "@kbn/set-map": "link:packages/kbn-set-map", diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index ad2fd0490197e..a343faa89a9a4 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -112,6 +112,7 @@ pageLoadAssetSize: searchprofiler: 67080 security: 65433 securitySolution: 66738 + serverlessObservability: 16582 sessionView: 77750 share: 71239 snapshotRestore: 79032 diff --git a/tsconfig.base.json b/tsconfig.base.json index 1e8bd1ef0ade0..4e252683b2a94 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1088,6 +1088,8 @@ "@kbn/server-http-tools/*": ["packages/kbn-server-http-tools/*"], "@kbn/server-route-repository": ["packages/kbn-server-route-repository"], "@kbn/server-route-repository/*": ["packages/kbn-server-route-repository/*"], + "@kbn/serverless-observability": ["x-pack/plugins/serverless_observability"], + "@kbn/serverless-observability/*": ["x-pack/plugins/serverless_observability/*"], "@kbn/session-notifications-plugin": ["test/plugin_functional/plugins/session_notifications"], "@kbn/session-notifications-plugin/*": ["test/plugin_functional/plugins/session_notifications/*"], "@kbn/session-view-plugin": ["x-pack/plugins/session_view"], diff --git a/x-pack/plugins/serverless_observability/.gitignore b/x-pack/plugins/serverless_observability/.gitignore new file mode 100644 index 0000000000000..c3dca1b96fcc2 --- /dev/null +++ b/x-pack/plugins/serverless_observability/.gitignore @@ -0,0 +1,2 @@ +/build +/target diff --git a/x-pack/plugins/serverless_observability/.i18nrc.json b/x-pack/plugins/serverless_observability/.i18nrc.json new file mode 100644 index 0000000000000..623c26e73e4da --- /dev/null +++ b/x-pack/plugins/serverless_observability/.i18nrc.json @@ -0,0 +1,7 @@ +{ + "prefix": "serverlessObservability", + "paths": { + "serverlessObservability": "." + }, + "translations": ["translations/ja-JP.json"] +} diff --git a/x-pack/plugins/serverless_observability/README.md b/x-pack/plugins/serverless_observability/README.md new file mode 100755 index 0000000000000..25d55bd95bb40 --- /dev/null +++ b/x-pack/plugins/serverless_observability/README.md @@ -0,0 +1,3 @@ +# serverlessObservability + +A witty, fitting description to come. \ No newline at end of file diff --git a/x-pack/plugins/serverless_observability/common/index.ts b/x-pack/plugins/serverless_observability/common/index.ts new file mode 100644 index 0000000000000..d6a5ea767034c --- /dev/null +++ b/x-pack/plugins/serverless_observability/common/index.ts @@ -0,0 +1,9 @@ +/* + * 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. + */ + +export const PLUGIN_ID = 'serverlessObservability'; +export const PLUGIN_NAME = 'serverlessObservability'; diff --git a/x-pack/plugins/serverless_observability/kibana.jsonc b/x-pack/plugins/serverless_observability/kibana.jsonc new file mode 100644 index 0000000000000..0f0e911e6807f --- /dev/null +++ b/x-pack/plugins/serverless_observability/kibana.jsonc @@ -0,0 +1,21 @@ +{ + "type": "plugin", + "id": "@kbn/serverless-observability", + "owner": "@elastic/appex-sharedux", + "description": "Serverless customizations for observability.", + "plugin": { + "id": "serverlessObservability", + "server": true, + "browser": true, + "configPath": [ + "xpack", + "serverless", + "observability" + ], + "requiredPlugins": [ + "observability" + ], + "optionalPlugins": [], + "requiredBundles": [] + } +} diff --git a/x-pack/plugins/serverless_observability/package.json b/x-pack/plugins/serverless_observability/package.json new file mode 100644 index 0000000000000..64b310d7eabae --- /dev/null +++ b/x-pack/plugins/serverless_observability/package.json @@ -0,0 +1,11 @@ +{ + "name": "@kbn/serverless-observability", + "version": "1.0.0", + "license": "Elastic License 2.0", + "private": true, + "scripts": { + "build": "yarn plugin-helpers build", + "plugin-helpers": "node ../../../scripts/plugin_helpers", + "kbn": "node ../../../scripts/kbn" + } +} \ No newline at end of file diff --git a/x-pack/plugins/serverless_observability/public/index.ts b/x-pack/plugins/serverless_observability/public/index.ts new file mode 100644 index 0000000000000..a785b68735375 --- /dev/null +++ b/x-pack/plugins/serverless_observability/public/index.ts @@ -0,0 +1,19 @@ +/* + * 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 { ServerlessObservabilityPlugin } from './plugin'; + +// This exports static code and TypeScript types, +// as well as, Kibana Platform `plugin()` initializer. +export function plugin() { + return new ServerlessObservabilityPlugin(); +} + +export type { + ServerlessObservabilityPluginSetup, + ServerlessObservabilityPluginStart, +} from './types'; diff --git a/x-pack/plugins/serverless_observability/public/plugin.ts b/x-pack/plugins/serverless_observability/public/plugin.ts new file mode 100644 index 0000000000000..dd1c5bfbc39c7 --- /dev/null +++ b/x-pack/plugins/serverless_observability/public/plugin.ts @@ -0,0 +1,33 @@ +/* + * 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 { CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; +import { + ServerlessObservabilityPluginSetup, + ServerlessObservabilityPluginStart, + AppPluginSetupDependencies, +} from './types'; + +export class ServerlessObservabilityPlugin + implements Plugin +{ + public setup( + _core: CoreSetup, + setupDeps: AppPluginSetupDependencies + ): ServerlessObservabilityPluginSetup { + setupDeps.observability.navigation.setIsSidebarEnabled(false); + + // Return methods that should be available to other plugins + return {}; + } + + public start(_core: CoreStart): ServerlessObservabilityPluginStart { + return {}; + } + + public stop() {} +} diff --git a/x-pack/plugins/serverless_observability/public/types.ts b/x-pack/plugins/serverless_observability/public/types.ts new file mode 100644 index 0000000000000..73ab40e9257f7 --- /dev/null +++ b/x-pack/plugins/serverless_observability/public/types.ts @@ -0,0 +1,21 @@ +/* + * 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 { ObservabilityPublicSetup } from '@kbn/observability-plugin/public'; + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ServerlessObservabilityPluginSetup {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ServerlessObservabilityPluginStart {} + +export interface AppPluginSetupDependencies { + observability: ObservabilityPublicSetup; +} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface AppPluginStartDependencies {} diff --git a/x-pack/plugins/serverless_observability/server/config.ts b/x-pack/plugins/serverless_observability/server/config.ts new file mode 100644 index 0000000000000..599a9f2bd7769 --- /dev/null +++ b/x-pack/plugins/serverless_observability/server/config.ts @@ -0,0 +1,23 @@ +/* + * 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 { schema, TypeOf } from '@kbn/config-schema'; +import { PluginConfigDescriptor } from '@kbn/core/server'; + +export * from './types'; + +const configSchema = schema.object({ + enabled: schema.boolean({ defaultValue: false }), +}); + +type ConfigType = TypeOf; + +export const config: PluginConfigDescriptor = { + schema: configSchema, +}; + +export type ServerlessObservabilityConfig = TypeOf; diff --git a/x-pack/plugins/serverless_observability/server/index.ts b/x-pack/plugins/serverless_observability/server/index.ts new file mode 100644 index 0000000000000..c45e363a429bf --- /dev/null +++ b/x-pack/plugins/serverless_observability/server/index.ts @@ -0,0 +1,23 @@ +/* + * 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 { PluginInitializerContext } from '@kbn/core/server'; + +import { ServerlessObservabilityPlugin } from './plugin'; +export { config } from './config'; + +// This exports static code and TypeScript types, +// as well as, Kibana Platform `plugin()` initializer. + +export function plugin(initializerContext: PluginInitializerContext) { + return new ServerlessObservabilityPlugin(initializerContext); +} + +export type { + ServerlessObservabilityPluginSetup, + ServerlessObservabilityPluginStart, +} from './types'; diff --git a/x-pack/plugins/serverless_observability/server/plugin.ts b/x-pack/plugins/serverless_observability/server/plugin.ts new file mode 100644 index 0000000000000..8b28ba2b0a4ac --- /dev/null +++ b/x-pack/plugins/serverless_observability/server/plugin.ts @@ -0,0 +1,26 @@ +/* + * 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 { PluginInitializerContext, Plugin } from '@kbn/core/server'; + +import { ServerlessObservabilityPluginSetup, ServerlessObservabilityPluginStart } from './types'; + +export class ServerlessObservabilityPlugin + implements Plugin +{ + constructor(_initializerContext: PluginInitializerContext) {} + + public setup() { + return {}; + } + + public start() { + return {}; + } + + public stop() {} +} diff --git a/x-pack/plugins/serverless_observability/server/types.ts b/x-pack/plugins/serverless_observability/server/types.ts new file mode 100644 index 0000000000000..f8a587103e886 --- /dev/null +++ b/x-pack/plugins/serverless_observability/server/types.ts @@ -0,0 +1,11 @@ +/* + * 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. + */ + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ServerlessObservabilityPluginSetup {} +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ServerlessObservabilityPluginStart {} diff --git a/x-pack/plugins/serverless_observability/tsconfig.json b/x-pack/plugins/serverless_observability/tsconfig.json new file mode 100644 index 0000000000000..4c9f7b818b430 --- /dev/null +++ b/x-pack/plugins/serverless_observability/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types" + }, + "include": [ + "index.ts", + "common/**/*.ts", + "public/**/*.ts", + "public/**/*.tsx", + "server/**/*.ts", + "../../../typings/**/*" + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/core", + "@kbn/config-schema", + "@kbn/observability-plugin", + ] +} diff --git a/yarn.lock b/yarn.lock index 6ffc80f4e34d5..1c0e10bfe2fd3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4913,6 +4913,10 @@ version "0.0.0" uid "" +"@kbn/serverless-observability@link:x-pack/plugins/serverless_observability": + version "0.0.0" + uid "" + "@kbn/session-notifications-plugin@link:test/plugin_functional/plugins/session_notifications": version "0.0.0" uid "" From a828a34e0cc498a8d5841dc7d068e8b8c45681ef Mon Sep 17 00:00:00 2001 From: Clint Andrew Hall Date: Sat, 25 Feb 2023 23:16:04 -0500 Subject: [PATCH 13/58] Add ability to hide Security navigation --- .../use_primary_navigation.tsx | 10 ++++++++++ .../public/common/lib/kibana/kibana_react.mock.ts | 2 ++ x-pack/plugins/security_solution/public/index.ts | 4 ++-- x-pack/plugins/security_solution/public/plugin.tsx | 12 ++++++++++-- x-pack/plugins/security_solution/public/types.ts | 9 +++++++-- 5 files changed, 31 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/use_primary_navigation.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/use_primary_navigation.tsx index 647193357b66b..867cda2bcf4e8 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/use_primary_navigation.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/use_primary_navigation.tsx @@ -9,10 +9,12 @@ import React, { useEffect, useState, useCallback } from 'react'; import { i18n } from '@kbn/i18n'; import type { KibanaPageTemplateProps } from '@kbn/shared-ux-page-kibana-template'; +import useObservable from 'react-use/lib/useObservable'; import type { PrimaryNavigationProps } from './types'; import { usePrimaryNavigationItems } from './use_navigation_items'; import { useIsGroupedNavigationEnabled } from '../helpers'; import { SecuritySideNav } from '../security_side_nav'; +import { useKibana } from '../../../lib/kibana'; const translatedNavTitle = i18n.translate('xpack.securitySolution.navigation.mainLabel', { defaultMessage: 'Security', @@ -45,6 +47,14 @@ export const usePrimaryNavigation = ({ selectedTabId, }); + const { isSidebarEnabled$ } = useKibana().services; + + const isSidebarEnabled = useObservable(isSidebarEnabled$); + + if (!isSidebarEnabled) { + return undefined; + } + return { canBeCollapsed: true, name: translatedNavTitle, diff --git a/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts b/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts index efa9ce4831be7..01b67365b6926 100644 --- a/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts +++ b/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts @@ -45,6 +45,7 @@ import { triggersActionsUiMock } from '@kbn/triggers-actions-ui-plugin/public/mo import { mockApm } from '../apm/service.mock'; import { cloudExperimentsMock } from '@kbn/cloud-experiments-plugin/common/mocks'; import { guidedOnboardingMock } from '@kbn/guided-onboarding-plugin/public/mocks'; +import { of } from 'rxjs'; const mockUiSettings: Record = { [DEFAULT_TIME_RANGE]: { from: 'now-15m', to: 'now', mode: 'quick' }, @@ -176,6 +177,7 @@ export const createStartServicesMock = ( triggersActionsUi, cloudExperiments, guidedOnboarding, + isSidebarEnabled$: of(true), } as unknown as StartServices; }; diff --git a/x-pack/plugins/security_solution/public/index.ts b/x-pack/plugins/security_solution/public/index.ts index 1f6f121e04209..7ac596d087fca 100644 --- a/x-pack/plugins/security_solution/public/index.ts +++ b/x-pack/plugins/security_solution/public/index.ts @@ -7,10 +7,10 @@ import type { PluginInitializerContext } from '@kbn/core/public'; import { Plugin } from './plugin'; -import type { PluginSetup } from './types'; +import type { PluginSetup, PluginStart } from './types'; export type { TimelineModel } from './timelines/store/timeline/model'; export const plugin = (context: PluginInitializerContext): Plugin => new Plugin(context); -export type { PluginSetup }; +export type { PluginSetup, PluginStart }; export { Plugin }; diff --git a/x-pack/plugins/security_solution/public/plugin.tsx b/x-pack/plugins/security_solution/public/plugin.tsx index abc9c41101e25..616168b52cbba 100644 --- a/x-pack/plugins/security_solution/public/plugin.tsx +++ b/x-pack/plugins/security_solution/public/plugin.tsx @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; import type { Subscription } from 'rxjs'; -import { Subject } from 'rxjs'; +import { BehaviorSubject, Subject } from 'rxjs'; import { combineLatestWith } from 'rxjs/operators'; import type * as H from 'history'; import type { @@ -84,6 +84,7 @@ export class Plugin implements IPlugin; constructor(private readonly initializerContext: PluginInitializerContext) { this.config = this.initializerContext.config.get(); @@ -91,6 +92,7 @@ export class Plugin implements IPlugin(true); } private appUpdater$ = new Subject(); @@ -159,6 +161,7 @@ export class Plugin implements IPlugin SecuritySolutionTemplateWrapper, }, + isSidebarEnabled$: this.isSidebarEnabled$, }; return services; }; @@ -296,7 +299,12 @@ export class Plugin implements IPlugin { + this.isSidebarEnabled$.next(enabled); + }, + }; } public stop() { diff --git a/x-pack/plugins/security_solution/public/types.ts b/x-pack/plugins/security_solution/public/types.ts index 65b43d3d4635b..76349ec3c8db5 100644 --- a/x-pack/plugins/security_solution/public/types.ts +++ b/x-pack/plugins/security_solution/public/types.ts @@ -43,6 +43,7 @@ import type { ThreatIntelligencePluginStart } from '@kbn/threat-intelligence-plu import type { CloudExperimentsPluginStart } from '@kbn/cloud-experiments-plugin/common'; import type { GuidedOnboardingPluginStart } from '@kbn/guided-onboarding-plugin/public'; import type { DataViewsServicePublic } from '@kbn/data-views-plugin/public'; +import type { BehaviorSubject } from 'rxjs'; import type { ResolverPluginSetup } from './resolver/types'; import type { Inspect } from '../common/search_strategy'; import type { Detections } from './detections'; @@ -116,13 +117,17 @@ export type StartServices = CoreStart & securityLayout: { getPluginWrapper: () => typeof SecuritySolutionTemplateWrapper; }; + isSidebarEnabled$: BehaviorSubject; }; export interface PluginSetup { resolver: () => Promise; } -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface PluginStart {} + +export interface PluginStart { + isSidebarEnabled$: BehaviorSubject; + setIsSidebarEnabled: (enabled: boolean) => void; +} export interface AppObservableLibs { kibana: CoreStart; From 64b62883a35eda40c09a97062c2ade6c35710ca3 Mon Sep 17 00:00:00 2001 From: Clint Andrew Hall Date: Sat, 25 Feb 2023 23:16:11 -0500 Subject: [PATCH 14/58] Create Serverless security plugin; hide internal nav via API --- .github/CODEOWNERS | 1 + config/serverless.security.yml | 10 +++++ docs/developer/plugin-list.asciidoc | 4 ++ package.json | 1 + packages/kbn-optimizer/limits.yml | 1 + tsconfig.base.json | 2 + .../security_solution/public/plugin.tsx | 10 ++--- .../plugins/security_solution/public/types.ts | 7 ++-- x-pack/plugins/serverless_security/.gitignore | 2 + .../plugins/serverless_security/.i18nrc.json | 9 ++++ x-pack/plugins/serverless_security/README.md | 3 ++ .../serverless_security/common/index.ts | 9 ++++ .../plugins/serverless_security/kibana.jsonc | 22 ++++++++++ .../plugins/serverless_security/package.json | 11 +++++ .../serverless_security/public/index.ts | 16 ++++++++ .../serverless_security/public/plugin.ts | 41 +++++++++++++++++++ .../serverless_security/public/types.ts | 28 +++++++++++++ .../serverless_security/server/config.ts | 23 +++++++++++ .../serverless_security/server/index.ts | 20 +++++++++ .../serverless_security/server/plugin.ts | 37 +++++++++++++++++ .../serverless_security/server/types.ts | 27 ++++++++++++ .../plugins/serverless_security/tsconfig.json | 23 +++++++++++ yarn.lock | 4 ++ 23 files changed, 301 insertions(+), 10 deletions(-) create mode 100644 x-pack/plugins/serverless_security/.gitignore create mode 100644 x-pack/plugins/serverless_security/.i18nrc.json create mode 100755 x-pack/plugins/serverless_security/README.md create mode 100644 x-pack/plugins/serverless_security/common/index.ts create mode 100644 x-pack/plugins/serverless_security/kibana.jsonc create mode 100644 x-pack/plugins/serverless_security/package.json create mode 100644 x-pack/plugins/serverless_security/public/index.ts create mode 100644 x-pack/plugins/serverless_security/public/plugin.ts create mode 100644 x-pack/plugins/serverless_security/public/types.ts create mode 100644 x-pack/plugins/serverless_security/server/config.ts create mode 100644 x-pack/plugins/serverless_security/server/index.ts create mode 100644 x-pack/plugins/serverless_security/server/plugin.ts create mode 100644 x-pack/plugins/serverless_security/server/types.ts create mode 100644 x-pack/plugins/serverless_security/tsconfig.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 23a5df45f9951..e84a4aaf463ce 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -548,6 +548,7 @@ packages/kbn-securitysolution-utils @elastic/security-solution-platform packages/kbn-server-http-tools @elastic/kibana-core packages/kbn-server-route-repository @elastic/apm-ui x-pack/plugins/serverless_observability @elastic/appex-sharedux +x-pack/plugins/serverless_security @elastic/appex-sharedux test/plugin_functional/plugins/session_notifications @elastic/kibana-core x-pack/plugins/session_view @elastic/awp-viz packages/kbn-set-map @elastic/kibana-operations diff --git a/config/serverless.security.yml b/config/serverless.security.yml index e69de29bb2d1d..a5e1c170ddccd 100644 --- a/config/serverless.security.yml +++ b/config/serverless.security.yml @@ -0,0 +1,10 @@ +xpack.apm.enabled: false +xpack.canvas.enabled: false +xpack.observability.enabled: false +xpack.reporting.enabled: false +xpack.uptime.enabled: false +xpack.watcher.enabled: false + +xpack.serverless.security.enabled: true + +uiSettings.overrides.defaultRoute: /app/security/get_started diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index fa1c4080fe42b..0502017da81a4 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -693,6 +693,10 @@ Kibana. |A witty, fitting description to come. +|{kib-repo}blob/{branch}/x-pack/plugins/serverless_security/README.md[serverlessSecurity] +|A witty, fitting description to come. + + |{kib-repo}blob/{branch}/x-pack/plugins/session_view/README.md[sessionView] |Session View is meant to provide a visualization into what is going on in a particular Linux environment where the agent is running. It looks likes a terminal emulator; however, it is a tool for introspecting process activity and understanding user and service behaviour in your Linux servers and infrastructure. It is a time-ordered series of process executions displayed in a tree over time. diff --git a/package.json b/package.json index 5e34275fd563e..44ce771c57a1a 100644 --- a/package.json +++ b/package.json @@ -550,6 +550,7 @@ "@kbn/server-http-tools": "link:packages/kbn-server-http-tools", "@kbn/server-route-repository": "link:packages/kbn-server-route-repository", "@kbn/serverless-observability": "link:x-pack/plugins/serverless_observability", + "@kbn/serverless-security": "link:x-pack/plugins/serverless_security", "@kbn/session-notifications-plugin": "link:test/plugin_functional/plugins/session_notifications", "@kbn/session-view-plugin": "link:x-pack/plugins/session_view", "@kbn/set-map": "link:packages/kbn-set-map", diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index a343faa89a9a4..886ca3817fe0f 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -113,6 +113,7 @@ pageLoadAssetSize: security: 65433 securitySolution: 66738 serverlessObservability: 16582 + serverlessSecurity: 16556 sessionView: 77750 share: 71239 snapshotRestore: 79032 diff --git a/tsconfig.base.json b/tsconfig.base.json index 4e252683b2a94..db51be8727dbf 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1090,6 +1090,8 @@ "@kbn/server-route-repository/*": ["packages/kbn-server-route-repository/*"], "@kbn/serverless-observability": ["x-pack/plugins/serverless_observability"], "@kbn/serverless-observability/*": ["x-pack/plugins/serverless_observability/*"], + "@kbn/serverless-security": ["x-pack/plugins/serverless_security"], + "@kbn/serverless-security/*": ["x-pack/plugins/serverless_security/*"], "@kbn/session-notifications-plugin": ["test/plugin_functional/plugins/session_notifications"], "@kbn/session-notifications-plugin/*": ["test/plugin_functional/plugins/session_notifications/*"], "@kbn/session-view-plugin": ["x-pack/plugins/session_view"], diff --git a/x-pack/plugins/security_solution/public/plugin.tsx b/x-pack/plugins/security_solution/public/plugin.tsx index 616168b52cbba..87bd7e72223a4 100644 --- a/x-pack/plugins/security_solution/public/plugin.tsx +++ b/x-pack/plugins/security_solution/public/plugin.tsx @@ -235,6 +235,9 @@ export class Plugin implements IPlugin { + this.isSidebarEnabled$.next(enabled); + }, }; } @@ -299,12 +302,7 @@ export class Plugin implements IPlugin { - this.isSidebarEnabled$.next(enabled); - }, - }; + return {}; } public stop() { diff --git a/x-pack/plugins/security_solution/public/types.ts b/x-pack/plugins/security_solution/public/types.ts index 76349ec3c8db5..4cdbce5c2cf47 100644 --- a/x-pack/plugins/security_solution/public/types.ts +++ b/x-pack/plugins/security_solution/public/types.ts @@ -122,13 +122,12 @@ export type StartServices = CoreStart & export interface PluginSetup { resolver: () => Promise; -} - -export interface PluginStart { - isSidebarEnabled$: BehaviorSubject; setIsSidebarEnabled: (enabled: boolean) => void; } +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface PluginStart {} + export interface AppObservableLibs { kibana: CoreStart; } diff --git a/x-pack/plugins/serverless_security/.gitignore b/x-pack/plugins/serverless_security/.gitignore new file mode 100644 index 0000000000000..c3dca1b96fcc2 --- /dev/null +++ b/x-pack/plugins/serverless_security/.gitignore @@ -0,0 +1,2 @@ +/build +/target diff --git a/x-pack/plugins/serverless_security/.i18nrc.json b/x-pack/plugins/serverless_security/.i18nrc.json new file mode 100644 index 0000000000000..e4519b9cdf9f5 --- /dev/null +++ b/x-pack/plugins/serverless_security/.i18nrc.json @@ -0,0 +1,9 @@ +{ + "prefix": "serverlessSecurity", + "paths": { + "serverlessSecurity": "." + }, + "translations": [ + "translations/ja-JP.json" + ] +} \ No newline at end of file diff --git a/x-pack/plugins/serverless_security/README.md b/x-pack/plugins/serverless_security/README.md new file mode 100755 index 0000000000000..33d229a31060d --- /dev/null +++ b/x-pack/plugins/serverless_security/README.md @@ -0,0 +1,3 @@ +# serverlessSecurity + +A witty, fitting description to come. \ No newline at end of file diff --git a/x-pack/plugins/serverless_security/common/index.ts b/x-pack/plugins/serverless_security/common/index.ts new file mode 100644 index 0000000000000..0dc5be6ddf9bb --- /dev/null +++ b/x-pack/plugins/serverless_security/common/index.ts @@ -0,0 +1,9 @@ +/* + * 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. + */ + +export const PLUGIN_ID = 'serverlessSecurity'; +export const PLUGIN_NAME = 'serverlessSecurity'; diff --git a/x-pack/plugins/serverless_security/kibana.jsonc b/x-pack/plugins/serverless_security/kibana.jsonc new file mode 100644 index 0000000000000..c94c93513209f --- /dev/null +++ b/x-pack/plugins/serverless_security/kibana.jsonc @@ -0,0 +1,22 @@ +{ + "type": "plugin", + "id": "@kbn/serverless-security", + "owner": "@elastic/appex-sharedux", + "description": "Serverless customizations for security.", + "plugin": { + "id": "serverlessSecurity", + "server": true, + "browser": true, + "configPath": [ + "xpack", + "serverless", + "security" + ], + "requiredPlugins": [ + "security", + "securitySolution" + ], + "optionalPlugins": [], + "requiredBundles": [] + } +} \ No newline at end of file diff --git a/x-pack/plugins/serverless_security/package.json b/x-pack/plugins/serverless_security/package.json new file mode 100644 index 0000000000000..0154207c22d6f --- /dev/null +++ b/x-pack/plugins/serverless_security/package.json @@ -0,0 +1,11 @@ +{ + "name": "@kbn/serverless-security", + "version": "1.0.0", + "license": "Elastic License 2.0", + "private": true, + "scripts": { + "build": "yarn plugin-helpers build", + "plugin-helpers": "node ../../../scripts/plugin_helpers", + "kbn": "node ../../../scripts/kbn" + } +} \ No newline at end of file diff --git a/x-pack/plugins/serverless_security/public/index.ts b/x-pack/plugins/serverless_security/public/index.ts new file mode 100644 index 0000000000000..ce7b6e989f4ca --- /dev/null +++ b/x-pack/plugins/serverless_security/public/index.ts @@ -0,0 +1,16 @@ +/* + * 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 { ServerlessSecurityPlugin } from './plugin'; + +// This exports static code and TypeScript types, +// as well as, Kibana Platform `plugin()` initializer. +export function plugin() { + return new ServerlessSecurityPlugin(); +} + +export type { ServerlessSecurityPluginSetup, ServerlessSecurityPluginStart } from './types'; diff --git a/x-pack/plugins/serverless_security/public/plugin.ts b/x-pack/plugins/serverless_security/public/plugin.ts new file mode 100644 index 0000000000000..4af166c8cf804 --- /dev/null +++ b/x-pack/plugins/serverless_security/public/plugin.ts @@ -0,0 +1,41 @@ +/* + * 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 { CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; +import { + ServerlessSecurityPluginSetup, + ServerlessSecurityPluginStart, + ServerlessSecurityPluginSetupDependencies, + ServerlessSecurityPluginStartDependencies, +} from './types'; + +export class ServerlessSecurityPlugin + implements + Plugin< + ServerlessSecurityPluginSetup, + ServerlessSecurityPluginStart, + ServerlessSecurityPluginSetupDependencies, + ServerlessSecurityPluginStartDependencies + > +{ + public setup( + _core: CoreSetup, + setupDeps: ServerlessSecurityPluginSetupDependencies + ): ServerlessSecurityPluginSetup { + setupDeps.securitySolution.setIsSidebarEnabled(false); + return {}; + } + + public start( + _core: CoreStart, + _startDeps: ServerlessSecurityPluginStartDependencies + ): ServerlessSecurityPluginStart { + return {}; + } + + public stop() {} +} diff --git a/x-pack/plugins/serverless_security/public/types.ts b/x-pack/plugins/serverless_security/public/types.ts new file mode 100644 index 0000000000000..58ac0b112abcf --- /dev/null +++ b/x-pack/plugins/serverless_security/public/types.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SecurityPluginSetup, SecurityPluginStart } from '@kbn/security-plugin/public'; +import { + PluginSetup as SecuritySolutionPluginSetup, + PluginStart as SecuritySolutionPluginStart, +} from '@kbn/security-solution-plugin/public'; + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ServerlessSecurityPluginSetup {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ServerlessSecurityPluginStart {} + +export interface ServerlessSecurityPluginSetupDependencies { + security: SecurityPluginSetup; + securitySolution: SecuritySolutionPluginSetup; +} + +export interface ServerlessSecurityPluginStartDependencies { + security: SecurityPluginStart; + securitySolution: SecuritySolutionPluginStart; +} diff --git a/x-pack/plugins/serverless_security/server/config.ts b/x-pack/plugins/serverless_security/server/config.ts new file mode 100644 index 0000000000000..758b22de1514e --- /dev/null +++ b/x-pack/plugins/serverless_security/server/config.ts @@ -0,0 +1,23 @@ +/* + * 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 { schema, TypeOf } from '@kbn/config-schema'; +import { PluginConfigDescriptor } from '@kbn/core/server'; + +export * from './types'; + +const configSchema = schema.object({ + enabled: schema.boolean({ defaultValue: false }), +}); + +type ConfigType = TypeOf; + +export const config: PluginConfigDescriptor = { + schema: configSchema, +}; + +export type ServerlessSecurityConfig = TypeOf; diff --git a/x-pack/plugins/serverless_security/server/index.ts b/x-pack/plugins/serverless_security/server/index.ts new file mode 100644 index 0000000000000..b2b6c8564f788 --- /dev/null +++ b/x-pack/plugins/serverless_security/server/index.ts @@ -0,0 +1,20 @@ +/* + * 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 { PluginInitializerContext } from '@kbn/core/server'; + +import { ServerlessSecurityPlugin } from './plugin'; +export { config } from './config'; + +// This exports static code and TypeScript types, +// as well as, Kibana Platform `plugin()` initializer. + +export function plugin(initializerContext: PluginInitializerContext) { + return new ServerlessSecurityPlugin(initializerContext); +} + +export type { ServerlessSecurityPluginSetup, ServerlessSecurityPluginStart } from './types'; diff --git a/x-pack/plugins/serverless_security/server/plugin.ts b/x-pack/plugins/serverless_security/server/plugin.ts new file mode 100644 index 0000000000000..7dfbae3b64a67 --- /dev/null +++ b/x-pack/plugins/serverless_security/server/plugin.ts @@ -0,0 +1,37 @@ +/* + * 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 { PluginInitializerContext, Plugin } from '@kbn/core/server'; + +import { + ServerlessSecurityPluginSetup, + ServerlessSecurityPluginStart, + ServerlessSecurityPluginSetupDependencies, + ServerlessSecurityPluginStartDependencies, +} from './types'; + +export class ServerlessSecurityPlugin + implements + Plugin< + ServerlessSecurityPluginSetup, + ServerlessSecurityPluginStart, + ServerlessSecurityPluginSetupDependencies, + ServerlessSecurityPluginStartDependencies + > +{ + constructor(_initializerContext: PluginInitializerContext) {} + + public setup() { + return {}; + } + + public start() { + return {}; + } + + public stop() {} +} diff --git a/x-pack/plugins/serverless_security/server/types.ts b/x-pack/plugins/serverless_security/server/types.ts new file mode 100644 index 0000000000000..be7167d030315 --- /dev/null +++ b/x-pack/plugins/serverless_security/server/types.ts @@ -0,0 +1,27 @@ +/* + * 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 { SecurityPluginSetup, SecurityPluginStart } from '@kbn/security-plugin/server'; +import { + PluginSetup as SecuritySolutionPluginSetup, + PluginStart as SecuritySolutionPluginStart, +} from '@kbn/security-solution-plugin/server'; + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ServerlessSecurityPluginSetup {} +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ServerlessSecurityPluginStart {} + +export interface ServerlessSecurityPluginSetupDependencies { + security: SecurityPluginSetup; + securitySolution: SecuritySolutionPluginSetup; +} + +export interface ServerlessSecurityPluginStartDependencies { + security: SecurityPluginStart; + securitySolution: SecuritySolutionPluginStart; +} diff --git a/x-pack/plugins/serverless_security/tsconfig.json b/x-pack/plugins/serverless_security/tsconfig.json new file mode 100644 index 0000000000000..7937d55df3797 --- /dev/null +++ b/x-pack/plugins/serverless_security/tsconfig.json @@ -0,0 +1,23 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types" + }, + "include": [ + "index.ts", + "common/**/*.ts", + "public/**/*.ts", + "public/**/*.tsx", + "server/**/*.ts", + "../../../typings/**/*" + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/core", + "@kbn/security-plugin", + "@kbn/security-solution-plugin", + "@kbn/config-schema", + ] +} diff --git a/yarn.lock b/yarn.lock index 1c0e10bfe2fd3..181e56313e85a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4917,6 +4917,10 @@ version "0.0.0" uid "" +"@kbn/serverless-security@link:x-pack/plugins/serverless_security": + version "0.0.0" + uid "" + "@kbn/session-notifications-plugin@link:test/plugin_functional/plugins/session_notifications": version "0.0.0" uid "" From e345a1bf48b658bd95fa5730ba3a9af54e491a49 Mon Sep 17 00:00:00 2001 From: Clint Andrew Hall Date: Sun, 26 Feb 2023 00:17:54 -0500 Subject: [PATCH 15/58] Create 'solution' chrome style, add API to place solution navigation --- .../src/chrome_service.test.ts | 2 + .../src/chrome_service.tsx | 114 +++++++++++++----- .../src/ui/index.ts | 1 + .../src/ui/solution/header.tsx | 111 +++++++++++++++++ .../src/ui/solution/index.ts | 9 ++ .../tsconfig.json | 3 +- .../src/chrome_service.mock.ts | 3 + .../core/chrome/core-chrome-browser/index.ts | 27 +++-- .../core-chrome-browser/src/contracts.ts | 6 +- .../chrome/core-chrome-browser/src/index.ts | 2 +- .../chrome/core-chrome-browser/src/types.ts | 3 + src/core/public/_variables.scss | 2 +- src/core/public/styles/rendering/_base.scss | 3 + 13 files changed, 238 insertions(+), 48 deletions(-) create mode 100644 packages/core/chrome/core-chrome-browser-internal/src/ui/solution/header.tsx create mode 100644 packages/core/chrome/core-chrome-browser-internal/src/ui/solution/index.ts diff --git a/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.test.ts b/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.test.ts index 37b1b9a2eab7d..0087c5d019f98 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.test.ts +++ b/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.test.ts @@ -126,6 +126,7 @@ describe('start', () => { Array [ Array [ "kbnBody", + "kbnBody--classicLayout", "kbnBody--noHeaderBanner", "kbnBody--chromeHidden", "kbnVersion-1-2-3", @@ -143,6 +144,7 @@ describe('start', () => { Array [ Array [ "kbnBody", + "kbnBody--classicLayout", "kbnBody--noHeaderBanner", "kbnBody--chromeHidden", "kbnVersion-8-0-0", diff --git a/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx b/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx index 4e0762aee8620..07f27067f3883 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx @@ -26,6 +26,7 @@ import type { ChromeGlobalHelpExtensionMenuLink, ChromeHelpExtension, ChromeUserBanner, + ChromeStyle, } from '@kbn/core-chrome-browser'; import type { CustomBrandingStart } from '@kbn/core-custom-branding-browser'; import { KIBANA_ASK_ELASTIC_LINK } from './constants'; @@ -33,7 +34,7 @@ import { DocTitleService } from './doc_title'; import { NavControlsService } from './nav_controls'; import { NavLinksService } from './nav_links'; import { RecentlyAccessedService } from './recently_accessed'; -import { Header } from './ui'; +import { Header, SolutionHeader } from './ui'; import type { InternalChromeStart } from './types'; const IS_LOCKED_KEY = 'core.chrome.isLocked'; @@ -119,6 +120,8 @@ export class ChromeService { const customNavLink$ = new BehaviorSubject(undefined); const helpSupportUrl$ = new BehaviorSubject(KIBANA_ASK_ELASTIC_LINK); const isNavDrawerLocked$ = new BehaviorSubject(localStorage.getItem(IS_LOCKED_KEY) === 'true'); + const chromeStyle$ = new BehaviorSubject('classic'); + const solutionNavigation$ = new BehaviorSubject(undefined); const getKbnVersionClass = () => { // we assume that the version is valid and has the form 'X.X.X' @@ -135,6 +138,9 @@ export class ChromeService { map(([headerBanner, isVisible]) => { return [ 'kbnBody', + chromeStyle$.getValue() === 'classic' + ? 'kbnBody--classicLayout' + : 'kbnBody--projectLayout', headerBanner ? 'kbnBody--hasHeaderBanner' : 'kbnBody--noHeaderBanner', isVisible ? 'kbnBody--chromeVisible' : 'kbnBody--chromeHidden', getKbnVersionClass(), @@ -163,6 +169,16 @@ export class ChromeService { const getIsNavDrawerLocked$ = isNavDrawerLocked$.pipe(takeUntil(this.stop$)); + const setChromeStyle = (style: ChromeStyle) => { + chromeStyle$.next(style); + }; + + const setSolutionNavigation = (navigation: JSX.Element) => { + solutionNavigation$.next(navigation); + }; + + const getChromeStyle$ = chromeStyle$.pipe(takeUntil(this.stop$)); + const isIE = () => { const ua = window.navigator.userAgent; const msie = ua.indexOf('MSIE '); // IE 10 or older @@ -203,41 +219,74 @@ export class ChromeService { }); } + const getHeaderComponent = () => { + const Component = ({ + style$, + navigation$, + }: { + style$: typeof chromeStyle$; + navigation$: typeof solutionNavigation$; + }) => { + if (style$.getValue() === 'solution') { + const navigation = navigation$.getValue(); + if (navigation) { + return ( + + ); + } + } + + return ( +
+ ); + }; + return ; + }; + return { navControls, navLinks, recentlyAccessed, docTitle, - - getHeaderComponent: () => ( -
- ), + getHeaderComponent, getIsVisible$: () => this.isVisible$, @@ -302,6 +351,9 @@ export class ChromeService { }, getBodyClasses$: () => bodyClasses$.pipe(takeUntil(this.stop$)), + setChromeStyle, + getChromeStyle$: () => getChromeStyle$, + setSolutionNavigation, }; } diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/index.ts b/packages/core/chrome/core-chrome-browser-internal/src/ui/index.ts index 5afd3e0f587bb..0fd5240eac29d 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/ui/index.ts +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/index.ts @@ -7,5 +7,6 @@ */ export { Header } from './header'; +export { SolutionHeader } from './solution'; export { LoadingIndicator } from './loading_indicator'; export type { NavType } from './header'; diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/solution/header.tsx b/packages/core/chrome/core-chrome-browser-internal/src/ui/solution/header.tsx new file mode 100644 index 0000000000000..0aef9755994d8 --- /dev/null +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/solution/header.tsx @@ -0,0 +1,111 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { + EuiButtonIcon, + EuiCollapsibleNav, + EuiHeader, + EuiHeaderLogo, + EuiHeaderSection, + EuiHeaderSectionItem, + EuiThemeProvider, + useEuiTheme, +} from '@elastic/eui'; +import { + ChromeBreadcrumb, + ChromeGlobalHelpExtensionMenuLink, + ChromeHelpExtension, +} from '@kbn/core-chrome-browser/src'; +import { Observable } from 'rxjs'; +import { MountPoint } from '@kbn/core-mount-utils-browser'; +import { InternalApplicationStart } from '@kbn/core-application-browser-internal'; +import { HeaderBreadcrumbs } from '../header/header_breadcrumbs'; +import { HeaderActionMenu } from '../header/header_action_menu'; +import { HeaderHelpMenu } from '../header/header_help_menu'; + +interface Props { + breadcrumbs$: Observable; + actionMenu$: Observable; + kibanaDocLink: string; + globalHelpExtensionMenuLinks$: Observable; + helpExtension$: Observable; + helpSupportUrl$: Observable; + kibanaVersion: string; + application: InternalApplicationStart; + navigation: JSX.Element; +} + +export const SolutionHeader = ({ + application, + kibanaDocLink, + kibanaVersion, + navigation, + ...observables +}: Props) => { + const { euiTheme, colorMode } = useEuiTheme(); + + const renderLogo = () => ( + e.preventDefault()} + aria-label="Go to home page" + /> + ); + + return ( + <> + + + {renderLogo()} + + + + + + + + + + + + + + + {}} + closeButtonProps={{ iconType: 'menuLeft' }} + showButtonIfDocked={true} + isDocked={true} + size={248} + hideCloseButton={false} + button={ + + + + } + > + {navigation} + + + + ); +}; diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/solution/index.ts b/packages/core/chrome/core-chrome-browser-internal/src/ui/solution/index.ts new file mode 100644 index 0000000000000..5c116bc6b30c8 --- /dev/null +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/solution/index.ts @@ -0,0 +1,9 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +export { SolutionHeader } from './header'; diff --git a/packages/core/chrome/core-chrome-browser-internal/tsconfig.json b/packages/core/chrome/core-chrome-browser-internal/tsconfig.json index 4d4d6cad3bc21..019f15681b6c1 100644 --- a/packages/core/chrome/core-chrome-browser-internal/tsconfig.json +++ b/packages/core/chrome/core-chrome-browser-internal/tsconfig.json @@ -5,7 +5,8 @@ "types": [ "jest", "node", - "react" + "react", + "@emotion/react/types/css-prop" ] }, "include": [ diff --git a/packages/core/chrome/core-chrome-browser-mocks/src/chrome_service.mock.ts b/packages/core/chrome/core-chrome-browser-mocks/src/chrome_service.mock.ts index 2f5c4deb1f38d..e33eca66322f8 100644 --- a/packages/core/chrome/core-chrome-browser-mocks/src/chrome_service.mock.ts +++ b/packages/core/chrome/core-chrome-browser-mocks/src/chrome_service.mock.ts @@ -61,6 +61,9 @@ const createStartContractMock = () => { setHeaderBanner: jest.fn(), hasHeaderBanner$: jest.fn(), getBodyClasses$: jest.fn(), + getChromeStyle$: jest.fn(), + setChromeStyle: jest.fn(), + setSolutionNavigation: jest.fn(), }; startContract.navLinks.getAll.mockReturnValue([]); startContract.getIsVisible$.mockReturnValue(new BehaviorSubject(false)); diff --git a/packages/core/chrome/core-chrome-browser/index.ts b/packages/core/chrome/core-chrome-browser/index.ts index 3fbef34126a4a..1d2dca4c957bc 100644 --- a/packages/core/chrome/core-chrome-browser/index.ts +++ b/packages/core/chrome/core-chrome-browser/index.ts @@ -7,25 +7,26 @@ */ export type { - ChromeUserBanner, + ChromeBadge, ChromeBreadcrumb, + ChromeBreadcrumbsAppendExtension, + ChromeDocTitle, + ChromeGlobalHelpExtensionMenuLink, ChromeHelpExtension, - ChromeHelpExtensionMenuLink, ChromeHelpExtensionLinkBase, + ChromeHelpExtensionMenuCustomLink, + ChromeHelpExtensionMenuDiscussLink, + ChromeHelpExtensionMenuDocumentationLink, + ChromeHelpExtensionMenuGitHubLink, + ChromeHelpExtensionMenuLink, ChromeHelpMenuActions, - ChromeNavLink, - ChromeBreadcrumbsAppendExtension, - ChromeNavLinks, ChromeNavControl, ChromeNavControls, - ChromeBadge, - ChromeHelpExtensionMenuGitHubLink, - ChromeHelpExtensionMenuDocumentationLink, - ChromeHelpExtensionMenuDiscussLink, - ChromeHelpExtensionMenuCustomLink, - ChromeGlobalHelpExtensionMenuLink, - ChromeDocTitle, - ChromeStart, + ChromeNavLink, + ChromeNavLinks, ChromeRecentlyAccessed, ChromeRecentlyAccessedHistoryItem, + ChromeStart, + ChromeStyle, + ChromeUserBanner, } from './src'; diff --git a/packages/core/chrome/core-chrome-browser/src/contracts.ts b/packages/core/chrome/core-chrome-browser/src/contracts.ts index a81d9c3c6338f..2a966e367d6e4 100644 --- a/packages/core/chrome/core-chrome-browser/src/contracts.ts +++ b/packages/core/chrome/core-chrome-browser/src/contracts.ts @@ -13,7 +13,7 @@ import type { ChromeDocTitle } from './doc_title'; import type { ChromeNavControls } from './nav_controls'; import type { ChromeHelpExtension } from './help_extension'; import type { ChromeBreadcrumb, ChromeBreadcrumbsAppendExtension } from './breadcrumb'; -import type { ChromeBadge, ChromeUserBanner } from './types'; +import type { ChromeBadge, ChromeStyle, ChromeUserBanner } from './types'; import { ChromeGlobalHelpExtensionMenuLink } from './help_extension'; /** @@ -150,4 +150,8 @@ export interface ChromeStart { * Get an observable of the current header banner presence state. */ hasHeaderBanner$(): Observable; + + setChromeStyle(style: ChromeStyle): void; + getChromeStyle$(): Observable; + setSolutionNavigation(solutionNavigation: JSX.Element): void; } diff --git a/packages/core/chrome/core-chrome-browser/src/index.ts b/packages/core/chrome/core-chrome-browser/src/index.ts index 716af097fded7..89ba12d616d0e 100644 --- a/packages/core/chrome/core-chrome-browser/src/index.ts +++ b/packages/core/chrome/core-chrome-browser/src/index.ts @@ -26,4 +26,4 @@ export type { ChromeRecentlyAccessed, ChromeRecentlyAccessedHistoryItem, } from './recently_accessed'; -export type { ChromeBadge, ChromeUserBanner } from './types'; +export type { ChromeBadge, ChromeUserBanner, ChromeStyle } from './types'; diff --git a/packages/core/chrome/core-chrome-browser/src/types.ts b/packages/core/chrome/core-chrome-browser/src/types.ts index 81b8c32a1a04c..40f29a073137d 100644 --- a/packages/core/chrome/core-chrome-browser/src/types.ts +++ b/packages/core/chrome/core-chrome-browser/src/types.ts @@ -20,3 +20,6 @@ export interface ChromeBadge { export interface ChromeUserBanner { content: MountPoint; } + +/** @public */ +export type ChromeStyle = 'classic' | 'solution'; diff --git a/src/core/public/_variables.scss b/src/core/public/_variables.scss index 6c21c9760be97..4b89cc0bdf1ad 100644 --- a/src/core/public/_variables.scss +++ b/src/core/public/_variables.scss @@ -1,7 +1,7 @@ @import '@elastic/eui/src/global_styling/variables/header'; // height of the header banner -$kbnHeaderBannerHeight: $euiSizeXL; // This value is also declared in `/x-pack/plugins/canvas/common/lib/constants.ts` +$kbnHeaderBannerHeight: $euiSizeXL; // This value is also declared in `/x-pack/plugins/canvas/common/lib/constants.ts` // total height of the header (when the banner is *not* present) $kbnHeaderOffset: $euiHeaderHeightCompensation * 2; // total height of the header when the banner is present diff --git a/src/core/public/styles/rendering/_base.scss b/src/core/public/styles/rendering/_base.scss index 9d4296ca3b4ef..a9ece9955e6ca 100644 --- a/src/core/public/styles/rendering/_base.scss +++ b/src/core/public/styles/rendering/_base.scss @@ -75,6 +75,9 @@ &.kbnBody--chromeHidden { @include kbnAffordForHeader(0); } + &.kbnBody--projectLayout { + @include kbnAffordForHeader($euiHeaderHeightCompensation); + } &.kbnBody--chromeHidden.kbnBody--hasHeaderBanner { @include kbnAffordForHeader($kbnHeaderBannerHeight); } From 48783181defe860952c4814a6ab0c676d5ebbb74 Mon Sep 17 00:00:00 2001 From: Clint Andrew Hall Date: Sun, 26 Feb 2023 00:56:06 -0500 Subject: [PATCH 16/58] Create Serverless plugin; alias core navigation to local API --- .github/CODEOWNERS | 1 + docs/developer/plugin-list.asciidoc | 4 +++ package.json | 1 + packages/kbn-optimizer/limits.yml | 1 + tsconfig.base.json | 2 ++ x-pack/plugins/serverless/.i18nrc.json | 7 ++++++ x-pack/plugins/serverless/README.md | 19 ++++++++++++++ x-pack/plugins/serverless/common/index.ts | 9 +++++++ x-pack/plugins/serverless/kibana.jsonc | 19 ++++++++++++++ x-pack/plugins/serverless/package.json | 11 ++++++++ x-pack/plugins/serverless/public/index.ts | 16 ++++++++++++ x-pack/plugins/serverless/public/plugin.ts | 25 +++++++++++++++++++ x-pack/plugins/serverless/public/types.ts | 13 ++++++++++ x-pack/plugins/serverless/server/config.ts | 23 +++++++++++++++++ x-pack/plugins/serverless/server/index.ts | 19 ++++++++++++++ x-pack/plugins/serverless/server/plugin.ts | 24 ++++++++++++++++++ x-pack/plugins/serverless/server/types.ts | 12 +++++++++ x-pack/plugins/serverless/tsconfig.json | 20 +++++++++++++++ .../plugins/serverless_security/tsconfig.json | 3 +-- yarn.lock | 4 +++ 20 files changed, 231 insertions(+), 2 deletions(-) create mode 100644 x-pack/plugins/serverless/.i18nrc.json create mode 100755 x-pack/plugins/serverless/README.md create mode 100644 x-pack/plugins/serverless/common/index.ts create mode 100644 x-pack/plugins/serverless/kibana.jsonc create mode 100644 x-pack/plugins/serverless/package.json create mode 100644 x-pack/plugins/serverless/public/index.ts create mode 100644 x-pack/plugins/serverless/public/plugin.ts create mode 100644 x-pack/plugins/serverless/public/types.ts create mode 100644 x-pack/plugins/serverless/server/config.ts create mode 100644 x-pack/plugins/serverless/server/index.ts create mode 100644 x-pack/plugins/serverless/server/plugin.ts create mode 100644 x-pack/plugins/serverless/server/types.ts create mode 100644 x-pack/plugins/serverless/tsconfig.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index e84a4aaf463ce..c9f7999ce5327 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -547,6 +547,7 @@ packages/kbn-securitysolution-t-grid @elastic/security-solution-platform packages/kbn-securitysolution-utils @elastic/security-solution-platform packages/kbn-server-http-tools @elastic/kibana-core packages/kbn-server-route-repository @elastic/apm-ui +x-pack/plugins/serverless @elastic/appex-sharedux x-pack/plugins/serverless_observability @elastic/appex-sharedux x-pack/plugins/serverless_security @elastic/appex-sharedux test/plugin_functional/plugins/session_notifications @elastic/kibana-core diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index 0502017da81a4..146300eb04bef 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -689,6 +689,10 @@ Kibana. |Welcome to the Kibana Security Solution plugin! This README will go over getting started with development and testing. +|{kib-repo}blob/{branch}/x-pack/plugins/serverless/README.md[serverless] +|A Kibana plugin + + |{kib-repo}blob/{branch}/x-pack/plugins/serverless_observability/README.md[serverlessObservability] |A witty, fitting description to come. diff --git a/package.json b/package.json index 44ce771c57a1a..5462bb7056554 100644 --- a/package.json +++ b/package.json @@ -549,6 +549,7 @@ "@kbn/securitysolution-utils": "link:packages/kbn-securitysolution-utils", "@kbn/server-http-tools": "link:packages/kbn-server-http-tools", "@kbn/server-route-repository": "link:packages/kbn-server-route-repository", + "@kbn/serverless": "link:x-pack/plugins/serverless", "@kbn/serverless-observability": "link:x-pack/plugins/serverless_observability", "@kbn/serverless-security": "link:x-pack/plugins/serverless_security", "@kbn/session-notifications-plugin": "link:test/plugin_functional/plugins/session_notifications", diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 886ca3817fe0f..d979ab1ce679f 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -112,6 +112,7 @@ pageLoadAssetSize: searchprofiler: 67080 security: 65433 securitySolution: 66738 + serverless: 16573 serverlessObservability: 16582 serverlessSecurity: 16556 sessionView: 77750 diff --git a/tsconfig.base.json b/tsconfig.base.json index db51be8727dbf..21db47056353d 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1088,6 +1088,8 @@ "@kbn/server-http-tools/*": ["packages/kbn-server-http-tools/*"], "@kbn/server-route-repository": ["packages/kbn-server-route-repository"], "@kbn/server-route-repository/*": ["packages/kbn-server-route-repository/*"], + "@kbn/serverless": ["x-pack/plugins/serverless"], + "@kbn/serverless/*": ["x-pack/plugins/serverless/*"], "@kbn/serverless-observability": ["x-pack/plugins/serverless_observability"], "@kbn/serverless-observability/*": ["x-pack/plugins/serverless_observability/*"], "@kbn/serverless-security": ["x-pack/plugins/serverless_security"], diff --git a/x-pack/plugins/serverless/.i18nrc.json b/x-pack/plugins/serverless/.i18nrc.json new file mode 100644 index 0000000000000..4e5106eb2cfca --- /dev/null +++ b/x-pack/plugins/serverless/.i18nrc.json @@ -0,0 +1,7 @@ +{ + "prefix": "serverless", + "paths": { + "serverless": "." + }, + "translations": ["translations/ja-JP.json"] +} diff --git a/x-pack/plugins/serverless/README.md b/x-pack/plugins/serverless/README.md new file mode 100755 index 0000000000000..4d279ec348054 --- /dev/null +++ b/x-pack/plugins/serverless/README.md @@ -0,0 +1,19 @@ +# serverless + +A Kibana plugin + +--- + +## Development + +See the [kibana contributing guide](https://github.com/elastic/kibana/blob/main/CONTRIBUTING.md) for instructions setting up your development environment. + +## Scripts + +
+
yarn kbn bootstrap
+
Execute this to install node_modules and setup the dependencies in your plugin and in Kibana
+ +
yarn plugin-helpers build
+
Execute this to create a distributable version of this plugin that can be installed in Kibana
+
diff --git a/x-pack/plugins/serverless/common/index.ts b/x-pack/plugins/serverless/common/index.ts new file mode 100644 index 0000000000000..f28f6aa0c3623 --- /dev/null +++ b/x-pack/plugins/serverless/common/index.ts @@ -0,0 +1,9 @@ +/* + * 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. + */ + +export const PLUGIN_ID = 'serverless'; +export const PLUGIN_NAME = 'serverless'; diff --git a/x-pack/plugins/serverless/kibana.jsonc b/x-pack/plugins/serverless/kibana.jsonc new file mode 100644 index 0000000000000..a952c98a0aba6 --- /dev/null +++ b/x-pack/plugins/serverless/kibana.jsonc @@ -0,0 +1,19 @@ +{ + "type": "plugin", + "id": "@kbn/serverless", + "owner": "@elastic/appex-sharedux", + "description": "The core Serverless plugin, providing APIs to Serverless Project plugins.", + "plugin": { + "id": "serverless", + "server": true, + "browser": true, + "configPath": [ + "xpack", + "serverless", + "plugin", + ], + "requiredPlugins": [], + "optionalPlugins": [], + "requiredBundles": [] + } +} \ No newline at end of file diff --git a/x-pack/plugins/serverless/package.json b/x-pack/plugins/serverless/package.json new file mode 100644 index 0000000000000..ec457f89fbcaa --- /dev/null +++ b/x-pack/plugins/serverless/package.json @@ -0,0 +1,11 @@ +{ + "name": "@kbn/serverless", + "version": "1.0.0", + "license": "Elastic License 2.0", + "private": true, + "scripts": { + "build": "yarn plugin-helpers build", + "plugin-helpers": "node ../../scripts/plugin_helpers", + "kbn": "node ../../scripts/kbn" + } +} \ No newline at end of file diff --git a/x-pack/plugins/serverless/public/index.ts b/x-pack/plugins/serverless/public/index.ts new file mode 100644 index 0000000000000..4e5b416edd87c --- /dev/null +++ b/x-pack/plugins/serverless/public/index.ts @@ -0,0 +1,16 @@ +/* + * 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 { ServerlessPlugin } from './plugin'; + +// This exports static code and TypeScript types, +// as well as, Kibana Platform `plugin()` initializer. +export function plugin() { + return new ServerlessPlugin(); +} + +export type { ServerlessPluginSetup, ServerlessPluginStart } from './types'; diff --git a/x-pack/plugins/serverless/public/plugin.ts b/x-pack/plugins/serverless/public/plugin.ts new file mode 100644 index 0000000000000..0c9816bd026ae --- /dev/null +++ b/x-pack/plugins/serverless/public/plugin.ts @@ -0,0 +1,25 @@ +/* + * 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 { CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; +import { ServerlessPluginSetup, ServerlessPluginStart } from './types'; + +export class ServerlessPlugin implements Plugin { + public setup(_core: CoreSetup): ServerlessPluginSetup { + return {}; + } + + public start(core: CoreStart): ServerlessPluginStart { + core.chrome.setChromeStyle('solution'); + return { + setServerlessNavigation: (navigation: JSX.Element) => + core.chrome.setSolutionNavigation(navigation), + }; + } + + public stop() {} +} diff --git a/x-pack/plugins/serverless/public/types.ts b/x-pack/plugins/serverless/public/types.ts new file mode 100644 index 0000000000000..4af0b062ede0c --- /dev/null +++ b/x-pack/plugins/serverless/public/types.ts @@ -0,0 +1,13 @@ +/* + * 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. + */ + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ServerlessPluginSetup {} + +export interface ServerlessPluginStart { + setServerlessNavigation: (navigation: JSX.Element) => void; +} diff --git a/x-pack/plugins/serverless/server/config.ts b/x-pack/plugins/serverless/server/config.ts new file mode 100644 index 0000000000000..dce4deda86049 --- /dev/null +++ b/x-pack/plugins/serverless/server/config.ts @@ -0,0 +1,23 @@ +/* + * 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 { schema, TypeOf } from '@kbn/config-schema'; +import { PluginConfigDescriptor } from '@kbn/core/server'; + +export * from './types'; + +const configSchema = schema.object({ + enabled: schema.boolean({ defaultValue: false }), +}); + +type ConfigType = TypeOf; + +export const config: PluginConfigDescriptor = { + schema: configSchema, +}; + +export type ServerlessConfig = TypeOf; diff --git a/x-pack/plugins/serverless/server/index.ts b/x-pack/plugins/serverless/server/index.ts new file mode 100644 index 0000000000000..ae805970e038e --- /dev/null +++ b/x-pack/plugins/serverless/server/index.ts @@ -0,0 +1,19 @@ +/* + * 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 { PluginInitializerContext } from '@kbn/core/server'; +import { ServerlessPlugin } from './plugin'; +export { config } from './config'; + +// This exports static code and TypeScript types, +// as well as, Kibana Platform `plugin()` initializer. + +export function plugin(initializerContext: PluginInitializerContext) { + return new ServerlessPlugin(initializerContext); +} + +export type { ServerlessPluginSetup, ServerlessPluginStart } from './types'; diff --git a/x-pack/plugins/serverless/server/plugin.ts b/x-pack/plugins/serverless/server/plugin.ts new file mode 100644 index 0000000000000..54bfb5c14dc4b --- /dev/null +++ b/x-pack/plugins/serverless/server/plugin.ts @@ -0,0 +1,24 @@ +/* + * 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 { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '@kbn/core/server'; + +import { ServerlessPluginSetup, ServerlessPluginStart } from './types'; + +export class ServerlessPlugin implements Plugin { + constructor(_initializerContext: PluginInitializerContext) {} + + public setup(_core: CoreSetup) { + return {}; + } + + public start(_core: CoreStart) { + return {}; + } + + public stop() {} +} diff --git a/x-pack/plugins/serverless/server/types.ts b/x-pack/plugins/serverless/server/types.ts new file mode 100644 index 0000000000000..92a804b34a948 --- /dev/null +++ b/x-pack/plugins/serverless/server/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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ServerlessPluginSetup {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ServerlessPluginStart {} diff --git a/x-pack/plugins/serverless/tsconfig.json b/x-pack/plugins/serverless/tsconfig.json new file mode 100644 index 0000000000000..ffdc7af796587 --- /dev/null +++ b/x-pack/plugins/serverless/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types" + }, + "include": [ + "index.ts", + "common/**/*.ts", + "public/**/*.ts", + "public/**/*.tsx", + "server/**/*.ts", + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/core", + "@kbn/config-schema", + ] +} diff --git a/x-pack/plugins/serverless_security/tsconfig.json b/x-pack/plugins/serverless_security/tsconfig.json index 7937d55df3797..86d85593b598f 100644 --- a/x-pack/plugins/serverless_security/tsconfig.json +++ b/x-pack/plugins/serverless_security/tsconfig.json @@ -7,7 +7,6 @@ "index.ts", "common/**/*.ts", "public/**/*.ts", - "public/**/*.tsx", "server/**/*.ts", "../../../typings/**/*" ], @@ -16,8 +15,8 @@ ], "kbn_references": [ "@kbn/core", + "@kbn/config-schema", "@kbn/security-plugin", "@kbn/security-solution-plugin", - "@kbn/config-schema", ] } diff --git a/yarn.lock b/yarn.lock index 181e56313e85a..31bcb2534defa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4921,6 +4921,10 @@ version "0.0.0" uid "" +"@kbn/serverless@link:x-pack/plugins/serverless": + version "0.0.0" + uid "" + "@kbn/session-notifications-plugin@link:test/plugin_functional/plugins/session_notifications": version "0.0.0" uid "" From 1ed5318118fea5055b53dcdc98533779aeed31f8 Mon Sep 17 00:00:00 2001 From: Clint Andrew Hall Date: Sun, 26 Feb 2023 00:56:54 -0500 Subject: [PATCH 17/58] Add Serverless as dependency to existing plugins; wire up navigation replacement --- config/serverless.yml | 2 ++ .../plugins/serverless_observability/kibana.jsonc | 1 + .../public/{plugin.ts => plugin.tsx} | 12 +++++++++--- .../serverless_observability/public/types.ts | 15 +++++++++++---- .../serverless_observability/tsconfig.json | 1 + x-pack/plugins/serverless_security/kibana.jsonc | 1 + .../public/{plugin.ts => plugin.tsx} | 4 +++- .../plugins/serverless_security/public/types.ts | 3 +++ x-pack/plugins/serverless_security/tsconfig.json | 2 ++ 9 files changed, 33 insertions(+), 8 deletions(-) rename x-pack/plugins/serverless_observability/public/{plugin.ts => plugin.tsx} (66%) rename x-pack/plugins/serverless_security/public/{plugin.ts => plugin.tsx} (85%) diff --git a/config/serverless.yml b/config/serverless.yml index e69de29bb2d1d..a4dfeaea49267 100644 --- a/config/serverless.yml +++ b/config/serverless.yml @@ -0,0 +1,2 @@ + +xpack.serverless.plugin.enabled: true diff --git a/x-pack/plugins/serverless_observability/kibana.jsonc b/x-pack/plugins/serverless_observability/kibana.jsonc index 0f0e911e6807f..abce6cd9e8350 100644 --- a/x-pack/plugins/serverless_observability/kibana.jsonc +++ b/x-pack/plugins/serverless_observability/kibana.jsonc @@ -13,6 +13,7 @@ "observability" ], "requiredPlugins": [ + "serverless", "observability" ], "optionalPlugins": [], diff --git a/x-pack/plugins/serverless_observability/public/plugin.ts b/x-pack/plugins/serverless_observability/public/plugin.tsx similarity index 66% rename from x-pack/plugins/serverless_observability/public/plugin.ts rename to x-pack/plugins/serverless_observability/public/plugin.tsx index dd1c5bfbc39c7..e6438ee8feff6 100644 --- a/x-pack/plugins/serverless_observability/public/plugin.ts +++ b/x-pack/plugins/serverless_observability/public/plugin.tsx @@ -5,11 +5,13 @@ * 2.0. */ +import React from 'react'; import { CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; import { ServerlessObservabilityPluginSetup, ServerlessObservabilityPluginStart, - AppPluginSetupDependencies, + ServerlessObservabilityPluginSetupDependencies, + ServerlessObservabilityPluginStartDependencies, } from './types'; export class ServerlessObservabilityPlugin @@ -17,7 +19,7 @@ export class ServerlessObservabilityPlugin { public setup( _core: CoreSetup, - setupDeps: AppPluginSetupDependencies + setupDeps: ServerlessObservabilityPluginSetupDependencies ): ServerlessObservabilityPluginSetup { setupDeps.observability.navigation.setIsSidebarEnabled(false); @@ -25,7 +27,11 @@ export class ServerlessObservabilityPlugin return {}; } - public start(_core: CoreStart): ServerlessObservabilityPluginStart { + public start( + _core: CoreStart, + { serverless }: ServerlessObservabilityPluginStartDependencies + ): ServerlessObservabilityPluginStart { + serverless.setServerlessNavigation(

Observability

); return {}; } diff --git a/x-pack/plugins/serverless_observability/public/types.ts b/x-pack/plugins/serverless_observability/public/types.ts index 73ab40e9257f7..e0fca61302ad7 100644 --- a/x-pack/plugins/serverless_observability/public/types.ts +++ b/x-pack/plugins/serverless_observability/public/types.ts @@ -5,7 +5,11 @@ * 2.0. */ -import { ObservabilityPublicSetup } from '@kbn/observability-plugin/public'; +import { ServerlessPluginSetup, ServerlessPluginStart } from '@kbn/serverless/public'; +import { + ObservabilityPublicSetup, + ObservabilityPublicStart, +} from '@kbn/observability-plugin/public'; // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface ServerlessObservabilityPluginSetup {} @@ -13,9 +17,12 @@ export interface ServerlessObservabilityPluginSetup {} // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface ServerlessObservabilityPluginStart {} -export interface AppPluginSetupDependencies { +export interface ServerlessObservabilityPluginSetupDependencies { observability: ObservabilityPublicSetup; + serverless: ServerlessPluginSetup; } -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface AppPluginStartDependencies {} +export interface ServerlessObservabilityPluginStartDependencies { + observability: ObservabilityPublicStart; + serverless: ServerlessPluginStart; +} diff --git a/x-pack/plugins/serverless_observability/tsconfig.json b/x-pack/plugins/serverless_observability/tsconfig.json index 4c9f7b818b430..8c9ec19e4736a 100644 --- a/x-pack/plugins/serverless_observability/tsconfig.json +++ b/x-pack/plugins/serverless_observability/tsconfig.json @@ -18,5 +18,6 @@ "@kbn/core", "@kbn/config-schema", "@kbn/observability-plugin", + "@kbn/serverless", ] } diff --git a/x-pack/plugins/serverless_security/kibana.jsonc b/x-pack/plugins/serverless_security/kibana.jsonc index c94c93513209f..71405b0e47707 100644 --- a/x-pack/plugins/serverless_security/kibana.jsonc +++ b/x-pack/plugins/serverless_security/kibana.jsonc @@ -13,6 +13,7 @@ "security" ], "requiredPlugins": [ + "serverless", "security", "securitySolution" ], diff --git a/x-pack/plugins/serverless_security/public/plugin.ts b/x-pack/plugins/serverless_security/public/plugin.tsx similarity index 85% rename from x-pack/plugins/serverless_security/public/plugin.ts rename to x-pack/plugins/serverless_security/public/plugin.tsx index 4af166c8cf804..f0f1fd2e0e9f0 100644 --- a/x-pack/plugins/serverless_security/public/plugin.ts +++ b/x-pack/plugins/serverless_security/public/plugin.tsx @@ -5,6 +5,7 @@ * 2.0. */ +import React from 'react'; import { CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; import { ServerlessSecurityPluginSetup, @@ -32,8 +33,9 @@ export class ServerlessSecurityPlugin public start( _core: CoreStart, - _startDeps: ServerlessSecurityPluginStartDependencies + startDeps: ServerlessSecurityPluginStartDependencies ): ServerlessSecurityPluginStart { + startDeps.serverless.setServerlessNavigation(

Security

); return {}; } diff --git a/x-pack/plugins/serverless_security/public/types.ts b/x-pack/plugins/serverless_security/public/types.ts index 58ac0b112abcf..1fc18893ce1fd 100644 --- a/x-pack/plugins/serverless_security/public/types.ts +++ b/x-pack/plugins/serverless_security/public/types.ts @@ -10,6 +10,7 @@ import { PluginSetup as SecuritySolutionPluginSetup, PluginStart as SecuritySolutionPluginStart, } from '@kbn/security-solution-plugin/public'; +import { ServerlessPluginSetup, ServerlessPluginStart } from '@kbn/serverless/public'; // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface ServerlessSecurityPluginSetup {} @@ -20,9 +21,11 @@ export interface ServerlessSecurityPluginStart {} export interface ServerlessSecurityPluginSetupDependencies { security: SecurityPluginSetup; securitySolution: SecuritySolutionPluginSetup; + serverless: ServerlessPluginSetup; } export interface ServerlessSecurityPluginStartDependencies { security: SecurityPluginStart; securitySolution: SecuritySolutionPluginStart; + serverless: ServerlessPluginStart; } diff --git a/x-pack/plugins/serverless_security/tsconfig.json b/x-pack/plugins/serverless_security/tsconfig.json index 86d85593b598f..ddf77b288e628 100644 --- a/x-pack/plugins/serverless_security/tsconfig.json +++ b/x-pack/plugins/serverless_security/tsconfig.json @@ -7,6 +7,7 @@ "index.ts", "common/**/*.ts", "public/**/*.ts", + "public/**/*.tsx", "server/**/*.ts", "../../../typings/**/*" ], @@ -18,5 +19,6 @@ "@kbn/config-schema", "@kbn/security-plugin", "@kbn/security-solution-plugin", + "@kbn/serverless", ] } From c32427a0d95e7797910169281854ddfd951948ea Mon Sep 17 00:00:00 2001 From: Rodney Norris Date: Wed, 1 Mar 2023 22:45:51 +0000 Subject: [PATCH 18/58] Enterprise Search: add ability to hide page solutionnav --- .../public/applications/index.tsx | 13 ++++++++++++- .../applications/shared/kibana/kibana_logic.ts | 8 ++++++++ .../public/applications/shared/layout/nav.tsx | 4 +++- .../applications/shared/layout/page_template.tsx | 6 +++++- x-pack/plugins/enterprise_search/public/index.ts | 2 ++ x-pack/plugins/enterprise_search/public/plugin.ts | 14 +++++++++++++- 6 files changed, 43 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/index.tsx b/x-pack/plugins/enterprise_search/public/applications/index.tsx index 11250d05a845e..f88ae1d0ab287 100644 --- a/x-pack/plugins/enterprise_search/public/applications/index.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/index.tsx @@ -36,7 +36,17 @@ import { mountLicensingLogic } from './shared/licensing'; export const renderApp = ( App: React.FC, - { params, core, plugins }: { params: AppMountParameters; core: CoreStart; plugins: PluginsStart }, + { + params, + core, + plugins, + getIsSidebarEnabled, + }: { + params: AppMountParameters; + core: CoreStart; + plugins: PluginsStart; + getIsSidebarEnabled: () => boolean; + }, { config, data }: { config: ClientConfigType; data: ClientData } ) => { const { publicUrl, errorConnectingMessage, ...initialData } = data; @@ -64,6 +74,7 @@ export const renderApp = ( charts: plugins.charts, cloud: plugins.cloud, uiSettings: core.uiSettings, + getIsSidebarEnabled, guidedOnboarding: plugins.guidedOnboarding, history: params.history, navigateToUrl: core.application.navigateToUrl, diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts b/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts index 070c273884772..5ab00b869e93f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts @@ -33,6 +33,7 @@ interface KibanaLogicProps { application: ApplicationStart; config: { host?: string }; productAccess: ProductAccess; + getIsSidebarEnabled: () => boolean; // Kibana core capabilities: Capabilities; history: ScopedHistory; @@ -49,9 +50,11 @@ interface KibanaLogicProps { // Optional plugins cloud?: CloudSetup; } + export interface KibanaValues extends Omit { cloud: Partial; isCloud: boolean; + isSidebarEnabled: boolean; navigateToUrl(path: string, options?: CreateHrefOptions): Promise; } @@ -63,6 +66,7 @@ export const KibanaLogic = kea>({ config: [props.config || {}, {}], charts: [props.charts, {}], cloud: [props.cloud || {}, {}], + getIsSidebarEnabled: [props.getIsSidebarEnabled, {}], guidedOnboarding: [props.guidedOnboarding, {}], history: [props.history, {}], navigateToUrl: [ @@ -83,6 +87,10 @@ export const KibanaLogic = kea>({ }), selectors: ({ selectors }) => ({ isCloud: [() => [selectors.cloud], (cloud?: Partial) => !!cloud?.isCloudEnabled], + isSidebarEnabled: [ + () => [selectors.getIsSidebarEnabled], + (getIsSidebarEnabled: () => boolean) => getIsSidebarEnabled(), + ], }), }); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.tsx index 8c7025312e387..b08a9256c42ed 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.tsx @@ -30,8 +30,9 @@ import { KibanaLogic } from '../kibana'; import { generateNavLink } from './nav_link_helpers'; export const useEnterpriseSearchNav = () => { - const { productAccess } = useValues(KibanaLogic); + const { isSidebarEnabled, productAccess } = useValues(KibanaLogic); + if (!isSidebarEnabled) return undefined; const enginesSectionEnabled = productAccess.hasSearchEnginesAccess; const navItems: Array> = [ @@ -243,6 +244,7 @@ export const useEnterpriseSearchNav = () => { export const useEnterpriseSearchEngineNav = (engineName?: string, isEmptyState?: boolean) => { const navItems = useEnterpriseSearchNav(); + if (!navItems) return undefined; if (!engineName) return navItems; const searchItem = navItems.find((item) => item.id === 'enginesSearch'); if (!searchItem || !searchItem.items) return navItems; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/layout/page_template.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/layout/page_template.tsx index 4793d11b56c76..d234b8374d0ae 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/layout/page_template.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/layout/page_template.tsx @@ -73,7 +73,11 @@ export const EnterpriseSearchPageTemplateWrapper: React.FC = ), }} isEmptyState={isEmptyState && !isLoading} - solutionNav={solutionNav ? { icon: 'logoEnterpriseSearch', ...solutionNav } : undefined} + solutionNav={ + solutionNav && solutionNav.items + ? { icon: 'logoEnterpriseSearch', ...solutionNav } + : undefined + } > {setPageChrome} {readOnlyMode && ( diff --git a/x-pack/plugins/enterprise_search/public/index.ts b/x-pack/plugins/enterprise_search/public/index.ts index b24616fc4b3d8..8dc84c6934e42 100644 --- a/x-pack/plugins/enterprise_search/public/index.ts +++ b/x-pack/plugins/enterprise_search/public/index.ts @@ -12,3 +12,5 @@ import { EnterpriseSearchPlugin } from './plugin'; export const plugin = (initializerContext: PluginInitializerContext) => { return new EnterpriseSearchPlugin(initializerContext); }; + +export type { EnterpriseSearchPublicSetup, EnterpriseSearchPublicStart } from './plugin'; diff --git a/x-pack/plugins/enterprise_search/public/plugin.ts b/x-pack/plugins/enterprise_search/public/plugin.ts index add4d9f69b56b..e733d1811a1d1 100644 --- a/x-pack/plugins/enterprise_search/public/plugin.ts +++ b/x-pack/plugins/enterprise_search/public/plugin.ts @@ -45,6 +45,9 @@ export interface ClientData extends InitialAppData { errorConnectingMessage?: string; } +export type EnterpriseSearchPublicSetup = ReturnType; +export type EnterpriseSearchPublicStart = ReturnType; + interface PluginsSetup { cloud?: CloudSetup; home?: HomePublicPluginSetup; @@ -64,6 +67,7 @@ export class EnterpriseSearchPlugin implements Plugin { private config: ClientConfigType; private hasInitialized: boolean = false; private data: ClientData = {} as ClientData; + private isSidebarEnabled: boolean = true; constructor(initializerContext: PluginInitializerContext) { this.config = initializerContext.config.get(); @@ -288,6 +292,14 @@ export class EnterpriseSearchPlugin implements Plugin { showOnHomePage: false, }); } + + return { + navigation: { + setIsSidebarEnabled: (enabled: boolean) => { + this.isSidebarEnabled = enabled; + }, + }, + }; } public start(core: CoreStart) { @@ -312,7 +324,7 @@ export class EnterpriseSearchPlugin implements Plugin { : undefined; const plugins = { ...pluginsStart, cloud } as PluginsStart; - return { params, core: coreStart, plugins }; + return { params, core: coreStart, plugins, getIsSidebarEnabled: () => this.isSidebarEnabled }; } private getPluginData() { From 38a54055971954e5aebe9cde8b7b9dd2d0620c37 Mon Sep 17 00:00:00 2001 From: Rodney Norris Date: Wed, 1 Mar 2023 22:48:34 +0000 Subject: [PATCH 19/58] management: add ability to hide page solutionnav --- .../management_app/management_app.tsx | 28 +++++++++++-------- src/plugins/management/public/plugin.ts | 7 +++++ src/plugins/management/public/types.ts | 1 + 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/plugins/management/public/components/management_app/management_app.tsx b/src/plugins/management/public/components/management_app/management_app.tsx index bc0b88e7dffcb..f0a3eb57db139 100644 --- a/src/plugins/management/public/components/management_app/management_app.tsx +++ b/src/plugins/management/public/components/management_app/management_app.tsx @@ -34,6 +34,7 @@ export interface ManagementAppDependencies { sections: SectionsServiceStart; kibanaVersion: string; setBreadcrumbs: (newBreadcrumbs: ChromeBreadcrumb[]) => void; + getIsSidebarEnabled: () => boolean; } export const ManagementApp = ({ dependencies, history, theme$ }: ManagementAppProps) => { @@ -75,18 +76,21 @@ export const ManagementApp = ({ dependencies, history, theme$ }: ManagementAppPr return null; } - const solution: KibanaPageTemplateProps['solutionNav'] = { - name: i18n.translate('management.nav.label', { - defaultMessage: 'Management', - }), - icon: 'managementApp', - 'data-test-subj': 'mgtSideBarNav', - items: managementSidebarNav({ - selectedId, - sections, - history, - }), - }; + const solution: KibanaPageTemplateProps['solutionNav'] | undefined = + dependencies.getIsSidebarEnabled() + ? { + name: i18n.translate('management.nav.label', { + defaultMessage: 'Management', + }), + icon: 'managementApp', + 'data-test-subj': 'mgtSideBarNav', + items: managementSidebarNav({ + selectedId, + sections, + history, + }), + } + : undefined; return ( diff --git a/src/plugins/management/public/plugin.ts b/src/plugins/management/public/plugin.ts index 0cdb83d4b6793..39de850e2c72a 100644 --- a/src/plugins/management/public/plugin.ts +++ b/src/plugins/management/public/plugin.ts @@ -71,6 +71,8 @@ export class ManagementPlugin private hasAnyEnabledApps = true; + private isSidebarEnabled = true; + constructor(private initializerContext: PluginInitializerContext) {} public setup(core: CoreSetup, { home, share }: ManagementSetupDependencies) { @@ -93,6 +95,7 @@ export class ManagementPlugin visible: () => this.hasAnyEnabledApps, }); } + const managementPlugin = this; core.application.register({ id: MANAGEMENT_APP_ID, @@ -111,6 +114,7 @@ export class ManagementPlugin sections: getSectionsServiceStartPrivate(), kibanaVersion, setBreadcrumbs: coreStart.chrome.setBreadcrumbs, + getIsSidebarEnabled: () => managementPlugin.isSidebarEnabled, }); }, }); @@ -118,6 +122,9 @@ export class ManagementPlugin return { sections: this.managementSections.setup(), locator, + setIsSidebarEnabled: (enabled: boolean) => { + this.isSidebarEnabled = enabled; + }, }; } diff --git a/src/plugins/management/public/types.ts b/src/plugins/management/public/types.ts index 5d8d963ea981e..b9896a0e38b21 100644 --- a/src/plugins/management/public/types.ts +++ b/src/plugins/management/public/types.ts @@ -16,6 +16,7 @@ import type { ManagementAppLocatorParams } from '../common/locator'; export interface ManagementSetup { sections: SectionsServiceSetup; locator: LocatorPublic; + setIsSidebarEnabled: (enabled: boolean) => void; } export interface DefinedSections { From 3468498ae7ba8c9ff8a9fa1395a9a80baf32b5c2 Mon Sep 17 00:00:00 2001 From: semd Date: Thu, 2 Mar 2023 19:27:41 +0100 Subject: [PATCH 20/58] migration of security nav to package --- packages/shared-ux/side_navigation/README.mdx | 24 +++++ packages/shared-ux/side_navigation/index.ts | 19 ++++ .../shared-ux/side_navigation/jest.config.js | 13 +++ .../shared-ux/side_navigation/kibana.jsonc | 5 + .../shared-ux/side_navigation/package.json | 6 ++ .../side_navigation/src/beta_badge.tsx | 34 +++++++ .../shared-ux/side_navigation/src/index.tsx | 21 ++++ .../src/side_navigation.styles.ts | 29 ++++++ .../src/side_navigation.test.tsx | 80 ++++++---------- .../side_navigation/src/side_navigation.tsx | 71 ++++++++------ .../src/side_navigation_panel.styles.ts | 74 +++++++++++++++ .../src/side_navigation_panel.tsx | 74 ++++++++------- .../src}/solution_grouped_nav_panel.test.tsx | 59 ++++++------ .../side_navigation/src}/telemetry/const.ts | 6 +- .../src}/telemetry/telemetry_context.tsx | 5 +- .../shared-ux/side_navigation/src}/types.ts | 40 ++++---- .../shared-ux/side_navigation/tsconfig.json | 26 +++++ .../components/navigation/nav_links.test.ts | 95 ------------------- .../common/components/navigation/nav_links.ts | 41 -------- .../navigation/security_side_nav/index.ts | 4 +- .../security_side_nav/security_side_nav.tsx | 42 ++++---- .../solution_grouped_nav/icons/spaces.tsx | 26 ----- .../navigation/solution_grouped_nav/index.ts | 8 -- .../solution_grouped_nav.styles.tsx | 27 ------ .../solution_grouped_nav_panel.styles.tsx | 69 -------------- .../common/components/navigation/types.ts | 17 ---- 26 files changed, 445 insertions(+), 470 deletions(-) create mode 100644 packages/shared-ux/side_navigation/README.mdx create mode 100644 packages/shared-ux/side_navigation/index.ts create mode 100644 packages/shared-ux/side_navigation/jest.config.js create mode 100644 packages/shared-ux/side_navigation/kibana.jsonc create mode 100644 packages/shared-ux/side_navigation/package.json create mode 100644 packages/shared-ux/side_navigation/src/beta_badge.tsx create mode 100644 packages/shared-ux/side_navigation/src/index.tsx create mode 100644 packages/shared-ux/side_navigation/src/side_navigation.styles.ts rename x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/solution_grouped_nav.test.tsx => packages/shared-ux/side_navigation/src/side_navigation.test.tsx (60%) rename x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/solution_grouped_nav.tsx => packages/shared-ux/side_navigation/src/side_navigation.tsx (78%) create mode 100644 packages/shared-ux/side_navigation/src/side_navigation_panel.styles.ts rename x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/solution_grouped_nav_panel.tsx => packages/shared-ux/side_navigation/src/side_navigation_panel.tsx (65%) rename {x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav => packages/shared-ux/side_navigation/src}/solution_grouped_nav_panel.test.tsx (75%) rename {x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav => packages/shared-ux/side_navigation/src}/telemetry/const.ts (63%) rename {x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav => packages/shared-ux/side_navigation/src}/telemetry/telemetry_context.tsx (81%) rename {x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav => packages/shared-ux/side_navigation/src}/types.ts (53%) create mode 100644 packages/shared-ux/side_navigation/tsconfig.json delete mode 100644 x-pack/plugins/security_solution/public/common/components/navigation/nav_links.test.ts delete mode 100644 x-pack/plugins/security_solution/public/common/components/navigation/nav_links.ts delete mode 100644 x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/icons/spaces.tsx delete mode 100644 x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/index.ts delete mode 100644 x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/solution_grouped_nav.styles.tsx delete mode 100644 x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/solution_grouped_nav_panel.styles.tsx diff --git a/packages/shared-ux/side_navigation/README.mdx b/packages/shared-ux/side_navigation/README.mdx new file mode 100644 index 0000000000000..740837bb47a63 --- /dev/null +++ b/packages/shared-ux/side_navigation/README.mdx @@ -0,0 +1,24 @@ +TODO +--- +id: sharedUX/Components/Toolbar +slug: /shared-ux/components/button_toolbar +title: Toolbar +summary: An implementation of the popover, primary button, icon button group and add from library button +tags: ['shared-ux', 'component', 'toolbar'] +date: 2022-07-28 +--- + +This `toolbar` component accepts a `children` prop. Children can include a `popover` or a generic `button`. It can also include the `IconButtonGroup` and `AddFromLibrary` component for soltuions. +Styling of the popover and button follow the primary styles. + + +```jsx + + {{ + primaryButton, + iconButtonGroup, + extraButtons, + addFromLibraryButton, + }} + +``` \ No newline at end of file diff --git a/packages/shared-ux/side_navigation/index.ts b/packages/shared-ux/side_navigation/index.ts new file mode 100644 index 0000000000000..acc1c111dbe5a --- /dev/null +++ b/packages/shared-ux/side_navigation/index.ts @@ -0,0 +1,19 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +export { SideNavigation, type SideNavigationProps } from './src'; +export type { + SideNavItem, + LinkCategory, + LinkCategories, + DefaultSideNavItem, + CustomSideNavItem, + isCustomItem, + isDefaultItem, + Tracker, +} from './src/types'; diff --git a/packages/shared-ux/side_navigation/jest.config.js b/packages/shared-ux/side_navigation/jest.config.js new file mode 100644 index 0000000000000..e52b5a5d24a9b --- /dev/null +++ b/packages/shared-ux/side_navigation/jest.config.js @@ -0,0 +1,13 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/packages/shared-ux/side_navigation'], +}; diff --git a/packages/shared-ux/side_navigation/kibana.jsonc b/packages/shared-ux/side_navigation/kibana.jsonc new file mode 100644 index 0000000000000..eb5c53604fde5 --- /dev/null +++ b/packages/shared-ux/side_navigation/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/shared-ux-side-navigation", + "owner": "@elastic/appex-sharedux" +} diff --git a/packages/shared-ux/side_navigation/package.json b/packages/shared-ux/side_navigation/package.json new file mode 100644 index 0000000000000..0567384c50058 --- /dev/null +++ b/packages/shared-ux/side_navigation/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/shared-ux-side-navigation", + "private": true, + "version": "1.0.0", + "license": "SSPL-1.0 OR Elastic License 2.0" +} \ No newline at end of file diff --git a/packages/shared-ux/side_navigation/src/beta_badge.tsx b/packages/shared-ux/side_navigation/src/beta_badge.tsx new file mode 100644 index 0000000000000..4393ee391190b --- /dev/null +++ b/packages/shared-ux/side_navigation/src/beta_badge.tsx @@ -0,0 +1,34 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { css } from '@emotion/react'; +import { EuiBetaBadge, useEuiTheme } from '@elastic/eui'; + +export const BETA_LABEL = i18n.translate('sharedUXPackages.side_navigation.beta.label', { + defaultMessage: 'Beta', +}); + +export const BetaBadge = ({ text, className }: { text?: string; className?: string }) => { + const { euiTheme } = useEuiTheme(); + + return ( + + ); +}; diff --git a/packages/shared-ux/side_navigation/src/index.tsx b/packages/shared-ux/side_navigation/src/index.tsx new file mode 100644 index 0000000000000..e2c1cc595a824 --- /dev/null +++ b/packages/shared-ux/side_navigation/src/index.tsx @@ -0,0 +1,21 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import React, { lazy, Suspense } from 'react'; +import { EuiLoadingSpinner } from '@elastic/eui'; +import type { SideNavigationProps } from './side_navigation'; + +export { SideNavigationProps as SideNavigationProps }; + +const SideNavigationLazy = lazy(() => import('./side_navigation')); + +export const SideNavigation = (props: SideNavigationProps) => ( + }> + + +); diff --git a/packages/shared-ux/side_navigation/src/side_navigation.styles.ts b/packages/shared-ux/side_navigation/src/side_navigation.styles.ts new file mode 100644 index 0000000000000..57b9784ab1b19 --- /dev/null +++ b/packages/shared-ux/side_navigation/src/side_navigation.styles.ts @@ -0,0 +1,29 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import { transparentize, type EuiThemeComputed } from '@elastic/eui'; +import { css } from '@emotion/css'; + +export const SideNavItemStyles = (euiTheme: EuiThemeComputed<{}>) => css` + font-weight: ${euiTheme.font.weight.regular}; + &.solutionGroupedNavItem--isPrimary * { + font-weight: ${euiTheme.font.weight.bold}; + } + &:focus, + &:focus-within, + &:hover, + &.solutionGroupedNavItem--isActive { + background-color: ${transparentize(euiTheme.colors.primary, 0.1)}; + } + .solutionGroupedNavItemButton:focus, + .solutionGroupedNavItemButton:focus-within, + .solutionGroupedNavItemButton:hover { + transform: none; /* prevent translationY transform that causes misalignment within the list item */ + background-color: ${transparentize(euiTheme.colors.primary, 0.2)}; + } +`; diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/solution_grouped_nav.test.tsx b/packages/shared-ux/side_navigation/src/side_navigation.test.tsx similarity index 60% rename from x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/solution_grouped_nav.test.tsx rename to packages/shared-ux/side_navigation/src/side_navigation.test.tsx index 91c71946d649e..0e2270f44dc26 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/solution_grouped_nav.test.tsx +++ b/packages/shared-ux/side_navigation/src/side_navigation.test.tsx @@ -1,16 +1,14 @@ /* * 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. + * 2.0 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 or the Server + * Side Public License, v 1. */ import React from 'react'; import { render, waitFor } from '@testing-library/react'; -import { SecurityPageName } from '../../../../app/types'; -import { TestProviders } from '../../../mock'; -import type { SolutionGroupedNavProps } from './solution_grouped_nav'; -import { SolutionGroupedNav } from './solution_grouped_nav'; +import { SideNavigation, type SideNavigationProps } from './side_navigation'; import type { SideNavItem } from './types'; import { METRIC_TYPE } from '@kbn/analytics'; import { TELEMETRY_EVENT } from './telemetry/const'; @@ -19,12 +17,12 @@ const mockTrack = jest.fn(); const mockItems: SideNavItem[] = [ { - id: SecurityPageName.dashboardsLanding, + id: 'dashboardsLanding', label: 'Dashboards', href: '/dashboards', items: [ { - id: SecurityPageName.overview, + id: 'overview', label: 'Overview', href: '/overview', description: 'Overview description', @@ -32,26 +30,16 @@ const mockItems: SideNavItem[] = [ ], }, { - id: SecurityPageName.alerts, + id: 'alerts', label: 'Alerts', href: '/alerts', }, ]; -const renderNav = (props: Partial = {}) => - render( - , - { - wrapper: TestProviders, - } - ); - -describe('SolutionGroupedNav', () => { +const renderNav = (props: Partial = {}) => + render(); + +describe('SideNavigation', () => { beforeEach(() => { jest.clearAllMocks(); }); @@ -66,13 +54,11 @@ describe('SolutionGroupedNav', () => { it('should contain correct href in links', () => { const result = renderNav(); expect( - result - .getByTestId(`groupedNavItemLink-${SecurityPageName.dashboardsLanding}`) - .getAttribute('href') + result.getByTestId(`groupedNavItemLink-${'dashboardsLanding'}`).getAttribute('href') ).toBe('/dashboards'); - expect( - result.getByTestId(`groupedNavItemLink-${SecurityPageName.alerts}`).getAttribute('href') - ).toBe('/alerts'); + expect(result.getByTestId(`groupedNavItemLink-${'alerts'}`).getAttribute('href')).toBe( + '/alerts' + ); }); it('should call onClick callback if link clicked', () => { @@ -82,14 +68,14 @@ describe('SolutionGroupedNav', () => { const items = [ ...mockItems, { - id: SecurityPageName.exploreLanding, + id: 'exploreLanding', label: 'Explore', href: '/explore', onClick: mockOnClick, }, ]; const result = renderNav({ items }); - result.getByTestId(`groupedNavItemLink-${SecurityPageName.exploreLanding}`).click(); + result.getByTestId(`groupedNavItemLink-${'exploreLanding'}`).click(); expect(mockOnClick).toHaveBeenCalled(); }); @@ -97,16 +83,16 @@ describe('SolutionGroupedNav', () => { const items = [ ...mockItems, { - id: SecurityPageName.exploreLanding, + id: 'exploreLanding', label: 'Explore', href: '/explore', }, ]; const result = renderNav({ items }); - result.getByTestId(`groupedNavItemLink-${SecurityPageName.exploreLanding}`).click(); + result.getByTestId(`groupedNavItemLink-${'exploreLanding'}`).click(); expect(mockTrack).toHaveBeenCalledWith( METRIC_TYPE.CLICK, - `${TELEMETRY_EVENT.NAVIGATION}${SecurityPageName.exploreLanding}` + `${TELEMETRY_EVENT.NAVIGATION}${'exploreLanding'}` ); }); }); @@ -114,19 +100,15 @@ describe('SolutionGroupedNav', () => { describe('panel button toggle', () => { it('should render the group button only for grouped items', () => { const result = renderNav(); - expect( - result.getByTestId(`groupedNavItemButton-${SecurityPageName.dashboardsLanding}`) - ).toBeInTheDocument(); - expect( - result.queryByTestId(`groupedNavItemButton-${SecurityPageName.alerts}`) - ).not.toBeInTheDocument(); + expect(result.getByTestId(`groupedNavItemButton-${'dashboardsLanding'}`)).toBeInTheDocument(); + expect(result.queryByTestId(`groupedNavItemButton-${'alerts'}`)).not.toBeInTheDocument(); }); it('should render the group panel when button is clicked', () => { const result = renderNav(); expect(result.queryByTestId('groupedNavPanel')).not.toBeInTheDocument(); - result.getByTestId(`groupedNavItemButton-${SecurityPageName.dashboardsLanding}`).click(); + result.getByTestId(`groupedNavItemButton-${'dashboardsLanding'}`).click(); expect(result.getByTestId('groupedNavPanel')).toBeInTheDocument(); expect(result.getByText('Overview')).toBeInTheDocument(); }); @@ -135,19 +117,19 @@ describe('SolutionGroupedNav', () => { const result = renderNav(); expect(result.queryByTestId('groupedNavPanel')).not.toBeInTheDocument(); - result.getByTestId(`groupedNavItemButton-${SecurityPageName.dashboardsLanding}`).click(); + result.getByTestId(`groupedNavItemButton-${'dashboardsLanding'}`).click(); expect(mockTrack).toHaveBeenCalledWith( METRIC_TYPE.CLICK, - `${TELEMETRY_EVENT.GROUPED_NAVIGATION_TOGGLE}${SecurityPageName.dashboardsLanding}` + `${TELEMETRY_EVENT.GROUPED_NAVIGATION_TOGGLE}${'dashboardsLanding'}` ); }); it('should close the group panel when the same button is clicked', () => { const result = renderNav(); - result.getByTestId(`groupedNavItemButton-${SecurityPageName.dashboardsLanding}`).click(); + result.getByTestId(`groupedNavItemButton-${'dashboardsLanding'}`).click(); expect(result.getByTestId('groupedNavPanel')).toBeInTheDocument(); - result.getByTestId(`groupedNavItemButton-${SecurityPageName.dashboardsLanding}`).click(); + result.getByTestId(`groupedNavItemButton-${'dashboardsLanding'}`).click(); waitFor(() => { expect(result.queryByTestId('groupedNavPanel')).not.toBeInTheDocument(); @@ -158,12 +140,12 @@ describe('SolutionGroupedNav', () => { const items = [ ...mockItems, { - id: SecurityPageName.exploreLanding, + id: 'exploreLanding', label: 'Explore', href: '/explore', items: [ { - id: SecurityPageName.users, + id: 'users', label: 'Users', href: '/users', description: 'Users description', @@ -173,11 +155,11 @@ describe('SolutionGroupedNav', () => { ]; const result = renderNav({ items }); - result.getByTestId(`groupedNavItemButton-${SecurityPageName.dashboardsLanding}`).click(); + result.getByTestId(`groupedNavItemButton-${'dashboardsLanding'}`).click(); expect(result.getByTestId('groupedNavPanel')).toBeInTheDocument(); expect(result.getByText('Overview')).toBeInTheDocument(); - result.getByTestId(`groupedNavItemButton-${SecurityPageName.exploreLanding}`).click(); + result.getByTestId(`groupedNavItemButton-${'exploreLanding'}`).click(); expect(result.queryByTestId('groupedNavPanel')).toBeInTheDocument(); expect(result.getByText('Users')).toBeInTheDocument(); }); diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/solution_grouped_nav.tsx b/packages/shared-ux/side_navigation/src/side_navigation.tsx similarity index 78% rename from x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/solution_grouped_nav.tsx rename to packages/shared-ux/side_navigation/src/side_navigation.tsx index 382cb5b4d4293..dd0e2ad3e70d9 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/solution_grouped_nav.tsx +++ b/packages/shared-ux/side_navigation/src/side_navigation.tsx @@ -1,8 +1,9 @@ /* * 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. + * 2.0 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 or the Server + * Side Public License, v 1. */ import React, { Fragment, useCallback, useMemo, useRef, useState } from 'react'; @@ -12,29 +13,29 @@ import { EuiFlexItem, EuiLink, useIsWithinBreakpoints, + useEuiTheme, + EuiListGroupItem, } from '@elastic/eui'; import classNames from 'classnames'; import { METRIC_TYPE } from '@kbn/analytics'; -import { SolutionNavPanel } from './solution_grouped_nav_panel'; -import { EuiListGroupItemStyled } from './solution_grouped_nav.styles'; +import { SideNavigationPanel } from './side_navigation_panel'; import type { DefaultSideNavItem, SideNavItem, Tracker } from './types'; -import { isCustomItem, isDefaultItem } from './types'; -import { EuiIconSpaces } from './icons/spaces'; -import type { LinkCategories } from '../../../links'; +import { isCustomItem, isDefaultItem, type LinkCategories } from './types'; import { TELEMETRY_EVENT } from './telemetry/const'; import { TelemetryContextProvider, useTelemetryContext } from './telemetry/telemetry_context'; +import { SideNavItemStyles } from './side_navigation.styles'; -export interface SolutionGroupedNavProps { +export interface SideNavigationProps { items: SideNavItem[]; selectedId: string; footerItems?: SideNavItem[]; - bottomOffset?: string; - // This enables Telemetry tracking inside group nav, this has to be binded with appId + panelBottomOffset?: string; + // This enables Telemetry tracking inside side navigation, this has to be bound with the plugin appId // e.g.: usageCollection?.reportUiCounter?.bind(null, appId) tracker?: Tracker; } -export interface SolutionNavItemsProps { +export interface SideNavigationItemsProps { items: SideNavItem[]; selectedId: string; activePanelNavId: ActivePanelNav; @@ -42,7 +43,7 @@ export interface SolutionNavItemsProps { navItemsById: NavItemsById; onOpenPanelNav: (id: string) => void; } -export interface SolutionNavItemProps { +export interface SideNavigationItemProps { item: SideNavItem; isSelected: boolean; isActive: boolean; @@ -56,11 +57,11 @@ type NavItemsById = Record< { title: string; panelItems: DefaultSideNavItem[]; categories?: LinkCategories } >; -export const SolutionGroupedNavComponent: React.FC = ({ +export const SideNavigationComponent: React.FC = ({ items, selectedId, footerItems = [], - bottomOffset, + panelBottomOffset, tracker, }) => { const isMobileSize = useIsWithinBreakpoints(['xs', 's']); @@ -112,16 +113,16 @@ export const SolutionGroupedNavComponent: React.FC = ({ } const { panelItems, title, categories } = navItemsById[activePanelNavId]; return ( - ); - }, [activePanelNavId, bottomOffset, navItemsById, onClosePanelNav, onOutsidePanelClick]); + }, [activePanelNavId, panelBottomOffset, navItemsById, onClosePanelNav, onOutsidePanelClick]); return ( @@ -130,7 +131,7 @@ export const SolutionGroupedNavComponent: React.FC = ({ - = ({ - = ({ ); }; -export const SolutionGroupedNav = React.memo(SolutionGroupedNavComponent); +export const SideNavigation = React.memo(SideNavigationComponent); -const SolutionNavItems: React.FC = ({ +const SideNavigationItems: React.FC = ({ items, selectedId, activePanelNavId, @@ -172,7 +173,7 @@ const SolutionNavItems: React.FC = ({ }) => ( <> {items.map((item) => ( - = ({ ); -const SolutionNavItemComponent: React.FC = ({ +const SideNavigationItemComponent: React.FC = ({ item, isSelected, isActive, hasPanelNav, onOpenPanelNav, }) => { + const { euiTheme } = useEuiTheme(); const { tracker } = useTelemetryContext(); if (isCustomItem(item)) { @@ -203,10 +205,16 @@ const SolutionNavItemComponent: React.FC = ({ tracker?.(METRIC_TYPE.CLICK, `${TELEMETRY_EVENT.NAVIGATION}${id}`); onClick?.(ev); }; - const itemClassNames = classNames('solutionGroupedNavItem', { - 'solutionGroupedNavItem--isActive': isActive, - 'solutionGroupedNavItem--isPrimary': isSelected, - }); + + const sideNavItemStyles = SideNavItemStyles(euiTheme); + const itemClassNames = classNames( + 'solutionGroupedNavItem', + { + 'solutionGroupedNavItem--isActive': isActive, + 'solutionGroupedNavItem--isPrimary': isSelected, + }, + sideNavItemStyles + ); const buttonClassNames = classNames('solutionGroupedNavItemButton'); const onButtonClick: React.MouseEventHandler = (ev) => { @@ -225,7 +233,7 @@ const SolutionNavItemComponent: React.FC = ({ color={isSelected ? 'primary' : 'text'} data-test-subj={`groupedNavItemLink-${id}`} > - = ({ className: buttonClassNames, color: isActive ? 'primary' : 'text', onClick: onButtonClick, - iconType: EuiIconSpaces, + iconType: 'spaces', iconSize: 'm', 'aria-label': 'Toggle group nav', 'data-test-subj': `groupedNavItemButton-${id}`, @@ -248,4 +256,7 @@ const SolutionNavItemComponent: React.FC = ({ ); }; -const SolutionNavItem = React.memo(SolutionNavItemComponent); +const SideNavItem = React.memo(SideNavigationItemComponent); + +// eslint-disable-next-line import/no-default-export +export default SideNavigation; diff --git a/packages/shared-ux/side_navigation/src/side_navigation_panel.styles.ts b/packages/shared-ux/side_navigation/src/side_navigation_panel.styles.ts new file mode 100644 index 0000000000000..29941a0aeaf6f --- /dev/null +++ b/packages/shared-ux/side_navigation/src/side_navigation_panel.styles.ts @@ -0,0 +1,74 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import { transparentize, type EuiThemeComputed } from '@elastic/eui'; +import { css, injectGlobal } from '@emotion/css'; + +const EUI_HEADER_HEIGHT = '93px'; +const PANEL_LEFT_OFFSET = '248px'; +const PANEL_WIDTH = '340px'; + +export const panelClass = 'solutionGroupedNavPanel'; + +export const SideNavPanelStyles = ( + euiTheme: EuiThemeComputed<{}>, + { $bottomOffset, $topOffset }: { $bottomOffset?: string; $topOffset?: string } = {} +) => { + // We need to add the banner height to the top space when the header banner is present + injectGlobal(` + body.kbnBody--hasHeaderBanner .${panelClass} { + top: calc(${EUI_HEADER_HEIGHT} + ${euiTheme.size.xl}); + } + `); + + return css` + position: fixed; + top: ${$topOffset ?? EUI_HEADER_HEIGHT}; + left: ${PANEL_LEFT_OFFSET}; + bottom: 0; + width: ${PANEL_WIDTH}; + height: inherit; + + // If the bottom bar is visible add padding to the navigation + ${$bottomOffset != null && + ` + height: inherit; + bottom: ${$bottomOffset}; + box-shadow: + // left + -${euiTheme.size.s} 0 ${euiTheme.size.s} -${euiTheme.size.s} rgb(0 0 0 / 15%), + // right + ${euiTheme.size.s} 0 ${euiTheme.size.s} -${euiTheme.size.s} rgb(0 0 0 / 15%), + // bottom inset to match timeline bar top shadow + inset 0 -6px ${euiTheme.size.xs} -${euiTheme.size.xs} rgb(0 0 0 / 15%); + `} + + .solutionGroupedNavPanelLink { + .solutionGroupedNavPanelLinkItem { + background-color: transparent; /* originally white, it prevents panel to remove the bottom inset box shadow */ + &:hover { + background-color: ${transparentize(euiTheme.colors.primary, 0.1)}; + } + dt { + color: ${euiTheme.colors.primaryText}; + } + dd { + color: ${euiTheme.colors.darkestShade}; + } + } + } + `; +}; + +export const SideNavTitleStyles = ( + euiTheme: EuiThemeComputed<{}>, + { $paddingTop = false }: { $paddingTop?: boolean } = {} +) => css` + padding-left: ${euiTheme.size.s}; + ${$paddingTop && `padding-top: ${euiTheme.size.s};`} +`; diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/solution_grouped_nav_panel.tsx b/packages/shared-ux/side_navigation/src/side_navigation_panel.tsx similarity index 65% rename from x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/solution_grouped_nav_panel.tsx rename to packages/shared-ux/side_navigation/src/side_navigation_panel.tsx index 25c1a9565b8b8..a3147037ba7e1 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/solution_grouped_nav_panel.tsx +++ b/packages/shared-ux/side_navigation/src/side_navigation_panel.tsx @@ -1,8 +1,9 @@ /* * 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. + * 2.0 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 or the Server + * Side Public License, v 1. */ import React, { Fragment, useCallback } from 'react'; @@ -18,25 +19,21 @@ import { EuiPanel, EuiPortal, EuiSpacer, + EuiTitle, EuiWindowEvent, keys, + useEuiTheme, useIsWithinMinBreakpoint, } from '@elastic/eui'; import classNames from 'classnames'; import { METRIC_TYPE } from '@kbn/analytics'; -import { - EuiPanelStyled, - EuiTitleStyled, - GlobalPanelStyle, - panelClass, -} from './solution_grouped_nav_panel.styles'; -import type { DefaultSideNavItem } from './types'; -import type { LinkCategories } from '../../../links/types'; -import { NavItemBetaBadge } from '../nav_item_beta_badge'; +import type { DefaultSideNavItem, LinkCategories } from './types'; +import { BetaBadge } from './beta_badge'; import { TELEMETRY_EVENT } from './telemetry/const'; import { useTelemetryContext } from './telemetry/telemetry_context'; +import { SideNavPanelStyles, panelClass, SideNavTitleStyles } from './side_navigation_panel.styles'; -export interface SolutionNavPanelProps { +export interface SideNavigationPanelProps { onClose: () => void; onOutsideClick: () => void; title: string; @@ -44,12 +41,12 @@ export interface SolutionNavPanelProps { categories?: LinkCategories; bottomOffset?: string; } -export interface SolutionNavPanelCategoriesProps { +export interface SideNavigationPanelCategoriesProps { categories: LinkCategories; items: DefaultSideNavItem[]; onClose: () => void; } -export interface SolutionNavPanelItemsProps { +export interface SideNavigationPanelItemsProps { items: DefaultSideNavItem[]; onClose: () => void; } @@ -57,7 +54,7 @@ export interface SolutionNavPanelItemsProps { /** * Renders the side navigation panel for secondary links */ -const SolutionNavPanelComponent: React.FC = ({ +const SideNavigationPanelComponent: React.FC = ({ onClose, onOutsideClick, title, @@ -65,11 +62,18 @@ const SolutionNavPanelComponent: React.FC = ({ items, bottomOffset, }) => { + const { euiTheme } = useEuiTheme(); const isLargerBreakpoint = useIsWithinMinBreakpoint('l'); - const panelClasses = classNames(panelClass, 'eui-yScroll'); // Only larger breakpoint needs to add bottom offset, other sizes should have full height - const bottomOffsetLargerBreakpoint = isLargerBreakpoint ? bottomOffset : undefined; + const $bottomOffset = isLargerBreakpoint ? bottomOffset : undefined; + const $topOffset = isLargerBreakpoint ? '48px' : undefined; // TODO: parametrize like bottomOffset + const hasShadow = !$bottomOffset; + + const sideNavPanelStyles = SideNavPanelStyles(euiTheme, { $bottomOffset, $topOffset }); + const sideNavTitleStyles = SideNavTitleStyles(euiTheme, { $paddingTop: true }); + const panelClasses = classNames(panelClass, 'eui-yScroll', sideNavPanelStyles); + const titleClasses = classNames(sideNavTitleStyles); // ESC key closes PanelNav const onKeyDown = useCallback( @@ -83,54 +87,58 @@ const SolutionNavPanelComponent: React.FC = ({ return ( <> - + {/* */} - - + {title} - + {categories ? ( - ) : ( - + )} - + ); }; -export const SolutionNavPanel = React.memo(SolutionNavPanelComponent); +export const SideNavigationPanel = React.memo(SideNavigationPanelComponent); -const SolutionNavPanelCategories: React.FC = ({ +const SideNavigationPanelCategories: React.FC = ({ categories, items, onClose, }) => { + const { euiTheme } = useEuiTheme(); + const sideNavTitleStyles = SideNavTitleStyles(euiTheme); + const titleClasses = classNames(sideNavTitleStyles); + const itemsMap = new Map(items.map((item) => [item.id, item])); return ( @@ -150,11 +158,11 @@ const SolutionNavPanelCategories: React.FC = ({ return ( - +

{label}

-
+ - +
); @@ -163,7 +171,7 @@ const SolutionNavPanelCategories: React.FC = ({ ); }; -const SolutionNavPanelItems: React.FC = ({ items, onClose }) => { +const SideNavigationPanelItems: React.FC = ({ items, onClose }) => { const panelLinkClassNames = classNames('solutionGroupedNavPanelLink'); const panelLinkItemClassNames = classNames('solutionGroupedNavPanelLinkItem'); const { tracker } = useTelemetryContext(); @@ -184,7 +192,7 @@ const SolutionNavPanelItems: React.FC = ({ items, on {label} - {isBeta && } + {isBeta && } {description} diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/solution_grouped_nav_panel.test.tsx b/packages/shared-ux/side_navigation/src/solution_grouped_nav_panel.test.tsx similarity index 75% rename from x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/solution_grouped_nav_panel.test.tsx rename to packages/shared-ux/side_navigation/src/solution_grouped_nav_panel.test.tsx index aeb7e21f21bab..bb9e9c5780477 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/solution_grouped_nav_panel.test.tsx +++ b/packages/shared-ux/side_navigation/src/solution_grouped_nav_panel.test.tsx @@ -1,23 +1,20 @@ /* * 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. + * 2.0 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 or the Server + * Side Public License, v 1. */ -import type { LinkCategories } from '../../../links'; import React from 'react'; import { render, waitFor } from '@testing-library/react'; -import { SecurityPageName } from '../../../../app/types'; -import { TestProviders } from '../../../mock'; -import type { SolutionNavPanelProps } from './solution_grouped_nav_panel'; -import { SolutionNavPanel } from './solution_grouped_nav_panel'; +import { SideNavigationPanel, type SideNavigationPanelProps } from './side_navigation_panel'; import type { DefaultSideNavItem } from './types'; -import { bottomNavOffset } from '../../../lib/helpers'; -import { BETA } from '@kbn/kubernetes-security-plugin/common/translations'; +import { BETA_LABEL } from './beta_badge'; import { TELEMETRY_EVENT } from './telemetry/const'; import { METRIC_TYPE } from '@kbn/analytics'; import { TelemetryContextProvider } from './telemetry/telemetry_context'; +import type { LinkCategories } from './types'; const mockUseIsWithinMinBreakpoint = jest.fn(() => true); jest.mock('@elastic/eui', () => { @@ -32,19 +29,19 @@ const mockTrack = jest.fn(); const mockItems: DefaultSideNavItem[] = [ { - id: SecurityPageName.hosts, + id: 'hosts', label: 'Hosts', href: '/hosts', description: 'Hosts description', }, { - id: SecurityPageName.network, + id: 'network', label: 'Network', href: '/network', description: 'Network description', }, { - id: SecurityPageName.kubernetes, + id: 'kubernetes', label: 'Kubernetes', href: '/kubernetes', description: 'Kubernetes description', @@ -57,7 +54,7 @@ const betaMockItemsCount = mockItems.filter((item) => item.isBeta).length; const mockCategories: LinkCategories = [ { label: 'HOSTS CATEGORY', - linkIds: [SecurityPageName.hosts], + linkIds: ['hosts'], }, { label: 'Empty category', @@ -65,15 +62,16 @@ const mockCategories: LinkCategories = [ }, ]; +const bottomNavOffset = '10px'; const PANEL_TITLE = 'test title'; const mockOnClose = jest.fn(); const mockOnOutsideClick = jest.fn(); -const renderNavPanel = (props: Partial = {}) => +const renderNavPanel = (props: Partial = {}) => render( <>
- = {}) => {...props} /> - , - { - wrapper: TestProviders, - } + ); describe('SolutionGroupedNav', () => { @@ -103,7 +98,7 @@ describe('SolutionGroupedNav', () => { expect(result.getByText(item.description)).toBeInTheDocument(); } }); - expect(result.queryAllByText(BETA).length).toBe(betaMockItemsCount); + expect(result.queryAllByText(BETA_LABEL).length).toBe(betaMockItemsCount); }); it('should only render categories with items', () => { @@ -121,12 +116,12 @@ describe('SolutionGroupedNav', () => { describe('links', () => { it('should contain correct href in links', () => { const result = renderNavPanel(); - expect( - result.getByTestId(`groupedNavPanelLink-${SecurityPageName.hosts}`).getAttribute('href') - ).toBe('/hosts'); - expect( - result.getByTestId(`groupedNavPanelLink-${SecurityPageName.network}`).getAttribute('href') - ).toBe('/network'); + expect(result.getByTestId(`groupedNavPanelLink-${'hosts'}`).getAttribute('href')).toBe( + '/hosts' + ); + expect(result.getByTestId(`groupedNavPanelLink-${'network'}`).getAttribute('href')).toBe( + '/network' + ); }); it('should call onClick callback if link clicked', () => { @@ -136,14 +131,14 @@ describe('SolutionGroupedNav', () => { const items = [ ...mockItems, { - id: SecurityPageName.users, + id: 'users', label: 'Users', href: '/users', onClick: mockOnClick, }, ]; const result = renderNavPanel({ items }); - result.getByTestId(`groupedNavPanelLink-${SecurityPageName.users}`).click(); + result.getByTestId(`groupedNavPanelLink-${'users'}`).click(); expect(mockOnClick).toHaveBeenCalled(); }); @@ -154,17 +149,17 @@ describe('SolutionGroupedNav', () => { const items = [ ...mockItems, { - id: SecurityPageName.users, + id: 'users', label: 'Users', href: '/users', onClick: mockOnClick, }, ]; const result = renderNavPanel({ items }); - result.getByTestId(`groupedNavPanelLink-${SecurityPageName.users}`).click(); + result.getByTestId(`groupedNavPanelLink-${'users'}`).click(); expect(mockTrack).toHaveBeenCalledWith( METRIC_TYPE.CLICK, - `${TELEMETRY_EVENT.GROUPED_NAVIGATION}${SecurityPageName.users}` + `${TELEMETRY_EVENT.GROUPED_NAVIGATION}${'users'}` ); }); }); @@ -188,7 +183,7 @@ describe('SolutionGroupedNav', () => { describe('close', () => { it('should call onClose callback if link clicked', () => { const result = renderNavPanel(); - result.getByTestId(`groupedNavPanelLink-${SecurityPageName.hosts}`).click(); + result.getByTestId(`groupedNavPanelLink-${'hosts'}`).click(); expect(mockOnClose).toHaveBeenCalled(); }); diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/telemetry/const.ts b/packages/shared-ux/side_navigation/src/telemetry/const.ts similarity index 63% rename from x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/telemetry/const.ts rename to packages/shared-ux/side_navigation/src/telemetry/const.ts index 3daaaf0e61fec..ff677b91e864e 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/telemetry/const.ts +++ b/packages/shared-ux/side_navigation/src/telemetry/const.ts @@ -1,9 +1,11 @@ /* * 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. + * 2.0 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 or the Server + * Side Public License, v 1. */ + export enum TELEMETRY_EVENT { GROUPED_NAVIGATION = 'grouped_navigation_', GROUPED_NAVIGATION_TOGGLE = 'grouped_navigation_toggle_', diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/telemetry/telemetry_context.tsx b/packages/shared-ux/side_navigation/src/telemetry/telemetry_context.tsx similarity index 81% rename from x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/telemetry/telemetry_context.tsx rename to packages/shared-ux/side_navigation/src/telemetry/telemetry_context.tsx index c7e97969ad31c..53a4b355dcb45 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/telemetry/telemetry_context.tsx +++ b/packages/shared-ux/side_navigation/src/telemetry/telemetry_context.tsx @@ -1,8 +1,9 @@ /* * 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. + * 2.0 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 or the Server + * Side Public License, v 1. */ import type { FC } from 'react'; diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/types.ts b/packages/shared-ux/side_navigation/src/types.ts similarity index 53% rename from x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/types.ts rename to packages/shared-ux/side_navigation/src/types.ts index 46280fb82e18c..3612f6d65c723 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/types.ts +++ b/packages/shared-ux/side_navigation/src/types.ts @@ -1,41 +1,47 @@ /* * 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. + * 2.0 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 or the Server + * Side Public License, v 1. */ import type React from 'react'; import type { UiCounterMetricType } from '@kbn/analytics'; -import type { SecurityPageName } from '../../../../app/types'; -import type { LinkCategories } from '../../../links/types'; - -export type Tracker = ( - type: UiCounterMetricType, - event: string | string[], - count?: number | undefined -) => void; -export interface DefaultSideNavItem { - id: SecurityPageName; +export interface DefaultSideNavItem { + id: T; label: string; href: string; onClick?: React.MouseEventHandler; description?: string; - items?: DefaultSideNavItem[]; - categories?: LinkCategories; + items?: Array>; + categories?: LinkCategories; isBeta?: boolean; betaOptions?: { text: string; }; } -export interface CustomSideNavItem { - id: string; +export interface CustomSideNavItem { + id: T; render: (isSelected: boolean) => React.ReactNode; } -export type SideNavItem = DefaultSideNavItem | CustomSideNavItem; +export type SideNavItem = DefaultSideNavItem | CustomSideNavItem; + +export interface LinkCategory { + label: string; + linkIds: readonly T[]; +} + +export type LinkCategories = Readonly>>; + +export type Tracker = ( + type: UiCounterMetricType, + event: string | string[], + count?: number | undefined +) => void; export const isCustomItem = (navItem: SideNavItem): navItem is CustomSideNavItem => 'render' in navItem; diff --git a/packages/shared-ux/side_navigation/tsconfig.json b/packages/shared-ux/side_navigation/tsconfig.json new file mode 100644 index 0000000000000..b33b000bb88ec --- /dev/null +++ b/packages/shared-ux/side_navigation/tsconfig.json @@ -0,0 +1,26 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node", + "react", + "@emotion/react/types/css-prop", + "@testing-library/jest-dom", + "@testing-library/react", + "@kbn/ambient-ui-types" + ] + }, + "include": [ + "**/*.ts", + "**/*.tsx", + ], + "kbn_references": [ + "@kbn/i18n", + "@kbn/test-jest-helpers", + ], + "exclude": [ + "target/**/*", + ] +} diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/nav_links.test.ts b/x-pack/plugins/security_solution/public/common/components/navigation/nav_links.test.ts deleted file mode 100644 index c44873414ca11..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/navigation/nav_links.test.ts +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { renderHook } from '@testing-library/react-hooks'; -import { SecurityPageName } from '../../../app/types'; -import type { AppLinkItems } from '../../links'; -import { TestProviders } from '../../mock'; -import { useAppNavLinks, useAppRootNavLink } from './nav_links'; -import type { NavLinkItem } from './types'; - -const mockNavLinks: AppLinkItems = [ - { - description: 'description', - id: SecurityPageName.administration, - links: [ - { - description: 'description 2', - id: SecurityPageName.endpoints, - links: [], - path: '/path_2', - title: 'title 2', - sideNavDisabled: true, - landingIcon: 'someicon', - landingImage: 'someimage', - skipUrlState: true, - }, - ], - path: '/path', - title: 'title', - }, -]; - -jest.mock('../../links', () => ({ - useAppLinks: () => mockNavLinks, -})); - -const renderUseAppNavLinks = () => - renderHook<{}, NavLinkItem[]>(() => useAppNavLinks(), { wrapper: TestProviders }); - -const renderUseAppRootNavLink = (id: SecurityPageName) => - renderHook<{ id: SecurityPageName }, NavLinkItem | undefined>(() => useAppRootNavLink(id), { - wrapper: TestProviders, - }); - -describe('useAppNavLinks', () => { - it('should return all nav links', () => { - const { result } = renderUseAppNavLinks(); - expect(result.current).toMatchInlineSnapshot(` - Array [ - Object { - "description": "description", - "id": "administration", - "links": Array [ - Object { - "description": "description 2", - "disabled": true, - "icon": "someicon", - "id": "endpoints", - "image": "someimage", - "skipUrlState": true, - "title": "title 2", - }, - ], - "title": "title", - }, - ] - `); - }); - - it('should return a root nav links', () => { - const { result } = renderUseAppRootNavLink(SecurityPageName.administration); - expect(result.current).toMatchInlineSnapshot(` - Object { - "description": "description", - "id": "administration", - "links": Array [ - Object { - "description": "description 2", - "disabled": true, - "icon": "someicon", - "id": "endpoints", - "image": "someimage", - "skipUrlState": true, - "title": "title 2", - }, - ], - "title": "title", - } - `); - }); -}); diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/nav_links.ts b/x-pack/plugins/security_solution/public/common/components/navigation/nav_links.ts deleted file mode 100644 index 5fff0a9649940..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/navigation/nav_links.ts +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { useMemo } from 'react'; -import { useAppLinks } from '../../links'; -import type { SecurityPageName } from '../../../app/types'; -import type { NavLinkItem } from './types'; -import type { AppLinkItems } from '../../links/types'; - -export const useAppNavLinks = (): NavLinkItem[] => { - const appLinks = useAppLinks(); - const navLinks = useMemo(() => formatNavLinkItems(appLinks), [appLinks]); - return navLinks; -}; - -export const useAppRootNavLink = (linkId: SecurityPageName): NavLinkItem | undefined => { - return useAppNavLinks().find(({ id }) => id === linkId); -}; - -const formatNavLinkItems = (appLinks: AppLinkItems): NavLinkItem[] => - appLinks.map((link) => ({ - id: link.id, - title: link.title, - ...(link.categories != null ? { categories: link.categories } : {}), - ...(link.description != null ? { description: link.description } : {}), - ...(link.sideNavDisabled === true ? { disabled: true } : {}), - ...(link.landingIcon != null ? { icon: link.landingIcon } : {}), - ...(link.landingImage != null ? { image: link.landingImage } : {}), - ...(link.skipUrlState != null ? { skipUrlState: link.skipUrlState } : {}), - ...(link.isBeta != null ? { isBeta: link.isBeta } : {}), - ...(link.betaOptions != null ? { betaOptions: link.betaOptions } : {}), - ...(link.links && link.links.length - ? { - links: formatNavLinkItems(link.links), - } - : {}), - })); diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/security_side_nav/index.ts b/x-pack/plugins/security_solution/public/common/components/navigation/security_side_nav/index.ts index a2c866e604e16..2f04fbdd4bf2e 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/security_side_nav/index.ts +++ b/x-pack/plugins/security_solution/public/common/components/navigation/security_side_nav/index.ts @@ -5,4 +5,6 @@ * 2.0. */ -export { SecuritySideNav } from './security_side_nav'; +import { SecuritySideNav } from './security_side_nav'; + +export { SecuritySideNav }; diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/security_side_nav/security_side_nav.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/security_side_nav/security_side_nav.tsx index dc4d7baafa33f..5cb8f8e98633a 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/security_side_nav/security_side_nav.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/security_side_nav/security_side_nav.tsx @@ -7,28 +7,27 @@ import React, { useMemo, useCallback } from 'react'; import { EuiHorizontalRule, EuiListGroupItem, EuiLoadingSpinner } from '@elastic/eui'; -import { SecurityPageName } from '../../../../app/types'; -import { getAncestorLinksInfo } from '../../../links'; -import { useRouteSpy } from '../../../utils/route/use_route_spy'; -import { SecuritySolutionLinkAnchor, useGetSecuritySolutionLinkProps } from '../../links'; -import { useAppNavLinks } from '../nav_links'; -import { SolutionGroupedNav } from '../solution_grouped_nav'; +import { SideNavigation } from '@kbn/shared-ux-side-navigation'; import type { CustomSideNavItem, DefaultSideNavItem, SideNavItem, -} from '../solution_grouped_nav/types'; -import type { NavLinkItem } from '../types'; +} from '@kbn/shared-ux-side-navigation'; +import { SecurityPageName } from '../../../../app/types'; +import { getAncestorLinksInfo, type NavigationLink } from '../../../links'; +import { useRouteSpy } from '../../../utils/route/use_route_spy'; +import { SecuritySolutionLinkAnchor, useGetSecuritySolutionLinkProps } from '../../links'; import { EuiIconLaunch } from './icons/launch'; import { useShowTimeline } from '../../../utils/timeline/use_show_timeline'; import { useIsPolicySettingsBarVisible } from '../../../../management/pages/policy/view/policy_hooks'; import { bottomNavOffset } from '../../../lib/helpers'; import { track } from '../../../lib/telemetry'; +import { useNavLinks } from '../../../links/nav_links'; const isFooterNavItem = (id: SecurityPageName) => id === SecurityPageName.landing || id === SecurityPageName.administration; -type FormatSideNavItems = (navItems: NavLinkItem) => SideNavItem; +type FormatSideNavItems = (navItems: NavigationLink) => SideNavItem; /** * Renders the navigation item for "Get Started" custom link @@ -56,14 +55,14 @@ const GetStartedCustomLinkComponent: React.FC<{ const GetStartedCustomLink = React.memo(GetStartedCustomLinkComponent); /** - * Returns a function to format generic `NavLinkItem` array to the `SideNavItem` type + * Returns a function to format generic `NavigationLink` array to the `SideNavItem` type */ const useFormatSideNavItem = (): FormatSideNavItems => { const getSecuritySolutionLinkProps = useGetSecuritySolutionLinkProps(); // adds href and onClick props const formatSideNavItem: FormatSideNavItems = useCallback( (navLinkItem) => { - const formatDefaultItem = (navItem: NavLinkItem): DefaultSideNavItem => ({ + const formatDefaultItem = (navItem: NavigationLink): DefaultSideNavItem => ({ id: navItem.id, label: navItem.title, ...getSecuritySolutionLinkProps({ @@ -91,7 +90,7 @@ const useFormatSideNavItem = (): FormatSideNavItems => { : {}), }); - const formatGetStartedItem = (navItem: NavLinkItem): CustomSideNavItem => ({ + const formatGetStartedItem = (navItem: NavigationLink): CustomSideNavItem => ({ id: navItem.id, render: (isSelected) => ( @@ -113,25 +112,26 @@ const useFormatSideNavItem = (): FormatSideNavItems => { * Returns the formatted `items` and `footerItems` to be rendered in the navigation */ const useSideNavItems = () => { - const appNavLinks = useAppNavLinks(); + const navLinks = useNavLinks(); const formatSideNavItem = useFormatSideNavItem(); const sideNavItems = useMemo(() => { const mainNavItems: SideNavItem[] = []; const footerNavItems: SideNavItem[] = []; - appNavLinks.forEach((appNavLink) => { - if (appNavLink.disabled) { + navLinks.forEach((navLink) => { + if (navLink.disabled) { return; } - if (isFooterNavItem(appNavLink.id)) { - footerNavItems.push(formatSideNavItem(appNavLink)); + if (isFooterNavItem(navLink.id)) { + footerNavItems.push(formatSideNavItem(navLink)); } else { - mainNavItems.push(formatSideNavItem(appNavLink)); + mainNavItems.push(formatSideNavItem(navLink)); } }); + return [mainNavItems, footerNavItems]; - }, [appNavLinks, formatSideNavItem]); + }, [navLinks, formatSideNavItem]); return sideNavItems; }; @@ -164,11 +164,11 @@ export const SecuritySideNav: React.FC = () => { } return ( - ); diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/icons/spaces.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/icons/spaces.tsx deleted file mode 100644 index 9307ebf713320..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/icons/spaces.tsx +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { SVGProps } from 'react'; -import React from 'react'; - -export const EuiIconSpaces: React.FC> = ({ ...props }) => ( - - - -); diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/index.ts b/x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/index.ts deleted file mode 100644 index 513976920a216..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export { SolutionGroupedNav } from './solution_grouped_nav'; diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/solution_grouped_nav.styles.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/solution_grouped_nav.styles.tsx deleted file mode 100644 index 79e77565bb6f3..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/solution_grouped_nav.styles.tsx +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { EuiListGroupItem, transparentize } from '@elastic/eui'; -import styled from 'styled-components'; - -export const EuiListGroupItemStyled = styled(EuiListGroupItem)` - font-weight: ${({ theme }) => theme.eui.euiFontWeightRegular}; - &.solutionGroupedNavItem--isPrimary * { - font-weight: ${({ theme }) => theme.eui.euiFontWeightBold}; - } - &:focus, - &:focus-within, - &:hover, - &.solutionGroupedNavItem--isActive { - background-color: ${({ theme }) => transparentize(theme.eui.euiColorPrimary, 0.1)}; - } - .solutionGroupedNavItemButton:focus, - .solutionGroupedNavItemButton:focus-within, - .solutionGroupedNavItemButton:hover { - transform: none; /* prevent translationY transform that causes misalignment within the list item */ - background-color: ${({ theme }) => transparentize(theme.eui.euiColorPrimary, 0.2)}; - } -`; diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/solution_grouped_nav_panel.styles.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/solution_grouped_nav_panel.styles.tsx deleted file mode 100644 index bf2a444742cb6..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/navigation/solution_grouped_nav/solution_grouped_nav_panel.styles.tsx +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { EuiPanel, EuiTitle, transparentize } from '@elastic/eui'; -import styled, { createGlobalStyle } from 'styled-components'; - -const EUI_HEADER_HEIGHT = '93px'; -const PANEL_LEFT_OFFSET = '249px'; -const PANEL_WIDTH = '340px'; - -export const panelClass = 'solutionGroupedNavPanel'; - -/** - * We need to target the body to check the header banner class which is a parent element - * of the portal panel. A global style is needed. - * (This rule should be moved to the Kibana core rendering styles if this navigation becomes generic) - */ -export const GlobalPanelStyle = createGlobalStyle<{ theme: { eui: { euiSizeXL: string } } }>` - body.kbnBody--hasHeaderBanner .${panelClass} { - top: calc(${EUI_HEADER_HEIGHT} + ${({ theme }) => theme.eui.euiSizeXL}); - } -`; - -export const EuiPanelStyled = styled(EuiPanel)<{ $bottomOffset?: string }>` - position: fixed; - top: ${EUI_HEADER_HEIGHT}; - left: ${PANEL_LEFT_OFFSET}; - bottom: 0; - width: ${PANEL_WIDTH}; - height: inherit; - - // If the bottom bar is visible add padding to the navigation - ${({ $bottomOffset, theme }) => - $bottomOffset != null && - ` - height: inherit; - bottom: ${$bottomOffset}; - box-shadow: - // left - -${theme.eui.euiSizeS} 0 ${theme.eui.euiSizeS} -${theme.eui.euiSizeS} rgb(0 0 0 / 15%), - // right - ${theme.eui.euiSizeS} 0 ${theme.eui.euiSizeS} -${theme.eui.euiSizeS} rgb(0 0 0 / 15%), - // bottom inset to match timeline bar top shadow - inset 0 -6px ${theme.eui.euiSizeXS} -${theme.eui.euiSizeXS} rgb(0 0 0 / 15%); - `} - - .solutionGroupedNavPanelLink { - .solutionGroupedNavPanelLinkItem { - background-color: transparent; /* originally white, it prevents panel to remove the bottom inset box shadow */ - &:hover { - background-color: ${({ theme }) => transparentize(theme.eui.euiColorPrimary, 0.1)}; - } - dt { - color: ${({ theme }) => theme.eui.euiColorPrimaryText}; - } - dd { - color: ${({ theme }) => theme.eui.euiColorDarkestShade}; - } - } - } -`; - -export const EuiTitleStyled = styled(EuiTitle)<{ $paddingTop?: boolean }>` - padding-left: ${({ theme }) => theme.eui.euiSizeS}; - ${({ $paddingTop = false, theme }) => $paddingTop && `padding-top: ${theme.eui.euiSizeS};`}; -`; diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/types.ts b/x-pack/plugins/security_solution/public/common/components/navigation/types.ts index b0190d40ced78..9a67f90bfc651 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/types.ts +++ b/x-pack/plugins/security_solution/public/common/components/navigation/types.ts @@ -5,9 +5,7 @@ * 2.0. */ -import type { IconType } from '@elastic/eui'; import { SecurityPageName } from '../../../app/types'; -import type { LinkCategories } from '../../links'; export type SearchNavTab = NavTab | { urlKey: UrlStateType; isDetailPage: boolean }; @@ -99,18 +97,3 @@ export interface SecuritySolutionTabNavigationProps { } export type NavigateToUrl = (url: string) => void; -export interface NavLinkItem { - categories?: LinkCategories; - description?: string; - disabled?: boolean; - icon?: IconType; - id: SecurityPageName; - links?: NavLinkItem[]; - image?: string; - title: string; - skipUrlState?: boolean; - isBeta?: boolean; - betaOptions?: { - text: string; - }; -} From a4d202f9ed89afb46ef8ab7468d85801fbfb9ac2 Mon Sep 17 00:00:00 2001 From: semd Date: Thu, 2 Mar 2023 19:32:33 +0100 Subject: [PATCH 21/58] nav links definition --- .../public/app/deep_links/index.ts | 4 +- .../public/common/links/links.ts | 96 +++++++------------ .../public/common/links/nav_links.test.ts | 57 +++++++++++ .../public/common/links/nav_links.ts | 43 +++++++++ .../public/common/links/types.ts | 16 ++++ .../landing_pages/pages/dashboards.test.tsx | 8 +- .../public/landing_pages/pages/dashboards.tsx | 4 +- .../public/landing_pages/pages/explore.tsx | 4 +- .../landing_pages/pages/manage.test.tsx | 8 +- .../public/landing_pages/pages/manage.tsx | 10 +- 10 files changed, 171 insertions(+), 79 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/common/links/nav_links.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/links/nav_links.ts diff --git a/x-pack/plugins/security_solution/public/app/deep_links/index.ts b/x-pack/plugins/security_solution/public/app/deep_links/index.ts index 9853c52502fb2..729df8039889e 100644 --- a/x-pack/plugins/security_solution/public/app/deep_links/index.ts +++ b/x-pack/plugins/security_solution/public/app/deep_links/index.ts @@ -77,7 +77,7 @@ import { USERS_PATH, } from '../../../common/constants'; import type { ExperimentalFeatures } from '../../../common/experimental_features'; -import { hasCapabilities, subscribeAppLinks } from '../../common/links'; +import { appLinks$, hasCapabilities } from '../../common/links'; import type { AppLinkItems } from '../../common/links/types'; export const FEATURE = { @@ -626,7 +626,7 @@ const formatDeepLinks = (appLinks: AppLinkItems): AppDeepLink[] => * Registers any change in appLinks to be updated in app deepLinks */ export const registerDeepLinksUpdater = (appUpdater$: Subject): Subscription => { - return subscribeAppLinks((appLinks) => { + return appLinks$.subscribe((appLinks) => { appUpdater$.next(() => ({ navLinkStatus: AppNavLinkStatus.hidden, // needed to prevent main security link to switch to visible after update deepLinks: formatDeepLinks(appLinks), diff --git a/x-pack/plugins/security_solution/public/common/links/links.ts b/x-pack/plugins/security_solution/public/common/links/links.ts index de30840d02d9d..be1f206e339b2 100644 --- a/x-pack/plugins/security_solution/public/common/links/links.ts +++ b/x-pack/plugins/security_solution/public/common/links/links.ts @@ -7,7 +7,8 @@ import type { Capabilities } from '@kbn/core/public'; import { get, isArray } from 'lodash'; -import { useEffect, useState } from 'react'; +import { useMemo } from 'react'; +import useObservable from 'react-use/lib/useObservable'; import { BehaviorSubject } from 'rxjs'; import type { SecurityPageName } from '../../../common/constants'; import type { @@ -20,72 +21,48 @@ import type { } from './types'; /** - * App links updater, it keeps the value of the app links in sync with all application. - * It can be updated using `updateAppLinks` or `excludeAppLink` - * Read it using `subscribeAppLinks` or `useAppLinks` hook. + * App links updater, it stores the `appLinkItems` recursive hierarchy and keeps + * the value of the app links in sync with all application components. + * It can be updated using `updateAppLinks`. + * Read it using subscription or `useAppLinks` hook. */ -const appLinksUpdater$ = new BehaviorSubject<{ - links: AppLinkItems; - normalizedLinks: NormalizedLinks; -}>({ - links: [], // stores the appLinkItems recursive hierarchy - normalizedLinks: {}, // stores a flatten normalized object for direct id access -}); +const appLinksUpdater$ = new BehaviorSubject([]); +// stores a flatten normalized appLinkItems object for internal direct id access +const normalizedAppLinksUpdater$ = new BehaviorSubject({}); -const getAppLinksValue = (): AppLinkItems => appLinksUpdater$.getValue().links; -const getNormalizedLinksValue = (): NormalizedLinks => appLinksUpdater$.getValue().normalizedLinks; +// AppLinks observable +export const appLinks$ = appLinksUpdater$.asObservable(); /** - * Subscribes to the updater to get the app links updates + * Updates the app links applying the filter by permissions */ -export const subscribeAppLinks = (onChange: (links: AppLinkItems) => void) => - appLinksUpdater$.subscribe(({ links }) => onChange(links)); +export const updateAppLinks = ( + appLinksToUpdate: AppLinkItems, + linksPermissions: LinksPermissions +) => { + const filteredAppLinks = getFilteredAppLinks(appLinksToUpdate, linksPermissions); + appLinksUpdater$.next(Object.freeze(filteredAppLinks)); + normalizedAppLinksUpdater$.next(Object.freeze(getNormalizedLinks(filteredAppLinks))); +}; /** * Hook to get the app links updated value */ -export const useAppLinks = (): AppLinkItems => { - const [appLinks, setAppLinks] = useState(getAppLinksValue); - - useEffect(() => { - const linksSubscription = subscribeAppLinks((newAppLinks) => { - setAppLinks(newAppLinks); - }); - return () => linksSubscription.unsubscribe(); - }, []); - - return appLinks; -}; +export const useAppLinks = (): AppLinkItems => + useObservable(appLinksUpdater$, appLinksUpdater$.getValue()); +/** + * Hook to get the normalized app links updated value + */ +export const useNormalizedAppLinks = (): NormalizedLinks => + useObservable(normalizedAppLinksUpdater$, normalizedAppLinksUpdater$.getValue()); /** * Hook to check if a link exists in the application links, * It can be used to know if a link access is authorized. */ export const useLinkExists = (id: SecurityPageName): boolean => { - const [linkExists, setLinkExists] = useState(!!getNormalizedLink(id)); - - useEffect(() => { - const linksSubscription = subscribeAppLinks(() => { - setLinkExists(!!getNormalizedLink(id)); - }); - return () => linksSubscription.unsubscribe(); - }, [id]); - - return linkExists; -}; - -/** - * Updates the app links applying the filter by permissions - */ -export const updateAppLinks = ( - appLinksToUpdate: AppLinkItems, - linksPermissions: LinksPermissions -) => { - const filteredAppLinks = getFilteredAppLinks(appLinksToUpdate, linksPermissions); - appLinksUpdater$.next({ - links: Object.freeze(filteredAppLinks), - normalizedLinks: Object.freeze(getNormalizedLinks(filteredAppLinks)), - }); + const normalizedLinks = useNormalizedAppLinks(); + return useMemo(() => !!normalizedLinks[id], [normalizedLinks, id]); }; /** @@ -128,6 +105,10 @@ export const needsUrlState = (id: SecurityPageName): boolean => { return !getNormalizedLink(id)?.skipUrlState; }; +export const getLinksWithHiddenTimeline = (): LinkInfo[] => { + return Object.values(normalizedAppLinksUpdater$.getValue()).filter((link) => link.hideTimeline); +}; + // Internal functions /** @@ -136,8 +117,8 @@ export const needsUrlState = (id: SecurityPageName): boolean => { const getNormalizedLinks = ( currentLinks: AppLinkItems, parentId?: SecurityPageName -): NormalizedLinks => { - return currentLinks.reduce((normalized, { links, ...currentLink }) => { +): NormalizedLinks => + currentLinks.reduce((normalized, { links, ...currentLink }) => { normalized[currentLink.id] = { ...currentLink, parentId, @@ -147,10 +128,9 @@ const getNormalizedLinks = ( } return normalized; }, {}); -}; const getNormalizedLink = (id: SecurityPageName): Readonly | undefined => - getNormalizedLinksValue()[id]; + normalizedAppLinksUpdater$.getValue()[id]; const getFilteredAppLinks = ( appLinkToFilter: AppLinkItems, @@ -226,7 +206,3 @@ const isLinkAllowed = ( } return true; }; - -export const getLinksWithHiddenTimeline = (): LinkInfo[] => { - return Object.values(getNormalizedLinksValue()).filter((link) => link.hideTimeline); -}; diff --git a/x-pack/plugins/security_solution/public/common/links/nav_links.test.ts b/x-pack/plugins/security_solution/public/common/links/nav_links.test.ts new file mode 100644 index 0000000000000..d8decac43a86a --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/links/nav_links.test.ts @@ -0,0 +1,57 @@ +/* + * 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 { AppLinkItems } from './types'; +import { formatNavigationLinks } from './nav_links'; +import { SecurityPageName } from '../../app/types'; + +const mockNavLinks: AppLinkItems = [ + { + description: 'description', + id: SecurityPageName.administration, + links: [ + { + description: 'description 2', + id: SecurityPageName.endpoints, + links: [], + path: '/path_2', + title: 'title 2', + sideNavDisabled: true, + landingIcon: 'someicon', + landingImage: 'someimage', + skipUrlState: true, + }, + ], + path: '/path', + title: 'title', + }, +]; + +describe('formatNavigationLinks', () => { + it('should format links', () => { + expect(formatNavigationLinks(mockNavLinks)).toMatchInlineSnapshot(` + Array [ + Object { + "description": "description", + "id": "administration", + "links": Array [ + Object { + "description": "description 2", + "disabled": true, + "icon": "someicon", + "id": "endpoints", + "image": "someimage", + "skipUrlState": true, + "title": "title 2", + }, + ], + "title": "title", + }, + ] + `); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/links/nav_links.ts b/x-pack/plugins/security_solution/public/common/links/nav_links.ts new file mode 100644 index 0000000000000..0882bdf233601 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/links/nav_links.ts @@ -0,0 +1,43 @@ +/* + * 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 useObservable from 'react-use/lib/useObservable'; +import { map } from 'rxjs'; +import { appLinks$ } from './links'; +import type { SecurityPageName } from '../../app/types'; +import type { AppLinkItems, NavigationLink } from './types'; + +export const formatNavigationLinks = (appLinks: AppLinkItems): NavigationLink[] => + appLinks.map((link) => ({ + id: link.id, + title: link.title, + ...(link.categories != null ? { categories: link.categories } : {}), + ...(link.description != null ? { description: link.description } : {}), + ...(link.sideNavDisabled === true ? { disabled: true } : {}), + ...(link.landingIcon != null ? { icon: link.landingIcon } : {}), + ...(link.landingImage != null ? { image: link.landingImage } : {}), + ...(link.skipUrlState != null ? { skipUrlState: link.skipUrlState } : {}), + ...(link.isBeta != null ? { isBeta: link.isBeta } : {}), + ...(link.betaOptions != null ? { betaOptions: link.betaOptions } : {}), + ...(link.links?.length && { + links: formatNavigationLinks(link.links), + }), + })); + +/** + * Navigation links observable based on Security AppLinks, + * It is used to generate the side navigation items + */ +export const navLinks$ = appLinks$.pipe(map(formatNavigationLinks)); + +export const useNavLinks = (): NavigationLink[] => { + return useObservable(navLinks$, []); +}; + +export const useRootNavLink = (linkId: SecurityPageName): NavigationLink | undefined => { + return useNavLinks().find(({ id }) => id === linkId); +}; diff --git a/x-pack/plugins/security_solution/public/common/links/types.ts b/x-pack/plugins/security_solution/public/common/links/types.ts index f9a2c5776262f..162415fb66a16 100644 --- a/x-pack/plugins/security_solution/public/common/links/types.ts +++ b/x-pack/plugins/security_solution/public/common/links/types.ts @@ -131,3 +131,19 @@ export type AppLinkItems = Readonly; export type LinkInfo = Omit; export type NormalizedLink = LinkInfo & { parentId?: SecurityPageName }; export type NormalizedLinks = Partial>; + +export interface NavigationLink { + categories?: LinkCategories; + description?: string; + disabled?: boolean; + icon?: IconType; + id: SecurityPageName; + links?: NavigationLink[]; + image?: string; + title: string; + skipUrlState?: boolean; + isBeta?: boolean; + betaOptions?: { + text: string; + }; +} diff --git a/x-pack/plugins/security_solution/public/landing_pages/pages/dashboards.test.tsx b/x-pack/plugins/security_solution/public/landing_pages/pages/dashboards.test.tsx index 97eb89695bc98..112786b861df3 100644 --- a/x-pack/plugins/security_solution/public/landing_pages/pages/dashboards.test.tsx +++ b/x-pack/plugins/security_solution/public/landing_pages/pages/dashboards.test.tsx @@ -10,9 +10,9 @@ import React from 'react'; import { SecurityPageName } from '../../app/types'; import { TestProviders } from '../../common/mock'; import { DashboardsLandingPage } from './dashboards'; -import type { NavLinkItem } from '../../common/components/navigation/types'; import { useCapabilities } from '../../common/lib/kibana'; import * as telemetry from '../../common/lib/telemetry'; +import type { NavigationLink } from '../../common/links'; jest.mock('../../common/lib/kibana'); jest.mock('../../common/utils/route/spy_routes', () => ({ SpyRoute: () => null })); @@ -28,7 +28,7 @@ const spyTrack = jest.spyOn(telemetry, 'track'); const OVERVIEW_ITEM_LABEL = 'Overview'; const DETECTION_RESPONSE_ITEM_LABEL = 'Detection & Response'; -const APP_DASHBOARD_LINKS: NavLinkItem = { +const APP_DASHBOARD_LINKS: NavigationLink = { id: SecurityPageName.dashboardsLanding, title: 'Dashboards', links: [ @@ -49,8 +49,8 @@ const APP_DASHBOARD_LINKS: NavLinkItem = { const URL = '/path/to/dashboards'; const mockAppManageLink = jest.fn(() => APP_DASHBOARD_LINKS); -jest.mock('../../common/components/navigation/nav_links', () => ({ - useAppRootNavLink: () => mockAppManageLink(), +jest.mock('../../common/links/nav_links', () => ({ + useRootNavLink: () => mockAppManageLink(), })); const CREATE_DASHBOARD_LINK = { isLoading: false, url: URL }; diff --git a/x-pack/plugins/security_solution/public/landing_pages/pages/dashboards.tsx b/x-pack/plugins/security_solution/public/landing_pages/pages/dashboards.tsx index ee97955b659ab..1bd7b69adcb72 100644 --- a/x-pack/plugins/security_solution/public/landing_pages/pages/dashboards.tsx +++ b/x-pack/plugins/security_solution/public/landing_pages/pages/dashboards.tsx @@ -18,7 +18,7 @@ import { LEGACY_DASHBOARD_APP_ID } from '@kbn/dashboard-plugin/public'; import { SecurityPageName } from '../../app/types'; import { DashboardsTable } from '../../common/components/dashboards/dashboards_table'; import { Title } from '../../common/components/header_page/title'; -import { useAppRootNavLink } from '../../common/components/navigation/nav_links'; +import { useRootNavLink } from '../../common/links/nav_links'; import { SecuritySolutionPageWrapper } from '../../common/components/page_wrapper'; import { useCreateSecurityDashboardLink } from '../../common/containers/dashboards/use_create_security_dashboard_link'; import { useCapabilities, useNavigateTo } from '../../common/lib/kibana'; @@ -60,7 +60,7 @@ const Header: React.FC<{ canCreateDashboard: boolean }> = ({ canCreateDashboard }; export const DashboardsLandingPage = () => { - const dashboardLinks = useAppRootNavLink(SecurityPageName.dashboardsLanding)?.links ?? []; + const dashboardLinks = useRootNavLink(SecurityPageName.dashboardsLanding)?.links ?? []; const { show: canReadDashboard, createNew: canCreateDashboard } = useCapabilities(LEGACY_DASHBOARD_APP_ID); diff --git a/x-pack/plugins/security_solution/public/landing_pages/pages/explore.tsx b/x-pack/plugins/security_solution/public/landing_pages/pages/explore.tsx index 17a0d7569b965..26dd3009e1d03 100644 --- a/x-pack/plugins/security_solution/public/landing_pages/pages/explore.tsx +++ b/x-pack/plugins/security_solution/public/landing_pages/pages/explore.tsx @@ -7,14 +7,14 @@ import React from 'react'; import { SecurityPageName } from '../../app/types'; import { HeaderPage } from '../../common/components/header_page'; -import { useAppRootNavLink } from '../../common/components/navigation/nav_links'; +import { useRootNavLink } from '../../common/links/nav_links'; import { SecuritySolutionPageWrapper } from '../../common/components/page_wrapper'; import { SpyRoute } from '../../common/utils/route/spy_routes'; import { LandingLinksImages } from '../components/landing_links_images'; import { EXPLORE_PAGE_TITLE } from './translations'; export const ExploreLandingPage = () => { - const exploreLinks = useAppRootNavLink(SecurityPageName.exploreLanding)?.links ?? []; + const exploreLinks = useRootNavLink(SecurityPageName.exploreLanding)?.links ?? []; return ( diff --git a/x-pack/plugins/security_solution/public/landing_pages/pages/manage.test.tsx b/x-pack/plugins/security_solution/public/landing_pages/pages/manage.test.tsx index 67eb06b60cca6..e900fad75546a 100644 --- a/x-pack/plugins/security_solution/public/landing_pages/pages/manage.test.tsx +++ b/x-pack/plugins/security_solution/public/landing_pages/pages/manage.test.tsx @@ -10,14 +10,14 @@ import React from 'react'; import { SecurityPageName } from '../../app/types'; import { TestProviders } from '../../common/mock'; import { ManagementCategories } from './manage'; -import type { NavLinkItem } from '../../common/components/navigation/types'; +import type { NavigationLink } from '../../common/links'; const RULES_ITEM_LABEL = 'elastic rules!'; const EXCEPTIONS_ITEM_LABEL = 'exceptional!'; const CATEGORY_1_LABEL = 'first tests category'; const CATEGORY_2_LABEL = 'second tests category'; -const defaultAppManageLink: NavLinkItem = { +const defaultAppManageLink: NavigationLink = { id: SecurityPageName.administration, title: 'admin', categories: [ @@ -47,8 +47,8 @@ const defaultAppManageLink: NavLinkItem = { }; const mockAppManageLink = jest.fn(() => defaultAppManageLink); -jest.mock('../../common/components/navigation/nav_links', () => ({ - useAppRootNavLink: () => mockAppManageLink(), +jest.mock('../../common/links/nav_links', () => ({ + useRootNavLink: () => mockAppManageLink(), })); describe('ManagementCategories', () => { diff --git a/x-pack/plugins/security_solution/public/landing_pages/pages/manage.tsx b/x-pack/plugins/security_solution/public/landing_pages/pages/manage.tsx index cb77921a0b673..37e2391801cac 100644 --- a/x-pack/plugins/security_solution/public/landing_pages/pages/manage.tsx +++ b/x-pack/plugins/security_solution/public/landing_pages/pages/manage.tsx @@ -10,8 +10,8 @@ import styled from 'styled-components'; import { SecurityPageName } from '../../app/types'; import { HeaderPage } from '../../common/components/header_page'; -import { useAppRootNavLink } from '../../common/components/navigation/nav_links'; -import type { NavLinkItem } from '../../common/components/navigation/types'; +import { useRootNavLink } from '../../common/links/nav_links'; +import type { NavigationLink } from '../../common/links'; import { SecuritySolutionPageWrapper } from '../../common/components/page_wrapper'; import { SpyRoute } from '../../common/utils/route/spy_routes'; import { LandingLinksIcons } from '../components/landing_links_icons'; @@ -30,14 +30,14 @@ const StyledEuiHorizontalRule = styled(EuiHorizontalRule)` margin-bottom: ${({ theme }) => theme.eui.euiSizeL}; `; -type ManagementCategories = Array<{ label: string; links: NavLinkItem[] }>; +type ManagementCategories = Array<{ label: string; links: NavigationLink[] }>; const useManagementCategories = (): ManagementCategories => { - const { links = [], categories = [] } = useAppRootNavLink(SecurityPageName.administration) ?? {}; + const { links = [], categories = [] } = useRootNavLink(SecurityPageName.administration) ?? {}; const manageLinksById = Object.fromEntries(links.map((link) => [link.id, link])); return categories.reduce((acc, { label, linkIds }) => { - const linksItem = linkIds.reduce((linksAcc, linkId) => { + const linksItem = linkIds.reduce((linksAcc, linkId) => { if (manageLinksById[linkId]) { linksAcc.push(manageLinksById[linkId]); } From f0515bddf1508471e8adfd9f83a155986c1255ca Mon Sep 17 00:00:00 2001 From: semd Date: Thu, 2 Mar 2023 19:33:05 +0100 Subject: [PATCH 22/58] integrate navigation to serverless security --- package.json | 1 + .../src/ui/solution/header.tsx | 2 ++ tsconfig.base.json | 2 ++ x-pack/plugins/security_solution/common/index.ts | 4 +++- x-pack/plugins/security_solution/public/plugin.tsx | 7 +++++-- x-pack/plugins/security_solution/public/types.ts | 8 +++++--- x-pack/plugins/serverless_security/kibana.jsonc | 3 ++- .../plugins/serverless_security/public/plugin.tsx | 13 +++++++++++-- yarn.lock | 4 ++++ 9 files changed, 35 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 5462bb7056554..0bfce2a2b7337 100644 --- a/package.json +++ b/package.json @@ -604,6 +604,7 @@ "@kbn/shared-ux-router": "link:packages/shared-ux/router/impl", "@kbn/shared-ux-router-mocks": "link:packages/shared-ux/router/mocks", "@kbn/shared-ux-router-types": "link:packages/shared-ux/router/types", + "@kbn/shared-ux-side-navigation": "link:packages/shared-ux/side_navigation", "@kbn/shared-ux-storybook-config": "link:packages/shared-ux/storybook/config", "@kbn/shared-ux-storybook-mock": "link:packages/shared-ux/storybook/mock", "@kbn/shared-ux-utility": "link:packages/kbn-shared-ux-utility", diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/solution/header.tsx b/packages/core/chrome/core-chrome-browser-internal/src/ui/solution/header.tsx index 0aef9755994d8..29e8c713265e9 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/ui/solution/header.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/solution/header.tsx @@ -89,6 +89,8 @@ export const SolutionHeader = ({ css={{ borderInlineEndWidth: 1, background: euiTheme.colors.darkestShade, + display: 'flex', + flexDirection: 'row', }} isOpen={true} onClose={() => {}} diff --git a/tsconfig.base.json b/tsconfig.base.json index 21db47056353d..6afda4ae50fcd 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1198,6 +1198,8 @@ "@kbn/shared-ux-router-mocks/*": ["packages/shared-ux/router/mocks/*"], "@kbn/shared-ux-router-types": ["packages/shared-ux/router/types"], "@kbn/shared-ux-router-types/*": ["packages/shared-ux/router/types/*"], + "@kbn/shared-ux-side-navigation": ["packages/shared-ux/side_navigation"], + "@kbn/shared-ux-side-navigation/*": ["packages/shared-ux/side_navigation/*"], "@kbn/shared-ux-storybook-config": ["packages/shared-ux/storybook/config"], "@kbn/shared-ux-storybook-config/*": ["packages/shared-ux/storybook/config/*"], "@kbn/shared-ux-storybook-mock": ["packages/shared-ux/storybook/mock"], diff --git a/x-pack/plugins/security_solution/common/index.ts b/x-pack/plugins/security_solution/common/index.ts index 148d6a6a5c007..7aab77e56bba8 100644 --- a/x-pack/plugins/security_solution/common/index.ts +++ b/x-pack/plugins/security_solution/common/index.ts @@ -5,9 +5,11 @@ * 2.0. */ +import { APP_UI_ID, SecurityPageName } from './constants'; + // TODO(jbudz): should be removed when upgrading to TS@4.8 // this is a skip for the errors created when typechecking with isolatedModules -export {}; +export { APP_UI_ID, SecurityPageName }; // Careful of exporting anything from this file as any file(s) you export here will cause your page bundle size to increase. // If you're using functions/types/etc... internally it's best to import directly from their paths than expose the functions/types/etc... here. diff --git a/x-pack/plugins/security_solution/public/plugin.tsx b/x-pack/plugins/security_solution/public/plugin.tsx index 87bd7e72223a4..82eb7ef68a1c3 100644 --- a/x-pack/plugins/security_solution/public/plugin.tsx +++ b/x-pack/plugins/security_solution/public/plugin.tsx @@ -45,6 +45,7 @@ import { import { getDeepLinks, registerDeepLinksUpdater } from './app/deep_links'; import type { LinksPermissions } from './common/links'; import { updateAppLinks } from './common/links'; +import { navLinks$ } from './common/links/nav_links'; import { licenseService } from './common/hooks/use_license'; import type { SecuritySolutionUiConfigType } from './common/types'; import { ExperimentalFeaturesService } from './common/experimental_features_service'; @@ -241,7 +242,7 @@ export class Plugin implements IPlugin void; } -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface PluginStart {} +export interface PluginStart { + navLinks$: Observable; +} export interface AppObservableLibs { kibana: CoreStart; diff --git a/x-pack/plugins/serverless_security/kibana.jsonc b/x-pack/plugins/serverless_security/kibana.jsonc index 71405b0e47707..99def198e6b46 100644 --- a/x-pack/plugins/serverless_security/kibana.jsonc +++ b/x-pack/plugins/serverless_security/kibana.jsonc @@ -15,7 +15,8 @@ "requiredPlugins": [ "serverless", "security", - "securitySolution" + "securitySolution", + "kibanaReact" ], "optionalPlugins": [], "requiredBundles": [] diff --git a/x-pack/plugins/serverless_security/public/plugin.tsx b/x-pack/plugins/serverless_security/public/plugin.tsx index f0f1fd2e0e9f0..2d163df117c05 100644 --- a/x-pack/plugins/serverless_security/public/plugin.tsx +++ b/x-pack/plugins/serverless_security/public/plugin.tsx @@ -13,6 +13,8 @@ import { ServerlessSecurityPluginSetupDependencies, ServerlessSecurityPluginStartDependencies, } from './types'; +import { SecuritySideNavigation } from './components/side_navigation'; +import { getKibanaServicesProvider } from './services'; export class ServerlessSecurityPlugin implements @@ -32,10 +34,17 @@ export class ServerlessSecurityPlugin } public start( - _core: CoreStart, + core: CoreStart, startDeps: ServerlessSecurityPluginStartDependencies ): ServerlessSecurityPluginStart { - startDeps.serverless.setServerlessNavigation(

Security

); + const KibanaServicesProvider = getKibanaServicesProvider(core, startDeps); + + startDeps.serverless.setServerlessNavigation( + + + + ); + return {}; } diff --git a/yarn.lock b/yarn.lock index 31bcb2534defa..e1ee63c044f94 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5133,6 +5133,10 @@ version "0.0.0" uid "" +"@kbn/shared-ux-side-navigation@link:packages/shared-ux/side_navigation": + version "0.0.0" + uid "" + "@kbn/shared-ux-storybook-config@link:packages/shared-ux/storybook/config": version "0.0.0" uid "" From e723dea4903f4ea105009715c7baa592dbe7a1db Mon Sep 17 00:00:00 2001 From: semd Date: Thu, 2 Mar 2023 19:33:41 +0100 Subject: [PATCH 23/58] integrate navigation to serverless security --- .../side_navigation/get_started_nav_item.tsx | 45 +++++++ .../components/side_navigation/index.ts | 7 + .../side_navigation/side_navigation.tsx | 44 +++++++ .../public/hooks/use_link_props.ts | 65 +++++++++ .../public/hooks/use_side_nav_items.ts | 123 ++++++++++++++++++ .../serverless_security/public/services.tsx | 34 +++++ 6 files changed, 318 insertions(+) create mode 100644 x-pack/plugins/serverless_security/public/components/side_navigation/get_started_nav_item.tsx create mode 100644 x-pack/plugins/serverless_security/public/components/side_navigation/index.ts create mode 100644 x-pack/plugins/serverless_security/public/components/side_navigation/side_navigation.tsx create mode 100644 x-pack/plugins/serverless_security/public/hooks/use_link_props.ts create mode 100644 x-pack/plugins/serverless_security/public/hooks/use_side_nav_items.ts create mode 100644 x-pack/plugins/serverless_security/public/services.tsx diff --git a/x-pack/plugins/serverless_security/public/components/side_navigation/get_started_nav_item.tsx b/x-pack/plugins/serverless_security/public/components/side_navigation/get_started_nav_item.tsx new file mode 100644 index 0000000000000..f65f0c6288354 --- /dev/null +++ b/x-pack/plugins/serverless_security/public/components/side_navigation/get_started_nav_item.tsx @@ -0,0 +1,45 @@ +/* + * 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 React from 'react'; +import { SecurityPageName } from '@kbn/security-solution-plugin/common'; +import { EuiLink, EuiListGroupItem, EuiHorizontalRule } from '@elastic/eui'; +import type { NavigationLink } from '@kbn/security-solution-plugin/public/common/links'; +import type { CustomSideNavItem } from '@kbn/shared-ux-side-navigation'; +import { useLinkProps } from '../../hooks/use_link_props'; + +/** + * Renders the navigation item for "Get Started" custom link + */ +const GetStartedCustomLinkComponent: React.FC<{ + isSelected: boolean; + title: string; +}> = ({ isSelected, title }) => { + const linkProps = useLinkProps({ deepLinkId: SecurityPageName.landing }); + return ( + + + + + ); +}; +export const GetStartedCustomLink = React.memo(GetStartedCustomLinkComponent); + +export const formatGetStartedItem = (navItem: NavigationLink): CustomSideNavItem => ({ + id: navItem.id, + render: (isSelected: boolean) => ( + + ), +}); diff --git a/x-pack/plugins/serverless_security/public/components/side_navigation/index.ts b/x-pack/plugins/serverless_security/public/components/side_navigation/index.ts new file mode 100644 index 0000000000000..6ac2942940587 --- /dev/null +++ b/x-pack/plugins/serverless_security/public/components/side_navigation/index.ts @@ -0,0 +1,7 @@ +/* + * 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. + */ +export { SecuritySideNavigation } from './side_navigation'; diff --git a/x-pack/plugins/serverless_security/public/components/side_navigation/side_navigation.tsx b/x-pack/plugins/serverless_security/public/components/side_navigation/side_navigation.tsx new file mode 100644 index 0000000000000..bc4be049e1aa9 --- /dev/null +++ b/x-pack/plugins/serverless_security/public/components/side_navigation/side_navigation.tsx @@ -0,0 +1,44 @@ +/* + * 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 React from 'react'; +import { i18n } from '@kbn/i18n'; +import { SolutionNav } from '@kbn/shared-ux-page-solution-nav'; +import { EuiLoadingSpinner } from '@elastic/eui'; +import { SideNavigation } from '@kbn/shared-ux-side-navigation'; +import { useSideNavItems, useSideNavSelectedId } from '../../hooks/use_side_nav_items'; + +const translatedNavTitle = i18n.translate('xpack.securitySolution.navigation.mainLabel', { + defaultMessage: 'Security', +}); + +export const SecuritySideNavigation: React.FC = () => { + const [items, footerItems] = useSideNavItems(); + const selectedId = useSideNavSelectedId(); + + if (items.length === 0 && footerItems.length === 0) { + return ; + } + + return ( + + } + closeFlyoutButtonPosition={'inside'} + /> + ); +}; diff --git a/x-pack/plugins/serverless_security/public/hooks/use_link_props.ts b/x-pack/plugins/serverless_security/public/hooks/use_link_props.ts new file mode 100644 index 0000000000000..49d525cf51d66 --- /dev/null +++ b/x-pack/plugins/serverless_security/public/hooks/use_link_props.ts @@ -0,0 +1,65 @@ +/* + * 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 { APP_UI_ID, type SecurityPageName } from '@kbn/security-solution-plugin/common'; +import { useMemo, useCallback, type MouseEventHandler, type MouseEvent } from 'react'; +import { useKibana, type Services } from '../services'; + +interface LinkProps { + onClick: MouseEventHandler; + href: string; +} + +interface GetLinkPropsParams { + deepLinkId: SecurityPageName; + path?: string; + appId?: string; + onClick?: MouseEventHandler; +} + +type GetLinkProps = (params: GetLinkPropsParams) => LinkProps; + +export const useLinkProps: GetLinkProps = (props) => { + const { application } = useKibana().services; + return useMemo(() => getLinkProps({ ...props, application }), [application, props]); +}; + +export const useGetLinkProps: () => GetLinkProps = () => { + const { application } = useKibana().services; + return useCallback( + (props) => getLinkProps({ ...props, application }), + [application] + ); +}; + +const getLinkProps = ({ + deepLinkId, + path, + onClick: onClickProps, + appId = APP_UI_ID, + application, +}: GetLinkPropsParams & { application: Services['application'] }): LinkProps => { + const { getUrlForApp, navigateToUrl } = application; + const url = getUrlForApp(appId, { deepLinkId, path }); + return { + href: url, + onClick: (ev) => { + if (isModifiedEvent(ev)) { + return; + } + + ev.preventDefault(); + navigateToUrl(url); + if (onClickProps) { + onClickProps(ev); + } + }, + }; +}; + +const isModifiedEvent = (event: MouseEvent) => + event.metaKey || event.altKey || event.ctrlKey || event.shiftKey; diff --git a/x-pack/plugins/serverless_security/public/hooks/use_side_nav_items.ts b/x-pack/plugins/serverless_security/public/hooks/use_side_nav_items.ts new file mode 100644 index 0000000000000..557b2c25ea397 --- /dev/null +++ b/x-pack/plugins/serverless_security/public/hooks/use_side_nav_items.ts @@ -0,0 +1,123 @@ +/* + * 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 { useCallback, useMemo } from 'react'; +import type { NavigationLink } from '@kbn/security-solution-plugin/public/common/links'; +import { APP_UI_ID, SecurityPageName } from '@kbn/security-solution-plugin/common'; +import type { SideNavItem, DefaultSideNavItem } from '@kbn/shared-ux-side-navigation'; +import useObservable from 'react-use/lib/useObservable'; +import { matchPath, useLocation } from 'react-router-dom'; +import { useKibana } from '../services'; +import { useGetLinkProps } from './use_link_props'; +import { formatGetStartedItem } from '../components/side_navigation/get_started_nav_item'; + +const isFooterNavItem = (id: SecurityPageName) => + id === SecurityPageName.landing || id === SecurityPageName.administration; + +const isGetStartedNavItem = (id: SecurityPageName) => id === SecurityPageName.landing; + +const useFindItemsByPath = () => { + const { getUrlForApp } = useKibana().services.application; + + // DFS for the item that matches the path, returns all item hierarchy when found + const findItemsByPath = useCallback( + (navLinks: NavigationLink[], pathname: string): NavigationLink[] => { + for (const navLink of navLinks) { + if (navLink.links?.length) { + const found = findItemsByPath(navLink.links, pathname); + if (found.length) { + found.unshift(navLink); + return found; + } + } + const path = getUrlForApp(APP_UI_ID, { deepLinkId: navLink.id }); + if (matchPath(pathname, { path })) { + return [navLink]; + } + } + return []; + }, + [getUrlForApp] + ); + + return findItemsByPath; +}; + +/** + * Returns the formatted `items` and `footerItems` to be rendered in the navigation + */ +export const useSideNavItems = () => { + const { securitySolution } = useKibana().services; + const navLinks = useObservable(securitySolution.navLinks$, []); + const getLinkProps = useGetLinkProps(); + + const formatDefaultItem = useCallback( + (navItem: NavigationLink): DefaultSideNavItem => ({ + id: navItem.id, + label: navItem.title, + ...getLinkProps({ deepLinkId: navItem.id }), + ...(navItem.categories?.length && { categories: navItem.categories }), + ...(navItem.links?.length && { + items: navItem.links.reduce((acc, current) => { + if (!current.disabled) { + acc.push({ + id: current.id, + label: current.title, + description: current.description, + isBeta: current.isBeta, + betaOptions: current.betaOptions, + ...getLinkProps({ deepLinkId: current.id }), + }); + } + return acc; + }, []), + }), + }), + [getLinkProps] + ); + + const sideNavItems = useMemo(() => { + const mainNavItems: SideNavItem[] = []; + const footerNavItems: SideNavItem[] = []; + navLinks.forEach((navLink) => { + if (navLink.disabled) { + return; + } + const sideNavItem = isGetStartedNavItem(navLink.id) + ? formatGetStartedItem(navLink) + : formatDefaultItem(navLink); + + if (isFooterNavItem(navLink.id)) { + footerNavItems.push(sideNavItem); + } else { + mainNavItems.push(sideNavItem); + } + }); + + return [mainNavItems, footerNavItems]; + }, [navLinks, formatDefaultItem]); + + return sideNavItems; +}; + +/** + * Returns the selected item id, which is the root item in the links hierarchy + */ +export const useSideNavSelectedId = (): string => { + const { pathname } = useLocation(); // TODO: solve (not) updating problem + const { securitySolution } = useKibana().services; + const navLinks = useObservable(securitySolution.navLinks$, []); + + const findItemsByPath = useFindItemsByPath(); + + const selectedId: string = useMemo(() => { + const [rootNavItem] = findItemsByPath(navLinks, pathname); + return rootNavItem?.id ?? ''; + }, [navLinks, findItemsByPath, pathname]); + + return selectedId; +}; diff --git a/x-pack/plugins/serverless_security/public/services.tsx b/x-pack/plugins/serverless_security/public/services.tsx new file mode 100644 index 0000000000000..56a2320948185 --- /dev/null +++ b/x-pack/plugins/serverless_security/public/services.tsx @@ -0,0 +1,34 @@ +/* + * 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 { CoreStart } from '@kbn/core/public'; +import React from 'react'; +import { + KibanaContextProvider, + useKibana as useKibanaReact, +} from '@kbn/kibana-react-plugin/public'; +import { BrowserRouter } from 'react-router-dom'; +import { ServerlessSecurityPluginStartDependencies } from './types'; + +export type Services = CoreStart & ServerlessSecurityPluginStartDependencies; + +// TODO: Replace by with global history +export const getKibanaServicesProvider = ( + core: CoreStart, + pluginsStart: ServerlessSecurityPluginStartDependencies +): React.FC => { + const services: Services = { ...core, ...pluginsStart }; + return ({ children }) => { + return ( + + {children} + + ); + }; +}; + +export const useKibana = () => useKibanaReact(); From 8e34c7f1c93c62d7dca36c64a73d9bc08a2b16a9 Mon Sep 17 00:00:00 2001 From: semd Date: Thu, 2 Mar 2023 20:20:39 +0100 Subject: [PATCH 24/58] discover item for testing --- .../public/common/links/app_links.ts | 6 +++--- .../public/hooks/use_side_nav_items.ts | 11 +++++++++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/links/app_links.ts b/x-pack/plugins/security_solution/public/common/links/app_links.ts index c00fa87f878da..92c984a4b925f 100644 --- a/x-pack/plugins/security_solution/public/common/links/app_links.ts +++ b/x-pack/plugins/security_solution/public/common/links/app_links.ts @@ -18,16 +18,16 @@ import type { StartPlugins } from '../../types'; const casesLinks = getCasesLinkItems(); -export const links = Object.freeze([ +export const links: AppLinkItems = Object.freeze([ dashboardsLandingLinks, detectionLinks, cloudSecurityPostureRootLinks, timelinesLinks, casesLinks, threatHuntingLandingLinks, + indicatorsLinks, gettingStartedLinks, managementLinks, - indicatorsLinks, ]); export const getFilteredLinks = async ( @@ -43,8 +43,8 @@ export const getFilteredLinks = async ( timelinesLinks, casesLinks, threatHuntingLandingLinks, + indicatorsLinks, gettingStartedLinks, managementFilteredLinks, - indicatorsLinks, ]); }; diff --git a/x-pack/plugins/serverless_security/public/hooks/use_side_nav_items.ts b/x-pack/plugins/serverless_security/public/hooks/use_side_nav_items.ts index 557b2c25ea397..56666a79c5201 100644 --- a/x-pack/plugins/serverless_security/public/hooks/use_side_nav_items.ts +++ b/x-pack/plugins/serverless_security/public/hooks/use_side_nav_items.ts @@ -51,7 +51,7 @@ const useFindItemsByPath = () => { * Returns the formatted `items` and `footerItems` to be rendered in the navigation */ export const useSideNavItems = () => { - const { securitySolution } = useKibana().services; + const { securitySolution, http } = useKibana().services; const navLinks = useObservable(securitySolution.navLinks$, []); const getLinkProps = useGetLinkProps(); @@ -98,8 +98,15 @@ export const useSideNavItems = () => { } }); + // TODO: remove, just extra external item for testing + mainNavItems.push({ + id: 'discover', + label: 'Discover', + href: http.basePath.prepend('/app/discover'), + }); + return [mainNavItems, footerNavItems]; - }, [navLinks, formatDefaultItem]); + }, [navLinks, formatDefaultItem, http]); return sideNavItems; }; From 5b84d8f56159f82595011256e4aa74bfdba8ac77 Mon Sep 17 00:00:00 2001 From: Sergi Massaneda Date: Thu, 2 Mar 2023 20:54:35 +0100 Subject: [PATCH 25/58] Delete README.mdx --- packages/shared-ux/side_navigation/README.mdx | 24 ------------------- 1 file changed, 24 deletions(-) delete mode 100644 packages/shared-ux/side_navigation/README.mdx diff --git a/packages/shared-ux/side_navigation/README.mdx b/packages/shared-ux/side_navigation/README.mdx deleted file mode 100644 index 740837bb47a63..0000000000000 --- a/packages/shared-ux/side_navigation/README.mdx +++ /dev/null @@ -1,24 +0,0 @@ -TODO ---- -id: sharedUX/Components/Toolbar -slug: /shared-ux/components/button_toolbar -title: Toolbar -summary: An implementation of the popover, primary button, icon button group and add from library button -tags: ['shared-ux', 'component', 'toolbar'] -date: 2022-07-28 ---- - -This `toolbar` component accepts a `children` prop. Children can include a `popover` or a generic `button`. It can also include the `IconButtonGroup` and `AddFromLibrary` component for soltuions. -Styling of the popover and button follow the primary styles. - - -```jsx - - {{ - primaryButton, - iconButtonGroup, - extraButtons, - addFromLibraryButton, - }} - -``` \ No newline at end of file From 1f25dee43110467dc23b7bf80d54c8fbd66acd9e Mon Sep 17 00:00:00 2001 From: semd Date: Mon, 6 Mar 2023 12:51:03 +0100 Subject: [PATCH 26/58] side nav collapsible implemented using local storage --- .../src/ui/solution/header.tsx | 38 +------- .../side_navigation/side_navigation.tsx | 87 ++++++++++++++----- 2 files changed, 68 insertions(+), 57 deletions(-) diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/solution/header.tsx b/packages/core/chrome/core-chrome-browser-internal/src/ui/solution/header.tsx index 29e8c713265e9..56a099445facf 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/ui/solution/header.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/solution/header.tsx @@ -7,16 +7,7 @@ */ import React from 'react'; -import { - EuiButtonIcon, - EuiCollapsibleNav, - EuiHeader, - EuiHeaderLogo, - EuiHeaderSection, - EuiHeaderSectionItem, - EuiThemeProvider, - useEuiTheme, -} from '@elastic/eui'; +import { EuiHeader, EuiHeaderLogo, EuiHeaderSection, EuiHeaderSectionItem } from '@elastic/eui'; import { ChromeBreadcrumb, ChromeGlobalHelpExtensionMenuLink, @@ -48,8 +39,6 @@ export const SolutionHeader = ({ navigation, ...observables }: Props) => { - const { euiTheme, colorMode } = useEuiTheme(); - const renderLogo = () => ( - - {}} - closeButtonProps={{ iconType: 'menuLeft' }} - showButtonIfDocked={true} - isDocked={true} - size={248} - hideCloseButton={false} - button={ - - - - } - > - {navigation} - - + {navigation} ); }; diff --git a/x-pack/plugins/serverless_security/public/components/side_navigation/side_navigation.tsx b/x-pack/plugins/serverless_security/public/components/side_navigation/side_navigation.tsx index bc4be049e1aa9..db63bd2ed038d 100644 --- a/x-pack/plugins/serverless_security/public/components/side_navigation/side_navigation.tsx +++ b/x-pack/plugins/serverless_security/public/components/side_navigation/side_navigation.tsx @@ -5,40 +5,85 @@ * 2.0. */ -import React from 'react'; +import React, { useCallback } from 'react'; +import useLocalStorage from 'react-use/lib/useLocalStorage'; import { i18n } from '@kbn/i18n'; import { SolutionNav } from '@kbn/shared-ux-page-solution-nav'; -import { EuiLoadingSpinner } from '@elastic/eui'; +import { + EuiButtonIcon, + EuiCollapsibleNav, + EuiLoadingSpinner, + EuiThemeProvider, + useEuiTheme, +} from '@elastic/eui'; import { SideNavigation } from '@kbn/shared-ux-side-navigation'; import { useSideNavItems, useSideNavSelectedId } from '../../hooks/use_side_nav_items'; -const translatedNavTitle = i18n.translate('xpack.securitySolution.navigation.mainLabel', { +const LOCAL_STORAGE_IS_OPEN_KEY = 'SECURITY_SERVERLESS_SIDE_NAVIGATION_OPEN' as const; + +const translatedNavTitle = i18n.translate('xpack.securityServerless.navigation.mainLabel', { defaultMessage: 'Security', }); export const SecuritySideNavigation: React.FC = () => { const [items, footerItems] = useSideNavItems(); const selectedId = useSideNavSelectedId(); + const { euiTheme, colorMode } = useEuiTheme(); + + const [isOpen, setIsOpen] = useLocalStorage(LOCAL_STORAGE_IS_OPEN_KEY, true); + + const toggleOpen = useCallback(() => { + setIsOpen(!isOpen); + }, [isOpen, setIsOpen]); - if (items.length === 0 && footerItems.length === 0) { - return ; - } + const isSideNavLoading = items.length === 0 && footerItems.length === 0; return ( - - } - closeFlyoutButtonPosition={'inside'} - /> + + + + + } + > + {isOpen && + (isSideNavLoading ? ( + + ) : ( + + } + closeFlyoutButtonPosition={'inside'} + /> + ))} + + ); }; From 5ad42575a9fe3447925ab5c6753452c554ae999e Mon Sep 17 00:00:00 2001 From: semd Date: Mon, 6 Mar 2023 12:52:20 +0100 Subject: [PATCH 27/58] missed rename --- .../public/components/side_navigation/side_navigation.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/serverless_security/public/components/side_navigation/side_navigation.tsx b/x-pack/plugins/serverless_security/public/components/side_navigation/side_navigation.tsx index db63bd2ed038d..9ac5cacc8e523 100644 --- a/x-pack/plugins/serverless_security/public/components/side_navigation/side_navigation.tsx +++ b/x-pack/plugins/serverless_security/public/components/side_navigation/side_navigation.tsx @@ -36,7 +36,7 @@ export const SecuritySideNavigation: React.FC = () => { setIsOpen(!isOpen); }, [isOpen, setIsOpen]); - const isSideNavLoading = items.length === 0 && footerItems.length === 0; + const isLoading = items.length === 0 && footerItems.length === 0; return ( @@ -65,7 +65,7 @@ export const SecuritySideNavigation: React.FC = () => { } > {isOpen && - (isSideNavLoading ? ( + (isLoading ? ( ) : ( Date: Wed, 1 Mar 2023 22:53:07 +0000 Subject: [PATCH 28/58] Enterprise Search(search_index): hide search engines when app search unavailable --- .../header_actions/header_actions.tsx | 19 +++++++++++++------ .../components/search_index/search_index.tsx | 7 +++++-- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/header_actions/header_actions.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/header_actions/header_actions.tsx index f6cb956478fde..4d6ca941351f5 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/header_actions/header_actions.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/header_actions/header_actions.tsx @@ -15,15 +15,22 @@ import { SearchEnginesPopover } from './search_engines_popover'; import { SyncsContextMenu } from './syncs_context_menu'; // Used to populate rightSideItems of an EuiPageTemplate, which is rendered right-to-left -export const getHeaderActions = (indexData?: ElasticsearchIndexWithIngestion) => { +export const getHeaderActions = ( + indexData?: ElasticsearchIndexWithIngestion, + hasAppSearchAccess?: boolean +) => { const ingestionMethod = getIngestionMethod(indexData); return [ ...(isCrawlerIndex(indexData) && indexData.connector ? [] : []), ...(isConnectorIndex(indexData) ? [] : []), - , + ...(hasAppSearchAccess || hasAppSearchAccess === undefined + ? [ + , + ] + : []), ]; }; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/search_index.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/search_index.tsx index 52ae6628f8081..ab85ee26ba12e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/search_index.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/search_index.tsx @@ -75,7 +75,10 @@ export const SearchIndex: React.FC = () => { * This needs to be checked for any of the 3 registered search guideIds * Putting it here guarantees that if a user is viewing an index with data, it'll be marked as complete */ - const { guidedOnboarding } = useValues(KibanaLogic); + const { + guidedOnboarding, + productAccess: { hasAppSearchAccess }, + } = useValues(KibanaLogic); const isAppGuideActive = useObservable( guidedOnboarding.guidedOnboardingApi!.isGuideStepActive$('appSearch', 'add_data') ); @@ -222,7 +225,7 @@ export const SearchIndex: React.FC = () => { isLoading={isInitialLoading} pageHeader={{ pageTitle: indexName, - rightSideItems: getHeaderActions(index), + rightSideItems: getHeaderActions(index, hasAppSearchAccess), }} > {isCrawlerIndex(index) && !index.connector ? ( From 773a37261cf21d754972ae4768af73aac78127f7 Mon Sep 17 00:00:00 2001 From: Rodney Norris Date: Wed, 1 Mar 2023 22:57:08 +0000 Subject: [PATCH 29/58] poc: introducing serverless search plugin --- .github/CODEOWNERS | 1 + config/serverless.es.yml | 10 ++++ package.json | 1 + tsconfig.base.json | 2 + x-pack/plugins/serverless_search/.gitignore | 2 + x-pack/plugins/serverless_search/.i18nrc.json | 9 ++++ x-pack/plugins/serverless_search/README.md | 3 ++ .../plugins/serverless_search/common/index.ts | 9 ++++ x-pack/plugins/serverless_search/kibana.jsonc | 23 ++++++++ x-pack/plugins/serverless_search/package.json | 11 ++++ .../plugins/serverless_search/public/index.ts | 16 ++++++ .../serverless_search/public/layout/nav.tsx | 54 +++++++++++++++++++ .../serverless_search/public/plugin.tsx | 45 ++++++++++++++++ .../plugins/serverless_search/public/types.ts | 31 +++++++++++ .../serverless_search/server/config.ts | 23 ++++++++ .../plugins/serverless_search/server/index.ts | 20 +++++++ .../serverless_search/server/plugin.ts | 26 +++++++++ .../plugins/serverless_search/server/types.ts | 11 ++++ .../plugins/serverless_search/tsconfig.json | 24 +++++++++ yarn.lock | 4 ++ 20 files changed, 325 insertions(+) create mode 100644 x-pack/plugins/serverless_search/.gitignore create mode 100644 x-pack/plugins/serverless_search/.i18nrc.json create mode 100755 x-pack/plugins/serverless_search/README.md create mode 100644 x-pack/plugins/serverless_search/common/index.ts create mode 100644 x-pack/plugins/serverless_search/kibana.jsonc create mode 100644 x-pack/plugins/serverless_search/package.json create mode 100644 x-pack/plugins/serverless_search/public/index.ts create mode 100644 x-pack/plugins/serverless_search/public/layout/nav.tsx create mode 100644 x-pack/plugins/serverless_search/public/plugin.tsx create mode 100644 x-pack/plugins/serverless_search/public/types.ts create mode 100644 x-pack/plugins/serverless_search/server/config.ts create mode 100644 x-pack/plugins/serverless_search/server/index.ts create mode 100644 x-pack/plugins/serverless_search/server/plugin.ts create mode 100644 x-pack/plugins/serverless_search/server/types.ts create mode 100644 x-pack/plugins/serverless_search/tsconfig.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index c9f7999ce5327..436d262955e43 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -549,6 +549,7 @@ packages/kbn-server-http-tools @elastic/kibana-core packages/kbn-server-route-repository @elastic/apm-ui x-pack/plugins/serverless @elastic/appex-sharedux x-pack/plugins/serverless_observability @elastic/appex-sharedux +x-pack/plugins/serverless_search @elastic/appex-sharedux @elastic/enterprise-search-frontend x-pack/plugins/serverless_security @elastic/appex-sharedux test/plugin_functional/plugins/session_notifications @elastic/kibana-core x-pack/plugins/session_view @elastic/awp-viz diff --git a/config/serverless.es.yml b/config/serverless.es.yml index e69de29bb2d1d..73e90be302b27 100644 --- a/config/serverless.es.yml +++ b/config/serverless.es.yml @@ -0,0 +1,10 @@ +xpack.apm.enabled: false +xpack.canvas.enabled: false +xpack.reporting.enabled: false +xpack.uptime.enabled: false +xpack.watcher.enabled: false + +enterpriseSearch.enabled: true +xpack.serverless.search.enabled: true + +uiSettings.overrides.defaultRoute: /app/enterprise_search/content/search_indices diff --git a/package.json b/package.json index 5462bb7056554..d8294b067b9be 100644 --- a/package.json +++ b/package.json @@ -551,6 +551,7 @@ "@kbn/server-route-repository": "link:packages/kbn-server-route-repository", "@kbn/serverless": "link:x-pack/plugins/serverless", "@kbn/serverless-observability": "link:x-pack/plugins/serverless_observability", + "@kbn/serverless-search": "link:x-pack/plugins/serverless_search", "@kbn/serverless-security": "link:x-pack/plugins/serverless_security", "@kbn/session-notifications-plugin": "link:test/plugin_functional/plugins/session_notifications", "@kbn/session-view-plugin": "link:x-pack/plugins/session_view", diff --git a/tsconfig.base.json b/tsconfig.base.json index 21db47056353d..1a6e0c069aaef 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1092,6 +1092,8 @@ "@kbn/serverless/*": ["x-pack/plugins/serverless/*"], "@kbn/serverless-observability": ["x-pack/plugins/serverless_observability"], "@kbn/serverless-observability/*": ["x-pack/plugins/serverless_observability/*"], + "@kbn/serverless-search": ["x-pack/plugins/serverless_search"], + "@kbn/serverless-search/*": ["x-pack/plugins/serverless_search/*"], "@kbn/serverless-security": ["x-pack/plugins/serverless_security"], "@kbn/serverless-security/*": ["x-pack/plugins/serverless_security/*"], "@kbn/session-notifications-plugin": ["test/plugin_functional/plugins/session_notifications"], diff --git a/x-pack/plugins/serverless_search/.gitignore b/x-pack/plugins/serverless_search/.gitignore new file mode 100644 index 0000000000000..c3dca1b96fcc2 --- /dev/null +++ b/x-pack/plugins/serverless_search/.gitignore @@ -0,0 +1,2 @@ +/build +/target diff --git a/x-pack/plugins/serverless_search/.i18nrc.json b/x-pack/plugins/serverless_search/.i18nrc.json new file mode 100644 index 0000000000000..7b0dcef6895ed --- /dev/null +++ b/x-pack/plugins/serverless_search/.i18nrc.json @@ -0,0 +1,9 @@ +{ + "prefix": "serverlessSearch", + "paths": { + "serverlessSearch": "." + }, + "translations": [ + "translations/ja-JP.json" + ] +} diff --git a/x-pack/plugins/serverless_search/README.md b/x-pack/plugins/serverless_search/README.md new file mode 100755 index 0000000000000..9758a7616785d --- /dev/null +++ b/x-pack/plugins/serverless_search/README.md @@ -0,0 +1,3 @@ +# serverlessSearch + +A witty, fitting description to come. diff --git a/x-pack/plugins/serverless_search/common/index.ts b/x-pack/plugins/serverless_search/common/index.ts new file mode 100644 index 0000000000000..539748f2cea38 --- /dev/null +++ b/x-pack/plugins/serverless_search/common/index.ts @@ -0,0 +1,9 @@ +/* + * 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. + */ + +export const PLUGIN_ID = 'serverlessSearch'; +export const PLUGIN_NAME = 'serverlessSearch'; diff --git a/x-pack/plugins/serverless_search/kibana.jsonc b/x-pack/plugins/serverless_search/kibana.jsonc new file mode 100644 index 0000000000000..b548823567ce0 --- /dev/null +++ b/x-pack/plugins/serverless_search/kibana.jsonc @@ -0,0 +1,23 @@ +{ + "type": "plugin", + "id": "@kbn/serverless-search", + "owner": "@elastic/appex-sharedux", + "description": "Serverless customizations for search.", + "plugin": { + "id": "serverlessSearch", + "server": true, + "browser": true, + "configPath": [ + "xpack", + "serverless", + "search" + ], + "requiredPlugins": [ + "serverless", + "enterpriseSearch", + "management" + ], + "optionalPlugins": [], + "requiredBundles": [] + } +} diff --git a/x-pack/plugins/serverless_search/package.json b/x-pack/plugins/serverless_search/package.json new file mode 100644 index 0000000000000..b7820231076ee --- /dev/null +++ b/x-pack/plugins/serverless_search/package.json @@ -0,0 +1,11 @@ +{ + "name": "@kbn/serverless-search", + "version": "1.0.0", + "license": "Elastic License 2.0", + "private": true, + "scripts": { + "build": "yarn plugin-helpers build", + "plugin-helpers": "node ../../../scripts/plugin_helpers", + "kbn": "node ../../../scripts/kbn" + } +} diff --git a/x-pack/plugins/serverless_search/public/index.ts b/x-pack/plugins/serverless_search/public/index.ts new file mode 100644 index 0000000000000..5031ccc61d1ac --- /dev/null +++ b/x-pack/plugins/serverless_search/public/index.ts @@ -0,0 +1,16 @@ +/* + * 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 { ServerlessSearchPlugin } from './plugin'; + +// This exports static code and TypeScript types, +// as well as, Kibana Platform `plugin()` initializer. +export function plugin() { + return new ServerlessSearchPlugin(); +} + +export type { ServerlessSearchPluginSetup, ServerlessSearchPluginStart } from './types'; diff --git a/x-pack/plugins/serverless_search/public/layout/nav.tsx b/x-pack/plugins/serverless_search/public/layout/nav.tsx new file mode 100644 index 0000000000000..b4ebbf328da1a --- /dev/null +++ b/x-pack/plugins/serverless_search/public/layout/nav.tsx @@ -0,0 +1,54 @@ +/* + * 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 React from 'react'; +import { EuiCollapsibleNavGroup, EuiListGroup } from '@elastic/eui'; +import { ApplicationStart, HttpSetup } from '@kbn/core/public'; + +export interface ServerlessSearchCollapsibleNavigationProps { + http: HttpSetup; + navigateToUrl: ApplicationStart['navigateToUrl']; +} + +export const ServerlessSearchCollapsibleNavigation = ({ + http, + navigateToUrl, +}: ServerlessSearchCollapsibleNavigationProps) => { + const navigateTo = (url: string) => () => { + navigateToUrl(http.basePath.prepend(url)); + }; + const navItems = [ + { + label: 'Overview', + onClick: navigateTo('/app/enterprise_search/overview'), + }, + { + label: 'Indices', + onClick: navigateTo('/app/enterprise_search/content/search_indices'), + }, + { + label: 'Engines', + onClick: navigateTo('/app/enterprise_search/content/engines'), + }, + { + label: 'API keys', + onClick: navigateTo('/app/management/security/api_keys'), + }, + { + label: 'Ingest pipelines', + onClick: navigateTo('/app/management/ingest/ingest_pipelines'), + }, + ]; + + return ( + <> + + + + + ); +}; diff --git a/x-pack/plugins/serverless_search/public/plugin.tsx b/x-pack/plugins/serverless_search/public/plugin.tsx new file mode 100644 index 0000000000000..1b2a29452ca9d --- /dev/null +++ b/x-pack/plugins/serverless_search/public/plugin.tsx @@ -0,0 +1,45 @@ +/* + * 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 React from 'react'; +import { CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; +import { + ServerlessSearchPluginSetup, + ServerlessSearchPluginSetupDependencies, + ServerlessSearchPluginStart, + ServerlessSearchPluginStartDependencies, +} from './types'; + +import { ServerlessSearchCollapsibleNavigation } from './layout/nav'; + +export class ServerlessSearchPlugin + implements Plugin +{ + public setup( + _core: CoreSetup, + setupDeps: ServerlessSearchPluginSetupDependencies + ): ServerlessSearchPluginSetup { + setupDeps.enterpriseSearch.navigation.setIsSidebarEnabled(false); + setupDeps.management.setIsSidebarEnabled(false); + return {}; + } + + public start( + core: CoreStart, + { serverless }: ServerlessSearchPluginStartDependencies + ): ServerlessSearchPluginStart { + serverless.setServerlessNavigation( + + ); + return {}; + } + + public stop() {} +} diff --git a/x-pack/plugins/serverless_search/public/types.ts b/x-pack/plugins/serverless_search/public/types.ts new file mode 100644 index 0000000000000..ad66e6df85c74 --- /dev/null +++ b/x-pack/plugins/serverless_search/public/types.ts @@ -0,0 +1,31 @@ +/* + * 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 { ServerlessPluginSetup, ServerlessPluginStart } from '@kbn/serverless/public'; +import { ManagementSetup, ManagementStart } from '@kbn/management-plugin/public'; +import { + EnterpriseSearchPublicSetup, + EnterpriseSearchPublicStart, +} from '@kbn/enterprise-search-plugin/public'; + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ServerlessSearchPluginSetup {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ServerlessSearchPluginStart {} + +export interface ServerlessSearchPluginSetupDependencies { + enterpriseSearch: EnterpriseSearchPublicSetup; + management: ManagementSetup; + serverless: ServerlessPluginSetup; +} + +export interface ServerlessSearchPluginStartDependencies { + enterpriseSearch: EnterpriseSearchPublicStart; + management: ManagementStart; + serverless: ServerlessPluginStart; +} diff --git a/x-pack/plugins/serverless_search/server/config.ts b/x-pack/plugins/serverless_search/server/config.ts new file mode 100644 index 0000000000000..546c594aaabfb --- /dev/null +++ b/x-pack/plugins/serverless_search/server/config.ts @@ -0,0 +1,23 @@ +/* + * 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 { schema, TypeOf } from '@kbn/config-schema'; +import { PluginConfigDescriptor } from '@kbn/core/server'; + +export * from './types'; + +const configSchema = schema.object({ + enabled: schema.boolean({ defaultValue: false }), +}); + +type ConfigType = TypeOf; + +export const config: PluginConfigDescriptor = { + schema: configSchema, +}; + +export type ServerlessSearchConfig = TypeOf; diff --git a/x-pack/plugins/serverless_search/server/index.ts b/x-pack/plugins/serverless_search/server/index.ts new file mode 100644 index 0000000000000..90e0b170d4a71 --- /dev/null +++ b/x-pack/plugins/serverless_search/server/index.ts @@ -0,0 +1,20 @@ +/* + * 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 { PluginInitializerContext } from '@kbn/core/server'; + +import { ServerlessSearchPlugin } from './plugin'; +export { config } from './config'; + +// This exports static code and TypeScript types, +// as well as, Kibana Platform `plugin()` initializer. + +export function plugin(initializerContext: PluginInitializerContext) { + return new ServerlessSearchPlugin(initializerContext); +} + +export type { ServerlessSearchPluginSetup, ServerlessSearchPluginStart } from './types'; diff --git a/x-pack/plugins/serverless_search/server/plugin.ts b/x-pack/plugins/serverless_search/server/plugin.ts new file mode 100644 index 0000000000000..99d9bf01da0df --- /dev/null +++ b/x-pack/plugins/serverless_search/server/plugin.ts @@ -0,0 +1,26 @@ +/* + * 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 { PluginInitializerContext, Plugin } from '@kbn/core/server'; + +import { ServerlessSearchPluginSetup, ServerlessSearchPluginStart } from './types'; + +export class ServerlessSearchPlugin + implements Plugin +{ + constructor(_initializerContext: PluginInitializerContext) {} + + public setup() { + return {}; + } + + public start() { + return {}; + } + + public stop() {} +} diff --git a/x-pack/plugins/serverless_search/server/types.ts b/x-pack/plugins/serverless_search/server/types.ts new file mode 100644 index 0000000000000..6011e2eb60fa0 --- /dev/null +++ b/x-pack/plugins/serverless_search/server/types.ts @@ -0,0 +1,11 @@ +/* + * 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. + */ + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ServerlessSearchPluginSetup {} +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ServerlessSearchPluginStart {} diff --git a/x-pack/plugins/serverless_search/tsconfig.json b/x-pack/plugins/serverless_search/tsconfig.json new file mode 100644 index 0000000000000..c8150e5a71926 --- /dev/null +++ b/x-pack/plugins/serverless_search/tsconfig.json @@ -0,0 +1,24 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types" + }, + "include": [ + "index.ts", + "common/**/*.ts", + "public/**/*.ts", + "public/**/*.tsx", + "server/**/*.ts", + "../../../typings/**/*" + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/core", + "@kbn/config-schema", + "@kbn/enterprise-search-plugin", + "@kbn/management-plugin", + "@kbn/serverless", + ] +} diff --git a/yarn.lock b/yarn.lock index 31bcb2534defa..fb997b869e3a2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4917,6 +4917,10 @@ version "0.0.0" uid "" +"@kbn/serverless-search@link:x-pack/plugins/serverless_search": + version "0.0.0" + uid "" + "@kbn/serverless-security@link:x-pack/plugins/serverless_security": version "0.0.0" uid "" From eeef2570ffa8f82c1baf926ec01b412c1b4de9da Mon Sep 17 00:00:00 2001 From: Rodney Norris Date: Thu, 2 Mar 2023 19:32:46 +0000 Subject: [PATCH 30/58] Enterprise Search: pass isSidebarEnabled by value instead of a get function --- .../enterprise_search/public/applications/index.tsx | 6 +++--- .../public/applications/shared/kibana/kibana_logic.ts | 9 ++------- x-pack/plugins/enterprise_search/public/plugin.ts | 2 +- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/index.tsx b/x-pack/plugins/enterprise_search/public/applications/index.tsx index f88ae1d0ab287..5b3b050d72238 100644 --- a/x-pack/plugins/enterprise_search/public/applications/index.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/index.tsx @@ -40,12 +40,12 @@ export const renderApp = ( params, core, plugins, - getIsSidebarEnabled, + isSidebarEnabled, }: { params: AppMountParameters; core: CoreStart; plugins: PluginsStart; - getIsSidebarEnabled: () => boolean; + isSidebarEnabled: boolean; }, { config, data }: { config: ClientConfigType; data: ClientData } ) => { @@ -74,7 +74,7 @@ export const renderApp = ( charts: plugins.charts, cloud: plugins.cloud, uiSettings: core.uiSettings, - getIsSidebarEnabled, + isSidebarEnabled, guidedOnboarding: plugins.guidedOnboarding, history: params.history, navigateToUrl: core.application.navigateToUrl, diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts b/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts index 5ab00b869e93f..418baa7050732 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts @@ -33,7 +33,7 @@ interface KibanaLogicProps { application: ApplicationStart; config: { host?: string }; productAccess: ProductAccess; - getIsSidebarEnabled: () => boolean; + isSidebarEnabled: boolean; // Kibana core capabilities: Capabilities; history: ScopedHistory; @@ -54,7 +54,6 @@ interface KibanaLogicProps { export interface KibanaValues extends Omit { cloud: Partial; isCloud: boolean; - isSidebarEnabled: boolean; navigateToUrl(path: string, options?: CreateHrefOptions): Promise; } @@ -66,9 +65,9 @@ export const KibanaLogic = kea>({ config: [props.config || {}, {}], charts: [props.charts, {}], cloud: [props.cloud || {}, {}], - getIsSidebarEnabled: [props.getIsSidebarEnabled, {}], guidedOnboarding: [props.guidedOnboarding, {}], history: [props.history, {}], + isSidebarEnabled: [props.isSidebarEnabled, {}], navigateToUrl: [ (url: string, options?: CreateHrefOptions) => { const deps = { history: props.history, http: HttpLogic.values.http }; @@ -87,10 +86,6 @@ export const KibanaLogic = kea>({ }), selectors: ({ selectors }) => ({ isCloud: [() => [selectors.cloud], (cloud?: Partial) => !!cloud?.isCloudEnabled], - isSidebarEnabled: [ - () => [selectors.getIsSidebarEnabled], - (getIsSidebarEnabled: () => boolean) => getIsSidebarEnabled(), - ], }), }); diff --git a/x-pack/plugins/enterprise_search/public/plugin.ts b/x-pack/plugins/enterprise_search/public/plugin.ts index e733d1811a1d1..9aded65b54eb5 100644 --- a/x-pack/plugins/enterprise_search/public/plugin.ts +++ b/x-pack/plugins/enterprise_search/public/plugin.ts @@ -324,7 +324,7 @@ export class EnterpriseSearchPlugin implements Plugin { : undefined; const plugins = { ...pluginsStart, cloud } as PluginsStart; - return { params, core: coreStart, plugins, getIsSidebarEnabled: () => this.isSidebarEnabled }; + return { params, core: coreStart, plugins, isSidebarEnabled: this.isSidebarEnabled }; } private getPluginData() { From 0385ef5e9e4d3e6e4ffef67a3b8d0759f8b3752b Mon Sep 17 00:00:00 2001 From: semd Date: Wed, 8 Mar 2023 12:04:25 +0100 Subject: [PATCH 31/58] use core history for location updates --- .../src/ui/solution/header.tsx | 3 ++- x-pack/plugins/serverless_security/public/services.tsx | 10 ++-------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/solution/header.tsx b/packages/core/chrome/core-chrome-browser-internal/src/ui/solution/header.tsx index 56a099445facf..c2bb847e0ab89 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/ui/solution/header.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/solution/header.tsx @@ -7,6 +7,7 @@ */ import React from 'react'; +import { Router } from 'react-router-dom'; import { EuiHeader, EuiHeaderLogo, EuiHeaderSection, EuiHeaderSectionItem } from '@elastic/eui'; import { ChromeBreadcrumb, @@ -73,7 +74,7 @@ export const SolutionHeader = ({ - {navigation} + {navigation} ); }; diff --git a/x-pack/plugins/serverless_security/public/services.tsx b/x-pack/plugins/serverless_security/public/services.tsx index 56a2320948185..4071da69967d2 100644 --- a/x-pack/plugins/serverless_security/public/services.tsx +++ b/x-pack/plugins/serverless_security/public/services.tsx @@ -11,23 +11,17 @@ import { KibanaContextProvider, useKibana as useKibanaReact, } from '@kbn/kibana-react-plugin/public'; -import { BrowserRouter } from 'react-router-dom'; -import { ServerlessSecurityPluginStartDependencies } from './types'; +import type { ServerlessSecurityPluginStartDependencies } from './types'; export type Services = CoreStart & ServerlessSecurityPluginStartDependencies; -// TODO: Replace by with global history export const getKibanaServicesProvider = ( core: CoreStart, pluginsStart: ServerlessSecurityPluginStartDependencies ): React.FC => { const services: Services = { ...core, ...pluginsStart }; return ({ children }) => { - return ( - - {children} - - ); + return {children}; }; }; From 3d9eaafa85695e1a3cbcd8bc9a496fc313eb99b2 Mon Sep 17 00:00:00 2001 From: semd Date: Wed, 8 Mar 2023 12:05:24 +0100 Subject: [PATCH 32/58] standarize sideNavigation items and remove special case --- packages/shared-ux/side_navigation/index.ts | 11 +- .../side_navigation/src/side_navigation.tsx | 80 ++++---- .../shared-ux/side_navigation/src/types.ts | 20 +- .../security_side_nav/security_side_nav.tsx | 52 ++--- .../side_navigation/get_started_nav_item.tsx | 39 +--- .../side_navigation/side_navigation.tsx | 11 +- .../public/hooks/use_link_props.ts | 2 +- .../public/hooks/use_side_nav_items.ts | 191 +++++++++--------- 8 files changed, 173 insertions(+), 233 deletions(-) diff --git a/packages/shared-ux/side_navigation/index.ts b/packages/shared-ux/side_navigation/index.ts index acc1c111dbe5a..6ddebb544042e 100644 --- a/packages/shared-ux/side_navigation/index.ts +++ b/packages/shared-ux/side_navigation/index.ts @@ -7,13 +7,4 @@ */ export { SideNavigation, type SideNavigationProps } from './src'; -export type { - SideNavItem, - LinkCategory, - LinkCategories, - DefaultSideNavItem, - CustomSideNavItem, - isCustomItem, - isDefaultItem, - Tracker, -} from './src/types'; +export type { SideNavItem, LinkCategory, LinkCategories, Tracker } from './src/types'; diff --git a/packages/shared-ux/side_navigation/src/side_navigation.tsx b/packages/shared-ux/side_navigation/src/side_navigation.tsx index dd0e2ad3e70d9..c4a6865e5efb8 100644 --- a/packages/shared-ux/side_navigation/src/side_navigation.tsx +++ b/packages/shared-ux/side_navigation/src/side_navigation.tsx @@ -6,7 +6,9 @@ * Side Public License, v 1. */ -import React, { Fragment, useCallback, useMemo, useRef, useState } from 'react'; +/* eslint-disable @elastic/eui/href-or-on-click */ + +import React, { useCallback, useMemo, useRef, useState } from 'react'; import { EuiListGroup, EuiFlexGroup, @@ -15,13 +17,13 @@ import { useIsWithinBreakpoints, useEuiTheme, EuiListGroupItem, + EuiHorizontalRule, } from '@elastic/eui'; import classNames from 'classnames'; import { METRIC_TYPE } from '@kbn/analytics'; import { SideNavigationPanel } from './side_navigation_panel'; -import type { DefaultSideNavItem, SideNavItem, Tracker } from './types'; -import { isCustomItem, isDefaultItem, type LinkCategories } from './types'; +import type { LinkCategories, SideNavItem, Tracker } from './types'; import { TELEMETRY_EVENT } from './telemetry/const'; import { TelemetryContextProvider, useTelemetryContext } from './telemetry/telemetry_context'; import { SideNavItemStyles } from './side_navigation.styles'; @@ -54,7 +56,7 @@ export interface SideNavigationItemProps { type ActivePanelNav = string | null; type NavItemsById = Record< string, - { title: string; panelItems: DefaultSideNavItem[]; categories?: LinkCategories } + { title: string; panelItems: SideNavItem[]; categories?: LinkCategories } >; export const SideNavigationComponent: React.FC = ({ @@ -95,7 +97,7 @@ export const SideNavigationComponent: React.FC = ({ const navItemsById = useMemo( () => [...items, ...footerItems].reduce((acc, navItem) => { - if (isDefaultItem(navItem) && navItem.items && navItem.items.length > 0) { + if (navItem.items && navItem.items.length > 0) { acc[navItem.id] = { title: navItem.label, panelItems: navItem.items, @@ -195,11 +197,7 @@ const SideNavigationItemComponent: React.FC = ({ const { euiTheme } = useEuiTheme(); const { tracker } = useTelemetryContext(); - if (isCustomItem(item)) { - return {item.render(isSelected)}; - } - - const { id, href, label, onClick } = item; + const { id, href, label, onClick, labelSize, iconType, appendSeparator } = item; const onLinkClicked: React.MouseEventHandler = (ev) => { tracker?.(METRIC_TYPE.CLICK, `${TELEMETRY_EVENT.NAVIGATION}${id}`); @@ -225,35 +223,41 @@ const SideNavigationItemComponent: React.FC = ({ }; return ( - // eslint-disable-next-line @elastic/eui/href-or-on-click - - + - + data-test-subj={`groupedNavItemLink-${id}`} + > + + + {appendSeparator && } + ); }; const SideNavItem = React.memo(SideNavigationItemComponent); diff --git a/packages/shared-ux/side_navigation/src/types.ts b/packages/shared-ux/side_navigation/src/types.ts index 3612f6d65c723..1168be03dd93a 100644 --- a/packages/shared-ux/side_navigation/src/types.ts +++ b/packages/shared-ux/side_navigation/src/types.ts @@ -8,28 +8,25 @@ import type React from 'react'; import type { UiCounterMetricType } from '@kbn/analytics'; +import type { EuiListGroupItemProps, IconType } from '@elastic/eui'; -export interface DefaultSideNavItem { +export interface SideNavItem { id: T; label: string; href: string; onClick?: React.MouseEventHandler; description?: string; - items?: Array>; + items?: Array>; categories?: LinkCategories; + iconType?: IconType; + labelSize?: EuiListGroupItemProps['size']; + appendSeparator?: boolean; isBeta?: boolean; betaOptions?: { text: string; }; } -export interface CustomSideNavItem { - id: T; - render: (isSelected: boolean) => React.ReactNode; -} - -export type SideNavItem = DefaultSideNavItem | CustomSideNavItem; - export interface LinkCategory { label: string; linkIds: readonly T[]; @@ -42,8 +39,3 @@ export type Tracker = ( event: string | string[], count?: number | undefined ) => void; - -export const isCustomItem = (navItem: SideNavItem): navItem is CustomSideNavItem => - 'render' in navItem; -export const isDefaultItem = (navItem: SideNavItem): navItem is DefaultSideNavItem => - !isCustomItem(navItem); diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/security_side_nav/security_side_nav.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/security_side_nav/security_side_nav.tsx index 5cb8f8e98633a..c5e8f224f8cb3 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/security_side_nav/security_side_nav.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/security_side_nav/security_side_nav.tsx @@ -6,18 +6,13 @@ */ import React, { useMemo, useCallback } from 'react'; -import { EuiHorizontalRule, EuiListGroupItem, EuiLoadingSpinner } from '@elastic/eui'; +import { EuiLoadingSpinner } from '@elastic/eui'; import { SideNavigation } from '@kbn/shared-ux-side-navigation'; -import type { - CustomSideNavItem, - DefaultSideNavItem, - SideNavItem, -} from '@kbn/shared-ux-side-navigation'; +import type { SideNavItem } from '@kbn/shared-ux-side-navigation'; import { SecurityPageName } from '../../../../app/types'; import { getAncestorLinksInfo, type NavigationLink } from '../../../links'; import { useRouteSpy } from '../../../utils/route/use_route_spy'; -import { SecuritySolutionLinkAnchor, useGetSecuritySolutionLinkProps } from '../../links'; -import { EuiIconLaunch } from './icons/launch'; +import { useGetSecuritySolutionLinkProps } from '../../links'; import { useShowTimeline } from '../../../utils/timeline/use_show_timeline'; import { useIsPolicySettingsBarVisible } from '../../../../management/pages/policy/view/policy_hooks'; import { bottomNavOffset } from '../../../lib/helpers'; @@ -29,31 +24,6 @@ const isFooterNavItem = (id: SecurityPageName) => type FormatSideNavItems = (navItems: NavigationLink) => SideNavItem; -/** - * Renders the navigation item for "Get Started" custom link - */ -const GetStartedCustomLinkComponent: React.FC<{ - isSelected: boolean; - title: string; -}> = ({ isSelected, title }) => ( - - - - -); -const GetStartedCustomLink = React.memo(GetStartedCustomLinkComponent); - /** * Returns a function to format generic `NavigationLink` array to the `SideNavItem` type */ @@ -62,7 +32,7 @@ const useFormatSideNavItem = (): FormatSideNavItems => { const formatSideNavItem: FormatSideNavItems = useCallback( (navLinkItem) => { - const formatDefaultItem = (navItem: NavigationLink): DefaultSideNavItem => ({ + const formatDefaultItem = (navItem: NavigationLink): SideNavItem => ({ id: navItem.id, label: navItem.title, ...getSecuritySolutionLinkProps({ @@ -73,7 +43,7 @@ const useFormatSideNavItem = (): FormatSideNavItems => { : {}), ...(navItem.links && navItem.links.length > 0 ? { - items: navItem.links.reduce((acc, current) => { + items: navItem.links.reduce((acc, current) => { if (!current.disabled) { acc.push({ id: current.id, @@ -90,11 +60,15 @@ const useFormatSideNavItem = (): FormatSideNavItems => { : {}), }); - const formatGetStartedItem = (navItem: NavigationLink): CustomSideNavItem => ({ + const formatGetStartedItem = (navItem: NavigationLink): SideNavItem => ({ id: navItem.id, - render: (isSelected) => ( - - ), + label: navItem.title.toUpperCase(), + labelSize: 'xs', + iconType: 'launch', + ...getSecuritySolutionLinkProps({ + deepLinkId: navItem.id, + }), + appendSeparator: true, }); if (navLinkItem.id === SecurityPageName.landing) { diff --git a/x-pack/plugins/serverless_security/public/components/side_navigation/get_started_nav_item.tsx b/x-pack/plugins/serverless_security/public/components/side_navigation/get_started_nav_item.tsx index f65f0c6288354..fc4b993eaeef7 100644 --- a/x-pack/plugins/serverless_security/public/components/side_navigation/get_started_nav_item.tsx +++ b/x-pack/plugins/serverless_security/public/components/side_navigation/get_started_nav_item.tsx @@ -5,41 +5,4 @@ * 2.0. */ -import React from 'react'; -import { SecurityPageName } from '@kbn/security-solution-plugin/common'; -import { EuiLink, EuiListGroupItem, EuiHorizontalRule } from '@elastic/eui'; -import type { NavigationLink } from '@kbn/security-solution-plugin/public/common/links'; -import type { CustomSideNavItem } from '@kbn/shared-ux-side-navigation'; -import { useLinkProps } from '../../hooks/use_link_props'; - -/** - * Renders the navigation item for "Get Started" custom link - */ -const GetStartedCustomLinkComponent: React.FC<{ - isSelected: boolean; - title: string; -}> = ({ isSelected, title }) => { - const linkProps = useLinkProps({ deepLinkId: SecurityPageName.landing }); - return ( - - - - - ); -}; -export const GetStartedCustomLink = React.memo(GetStartedCustomLinkComponent); - -export const formatGetStartedItem = (navItem: NavigationLink): CustomSideNavItem => ({ - id: navItem.id, - render: (isSelected: boolean) => ( - - ), -}); +export {}; diff --git a/x-pack/plugins/serverless_security/public/components/side_navigation/side_navigation.tsx b/x-pack/plugins/serverless_security/public/components/side_navigation/side_navigation.tsx index 9ac5cacc8e523..0430438f672c1 100644 --- a/x-pack/plugins/serverless_security/public/components/side_navigation/side_navigation.tsx +++ b/x-pack/plugins/serverless_security/public/components/side_navigation/side_navigation.tsx @@ -17,7 +17,11 @@ import { useEuiTheme, } from '@elastic/eui'; import { SideNavigation } from '@kbn/shared-ux-side-navigation'; -import { useSideNavItems, useSideNavSelectedId } from '../../hooks/use_side_nav_items'; +import { + usePartitionFooterNavItems, + useSideNavItems, + useSideNavSelectedId, +} from '../../hooks/use_side_nav_items'; const LOCAL_STORAGE_IS_OPEN_KEY = 'SECURITY_SERVERLESS_SIDE_NAVIGATION_OPEN' as const; @@ -26,9 +30,10 @@ const translatedNavTitle = i18n.translate('xpack.securityServerless.navigation.m }); export const SecuritySideNavigation: React.FC = () => { - const [items, footerItems] = useSideNavItems(); - const selectedId = useSideNavSelectedId(); const { euiTheme, colorMode } = useEuiTheme(); + const sideNavItems = useSideNavItems(); + const selectedId = useSideNavSelectedId(sideNavItems); + const [items, footerItems] = usePartitionFooterNavItems(sideNavItems); const [isOpen, setIsOpen] = useLocalStorage(LOCAL_STORAGE_IS_OPEN_KEY, true); diff --git a/x-pack/plugins/serverless_security/public/hooks/use_link_props.ts b/x-pack/plugins/serverless_security/public/hooks/use_link_props.ts index 49d525cf51d66..6cdaef0d94d31 100644 --- a/x-pack/plugins/serverless_security/public/hooks/use_link_props.ts +++ b/x-pack/plugins/serverless_security/public/hooks/use_link_props.ts @@ -15,7 +15,7 @@ interface LinkProps { } interface GetLinkPropsParams { - deepLinkId: SecurityPageName; + deepLinkId?: SecurityPageName; path?: string; appId?: string; onClick?: MouseEventHandler; diff --git a/x-pack/plugins/serverless_security/public/hooks/use_side_nav_items.ts b/x-pack/plugins/serverless_security/public/hooks/use_side_nav_items.ts index 56666a79c5201..58a2bd04ad1b5 100644 --- a/x-pack/plugins/serverless_security/public/hooks/use_side_nav_items.ts +++ b/x-pack/plugins/serverless_security/public/hooks/use_side_nav_items.ts @@ -5,126 +5,137 @@ * 2.0. */ -import { useCallback, useMemo } from 'react'; -import type { NavigationLink } from '@kbn/security-solution-plugin/public/common/links'; -import { APP_UI_ID, SecurityPageName } from '@kbn/security-solution-plugin/common'; -import type { SideNavItem, DefaultSideNavItem } from '@kbn/shared-ux-side-navigation'; +import { useMemo } from 'react'; import useObservable from 'react-use/lib/useObservable'; import { matchPath, useLocation } from 'react-router-dom'; +import { partition } from 'lodash/fp'; +import { SecurityPageName } from '@kbn/security-solution-plugin/common'; +import type { SideNavItem } from '@kbn/shared-ux-side-navigation'; import { useKibana } from '../services'; import { useGetLinkProps } from './use_link_props'; -import { formatGetStartedItem } from '../components/side_navigation/get_started_nav_item'; -const isFooterNavItem = (id: SecurityPageName) => +const isFooterNavItem = (id: string) => id === SecurityPageName.landing || id === SecurityPageName.administration; -const isGetStartedNavItem = (id: SecurityPageName) => id === SecurityPageName.landing; +const isGetStartedNavItem = (id: string) => id === SecurityPageName.landing; -const useFindItemsByPath = () => { - const { getUrlForApp } = useKibana().services.application; - - // DFS for the item that matches the path, returns all item hierarchy when found - const findItemsByPath = useCallback( - (navLinks: NavigationLink[], pathname: string): NavigationLink[] => { - for (const navLink of navLinks) { - if (navLink.links?.length) { - const found = findItemsByPath(navLink.links, pathname); - if (found.length) { - found.unshift(navLink); - return found; - } - } - const path = getUrlForApp(APP_UI_ID, { deepLinkId: navLink.id }); - if (matchPath(pathname, { path })) { - return [navLink]; - } +// DFS for the sideNavItem matching the current `pathname`, returns all item hierarchy when found +const findItemsByPath = (sideNavItems: SideNavItem[], pathname: string): SideNavItem[] => { + for (const sideNavItem of sideNavItems) { + if (sideNavItem.items?.length) { + const found = findItemsByPath(sideNavItem.items, pathname); + if (found.length) { + found.unshift(sideNavItem); + return found; } - return []; - }, - [getUrlForApp] - ); - - return findItemsByPath; + } + if (matchPath(pathname, { path: sideNavItem.href })) { + return [sideNavItem]; + } + } + return []; }; /** - * Returns the formatted `items` and `footerItems` to be rendered in the navigation + * Returns all the formatted SideNavItems, including external links */ -export const useSideNavItems = () => { - const { securitySolution, http } = useKibana().services; +export const useSideNavItems = (): SideNavItem[] => { + const { securitySolution } = useKibana().services; const navLinks = useObservable(securitySolution.navLinks$, []); const getLinkProps = useGetLinkProps(); - const formatDefaultItem = useCallback( - (navItem: NavigationLink): DefaultSideNavItem => ({ - id: navItem.id, - label: navItem.title, - ...getLinkProps({ deepLinkId: navItem.id }), - ...(navItem.categories?.length && { categories: navItem.categories }), - ...(navItem.links?.length && { - items: navItem.links.reduce((acc, current) => { - if (!current.disabled) { - acc.push({ - id: current.id, - label: current.title, - description: current.description, - isBeta: current.isBeta, - betaOptions: current.betaOptions, - ...getLinkProps({ deepLinkId: current.id }), - }); - } - return acc; - }, []), - }), - }), - [getLinkProps] + const securitySideNavItems = useMemo( + () => + navLinks.reduce((items, navLink) => { + if (navLink.disabled) { + return items; + } + if (isGetStartedNavItem(navLink.id)) { + items.push({ + id: navLink.id, + label: navLink.title.toUpperCase(), + ...getLinkProps({ deepLinkId: navLink.id }), + labelSize: 'xs', + iconType: 'launch', + appendSeparator: true, + }); + } else { + // default sideNavItem formatting + items.push({ + id: navLink.id, + label: navLink.title, + ...getLinkProps({ deepLinkId: navLink.id }), + ...(navLink.categories?.length && { categories: navLink.categories }), + ...(navLink.links?.length && { + items: navLink.links.reduce((acc, current) => { + if (!current.disabled) { + acc.push({ + id: current.id, + label: current.title, + description: current.description, + isBeta: current.isBeta, + betaOptions: current.betaOptions, + ...getLinkProps({ deepLinkId: current.id }), + }); + } + return acc; + }, []), + }), + }); + } + return items; + }, []), + [getLinkProps, navLinks] ); - const sideNavItems = useMemo(() => { - const mainNavItems: SideNavItem[] = []; - const footerNavItems: SideNavItem[] = []; - navLinks.forEach((navLink) => { - if (navLink.disabled) { - return; - } - const sideNavItem = isGetStartedNavItem(navLink.id) - ? formatGetStartedItem(navLink) - : formatDefaultItem(navLink); + const sideNavItems = useAddExternalSideNavItems(securitySideNavItems); - if (isFooterNavItem(navLink.id)) { - footerNavItems.push(sideNavItem); - } else { - mainNavItems.push(sideNavItem); - } - }); + return sideNavItems; +}; - // TODO: remove, just extra external item for testing - mainNavItems.push({ - id: 'discover', - label: 'Discover', - href: http.basePath.prepend('/app/discover'), - }); +/** + * @param securitySideNavItems the sideNavItems for Security pages + * @returns sideNavItems with Security and external links + */ +const useAddExternalSideNavItems = (securitySideNavItems: SideNavItem[]) => { + const getLinkProps = useGetLinkProps(); - return [mainNavItems, footerNavItems]; - }, [navLinks, formatDefaultItem, http]); + const sideNavItemsWithExternals = useMemo( + () => [ + ...securitySideNavItems, + { + id: 'discover', + label: 'Discover', + ...getLinkProps({ appId: 'discover' }), + }, + ], + [securitySideNavItems, getLinkProps] + ); - return sideNavItems; + return sideNavItemsWithExternals; }; /** - * Returns the selected item id, which is the root item in the links hierarchy + * Partitions the sideNavItems into main and footer SideNavItems + * @param sideNavItems array for all SideNavItems + * @returns `[items, footerItems]` to be used in the side navigation component */ -export const useSideNavSelectedId = (): string => { - const { pathname } = useLocation(); // TODO: solve (not) updating problem - const { securitySolution } = useKibana().services; - const navLinks = useObservable(securitySolution.navLinks$, []); +export const usePartitionFooterNavItems = ( + sideNavItems: SideNavItem[] +): [SideNavItem[], SideNavItem[]] => + useMemo(() => partition((item) => !isFooterNavItem(item.id), sideNavItems), [sideNavItems]); - const findItemsByPath = useFindItemsByPath(); +/** + * Returns the selected item id, which is the root item in the links hierarchy + */ +export const useSideNavSelectedId = (sideNavItems: SideNavItem[]): string => { + const { http } = useKibana().services; + const { pathname } = useLocation(); const selectedId: string = useMemo(() => { - const [rootNavItem] = findItemsByPath(navLinks, pathname); + const [rootNavItem] = findItemsByPath(sideNavItems, http.basePath.prepend(pathname)); return rootNavItem?.id ?? ''; - }, [navLinks, findItemsByPath, pathname]); + }, [sideNavItems, pathname, http]); return selectedId; }; From 1fa69bdb01fcfac0b7e126d3464de62c658ed1a9 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 8 Mar 2023 17:00:09 +0000 Subject: [PATCH 33/58] [CI] Auto-commit changed files from 'node scripts/generate codeowners' --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 2f51b5496d505..116ac78391ab1 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -550,7 +550,7 @@ packages/kbn-server-http-tools @elastic/kibana-core packages/kbn-server-route-repository @elastic/apm-ui x-pack/plugins/serverless @elastic/appex-sharedux x-pack/plugins/serverless_observability @elastic/appex-sharedux -x-pack/plugins/serverless_search @elastic/appex-sharedux @elastic/enterprise-search-frontend +x-pack/plugins/serverless_search @elastic/appex-sharedux x-pack/plugins/serverless_security @elastic/appex-sharedux test/plugin_functional/plugins/session_notifications @elastic/kibana-core x-pack/plugins/session_view @elastic/awp-viz From 56a1c1113f5d7296abb4301aac0229148d90f86f Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 8 Mar 2023 17:08:37 +0000 Subject: [PATCH 34/58] [CI] Auto-commit changed files from 'node scripts/build_plugin_list_docs' --- docs/developer/plugin-list.asciidoc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index c4b801eb4edc4..9aa9acede1b3b 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -697,6 +697,10 @@ Kibana. |A witty, fitting description to come. +|{kib-repo}blob/{branch}/x-pack/plugins/serverless_search/README.md[serverlessSearch] +|A witty, fitting description to come. + + |{kib-repo}blob/{branch}/x-pack/plugins/serverless_security/README.md[serverlessSecurity] |A witty, fitting description to come. From da5e079d32840f03729ac0b2abbe2054ff246fec Mon Sep 17 00:00:00 2001 From: Clint Andrew Hall Date: Wed, 8 Mar 2023 22:03:26 -0500 Subject: [PATCH 35/58] fix types --- packages/kbn-optimizer/limits.yml | 1 + src/plugins/management/public/mocks/index.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index d979ab1ce679f..a053306e3da50 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -114,6 +114,7 @@ pageLoadAssetSize: securitySolution: 66738 serverless: 16573 serverlessObservability: 16582 + serverlessSearch: 17548 serverlessSecurity: 16556 sessionView: 77750 share: 71239 diff --git a/src/plugins/management/public/mocks/index.ts b/src/plugins/management/public/mocks/index.ts index 92f6f9e1ed4ce..733c98876969a 100644 --- a/src/plugins/management/public/mocks/index.ts +++ b/src/plugins/management/public/mocks/index.ts @@ -39,6 +39,7 @@ const createSetupContract = (): ManagementSetup => ({ state: {}, })), }, + setIsSidebarEnabled: jest.fn(() => true), }); const createStartContract = (): ManagementStart => ({ From 57963b39fee32c82e03e4ecbc3da19ae9ee03dbf Mon Sep 17 00:00:00 2001 From: Rodney Norris Date: Thu, 9 Mar 2023 17:17:57 +0000 Subject: [PATCH 36/58] chore: fix tests and types for isSidebarEnabled --- .../__mocks__/kea_logic/kibana_logic.mock.ts | 1 + .../public/applications/index.test.tsx | 1 + .../applications/shared/layout/nav.test.tsx | 22 +++++++++---------- .../management/management_service.test.ts | 3 +++ 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea_logic/kibana_logic.mock.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea_logic/kibana_logic.mock.ts index ff4760c11f5c1..a4e5bfeae0150 100644 --- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea_logic/kibana_logic.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea_logic/kibana_logic.mock.ts @@ -31,6 +31,7 @@ export const mockKibanaValues = { guidedOnboarding: {}, history: mockHistory, isCloud: false, + isSidebarEnabled: true, navigateToUrl: jest.fn(), productAccess: { hasAppSearchAccess: true, diff --git a/x-pack/plugins/enterprise_search/public/applications/index.test.tsx b/x-pack/plugins/enterprise_search/public/applications/index.test.tsx index 0915e17ac6ab1..be5d263d531cd 100644 --- a/x-pack/plugins/enterprise_search/public/applications/index.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/index.test.tsx @@ -33,6 +33,7 @@ describe('renderApp', () => { licensing: licensingMock.createStart(), security: securityMock.createStart(), }, + isSidebarEnabled: true, } as any; const pluginData = { config: {}, diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.test.tsx index 4db2e43c51fb2..e220736bb0ac2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.test.tsx @@ -105,7 +105,7 @@ describe('useEnterpriseSearchContentNav', () => { mockKibanaValues.uiSettings.get.mockReturnValue(false); const esNav = useEnterpriseSearchNav(); - const searchNav = esNav.find((item) => item.id === 'search'); + const searchNav = esNav?.find((item) => item.id === 'search'); expect(searchNav).not.toBeUndefined(); expect(searchNav).toEqual({ id: 'search', @@ -135,7 +135,7 @@ describe('useEnterpriseSearchContentNav', () => { setMockValues({ productAccess: workplaceSearchProductAccess }); const esNav = useEnterpriseSearchNav(); - const searchNav = esNav.find((item) => item.id === 'search'); + const searchNav = esNav?.find((item) => item.id === 'search'); expect(searchNav).not.toBeUndefined(); expect(searchNav).toEqual({ id: 'search', @@ -170,7 +170,7 @@ describe('useEnterpriseSearchContentNav', () => { setMockValues({ productAccess: appSearchProductAccess }); const esNav = useEnterpriseSearchNav(); - const searchNav = esNav.find((item) => item.id === 'search'); + const searchNav = esNav?.find((item) => item.id === 'search'); expect(searchNav).not.toBeUndefined(); expect(searchNav).toEqual({ id: 'search', @@ -204,7 +204,7 @@ describe('useEnterpriseSearchContentNav', () => { setMockValues({ productAccess: fullProductAccess }); const esNav = useEnterpriseSearchNav(); - expect(esNav.find((item) => item.id === 'enginesSearch')).toBeUndefined(); + expect(esNav?.find((item) => item.id === 'enginesSearch')).toBeUndefined(); }); }); @@ -304,7 +304,7 @@ describe('useEnterpriseSearchContentNav Engines feature flag', () => { setMockValues({ productAccess: fullProductAccess }); const esNav = useEnterpriseSearchNav(); - expect(esNav.find((item) => item.id === 'standaloneExperiences')).toBeUndefined(); + expect(esNav?.find((item) => item.id === 'standaloneExperiences')).toBeUndefined(); }); it('excludes App Search when the user has no access to it', () => { const fullProductAccess: ProductAccess = { @@ -315,7 +315,7 @@ describe('useEnterpriseSearchContentNav Engines feature flag', () => { setMockValues({ productAccess: fullProductAccess }); const esNav = useEnterpriseSearchNav(); - const standAloneNav = esNav.find((item) => item.id === 'standaloneExperiences'); + const standAloneNav = esNav?.find((item) => item.id === 'standaloneExperiences'); expect(standAloneNav).not.toBeUndefined(); expect(standAloneNav).toEqual({ id: 'standaloneExperiences', @@ -338,7 +338,7 @@ describe('useEnterpriseSearchContentNav Engines feature flag', () => { setMockValues({ productAccess: fullProductAccess }); const esNav = useEnterpriseSearchNav(); - const standAloneNav = esNav.find((item) => item.id === 'standaloneExperiences'); + const standAloneNav = esNav?.find((item) => item.id === 'standaloneExperiences'); expect(standAloneNav).not.toBeUndefined(); expect(standAloneNav).toEqual({ id: 'standaloneExperiences', @@ -444,14 +444,14 @@ describe('useEnterpriseSearchEngineNav', () => { it('returns selected engine sub nav items', () => { const engineName = 'my-test-engine'; const navItems = useEnterpriseSearchEngineNav(engineName); - expect(navItems.map((ni) => ni.name)).toEqual([ + expect(navItems?.map((ni) => ni.name)).toEqual([ 'Overview', 'Content', 'Search', 'Behavioral Analytics', 'Standalone Experiences', ]); - const searchItem = navItems.find((ni) => ni.id === 'enginesSearch'); + const searchItem = navItems?.find((ni) => ni.id === 'enginesSearch'); expect(searchItem).not.toBeUndefined(); expect(searchItem!.items).not.toBeUndefined(); // @ts-ignore @@ -501,14 +501,14 @@ describe('useEnterpriseSearchEngineNav', () => { it('returns selected engine without tabs when isEmpty', () => { const engineName = 'my-test-engine'; const navItems = useEnterpriseSearchEngineNav(engineName, true); - expect(navItems.map((ni) => ni.name)).toEqual([ + expect(navItems?.map((ni) => ni.name)).toEqual([ 'Overview', 'Content', 'Search', 'Behavioral Analytics', 'Standalone Experiences', ]); - const searchItem = navItems.find((ni) => ni.id === 'enginesSearch'); + const searchItem = navItems?.find((ni) => ni.id === 'enginesSearch'); expect(searchItem).not.toBeUndefined(); expect(searchItem!.items).not.toBeUndefined(); // @ts-ignore diff --git a/x-pack/plugins/security/public/management/management_service.test.ts b/x-pack/plugins/security/public/management/management_service.test.ts index 49fdd6864ec3d..7e0eef83cca1d 100644 --- a/x-pack/plugins/security/public/management/management_service.test.ts +++ b/x-pack/plugins/security/public/management/management_service.test.ts @@ -25,6 +25,7 @@ import { rolesManagementApp } from './roles'; import { usersManagementApp } from './users'; const mockSection = createManagementSectionMock(); +const mockSetIsSidebarEnabled = (enabled: boolean) => {}; describe('ManagementService', () => { describe('setup()', () => { @@ -41,6 +42,7 @@ describe('ManagementService', () => { } as DefinedSections, }, locator: {} as any, + setIsSidebarEnabled: mockSetIsSidebarEnabled, }; const service = new ManagementService(); @@ -103,6 +105,7 @@ describe('ManagementService', () => { } as DefinedSections, }, locator: {} as any, + setIsSidebarEnabled: mockSetIsSidebarEnabled, }; service.setup({ From 724f5e28e304c0fb9f4eb8d5c43b31fbb5a34949 Mon Sep 17 00:00:00 2001 From: Yulia Cech Date: Mon, 6 Mar 2023 11:43:48 +0100 Subject: [PATCH 37/58] [Serverless] Add a config to disable Upgrade Assistant --- config/serverless.yml | 2 ++ x-pack/plugins/upgrade_assistant/server/config.ts | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/config/serverless.yml b/config/serverless.yml index 3ceed35abeeff..ffe0998840d02 100644 --- a/config/serverless.yml +++ b/config/serverless.yml @@ -1,3 +1,5 @@ xpack.serverless.plugin.enabled: true xpack.fleet.enableExperimental: ['fleetServerStandalone'] + +xpack.upgrade_assistant.enabled: false diff --git a/x-pack/plugins/upgrade_assistant/server/config.ts b/x-pack/plugins/upgrade_assistant/server/config.ts index 6202a6680708a..bf872f50b5222 100644 --- a/x-pack/plugins/upgrade_assistant/server/config.ts +++ b/x-pack/plugins/upgrade_assistant/server/config.ts @@ -12,6 +12,11 @@ import { PluginConfigDescriptor } from '@kbn/core/server'; // even for minor releases. // ------------------------------- const configSchema = schema.object({ + /** + * Disables the plugin. + */ + enabled: schema.boolean({ defaultValue: true }), + featureSet: schema.object({ /** * Ml Snapshot should only be enabled for major version upgrades. Currently this @@ -39,6 +44,9 @@ const configSchema = schema.object({ */ reindexCorrectiveActions: schema.boolean({ defaultValue: false }), }), + /** + * This config allows to hide the UI without disabling the plugin. + */ ui: schema.object({ enabled: schema.boolean({ defaultValue: true }), }), From 9a1b42d2b2f147aeb4af12d804a46a1c00d431a4 Mon Sep 17 00:00:00 2001 From: Yulia Cech Date: Mon, 6 Mar 2023 13:31:16 +0100 Subject: [PATCH 38/58] [Serverless] Add a config to disable Rollup jobs --- config/serverless.yml | 1 + x-pack/plugins/rollup/server/config.ts | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/config/serverless.yml b/config/serverless.yml index ffe0998840d02..33b7f6d1a19dd 100644 --- a/config/serverless.yml +++ b/config/serverless.yml @@ -3,3 +3,4 @@ xpack.serverless.plugin.enabled: true xpack.fleet.enableExperimental: ['fleetServerStandalone'] xpack.upgrade_assistant.enabled: false +xpack.rollup.enabled: false diff --git a/x-pack/plugins/rollup/server/config.ts b/x-pack/plugins/rollup/server/config.ts index 235202a23db24..953cd4b283f97 100644 --- a/x-pack/plugins/rollup/server/config.ts +++ b/x-pack/plugins/rollup/server/config.ts @@ -22,6 +22,11 @@ const schemaLatest = schema.object( ui: schema.object({ enabled: schema.boolean({ defaultValue: true }), }), + /** + * Disables the plugin. + * Added back in 8.8. + */ + enabled: schema.boolean({ defaultValue: true }), }, { defaultValue: undefined } ); From c290858a9c58d4715eb272b6b477b395c0c48b64 Mon Sep 17 00:00:00 2001 From: Yulia Cech Date: Mon, 6 Mar 2023 14:44:30 +0100 Subject: [PATCH 39/58] [Serverless] Move watcher config to the file serverless.yml --- config/serverless.oblt.yml | 1 - config/serverless.security.yml | 1 - config/serverless.yml | 1 + 3 files changed, 1 insertion(+), 2 deletions(-) diff --git a/config/serverless.oblt.yml b/config/serverless.oblt.yml index b35e67c81aee7..6023b65b82e82 100644 --- a/config/serverless.oblt.yml +++ b/config/serverless.oblt.yml @@ -3,7 +3,6 @@ xpack.canvas.enabled: false xpack.cloudSecurityPosture.enabled: false xpack.reporting.enabled: false xpack.securitySolution.enabled: false -xpack.watcher.enabled: false xpack.serverless.observability.enabled: true diff --git a/config/serverless.security.yml b/config/serverless.security.yml index a5e1c170ddccd..241c87d23ab4d 100644 --- a/config/serverless.security.yml +++ b/config/serverless.security.yml @@ -3,7 +3,6 @@ xpack.canvas.enabled: false xpack.observability.enabled: false xpack.reporting.enabled: false xpack.uptime.enabled: false -xpack.watcher.enabled: false xpack.serverless.security.enabled: true diff --git a/config/serverless.yml b/config/serverless.yml index 33b7f6d1a19dd..248724be3eb5a 100644 --- a/config/serverless.yml +++ b/config/serverless.yml @@ -4,3 +4,4 @@ xpack.fleet.enableExperimental: ['fleetServerStandalone'] xpack.upgrade_assistant.enabled: false xpack.rollup.enabled: false +xpack.watcher.enabled: false From aad4053d65f9bfba184e2b12cd678c9819f73cba Mon Sep 17 00:00:00 2001 From: Yulia Cech Date: Mon, 6 Mar 2023 15:01:57 +0100 Subject: [PATCH 40/58] [Serverless] Add a config to disable CCR --- config/serverless.yml | 1 + x-pack/plugins/cross_cluster_replication/server/config.ts | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/config/serverless.yml b/config/serverless.yml index 248724be3eb5a..d4c9085caae7a 100644 --- a/config/serverless.yml +++ b/config/serverless.yml @@ -5,3 +5,4 @@ xpack.fleet.enableExperimental: ['fleetServerStandalone'] xpack.upgrade_assistant.enabled: false xpack.rollup.enabled: false xpack.watcher.enabled: false +xpack.ccr.enabled: false diff --git a/x-pack/plugins/cross_cluster_replication/server/config.ts b/x-pack/plugins/cross_cluster_replication/server/config.ts index bac5f917f22a6..4cba6d0707abb 100644 --- a/x-pack/plugins/cross_cluster_replication/server/config.ts +++ b/x-pack/plugins/cross_cluster_replication/server/config.ts @@ -22,6 +22,11 @@ const schemaLatest = schema.object( ui: schema.object({ enabled: schema.boolean({ defaultValue: true }), }), + /** + * Disables the plugin. + * Added back in 8.8. + */ + enabled: schema.boolean({ defaultValue: true }), }, { defaultValue: undefined } ); From 2a6a1145abcee4602ac12fb5f1aafdf35ed21234 Mon Sep 17 00:00:00 2001 From: Yulia Cech Date: Mon, 6 Mar 2023 16:36:58 +0100 Subject: [PATCH 41/58] [Serverless] Add a config to disable ILM --- config/serverless.yml | 1 + x-pack/plugins/index_lifecycle_management/server/config.ts | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/config/serverless.yml b/config/serverless.yml index d4c9085caae7a..c563d15165468 100644 --- a/config/serverless.yml +++ b/config/serverless.yml @@ -6,3 +6,4 @@ xpack.upgrade_assistant.enabled: false xpack.rollup.enabled: false xpack.watcher.enabled: false xpack.ccr.enabled: false +xpack.ilm.enabled: false diff --git a/x-pack/plugins/index_lifecycle_management/server/config.ts b/x-pack/plugins/index_lifecycle_management/server/config.ts index 737cc6a472c7a..7fdec20bbb050 100644 --- a/x-pack/plugins/index_lifecycle_management/server/config.ts +++ b/x-pack/plugins/index_lifecycle_management/server/config.ts @@ -24,6 +24,11 @@ const schemaLatest = schema.object( }), // Cloud requires the ability to hide internal node attributes from users. filteredNodeAttributes: schema.arrayOf(schema.string(), { defaultValue: [] }), + /** + * Disables the plugin. + * Added back in 8.8. + */ + enabled: schema.boolean({ defaultValue: true }), }, { defaultValue: undefined } ); From 807e11a3d56664b74f744a6fa42e302df0fee198 Mon Sep 17 00:00:00 2001 From: Yulia Cech Date: Mon, 6 Mar 2023 17:22:14 +0100 Subject: [PATCH 42/58] [Serverless] Add a config to disable Remote clusters --- config/serverless.yml | 1 + x-pack/plugins/remote_clusters/server/config.ts | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/config/serverless.yml b/config/serverless.yml index c563d15165468..f157e53412324 100644 --- a/config/serverless.yml +++ b/config/serverless.yml @@ -7,3 +7,4 @@ xpack.rollup.enabled: false xpack.watcher.enabled: false xpack.ccr.enabled: false xpack.ilm.enabled: false +xpack.remote_clusters.enabled: false diff --git a/x-pack/plugins/remote_clusters/server/config.ts b/x-pack/plugins/remote_clusters/server/config.ts index 32db006e8171a..4f6c56191cd89 100644 --- a/x-pack/plugins/remote_clusters/server/config.ts +++ b/x-pack/plugins/remote_clusters/server/config.ts @@ -22,6 +22,11 @@ const schemaLatest = schema.object( ui: schema.object({ enabled: schema.boolean({ defaultValue: true }), }), + /** + * Disables the plugin. + * Added back in 8.8. + */ + enabled: schema.boolean({ defaultValue: true }), }, { defaultValue: undefined } ); From 101bfe6e1219352e2318e85d8ce32f524de66e46 Mon Sep 17 00:00:00 2001 From: Yulia Cech Date: Mon, 6 Mar 2023 17:53:23 +0100 Subject: [PATCH 43/58] [Serverless] Add a config to disable Snapshot & Restore --- config/serverless.yml | 1 + x-pack/plugins/snapshot_restore/server/config.ts | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/config/serverless.yml b/config/serverless.yml index f157e53412324..d0eeb84c4c551 100644 --- a/config/serverless.yml +++ b/config/serverless.yml @@ -8,3 +8,4 @@ xpack.watcher.enabled: false xpack.ccr.enabled: false xpack.ilm.enabled: false xpack.remote_clusters.enabled: false +xpack.snapshot_restore.enabled: false diff --git a/x-pack/plugins/snapshot_restore/server/config.ts b/x-pack/plugins/snapshot_restore/server/config.ts index d259b6674391a..e2452e5b58e54 100644 --- a/x-pack/plugins/snapshot_restore/server/config.ts +++ b/x-pack/plugins/snapshot_restore/server/config.ts @@ -25,6 +25,11 @@ const schemaLatest = schema.object( slm_ui: schema.object({ enabled: schema.boolean({ defaultValue: true }), }), + /** + * Disables the plugin. + * Added back in 8.8. + */ + enabled: schema.boolean({ defaultValue: true }), }, { defaultValue: undefined } ); From 81648a34576d740e298080c7347c2c3d45682ca6 Mon Sep 17 00:00:00 2001 From: Yulia Cech Date: Thu, 9 Mar 2023 17:45:34 +0100 Subject: [PATCH 44/58] [Serverless] Add a config to disable License Management --- config/serverless.yml | 1 + x-pack/plugins/license_management/server/config.ts | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/config/serverless.yml b/config/serverless.yml index d0eeb84c4c551..ec4f5ab51d430 100644 --- a/config/serverless.yml +++ b/config/serverless.yml @@ -9,3 +9,4 @@ xpack.ccr.enabled: false xpack.ilm.enabled: false xpack.remote_clusters.enabled: false xpack.snapshot_restore.enabled: false +xpack.license_management.enabled: false diff --git a/x-pack/plugins/license_management/server/config.ts b/x-pack/plugins/license_management/server/config.ts index 42beba0ea5c09..23449bc19e793 100644 --- a/x-pack/plugins/license_management/server/config.ts +++ b/x-pack/plugins/license_management/server/config.ts @@ -22,6 +22,11 @@ const schemaLatest = schema.object( ui: schema.object({ enabled: schema.boolean({ defaultValue: true }), }), + /** + * Disables the plugin. + * Added back in 8.8. + */ + enabled: schema.boolean({ defaultValue: true }), }, { defaultValue: undefined } ); From 590003fda754c0b3e27cc9957e0d1ead6e57f1c9 Mon Sep 17 00:00:00 2001 From: Yulia Cech Date: Fri, 10 Mar 2023 14:32:39 +0100 Subject: [PATCH 45/58] [Serverless] Add a comment --- config/serverless.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/config/serverless.yml b/config/serverless.yml index ec4f5ab51d430..c232a15421f2d 100644 --- a/config/serverless.yml +++ b/config/serverless.yml @@ -2,6 +2,7 @@ xpack.serverless.plugin.enabled: true xpack.fleet.enableExperimental: ['fleetServerStandalone'] +# Management team plugins xpack.upgrade_assistant.enabled: false xpack.rollup.enabled: false xpack.watcher.enabled: false From 4d932a740b928dac4cbea2f644e049586a7fbd97 Mon Sep 17 00:00:00 2001 From: Clint Andrew Hall Date: Fri, 10 Mar 2023 20:54:02 -0500 Subject: [PATCH 46/58] fix translations --- x-pack/plugins/serverless/.i18nrc.json | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 x-pack/plugins/serverless/.i18nrc.json diff --git a/x-pack/plugins/serverless/.i18nrc.json b/x-pack/plugins/serverless/.i18nrc.json deleted file mode 100644 index 4e5106eb2cfca..0000000000000 --- a/x-pack/plugins/serverless/.i18nrc.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "prefix": "serverless", - "paths": { - "serverless": "." - }, - "translations": ["translations/ja-JP.json"] -} From a065c37ac287bad0b93c9698c724f2058304d694 Mon Sep 17 00:00:00 2001 From: Clint Andrew Hall Date: Sun, 12 Mar 2023 23:33:21 -0400 Subject: [PATCH 47/58] [dx] Add project type switcher --- config/serverless.yml | 3 +- package.json | 1 + .../src/chrome_service.tsx | 1 + .../src/ui/solution/header.tsx | 7 ++ src/cli/serve/serve.js | 44 ++++++++- x-pack/plugins/security/public/config.ts | 1 + .../nav_control/nav_control_service.tsx | 7 +- x-pack/plugins/security/public/plugin.tsx | 1 + x-pack/plugins/security/server/config.ts | 1 + x-pack/plugins/serverless/common/index.ts | 2 + .../serverless/public/components/index.ts | 8 ++ .../public/components/switcher/index.tsx | 96 +++++++++++++++++++ .../public/components/switcher/item.tsx | 20 ++++ .../public/components/switcher/loader.tsx | 26 +++++ .../public/components/switcher/logo.tsx | 31 ++++++ x-pack/plugins/serverless/public/index.ts | 2 - x-pack/plugins/serverless/public/plugin.ts | 25 ----- x-pack/plugins/serverless/public/plugin.tsx | 54 +++++++++++ x-pack/plugins/serverless/public/types.ts | 2 + x-pack/plugins/serverless/server/plugin.ts | 41 +++++++- 20 files changed, 337 insertions(+), 36 deletions(-) create mode 100644 x-pack/plugins/serverless/public/components/index.ts create mode 100644 x-pack/plugins/serverless/public/components/switcher/index.tsx create mode 100644 x-pack/plugins/serverless/public/components/switcher/item.tsx create mode 100644 x-pack/plugins/serverless/public/components/switcher/loader.tsx create mode 100644 x-pack/plugins/serverless/public/components/switcher/logo.tsx delete mode 100644 x-pack/plugins/serverless/public/plugin.ts create mode 100644 x-pack/plugins/serverless/public/plugin.tsx diff --git a/config/serverless.yml b/config/serverless.yml index 3ceed35abeeff..2d53f3de01d3d 100644 --- a/config/serverless.yml +++ b/config/serverless.yml @@ -1,3 +1,4 @@ - +newsfeed.enabled: false +xpack.security.showNavLinks: false xpack.serverless.plugin.enabled: true xpack.fleet.enableExperimental: ['fleetServerStandalone'] diff --git a/package.json b/package.json index a5e9d7d8273af..3627ccca4abc2 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "lint:es": "node scripts/eslint", "lint:style": "node scripts/stylelint", "makelogs": "node scripts/makelogs", + "serverless": "node scripts/kibana --dev --serverless", "serverless-es": "node scripts/kibana --dev --serverless=es", "serverless-oblt": "node scripts/kibana --dev --serverless=oblt", "serverless-security": "node scripts/kibana --dev --serverless=security", diff --git a/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx b/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx index 07f27067f3883..99ad7869f429a 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx @@ -241,6 +241,7 @@ export class ChromeService { breadcrumbs$={breadcrumbs$.pipe(takeUntil(this.stop$))} helpExtension$={helpExtension$.pipe(takeUntil(this.stop$))} helpSupportUrl$={helpSupportUrl$.pipe(takeUntil(this.stop$))} + navControlsRight$={navControls.getRight$()} kibanaDocLink={docLinks.links.kibana.guide} kibanaVersion={injectedMetadata.getKibanaVersion()} /> diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/solution/header.tsx b/packages/core/chrome/core-chrome-browser-internal/src/ui/solution/header.tsx index 0aef9755994d8..7536371eb4a19 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/ui/solution/header.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/solution/header.tsx @@ -21,6 +21,7 @@ import { ChromeBreadcrumb, ChromeGlobalHelpExtensionMenuLink, ChromeHelpExtension, + ChromeNavControl, } from '@kbn/core-chrome-browser/src'; import { Observable } from 'rxjs'; import { MountPoint } from '@kbn/core-mount-utils-browser'; @@ -28,6 +29,7 @@ import { InternalApplicationStart } from '@kbn/core-application-browser-internal import { HeaderBreadcrumbs } from '../header/header_breadcrumbs'; import { HeaderActionMenu } from '../header/header_action_menu'; import { HeaderHelpMenu } from '../header/header_help_menu'; +import { HeaderNavControls } from '../header/header_nav_controls'; interface Props { breadcrumbs$: Observable; @@ -39,6 +41,7 @@ interface Props { kibanaVersion: string; application: InternalApplicationStart; navigation: JSX.Element; + navControlsRight$: Observable; } export const SolutionHeader = ({ @@ -82,6 +85,10 @@ export const SolutionHeader = ({ + + + + diff --git a/src/cli/serve/serve.js b/src/cli/serve/serve.js index 4a875d6955428..bd5d0f47e7e11 100644 --- a/src/cli/serve/serve.js +++ b/src/cli/serve/serve.js @@ -8,7 +8,7 @@ import { set as lodashSet } from '@kbn/safer-lodash-set'; import _ from 'lodash'; -import { statSync } from 'fs'; +import { statSync, copyFileSync, existsSync } from 'fs'; import { resolve } from 'path'; import url from 'url'; @@ -29,7 +29,7 @@ function getServerlessProjectMode(opts) { return null; } - if (VALID_SERVERLESS_PROJECT_MODE.includes(opts.serverless)) { + if (VALID_SERVERLESS_PROJECT_MODE.includes(opts.serverless) || opts.serverless === true) { return opts.serverless; } @@ -97,6 +97,33 @@ function maybeAddConfig(name, configs, method) { } } +/** + * @param {string} file + * @param {'es' | 'security' | 'oblt' | true} mode + * @param {string[]} configs + * @param {'push' | 'unshift'} method + */ +function maybeSetRecentConfig(file, mode, configs, method) { + const path = resolve(getConfigDirectory(), file); + + try { + if (mode === true && !existsSync(path)) { + const esPath = path.replace('recent', 'es'); + copyFileSync(esPath, path); + } else if (mode !== true) { + copyFileSync(path.replace('recent', mode), path); + } + + configs[method](path); + } catch (err) { + if (err.code === 'ENOENT') { + return; + } + + throw err; + } +} + /** * @returns {string[]} */ @@ -234,7 +261,14 @@ export default function (program) { '--run-examples', 'Adds plugin paths for all the Kibana example plugins and runs with no base path' ) - .option('--serverless ', 'Start Kibana in a serverless project mode'); + .option( + '--serverless', + 'Start Kibana in the most recent serverless project mode, (default is es)' + ) + .option( + '--serverless ', + 'Start Kibana in a specific serverless project mode' + ); } if (DEV_MODE_SUPPORTED) { @@ -264,7 +298,7 @@ export default function (program) { // we "unshift" .serverless. config so that it only overrides defaults if (serverlessMode) { maybeAddConfig(`serverless.yml`, configs, 'push'); - maybeAddConfig(`serverless.${serverlessMode}.yml`, configs, 'unshift'); + maybeSetRecentConfig('serverless.recent.yml', serverlessMode, configs, 'unshift'); } // .dev. configs are "pushed" so that they override all other config files @@ -272,7 +306,7 @@ export default function (program) { maybeAddConfig('kibana.dev.yml', configs, 'push'); if (serverlessMode) { maybeAddConfig(`serverless.dev.yml`, configs, 'push'); - maybeAddConfig(`serverless.${serverlessMode}.dev.yml`, configs, 'push'); + maybeSetRecentConfig('serverless.recent.dev.yml', serverlessMode, configs, 'unshift'); } } diff --git a/x-pack/plugins/security/public/config.ts b/x-pack/plugins/security/public/config.ts index 440bd8da27d90..6a5a8dac01500 100644 --- a/x-pack/plugins/security/public/config.ts +++ b/x-pack/plugins/security/public/config.ts @@ -9,4 +9,5 @@ export interface ConfigType { loginAssistanceMessage: string; showInsecureClusterWarning: boolean; sameSiteCookies: 'Strict' | 'Lax' | 'None' | undefined; + showNavLinks: boolean; } diff --git a/x-pack/plugins/security/public/nav_control/nav_control_service.tsx b/x-pack/plugins/security/public/nav_control/nav_control_service.tsx index 91d0c33ade107..a6fd2eaf61fd1 100644 --- a/x-pack/plugins/security/public/nav_control/nav_control_service.tsx +++ b/x-pack/plugins/security/public/nav_control/nav_control_service.tsx @@ -29,6 +29,7 @@ interface SetupDeps { securityLicense: SecurityLicense; logoutUrl: string; securityApiClients: SecurityApiClients; + showNavLinks: boolean; } interface StartDeps { @@ -54,16 +55,18 @@ export class SecurityNavControlService { private securityApiClients!: SecurityApiClients; private navControlRegistered!: boolean; + private showNavLinks!: boolean; private securityFeaturesSubscription?: Subscription; private readonly stop$ = new ReplaySubject(1); private userMenuLinks$ = new BehaviorSubject([]); - public setup({ securityLicense, logoutUrl, securityApiClients }: SetupDeps) { + public setup({ securityLicense, logoutUrl, securityApiClients, showNavLinks }: SetupDeps) { this.securityLicense = securityLicense; this.logoutUrl = logoutUrl; this.securityApiClients = securityApiClients; + this.showNavLinks = showNavLinks; } public start({ core, authc }: StartDeps): SecurityNavControlServiceStart { @@ -72,7 +75,7 @@ export class SecurityNavControlService { const isAnonymousPath = core.http.anonymousPaths.isAnonymous(window.location.pathname); const shouldRegisterNavControl = - !isAnonymousPath && showLinks && !this.navControlRegistered; + this.showNavLinks && !isAnonymousPath && showLinks && !this.navControlRegistered; if (shouldRegisterNavControl) { this.registerSecurityNavControl(core, authc); } diff --git a/x-pack/plugins/security/public/plugin.tsx b/x-pack/plugins/security/public/plugin.tsx index c56c40f63b4d0..084a34e635dcf 100644 --- a/x-pack/plugins/security/public/plugin.tsx +++ b/x-pack/plugins/security/public/plugin.tsx @@ -107,6 +107,7 @@ export class SecurityPlugin securityLicense: license, logoutUrl: getLogoutUrl(core.http), securityApiClients: this.securityApiClients, + showNavLinks: this.config.showNavLinks, }); this.analyticsService.setup({ diff --git a/x-pack/plugins/security/server/config.ts b/x-pack/plugins/security/server/config.ts index 5620b35c1fef7..91abf77a376f8 100644 --- a/x-pack/plugins/security/server/config.ts +++ b/x-pack/plugins/security/server/config.ts @@ -204,6 +204,7 @@ export const ConfigSchema = schema.object({ loginAssistanceMessage: schema.string({ defaultValue: '' }), showInsecureClusterWarning: schema.boolean({ defaultValue: true }), loginHelp: schema.maybe(schema.string()), + showNavLinks: schema.boolean({ defaultValue: true }), cookieName: schema.string({ defaultValue: 'sid' }), encryptionKey: schema.conditional( schema.contextRef('dist'), diff --git a/x-pack/plugins/serverless/common/index.ts b/x-pack/plugins/serverless/common/index.ts index f28f6aa0c3623..d171cea3f5d68 100644 --- a/x-pack/plugins/serverless/common/index.ts +++ b/x-pack/plugins/serverless/common/index.ts @@ -7,3 +7,5 @@ export const PLUGIN_ID = 'serverless'; export const PLUGIN_NAME = 'serverless'; + +export const API_SWITCH_PROJECT = '/api/serverless/switch_project'; diff --git a/x-pack/plugins/serverless/public/components/index.ts b/x-pack/plugins/serverless/public/components/index.ts new file mode 100644 index 0000000000000..c5dd3bd763481 --- /dev/null +++ b/x-pack/plugins/serverless/public/components/index.ts @@ -0,0 +1,8 @@ +/* + * 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. + */ + +export { Switcher } from './switcher'; diff --git a/x-pack/plugins/serverless/public/components/switcher/index.tsx b/x-pack/plugins/serverless/public/components/switcher/index.tsx new file mode 100644 index 0000000000000..6d9b673a676c2 --- /dev/null +++ b/x-pack/plugins/serverless/public/components/switcher/index.tsx @@ -0,0 +1,96 @@ +/* + * 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 React, { useState } from 'react'; +import ReactDOM from 'react-dom'; +import { + EuiPopover, + useGeneratedHtmlId, + EuiPopoverTitle, + EuiTitle, + EuiSpacer, + EuiHeaderSectionItemButton, +} from '@elastic/eui'; +import { HttpStart } from '@kbn/core-http-browser'; +import { API_SWITCH_PROJECT } from '../../../common'; +import { ProjectType } from '../../types'; +import { Loader } from './loader'; +import { SwitcherItem } from './item'; + +export const Switcher = ({ http }: { http: HttpStart }) => { + const [isOpen, setIsOpen] = useState(false); + const id = useGeneratedHtmlId({ + prefix: 'switcherPopover', + }); + + const onButtonClick = () => { + setIsOpen(!isOpen); + }; + + const closePopover = () => { + setIsOpen(false); + }; + + const handleSwitch = (project: ProjectType, e: React.MouseEvent) => { + e.preventDefault(); + closePopover(); + http.post(API_SWITCH_PROJECT, { body: JSON.stringify({ id: project }) }); + ReactDOM.render(, document.body); + setTimeout(() => { + window.location.href = '/'; + }, 2000); + return false; + }; + + const items = ( + <> + handleSwitch('oblt', e)} + /> + + handleSwitch('es', e)} + /> + + handleSwitch('security', e)} + /> + + ); + + const button = ( + + ); + + return ( + + Developer Tools + + +

Switch Project Type

+
+ +
{items}
+
+ ); +}; diff --git a/x-pack/plugins/serverless/public/components/switcher/item.tsx b/x-pack/plugins/serverless/public/components/switcher/item.tsx new file mode 100644 index 0000000000000..e158208acd889 --- /dev/null +++ b/x-pack/plugins/serverless/public/components/switcher/item.tsx @@ -0,0 +1,20 @@ +/* + * 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 React from 'react'; +import { EuiButtonEmpty, type EuiIconProps } from '@elastic/eui'; + +interface ItemProps extends Pick { + label: string; + onClick: React.MouseEventHandler; +} + +export const SwitcherItem = ({ type, label, onClick }: ItemProps) => ( + + {label} + +); diff --git a/x-pack/plugins/serverless/public/components/switcher/loader.tsx b/x-pack/plugins/serverless/public/components/switcher/loader.tsx new file mode 100644 index 0000000000000..a3a9178d0380b --- /dev/null +++ b/x-pack/plugins/serverless/public/components/switcher/loader.tsx @@ -0,0 +1,26 @@ +/* + * 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 React from 'react'; + +import { i18n } from '@kbn/i18n'; + +import { Logo, type Props } from './logo'; + +export const Loader = (props: Props) => ( +
+
+ +
+ {i18n.translate('core.ui.welcomeMessage', { + defaultMessage: 'Loading Project', + })} +
+
+
+
+); diff --git a/x-pack/plugins/serverless/public/components/switcher/logo.tsx b/x-pack/plugins/serverless/public/components/switcher/logo.tsx new file mode 100644 index 0000000000000..a887a75edcd9b --- /dev/null +++ b/x-pack/plugins/serverless/public/components/switcher/logo.tsx @@ -0,0 +1,31 @@ +/* + * 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 React from 'react'; +import { EuiIcon } from '@elastic/eui'; +import type { ProjectType } from '../../types'; + +export interface Props { + project: ProjectType; +} + +export const Logo = ({ project }: Props) => { + let type = 'logoElastic'; + switch (project) { + case 'es': + type = 'logoElasticsearch'; + break; + case 'security': + type = 'logoSecurity'; + break; + case 'oblt': + type = 'logoObservability'; + break; + } + + return ; +}; diff --git a/x-pack/plugins/serverless/public/index.ts b/x-pack/plugins/serverless/public/index.ts index 4e5b416edd87c..d47e125a14ab6 100644 --- a/x-pack/plugins/serverless/public/index.ts +++ b/x-pack/plugins/serverless/public/index.ts @@ -7,8 +7,6 @@ import { ServerlessPlugin } from './plugin'; -// This exports static code and TypeScript types, -// as well as, Kibana Platform `plugin()` initializer. export function plugin() { return new ServerlessPlugin(); } diff --git a/x-pack/plugins/serverless/public/plugin.ts b/x-pack/plugins/serverless/public/plugin.ts deleted file mode 100644 index 0c9816bd026ae..0000000000000 --- a/x-pack/plugins/serverless/public/plugin.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; -import { ServerlessPluginSetup, ServerlessPluginStart } from './types'; - -export class ServerlessPlugin implements Plugin { - public setup(_core: CoreSetup): ServerlessPluginSetup { - return {}; - } - - public start(core: CoreStart): ServerlessPluginStart { - core.chrome.setChromeStyle('solution'); - return { - setServerlessNavigation: (navigation: JSX.Element) => - core.chrome.setSolutionNavigation(navigation), - }; - } - - public stop() {} -} diff --git a/x-pack/plugins/serverless/public/plugin.tsx b/x-pack/plugins/serverless/public/plugin.tsx new file mode 100644 index 0000000000000..48c8e1d2b684c --- /dev/null +++ b/x-pack/plugins/serverless/public/plugin.tsx @@ -0,0 +1,54 @@ +/* + * 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 * as Rx from 'rxjs'; +import ReactDOM from 'react-dom'; +import React from 'react'; +import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; +import { CoreSetup, CoreStart, Plugin, CoreTheme } from '@kbn/core/public'; +import { I18nProvider } from '@kbn/i18n-react'; +import { HttpStart } from '@kbn/core-http-browser'; + +import { ServerlessPluginSetup, ServerlessPluginStart } from './types'; +import { Switcher } from './components'; + +export class ServerlessPlugin implements Plugin { + public setup(_core: CoreSetup): ServerlessPluginSetup { + return {}; + } + + public start(core: CoreStart): ServerlessPluginStart { + core.chrome.setChromeStyle('solution'); + + if (process.env.NODE_ENV !== 'production') { + core.chrome.navControls.registerRight({ + order: 500, + mount: (target) => this.mount(target, core.theme.theme$, core.http), + }); + } + + return { + setServerlessNavigation: (navigation: JSX.Element) => + core.chrome.setSolutionNavigation(navigation), + }; + } + + public stop() {} + + private mount(targetDomElement: HTMLElement, theme$: Rx.Observable, http: HttpStart) { + ReactDOM.render( + + + + + , + targetDomElement + ); + + return () => ReactDOM.unmountComponentAtNode(targetDomElement); + } +} diff --git a/x-pack/plugins/serverless/public/types.ts b/x-pack/plugins/serverless/public/types.ts index 4af0b062ede0c..787ea16daec9f 100644 --- a/x-pack/plugins/serverless/public/types.ts +++ b/x-pack/plugins/serverless/public/types.ts @@ -11,3 +11,5 @@ export interface ServerlessPluginSetup {} export interface ServerlessPluginStart { setServerlessNavigation: (navigation: JSX.Element) => void; } + +export type ProjectType = 'oblt' | 'security' | 'es'; diff --git a/x-pack/plugins/serverless/server/plugin.ts b/x-pack/plugins/serverless/server/plugin.ts index 54bfb5c14dc4b..1cc3d4973e50e 100644 --- a/x-pack/plugins/serverless/server/plugin.ts +++ b/x-pack/plugins/serverless/server/plugin.ts @@ -5,14 +5,53 @@ * 2.0. */ +import { copyFileSync, existsSync } from 'fs'; import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '@kbn/core/server'; +import { schema, TypeOf } from '@kbn/config-schema'; +import { getConfigDirectory } from '@kbn/utils'; +import { resolve } from 'path'; import { ServerlessPluginSetup, ServerlessPluginStart } from './types'; +import { API_SWITCH_PROJECT } from '../common'; + +const switchBodySchema = schema.object({ + id: schema.string(), +}); + +type SwitchReqBody = TypeOf; export class ServerlessPlugin implements Plugin { constructor(_initializerContext: PluginInitializerContext) {} - public setup(_core: CoreSetup) { + public setup(core: CoreSetup) { + const router = core.http.createRouter(); + + if (process.env.NODE_ENV !== 'production') { + router.post( + { + path: API_SWITCH_PROJECT, + validate: { + body: switchBodySchema, + }, + }, + async (_context, request, response) => { + const { id } = request.body; + const path = resolve(getConfigDirectory(), `serverless.${id}.yml`); + + try { + if (existsSync(path)) { + copyFileSync(path, resolve(getConfigDirectory(), 'serverless.recent.yml')); + return response.ok({ body: id }); + } + } catch (e) { + return response.badRequest({ body: e }); + } + + return response.badRequest(); + } + ); + } + return {}; } From f39682d0781545b18010b01b56854aa29dd84c1f Mon Sep 17 00:00:00 2001 From: Clint Andrew Hall Date: Mon, 13 Mar 2023 13:55:59 -0400 Subject: [PATCH 48/58] remove translations --- x-pack/plugins/serverless_observability/.i18nrc.json | 7 ------- x-pack/plugins/serverless_search/.i18nrc.json | 9 --------- x-pack/plugins/serverless_security/.i18nrc.json | 9 --------- 3 files changed, 25 deletions(-) delete mode 100644 x-pack/plugins/serverless_observability/.i18nrc.json delete mode 100644 x-pack/plugins/serverless_search/.i18nrc.json delete mode 100644 x-pack/plugins/serverless_security/.i18nrc.json diff --git a/x-pack/plugins/serverless_observability/.i18nrc.json b/x-pack/plugins/serverless_observability/.i18nrc.json deleted file mode 100644 index 623c26e73e4da..0000000000000 --- a/x-pack/plugins/serverless_observability/.i18nrc.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "prefix": "serverlessObservability", - "paths": { - "serverlessObservability": "." - }, - "translations": ["translations/ja-JP.json"] -} diff --git a/x-pack/plugins/serverless_search/.i18nrc.json b/x-pack/plugins/serverless_search/.i18nrc.json deleted file mode 100644 index 7b0dcef6895ed..0000000000000 --- a/x-pack/plugins/serverless_search/.i18nrc.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "prefix": "serverlessSearch", - "paths": { - "serverlessSearch": "." - }, - "translations": [ - "translations/ja-JP.json" - ] -} diff --git a/x-pack/plugins/serverless_security/.i18nrc.json b/x-pack/plugins/serverless_security/.i18nrc.json deleted file mode 100644 index e4519b9cdf9f5..0000000000000 --- a/x-pack/plugins/serverless_security/.i18nrc.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "prefix": "serverlessSecurity", - "paths": { - "serverlessSecurity": "." - }, - "translations": [ - "translations/ja-JP.json" - ] -} \ No newline at end of file From 11838ecc9b160f64c0de96f500e717c8be4c42e5 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Mon, 13 Mar 2023 18:01:14 +0000 Subject: [PATCH 49/58] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- x-pack/plugins/serverless/tsconfig.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/x-pack/plugins/serverless/tsconfig.json b/x-pack/plugins/serverless/tsconfig.json index ffdc7af796587..8b0bdb1062f40 100644 --- a/x-pack/plugins/serverless/tsconfig.json +++ b/x-pack/plugins/serverless/tsconfig.json @@ -16,5 +16,10 @@ "kbn_references": [ "@kbn/core", "@kbn/config-schema", + "@kbn/core-http-browser", + "@kbn/i18n", + "@kbn/kibana-react-plugin", + "@kbn/i18n-react", + "@kbn/utils", ] } From 59ad7468bcf7f873984a2755b31555b7e1ce78cb Mon Sep 17 00:00:00 2001 From: Clint Andrew Hall Date: Mon, 13 Mar 2023 14:25:10 -0400 Subject: [PATCH 50/58] fix types --- .../security/public/nav_control/nav_control_service.tsx | 6 +++--- x-pack/plugins/serverless/kibana.jsonc | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/security/public/nav_control/nav_control_service.tsx b/x-pack/plugins/security/public/nav_control/nav_control_service.tsx index a6fd2eaf61fd1..0b32a6044a1cd 100644 --- a/x-pack/plugins/security/public/nav_control/nav_control_service.tsx +++ b/x-pack/plugins/security/public/nav_control/nav_control_service.tsx @@ -29,7 +29,7 @@ interface SetupDeps { securityLicense: SecurityLicense; logoutUrl: string; securityApiClients: SecurityApiClients; - showNavLinks: boolean; + showNavLinks?: boolean; } interface StartDeps { @@ -55,7 +55,7 @@ export class SecurityNavControlService { private securityApiClients!: SecurityApiClients; private navControlRegistered!: boolean; - private showNavLinks!: boolean; + private showNavLinks = true; private securityFeaturesSubscription?: Subscription; @@ -66,7 +66,7 @@ export class SecurityNavControlService { this.securityLicense = securityLicense; this.logoutUrl = logoutUrl; this.securityApiClients = securityApiClients; - this.showNavLinks = showNavLinks; + this.showNavLinks = showNavLinks || this.showNavLinks; } public start({ core, authc }: StartDeps): SecurityNavControlServiceStart { diff --git a/x-pack/plugins/serverless/kibana.jsonc b/x-pack/plugins/serverless/kibana.jsonc index a952c98a0aba6..af79877f1079f 100644 --- a/x-pack/plugins/serverless/kibana.jsonc +++ b/x-pack/plugins/serverless/kibana.jsonc @@ -12,7 +12,9 @@ "serverless", "plugin", ], - "requiredPlugins": [], + "requiredPlugins": [ + "kibanaReact", + ], "optionalPlugins": [], "requiredBundles": [] } From a1aa9cd65a5d885d3ea43a82411329fb67166a0f Mon Sep 17 00:00:00 2001 From: Clint Andrew Hall Date: Mon, 13 Mar 2023 14:43:35 -0400 Subject: [PATCH 51/58] fix i18n --- x-pack/.i18nrc.json | 69 ++++++++++++++----- .../public/components/switcher/loader.tsx | 8 +-- 2 files changed, 53 insertions(+), 24 deletions(-) diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json index fbde4b1e73c7e..b577466ac07b7 100644 --- a/x-pack/.i18nrc.json +++ b/x-pack/.i18nrc.json @@ -25,9 +25,15 @@ "xpack.features": "plugins/features", "xpack.dataVisualizer": "plugins/data_visualizer", "xpack.fileUpload": "plugins/file_upload", - "xpack.globalSearch": ["plugins/global_search"], - "xpack.globalSearchBar": ["plugins/global_search_bar"], - "xpack.graph": ["plugins/graph"], + "xpack.globalSearch": [ + "plugins/global_search" + ], + "xpack.globalSearchBar": [ + "plugins/global_search_bar" + ], + "xpack.graph": [ + "plugins/graph" + ], "xpack.grokDebugger": "plugins/grokdebugger", "xpack.idxMgmt": "plugins/index_management", "xpack.indexLifecycleMgmt": "plugins/index_lifecycle_management", @@ -40,45 +46,74 @@ "xpack.licenseMgmt": "plugins/license_management", "xpack.licensing": "plugins/licensing", "xpack.lists": "plugins/lists", - "xpack.logstash": ["plugins/logstash"], + "xpack.logstash": [ + "plugins/logstash" + ], "xpack.main": "legacy/plugins/xpack_main", - "xpack.maps": ["plugins/maps"], - "xpack.aiops": ["packages/ml/aiops_components", "plugins/aiops"], - "xpack.ml": ["packages/ml/date_picker", "plugins/ml"], - "xpack.monitoring": ["plugins/monitoring"], - "xpack.osquery": ["plugins/osquery"], + "xpack.maps": [ + "plugins/maps" + ], + "xpack.aiops": [ + "packages/ml/aiops_components", + "plugins/aiops" + ], + "xpack.ml": [ + "packages/ml/date_picker", + "plugins/ml" + ], + "xpack.monitoring": [ + "plugins/monitoring" + ], + "xpack.osquery": [ + "plugins/osquery" + ], "xpack.painlessLab": "plugins/painless_lab", - "xpack.profiling": [ "plugins/profiling" ], + "xpack.profiling": [ + "plugins/profiling" + ], "xpack.remoteClusters": "plugins/remote_clusters", - "xpack.reporting": ["plugins/reporting"], - "xpack.rollupJobs": ["plugins/rollup"], + "xpack.reporting": [ + "plugins/reporting" + ], + "xpack.rollupJobs": [ + "plugins/rollup" + ], "xpack.runtimeFields": "plugins/runtime_fields", "xpack.screenshotting": "plugins/screenshotting", "xpack.searchProfiler": "plugins/searchprofiler", "xpack.security": "plugins/security", "xpack.server": "legacy/server", + "xpack.serverless": "plugins/serverless", "xpack.securitySolution": "plugins/security_solution", "xpack.sessionView": "plugins/session_view", "xpack.snapshotRestore": "plugins/snapshot_restore", "xpack.spaces": "plugins/spaces", - "xpack.savedObjectsTagging": ["plugins/saved_objects_tagging"], + "xpack.savedObjectsTagging": [ + "plugins/saved_objects_tagging" + ], "xpack.taskManager": "legacy/plugins/task_manager", "xpack.timelines": "plugins/timelines", "xpack.transform": "plugins/transform", "xpack.triggersActionsUI": "plugins/triggers_actions_ui", "xpack.upgradeAssistant": "plugins/upgrade_assistant", - "xpack.synthetics": ["plugins/synthetics"], - "xpack.ux": ["plugins/ux"], + "xpack.synthetics": [ + "plugins/synthetics" + ], + "xpack.ux": [ + "plugins/ux" + ], "xpack.urlDrilldown": "plugins/drilldowns/url_drilldown", "xpack.watcher": "plugins/watcher", "xpack.observability": "plugins/observability", "xpack.banners": "plugins/banners", "xpack.threatIntelligence": "plugins/threat_intelligence" }, - "exclude": ["examples"], + "exclude": [ + "examples" + ], "translations": [ "@kbn/translations-plugin/translations/zh-CN.json", "@kbn/translations-plugin/translations/ja-JP.json", "@kbn/translations-plugin/translations/fr-FR.json" ] -} +} \ No newline at end of file diff --git a/x-pack/plugins/serverless/public/components/switcher/loader.tsx b/x-pack/plugins/serverless/public/components/switcher/loader.tsx index a3a9178d0380b..d214b7bc271ea 100644 --- a/x-pack/plugins/serverless/public/components/switcher/loader.tsx +++ b/x-pack/plugins/serverless/public/components/switcher/loader.tsx @@ -7,19 +7,13 @@ import React from 'react'; -import { i18n } from '@kbn/i18n'; - import { Logo, type Props } from './logo'; export const Loader = (props: Props) => (
-
- {i18n.translate('core.ui.welcomeMessage', { - defaultMessage: 'Loading Project', - })} -
+
Loading Project
From 15753b69c64f2f2424e869ae7176e6469b9517c8 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Mon, 13 Mar 2023 19:22:22 +0000 Subject: [PATCH 52/58] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- x-pack/plugins/serverless/tsconfig.json | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugins/serverless/tsconfig.json b/x-pack/plugins/serverless/tsconfig.json index 8b0bdb1062f40..444ba6119409f 100644 --- a/x-pack/plugins/serverless/tsconfig.json +++ b/x-pack/plugins/serverless/tsconfig.json @@ -17,7 +17,6 @@ "@kbn/core", "@kbn/config-schema", "@kbn/core-http-browser", - "@kbn/i18n", "@kbn/kibana-react-plugin", "@kbn/i18n-react", "@kbn/utils", From 8f969b459a8718d37df79746fda435344d034192 Mon Sep 17 00:00:00 2001 From: Clint Andrew Hall Date: Mon, 13 Mar 2023 18:42:53 -0400 Subject: [PATCH 53/58] fix test --- x-pack/plugins/security/server/config.test.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/x-pack/plugins/security/server/config.test.ts b/x-pack/plugins/security/server/config.test.ts index 49c1e4b951e92..ea255d61ee255 100644 --- a/x-pack/plugins/security/server/config.test.ts +++ b/x-pack/plugins/security/server/config.test.ts @@ -71,6 +71,7 @@ describe('config schema', () => { "lifespan": "P30D", }, "showInsecureClusterWarning": true, + "showNavLinks": true, } `); @@ -125,6 +126,7 @@ describe('config schema', () => { "lifespan": "P30D", }, "showInsecureClusterWarning": true, + "showNavLinks": true, } `); @@ -178,6 +180,7 @@ describe('config schema', () => { "lifespan": "P30D", }, "showInsecureClusterWarning": true, + "showNavLinks": true, } `); }); From 2c8537eca46e6da3039b2bbb3475bfabf3957915 Mon Sep 17 00:00:00 2001 From: semd Date: Tue, 14 Mar 2023 19:09:28 +0100 Subject: [PATCH 54/58] use to new package nav --- config/serverless.security.yml | 1 + .../public/components/side_navigation/side_navigation.tsx | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/config/serverless.security.yml b/config/serverless.security.yml index a5e1c170ddccd..ed910784c5940 100644 --- a/config/serverless.security.yml +++ b/config/serverless.security.yml @@ -1,3 +1,4 @@ +enterpriseSearch.enabled: false xpack.apm.enabled: false xpack.canvas.enabled: false xpack.observability.enabled: false diff --git a/x-pack/plugins/serverless_security/public/components/side_navigation/side_navigation.tsx b/x-pack/plugins/serverless_security/public/components/side_navigation/side_navigation.tsx index 87195fe28e8ea..bc1c4a1c453a6 100644 --- a/x-pack/plugins/serverless_security/public/components/side_navigation/side_navigation.tsx +++ b/x-pack/plugins/serverless_security/public/components/side_navigation/side_navigation.tsx @@ -7,8 +7,6 @@ import React, { useCallback } from 'react'; import useLocalStorage from 'react-use/lib/useLocalStorage'; -import { i18n } from '@kbn/i18n'; -import { SolutionNav } from '@kbn/shared-ux-page-solution-nav'; import { EuiButtonIcon, EuiCollapsibleNav, @@ -16,6 +14,8 @@ import { EuiThemeProvider, useEuiTheme, } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { SolutionNav } from '@kbn/shared-ux-page-solution-nav'; import { SolutionSideNav } from '@kbn/security-solution-side-nav'; import { usePartitionFooterNavItems, @@ -82,7 +82,7 @@ export const SecuritySideNavigation: React.FC = () => { items={items} footerItems={footerItems} selectedId={selectedId} - panelBottomOffset={undefined} + panelTopOffset={`calc(${euiTheme.size.l} * 2)`} /> } closeFlyoutButtonPosition={'inside'} From fbe43c3dad4f3fbaf286a2d2e90b8c424f4e5cab Mon Sep 17 00:00:00 2001 From: semd Date: Tue, 14 Mar 2023 19:13:20 +0100 Subject: [PATCH 55/58] package change --- package.json | 1 - tsconfig.base.json | 2 -- yarn.lock | 4 ---- 3 files changed, 7 deletions(-) diff --git a/package.json b/package.json index 18b133c570ea4..3627ccca4abc2 100644 --- a/package.json +++ b/package.json @@ -609,7 +609,6 @@ "@kbn/shared-ux-router": "link:packages/shared-ux/router/impl", "@kbn/shared-ux-router-mocks": "link:packages/shared-ux/router/mocks", "@kbn/shared-ux-router-types": "link:packages/shared-ux/router/types", - "@kbn/shared-ux-side-navigation": "link:packages/shared-ux/side_navigation", "@kbn/shared-ux-storybook-config": "link:packages/shared-ux/storybook/config", "@kbn/shared-ux-storybook-mock": "link:packages/shared-ux/storybook/mock", "@kbn/shared-ux-utility": "link:packages/kbn-shared-ux-utility", diff --git a/tsconfig.base.json b/tsconfig.base.json index dc79f19ff366e..8680223902fd7 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1206,8 +1206,6 @@ "@kbn/shared-ux-router-mocks/*": ["packages/shared-ux/router/mocks/*"], "@kbn/shared-ux-router-types": ["packages/shared-ux/router/types"], "@kbn/shared-ux-router-types/*": ["packages/shared-ux/router/types/*"], - "@kbn/shared-ux-side-navigation": ["packages/shared-ux/side_navigation"], - "@kbn/shared-ux-side-navigation/*": ["packages/shared-ux/side_navigation/*"], "@kbn/shared-ux-storybook-config": ["packages/shared-ux/storybook/config"], "@kbn/shared-ux-storybook-config/*": ["packages/shared-ux/storybook/config/*"], "@kbn/shared-ux-storybook-mock": ["packages/shared-ux/storybook/mock"], diff --git a/yarn.lock b/yarn.lock index 0bc63f7a9a1cb..d383dfbf49698 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5149,10 +5149,6 @@ version "0.0.0" uid "" -"@kbn/shared-ux-side-navigation@link:packages/shared-ux/side_navigation": - version "0.0.0" - uid "" - "@kbn/shared-ux-storybook-config@link:packages/shared-ux/storybook/config": version "0.0.0" uid "" From a5fe06ac8edf735f74d3ab440d277a4e49436d7c Mon Sep 17 00:00:00 2001 From: semd Date: Tue, 14 Mar 2023 19:22:10 +0100 Subject: [PATCH 56/58] sync --- .../src/solution_side_nav_panel.test.tsx | 35 ------------------- src/core/public/_variables.scss | 2 +- .../plugins/security_solution/common/index.ts | 3 +- .../navigation/security_side_nav/index.ts | 4 +-- .../public/common/links/app_links.ts | 6 ++-- 5 files changed, 6 insertions(+), 44 deletions(-) diff --git a/packages/security-solution/side_nav/src/solution_side_nav_panel.test.tsx b/packages/security-solution/side_nav/src/solution_side_nav_panel.test.tsx index cefd98e2551e8..eb0f887eab96c 100644 --- a/packages/security-solution/side_nav/src/solution_side_nav_panel.test.tsx +++ b/packages/security-solution/side_nav/src/solution_side_nav_panel.test.tsx @@ -8,13 +8,8 @@ import React from 'react'; import { render, waitFor } from '@testing-library/react'; -<<<<<<<< HEAD:packages/shared-ux/side_navigation/src/solution_grouped_nav_panel.test.tsx -import { SideNavigationPanel, type SideNavigationPanelProps } from './side_navigation_panel'; -import type { DefaultSideNavItem } from './types'; -======== import { SolutionSideNavPanel, type SolutionSideNavPanelProps } from './solution_side_nav_panel'; import type { SolutionSideNavItem } from './types'; ->>>>>>>> clintandrewhall/serverless/poc/navigation:packages/security-solution/side_nav/src/solution_side_nav_panel.test.tsx import { BETA_LABEL } from './beta_badge'; import { TELEMETRY_EVENT } from './telemetry/const'; import { METRIC_TYPE } from '@kbn/analytics'; @@ -71,20 +66,12 @@ const bottomNavOffset = '10px'; const PANEL_TITLE = 'test title'; const mockOnClose = jest.fn(); const mockOnOutsideClick = jest.fn(); -<<<<<<<< HEAD:packages/shared-ux/side_navigation/src/solution_grouped_nav_panel.test.tsx -const renderNavPanel = (props: Partial = {}) => -======== const renderNavPanel = (props: Partial = {}) => ->>>>>>>> clintandrewhall/serverless/poc/navigation:packages/security-solution/side_nav/src/solution_side_nav_panel.test.tsx render( <>
-<<<<<<<< HEAD:packages/shared-ux/side_navigation/src/solution_grouped_nav_panel.test.tsx - >>>>>>> clintandrewhall/serverless/poc/navigation:packages/security-solution/side_nav/src/solution_side_nav_panel.test.tsx items={mockItems} title={PANEL_TITLE} onClose={mockOnClose} @@ -129,17 +116,10 @@ describe('SolutionSideNavPanel', () => { describe('links', () => { it('should contain correct href in links', () => { const result = renderNavPanel(); -<<<<<<<< HEAD:packages/shared-ux/side_navigation/src/solution_grouped_nav_panel.test.tsx - expect(result.getByTestId(`groupedNavPanelLink-${'hosts'}`).getAttribute('href')).toBe( - '/hosts' - ); - expect(result.getByTestId(`groupedNavPanelLink-${'network'}`).getAttribute('href')).toBe( -======== expect(result.getByTestId(`solutionSideNavPanelLink-${'hosts'}`).getAttribute('href')).toBe( '/hosts' ); expect(result.getByTestId(`solutionSideNavPanelLink-${'network'}`).getAttribute('href')).toBe( ->>>>>>>> clintandrewhall/serverless/poc/navigation:packages/security-solution/side_nav/src/solution_side_nav_panel.test.tsx '/network' ); }); @@ -158,11 +138,7 @@ describe('SolutionSideNavPanel', () => { }, ]; const result = renderNavPanel({ items }); -<<<<<<<< HEAD:packages/shared-ux/side_navigation/src/solution_grouped_nav_panel.test.tsx - result.getByTestId(`groupedNavPanelLink-${'users'}`).click(); -======== result.getByTestId(`solutionSideNavPanelLink-${'users'}`).click(); ->>>>>>>> clintandrewhall/serverless/poc/navigation:packages/security-solution/side_nav/src/solution_side_nav_panel.test.tsx expect(mockOnClick).toHaveBeenCalled(); }); @@ -180,17 +156,10 @@ describe('SolutionSideNavPanel', () => { }, ]; const result = renderNavPanel({ items }); -<<<<<<<< HEAD:packages/shared-ux/side_navigation/src/solution_grouped_nav_panel.test.tsx - result.getByTestId(`groupedNavPanelLink-${'users'}`).click(); - expect(mockTrack).toHaveBeenCalledWith( - METRIC_TYPE.CLICK, - `${TELEMETRY_EVENT.GROUPED_NAVIGATION}${'users'}` -======== result.getByTestId(`solutionSideNavPanelLink-${'users'}`).click(); expect(mockTrack).toHaveBeenCalledWith( METRIC_TYPE.CLICK, `${TELEMETRY_EVENT.PANEL_NAVIGATION}${'users'}` ->>>>>>>> clintandrewhall/serverless/poc/navigation:packages/security-solution/side_nav/src/solution_side_nav_panel.test.tsx ); }); }); @@ -216,11 +185,7 @@ describe('SolutionSideNavPanel', () => { describe('close', () => { it('should call onClose callback if link clicked', () => { const result = renderNavPanel(); -<<<<<<<< HEAD:packages/shared-ux/side_navigation/src/solution_grouped_nav_panel.test.tsx - result.getByTestId(`groupedNavPanelLink-${'hosts'}`).click(); -======== result.getByTestId(`solutionSideNavPanelLink-${'hosts'}`).click(); ->>>>>>>> clintandrewhall/serverless/poc/navigation:packages/security-solution/side_nav/src/solution_side_nav_panel.test.tsx expect(mockOnClose).toHaveBeenCalled(); }); diff --git a/src/core/public/_variables.scss b/src/core/public/_variables.scss index 048ffaa449fd5..010ebdc933a9f 100644 --- a/src/core/public/_variables.scss +++ b/src/core/public/_variables.scss @@ -3,6 +3,6 @@ // height of the header banner $kbnHeaderBannerHeight: $euiSizeXL; // This value is also declared in `/x-pack/plugins/canvas/common/lib/constants.ts` // total height of the header (when the banner is *not* present) -$kbnHeaderOffset: $euiHeaderHeightCompensation; // <- TODO: this change is a hack for demo purposes. +$kbnHeaderOffset: $euiHeaderHeightCompensation * 2; // total height of the header when the banner is present $kbnHeaderOffsetWithBanner: $kbnHeaderOffset + $kbnHeaderBannerHeight; \ No newline at end of file diff --git a/x-pack/plugins/security_solution/common/index.ts b/x-pack/plugins/security_solution/common/index.ts index 1557fd605729e..3133996d79ed8 100644 --- a/x-pack/plugins/security_solution/common/index.ts +++ b/x-pack/plugins/security_solution/common/index.ts @@ -8,9 +8,8 @@ // TODO(jbudz): should be removed when upgrading to TS@4.8 // this is a skip for the errors created when typechecking with isolatedModules export {}; - -export { ELASTIC_SECURITY_RULE_ID } from './detection_engine/constants'; export { APP_UI_ID, SecurityPageName } from './constants'; +export { ELASTIC_SECURITY_RULE_ID } from './detection_engine/constants'; // Careful of exporting anything from this file as any file(s) you export here will cause your page bundle size to increase. // If you're using functions/types/etc... internally it's best to import directly from their paths than expose the functions/types/etc... here. diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/security_side_nav/index.ts b/x-pack/plugins/security_solution/public/common/components/navigation/security_side_nav/index.ts index 2f04fbdd4bf2e..a2c866e604e16 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/security_side_nav/index.ts +++ b/x-pack/plugins/security_solution/public/common/components/navigation/security_side_nav/index.ts @@ -5,6 +5,4 @@ * 2.0. */ -import { SecuritySideNav } from './security_side_nav'; - -export { SecuritySideNav }; +export { SecuritySideNav } from './security_side_nav'; diff --git a/x-pack/plugins/security_solution/public/common/links/app_links.ts b/x-pack/plugins/security_solution/public/common/links/app_links.ts index 92c984a4b925f..c00fa87f878da 100644 --- a/x-pack/plugins/security_solution/public/common/links/app_links.ts +++ b/x-pack/plugins/security_solution/public/common/links/app_links.ts @@ -18,16 +18,16 @@ import type { StartPlugins } from '../../types'; const casesLinks = getCasesLinkItems(); -export const links: AppLinkItems = Object.freeze([ +export const links = Object.freeze([ dashboardsLandingLinks, detectionLinks, cloudSecurityPostureRootLinks, timelinesLinks, casesLinks, threatHuntingLandingLinks, - indicatorsLinks, gettingStartedLinks, managementLinks, + indicatorsLinks, ]); export const getFilteredLinks = async ( @@ -43,8 +43,8 @@ export const getFilteredLinks = async ( timelinesLinks, casesLinks, threatHuntingLandingLinks, - indicatorsLinks, gettingStartedLinks, managementFilteredLinks, + indicatorsLinks, ]); }; From fd091dc4683ac44579b0225bb60350a8d35c31f0 Mon Sep 17 00:00:00 2001 From: semd Date: Tue, 14 Mar 2023 19:24:58 +0100 Subject: [PATCH 57/58] newline --- src/core/public/_variables.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/public/_variables.scss b/src/core/public/_variables.scss index 010ebdc933a9f..4b89cc0bdf1ad 100644 --- a/src/core/public/_variables.scss +++ b/src/core/public/_variables.scss @@ -5,4 +5,4 @@ $kbnHeaderBannerHeight: $euiSizeXL; // This value is also declared in `/x-pack/p // total height of the header (when the banner is *not* present) $kbnHeaderOffset: $euiHeaderHeightCompensation * 2; // total height of the header when the banner is present -$kbnHeaderOffsetWithBanner: $kbnHeaderOffset + $kbnHeaderBannerHeight; \ No newline at end of file +$kbnHeaderOffsetWithBanner: $kbnHeaderOffset + $kbnHeaderBannerHeight; From 776e4763d1f95cdcacb01c80145f20fe817c741b Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 15 Mar 2023 17:35:30 +0000 Subject: [PATCH 58/58] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- x-pack/plugins/serverless_security/tsconfig.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/x-pack/plugins/serverless_security/tsconfig.json b/x-pack/plugins/serverless_security/tsconfig.json index ddf77b288e628..7dd3bb150f904 100644 --- a/x-pack/plugins/serverless_security/tsconfig.json +++ b/x-pack/plugins/serverless_security/tsconfig.json @@ -20,5 +20,9 @@ "@kbn/security-plugin", "@kbn/security-solution-plugin", "@kbn/serverless", + "@kbn/i18n", + "@kbn/shared-ux-page-solution-nav", + "@kbn/security-solution-side-nav", + "@kbn/kibana-react-plugin", ] }