From 2b1cd4d0805a3072fbe3da01c7c3d4ce8d709081 Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Fri, 3 Nov 2023 13:11:05 -0400 Subject: [PATCH] Disable guided onboarding on serverless (#168303) --- config/serverless.yml | 5 +- .../guided_onboarding_example/kibana.jsonc | 4 +- .../public/components/app.tsx | 2 +- .../public/components/main.tsx | 24 +++--- .../public/components/step_four.tsx | 12 ++- .../public/components/step_one.tsx | 30 +++---- .../server/plugin.ts | 4 +- src/plugins/guided_onboarding/README.md | 82 ++++++++++--------- .../guided_onboarding/server/config.ts | 24 ++++++ src/plugins/guided_onboarding/server/index.ts | 2 + src/plugins/guided_onboarding/tsconfig.json | 2 + .../public/application/components/home_app.js | 2 +- src/plugins/home/public/plugin.ts | 4 +- .../common/types/kibana_deps.ts | 2 +- x-pack/plugins/enterprise_search/kibana.jsonc | 4 +- .../components/search_index/search_index.tsx | 51 +++++++----- .../shared/kibana/kibana_logic.ts | 2 +- .../enterprise_search/server/plugin.ts | 8 +- x-pack/plugins/exploratory_view/kibana.jsonc | 1 - .../plugins/exploratory_view/public/plugin.ts | 2 - x-pack/plugins/exploratory_view/tsconfig.json | 1 - x-pack/plugins/fleet/kibana.jsonc | 2 +- .../confirm_incoming_data_with_preview.tsx | 2 +- .../confirm_incoming_data.tsx | 2 +- .../hooks/use_is_guided_onboarding_active.ts | 2 +- x-pack/plugins/fleet/public/plugin.ts | 4 +- x-pack/plugins/observability/kibana.jsonc | 6 +- x-pack/plugins/observability/public/plugin.ts | 2 +- x-pack/plugins/observability/server/plugin.ts | 4 +- .../plugins/observability_shared/kibana.jsonc | 4 +- .../observability_shared/public/plugin.ts | 4 +- x-pack/plugins/security_solution/kibana.jsonc | 4 +- .../guided_onboarding_tour/tour.tsx | 32 ++++---- .../rules_table/alternative_tour/tour.tsx | 7 +- .../rules_management_tour.tsx | 8 +- .../plugins/security_solution/public/types.ts | 2 +- .../security_solution/server/plugin.ts | 6 +- .../server/plugin_contract.ts | 2 +- .../e2e/explore/guided_onboarding/tour.cy.ts | 2 +- 39 files changed, 208 insertions(+), 155 deletions(-) create mode 100644 src/plugins/guided_onboarding/server/config.ts diff --git a/config/serverless.yml b/config/serverless.yml index fc8ac64f1a09e..fb3528514ef66 100644 --- a/config/serverless.yml +++ b/config/serverless.yml @@ -51,9 +51,12 @@ xpack.index_management.enableDataStreamsStorageColumn: false dev_tools.deeplinks.navLinkStatus: visible management.deeplinks.navLinkStatus: visible +# Onboarding team UI configurations +xpack.cloud_integrations.data_migration.enabled: false +guided_onboarding.enabled: false + # Other disabled plugins xpack.canvas.enabled: false -xpack.cloud_integrations.data_migration.enabled: false data.search.sessions.enabled: false advanced_settings.enabled: false diff --git a/examples/guided_onboarding_example/kibana.jsonc b/examples/guided_onboarding_example/kibana.jsonc index e0fe519a29ff3..51a53dae720dd 100644 --- a/examples/guided_onboarding_example/kibana.jsonc +++ b/examples/guided_onboarding_example/kibana.jsonc @@ -8,7 +8,9 @@ "server": true, "browser": true, "requiredPlugins": [ - "navigation", + "navigation" + ], + "optionalPlugins": [ "guidedOnboarding" ] } diff --git a/examples/guided_onboarding_example/public/components/app.tsx b/examples/guided_onboarding_example/public/components/app.tsx index 3b289be9a8dd9..1230600ddc70e 100755 --- a/examples/guided_onboarding_example/public/components/app.tsx +++ b/examples/guided_onboarding_example/public/components/app.tsx @@ -39,7 +39,7 @@ export const GuidedOnboardingExampleApp = (props: GuidedOnboardingExampleAppDeps /> } /> - {guidedOnboarding.guidedOnboardingApi?.isEnabled ? ( + {guidedOnboarding?.guidedOnboardingApi?.isEnabled ? ( diff --git a/examples/guided_onboarding_example/public/components/main.tsx b/examples/guided_onboarding_example/public/components/main.tsx index ba434c0c4c86b..1a19e1a745bf1 100644 --- a/examples/guided_onboarding_example/public/components/main.tsx +++ b/examples/guided_onboarding_example/public/components/main.tsx @@ -31,7 +31,7 @@ import type { GuideState, GuideStepIds, GuideId, GuideStep } from '@kbn/guided-o import type { GuidedOnboardingPluginStart } from '@kbn/guided-onboarding-plugin/public'; interface MainProps { - guidedOnboarding: GuidedOnboardingPluginStart; + guidedOnboarding?: GuidedOnboardingPluginStart; notifications: CoreStart['notifications']; } @@ -48,10 +48,7 @@ const selectOptions: EuiSelectOption[] = exampleGuideIds.map((guideId) => ({ text: guideId, })); export const Main = (props: MainProps) => { - const { - guidedOnboarding: { guidedOnboardingApi }, - notifications, - } = props; + const { guidedOnboarding, notifications } = props; const history = useHistory(); const [guidesState, setGuidesState] = useState(undefined); const [activeGuide, setActiveGuide] = useState(undefined); @@ -61,12 +58,12 @@ export const Main = (props: MainProps) => { useEffect(() => { const fetchGuidesState = async () => { - const newGuidesState = await guidedOnboardingApi?.fetchAllGuidesState(); + const newGuidesState = await guidedOnboarding?.guidedOnboardingApi?.fetchAllGuidesState(); setGuidesState(newGuidesState ? newGuidesState.state : []); }; fetchGuidesState(); - }, [guidedOnboardingApi]); + }, [guidedOnboarding]); useEffect(() => { const newActiveGuide = guidesState?.find((guide) => guide.isActive === true); @@ -76,7 +73,10 @@ export const Main = (props: MainProps) => { }, [guidesState, setActiveGuide]); const activateGuide = async (guideId: GuideId, guideState?: GuideState) => { - const response = await guidedOnboardingApi?.activateGuide(guideId, guideState); + const response = await guidedOnboarding?.guidedOnboardingApi?.activateGuide( + guideId, + guideState + ); if (response) { notifications.toasts.addSuccess( @@ -92,7 +92,9 @@ export const Main = (props: MainProps) => { return; } - const selectedGuideConfig = await guidedOnboardingApi?.getGuideConfig(selectedGuide); + const selectedGuideConfig = await guidedOnboarding?.guidedOnboardingApi?.getGuideConfig( + selectedGuide + ); if (!selectedGuideConfig) { return; @@ -134,7 +136,7 @@ export const Main = (props: MainProps) => { guideId: selectedGuide!, }; - const response = await guidedOnboardingApi?.updatePluginState( + const response = await guidedOnboarding?.guidedOnboardingApi?.updatePluginState( { status: 'in_progress', guide: updatedGuideState }, true ); @@ -170,7 +172,7 @@ export const Main = (props: MainProps) => {

diff --git a/examples/guided_onboarding_example/public/components/step_four.tsx b/examples/guided_onboarding_example/public/components/step_four.tsx index 44c238b417fa2..e3abf249d2f8d 100644 --- a/examples/guided_onboarding_example/public/components/step_four.tsx +++ b/examples/guided_onboarding_example/public/components/step_four.tsx @@ -16,24 +16,22 @@ import { EuiPageHeader, EuiPageSection, EuiCode } from '@elastic/eui'; import { useParams } from 'react-router-dom'; interface StepFourProps { - guidedOnboarding: GuidedOnboardingPluginStart; + guidedOnboarding?: GuidedOnboardingPluginStart; } -export const StepFour: React.FC = ({ - guidedOnboarding: { guidedOnboardingApi }, -}) => { +export const StepFour: React.FC = ({ guidedOnboarding }) => { const { indexName } = useParams<{ indexName: string }>(); const [, setIsTourStepOpen] = useState(false); useEffect(() => { - const subscription = guidedOnboardingApi + const subscription = guidedOnboarding?.guidedOnboardingApi ?.isGuideStepActive$('testGuide', 'step4') .subscribe((isStepActive) => { setIsTourStepOpen(isStepActive); }); return () => subscription?.unsubscribe(); - }, [guidedOnboardingApi]); + }, [guidedOnboarding]); return ( <> @@ -65,7 +63,7 @@ export const StepFour: React.FC = ({ { - await guidedOnboardingApi?.completeGuideStep('testGuide', 'step4'); + await guidedOnboarding?.guidedOnboardingApi?.completeGuideStep('testGuide', 'step4'); }} > Complete step 4 diff --git a/examples/guided_onboarding_example/public/components/step_one.tsx b/examples/guided_onboarding_example/public/components/step_one.tsx index 924f4a3952dda..462dd3e78c381 100644 --- a/examples/guided_onboarding_example/public/components/step_one.tsx +++ b/examples/guided_onboarding_example/public/components/step_one.tsx @@ -23,27 +23,25 @@ import { EuiFormRow, } from '@elastic/eui'; -import useObservable from 'react-use/lib/useObservable'; - import { GuidedOnboardingPluginStart } from '@kbn/guided-onboarding-plugin/public/types'; interface GuidedOnboardingExampleAppDeps { - guidedOnboarding: GuidedOnboardingPluginStart; + guidedOnboarding?: GuidedOnboardingPluginStart; } export const StepOne = ({ guidedOnboarding }: GuidedOnboardingExampleAppDeps) => { - const { guidedOnboardingApi } = guidedOnboarding; - const [isTourStepOpen, setIsTourStepOpen] = useState(false); const [indexName, setIndexName] = useState('test1234'); - const isTourActive = useObservable( - guidedOnboardingApi!.isGuideStepActive$('testGuide', 'step1'), - false - ); useEffect(() => { - setIsTourStepOpen(isTourActive); - }, [isTourActive]); + const subscription = guidedOnboarding?.guidedOnboardingApi + ?.isGuideStepActive$('testGuide', 'step1') + .subscribe((isStepActive) => { + setIsTourStepOpen(isStepActive); + }); + return () => subscription?.unsubscribe(); + }, [guidedOnboarding]); + return ( <> @@ -107,9 +105,13 @@ export const StepOne = ({ guidedOnboarding }: GuidedOnboardingExampleAppDeps) => > { - await guidedOnboardingApi?.completeGuideStep('testGuide', 'step1', { - indexName, - }); + await guidedOnboarding?.guidedOnboardingApi?.completeGuideStep( + 'testGuide', + 'step1', + { + indexName, + } + ); }} > Complete step 1 diff --git a/examples/guided_onboarding_example/server/plugin.ts b/examples/guided_onboarding_example/server/plugin.ts index 40b90f9aa77c1..b53f087b9c190 100644 --- a/examples/guided_onboarding_example/server/plugin.ts +++ b/examples/guided_onboarding_example/server/plugin.ts @@ -11,7 +11,7 @@ import { PluginInitializerContext, CoreSetup, Plugin, Logger } from '@kbn/core/s import { testGuideId, testGuideConfig } from '@kbn/guided-onboarding'; interface PluginsSetup { - guidedOnboarding: GuidedOnboardingPluginSetup; + guidedOnboarding?: GuidedOnboardingPluginSetup; } export class GuidedOnboardingExamplePlugin implements Plugin { @@ -23,7 +23,7 @@ export class GuidedOnboardingExamplePlugin implements Plugin { public setup(coreSetup: CoreSetup, { guidedOnboarding }: PluginsSetup) { this.logger.debug('guidedOnboardingExample: Setup'); - guidedOnboarding.registerGuideConfig(testGuideId, testGuideConfig); + guidedOnboarding?.registerGuideConfig(testGuideId, testGuideConfig); return {}; } diff --git a/src/plugins/guided_onboarding/README.md b/src/plugins/guided_onboarding/README.md index 1d1db6d13098a..50132b0a03e9d 100755 --- a/src/plugins/guided_onboarding/README.md +++ b/src/plugins/guided_onboarding/README.md @@ -1,14 +1,15 @@ # Guided Onboarding -This plugin contains the code for the Guided Onboarding project. Guided onboarding consists of guides for Solutions (Enterprise Search, Observability, Security) that can be completed as a checklist of steps. The guides help users to ingest their data and to navigate to the correct Solutions pages. +This plugin contains the code for the Guided Onboarding project. Guided onboarding consists of guides for Solutions (Enterprise Search, Observability, Security) that can be completed as a checklist of steps. The guides help users to ingest their data and to navigate to the correct Solutions pages. -The guided onboarding plugin includes a client-side code for the UI and the server-side code for the internal API. +The guided onboarding plugin includes a client-side code for the UI and the server-side code for the internal API. The client-side code registers a button in the Kibana header that controls the guided onboarding panel (checklist) depending on the current state. There is also an API service exposed from the client-side start contract. The API service is intended for external use by other plugins that need to react to the guided onboarding state, for example hide or display UI elements if a guide step is active. -Besides the internal API routes, the server-side code also exposes a function to register guide configs from the server-side setup start contract. This function is intended for external use by any plugin that need to add a new guide or modify an existing one. +Besides the internal API routes, the server-side code also exposes a function to register guide configs from the server-side setup start contract. This function is intended for external use by any plugin that need to add a new guide or modify an existing one. --- + ## Current functionality The solution plugins register their config that specifies the title, the description and the steps of a guide. The config is fetched via an http request to a guided onboarding endpoint. This endpoint should only be used internally by the guided onboarding API service. The configs are basically just `js` objects that are loaded into the Kibana server memory on startup. Because configs are not expected to be changed by the user, we don’t need to use saved objects. @@ -20,6 +21,7 @@ A step is completed on the solution page code by calling a function in the guide The plugin’s state keeps track of which guide has been started and its current progress. The state also includes the information if the user has started any guide, has completed any guide or if they skipped the guided onboarding, or if they quit the guide before completion. We also store the date when the user first looked at the landing page and if they haven't started any guide, the header button is displayed for the first 30 days. When clicked, the button redirects the user back to the landing page to start a guide. ## Architecture description + The guided onboarding is currently implemented in a separate `guided_onboarding` plugin that contains the code for the header button ([link](https://github.com/elastic/kibana/blob/main/src/plugins/guided_onboarding/public/components/guide_button.tsx)), the dropdown panel ([link](https://github.com/elastic/kibana/blob/main/src/plugins/guided_onboarding/public/components/guide_panel.tsx)) and the API service ([link](https://github.com/elastic/kibana/blob/main/src/plugins/guided_onboarding/public/services/api.service.ts)) exposed out of the client side that can be used by other plugins to get/update the state of the guided onboarding. For example, when a user goes through the SIEM guide they are first taken to the integrations page where they follow some EUI tour steps and install the Elastic Agent and the Elastic Defend integration. The code on the integrations page uses the guided onboarding API service to check if a guide for the Elastic Defend integration is currently in progress. If yes, the page will display the EUI tour steps to guide the user. The page will also use the API service to update the guided onboarding state to the next step when the user completes the installation. @@ -35,27 +37,33 @@ When starting Kibana with `yarn start --run-examples` the `guided_onboarding_exa 1. Guided onboarding is only enabled on cloud. Update your `kibana.dev.yml` file with `xpack.cloud.id: 'testID'` to imitate the Cloud environment. -2. Start Kibana with the example plugins enabled: `yarn start --run-examples`. +2. Start Kibana with the example plugins enabled: `yarn start --run-examples`. 3. Navigate to `/app/home#/getting_started` to view the onboarding landing page and start a guide. Alternatively, you can also start a guide within the guided onboarding example plugin at `/app/guidedOnboardingExample`. The example plugin includes a sample guide that showcases the framework's capabilities. It also provides a form to dynamically start a guide at a specific step. ## Client side: API service -*Also see `KIBANA_FOLDER/examples/guided_onboarding_example` for code examples.* -The guided onboarding plugin exposes an API service from its start contract that is intended to be used by other plugins. The API service allows consumers to access the current state of the guided onboarding process and manipulate it. +_Also see `KIBANA_FOLDER/examples/guided_onboarding_example` for code examples._ + +The guided onboarding plugin exposes an API service from its start contract that is intended to be used by other plugins. The API service allows consumers to access the current state of the guided onboarding process and manipulate it. To use the API service in your plugin, declare the guided onboarding plugin as a dependency in the file `kibana.json` of your plugin. Add the API service to your plugin's start dependencies to rely on the provided TypeScript interface: + ```js export interface AppPluginStartDependencies { - guidedOnboarding: GuidedOnboardingPluginStart; + guidedOnboarding?: GuidedOnboardingPluginStart; } ``` + The API service is now available to your plugin in the setup lifecycle function of your plugin + ```js // startDependencies is of type AppPluginStartDependencies const [coreStart, startDependencies] = await core.getStartServices(); ``` + or in the start lifecycle function of your plugin. + ```js public start(core: CoreStart, startDependencies: AppPluginStartDependencies) { ... @@ -63,35 +71,30 @@ public start(core: CoreStart, startDependencies: AppPluginStartDependencies) { ``` ### isGuideStepActive$(guideId: GuideId, stepId: GuideStepIds): Observable\ -*Also see `KIBANA_FOLDER/examples/guided_onboarding_example/public/components/step_one.tsx`.* -The API service exposes an Observable that contains a boolean value for the state of a specific guide step. For example, if your plugin needs to check if the "Add data" step of the SIEM guide is currently active, you could use the following code snippet. +_Also see `KIBANA_FOLDER/examples/guided_onboarding_example/public/components/step_one.tsx`._ -```js -const { guidedOnboardingApi } = guidedOnboarding; -const isDataStepActive = useObservable(guidedOnboardingApi!.isGuideStepActive$('siem', 'add_data')); -useEffect(() => { - // do some logic depending on the step state -}, [isDataStepActive]); -``` +The API service exposes an Observable that contains a boolean value for the state of a specific guide step. For example, if your plugin needs to check if the "Add data" step of the SIEM guide is currently active, you could use the following code snippet. -Alternatively, you can subscribe to the Observable directly. ```js useEffect(() => { - const subscription = guidedOnboardingApi?.isGuideStepActive$('siem', 'add_data').subscribe((isDataStepACtive) => { - // do some logic depending on the step state + const subscription = guidedOnboardingApi + ?.isGuideStepActive$('siem', 'add_data') + .subscribe((isDataStepActive) => { + // do some logic depending on the step state }); - return () => subscription?.unsubscribe(); + return () => subscription?.unsubscribe(); }, [guidedOnboardingApi]); ``` ### isGuideStepReadyToComplete$(guideId: GuideId, stepId: GuideStepIds): Observable\ -Similar to `isGuideStepActive$`, the observable `isGuideStepReadyToComplete$` can be used to track the state of a step that is configured for manual completion. The observable broadcasts `true` when the manual completion popover is displayed and the user can mark the step "done". In this state the step is not in progress anymore but is not yet fully completed. +Similar to `isGuideStepActive$`, the observable `isGuideStepReadyToComplete$` can be used to track the state of a step that is configured for manual completion. The observable broadcasts `true` when the manual completion popover is displayed and the user can mark the step "done". In this state the step is not in progress anymore but is not yet fully completed. ### completeGuideStep(guideId: GuideId, stepId: GuideStepIds, params?: GuideParams): Promise\<{ pluginState: PluginState } | undefined\> -The API service exposes an async function to mark a guide step as completed. -If the specified guide step is not currently active, the function is a noop. In that case the return value is `undefined`, + +The API service exposes an async function to mark a guide step as completed. +If the specified guide step is not currently active, the function is a noop. In that case the return value is `undefined`, otherwise an updated `PluginState` is returned. ``` @@ -99,34 +102,39 @@ await guidedOnboardingApi?.completeGuideStep('siem', 'add_data'); ``` The function also accepts an optional argument `params` that will be saved in the state and later used for step URLs with dynamic parameters. For example, step 2 of the guide has a dynamic parameter `indexID` in its location path: + ```js const step2Config = { - id: 'step2', - description: 'Step with dynamic url', - location: { - appID: 'test', path: 'testPath/{indexID}' - } + id: 'step2', + description: 'Step with dynamic url', + location: { + appID: 'test', + path: 'testPath/{indexID}', + }, }; ``` -The value of the parameter `indexID` needs to be passed to the API service when completing step 1: `completeGuideStep('testGuide', 'step1', { indexID: 'testIndex' })` + +The value of the parameter `indexID` needs to be passed to the API service when completing step 1: `completeGuideStep('testGuide', 'step1', { indexID: 'testIndex' })` ## Guides config -To use the API service, you need to know a guide ID (currently one of `appSearch`, `websiteSearch`, `databaseSearch`, `kubernetes`, `siem`) and a step ID (for example, `add_data`, `search_experience`, `rules` etc). The consumers of guided onboarding register their guide configs themselves and have therefore full control over the guide ID and step IDs used for their guide. For more details on registering a guide config, see below. + +To use the API service, you need to know a guide ID (currently one of `appSearch`, `websiteSearch`, `databaseSearch`, `kubernetes`, `siem`) and a step ID (for example, `add_data`, `search_experience`, `rules` etc). The consumers of guided onboarding register their guide configs themselves and have therefore full control over the guide ID and step IDs used for their guide. For more details on registering a guide config, see below. ## Server side: register a guide config -The guided onboarding exposes a function `registerGuideConfig(guideId: GuideId, guideConfig: GuideConfig)` function in its setup contract. This function allows consumers to register a guide config for a specified guide ID. The function throws an error if a config already exists for the guide ID. See code examples in following plugins: + +The guided onboarding exposes a function `registerGuideConfig(guideId: GuideId, guideConfig: GuideConfig)` function in its setup contract. This function allows consumers to register a guide config for a specified guide ID. The function throws an error if a config already exists for the guide ID. See code examples in following plugins: - enterprise search: `x-pack/plugins/enterprise_search/server/plugin.ts` - observability: `x-pack/plugins/observability/server/plugin.ts` - security solution: `x-pack/plugins/security_solution/server/plugin.ts` - ## Adding a new guide + Follow these simple steps to add a new guide to the guided onboarding framework. For more detailed information about framework functionality and architecture and about API services exposed by the plugin, please read the full readme. -1. Declare the `guidedOnboarding` plugin as a dependency in your plugin's `kibana.json` file. Add the guided onboarding plugin's client-side start contract to your plugin's client-side start dependencies and the guided onboarding plugin's server-side setup contract to your plugin's server-side dependencies. +1. Declare the `guidedOnboarding` plugin as an optional dependency in your plugin's `kibana.json` file. Add the guided onboarding plugin's client-side start contract to your plugin's client-side start dependencies and the guided onboarding plugin's server-side setup contract to your plugin's server-side dependencies. 2. Define the configuration for your guide. At a high level, this includes a title, description, and list of steps. See this [example config](https://github.com/elastic/kibana/blob/main/packages/kbn-guided-onboarding/src/common/test_guide_config.ts) or consult the `GuideConfig` interface. -3. Register your guide during your plugin's server-side setup by calling a function exposed by the guided onboarding plugin: `registerGuideConfig(guideId: GuideId, guideConfig: GuideConfig)`. For an example, see this [example plugin](https://github.com/elastic/kibana/blob/main/examples/guided_onboarding_example/server/plugin.ts). -4. Update the cards on the landing page to include your guide in the use case selection. Make sure that the card doesn't have the property `navigateTo` because that is only used for cards that redirect to Kibana pages and don't start a guide. Also add the same value to the property `guideId` as used in the guide config. Landing page cards are configured in this [kbn-guided-onboarding package](https://github.com/elastic/kibana/blob/main/packages/kbn-guided-onboarding/src/components/landing_page/guide_cards.constants.tsx). -5. Integrate the new guide into your Kibana pages by using the guided onboarding client-side API service. Make sure your Kibana pages correctly display UI elements depending on the active guide step and the UI flow is straight forward to complete the guide. See existing guides for an example and read more about the API service in this file, the section "Client-side: API service". -6. Optionally, update the example plugin's [form](https://github.com/elastic/kibana/blob/main/examples/guided_onboarding_example/public/components/main.tsx#L38) to be able to start your guide from that page and activate any step in your guide (useful to test your guide steps). +3. Register your guide during your plugin's server-side setup by calling a function exposed by the guided onboarding plugin: `registerGuideConfig(guideId: GuideId, guideConfig: GuideConfig)`. For an example, see this [example plugin](https://github.com/elastic/kibana/blob/main/examples/guided_onboarding_example/server/plugin.ts). +4. Update the cards on the landing page to include your guide in the use case selection. Make sure that the card doesn't have the property `navigateTo` because that is only used for cards that redirect to Kibana pages and don't start a guide. Also add the same value to the property `guideId` as used in the guide config. Landing page cards are configured in this [kbn-guided-onboarding package](https://github.com/elastic/kibana/blob/main/packages/kbn-guided-onboarding/src/components/landing_page/guide_cards.constants.tsx). +5. Integrate the new guide into your Kibana pages by using the guided onboarding client-side API service. Make sure your Kibana pages correctly display UI elements depending on the active guide step and the UI flow is straight forward to complete the guide. See existing guides for an example and read more about the API service in this file, the section "Client-side: API service". +6. Optionally, update the example plugin's [form](https://github.com/elastic/kibana/blob/main/examples/guided_onboarding_example/public/components/main.tsx#L38) to be able to start your guide from that page and activate any step in your guide (useful to test your guide steps). diff --git a/src/plugins/guided_onboarding/server/config.ts b/src/plugins/guided_onboarding/server/config.ts new file mode 100644 index 0000000000000..c118dccc025ce --- /dev/null +++ b/src/plugins/guided_onboarding/server/config.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 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 { offeringBasedSchema, schema, TypeOf } from '@kbn/config-schema'; +import { PluginConfigDescriptor } from '@kbn/core-plugins-server'; + +const configSchema = schema.object({ + enabled: offeringBasedSchema({ + // guided_onboarding is disabled in serverless; refer to the serverless.yml file as the source of truth + // We take this approach in order to have a central place (serverless.yml) to view disabled plugins across Kibana + serverless: schema.boolean({ defaultValue: true }), + }), +}); + +export type GuidedOnboardingConfig = TypeOf; + +export const config: PluginConfigDescriptor = { + schema: configSchema, +}; diff --git a/src/plugins/guided_onboarding/server/index.ts b/src/plugins/guided_onboarding/server/index.ts index 12eb46043cf23..823cc4daf3578 100755 --- a/src/plugins/guided_onboarding/server/index.ts +++ b/src/plugins/guided_onboarding/server/index.ts @@ -14,3 +14,5 @@ export function plugin(initializerContext: PluginInitializerContext) { } export type { GuidedOnboardingPluginSetup, GuidedOnboardingPluginStart } from './types'; + +export { config } from './config'; diff --git a/src/plugins/guided_onboarding/tsconfig.json b/src/plugins/guided_onboarding/tsconfig.json index 88569a7a43238..ff4d6052517cc 100644 --- a/src/plugins/guided_onboarding/tsconfig.json +++ b/src/plugins/guided_onboarding/tsconfig.json @@ -13,6 +13,8 @@ "@kbn/i18n-react", "@kbn/core-application-browser-mocks", "@kbn/core-notifications-browser-mocks", + "@kbn/config-schema", + "@kbn/core-plugins-server", "@kbn/test-jest-helpers", "@kbn/i18n", "@kbn/core-http-browser", diff --git a/src/plugins/home/public/application/components/home_app.js b/src/plugins/home/public/application/components/home_app.js index 4289d29cf7aec..4c45fdae567ce 100644 --- a/src/plugins/home/public/application/components/home_app.js +++ b/src/plugins/home/public/application/components/home_app.js @@ -71,7 +71,7 @@ export function HomeApp({ directories, solutions }) { - {guidedOnboardingService.isEnabled && ( + {guidedOnboardingService?.isEnabled && ( diff --git a/src/plugins/home/public/plugin.ts b/src/plugins/home/public/plugin.ts index b7270058aae6c..83ef867c5c198 100644 --- a/src/plugins/home/public/plugin.ts +++ b/src/plugins/home/public/plugin.ts @@ -41,7 +41,7 @@ import { export interface HomePluginStartDependencies { dataViews: DataViewsPublicPluginStart; urlForwarding: UrlForwardingStart; - guidedOnboarding: GuidedOnboardingPluginStart; + guidedOnboarding?: GuidedOnboardingPluginStart; } export interface HomePluginSetupDependencies { @@ -104,7 +104,7 @@ export class HomePublicPlugin addDataService: this.addDataService, featureCatalogue: this.featuresCatalogueRegistry, welcomeService: this.welcomeService, - guidedOnboardingService: guidedOnboarding.guidedOnboardingApi, + guidedOnboardingService: guidedOnboarding?.guidedOnboardingApi, cloud, }); coreStart.chrome.docTitle.change( diff --git a/x-pack/plugins/enterprise_search/common/types/kibana_deps.ts b/x-pack/plugins/enterprise_search/common/types/kibana_deps.ts index 1e268aad1f73c..2379692abb736 100644 --- a/x-pack/plugins/enterprise_search/common/types/kibana_deps.ts +++ b/x-pack/plugins/enterprise_search/common/types/kibana_deps.ts @@ -22,7 +22,7 @@ export interface KibanaDeps { data: DataPublicPluginStart; discover: DiscoverStart; features: FeaturesPluginStart; - guidedOnboarding: GuidedOnboardingPluginStart; + guidedOnboarding?: GuidedOnboardingPluginStart; licensing: LicensingPluginStart; security: SecurityPluginStart; share: SharePluginStart; diff --git a/x-pack/plugins/enterprise_search/kibana.jsonc b/x-pack/plugins/enterprise_search/kibana.jsonc index cfedcbfa2f51d..b43c22b8cb57c 100644 --- a/x-pack/plugins/enterprise_search/kibana.jsonc +++ b/x-pack/plugins/enterprise_search/kibana.jsonc @@ -20,7 +20,6 @@ "logsShared", "cloud", "esUiShared", - "guidedOnboarding", "lens", "embeddable", "share" @@ -31,7 +30,8 @@ "home", "ml", "spaces", - "usageCollection" + "usageCollection", + "guidedOnboarding", ], "requiredBundles": [ "kibanaReact" 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 8c62e5802302d..a0ec79d121486 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 @@ -11,8 +11,6 @@ import { useParams } from 'react-router-dom'; import { useValues } from 'kea'; -import useObservable from 'react-use/lib/useObservable'; - import { EuiTabbedContent, EuiTabbedContentTab } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -80,24 +78,39 @@ export const SearchIndex: React.FC = () => { productAccess: { hasAppSearchAccess }, productFeatures: { hasDefaultIngestPipeline }, } = useValues(KibanaLogic); - const isAppGuideActive = useObservable( - guidedOnboarding.guidedOnboardingApi!.isGuideStepActive$('appSearch', 'add_data') - ); - const isWebsiteGuideActive = useObservable( - guidedOnboarding.guidedOnboardingApi!.isGuideStepActive$('websiteSearch', 'add_data') - ); - const isDatabaseGuideActive = useObservable( - guidedOnboarding.guidedOnboardingApi!.isGuideStepActive$('databaseSearch', 'add_data') - ); + + useEffect(() => { + const subscription = guidedOnboarding?.guidedOnboardingApi + ?.isGuideStepActive$('appSearch', 'add_data') + .subscribe((isStepActive) => { + if (isStepActive && index?.count) { + guidedOnboarding?.guidedOnboardingApi?.completeGuideStep('appSearch', 'add_data'); + } + }); + return () => subscription?.unsubscribe(); + }, [guidedOnboarding, index?.count]); + + useEffect(() => { + const subscription = guidedOnboarding?.guidedOnboardingApi + ?.isGuideStepActive$('websiteSearch', 'add_data') + .subscribe((isStepActive) => { + if (isStepActive && index?.count) { + guidedOnboarding?.guidedOnboardingApi?.completeGuideStep('websiteSearch', 'add_data'); + } + }); + return () => subscription?.unsubscribe(); + }, [guidedOnboarding, index?.count]); + useEffect(() => { - if (isAppGuideActive && index?.count) { - guidedOnboarding.guidedOnboardingApi?.completeGuideStep('appSearch', 'add_data'); - } else if (isWebsiteGuideActive && index?.count) { - guidedOnboarding.guidedOnboardingApi?.completeGuideStep('websiteSearch', 'add_data'); - } else if (isDatabaseGuideActive && index?.count) { - guidedOnboarding.guidedOnboardingApi?.completeGuideStep('databaseSearch', 'add_data'); - } - }, [isAppGuideActive, isWebsiteGuideActive, isDatabaseGuideActive, index?.count]); + const subscription = guidedOnboarding?.guidedOnboardingApi + ?.isGuideStepActive$('databaseSearch', 'add_data') + .subscribe((isStepActive) => { + if (isStepActive && index?.count) { + guidedOnboarding.guidedOnboardingApi?.completeGuideStep('databaseSearch', 'add_data'); + } + }); + return () => subscription?.unsubscribe(); + }, [guidedOnboarding, index?.count]); const ALL_INDICES_TABS: EuiTabbedContentTab[] = [ { 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 7bb064a18041a..4f5871b82a378 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 @@ -40,7 +40,7 @@ export interface KibanaLogicProps { cloud?: CloudSetup; config: ClientConfigType; data: DataPublicPluginStart; - guidedOnboarding: GuidedOnboardingPluginStart; + guidedOnboarding?: GuidedOnboardingPluginStart; history: ScopedHistory; isSidebarEnabled: boolean; lens: LensPublicStart; diff --git a/x-pack/plugins/enterprise_search/server/plugin.ts b/x-pack/plugins/enterprise_search/server/plugin.ts index 02444bd6efa72..4a5e3689ded5c 100644 --- a/x-pack/plugins/enterprise_search/server/plugin.ts +++ b/x-pack/plugins/enterprise_search/server/plugin.ts @@ -87,7 +87,7 @@ interface PluginsSetup { customIntegrations?: CustomIntegrationsPluginSetup; features: FeaturesPluginSetup; globalSearch: GlobalSearchPluginSetup; - guidedOnboarding: GuidedOnboardingPluginSetup; + guidedOnboarding?: GuidedOnboardingPluginSetup; logsShared: LogsSharedPluginSetup; ml?: MlPluginSetup; security: SecurityPluginSetup; @@ -294,13 +294,13 @@ export class EnterpriseSearchPlugin implements Plugin { * Register a config for the search guide */ if (config.canDeployEntSearch) { - guidedOnboarding.registerGuideConfig(appSearchGuideId, appSearchGuideConfig); + guidedOnboarding?.registerGuideConfig(appSearchGuideId, appSearchGuideConfig); } if (config.hasWebCrawler) { - guidedOnboarding.registerGuideConfig(websiteSearchGuideId, websiteSearchGuideConfig); + guidedOnboarding?.registerGuideConfig(websiteSearchGuideId, websiteSearchGuideConfig); } if (config.hasConnectors) { - guidedOnboarding.registerGuideConfig(databaseSearchGuideId, databaseSearchGuideConfig); + guidedOnboarding?.registerGuideConfig(databaseSearchGuideId, databaseSearchGuideConfig); } /** diff --git a/x-pack/plugins/exploratory_view/kibana.jsonc b/x-pack/plugins/exploratory_view/kibana.jsonc index 2c5807a2bbd41..e541ad7858650 100644 --- a/x-pack/plugins/exploratory_view/kibana.jsonc +++ b/x-pack/plugins/exploratory_view/kibana.jsonc @@ -15,7 +15,6 @@ "dataViews", "features", "files", - "guidedOnboarding", "inspector", "lens", "observabilityShared", diff --git a/x-pack/plugins/exploratory_view/public/plugin.ts b/x-pack/plugins/exploratory_view/public/plugin.ts index 92f1efa965185..064e5295b1d5e 100644 --- a/x-pack/plugins/exploratory_view/public/plugin.ts +++ b/x-pack/plugins/exploratory_view/public/plugin.ts @@ -33,7 +33,6 @@ import { } from '@kbn/triggers-actions-ui-plugin/public'; import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; import { SecurityPluginStart } from '@kbn/security-plugin/public'; -import { GuidedOnboardingPluginStart } from '@kbn/guided-onboarding-plugin/public'; import { SpacesPluginStart } from '@kbn/spaces-plugin/public'; import { LicensingPluginStart } from '@kbn/licensing-plugin/public'; import { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; @@ -60,7 +59,6 @@ export interface ExploratoryViewPublicPluginsStart { discover: DiscoverStart; embeddable: EmbeddableStart; - guidedOnboarding: GuidedOnboardingPluginStart; lens: LensPublicStart; licensing: LicensingPluginStart; observabilityShared: ObservabilitySharedPluginStart; diff --git a/x-pack/plugins/exploratory_view/tsconfig.json b/x-pack/plugins/exploratory_view/tsconfig.json index 6cb12bc9582de..4f1f44f484505 100644 --- a/x-pack/plugins/exploratory_view/tsconfig.json +++ b/x-pack/plugins/exploratory_view/tsconfig.json @@ -16,7 +16,6 @@ "@kbn/lens-plugin", "@kbn/spaces-plugin", "@kbn/unified-search-plugin", - "@kbn/guided-onboarding-plugin", "@kbn/discover-plugin", "@kbn/i18n", "@kbn/data-views-plugin", diff --git a/x-pack/plugins/fleet/kibana.jsonc b/x-pack/plugins/fleet/kibana.jsonc index 909328120155a..c5e2eea368fed 100644 --- a/x-pack/plugins/fleet/kibana.jsonc +++ b/x-pack/plugins/fleet/kibana.jsonc @@ -21,7 +21,6 @@ "unifiedSearch", "savedObjectsTagging", "taskManager", - "guidedOnboarding", "files", "uiActions", "dashboard" @@ -36,6 +35,7 @@ "discover", "ingestPipelines", "spaces", + "guidedOnboarding", ], "requiredBundles": [ "kibanaReact", diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/confirm_incoming_data_with_preview.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/confirm_incoming_data_with_preview.tsx index 5e6443986c7c6..1642abe05d72b 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/confirm_incoming_data_with_preview.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/confirm_incoming_data_with_preview.tsx @@ -143,7 +143,7 @@ export const ConfirmIncomingDataWithPreview: React.FunctionComponent = ({ if (!isLoading && enrolledAgents > 0 && numAgentsWithData > 0) { setAgentDataConfirmed(true); if (isGuidedOnboardingActive) { - guidedOnboarding.guidedOnboardingApi?.completeGuidedOnboardingForIntegration( + guidedOnboarding?.guidedOnboardingApi?.completeGuidedOnboardingForIntegration( packageInfo?.name ); } diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/confirm_incoming_data.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/confirm_incoming_data.tsx index 9e87a30034fd4..2a111e5d638a9 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/confirm_incoming_data.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/confirm_incoming_data.tsx @@ -42,7 +42,7 @@ export const ConfirmIncomingData: React.FunctionComponent = ({ if (!isLoading && enrolledAgents > 0 && numAgentsWithData > 0) { setAgentDataConfirmed(true); if (installedPolicy?.name && isGuidedOnboardingActive) { - guidedOnboarding.guidedOnboardingApi?.completeGuidedOnboardingForIntegration( + guidedOnboarding?.guidedOnboardingApi?.completeGuidedOnboardingForIntegration( installedPolicy!.name ); } diff --git a/x-pack/plugins/fleet/public/hooks/use_is_guided_onboarding_active.ts b/x-pack/plugins/fleet/public/hooks/use_is_guided_onboarding_active.ts index 22b8ab9b1a231..6bd746d13def6 100644 --- a/x-pack/plugins/fleet/public/hooks/use_is_guided_onboarding_active.ts +++ b/x-pack/plugins/fleet/public/hooks/use_is_guided_onboarding_active.ts @@ -17,7 +17,7 @@ export const useIsGuidedOnboardingActive = (packageName?: string): boolean => { const { guidedOnboarding } = useStartServices(); const isGuidedOnboardingActiveForIntegration = useObservable( // if guided onboarding is not available, return false - guidedOnboarding.guidedOnboardingApi + guidedOnboarding?.guidedOnboardingApi ? guidedOnboarding.guidedOnboardingApi.isGuidedOnboardingActiveForIntegration$(packageName) : of(false) ); diff --git a/x-pack/plugins/fleet/public/plugin.ts b/x-pack/plugins/fleet/public/plugin.ts index 7c1aadeb530eb..ce99aa4ec8d30 100644 --- a/x-pack/plugins/fleet/public/plugin.ts +++ b/x-pack/plugins/fleet/public/plugin.ts @@ -129,7 +129,7 @@ export interface FleetStartDeps { share: SharePluginStart; cloud?: CloudStart; usageCollection?: UsageCollectionStart; - guidedOnboarding: GuidedOnboardingPluginStart; + guidedOnboarding?: GuidedOnboardingPluginStart; } export interface FleetStartServices extends CoreStart, Exclude { @@ -140,7 +140,7 @@ export interface FleetStartServices extends CoreStart, Exclude { diff --git a/x-pack/plugins/observability/kibana.jsonc b/x-pack/plugins/observability/kibana.jsonc index 94b61bc05b60a..86db25972fb1b 100644 --- a/x-pack/plugins/observability/kibana.jsonc +++ b/x-pack/plugins/observability/kibana.jsonc @@ -23,7 +23,6 @@ "exploratoryView", "features", "files", - "guidedOnboarding", "inspector", "lens", "observabilityShared", @@ -44,7 +43,8 @@ "usageCollection", "cloud", "spaces", - "serverless" + "serverless", + "guidedOnboarding" ], "requiredBundles": [ "data", @@ -58,4 +58,4 @@ "common" ] } -} \ No newline at end of file +} diff --git a/x-pack/plugins/observability/public/plugin.ts b/x-pack/plugins/observability/public/plugin.ts index 37d294f0fabe0..083686fdc8edd 100644 --- a/x-pack/plugins/observability/public/plugin.ts +++ b/x-pack/plugins/observability/public/plugin.ts @@ -127,7 +127,7 @@ export interface ObservabilityPublicPluginsStart { discover: DiscoverStart; embeddable: EmbeddableStart; exploratoryView: ExploratoryViewPublicStart; - guidedOnboarding: GuidedOnboardingPluginStart; + guidedOnboarding?: GuidedOnboardingPluginStart; lens: LensPublicStart; licensing: LicensingPluginStart; observabilityShared: ObservabilitySharedPluginStart; diff --git a/x-pack/plugins/observability/server/plugin.ts b/x-pack/plugins/observability/server/plugin.ts index c034a41fbf2d5..4e2c7a57ead13 100644 --- a/x-pack/plugins/observability/server/plugin.ts +++ b/x-pack/plugins/observability/server/plugin.ts @@ -63,7 +63,7 @@ export type ObservabilityPluginSetup = ReturnType; interface PluginSetup { alerting: PluginSetupContract; features: FeaturesSetup; - guidedOnboarding: GuidedOnboardingPluginSetup; + guidedOnboarding?: GuidedOnboardingPluginSetup; ruleRegistry: RuleRegistryPluginSetupContract; share: SharePluginSetup; spaces?: SpacesPluginSetup; @@ -344,7 +344,7 @@ export class ObservabilityPlugin implements Plugin { /** * Register a config for the observability guide */ - plugins.guidedOnboarding.registerGuideConfig(kubernetesGuideId, kubernetesGuideConfig); + plugins.guidedOnboarding?.registerGuideConfig(kubernetesGuideId, kubernetesGuideConfig); return { getAlertDetailsConfig() { diff --git a/x-pack/plugins/observability_shared/kibana.jsonc b/x-pack/plugins/observability_shared/kibana.jsonc index 1948fca972d22..857e198771787 100644 --- a/x-pack/plugins/observability_shared/kibana.jsonc +++ b/x-pack/plugins/observability_shared/kibana.jsonc @@ -7,8 +7,8 @@ "server": false, "browser": true, "configPath": ["xpack", "observability_shared"], - "requiredPlugins": ["cases", "guidedOnboarding", "uiActions", "embeddable", "share"], - "optionalPlugins": [], + "requiredPlugins": ["cases", "uiActions", "embeddable", "share"], + "optionalPlugins": ["guidedOnboarding"], "requiredBundles": ["data", "inspector", "kibanaReact", "kibanaUtils", "advancedSettings"], "extraPublicDirs": ["common"] } diff --git a/x-pack/plugins/observability_shared/public/plugin.ts b/x-pack/plugins/observability_shared/public/plugin.ts index faf8990622337..93d17b297cfbf 100644 --- a/x-pack/plugins/observability_shared/public/plugin.ts +++ b/x-pack/plugins/observability_shared/public/plugin.ts @@ -26,7 +26,7 @@ export interface ObservabilitySharedSetup { export interface ObservabilitySharedStart { spaces?: SpacesPluginStart; cases: CasesUiStart; - guidedOnboarding: GuidedOnboardingPluginStart; + guidedOnboarding?: GuidedOnboardingPluginStart; setIsSidebarEnabled: (isEnabled: boolean) => void; embeddable: EmbeddableStart; share: SharePluginStart; @@ -73,7 +73,7 @@ export class ObservabilitySharedPlugin implements Plugin { getUrlForApp: application.getUrlForApp, navigateToApp: application.navigateToApp, navigationSections$: this.navigationRegistry.sections$, - guidedOnboardingApi: plugins.guidedOnboarding.guidedOnboardingApi, + guidedOnboardingApi: plugins.guidedOnboarding?.guidedOnboardingApi, getPageTemplateServices: () => ({ coreStart: core }), isSidebarEnabled$: this.isSidebarEnabled$, }); diff --git a/x-pack/plugins/security_solution/kibana.jsonc b/x-pack/plugins/security_solution/kibana.jsonc index 70f03ded9314c..99c2f585eb3f7 100644 --- a/x-pack/plugins/security_solution/kibana.jsonc +++ b/x-pack/plugins/security_solution/kibana.jsonc @@ -28,7 +28,6 @@ "eventLog", "features", "fieldFormats", - "guidedOnboarding", "inspector", "kubernetesSecurity", "lens", @@ -69,7 +68,8 @@ "telemetry", "dataViewFieldEditor", "osquery", - "savedObjectsTaggingOss" + "savedObjectsTaggingOss", + "guidedOnboarding" ], "requiredBundles": [ "esUiShared", diff --git a/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/tour.tsx b/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/tour.tsx index d876da7803bbd..2cf0153a1a3fe 100644 --- a/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/tour.tsx +++ b/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/tour.tsx @@ -36,22 +36,26 @@ const initialState: TourContextValue = { const TourContext = createContext(initialState); export const RealTourContextProvider = ({ children }: { children: ReactChild }) => { - const { guidedOnboardingApi } = useKibana().services.guidedOnboarding; + const { guidedOnboarding } = useKibana().services; const isRulesTourActive = useObservable( - guidedOnboardingApi?.isGuideStepActive$(siemGuideId, SecurityStepId.rules).pipe( - // if no result after 30s the observable will error, but the error handler will just emit false - timeout(30000), - catchError((error) => of(false)) - ) ?? of(false), + guidedOnboarding?.guidedOnboardingApi + ?.isGuideStepActive$(siemGuideId, SecurityStepId.rules) + .pipe( + // if no result after 30s the observable will error, but the error handler will just emit false + timeout(30000), + catchError((error) => of(false)) + ) ?? of(false), false ); const isAlertsCasesTourActive = useObservable( - guidedOnboardingApi?.isGuideStepActive$(siemGuideId, SecurityStepId.alertsCases).pipe( - // if no result after 30s the observable will error, but the error handler will just emit false - timeout(30000), - catchError((error) => of(false)) - ) ?? of(false), + guidedOnboarding?.guidedOnboardingApi + ?.isGuideStepActive$(siemGuideId, SecurityStepId.alertsCases) + .pipe( + // if no result after 30s the observable will error, but the error handler will just emit false + timeout(30000), + catchError((error) => of(false)) + ) ?? of(false), false ); @@ -79,12 +83,12 @@ export const RealTourContextProvider = ({ children }: { children: ReactChild }) const [completeStep, setCompleteStep] = useState(null); useEffect(() => { - if (!completeStep || !guidedOnboardingApi) { + if (!completeStep || !guidedOnboarding?.guidedOnboardingApi) { return; } let ignore = false; const complete = async () => { - await guidedOnboardingApi.completeGuideStep(siemGuideId, completeStep); + await guidedOnboarding?.guidedOnboardingApi?.completeGuideStep(siemGuideId, completeStep); if (!ignore) { setCompleteStep(null); _setActiveStep(1); @@ -94,7 +98,7 @@ export const RealTourContextProvider = ({ children }: { children: ReactChild }) return () => { ignore = true; }; - }, [completeStep, guidedOnboardingApi]); + }, [completeStep, guidedOnboarding]); const endTourStep = useCallback((tourId: SecurityStepId) => { setCompleteStep(tourId); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/alternative_tour/tour.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/alternative_tour/tour.tsx index fe4ac1ae9989a..cba3fb13689db 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/alternative_tour/tour.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/alternative_tour/tour.tsx @@ -25,13 +25,10 @@ export const RulesPageTourComponent: React.FC = ({ children }) => { tourPopoverWidth: 300, }; - const { - storage, - guidedOnboarding: { guidedOnboardingApi }, - } = useKibana().services; + const { storage, guidedOnboarding } = useKibana().services; const isGuidedOnboardingActive = useObservable( - guidedOnboardingApi?.isGuideStepActive$(siemGuideId, 'rules') ?? of(false), + guidedOnboarding?.guidedOnboardingApi?.isGuideStepActive$(siemGuideId, 'rules') ?? of(false), true ); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table/guided_onboarding/rules_management_tour.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table/guided_onboarding/rules_management_tour.tsx index 5cd5a8e30808f..e27910df0b7e0 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table/guided_onboarding/rules_management_tour.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table/guided_onboarding/rules_management_tour.tsx @@ -44,12 +44,12 @@ export enum GuidedOnboardingRulesStatus { } export const RulesManagementTour = () => { - const { guidedOnboardingApi } = useKibana().services.guidedOnboarding; + const { guidedOnboarding } = useKibana().services; const { executeBulkAction } = useExecuteBulkAction(); const { actions } = useRulesTableContext(); const isRulesStepActive = useObservable( - guidedOnboardingApi?.isGuideStepActive$(siemGuideId, 'rules') ?? of(false), + guidedOnboarding?.guidedOnboardingApi?.isGuideStepActive$(siemGuideId, 'rules') ?? of(false), false ); @@ -106,9 +106,9 @@ export const RulesManagementTour = () => { // Synchronize the current "internal" tour step with the global one useEffect(() => { if (isRulesStepActive && tourStatus === GuidedOnboardingRulesStatus.completed) { - guidedOnboardingApi?.completeGuideStep('siem', 'rules'); + guidedOnboarding?.guidedOnboardingApi?.completeGuideStep('siem', 'rules'); } - }, [guidedOnboardingApi, isRulesStepActive, tourStatus]); + }, [guidedOnboarding, isRulesStepActive, tourStatus]); const enableDemoRule = useCallback(async () => { if (demoRule) { diff --git a/x-pack/plugins/security_solution/public/types.ts b/x-pack/plugins/security_solution/public/types.ts index 50d8409ca5560..0dcd00d3db78b 100644 --- a/x-pack/plugins/security_solution/public/types.ts +++ b/x-pack/plugins/security_solution/public/types.ts @@ -111,7 +111,7 @@ export interface StartPlugins { embeddable: EmbeddableStart; inspector: InspectorStart; fleet?: FleetStart; - guidedOnboarding: GuidedOnboardingPluginStart; + guidedOnboarding?: GuidedOnboardingPluginStart; kubernetesSecurity: KubernetesSecurityStart; lens: LensPublicStart; lists?: ListsPluginStart; diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index f9c78588ae5b4..24a33525d9f73 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -416,19 +416,19 @@ export class Plugin implements ISecuritySolutionPlugin { depsStart.cloudExperiments .getVariation('security-solutions.guided-onboarding-content', defaultGuideTranslations) .then((variation) => { - plugins.guidedOnboarding.registerGuideConfig( + plugins.guidedOnboarding?.registerGuideConfig( siemGuideId, getSiemGuideConfig(variation) ); }); } catch { - plugins.guidedOnboarding.registerGuideConfig( + plugins.guidedOnboarding?.registerGuideConfig( siemGuideId, getSiemGuideConfig(defaultGuideTranslations) ); } } else { - plugins.guidedOnboarding.registerGuideConfig( + plugins.guidedOnboarding?.registerGuideConfig( siemGuideId, getSiemGuideConfig(defaultGuideTranslations) ); diff --git a/x-pack/plugins/security_solution/server/plugin_contract.ts b/x-pack/plugins/security_solution/server/plugin_contract.ts index a02b3ebbc5384..b59d9aac37efe 100644 --- a/x-pack/plugins/security_solution/server/plugin_contract.ts +++ b/x-pack/plugins/security_solution/server/plugin_contract.ts @@ -61,7 +61,7 @@ export interface SecuritySolutionPluginSetupDependencies { usageCollection?: UsageCollectionPluginSetup; licensing: LicensingPluginSetup; osquery: OsqueryPluginSetup; - guidedOnboarding: GuidedOnboardingPluginSetup; + guidedOnboarding?: GuidedOnboardingPluginSetup; unifiedSearch: UnifiedSearchServerPluginSetup; } diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/explore/guided_onboarding/tour.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/explore/guided_onboarding/tour.cy.ts index 73527aeda9662..fc2896b2e6403 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/explore/guided_onboarding/tour.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/explore/guided_onboarding/tour.cy.ts @@ -30,7 +30,7 @@ import { login } from '../../../tasks/login'; import { visit } from '../../../tasks/navigation'; import { startAlertsCasesTour } from '../../../tasks/api_calls/tour'; -describe('Guided onboarding tour', { tags: ['@ess', '@brokenInServerless'] }, () => { +describe('Guided onboarding tour', { tags: ['@ess'] }, () => { before(() => { cleanKibana(); login();