Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Guided onboarding] Use Kibana features to grant access #155065

Merged
merged 22 commits into from
Apr 26, 2023
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
f47827c
[Guided onboarding] Add a Kibana feature for guided onboarding to han…
yuliacech Apr 17, 2023
40b3bab
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Apr 17, 2023
f57bc64
[Guided onboarding] Remove the header button and the landing page rou…
yuliacech Apr 19, 2023
1ba51f7
[Guided onboarding] Remove the help link if guided onboarding is disa…
yuliacech Apr 20, 2023
16d92f6
[Guided onboarding] Add an error state to the example plugin
yuliacech Apr 20, 2023
4d48b58
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Apr 20, 2023
802bba1
Merge branch 'main' into guided_onboarding/8.8/roles
yuliacech Apr 20, 2023
2c8af74
[Guided onboarding] Fix cloud links tests
yuliacech Apr 20, 2023
72e70d8
[Guided onboarding] Fix home plugin tests
yuliacech Apr 20, 2023
5e9cee7
[Guided onboarding] Update home plugin snapshots
yuliacech Apr 20, 2023
50e8d8d
[Guided onboarding] Fix security functional tests
yuliacech Apr 20, 2023
266697f
Merge branch 'main' into guided_onboarding/8.8/roles
yuliacech Apr 20, 2023
547f790
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Apr 20, 2023
f8d73b5
[Guided onboarding] Add a comment to the "read" feature privileges
yuliacech Apr 20, 2023
d09c6cc
Merge branch 'main' into guided_onboarding/8.8/roles
yuliacech Apr 24, 2023
77a5b7a
[Guided onboarding] Fix functional tests
yuliacech Apr 24, 2023
6ab8f1d
Merge branch 'main' into guided_onboarding/8.8/roles
yuliacech Apr 25, 2023
1abaab9
Merge branch 'main' into guided_onboarding/8.8/roles
yuliacech Apr 25, 2023
d58198a
Merge branch 'main' into guided_onboarding/8.8/roles
yuliacech Apr 25, 2023
57bcef3
Merge branch 'main' into guided_onboarding/8.8/roles
yuliacech Apr 25, 2023
17f68ed
Merge branch 'main' into guided_onboarding/8.8/roles
yuliacech Apr 26, 2023
98e2564
Merge branch 'main' into guided_onboarding/8.8/roles
yuliacech Apr 26, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 37 additions & 23 deletions examples/guided_onboarding_example/public/components/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,7 @@ import { FormattedMessage, I18nProvider } from '@kbn/i18n-react';
import { Router, Switch } from 'react-router-dom';
import { Route } from '@kbn/shared-ux-router';

import {
EuiPage,
EuiPageBody,
EuiPageContent_Deprecated as EuiPageContent,
EuiPageHeader,
EuiTitle,
} from '@elastic/eui';
import { EuiPageTemplate } from '@elastic/eui';

import { CoreStart, ScopedHistory } from '@kbn/core/public';

Expand All @@ -39,19 +33,17 @@ export const GuidedOnboardingExampleApp = (props: GuidedOnboardingExampleAppDeps

return (
<I18nProvider>
<EuiPage restrictWidth="1000px">
<EuiPageBody>
<EuiPageHeader>
<EuiTitle size="l">
<h1>
<FormattedMessage
id="guidedOnboardingExample.title"
defaultMessage="Guided onboarding examples"
/>
</h1>
</EuiTitle>
</EuiPageHeader>
<EuiPageContent>
<EuiPageTemplate restrictWidth={true} panelled={true}>
<EuiPageTemplate.Header
pageTitle={
<FormattedMessage
id="guidedOnboardingExample.title"
defaultMessage="Guided onboarding examples"
/>
}
/>
{guidedOnboarding.guidedOnboardingApi?.isEnabled ? (
<EuiPageTemplate.Section>
<Router history={history}>
<Switch>
<Route exact path="/">
Expand All @@ -75,9 +67,31 @@ export const GuidedOnboardingExampleApp = (props: GuidedOnboardingExampleAppDeps
/>
</Switch>
</Router>
</EuiPageContent>
</EuiPageBody>
</EuiPage>
</EuiPageTemplate.Section>
) : (
<EuiPageTemplate.EmptyPrompt
iconType="error"
color="danger"
title={
<h2>
<FormattedMessage
id="guidedOnboardingExample.errorTitle"
defaultMessage="Guided onboarding is disabled"
/>
</h2>
}
body={
<p>
<FormattedMessage
id="guidedOnboardingExample.errorDescription"
defaultMessage="Make sure your Kibana instance runs on Cloud and/or
your user has access to Setup guides feature."
/>
</p>
}
/>
)}
</EuiPageTemplate>
</I18nProvider>
);
};
2 changes: 2 additions & 0 deletions src/plugins/guided_onboarding/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ export const PLUGIN_ID = 'guidedOnboarding';
export const PLUGIN_NAME = 'guidedOnboarding';

export const API_BASE_PATH = '/api/guided_onboarding';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This API really should be /internal/guided_onboarding. I can't think of any reason why a user would need to use this API directly, and by using the /api path prefix, we are indicating it is public, meaning you can't make any breaking changes to it without approvals, a long deprecation period, etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree @lukeelmers, I opened this issue to change the route's prefix. Our team was not aware of this convention when setting up the plugin.
What do you think, do we have to handle the prefix issue as a breaking change or can we update it in the next minor? The plugin was first released in 8.7

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since it was introduced recently & is undocumented (and also something folks would be unlikely to discover and get much utility out of), then IMO if you fix it quickly, you are probably safe to change it, treat this as a bug, and document it in the release notes.


export const PLUGIN_FEATURE = 'guidedOnboardingFeature';
3 changes: 2 additions & 1 deletion src/plugins/guided_onboarding/kibana.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"server": true,
"browser": true,
"optionalPlugins": [
"cloud"
"cloud",
"features"
],
"requiredBundles": [
"kibanaReact"
Expand Down
1 change: 1 addition & 0 deletions src/plugins/guided_onboarding/public/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const apiServiceMock: jest.Mocked<GuidedOnboardingPluginStart> = {
isGuidePanelOpen$: new BehaviorSubject(false),
isLoading$: new BehaviorSubject(false),
getGuideConfig: jest.fn(),
isEnabled: true,
},
};

Expand Down
9 changes: 6 additions & 3 deletions src/plugins/guided_onboarding/public/plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import {
} from '@kbn/core/public';

import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';

import { PLUGIN_FEATURE } from '../common/constants';
import type {
AppPluginStartDependencies,
GuidedOnboardingPluginSetup,
Expand All @@ -43,11 +45,12 @@ export class GuidedOnboardingPlugin
): GuidedOnboardingPluginStart {
const { chrome, http, theme, application, notifications, uiSettings } = core;

// Guided onboarding UI is only available on cloud and if the access to the Kibana feature is granted
const isEnabled = !!(cloud?.isCloudEnabled && application.capabilities[PLUGIN_FEATURE].enabled);
// Initialize services
apiService.setup(http, !!cloud?.isCloudEnabled);
apiService.setup(http, isEnabled);

// Guided onboarding UI is only available on cloud
if (cloud?.isCloudEnabled) {
if (isEnabled) {
chrome.navControls.registerExtension({
order: 1000,
mount: (target) =>
Expand Down
18 changes: 11 additions & 7 deletions src/plugins/guided_onboarding/public/services/api.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,15 @@ import {
import { ConfigService } from './config.service';

export class ApiService implements GuidedOnboardingApi {
private isCloudEnabled: boolean | undefined;
private _isEnabled: boolean = false;
private client: HttpSetup | undefined;
private pluginState$!: BehaviorSubject<PluginState | undefined>;
public isLoading$ = new BehaviorSubject<boolean>(false);
public isGuidePanelOpen$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
private configService = new ConfigService();

public setup(httpClient: HttpSetup, isCloudEnabled: boolean) {
this.isCloudEnabled = isCloudEnabled;
public setup(httpClient: HttpSetup, isEnabled: boolean) {
this._isEnabled = isEnabled;
this.client = httpClient;
this.pluginState$ = new BehaviorSubject<PluginState | undefined>(undefined);
this.isGuidePanelOpen$ = new BehaviorSubject<boolean>(false);
Expand Down Expand Up @@ -94,7 +94,7 @@ export class ApiService implements GuidedOnboardingApi {
* Subsequently, the observable is updated automatically, when the state changes.
*/
public fetchPluginState$(): Observable<PluginState | undefined> {
if (!this.isCloudEnabled) {
if (!this._isEnabled) {
return of(undefined);
}
if (!this.client) {
Expand All @@ -118,7 +118,7 @@ export class ApiService implements GuidedOnboardingApi {
* where all guides are displayed with their corresponding status.
*/
public async fetchAllGuidesState(): Promise<{ state: GuideState[] } | undefined> {
if (!this.isCloudEnabled) {
if (!this._isEnabled) {
return undefined;
}
if (!this.client) {
Expand All @@ -143,7 +143,7 @@ export class ApiService implements GuidedOnboardingApi {
state: { status?: PluginStatus; guide?: GuideState },
panelState: boolean
): Promise<{ pluginState: PluginState } | undefined> {
if (!this.isCloudEnabled) {
if (!this._isEnabled) {
return undefined;
}
if (!this.client) {
Expand Down Expand Up @@ -467,7 +467,7 @@ export class ApiService implements GuidedOnboardingApi {
* @return {Promise} a promise with the guide config or undefined if the config is not found
*/
public async getGuideConfig(guideId: GuideId): Promise<GuideConfig | undefined> {
if (!this.isCloudEnabled) {
if (!this._isEnabled) {
return undefined;
}
if (!this.client) {
Expand All @@ -478,6 +478,10 @@ export class ApiService implements GuidedOnboardingApi {
this.isLoading$.next(false);
return config;
}

public get isEnabled() {
return this._isEnabled;
}
}

export const apiService = new ApiService();
1 change: 1 addition & 0 deletions src/plugins/guided_onboarding/public/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,5 @@ export interface GuidedOnboardingApi {
isGuidePanelOpen$: Observable<boolean>;
isLoading$: Observable<boolean>;
getGuideConfig: (guideId: GuideId) => Promise<GuideConfig | undefined>;
readonly isEnabled: boolean;
}
42 changes: 42 additions & 0 deletions src/plugins/guided_onboarding/server/feature.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* 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 type { KibanaFeatureConfig } from '@kbn/features-plugin/common';
import { i18n } from '@kbn/i18n';
import { DEFAULT_APP_CATEGORIES } from '@kbn/core/server';
import { PLUGIN_FEATURE, PLUGIN_ID } from '../common/constants';
import { guideStateSavedObjectsType, pluginStateSavedObjectsType } from './saved_objects';

export const GUIDED_ONBOARDING_FEATURE: KibanaFeatureConfig = {
id: PLUGIN_FEATURE,
name: i18n.translate('guidedOnboarding.featureRegistry.featureName', {
defaultMessage: 'Setup guides',
}),
category: DEFAULT_APP_CATEGORIES.management,
app: [PLUGIN_ID],
privileges: {
all: {
app: [PLUGIN_ID],
savedObject: {
all: [guideStateSavedObjectsType, pluginStateSavedObjectsType],
read: [],
},
ui: ['enabled'],
},
read: {
// we haven't implemented "read-only" access yet, so this feature can only be granted
// as "all" or "none"
disabled: true,
savedObject: {
all: [],
read: [],
},
ui: [],
},
},
};
6 changes: 5 additions & 1 deletion src/plugins/guided_onboarding/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import { PluginInitializerContext, CoreSetup, Plugin, Logger } from '@kbn/core/server';

import type { GuideId, GuideConfig } from '@kbn/guided-onboarding';
import { PluginSetupContract as FeaturesPluginSetup } from '@kbn/features-plugin/server';
import { GUIDED_ONBOARDING_FEATURE } from './feature';
import { GuidedOnboardingPluginSetup, GuidedOnboardingPluginStart } from './types';
import { defineRoutes } from './routes';
import { guideStateSavedObjects, pluginStateSavedObjects } from './saved_objects';
Expand All @@ -25,7 +27,7 @@ export class GuidedOnboardingPlugin
this.guidesConfig = {} as GuidesConfig;
}

public setup(core: CoreSetup) {
public setup(core: CoreSetup, plugins: { features?: FeaturesPluginSetup }) {
this.logger.debug('guidedOnboarding: Setup');
const router = core.http.createRouter();

Expand All @@ -36,6 +38,8 @@ export class GuidedOnboardingPlugin
core.savedObjects.registerType(guideStateSavedObjects);
core.savedObjects.registerType(pluginStateSavedObjects);

plugins.features?.registerKibanaFeature(GUIDED_ONBOARDING_FEATURE);

return {
registerGuideConfig: (guideId: GuideId, guideConfig: GuideConfig) => {
if (this.guidesConfig[guideId]) {
Expand Down
1 change: 1 addition & 0 deletions src/plugins/guided_onboarding/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"@kbn/core-http-browser",
"@kbn/core-http-browser-mocks",
"@kbn/config-schema",
"@kbn/features-plugin",
],
"exclude": [
"target/**/*",
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading