From d46cc94a81c1cbb972764f8d28c3f241e992a1ef Mon Sep 17 00:00:00 2001 From: Yulong Ruan Date: Fri, 15 Mar 2024 10:52:05 +0800 Subject: [PATCH 01/13] Filter left nav menu items according to the current workspace Signed-off-by: Yulong Ruan --- src/core/public/chrome/index.ts | 7 ++- src/core/public/chrome/nav_links/index.ts | 2 +- .../chrome/nav_links/nav_links_service.ts | 13 ++++- src/core/public/index.ts | 2 + src/plugins/workspace/public/plugin.ts | 55 +++++++++++++++++++ 5 files changed, 76 insertions(+), 3 deletions(-) diff --git a/src/core/public/chrome/index.ts b/src/core/public/chrome/index.ts index 4004c2c323f9..6790e1678f9c 100644 --- a/src/core/public/chrome/index.ts +++ b/src/core/public/chrome/index.ts @@ -45,7 +45,12 @@ export { ChromeHelpExtensionMenuGitHubLink, } from './ui/header/header_help_menu'; export { NavType } from './ui'; -export { ChromeNavLink, ChromeNavLinks, ChromeNavLinkUpdateableFields } from './nav_links'; +export { + ChromeNavLink, + ChromeNavLinks, + ChromeNavLinkUpdateableFields, + LinksUpdater, +} from './nav_links'; export { ChromeRecentlyAccessed, ChromeRecentlyAccessedHistoryItem } from './recently_accessed'; export { ChromeNavControl, ChromeNavControls } from './nav_controls'; export { ChromeDocTitle } from './doc_title'; diff --git a/src/core/public/chrome/nav_links/index.ts b/src/core/public/chrome/nav_links/index.ts index 4be7e0be49b8..a0c9e847239e 100644 --- a/src/core/public/chrome/nav_links/index.ts +++ b/src/core/public/chrome/nav_links/index.ts @@ -29,4 +29,4 @@ */ export { ChromeNavLink, ChromeNavLinkUpdateableFields } from './nav_link'; -export { ChromeNavLinks, NavLinksService } from './nav_links_service'; +export { ChromeNavLinks, LinksUpdater, NavLinksService } from './nav_links_service'; diff --git a/src/core/public/chrome/nav_links/nav_links_service.ts b/src/core/public/chrome/nav_links/nav_links_service.ts index 93c138eac62c..373d73901606 100644 --- a/src/core/public/chrome/nav_links/nav_links_service.ts +++ b/src/core/public/chrome/nav_links/nav_links_service.ts @@ -53,6 +53,13 @@ export interface ChromeNavLinks { */ getNavLinks$(): Observable>>; + /** + * Get an observable for the current link updaters. Link updater is used to modify the + * nav links, for example, filter the nav links or update a specific nav link's properties. + * {@link LinksUpdater} + */ + getLinkUpdaters$(): BehaviorSubject; + /** * Get the state of a navlink at this point in time. * @param id @@ -112,7 +119,7 @@ export interface ChromeNavLinks { getForceAppSwitcherNavigation$(): Observable; } -type LinksUpdater = (navLinks: Map) => Map; +export type LinksUpdater = (navLinks: Map) => Map; export class NavLinksService { private readonly stop$ = new ReplaySubject(1); @@ -151,6 +158,10 @@ export class NavLinksService { return navLinks$.pipe(map(sortNavLinks), takeUntil(this.stop$)); }, + getLinkUpdaters$: () => { + return linkUpdaters$; + }, + get(id: string) { const link = navLinks$.value.get(id); return link && link.properties; diff --git a/src/core/public/index.ts b/src/core/public/index.ts index c82457ef2184..a8707bd2f763 100644 --- a/src/core/public/index.ts +++ b/src/core/public/index.ts @@ -67,6 +67,7 @@ import { ChromeStart, ChromeRecentlyAccessed, ChromeRecentlyAccessedHistoryItem, + LinksUpdater, NavType, } from './chrome'; import { FatalErrorsSetup, FatalErrorsStart, FatalErrorInfo } from './fatal_errors'; @@ -329,6 +330,7 @@ export { ChromeRecentlyAccessed, ChromeRecentlyAccessedHistoryItem, ChromeStart, + LinksUpdater, IContextContainer, HandlerFunction, HandlerContextType, diff --git a/src/plugins/workspace/public/plugin.ts b/src/plugins/workspace/public/plugin.ts index eeaab74e8e44..b849574a14b5 100644 --- a/src/plugins/workspace/public/plugin.ts +++ b/src/plugins/workspace/public/plugin.ts @@ -12,6 +12,8 @@ import { CoreSetup, AppMountParameters, AppNavLinkStatus, + LinksUpdater, + WorkspaceAttribute, } from '../../../core/public'; import { WORKSPACE_FATAL_ERROR_APP_ID, @@ -22,6 +24,8 @@ import { getWorkspaceIdFromUrl } from '../../../core/public/utils'; import { Services } from './types'; import { WorkspaceClient } from './workspace_client'; import { WorkspaceMenu } from './components/workspace_menu/workspace_menu'; +import { NavLinkWrapper } from '../../../core/public/chrome/nav_links/nav_link'; +import { featureMatchesConfig } from './utils'; type WorkspaceAppType = (params: AppMountParameters, services: Services) => () => void; @@ -38,6 +42,53 @@ export class WorkspacePlugin implements Plugin<{}, {}, {}> { } } + /** + * Filter the nav links based on the feature configuration of workspace + */ + private filterByWorkspace(allNavLinks: NavLinkWrapper[], workspace: WorkspaceAttribute | null) { + if (!workspace || !workspace.features) return allNavLinks; + + const featureFilter = featureMatchesConfig(workspace.features); + return allNavLinks.filter((linkWrapper) => featureFilter(linkWrapper.properties)); + } + + /** + * Filter nav links by the current workspace, once the current workspace change, the nav links(left nav bar) + * should also be updated according to the configured features of the current workspace + */ + private filterNavLinks(core: CoreStart) { + const currentWorkspace$ = core.workspaces.currentWorkspace$; + let filterLinksByWorkspace: LinksUpdater; + + this.currentWorkspaceSubscription?.unsubscribe(); + this.currentWorkspaceSubscription = currentWorkspace$.subscribe((currentWorkspace) => { + const linkUpdaters$ = core.chrome.navLinks.getLinkUpdaters$(); + let linkUpdaters = linkUpdaters$.value; + + /** + * It should only have one link filter exist based on the current workspace at a given time + * So we need to filter out previous workspace link filter before adding new one after changing workspace + */ + linkUpdaters = linkUpdaters.filter((updater) => updater !== filterLinksByWorkspace); + + /** + * Whenever workspace changed, this function will filter out those links that should not + * be displayed. For example, some workspace may not have Observability features configured, in such case, + * the nav links of Observability features should not be displayed in left nav bar + */ + filterLinksByWorkspace = (navLinks) => { + const filteredNavLinks = this.filterByWorkspace([...navLinks.values()], currentWorkspace); + const newNavLinks = new Map(); + filteredNavLinks.forEach((chromeNavLink) => { + newNavLinks.set(chromeNavLink.id, chromeNavLink); + }); + return newNavLinks; + }; + + linkUpdaters$.next([...linkUpdaters, filterLinksByWorkspace]); + }); + } + public async setup(core: CoreSetup) { const workspaceClient = new WorkspaceClient(core.http, core.workspaces); await workspaceClient.init(); @@ -135,6 +186,10 @@ export class WorkspacePlugin implements Plugin<{}, {}, {}> { this.coreStart = core; this.currentWorkspaceSubscription = this._changeSavedObjectCurrentWorkspace(); + + // When starts, filter the nav links based on the current workspace + this.filterNavLinks(core); + return {}; } From e9170289404e58908f9509b2092520d54489abae Mon Sep 17 00:00:00 2001 From: Yulong Ruan Date: Thu, 21 Mar 2024 11:50:34 +0800 Subject: [PATCH 02/13] fix type error Signed-off-by: Yulong Ruan --- src/core/public/chrome/chrome_service.mock.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/public/chrome/chrome_service.mock.ts b/src/core/public/chrome/chrome_service.mock.ts index b6ce429528a7..00d3e2b4480a 100644 --- a/src/core/public/chrome/chrome_service.mock.ts +++ b/src/core/public/chrome/chrome_service.mock.ts @@ -51,6 +51,7 @@ const createStartContractMock = () => { update: jest.fn(), enableForcedAppSwitcherNavigation: jest.fn(), getForceAppSwitcherNavigation$: jest.fn(), + getLinkUpdaters$: jest.fn(), }, recentlyAccessed: { add: jest.fn(), From 42da2b27b71dd38789758bfdb969060b439712a5 Mon Sep 17 00:00:00 2001 From: Yulong Ruan Date: Thu, 21 Mar 2024 11:51:59 +0800 Subject: [PATCH 03/13] update changelog Signed-off-by: Yulong Ruan --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b00f9c3ae93a..78a28330f0b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -66,6 +66,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - [Multiple Datasource] Add TLS configuration for multiple data sources ([#6171](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6171)) - [Multiple Datasource] Refactor data source menu and interface to allow cleaner selection of component and related configurations ([#6256](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6256)) - [Workspace] Add create workspace page ([#6179](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6179)) +- [Workspace] Filter left nav menu items according to the current workspace ([#6234](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6234)) ### 🐛 Bug Fixes From 1a4d743775f0979d9a6a21ef2f771bd1e6a82556 Mon Sep 17 00:00:00 2001 From: Yulong Ruan Date: Thu, 21 Mar 2024 13:14:16 +0800 Subject: [PATCH 04/13] fix linter Signed-off-by: Yulong Ruan --- src/plugins/workspace/public/plugin.ts | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/plugins/workspace/public/plugin.ts b/src/plugins/workspace/public/plugin.ts index b849574a14b5..4266d096f8a5 100644 --- a/src/plugins/workspace/public/plugin.ts +++ b/src/plugins/workspace/public/plugin.ts @@ -13,7 +13,6 @@ import { AppMountParameters, AppNavLinkStatus, LinksUpdater, - WorkspaceAttribute, } from '../../../core/public'; import { WORKSPACE_FATAL_ERROR_APP_ID, @@ -24,7 +23,6 @@ import { getWorkspaceIdFromUrl } from '../../../core/public/utils'; import { Services } from './types'; import { WorkspaceClient } from './workspace_client'; import { WorkspaceMenu } from './components/workspace_menu/workspace_menu'; -import { NavLinkWrapper } from '../../../core/public/chrome/nav_links/nav_link'; import { featureMatchesConfig } from './utils'; type WorkspaceAppType = (params: AppMountParameters, services: Services) => () => void; @@ -42,16 +40,6 @@ export class WorkspacePlugin implements Plugin<{}, {}, {}> { } } - /** - * Filter the nav links based on the feature configuration of workspace - */ - private filterByWorkspace(allNavLinks: NavLinkWrapper[], workspace: WorkspaceAttribute | null) { - if (!workspace || !workspace.features) return allNavLinks; - - const featureFilter = featureMatchesConfig(workspace.features); - return allNavLinks.filter((linkWrapper) => featureFilter(linkWrapper.properties)); - } - /** * Filter nav links by the current workspace, once the current workspace change, the nav links(left nav bar) * should also be updated according to the configured features of the current workspace @@ -77,8 +65,18 @@ export class WorkspacePlugin implements Plugin<{}, {}, {}> { * the nav links of Observability features should not be displayed in left nav bar */ filterLinksByWorkspace = (navLinks) => { - const filteredNavLinks = this.filterByWorkspace([...navLinks.values()], currentWorkspace); - const newNavLinks = new Map(); + /** + * Do not filter the nav links when currently not in a workspace, or the current workspace + * has no feature configured + */ + if (!currentWorkspace || !currentWorkspace.features) return navLinks; + + const featureFilter = featureMatchesConfig(currentWorkspace.features); + const filteredNavLinks = [...navLinks.values()].filter((linkWrapper) => + featureFilter(linkWrapper.properties) + ); + + const newNavLinks = new Map(); filteredNavLinks.forEach((chromeNavLink) => { newNavLinks.set(chromeNavLink.id, chromeNavLink); }); From 05f52729890c9b9c427fc51a1a9dc63dd165dc3f Mon Sep 17 00:00:00 2001 From: Yulong Ruan Date: Thu, 21 Mar 2024 13:32:47 +0800 Subject: [PATCH 05/13] fixed failed tests Signed-off-by: Yulong Ruan --- .../__snapshots__/dashboard_listing.test.tsx.snap | 5 +++++ .../__snapshots__/dashboard_top_nav.test.tsx.snap | 6 ++++++ src/plugins/workspace/public/plugin.ts | 4 +++- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/plugins/dashboard/public/application/components/dashboard_listing/__snapshots__/dashboard_listing.test.tsx.snap b/src/plugins/dashboard/public/application/components/dashboard_listing/__snapshots__/dashboard_listing.test.tsx.snap index 5e6645f56d9c..f1635f41c318 100644 --- a/src/plugins/dashboard/public/application/components/dashboard_listing/__snapshots__/dashboard_listing.test.tsx.snap +++ b/src/plugins/dashboard/public/application/components/dashboard_listing/__snapshots__/dashboard_listing.test.tsx.snap @@ -224,6 +224,7 @@ exports[`dashboard listing hideWriteControls 1`] = ` "get": [MockFunction], "getAll": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], + "getLinkUpdaters$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], "showOnly": [MockFunction], @@ -1366,6 +1367,7 @@ exports[`dashboard listing render table listing with initial filters from URL 1` "get": [MockFunction], "getAll": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], + "getLinkUpdaters$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], "showOnly": [MockFunction], @@ -2569,6 +2571,7 @@ exports[`dashboard listing renders call to action when no dashboards exist 1`] = "get": [MockFunction], "getAll": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], + "getLinkUpdaters$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], "showOnly": [MockFunction], @@ -3772,6 +3775,7 @@ exports[`dashboard listing renders table rows 1`] = ` "get": [MockFunction], "getAll": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], + "getLinkUpdaters$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], "showOnly": [MockFunction], @@ -4975,6 +4979,7 @@ exports[`dashboard listing renders warning when listingLimit is exceeded 1`] = ` "get": [MockFunction], "getAll": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], + "getLinkUpdaters$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], "showOnly": [MockFunction], diff --git a/src/plugins/dashboard/public/application/components/dashboard_top_nav/__snapshots__/dashboard_top_nav.test.tsx.snap b/src/plugins/dashboard/public/application/components/dashboard_top_nav/__snapshots__/dashboard_top_nav.test.tsx.snap index 262deaaaeaf0..314d9e449c40 100644 --- a/src/plugins/dashboard/public/application/components/dashboard_top_nav/__snapshots__/dashboard_top_nav.test.tsx.snap +++ b/src/plugins/dashboard/public/application/components/dashboard_top_nav/__snapshots__/dashboard_top_nav.test.tsx.snap @@ -212,6 +212,7 @@ exports[`Dashboard top nav render in embed mode 1`] = ` "get": [MockFunction], "getAll": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], + "getLinkUpdaters$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], "showOnly": [MockFunction], @@ -1179,6 +1180,7 @@ exports[`Dashboard top nav render in embed mode, and force hide filter bar 1`] = "get": [MockFunction], "getAll": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], + "getLinkUpdaters$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], "showOnly": [MockFunction], @@ -2146,6 +2148,7 @@ exports[`Dashboard top nav render in embed mode, components can be forced show b "get": [MockFunction], "getAll": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], + "getLinkUpdaters$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], "showOnly": [MockFunction], @@ -3113,6 +3116,7 @@ exports[`Dashboard top nav render in full screen mode with appended URL param bu "get": [MockFunction], "getAll": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], + "getLinkUpdaters$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], "showOnly": [MockFunction], @@ -4080,6 +4084,7 @@ exports[`Dashboard top nav render in full screen mode, no componenets should be "get": [MockFunction], "getAll": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], + "getLinkUpdaters$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], "showOnly": [MockFunction], @@ -5047,6 +5052,7 @@ exports[`Dashboard top nav render with all components 1`] = ` "get": [MockFunction], "getAll": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], + "getLinkUpdaters$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], "showOnly": [MockFunction], diff --git a/src/plugins/workspace/public/plugin.ts b/src/plugins/workspace/public/plugin.ts index 4266d096f8a5..e4d80cc7d6d4 100644 --- a/src/plugins/workspace/public/plugin.ts +++ b/src/plugins/workspace/public/plugin.ts @@ -30,6 +30,7 @@ type WorkspaceAppType = (params: AppMountParameters, services: Services) => () = export class WorkspacePlugin implements Plugin<{}, {}, {}> { private coreStart?: CoreStart; private currentWorkspaceSubscription?: Subscription; + private currentWorkspaceIdSubscription?: Subscription; private _changeSavedObjectCurrentWorkspace() { if (this.coreStart) { return this.coreStart.workspaces.currentWorkspaceId$.subscribe((currentWorkspaceId) => { @@ -183,7 +184,7 @@ export class WorkspacePlugin implements Plugin<{}, {}, {}> { public start(core: CoreStart) { this.coreStart = core; - this.currentWorkspaceSubscription = this._changeSavedObjectCurrentWorkspace(); + this.currentWorkspaceIdSubscription = this._changeSavedObjectCurrentWorkspace(); // When starts, filter the nav links based on the current workspace this.filterNavLinks(core); @@ -193,5 +194,6 @@ export class WorkspacePlugin implements Plugin<{}, {}, {}> { public stop() { this.currentWorkspaceSubscription?.unsubscribe(); + this.currentWorkspaceIdSubscription?.unsubscribe(); } } From 8b20984c54b2c8f62d58767adda04a744f536c50 Mon Sep 17 00:00:00 2001 From: Yulong Ruan Date: Thu, 21 Mar 2024 15:10:16 +0800 Subject: [PATCH 06/13] fix tests Signed-off-by: Yulong Ruan --- src/core/public/chrome/chrome_service.mock.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/core/public/chrome/chrome_service.mock.ts b/src/core/public/chrome/chrome_service.mock.ts index 00d3e2b4480a..aaccf766f40f 100644 --- a/src/core/public/chrome/chrome_service.mock.ts +++ b/src/core/public/chrome/chrome_service.mock.ts @@ -30,7 +30,13 @@ import { BehaviorSubject } from 'rxjs'; import type { PublicMethodsOf } from '@osd/utility-types'; -import { ChromeBadge, ChromeBreadcrumb, ChromeService, InternalChromeStart } from './'; +import { + ChromeBadge, + ChromeBreadcrumb, + ChromeService, + InternalChromeStart, + LinksUpdater, +} from './'; import { getLogosMock } from '../../common/mocks'; const createSetupContractMock = () => { @@ -40,6 +46,7 @@ const createSetupContractMock = () => { }; const createStartContractMock = () => { + const linkUpdatersMock$ = new BehaviorSubject([]); const startContract: DeeplyMockedKeys = { getHeaderComponent: jest.fn(), navLinks: { @@ -51,7 +58,7 @@ const createStartContractMock = () => { update: jest.fn(), enableForcedAppSwitcherNavigation: jest.fn(), getForceAppSwitcherNavigation$: jest.fn(), - getLinkUpdaters$: jest.fn(), + getLinkUpdaters$: jest.fn().mockReturnValue(linkUpdatersMock$), }, recentlyAccessed: { add: jest.fn(), From 8367050133f64b04a21a773bbb8ec0d4c9f64a79 Mon Sep 17 00:00:00 2001 From: Yulong Ruan Date: Fri, 22 Mar 2024 17:05:15 +0800 Subject: [PATCH 07/13] Added a comment to featureMatchesConfig for loop Signed-off-by: Yulong Ruan --- src/plugins/workspace/public/utils.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/plugins/workspace/public/utils.ts b/src/plugins/workspace/public/utils.ts index 444b3aadadf3..510b760b3c57 100644 --- a/src/plugins/workspace/public/utils.ts +++ b/src/plugins/workspace/public/utils.ts @@ -25,6 +25,11 @@ export const featureMatchesConfig = (featureConfigs: string[]) => ({ }) => { let matched = false; + /** + * Iterate through each feature configuration to determine if the given feature matches any of them. + * Note: The loop will not break prematurely because the order of featureConfigs array matters. + * Later configurations may override previous ones, so each configuration must be evaluated in sequence. + */ for (const featureConfig of featureConfigs) { // '*' matches any feature if (featureConfig === '*') { From 54a6fbf4249fbfcb69b6cd431f775ad004237b3a Mon Sep 17 00:00:00 2001 From: Yulong Ruan Date: Fri, 22 Mar 2024 17:09:54 +0800 Subject: [PATCH 08/13] update chrome module README Signed-off-by: Yulong Ruan --- src/core/public/chrome/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/public/chrome/README.md b/src/core/public/chrome/README.md index 6ec765a3bb0b..7eeba28ff7f5 100644 --- a/src/core/public/chrome/README.md +++ b/src/core/public/chrome/README.md @@ -50,6 +50,10 @@ Get an observable for a sorted list of navlinks :- `getNavLinks$(): Observable>>` +Get an observable for the current link updaters. Link updater is used to modify the nav links, for example, filter the nav links or update a specific nav link's properties. + +`getLinkUpdaters$(): BehaviorSubject` + Get the state of a navlink at this point in time :- `get(id: string): ChromeNavLink | undefined` From aea2ab27ebb30255e3ff3b3a0bc5a08cc219f98e Mon Sep 17 00:00:00 2001 From: Yulong Ruan Date: Fri, 5 Apr 2024 22:42:30 +0800 Subject: [PATCH 09/13] use application app updater to filter nav links when in a workspace An "Application Not Found" page will be displayed if accessing app which is not configured by the workspace Signed-off-by: Yulong Ruan --- src/core/public/chrome/README.md | 4 -- src/core/public/chrome/chrome_service.mock.ts | 10 +--- src/core/public/chrome/index.ts | 7 +-- src/core/public/chrome/nav_links/index.ts | 2 +- .../chrome/nav_links/nav_links_service.ts | 13 +--- src/core/public/index.ts | 2 - .../dashboard_listing.test.tsx.snap | 5 -- .../dashboard_top_nav.test.tsx.snap | 6 -- src/plugins/workspace/public/plugin.ts | 54 +++++------------ src/plugins/workspace/public/utils.test.ts | 60 ++++++++++++++++++- src/plugins/workspace/public/utils.ts | 41 ++++++++++++- 11 files changed, 118 insertions(+), 86 deletions(-) diff --git a/src/core/public/chrome/README.md b/src/core/public/chrome/README.md index 7eeba28ff7f5..6ec765a3bb0b 100644 --- a/src/core/public/chrome/README.md +++ b/src/core/public/chrome/README.md @@ -50,10 +50,6 @@ Get an observable for a sorted list of navlinks :- `getNavLinks$(): Observable>>` -Get an observable for the current link updaters. Link updater is used to modify the nav links, for example, filter the nav links or update a specific nav link's properties. - -`getLinkUpdaters$(): BehaviorSubject` - Get the state of a navlink at this point in time :- `get(id: string): ChromeNavLink | undefined` diff --git a/src/core/public/chrome/chrome_service.mock.ts b/src/core/public/chrome/chrome_service.mock.ts index aaccf766f40f..b6ce429528a7 100644 --- a/src/core/public/chrome/chrome_service.mock.ts +++ b/src/core/public/chrome/chrome_service.mock.ts @@ -30,13 +30,7 @@ import { BehaviorSubject } from 'rxjs'; import type { PublicMethodsOf } from '@osd/utility-types'; -import { - ChromeBadge, - ChromeBreadcrumb, - ChromeService, - InternalChromeStart, - LinksUpdater, -} from './'; +import { ChromeBadge, ChromeBreadcrumb, ChromeService, InternalChromeStart } from './'; import { getLogosMock } from '../../common/mocks'; const createSetupContractMock = () => { @@ -46,7 +40,6 @@ const createSetupContractMock = () => { }; const createStartContractMock = () => { - const linkUpdatersMock$ = new BehaviorSubject([]); const startContract: DeeplyMockedKeys = { getHeaderComponent: jest.fn(), navLinks: { @@ -58,7 +51,6 @@ const createStartContractMock = () => { update: jest.fn(), enableForcedAppSwitcherNavigation: jest.fn(), getForceAppSwitcherNavigation$: jest.fn(), - getLinkUpdaters$: jest.fn().mockReturnValue(linkUpdatersMock$), }, recentlyAccessed: { add: jest.fn(), diff --git a/src/core/public/chrome/index.ts b/src/core/public/chrome/index.ts index 6790e1678f9c..4004c2c323f9 100644 --- a/src/core/public/chrome/index.ts +++ b/src/core/public/chrome/index.ts @@ -45,12 +45,7 @@ export { ChromeHelpExtensionMenuGitHubLink, } from './ui/header/header_help_menu'; export { NavType } from './ui'; -export { - ChromeNavLink, - ChromeNavLinks, - ChromeNavLinkUpdateableFields, - LinksUpdater, -} from './nav_links'; +export { ChromeNavLink, ChromeNavLinks, ChromeNavLinkUpdateableFields } from './nav_links'; export { ChromeRecentlyAccessed, ChromeRecentlyAccessedHistoryItem } from './recently_accessed'; export { ChromeNavControl, ChromeNavControls } from './nav_controls'; export { ChromeDocTitle } from './doc_title'; diff --git a/src/core/public/chrome/nav_links/index.ts b/src/core/public/chrome/nav_links/index.ts index a0c9e847239e..4be7e0be49b8 100644 --- a/src/core/public/chrome/nav_links/index.ts +++ b/src/core/public/chrome/nav_links/index.ts @@ -29,4 +29,4 @@ */ export { ChromeNavLink, ChromeNavLinkUpdateableFields } from './nav_link'; -export { ChromeNavLinks, LinksUpdater, NavLinksService } from './nav_links_service'; +export { ChromeNavLinks, NavLinksService } from './nav_links_service'; diff --git a/src/core/public/chrome/nav_links/nav_links_service.ts b/src/core/public/chrome/nav_links/nav_links_service.ts index 373d73901606..93c138eac62c 100644 --- a/src/core/public/chrome/nav_links/nav_links_service.ts +++ b/src/core/public/chrome/nav_links/nav_links_service.ts @@ -53,13 +53,6 @@ export interface ChromeNavLinks { */ getNavLinks$(): Observable>>; - /** - * Get an observable for the current link updaters. Link updater is used to modify the - * nav links, for example, filter the nav links or update a specific nav link's properties. - * {@link LinksUpdater} - */ - getLinkUpdaters$(): BehaviorSubject; - /** * Get the state of a navlink at this point in time. * @param id @@ -119,7 +112,7 @@ export interface ChromeNavLinks { getForceAppSwitcherNavigation$(): Observable; } -export type LinksUpdater = (navLinks: Map) => Map; +type LinksUpdater = (navLinks: Map) => Map; export class NavLinksService { private readonly stop$ = new ReplaySubject(1); @@ -158,10 +151,6 @@ export class NavLinksService { return navLinks$.pipe(map(sortNavLinks), takeUntil(this.stop$)); }, - getLinkUpdaters$: () => { - return linkUpdaters$; - }, - get(id: string) { const link = navLinks$.value.get(id); return link && link.properties; diff --git a/src/core/public/index.ts b/src/core/public/index.ts index 4620aa2cf358..cc51c7215964 100644 --- a/src/core/public/index.ts +++ b/src/core/public/index.ts @@ -67,7 +67,6 @@ import { ChromeStart, ChromeRecentlyAccessed, ChromeRecentlyAccessedHistoryItem, - LinksUpdater, NavType, } from './chrome'; import { FatalErrorsSetup, FatalErrorsStart, FatalErrorInfo } from './fatal_errors'; @@ -330,7 +329,6 @@ export { ChromeRecentlyAccessed, ChromeRecentlyAccessedHistoryItem, ChromeStart, - LinksUpdater, IContextContainer, HandlerFunction, HandlerContextType, diff --git a/src/plugins/dashboard/public/application/components/dashboard_listing/__snapshots__/dashboard_listing.test.tsx.snap b/src/plugins/dashboard/public/application/components/dashboard_listing/__snapshots__/dashboard_listing.test.tsx.snap index f1635f41c318..5e6645f56d9c 100644 --- a/src/plugins/dashboard/public/application/components/dashboard_listing/__snapshots__/dashboard_listing.test.tsx.snap +++ b/src/plugins/dashboard/public/application/components/dashboard_listing/__snapshots__/dashboard_listing.test.tsx.snap @@ -224,7 +224,6 @@ exports[`dashboard listing hideWriteControls 1`] = ` "get": [MockFunction], "getAll": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], - "getLinkUpdaters$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], "showOnly": [MockFunction], @@ -1367,7 +1366,6 @@ exports[`dashboard listing render table listing with initial filters from URL 1` "get": [MockFunction], "getAll": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], - "getLinkUpdaters$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], "showOnly": [MockFunction], @@ -2571,7 +2569,6 @@ exports[`dashboard listing renders call to action when no dashboards exist 1`] = "get": [MockFunction], "getAll": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], - "getLinkUpdaters$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], "showOnly": [MockFunction], @@ -3775,7 +3772,6 @@ exports[`dashboard listing renders table rows 1`] = ` "get": [MockFunction], "getAll": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], - "getLinkUpdaters$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], "showOnly": [MockFunction], @@ -4979,7 +4975,6 @@ exports[`dashboard listing renders warning when listingLimit is exceeded 1`] = ` "get": [MockFunction], "getAll": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], - "getLinkUpdaters$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], "showOnly": [MockFunction], diff --git a/src/plugins/dashboard/public/application/components/dashboard_top_nav/__snapshots__/dashboard_top_nav.test.tsx.snap b/src/plugins/dashboard/public/application/components/dashboard_top_nav/__snapshots__/dashboard_top_nav.test.tsx.snap index 314c932556e2..e7e215dfb6bb 100644 --- a/src/plugins/dashboard/public/application/components/dashboard_top_nav/__snapshots__/dashboard_top_nav.test.tsx.snap +++ b/src/plugins/dashboard/public/application/components/dashboard_top_nav/__snapshots__/dashboard_top_nav.test.tsx.snap @@ -212,7 +212,6 @@ exports[`Dashboard top nav render in embed mode 1`] = ` "get": [MockFunction], "getAll": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], - "getLinkUpdaters$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], "showOnly": [MockFunction], @@ -1181,7 +1180,6 @@ exports[`Dashboard top nav render in embed mode, and force hide filter bar 1`] = "get": [MockFunction], "getAll": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], - "getLinkUpdaters$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], "showOnly": [MockFunction], @@ -2150,7 +2148,6 @@ exports[`Dashboard top nav render in embed mode, components can be forced show b "get": [MockFunction], "getAll": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], - "getLinkUpdaters$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], "showOnly": [MockFunction], @@ -3119,7 +3116,6 @@ exports[`Dashboard top nav render in full screen mode with appended URL param bu "get": [MockFunction], "getAll": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], - "getLinkUpdaters$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], "showOnly": [MockFunction], @@ -4088,7 +4084,6 @@ exports[`Dashboard top nav render in full screen mode, no componenets should be "get": [MockFunction], "getAll": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], - "getLinkUpdaters$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], "showOnly": [MockFunction], @@ -5057,7 +5052,6 @@ exports[`Dashboard top nav render with all components 1`] = ` "get": [MockFunction], "getAll": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], - "getLinkUpdaters$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], "showOnly": [MockFunction], diff --git a/src/plugins/workspace/public/plugin.ts b/src/plugins/workspace/public/plugin.ts index 3dd6562bcca7..e687d8cb42c4 100644 --- a/src/plugins/workspace/public/plugin.ts +++ b/src/plugins/workspace/public/plugin.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { Subscription } from 'rxjs'; +import { BehaviorSubject, type Subscription } from 'rxjs'; import React from 'react'; import { i18n } from '@osd/i18n'; import { @@ -12,7 +12,8 @@ import { CoreSetup, AppMountParameters, AppNavLinkStatus, - LinksUpdater, + AppUpdater, + AppStatus, } from '../../../core/public'; import { WORKSPACE_FATAL_ERROR_APP_ID, @@ -26,7 +27,7 @@ import { WorkspaceClient } from './workspace_client'; import { SavedObjectsManagementPluginSetup } from '../../../plugins/saved_objects_management/public'; import { WorkspaceMenu } from './components/workspace_menu/workspace_menu'; import { getWorkspaceColumn } from './components/workspace_column'; -import { featureMatchesConfig } from './utils'; +import { isAppAccessibleInWorkspace } from './utils'; type WorkspaceAppType = (params: AppMountParameters, services: Services) => () => void; @@ -38,6 +39,7 @@ export class WorkspacePlugin implements Plugin<{}, {}, WorkspacePluginSetupDeps> private coreStart?: CoreStart; private currentWorkspaceSubscription?: Subscription; private currentWorkspaceIdSubscription?: Subscription; + private appUpdater$ = new BehaviorSubject(() => ({})) private _changeSavedObjectCurrentWorkspace() { if (this.coreStart) { return this.coreStart.workspaces.currentWorkspaceId$.subscribe((currentWorkspaceId) => { @@ -54,50 +56,24 @@ export class WorkspacePlugin implements Plugin<{}, {}, WorkspacePluginSetupDeps> */ private filterNavLinks(core: CoreStart) { const currentWorkspace$ = core.workspaces.currentWorkspace$; - let filterLinksByWorkspace: LinksUpdater; - this.currentWorkspaceSubscription?.unsubscribe(); - this.currentWorkspaceSubscription = currentWorkspace$.subscribe((currentWorkspace) => { - const linkUpdaters$ = core.chrome.navLinks.getLinkUpdaters$(); - let linkUpdaters = linkUpdaters$.value; - - /** - * It should only have one link filter exist based on the current workspace at a given time - * So we need to filter out previous workspace link filter before adding new one after changing workspace - */ - linkUpdaters = linkUpdaters.filter((updater) => updater !== filterLinksByWorkspace); - - /** - * Whenever workspace changed, this function will filter out those links that should not - * be displayed. For example, some workspace may not have Observability features configured, in such case, - * the nav links of Observability features should not be displayed in left nav bar - */ - filterLinksByWorkspace = (navLinks) => { - /** - * Do not filter the nav links when currently not in a workspace, or the current workspace - * has no feature configured - */ - if (!currentWorkspace || !currentWorkspace.features) return navLinks; - - const featureFilter = featureMatchesConfig(currentWorkspace.features); - const filteredNavLinks = [...navLinks.values()].filter((linkWrapper) => - featureFilter(linkWrapper.properties) - ); - - const newNavLinks = new Map(); - filteredNavLinks.forEach((chromeNavLink) => { - newNavLinks.set(chromeNavLink.id, chromeNavLink); - }); - return newNavLinks; - }; - linkUpdaters$.next([...linkUpdaters, filterLinksByWorkspace]); + this.currentWorkspaceSubscription = currentWorkspace$.subscribe((currentWorkspace) => { + if (currentWorkspace) { + this.appUpdater$.next((app) => { + if (isAppAccessibleInWorkspace(app, currentWorkspace)) { + return + } + return {status: AppStatus.inaccessible} + }) + } }); } public async setup(core: CoreSetup, { savedObjectsManagement }: WorkspacePluginSetupDeps) { const workspaceClient = new WorkspaceClient(core.http, core.workspaces); await workspaceClient.init(); + core.application.registerAppUpdater(this.appUpdater$) /** * Retrieve workspace id from url diff --git a/src/plugins/workspace/public/utils.test.ts b/src/plugins/workspace/public/utils.test.ts index 510a775cd745..f81e248c4469 100644 --- a/src/plugins/workspace/public/utils.test.ts +++ b/src/plugins/workspace/public/utils.test.ts @@ -3,7 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { featureMatchesConfig } from './utils'; +import { AppNavLinkStatus } from '../../../core/public'; +import { featureMatchesConfig, isAppAccessibleInWorkspace } from './utils'; describe('workspace utils: featureMatchesConfig', () => { it('feature configured with `*` should match any features', () => { @@ -91,3 +92,60 @@ describe('workspace utils: featureMatchesConfig', () => { ); }); }); + +describe('workspace utils: isAppAccessibleInWorkspace', () => { + it('any app is accessible when workspace has no features configured', () => { + expect( + isAppAccessibleInWorkspace( + { id: 'any_app', title: 'Any app', mount: jest.fn() }, + { id: 'workspace_id', name: 'workspace name' } + ) + ).toBe(true); + }); + + it('An app is accessible when the workspace has the app configured', () => { + expect( + isAppAccessibleInWorkspace( + { id: 'dev_tools', title: 'Any app', mount: jest.fn() }, + { id: 'workspace_id', name: 'workspace name', features: ['dev_tools'] } + ) + ).toBe(true); + }); + + it('An app is not accessible when the workspace does not have the app configured', () => { + expect( + isAppAccessibleInWorkspace( + { id: 'dev_tools', title: 'Any app', mount: jest.fn() }, + { id: 'workspace_id', name: 'workspace name', features: [] } + ) + ).toBe(false); + }); + + it('An app is accessible if the nav link is hidden', () => { + expect( + isAppAccessibleInWorkspace( + { + id: 'dev_tools', + title: 'Any app', + mount: jest.fn(), + navLinkStatus: AppNavLinkStatus.hidden, + }, + { id: 'workspace_id', name: 'workspace name', features: [] } + ) + ).toBe(true); + }); + + it('An app is accessible if it is chromeless', () => { + expect( + isAppAccessibleInWorkspace( + { + id: 'dev_tools', + title: 'Any app', + mount: jest.fn(), + chromeless: true, + }, + { id: 'workspace_id', name: 'workspace name', features: [] } + ) + ).toBe(true); + }); +}); diff --git a/src/plugins/workspace/public/utils.ts b/src/plugins/workspace/public/utils.ts index 510b760b3c57..51120cde4406 100644 --- a/src/plugins/workspace/public/utils.ts +++ b/src/plugins/workspace/public/utils.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { AppCategory } from '../../../core/public'; +import { App, AppCategory, AppNavLinkStatus, WorkspaceObject } from '../../../core/public'; /** * Checks if a given feature matches the provided feature configuration. @@ -60,3 +60,42 @@ export const featureMatchesConfig = (featureConfigs: string[]) => ({ return matched; }; + +/** + * Check if an app is accessible in a workspace based on the workspace configured features + */ +export function isAppAccessibleInWorkspace(app: App, workspace: WorkspaceObject) { + /** + * When workspace has no features configured, all apps are considered to be accessible + */ + if (!workspace.features) { + return true; + } + + /** + * The app is configured into a workspace, it is accessible after entering the workspace + */ + const featureMatch = featureMatchesConfig(workspace.features); + if (featureMatch({ id: app.id, category: app.category })) { + return true; + } + + /* + * An app with hidden nav link is not configurable by workspace, which means user won't be + * able to select/unselect it when configuring workspace features. Such apps are by default + * accessible when in a workspace. + */ + if (app.navLinkStatus === AppNavLinkStatus.hidden) { + return true; + } + + /** + * A chromeless app is not configurable by workspace, which means user won't be + * able to select/unselect it when configuring workspace features. Such apps are by default + * accessible when in a workspace. + */ + if (app.chromeless) { + return true; + } + return false; +} From ca541d8b65c7ac21b5a69ed8dab4341729fc82c2 Mon Sep 17 00:00:00 2001 From: Yulong Ruan Date: Sun, 7 Apr 2024 10:42:04 +0800 Subject: [PATCH 10/13] rename featureMatch -> featureMatcher Signed-off-by: Yulong Ruan --- src/plugins/workspace/public/plugin.ts | 4 ++++ src/plugins/workspace/public/utils.ts | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/plugins/workspace/public/plugin.ts b/src/plugins/workspace/public/plugin.ts index e687d8cb42c4..3b7b12cb33d7 100644 --- a/src/plugins/workspace/public/plugin.ts +++ b/src/plugins/workspace/public/plugin.ts @@ -64,6 +64,10 @@ export class WorkspacePlugin implements Plugin<{}, {}, WorkspacePluginSetupDeps> if (isAppAccessibleInWorkspace(app, currentWorkspace)) { return } + /** + * Change the app to `inaccessible` if it is not configured in the workspace + * If trying to access such app, an "Application Not Found" page will be displayed + */ return {status: AppStatus.inaccessible} }) } diff --git a/src/plugins/workspace/public/utils.ts b/src/plugins/workspace/public/utils.ts index 51120cde4406..e70a26028525 100644 --- a/src/plugins/workspace/public/utils.ts +++ b/src/plugins/workspace/public/utils.ts @@ -75,8 +75,8 @@ export function isAppAccessibleInWorkspace(app: App, workspace: WorkspaceObject) /** * The app is configured into a workspace, it is accessible after entering the workspace */ - const featureMatch = featureMatchesConfig(workspace.features); - if (featureMatch({ id: app.id, category: app.category })) { + const featureMatcher = featureMatchesConfig(workspace.features); + if (featureMatcher({ id: app.id, category: app.category })) { return true; } From 19609aee78dc3bde75001ea4ad4751680f181962 Mon Sep 17 00:00:00 2001 From: Yulong Ruan Date: Sun, 7 Apr 2024 13:37:22 +0800 Subject: [PATCH 11/13] update initial appUpdater$ value to return undefined Signed-off-by: Yulong Ruan --- src/plugins/workspace/public/plugin.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/plugins/workspace/public/plugin.ts b/src/plugins/workspace/public/plugin.ts index 3b7b12cb33d7..b5956ce0ac1c 100644 --- a/src/plugins/workspace/public/plugin.ts +++ b/src/plugins/workspace/public/plugin.ts @@ -39,7 +39,7 @@ export class WorkspacePlugin implements Plugin<{}, {}, WorkspacePluginSetupDeps> private coreStart?: CoreStart; private currentWorkspaceSubscription?: Subscription; private currentWorkspaceIdSubscription?: Subscription; - private appUpdater$ = new BehaviorSubject(() => ({})) + private appUpdater$ = new BehaviorSubject(() => undefined) private _changeSavedObjectCurrentWorkspace() { if (this.coreStart) { return this.coreStart.workspaces.currentWorkspaceId$.subscribe((currentWorkspaceId) => { @@ -61,6 +61,7 @@ export class WorkspacePlugin implements Plugin<{}, {}, WorkspacePluginSetupDeps> this.currentWorkspaceSubscription = currentWorkspace$.subscribe((currentWorkspace) => { if (currentWorkspace) { this.appUpdater$.next((app) => { + console.log(app) if (isAppAccessibleInWorkspace(app, currentWorkspace)) { return } @@ -186,6 +187,7 @@ export class WorkspacePlugin implements Plugin<{}, {}, WorkspacePluginSetupDeps> public start(core: CoreStart) { this.coreStart = core; + console.log(core.chrome.navLinks.getAll()) this.currentWorkspaceIdSubscription = this._changeSavedObjectCurrentWorkspace(); From fd2598de06ae522f47eb97e871f9126728441db8 Mon Sep 17 00:00:00 2001 From: Yulong Ruan Date: Sun, 7 Apr 2024 13:43:56 +0800 Subject: [PATCH 12/13] remove console log Signed-off-by: Yulong Ruan --- src/plugins/workspace/public/plugin.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/plugins/workspace/public/plugin.ts b/src/plugins/workspace/public/plugin.ts index b5956ce0ac1c..7c43ce7ebf8f 100644 --- a/src/plugins/workspace/public/plugin.ts +++ b/src/plugins/workspace/public/plugin.ts @@ -61,7 +61,6 @@ export class WorkspacePlugin implements Plugin<{}, {}, WorkspacePluginSetupDeps> this.currentWorkspaceSubscription = currentWorkspace$.subscribe((currentWorkspace) => { if (currentWorkspace) { this.appUpdater$.next((app) => { - console.log(app) if (isAppAccessibleInWorkspace(app, currentWorkspace)) { return } @@ -187,7 +186,6 @@ export class WorkspacePlugin implements Plugin<{}, {}, WorkspacePluginSetupDeps> public start(core: CoreStart) { this.coreStart = core; - console.log(core.chrome.navLinks.getAll()) this.currentWorkspaceIdSubscription = this._changeSavedObjectCurrentWorkspace(); From 7f7cefed58fa3f5e610f67054c573a8ffa32aa57 Mon Sep 17 00:00:00 2001 From: Yulong Ruan Date: Sun, 7 Apr 2024 15:34:39 +0800 Subject: [PATCH 13/13] fix linting Signed-off-by: Yulong Ruan --- src/plugins/workspace/public/plugin.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/plugins/workspace/public/plugin.ts b/src/plugins/workspace/public/plugin.ts index 3dfc148cef41..b27c4b3bdd4a 100644 --- a/src/plugins/workspace/public/plugin.ts +++ b/src/plugins/workspace/public/plugin.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { BehaviorSubject, type Subscription } from 'rxjs'; +import { BehaviorSubject, Subscription } from 'rxjs'; import React from 'react'; import { i18n } from '@osd/i18n'; import { @@ -40,7 +40,7 @@ export class WorkspacePlugin implements Plugin<{}, {}, WorkspacePluginSetupDeps> private coreStart?: CoreStart; private currentWorkspaceSubscription?: Subscription; private currentWorkspaceIdSubscription?: Subscription; - private appUpdater$ = new BehaviorSubject(() => undefined) + private appUpdater$ = new BehaviorSubject(() => undefined); private _changeSavedObjectCurrentWorkspace() { if (this.coreStart) { return this.coreStart.workspaces.currentWorkspaceId$.subscribe((currentWorkspaceId) => { @@ -63,14 +63,14 @@ export class WorkspacePlugin implements Plugin<{}, {}, WorkspacePluginSetupDeps> if (currentWorkspace) { this.appUpdater$.next((app) => { if (isAppAccessibleInWorkspace(app, currentWorkspace)) { - return + return; } /** * Change the app to `inaccessible` if it is not configured in the workspace * If trying to access such app, an "Application Not Found" page will be displayed */ - return {status: AppStatus.inaccessible} - }) + return { status: AppStatus.inaccessible }; + }); } }); } @@ -78,7 +78,7 @@ export class WorkspacePlugin implements Plugin<{}, {}, WorkspacePluginSetupDeps> public async setup(core: CoreSetup, { savedObjectsManagement }: WorkspacePluginSetupDeps) { const workspaceClient = new WorkspaceClient(core.http, core.workspaces); await workspaceClient.init(); - core.application.registerAppUpdater(this.appUpdater$) + core.application.registerAppUpdater(this.appUpdater$); /** * Retrieve workspace id from url