[Security Solution] Use observable to update deepLinks when setting changes (#134577)

* use observable to update deepLinks when setting changes

* change advanced setting texts

* test fix. app links needs always to be defined

* fix home page bug
This commit is contained in:
Sergi Massaneda 2022-06-17 17:59:50 +01:00 committed by GitHub
parent 3e948ba06f
commit 62e9b8b956
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 44 additions and 63 deletions

View file

@ -11,7 +11,7 @@ import { get } from 'lodash';
import { LicenseType } from '@kbn/licensing-plugin/common/types';
import { getCasesDeepLinks } from '@kbn/cases-plugin/public';
import { AppDeepLink, AppNavLinkStatus, AppUpdater, Capabilities } from '@kbn/core/public';
import { Subject } from 'rxjs';
import { Subject, Subscription } from 'rxjs';
import { SecurityPageName } from '../types';
import {
OVERVIEW,
@ -561,8 +561,8 @@ const formatDeepLinks = (appLinks: AppLinkItems): AppDeepLink[] =>
/**
* Registers any change in appLinks to be updated in app deepLinks
*/
export const registerDeepLinksUpdater = (appUpdater$: Subject<AppUpdater>) => {
subscribeAppLinks((appLinks) => {
export const registerDeepLinksUpdater = (appUpdater$: Subject<AppUpdater>): Subscription => {
return subscribeAppLinks((appLinks) => {
appUpdater$.next(() => ({
navLinkStatus: AppNavLinkStatus.hidden, // needed to prevent main security link to switch to visible after update
deepLinks: formatDeepLinks(appLinks),

View file

@ -8,7 +8,7 @@
import { i18n } from '@kbn/i18n';
import reduceReducers from 'reduce-reducers';
import { BehaviorSubject, Subject, Subscription } from 'rxjs';
import { pluck } from 'rxjs/operators';
import { combineLatestWith, pluck } from 'rxjs/operators';
import { AnyAction, Reducer } from 'redux';
import {
AppMountParameters,
@ -49,7 +49,7 @@ import {
} from '../common/constants';
import { getDeepLinks, registerDeepLinksUpdater } from './app/deep_links';
import { AppLinkItems, LinksPermissions, subscribeAppLinks, updateAppLinks } from './common/links';
import { LinksPermissions, updateAppLinks } from './common/links';
import { getSubPluginRoutesByCapabilities, manageOldSiemRoutes } from './helpers';
import { SecurityAppStore } from './common/store/store';
import { licenseService } from './common/hooks/use_license';
@ -81,7 +81,6 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
private appUpdater$ = new Subject<AppUpdater>();
private storage = new Storage(localStorage);
private licensingSubscription: Subscription | null = null;
/**
* Lazily instantiated subPlugins.
@ -141,7 +140,8 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
title: SOLUTION_NAME,
appRoute: APP_PATH,
category: DEFAULT_APP_CATEGORIES.security,
navLinkStatus: AppNavLinkStatus.hidden,
// Initializing app as visible to make sure it appears on the Kibana home page, it is hidden when deepLinks update
navLinkStatus: AppNavLinkStatus.visible,
searchable: true,
updater$: this.appUpdater$,
euiIconType: APP_ICON_SOLUTION,
@ -176,12 +176,10 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
mount: async (params: AppMountParameters) => {
const [coreStart] = await core.getStartServices();
const subscription = subscribeAppLinks((links: AppLinkItems) => {
// It has to be called once after deep links are initialized
if (links.length > 0) {
manageOldSiemRoutes(coreStart);
subscription.unsubscribe();
}
const subscription = this.appUpdater$.subscribe(() => {
// wait for app initialization to set the links
manageOldSiemRoutes(coreStart);
subscription.unsubscribe();
});
return () => true;
@ -205,6 +203,8 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
public start(core: CoreStart, plugins: StartPlugins) {
KibanaServices.init({ ...core, ...plugins, kibanaVersion: this.kibanaVersion });
ExperimentalFeaturesService.init({ experimentalFeatures: this.experimentalFeatures });
licenseService.start(plugins.licensing.license$);
if (plugins.fleet) {
const { registerExtension } = plugins.fleet;
@ -248,9 +248,7 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
}
public stop() {
if (this.licensingSubscription !== null) {
this.licensingSubscription.unsubscribe();
}
licenseService.stop();
return {};
}
@ -465,65 +463,47 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
* Register deepLinks and appUpdater for all app links, to change deepLinks as needed when licensing changes.
*/
async registerAppLinks(core: CoreStart, plugins: StartPlugins) {
licenseService.start(plugins.licensing.license$);
const licensing = licenseService.getLicenseInformation$();
const newNavEnabled = core.uiSettings.get(ENABLE_GROUPED_NAVIGATION, false);
if (newNavEnabled) {
registerDeepLinksUpdater(this.appUpdater$);
}
const { links, getFilteredLinks } = await this.lazyApplicationLinks();
const linksPermissions: LinksPermissions = {
experimentalFeatures: this.experimentalFeatures,
capabilities: core.application.capabilities,
};
const { license$ } = plugins.licensing;
const newNavEnabled$ = core.uiSettings.get$<boolean>(ENABLE_GROUPED_NAVIGATION, false);
if (licensing == null) {
// update without license (defaults to "basic")
updateAppLinks(links, linksPermissions);
let appLinksSubscription: Subscription | null = null;
license$.pipe(combineLatestWith(newNavEnabled$)).subscribe(async ([license, newNavEnabled]) => {
const linksPermissions: LinksPermissions = {
experimentalFeatures: this.experimentalFeatures,
capabilities: core.application.capabilities,
};
if (!newNavEnabled) {
// TODO: remove block when nav flag no longer needed
if (license.type !== undefined) {
linksPermissions.license = license;
}
if (appLinksSubscription) {
appLinksSubscription.unsubscribe();
appLinksSubscription = null;
}
if (newNavEnabled) {
appLinksSubscription = registerDeepLinksUpdater(this.appUpdater$);
} else {
// old nav links update
this.appUpdater$.next(() => ({
navLinkStatus: AppNavLinkStatus.hidden, // workaround to prevent main navLink to switch to visible after update. should not be needed
navLinkStatus: AppNavLinkStatus.hidden,
deepLinks: getDeepLinks(
this.experimentalFeatures,
undefined,
license.type,
core.application.capabilities
),
}));
}
// async links filtering
updateAppLinks(await getFilteredLinks(core, plugins), linksPermissions);
return;
}
this.licensingSubscription = licensing.subscribe(async (currentLicense) => {
if (currentLicense.type !== undefined) {
linksPermissions.license = currentLicense;
}
// set initial links to not block rendering
updateAppLinks(links, linksPermissions);
if (!newNavEnabled) {
// TODO: remove block when nav flag no longer needed
this.appUpdater$.next(() => ({
navLinkStatus: AppNavLinkStatus.hidden, // workaround to prevent main navLink to switch to visible after update. should not be needed
deepLinks: getDeepLinks(
this.experimentalFeatures,
currentLicense.type,
core.application.capabilities
),
}));
}
// async links filtering
updateAppLinks(await getFilteredLinks(core, plugins), linksPermissions);
// set filtered links asynchronously
const filteredLinks = await getFilteredLinks(core, plugins);
updateAppLinks(filteredLinks, linksPermissions);
});
}
}

View file

@ -150,18 +150,19 @@ export const initUiSettings = (
? {
[ENABLE_GROUPED_NAVIGATION]: {
name: i18n.translate('xpack.securitySolution.uiSettings.enableGroupedNavigation', {
defaultMessage: 'Enable grouped navigation',
defaultMessage: 'New streamlined navigation',
}),
value: false,
type: 'boolean',
description: i18n.translate(
'xpack.securitySolution.uiSettings.enableGroupedNavigationDescription',
{
defaultMessage: '<p>Enables the grouped side navigation for Security Solution</p>',
defaultMessage:
'<p>Improve your experience with the new navigation organized and optimized around the most important workflows.</p>',
}
),
category: [APP_ID],
requiresPageReload: true,
requiresPageReload: false,
schema: schema.boolean(),
},
}