From e35290a8a88681d6f63860f83b4baff8daa6be84 Mon Sep 17 00:00:00 2001 From: Yulong Ruan Date: Tue, 19 Sep 2023 17:21:04 +0800 Subject: [PATCH 01/11] feat: add core workspace module (#145) The core workspace module(WorkspaceService) is a foundational component that enables the implementation of workspace features within OSD plugins. The purpose of the core workspace module is to provide a framework for workspace implementations. This module does not implement specific workspace functionality but provides the essential infrastructure for plugins to extend and customize workspace features, it maintains a shared workspace state(observables) across the entire application to ensure a consistent and up-to-date view of workspace-related information to all parts of the application. --------- Signed-off-by: Yulong Ruan --- src/core/public/application/types.ts | 3 + src/core/public/core_system.ts | 8 + src/core/public/index.ts | 13 + src/core/public/mocks.ts | 4 + src/core/public/plugins/plugin_context.ts | 2 + .../public/plugins/plugins_service.test.ts | 3 + src/core/public/workspace/index.ts | 10 + .../workspace/workspaces_service.mock.ts | 36 +++ .../public/workspace/workspaces_service.ts | 134 ++++++++ .../dashboard_listing.test.tsx.snap | 240 +++++++++++++++ .../dashboard_top_nav.test.tsx.snap | 288 ++++++++++++++++++ 11 files changed, 741 insertions(+) create mode 100644 src/core/public/workspace/index.ts create mode 100644 src/core/public/workspace/workspaces_service.mock.ts create mode 100644 src/core/public/workspace/workspaces_service.ts diff --git a/src/core/public/application/types.ts b/src/core/public/application/types.ts index 7398aad65009..4744ab34cfd3 100644 --- a/src/core/public/application/types.ts +++ b/src/core/public/application/types.ts @@ -47,6 +47,7 @@ import { IUiSettingsClient } from '../ui_settings'; import { SavedObjectsStart } from '../saved_objects'; import { AppCategory } from '../../types'; import { ScopedHistory } from './scoped_history'; +import { WorkspacesStart } from '../workspace'; /** * Accessibility status of an application. @@ -334,6 +335,8 @@ export interface AppMountContext { injectedMetadata: { getInjectedVar: (name: string, defaultValue?: any) => unknown; }; + /** {@link WorkspacesService} */ + workspaces: WorkspacesStart; }; } diff --git a/src/core/public/core_system.ts b/src/core/public/core_system.ts index 8fe5a36ebb55..d7de8b4595d5 100644 --- a/src/core/public/core_system.ts +++ b/src/core/public/core_system.ts @@ -54,6 +54,7 @@ import { ContextService } from './context'; import { IntegrationsService } from './integrations'; import { CoreApp } from './core_app'; import type { InternalApplicationSetup, InternalApplicationStart } from './application/types'; +import { WorkspacesService } from './workspace'; interface Params { rootDomElement: HTMLElement; @@ -110,6 +111,7 @@ export class CoreSystem { private readonly rootDomElement: HTMLElement; private readonly coreContext: CoreContext; + private readonly workspaces: WorkspacesService; private fatalErrorsSetup: FatalErrorsSetup | null = null; constructor(params: Params) { @@ -138,6 +140,7 @@ export class CoreSystem { this.rendering = new RenderingService(); this.application = new ApplicationService(); this.integrations = new IntegrationsService(); + this.workspaces = new WorkspacesService(); this.coreContext = { coreId: Symbol('core'), env: injectedMetadata.env }; @@ -160,6 +163,7 @@ export class CoreSystem { const http = this.http.setup({ injectedMetadata, fatalErrors: this.fatalErrorsSetup }); const uiSettings = this.uiSettings.setup({ http, injectedMetadata }); const notifications = this.notifications.setup({ uiSettings }); + const workspaces = this.workspaces.setup(); const pluginDependencies = this.plugins.getOpaqueIds(); const context = this.context.setup({ @@ -176,6 +180,7 @@ export class CoreSystem { injectedMetadata, notifications, uiSettings, + workspaces, }; // Services that do not expose contracts at setup @@ -220,6 +225,7 @@ export class CoreSystem { targetDomElement: notificationsTargetDomElement, }); const application = await this.application.start({ http, overlays }); + const workspaces = this.workspaces.start({ application, http }); const chrome = await this.chrome.start({ application, docLinks, @@ -242,6 +248,7 @@ export class CoreSystem { overlays, savedObjects, uiSettings, + workspaces, })); const core: InternalCoreStart = { @@ -256,6 +263,7 @@ export class CoreSystem { overlays, uiSettings, fatalErrors, + workspaces, }; await this.plugins.start(core); diff --git a/src/core/public/index.ts b/src/core/public/index.ts index 03ef6b6392f9..14ab91e1cb13 100644 --- a/src/core/public/index.ts +++ b/src/core/public/index.ts @@ -87,6 +87,7 @@ import { HandlerParameters, } from './context'; import { Branding } from '../types'; +import { WorkspacesStart, WorkspacesSetup } from './workspace'; export type { Logos } from '../common'; export { PackageInfo, EnvironmentMode } from '../server/types'; @@ -102,6 +103,7 @@ export { StringValidation, StringValidationRegex, StringValidationRegexString, + WorkspaceAttribute, } from '../types'; export { @@ -239,6 +241,8 @@ export interface CoreSetup; + /** {@link WorkspacesSetup} */ + workspaces: WorkspacesSetup; } /** @@ -293,6 +297,8 @@ export interface CoreStart { getInjectedVar: (name: string, defaultValue?: any) => unknown; getBranding: () => Branding; }; + /** {@link WorkspacesStart} */ + workspaces: WorkspacesStart; } export { @@ -341,3 +347,10 @@ export { }; export { __osdBootstrap__ } from './osd_bootstrap'; + +export { + WorkspacesStart, + WorkspacesSetup, + WorkspacesService, + WorkspaceObservables, +} from './workspace'; diff --git a/src/core/public/mocks.ts b/src/core/public/mocks.ts index e863d627c801..722070d5a9ea 100644 --- a/src/core/public/mocks.ts +++ b/src/core/public/mocks.ts @@ -47,6 +47,7 @@ import { uiSettingsServiceMock } from './ui_settings/ui_settings_service.mock'; import { savedObjectsServiceMock } from './saved_objects/saved_objects_service.mock'; import { contextServiceMock } from './context/context_service.mock'; import { injectedMetadataServiceMock } from './injected_metadata/injected_metadata_service.mock'; +import { workspacesServiceMock } from './workspace/workspaces_service.mock'; export { chromeServiceMock } from './chrome/chrome_service.mock'; export { docLinksServiceMock } from './doc_links/doc_links_service.mock'; @@ -60,6 +61,7 @@ export { uiSettingsServiceMock } from './ui_settings/ui_settings_service.mock'; export { savedObjectsServiceMock } from './saved_objects/saved_objects_service.mock'; export { scopedHistoryMock } from './application/scoped_history.mock'; export { applicationServiceMock } from './application/application_service.mock'; +export { workspacesServiceMock } from './workspace/workspaces_service.mock'; function createCoreSetupMock({ basePath = '', @@ -85,6 +87,7 @@ function createCoreSetupMock({ getInjectedVar: injectedMetadataServiceMock.createSetupContract().getInjectedVar, getBranding: injectedMetadataServiceMock.createSetupContract().getBranding, }, + workspaces: workspacesServiceMock.createSetupContractMock(), }; return mock; @@ -106,6 +109,7 @@ function createCoreStartMock({ basePath = '' } = {}) { getBranding: injectedMetadataServiceMock.createStartContract().getBranding, }, fatalErrors: fatalErrorsServiceMock.createStartContract(), + workspaces: workspacesServiceMock.createStartContract(), }; return mock; diff --git a/src/core/public/plugins/plugin_context.ts b/src/core/public/plugins/plugin_context.ts index 42c40e91183f..87738fc7e57a 100644 --- a/src/core/public/plugins/plugin_context.ts +++ b/src/core/public/plugins/plugin_context.ts @@ -121,6 +121,7 @@ export function createPluginSetupContext< getBranding: deps.injectedMetadata.getBranding, }, getStartServices: () => plugin.startDependencies, + workspaces: deps.workspaces, }; } @@ -168,5 +169,6 @@ export function createPluginStartContext< getBranding: deps.injectedMetadata.getBranding, }, fatalErrors: deps.fatalErrors, + workspaces: deps.workspaces, }; } diff --git a/src/core/public/plugins/plugins_service.test.ts b/src/core/public/plugins/plugins_service.test.ts index b2cf4e8880cf..7e8c96c1f9d0 100644 --- a/src/core/public/plugins/plugins_service.test.ts +++ b/src/core/public/plugins/plugins_service.test.ts @@ -58,6 +58,7 @@ import { CoreSetup, CoreStart, PluginInitializerContext } from '..'; import { docLinksServiceMock } from '../doc_links/doc_links_service.mock'; import { savedObjectsServiceMock } from '../saved_objects/saved_objects_service.mock'; import { contextServiceMock } from '../context/context_service.mock'; +import { workspacesServiceMock } from '../workspace/workspaces_service.mock'; export let mockPluginInitializers: Map; @@ -108,6 +109,7 @@ describe('PluginsService', () => { injectedMetadata: injectedMetadataServiceMock.createStartContract(), notifications: notificationServiceMock.createSetupContract(), uiSettings: uiSettingsServiceMock.createSetupContract(), + workspaces: workspacesServiceMock.createSetupContractMock(), }; mockSetupContext = { ...mockSetupDeps, @@ -127,6 +129,7 @@ describe('PluginsService', () => { uiSettings: uiSettingsServiceMock.createStartContract(), savedObjects: savedObjectsServiceMock.createStartContract(), fatalErrors: fatalErrorsServiceMock.createStartContract(), + workspaces: workspacesServiceMock.createStartContract(), }; mockStartContext = { ...mockStartDeps, diff --git a/src/core/public/workspace/index.ts b/src/core/public/workspace/index.ts new file mode 100644 index 000000000000..4ef6aaae7fd4 --- /dev/null +++ b/src/core/public/workspace/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ +export { + WorkspacesStart, + WorkspacesService, + WorkspacesSetup, + WorkspaceObservables, +} from './workspaces_service'; diff --git a/src/core/public/workspace/workspaces_service.mock.ts b/src/core/public/workspace/workspaces_service.mock.ts new file mode 100644 index 000000000000..3c35315aa850 --- /dev/null +++ b/src/core/public/workspace/workspaces_service.mock.ts @@ -0,0 +1,36 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { BehaviorSubject } from 'rxjs'; +import { WorkspaceAttribute } from '..'; + +const currentWorkspaceId$ = new BehaviorSubject(''); +const workspaceList$ = new BehaviorSubject([]); +const currentWorkspace$ = new BehaviorSubject(null); +const initialized$ = new BehaviorSubject(false); +const workspaceEnabled$ = new BehaviorSubject(false); + +const createWorkspacesSetupContractMock = () => ({ + currentWorkspaceId$, + workspaceList$, + currentWorkspace$, + initialized$, + workspaceEnabled$, + registerWorkspaceMenuRender: jest.fn(), +}); + +const createWorkspacesStartContractMock = () => ({ + currentWorkspaceId$, + workspaceList$, + currentWorkspace$, + initialized$, + workspaceEnabled$, + renderWorkspaceMenu: jest.fn(), +}); + +export const workspacesServiceMock = { + createSetupContractMock: createWorkspacesSetupContractMock, + createStartContract: createWorkspacesStartContractMock, +}; diff --git a/src/core/public/workspace/workspaces_service.ts b/src/core/public/workspace/workspaces_service.ts new file mode 100644 index 000000000000..39519ccdddbe --- /dev/null +++ b/src/core/public/workspace/workspaces_service.ts @@ -0,0 +1,134 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { BehaviorSubject, combineLatest } from 'rxjs'; +import { isEqual } from 'lodash'; + +import { CoreService, WorkspaceAttribute } from '../../types'; +import { InternalApplicationStart } from '../application'; +import { HttpSetup } from '../http'; + +type WorkspaceMenuRenderFn = ({ + basePath, + getUrlForApp, + observables, +}: { + getUrlForApp: InternalApplicationStart['getUrlForApp']; + basePath: HttpSetup['basePath']; + observables: WorkspaceObservables; +}) => JSX.Element | null; + +type WorkspaceObject = WorkspaceAttribute & { readonly?: boolean }; + +export interface WorkspaceObservables { + currentWorkspaceId$: BehaviorSubject; + currentWorkspace$: BehaviorSubject; + workspaceList$: BehaviorSubject; + workspaceEnabled$: BehaviorSubject; + initialized$: BehaviorSubject; +} + +enum WORKSPACE_ERROR { + WORKSPACE_STALED = 'WORKSPACE_STALED', +} + +/** + * @public + */ +export interface WorkspacesSetup extends WorkspaceObservables { + registerWorkspaceMenuRender: (render: WorkspaceMenuRenderFn) => void; +} + +export interface WorkspacesStart extends WorkspaceObservables { + renderWorkspaceMenu: () => JSX.Element | null; +} + +export class WorkspacesService implements CoreService { + private currentWorkspaceId$ = new BehaviorSubject(''); + private workspaceList$ = new BehaviorSubject([]); + private currentWorkspace$ = new BehaviorSubject(null); + private initialized$ = new BehaviorSubject(false); + private workspaceEnabled$ = new BehaviorSubject(false); + private _renderWorkspaceMenu: WorkspaceMenuRenderFn | null = null; + + constructor() { + combineLatest([this.initialized$, this.workspaceList$, this.currentWorkspaceId$]).subscribe( + ([workspaceInitialized, workspaceList, currentWorkspaceId]) => { + if (workspaceInitialized) { + const currentWorkspace = workspaceList.find((w) => w && w.id === currentWorkspaceId); + + /** + * Do a simple idempotent verification here + */ + if (!isEqual(currentWorkspace, this.currentWorkspace$.getValue())) { + this.currentWorkspace$.next(currentWorkspace ?? null); + } + + if (currentWorkspaceId && !currentWorkspace?.id) { + /** + * Current workspace is staled + */ + this.currentWorkspaceId$.error({ + reason: WORKSPACE_ERROR.WORKSPACE_STALED, + }); + this.currentWorkspace$.error({ + reason: WORKSPACE_ERROR.WORKSPACE_STALED, + }); + } + } + } + ); + } + + public setup(): WorkspacesSetup { + return { + currentWorkspaceId$: this.currentWorkspaceId$, + currentWorkspace$: this.currentWorkspace$, + workspaceList$: this.workspaceList$, + initialized$: this.initialized$, + workspaceEnabled$: this.workspaceEnabled$, + registerWorkspaceMenuRender: (render: WorkspaceMenuRenderFn) => + (this._renderWorkspaceMenu = render), + }; + } + + public start({ + http, + application, + }: { + application: InternalApplicationStart; + http: HttpSetup; + }): WorkspacesStart { + const observables = { + currentWorkspaceId$: this.currentWorkspaceId$, + currentWorkspace$: this.currentWorkspace$, + workspaceList$: this.workspaceList$, + initialized$: this.initialized$, + workspaceEnabled$: this.workspaceEnabled$, + }; + return { + ...observables, + renderWorkspaceMenu: () => { + if (this._renderWorkspaceMenu) { + return this._renderWorkspaceMenu({ + basePath: http.basePath, + getUrlForApp: application.getUrlForApp, + observables, + }); + } + return null; + }, + }; + } + + public async stop() { + this.currentWorkspace$.unsubscribe(); + this.currentWorkspaceId$.unsubscribe(); + this.workspaceList$.unsubscribe(); + this.workspaceEnabled$.unsubscribe(); + this.initialized$.unsubscribe(); + this._renderWorkspaceMenu = null; + } +} 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 1b3c486615c8..d3d0fe94fb68 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 @@ -993,6 +993,54 @@ exports[`dashboard listing hideWriteControls 1`] = ` "allowTrackUserAgent": [MockFunction], "reportUiStats": [MockFunction], }, + "workspaces": Object { + "currentWorkspace$": BehaviorSubject { + "_isScalar": false, + "_value": null, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "currentWorkspaceId$": BehaviorSubject { + "_isScalar": false, + "_value": "", + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "initialized$": BehaviorSubject { + "_isScalar": false, + "_value": false, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "renderWorkspaceMenu": [MockFunction], + "workspaceEnabled$": BehaviorSubject { + "_isScalar": false, + "_value": false, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "workspaceList$": BehaviorSubject { + "_isScalar": false, + "_value": Array [], + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + }, } } > @@ -2085,6 +2133,54 @@ exports[`dashboard listing render table listing with initial filters from URL 1` "allowTrackUserAgent": [MockFunction], "reportUiStats": [MockFunction], }, + "workspaces": Object { + "currentWorkspace$": BehaviorSubject { + "_isScalar": false, + "_value": null, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "currentWorkspaceId$": BehaviorSubject { + "_isScalar": false, + "_value": "", + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "initialized$": BehaviorSubject { + "_isScalar": false, + "_value": false, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "renderWorkspaceMenu": [MockFunction], + "workspaceEnabled$": BehaviorSubject { + "_isScalar": false, + "_value": false, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "workspaceList$": BehaviorSubject { + "_isScalar": false, + "_value": Array [], + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + }, } } > @@ -3238,6 +3334,54 @@ exports[`dashboard listing renders call to action when no dashboards exist 1`] = "allowTrackUserAgent": [MockFunction], "reportUiStats": [MockFunction], }, + "workspaces": Object { + "currentWorkspace$": BehaviorSubject { + "_isScalar": false, + "_value": null, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "currentWorkspaceId$": BehaviorSubject { + "_isScalar": false, + "_value": "", + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "initialized$": BehaviorSubject { + "_isScalar": false, + "_value": false, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "renderWorkspaceMenu": [MockFunction], + "workspaceEnabled$": BehaviorSubject { + "_isScalar": false, + "_value": false, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "workspaceList$": BehaviorSubject { + "_isScalar": false, + "_value": Array [], + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + }, } } > @@ -4391,6 +4535,54 @@ exports[`dashboard listing renders table rows 1`] = ` "allowTrackUserAgent": [MockFunction], "reportUiStats": [MockFunction], }, + "workspaces": Object { + "currentWorkspace$": BehaviorSubject { + "_isScalar": false, + "_value": null, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "currentWorkspaceId$": BehaviorSubject { + "_isScalar": false, + "_value": "", + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "initialized$": BehaviorSubject { + "_isScalar": false, + "_value": false, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "renderWorkspaceMenu": [MockFunction], + "workspaceEnabled$": BehaviorSubject { + "_isScalar": false, + "_value": false, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "workspaceList$": BehaviorSubject { + "_isScalar": false, + "_value": Array [], + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + }, } } > @@ -5544,6 +5736,54 @@ exports[`dashboard listing renders warning when listingLimit is exceeded 1`] = ` "allowTrackUserAgent": [MockFunction], "reportUiStats": [MockFunction], }, + "workspaces": Object { + "currentWorkspace$": BehaviorSubject { + "_isScalar": false, + "_value": null, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "currentWorkspaceId$": BehaviorSubject { + "_isScalar": false, + "_value": "", + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "initialized$": BehaviorSubject { + "_isScalar": false, + "_value": false, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "renderWorkspaceMenu": [MockFunction], + "workspaceEnabled$": BehaviorSubject { + "_isScalar": false, + "_value": false, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "workspaceList$": BehaviorSubject { + "_isScalar": false, + "_value": Array [], + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + }, } } > 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 101e0e520304..0c48ffdc474a 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 @@ -859,6 +859,54 @@ exports[`Dashboard top nav render in embed mode 1`] = ` "allowTrackUserAgent": [MockFunction], "reportUiStats": [MockFunction], }, + "workspaces": Object { + "currentWorkspace$": BehaviorSubject { + "_isScalar": false, + "_value": null, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "currentWorkspaceId$": BehaviorSubject { + "_isScalar": false, + "_value": "", + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "initialized$": BehaviorSubject { + "_isScalar": false, + "_value": false, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "renderWorkspaceMenu": [MockFunction], + "workspaceEnabled$": BehaviorSubject { + "_isScalar": false, + "_value": false, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "workspaceList$": BehaviorSubject { + "_isScalar": false, + "_value": Array [], + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + }, } } > @@ -1776,6 +1824,54 @@ exports[`Dashboard top nav render in embed mode, and force hide filter bar 1`] = "allowTrackUserAgent": [MockFunction], "reportUiStats": [MockFunction], }, + "workspaces": Object { + "currentWorkspace$": BehaviorSubject { + "_isScalar": false, + "_value": null, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "currentWorkspaceId$": BehaviorSubject { + "_isScalar": false, + "_value": "", + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "initialized$": BehaviorSubject { + "_isScalar": false, + "_value": false, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "renderWorkspaceMenu": [MockFunction], + "workspaceEnabled$": BehaviorSubject { + "_isScalar": false, + "_value": false, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "workspaceList$": BehaviorSubject { + "_isScalar": false, + "_value": Array [], + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + }, } } > @@ -2693,6 +2789,54 @@ exports[`Dashboard top nav render in embed mode, components can be forced show b "allowTrackUserAgent": [MockFunction], "reportUiStats": [MockFunction], }, + "workspaces": Object { + "currentWorkspace$": BehaviorSubject { + "_isScalar": false, + "_value": null, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "currentWorkspaceId$": BehaviorSubject { + "_isScalar": false, + "_value": "", + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "initialized$": BehaviorSubject { + "_isScalar": false, + "_value": false, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "renderWorkspaceMenu": [MockFunction], + "workspaceEnabled$": BehaviorSubject { + "_isScalar": false, + "_value": false, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "workspaceList$": BehaviorSubject { + "_isScalar": false, + "_value": Array [], + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + }, } } > @@ -3610,6 +3754,54 @@ exports[`Dashboard top nav render in full screen mode with appended URL param bu "allowTrackUserAgent": [MockFunction], "reportUiStats": [MockFunction], }, + "workspaces": Object { + "currentWorkspace$": BehaviorSubject { + "_isScalar": false, + "_value": null, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "currentWorkspaceId$": BehaviorSubject { + "_isScalar": false, + "_value": "", + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "initialized$": BehaviorSubject { + "_isScalar": false, + "_value": false, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "renderWorkspaceMenu": [MockFunction], + "workspaceEnabled$": BehaviorSubject { + "_isScalar": false, + "_value": false, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "workspaceList$": BehaviorSubject { + "_isScalar": false, + "_value": Array [], + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + }, } } > @@ -4527,6 +4719,54 @@ exports[`Dashboard top nav render in full screen mode, no componenets should be "allowTrackUserAgent": [MockFunction], "reportUiStats": [MockFunction], }, + "workspaces": Object { + "currentWorkspace$": BehaviorSubject { + "_isScalar": false, + "_value": null, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "currentWorkspaceId$": BehaviorSubject { + "_isScalar": false, + "_value": "", + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "initialized$": BehaviorSubject { + "_isScalar": false, + "_value": false, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "renderWorkspaceMenu": [MockFunction], + "workspaceEnabled$": BehaviorSubject { + "_isScalar": false, + "_value": false, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "workspaceList$": BehaviorSubject { + "_isScalar": false, + "_value": Array [], + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + }, } } > @@ -5444,6 +5684,54 @@ exports[`Dashboard top nav render with all components 1`] = ` "allowTrackUserAgent": [MockFunction], "reportUiStats": [MockFunction], }, + "workspaces": Object { + "currentWorkspace$": BehaviorSubject { + "_isScalar": false, + "_value": null, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "currentWorkspaceId$": BehaviorSubject { + "_isScalar": false, + "_value": "", + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "initialized$": BehaviorSubject { + "_isScalar": false, + "_value": false, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "renderWorkspaceMenu": [MockFunction], + "workspaceEnabled$": BehaviorSubject { + "_isScalar": false, + "_value": false, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + "workspaceList$": BehaviorSubject { + "_isScalar": false, + "_value": Array [], + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + }, + }, } } > From e7552b6f410ee194436a0c46e6924b313bcc6da3 Mon Sep 17 00:00:00 2001 From: Yulong Ruan Date: Fri, 22 Sep 2023 17:29:52 +0800 Subject: [PATCH 02/11] add unit tests for workspace core service (#191) Signed-off-by: Yulong Ruan --- src/core/public/core_system.test.mocks.ts | 9 ++ src/core/public/core_system.test.ts | 25 +++++ src/core/public/core_system.ts | 1 + src/core/public/mocks.ts | 2 +- .../public/plugins/plugins_service.test.ts | 2 +- .../workspace/workspaces_service.mock.ts | 13 ++- .../workspace/workspaces_service.test.ts | 94 +++++++++++++++++++ .../public/workspace/workspaces_service.ts | 6 +- 8 files changed, 146 insertions(+), 6 deletions(-) create mode 100644 src/core/public/workspace/workspaces_service.test.ts diff --git a/src/core/public/core_system.test.mocks.ts b/src/core/public/core_system.test.mocks.ts index da09de341bc4..4025361e150d 100644 --- a/src/core/public/core_system.test.mocks.ts +++ b/src/core/public/core_system.test.mocks.ts @@ -42,6 +42,7 @@ import { docLinksServiceMock } from './doc_links/doc_links_service.mock'; import { renderingServiceMock } from './rendering/rendering_service.mock'; import { contextServiceMock } from './context/context_service.mock'; import { integrationsServiceMock } from './integrations/integrations_service.mock'; +import { workspacesServiceMock } from './workspace/workspaces_service.mock'; import { coreAppMock } from './core_app/core_app.mock'; export const MockInjectedMetadataService = injectedMetadataServiceMock.create(); @@ -145,3 +146,11 @@ export const CoreAppConstructor = jest.fn().mockImplementation(() => MockCoreApp jest.doMock('./core_app', () => ({ CoreApp: CoreAppConstructor, })); + +export const MockWorkspacesService = workspacesServiceMock.create(); +export const WorkspacesServiceConstructor = jest + .fn() + .mockImplementation(() => MockWorkspacesService); +jest.doMock('./workspace', () => ({ + WorkspacesService: WorkspacesServiceConstructor, +})); diff --git a/src/core/public/core_system.test.ts b/src/core/public/core_system.test.ts index b3acd696bc3d..81dfa97b9afa 100644 --- a/src/core/public/core_system.test.ts +++ b/src/core/public/core_system.test.ts @@ -55,6 +55,8 @@ import { MockIntegrationsService, CoreAppConstructor, MockCoreApp, + WorkspacesServiceConstructor, + MockWorkspacesService, } from './core_system.test.mocks'; import { CoreSystem } from './core_system'; @@ -99,6 +101,7 @@ describe('constructor', () => { expect(RenderingServiceConstructor).toHaveBeenCalledTimes(1); expect(IntegrationsServiceConstructor).toHaveBeenCalledTimes(1); expect(CoreAppConstructor).toHaveBeenCalledTimes(1); + expect(WorkspacesServiceConstructor).toHaveBeenCalledTimes(1); }); it('passes injectedMetadata param to InjectedMetadataService', () => { @@ -223,6 +226,11 @@ describe('#setup()', () => { expect(MockIntegrationsService.setup).toHaveBeenCalledTimes(1); }); + it('calls workspaces#setup()', async () => { + await setupCore(); + expect(MockWorkspacesService.setup).toHaveBeenCalledTimes(1); + }); + it('calls coreApp#setup()', async () => { await setupCore(); expect(MockCoreApp.setup).toHaveBeenCalledTimes(1); @@ -310,6 +318,15 @@ describe('#start()', () => { expect(MockIntegrationsService.start).toHaveBeenCalledTimes(1); }); + it('calls workspaces#start()', async () => { + await startCore(); + expect(MockWorkspacesService.start).toHaveBeenCalledTimes(1); + expect(MockWorkspacesService.start).toHaveBeenCalledWith({ + application: expect.any(Object), + http: expect.any(Object), + }); + }); + it('calls coreApp#start()', async () => { await startCore(); expect(MockCoreApp.start).toHaveBeenCalledTimes(1); @@ -364,6 +381,14 @@ describe('#stop()', () => { expect(MockIntegrationsService.stop).toHaveBeenCalled(); }); + it('calls workspaces.stop()', () => { + const coreSystem = createCoreSystem(); + + expect(MockWorkspacesService.stop).not.toHaveBeenCalled(); + coreSystem.stop(); + expect(MockWorkspacesService.stop).toHaveBeenCalled(); + }); + it('calls coreApp.stop()', () => { const coreSystem = createCoreSystem(); diff --git a/src/core/public/core_system.ts b/src/core/public/core_system.ts index d7de8b4595d5..0a64e0f4fa9a 100644 --- a/src/core/public/core_system.ts +++ b/src/core/public/core_system.ts @@ -311,6 +311,7 @@ export class CoreSystem { this.chrome.stop(); this.i18n.stop(); this.application.stop(); + this.workspaces.stop(); this.rootDomElement.textContent = ''; } } diff --git a/src/core/public/mocks.ts b/src/core/public/mocks.ts index 722070d5a9ea..3acc71424b91 100644 --- a/src/core/public/mocks.ts +++ b/src/core/public/mocks.ts @@ -87,7 +87,7 @@ function createCoreSetupMock({ getInjectedVar: injectedMetadataServiceMock.createSetupContract().getInjectedVar, getBranding: injectedMetadataServiceMock.createSetupContract().getBranding, }, - workspaces: workspacesServiceMock.createSetupContractMock(), + workspaces: workspacesServiceMock.createSetupContract(), }; return mock; diff --git a/src/core/public/plugins/plugins_service.test.ts b/src/core/public/plugins/plugins_service.test.ts index 7e8c96c1f9d0..f26626ed1ca3 100644 --- a/src/core/public/plugins/plugins_service.test.ts +++ b/src/core/public/plugins/plugins_service.test.ts @@ -109,7 +109,7 @@ describe('PluginsService', () => { injectedMetadata: injectedMetadataServiceMock.createStartContract(), notifications: notificationServiceMock.createSetupContract(), uiSettings: uiSettingsServiceMock.createSetupContract(), - workspaces: workspacesServiceMock.createSetupContractMock(), + workspaces: workspacesServiceMock.createSetupContract(), }; mockSetupContext = { ...mockSetupDeps, diff --git a/src/core/public/workspace/workspaces_service.mock.ts b/src/core/public/workspace/workspaces_service.mock.ts index 3c35315aa850..3b1408b03045 100644 --- a/src/core/public/workspace/workspaces_service.mock.ts +++ b/src/core/public/workspace/workspaces_service.mock.ts @@ -4,6 +4,9 @@ */ import { BehaviorSubject } from 'rxjs'; +import type { PublicMethodsOf } from '@osd/utility-types'; + +import { WorkspacesService } from './workspaces_service'; import { WorkspaceAttribute } from '..'; const currentWorkspaceId$ = new BehaviorSubject(''); @@ -30,7 +33,15 @@ const createWorkspacesStartContractMock = () => ({ renderWorkspaceMenu: jest.fn(), }); +export type WorkspacesServiceContract = PublicMethodsOf; +const createMock = (): jest.Mocked => ({ + setup: jest.fn().mockReturnValue(createWorkspacesSetupContractMock()), + start: jest.fn().mockReturnValue(createWorkspacesStartContractMock()), + stop: jest.fn(), +}); + export const workspacesServiceMock = { - createSetupContractMock: createWorkspacesSetupContractMock, + create: createMock, + createSetupContract: createWorkspacesSetupContractMock, createStartContract: createWorkspacesStartContractMock, }; diff --git a/src/core/public/workspace/workspaces_service.test.ts b/src/core/public/workspace/workspaces_service.test.ts new file mode 100644 index 000000000000..b2d84152da83 --- /dev/null +++ b/src/core/public/workspace/workspaces_service.test.ts @@ -0,0 +1,94 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { httpServiceMock } from '../http/http_service.mock'; +import { applicationServiceMock } from '../application/application_service.mock'; +import { WorkspacesService, WorkspacesSetup, WorkspacesStart } from './workspaces_service'; + +describe('WorkspacesService', () => { + let workspaces: WorkspacesService; + let workspacesSetup: WorkspacesSetup; + let workspacesStart: WorkspacesStart; + + beforeEach(() => { + workspaces = new WorkspacesService(); + workspacesSetup = workspaces.setup(); + workspacesStart = workspaces.start({ + http: httpServiceMock.createStartContract(), + application: applicationServiceMock.createInternalStartContract(), + }); + }); + + afterEach(() => { + workspaces.stop(); + }); + + it('workspace initialized$ state is false by default', () => { + expect(workspacesStart.initialized$.value).toBe(false); + }); + + it('workspace is not enabled by default', () => { + expect(workspacesStart.workspaceEnabled$.value).toBe(false); + }); + + it('currentWorkspace is not set by default', () => { + expect(workspacesStart.currentWorkspace$.value).toBe(null); + expect(workspacesStart.currentWorkspaceId$.value).toBe(''); + }); + + it('workspaceList$ is empty by default', () => { + expect(workspacesStart.workspaceList$.value.length).toBe(0); + }); + + it('should call menu render function', () => { + const renderFn = jest.fn(); + workspacesSetup.registerWorkspaceMenuRender(renderFn); + workspacesStart.renderWorkspaceMenu(); + expect(renderFn).toHaveBeenCalled(); + }); + + it('should return null if NO menu render function was registered', () => { + expect(workspacesStart.renderWorkspaceMenu()).toBe(null); + }); + + it('the current workspace should also updated after changing current workspace id', () => { + expect(workspacesStart.currentWorkspace$.value).toBe(null); + + workspacesStart.initialized$.next(true); + workspacesStart.workspaceList$.next([ + { id: 'workspace-1', name: 'workspace 1' }, + { id: 'workspace-2', name: 'workspace 2' }, + ]); + workspacesStart.currentWorkspaceId$.next('workspace-1'); + + expect(workspacesStart.currentWorkspace$.value).toEqual({ + id: 'workspace-1', + name: 'workspace 1', + }); + + workspacesStart.currentWorkspaceId$.next(''); + expect(workspacesStart.currentWorkspace$.value).toEqual(null); + }); + + it('should return error if the specified workspace id cannot be found', () => { + expect(workspacesStart.currentWorkspace$.hasError).toBe(false); + workspacesStart.initialized$.next(true); + workspacesStart.workspaceList$.next([ + { id: 'workspace-1', name: 'workspace 1' }, + { id: 'workspace-2', name: 'workspace 2' }, + ]); + workspacesStart.currentWorkspaceId$.next('workspace-3'); + expect(workspacesStart.currentWorkspace$.hasError).toBe(true); + }); + + it('should stop all observables when workspace service stopped', () => { + workspaces.stop(); + expect(workspacesStart.currentWorkspaceId$.isStopped).toBe(true); + expect(workspacesStart.currentWorkspace$.isStopped).toBe(true); + expect(workspacesStart.workspaceList$.isStopped).toBe(true); + expect(workspacesStart.workspaceEnabled$.isStopped).toBe(true); + expect(workspacesStart.initialized$.isStopped).toBe(true); + }); +}); diff --git a/src/core/public/workspace/workspaces_service.ts b/src/core/public/workspace/workspaces_service.ts index 39519ccdddbe..b5c8ff4cdba1 100644 --- a/src/core/public/workspace/workspaces_service.ts +++ b/src/core/public/workspace/workspaces_service.ts @@ -8,7 +8,7 @@ import { isEqual } from 'lodash'; import { CoreService, WorkspaceAttribute } from '../../types'; import { InternalApplicationStart } from '../application'; -import { HttpSetup } from '../http'; +import { HttpStart } from '../http'; type WorkspaceMenuRenderFn = ({ basePath, @@ -16,7 +16,7 @@ type WorkspaceMenuRenderFn = ({ observables, }: { getUrlForApp: InternalApplicationStart['getUrlForApp']; - basePath: HttpSetup['basePath']; + basePath: HttpStart['basePath']; observables: WorkspaceObservables; }) => JSX.Element | null; @@ -99,7 +99,7 @@ export class WorkspacesService implements CoreService Date: Fri, 22 Sep 2023 18:51:57 +0800 Subject: [PATCH 03/11] add missing type definition Signed-off-by: Yulong Ruan --- src/core/types/index.ts | 1 + src/core/types/workspace.ts | 15 +++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 src/core/types/workspace.ts diff --git a/src/core/types/index.ts b/src/core/types/index.ts index 9f620273e3b2..4afe9c537f75 100644 --- a/src/core/types/index.ts +++ b/src/core/types/index.ts @@ -39,3 +39,4 @@ export * from './ui_settings'; export * from './saved_objects'; export * from './serializable'; export * from './custom_branding'; +export * from './workspace'; diff --git a/src/core/types/workspace.ts b/src/core/types/workspace.ts new file mode 100644 index 000000000000..e99744183cac --- /dev/null +++ b/src/core/types/workspace.ts @@ -0,0 +1,15 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export interface WorkspaceAttribute { + id: string; + name: string; + description?: string; + features?: string[]; + color?: string; + icon?: string; + defaultVISTheme?: string; + reserved?: boolean; +} From 65aa8a192de9017eb7c6c10403d2554bda87ef08 Mon Sep 17 00:00:00 2001 From: Yulong Ruan Date: Mon, 25 Sep 2023 17:18:09 +0800 Subject: [PATCH 04/11] add changelog for workspace service module Signed-off-by: Yulong Ruan --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91fe7172b969..2d618c0c250e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - [Theme] Use themes' definitions to render the initial view ([#4936](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4936)) - [Theme] Make `next` theme the default ([#4854](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4854)) - [Discover] Update embeddable for saved searches ([#5081](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5081)) +- [Workspace] Add core workspace service module to enable the implementation of workspace features within OSD plugins ([#5092](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5092)) ### 🐛 Bug Fixes @@ -800,4 +801,4 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### 🔩 Tests -- Update caniuse to fix failed integration tests ([#2322](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2322)) +- Update caniuse to fix failed integration tests ([#2322](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2322)) \ No newline at end of file From a8e9582f39a6de8fc302802655559665dfb1cc7c Mon Sep 17 00:00:00 2001 From: Yulong Ruan Date: Tue, 26 Sep 2023 17:51:14 +0800 Subject: [PATCH 05/11] remove unnecessary workspace menu register Signed-off-by: Yulong Ruan --- src/core/public/core_system.test.ts | 4 -- src/core/public/core_system.ts | 2 +- .../workspace/workspaces_service.mock.ts | 2 - .../workspace/workspaces_service.test.ts | 22 +------- .../public/workspace/workspaces_service.ts | 51 ++----------------- .../dashboard_listing.test.tsx.snap | 5 -- .../dashboard_top_nav.test.tsx.snap | 6 --- 7 files changed, 7 insertions(+), 85 deletions(-) diff --git a/src/core/public/core_system.test.ts b/src/core/public/core_system.test.ts index 81dfa97b9afa..24de9ea6b9ea 100644 --- a/src/core/public/core_system.test.ts +++ b/src/core/public/core_system.test.ts @@ -321,10 +321,6 @@ describe('#start()', () => { it('calls workspaces#start()', async () => { await startCore(); expect(MockWorkspacesService.start).toHaveBeenCalledTimes(1); - expect(MockWorkspacesService.start).toHaveBeenCalledWith({ - application: expect.any(Object), - http: expect.any(Object), - }); }); it('calls coreApp#start()', async () => { diff --git a/src/core/public/core_system.ts b/src/core/public/core_system.ts index 0a64e0f4fa9a..a9dba002e44c 100644 --- a/src/core/public/core_system.ts +++ b/src/core/public/core_system.ts @@ -225,7 +225,7 @@ export class CoreSystem { targetDomElement: notificationsTargetDomElement, }); const application = await this.application.start({ http, overlays }); - const workspaces = this.workspaces.start({ application, http }); + const workspaces = this.workspaces.start(); const chrome = await this.chrome.start({ application, docLinks, diff --git a/src/core/public/workspace/workspaces_service.mock.ts b/src/core/public/workspace/workspaces_service.mock.ts index 3b1408b03045..a8d2a91bd3d1 100644 --- a/src/core/public/workspace/workspaces_service.mock.ts +++ b/src/core/public/workspace/workspaces_service.mock.ts @@ -21,7 +21,6 @@ const createWorkspacesSetupContractMock = () => ({ currentWorkspace$, initialized$, workspaceEnabled$, - registerWorkspaceMenuRender: jest.fn(), }); const createWorkspacesStartContractMock = () => ({ @@ -30,7 +29,6 @@ const createWorkspacesStartContractMock = () => ({ currentWorkspace$, initialized$, workspaceEnabled$, - renderWorkspaceMenu: jest.fn(), }); export type WorkspacesServiceContract = PublicMethodsOf; diff --git a/src/core/public/workspace/workspaces_service.test.ts b/src/core/public/workspace/workspaces_service.test.ts index b2d84152da83..b8f9b17ae0c8 100644 --- a/src/core/public/workspace/workspaces_service.test.ts +++ b/src/core/public/workspace/workspaces_service.test.ts @@ -3,22 +3,15 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { httpServiceMock } from '../http/http_service.mock'; -import { applicationServiceMock } from '../application/application_service.mock'; -import { WorkspacesService, WorkspacesSetup, WorkspacesStart } from './workspaces_service'; +import { WorkspacesService, WorkspacesStart } from './workspaces_service'; describe('WorkspacesService', () => { let workspaces: WorkspacesService; - let workspacesSetup: WorkspacesSetup; let workspacesStart: WorkspacesStart; beforeEach(() => { workspaces = new WorkspacesService(); - workspacesSetup = workspaces.setup(); - workspacesStart = workspaces.start({ - http: httpServiceMock.createStartContract(), - application: applicationServiceMock.createInternalStartContract(), - }); + workspacesStart = workspaces.start(); }); afterEach(() => { @@ -42,17 +35,6 @@ describe('WorkspacesService', () => { expect(workspacesStart.workspaceList$.value.length).toBe(0); }); - it('should call menu render function', () => { - const renderFn = jest.fn(); - workspacesSetup.registerWorkspaceMenuRender(renderFn); - workspacesStart.renderWorkspaceMenu(); - expect(renderFn).toHaveBeenCalled(); - }); - - it('should return null if NO menu render function was registered', () => { - expect(workspacesStart.renderWorkspaceMenu()).toBe(null); - }); - it('the current workspace should also updated after changing current workspace id', () => { expect(workspacesStart.currentWorkspace$.value).toBe(null); diff --git a/src/core/public/workspace/workspaces_service.ts b/src/core/public/workspace/workspaces_service.ts index b5c8ff4cdba1..f3d1400ce709 100644 --- a/src/core/public/workspace/workspaces_service.ts +++ b/src/core/public/workspace/workspaces_service.ts @@ -7,18 +7,6 @@ import { BehaviorSubject, combineLatest } from 'rxjs'; import { isEqual } from 'lodash'; import { CoreService, WorkspaceAttribute } from '../../types'; -import { InternalApplicationStart } from '../application'; -import { HttpStart } from '../http'; - -type WorkspaceMenuRenderFn = ({ - basePath, - getUrlForApp, - observables, -}: { - getUrlForApp: InternalApplicationStart['getUrlForApp']; - basePath: HttpStart['basePath']; - observables: WorkspaceObservables; -}) => JSX.Element | null; type WorkspaceObject = WorkspaceAttribute & { readonly?: boolean }; @@ -34,16 +22,8 @@ enum WORKSPACE_ERROR { WORKSPACE_STALED = 'WORKSPACE_STALED', } -/** - * @public - */ -export interface WorkspacesSetup extends WorkspaceObservables { - registerWorkspaceMenuRender: (render: WorkspaceMenuRenderFn) => void; -} - -export interface WorkspacesStart extends WorkspaceObservables { - renderWorkspaceMenu: () => JSX.Element | null; -} +export type WorkspacesSetup = WorkspaceObservables; +export type WorkspacesStart = WorkspaceObservables; export class WorkspacesService implements CoreService { private currentWorkspaceId$ = new BehaviorSubject(''); @@ -51,7 +31,6 @@ export class WorkspacesService implements CoreService(null); private initialized$ = new BehaviorSubject(false); private workspaceEnabled$ = new BehaviorSubject(false); - private _renderWorkspaceMenu: WorkspaceMenuRenderFn | null = null; constructor() { combineLatest([this.initialized$, this.workspaceList$, this.currentWorkspaceId$]).subscribe( @@ -89,38 +68,17 @@ export class WorkspacesService implements CoreService - (this._renderWorkspaceMenu = render), }; } - public start({ - http, - application, - }: { - application: InternalApplicationStart; - http: HttpStart; - }): WorkspacesStart { - const observables = { + public start(): WorkspacesStart { + return { currentWorkspaceId$: this.currentWorkspaceId$, currentWorkspace$: this.currentWorkspace$, workspaceList$: this.workspaceList$, initialized$: this.initialized$, workspaceEnabled$: this.workspaceEnabled$, }; - return { - ...observables, - renderWorkspaceMenu: () => { - if (this._renderWorkspaceMenu) { - return this._renderWorkspaceMenu({ - basePath: http.basePath, - getUrlForApp: application.getUrlForApp, - observables, - }); - } - return null; - }, - }; } public async stop() { @@ -129,6 +87,5 @@ export class WorkspacesService implements CoreService Date: Wed, 27 Sep 2023 09:26:15 +0800 Subject: [PATCH 06/11] Update test description per comment Co-authored-by: Miki Signed-off-by: Yulong Ruan Signed-off-by: Yulong Ruan --- src/core/public/workspace/workspaces_service.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/public/workspace/workspaces_service.test.ts b/src/core/public/workspace/workspaces_service.test.ts index b8f9b17ae0c8..3bedc734d92c 100644 --- a/src/core/public/workspace/workspaces_service.test.ts +++ b/src/core/public/workspace/workspaces_service.test.ts @@ -35,7 +35,7 @@ describe('WorkspacesService', () => { expect(workspacesStart.workspaceList$.value.length).toBe(0); }); - it('the current workspace should also updated after changing current workspace id', () => { + it('currentWorkspace is updated when currentWorkspaceId changes', () => { expect(workspacesStart.currentWorkspace$.value).toBe(null); workspacesStart.initialized$.next(true); From 60ab8c871e446ab7492369f7f08768c9340c5443 Mon Sep 17 00:00:00 2001 From: Yulong Ruan Date: Thu, 28 Sep 2023 16:15:45 +0800 Subject: [PATCH 07/11] remove unnecessary workspace enabled flag from core workspace module Signed-off-by: Yulong Ruan --- .../workspace/workspaces_service.mock.ts | 3 -- .../workspace/workspaces_service.test.ts | 5 -- .../public/workspace/workspaces_service.ts | 5 -- src/core/types/capabilities.ts | 3 ++ .../dashboard_listing.test.tsx.snap | 45 ---------------- .../dashboard_top_nav.test.tsx.snap | 54 ------------------- 6 files changed, 3 insertions(+), 112 deletions(-) diff --git a/src/core/public/workspace/workspaces_service.mock.ts b/src/core/public/workspace/workspaces_service.mock.ts index a8d2a91bd3d1..ae56c035eb3a 100644 --- a/src/core/public/workspace/workspaces_service.mock.ts +++ b/src/core/public/workspace/workspaces_service.mock.ts @@ -13,14 +13,12 @@ const currentWorkspaceId$ = new BehaviorSubject(''); const workspaceList$ = new BehaviorSubject([]); const currentWorkspace$ = new BehaviorSubject(null); const initialized$ = new BehaviorSubject(false); -const workspaceEnabled$ = new BehaviorSubject(false); const createWorkspacesSetupContractMock = () => ({ currentWorkspaceId$, workspaceList$, currentWorkspace$, initialized$, - workspaceEnabled$, }); const createWorkspacesStartContractMock = () => ({ @@ -28,7 +26,6 @@ const createWorkspacesStartContractMock = () => ({ workspaceList$, currentWorkspace$, initialized$, - workspaceEnabled$, }); export type WorkspacesServiceContract = PublicMethodsOf; diff --git a/src/core/public/workspace/workspaces_service.test.ts b/src/core/public/workspace/workspaces_service.test.ts index 3bedc734d92c..4eca97ef2ed2 100644 --- a/src/core/public/workspace/workspaces_service.test.ts +++ b/src/core/public/workspace/workspaces_service.test.ts @@ -22,10 +22,6 @@ describe('WorkspacesService', () => { expect(workspacesStart.initialized$.value).toBe(false); }); - it('workspace is not enabled by default', () => { - expect(workspacesStart.workspaceEnabled$.value).toBe(false); - }); - it('currentWorkspace is not set by default', () => { expect(workspacesStart.currentWorkspace$.value).toBe(null); expect(workspacesStart.currentWorkspaceId$.value).toBe(''); @@ -70,7 +66,6 @@ describe('WorkspacesService', () => { expect(workspacesStart.currentWorkspaceId$.isStopped).toBe(true); expect(workspacesStart.currentWorkspace$.isStopped).toBe(true); expect(workspacesStart.workspaceList$.isStopped).toBe(true); - expect(workspacesStart.workspaceEnabled$.isStopped).toBe(true); expect(workspacesStart.initialized$.isStopped).toBe(true); }); }); diff --git a/src/core/public/workspace/workspaces_service.ts b/src/core/public/workspace/workspaces_service.ts index f3d1400ce709..f4cad977deef 100644 --- a/src/core/public/workspace/workspaces_service.ts +++ b/src/core/public/workspace/workspaces_service.ts @@ -14,7 +14,6 @@ export interface WorkspaceObservables { currentWorkspaceId$: BehaviorSubject; currentWorkspace$: BehaviorSubject; workspaceList$: BehaviorSubject; - workspaceEnabled$: BehaviorSubject; initialized$: BehaviorSubject; } @@ -30,7 +29,6 @@ export class WorkspacesService implements CoreService([]); private currentWorkspace$ = new BehaviorSubject(null); private initialized$ = new BehaviorSubject(false); - private workspaceEnabled$ = new BehaviorSubject(false); constructor() { combineLatest([this.initialized$, this.workspaceList$, this.currentWorkspaceId$]).subscribe( @@ -67,7 +65,6 @@ export class WorkspacesService implements CoreService; + /** Workspaces capabilities. */ + workspaces: Record; + /** Custom capabilities, registered by plugins. undefined if the key does not exist */ [key: string]: Record> | undefined; } 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 5d32f6a74c78..2a0173850a4d 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 @@ -1021,15 +1021,6 @@ exports[`dashboard listing hideWriteControls 1`] = ` "observers": Array [], "thrownError": null, }, - "workspaceEnabled$": BehaviorSubject { - "_isScalar": false, - "_value": false, - "closed": false, - "hasError": false, - "isStopped": false, - "observers": Array [], - "thrownError": null, - }, "workspaceList$": BehaviorSubject { "_isScalar": false, "_value": Array [], @@ -2160,15 +2151,6 @@ exports[`dashboard listing render table listing with initial filters from URL 1` "observers": Array [], "thrownError": null, }, - "workspaceEnabled$": BehaviorSubject { - "_isScalar": false, - "_value": false, - "closed": false, - "hasError": false, - "isStopped": false, - "observers": Array [], - "thrownError": null, - }, "workspaceList$": BehaviorSubject { "_isScalar": false, "_value": Array [], @@ -3360,15 +3342,6 @@ exports[`dashboard listing renders call to action when no dashboards exist 1`] = "observers": Array [], "thrownError": null, }, - "workspaceEnabled$": BehaviorSubject { - "_isScalar": false, - "_value": false, - "closed": false, - "hasError": false, - "isStopped": false, - "observers": Array [], - "thrownError": null, - }, "workspaceList$": BehaviorSubject { "_isScalar": false, "_value": Array [], @@ -4560,15 +4533,6 @@ exports[`dashboard listing renders table rows 1`] = ` "observers": Array [], "thrownError": null, }, - "workspaceEnabled$": BehaviorSubject { - "_isScalar": false, - "_value": false, - "closed": false, - "hasError": false, - "isStopped": false, - "observers": Array [], - "thrownError": null, - }, "workspaceList$": BehaviorSubject { "_isScalar": false, "_value": Array [], @@ -5760,15 +5724,6 @@ exports[`dashboard listing renders warning when listingLimit is exceeded 1`] = ` "observers": Array [], "thrownError": null, }, - "workspaceEnabled$": BehaviorSubject { - "_isScalar": false, - "_value": false, - "closed": false, - "hasError": false, - "isStopped": false, - "observers": Array [], - "thrownError": null, - }, "workspaceList$": BehaviorSubject { "_isScalar": false, "_value": Array [], 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 0b877eb199c6..7a241fe344f5 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 @@ -887,15 +887,6 @@ exports[`Dashboard top nav render in embed mode 1`] = ` "observers": Array [], "thrownError": null, }, - "workspaceEnabled$": BehaviorSubject { - "_isScalar": false, - "_value": false, - "closed": false, - "hasError": false, - "isStopped": false, - "observers": Array [], - "thrownError": null, - }, "workspaceList$": BehaviorSubject { "_isScalar": false, "_value": Array [], @@ -1851,15 +1842,6 @@ exports[`Dashboard top nav render in embed mode, and force hide filter bar 1`] = "observers": Array [], "thrownError": null, }, - "workspaceEnabled$": BehaviorSubject { - "_isScalar": false, - "_value": false, - "closed": false, - "hasError": false, - "isStopped": false, - "observers": Array [], - "thrownError": null, - }, "workspaceList$": BehaviorSubject { "_isScalar": false, "_value": Array [], @@ -2815,15 +2797,6 @@ exports[`Dashboard top nav render in embed mode, components can be forced show b "observers": Array [], "thrownError": null, }, - "workspaceEnabled$": BehaviorSubject { - "_isScalar": false, - "_value": false, - "closed": false, - "hasError": false, - "isStopped": false, - "observers": Array [], - "thrownError": null, - }, "workspaceList$": BehaviorSubject { "_isScalar": false, "_value": Array [], @@ -3779,15 +3752,6 @@ exports[`Dashboard top nav render in full screen mode with appended URL param bu "observers": Array [], "thrownError": null, }, - "workspaceEnabled$": BehaviorSubject { - "_isScalar": false, - "_value": false, - "closed": false, - "hasError": false, - "isStopped": false, - "observers": Array [], - "thrownError": null, - }, "workspaceList$": BehaviorSubject { "_isScalar": false, "_value": Array [], @@ -4743,15 +4707,6 @@ exports[`Dashboard top nav render in full screen mode, no componenets should be "observers": Array [], "thrownError": null, }, - "workspaceEnabled$": BehaviorSubject { - "_isScalar": false, - "_value": false, - "closed": false, - "hasError": false, - "isStopped": false, - "observers": Array [], - "thrownError": null, - }, "workspaceList$": BehaviorSubject { "_isScalar": false, "_value": Array [], @@ -5707,15 +5662,6 @@ exports[`Dashboard top nav render with all components 1`] = ` "observers": Array [], "thrownError": null, }, - "workspaceEnabled$": BehaviorSubject { - "_isScalar": false, - "_value": false, - "closed": false, - "hasError": false, - "isStopped": false, - "observers": Array [], - "thrownError": null, - }, "workspaceList$": BehaviorSubject { "_isScalar": false, "_value": Array [], From 55c4b086f754e737049232a1c73d272665d40134 Mon Sep 17 00:00:00 2001 From: Yulong Ruan Date: Thu, 28 Sep 2023 18:42:30 +0800 Subject: [PATCH 08/11] fix tests and add comments Signed-off-by: Yulong Ruan --- .../capabilities/capabilities_service.mock.ts | 1 + src/core/public/index.ts | 7 +----- src/core/public/workspace/index.ts | 8 ++----- .../public/workspace/workspaces_service.ts | 23 ++++++++++++++++++- .../capabilities/capabilities_service.mock.ts | 1 + .../capabilities/capabilities_service.ts | 1 + .../capabilities/resolve_capabilities.test.ts | 1 + 7 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/core/public/application/capabilities/capabilities_service.mock.ts b/src/core/public/application/capabilities/capabilities_service.mock.ts index 971a43d06d05..d4490b60901b 100644 --- a/src/core/public/application/capabilities/capabilities_service.mock.ts +++ b/src/core/public/application/capabilities/capabilities_service.mock.ts @@ -37,6 +37,7 @@ const createStartContractMock = (): jest.Mocked => ({ catalogue: {}, management: {}, navLinks: {}, + workspaces: {}, }), }); diff --git a/src/core/public/index.ts b/src/core/public/index.ts index 14ab91e1cb13..2967b45f1d75 100644 --- a/src/core/public/index.ts +++ b/src/core/public/index.ts @@ -348,9 +348,4 @@ export { export { __osdBootstrap__ } from './osd_bootstrap'; -export { - WorkspacesStart, - WorkspacesSetup, - WorkspacesService, - WorkspaceObservables, -} from './workspace'; +export { WorkspacesStart, WorkspacesSetup, WorkspacesService } from './workspace'; diff --git a/src/core/public/workspace/index.ts b/src/core/public/workspace/index.ts index 4ef6aaae7fd4..4b9b2c86f649 100644 --- a/src/core/public/workspace/index.ts +++ b/src/core/public/workspace/index.ts @@ -2,9 +2,5 @@ * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 */ -export { - WorkspacesStart, - WorkspacesService, - WorkspacesSetup, - WorkspaceObservables, -} from './workspaces_service'; + +export { WorkspacesStart, WorkspacesService, WorkspacesSetup } from './workspaces_service'; diff --git a/src/core/public/workspace/workspaces_service.ts b/src/core/public/workspace/workspaces_service.ts index f4cad977deef..a7c62a76bec2 100644 --- a/src/core/public/workspace/workspaces_service.ts +++ b/src/core/public/workspace/workspaces_service.ts @@ -10,10 +10,31 @@ import { CoreService, WorkspaceAttribute } from '../../types'; type WorkspaceObject = WorkspaceAttribute & { readonly?: boolean }; -export interface WorkspaceObservables { +interface WorkspaceObservables { + /** + * Indicates the current activated workspace id, the value should be changed every time + * when switching to a different workspace + */ currentWorkspaceId$: BehaviorSubject; + + /** + * The workspace that is derived from `currentWorkspaceId` and `workspaceList`, if + * `currentWorkspaceId` cannot be found from `workspaceList`, it will return an error + * + * This value MUST NOT set manually from outside of WorkspacesService + */ currentWorkspace$: BehaviorSubject; + + /** + * The list of available workspaces. This workspace list should be set by whoever + * the workspace functionalities + */ workspaceList$: BehaviorSubject; + + /** + * This is a flag which indicates the WorkspacesService module is initialized and ready + * for consuming by others. For example, the `workspaceList` has been set, etc + */ initialized$: BehaviorSubject; } diff --git a/src/core/server/capabilities/capabilities_service.mock.ts b/src/core/server/capabilities/capabilities_service.mock.ts index fb0785aac947..f8be8f22b70c 100644 --- a/src/core/server/capabilities/capabilities_service.mock.ts +++ b/src/core/server/capabilities/capabilities_service.mock.ts @@ -52,6 +52,7 @@ const createCapabilitiesMock = (): Capabilities => { navLinks: {}, management: {}, catalogue: {}, + workspaces: {}, }; }; diff --git a/src/core/server/capabilities/capabilities_service.ts b/src/core/server/capabilities/capabilities_service.ts index b92166427271..5153cd5ded1d 100644 --- a/src/core/server/capabilities/capabilities_service.ts +++ b/src/core/server/capabilities/capabilities_service.ts @@ -123,6 +123,7 @@ const defaultCapabilities: Capabilities = { navLinks: {}, management: {}, catalogue: {}, + workspaces: {}, }; /** @internal */ diff --git a/src/core/server/capabilities/resolve_capabilities.test.ts b/src/core/server/capabilities/resolve_capabilities.test.ts index 25968d858e7a..b307929d2671 100644 --- a/src/core/server/capabilities/resolve_capabilities.test.ts +++ b/src/core/server/capabilities/resolve_capabilities.test.ts @@ -42,6 +42,7 @@ describe('resolveCapabilities', () => { navLinks: {}, catalogue: {}, management: {}, + workspaces: {}, }; request = httpServerMock.createOpenSearchDashboardsRequest(); }); From f4cf5a3f82267839b909fc39987632b12bb01916 Mon Sep 17 00:00:00 2001 From: Yulong Ruan Date: Thu, 28 Sep 2023 22:38:52 +0800 Subject: [PATCH 09/11] update failed snapshot Signed-off-by: Yulong Ruan --- .../chrome/ui/header/__snapshots__/header.test.tsx.snap | 2 ++ src/core/server/capabilities/capabilities_service.test.ts | 4 ++++ src/core/server/capabilities/resolve_capabilities.test.ts | 1 + .../__snapshots__/dashboard_listing.test.tsx.snap | 5 +++++ .../__snapshots__/dashboard_top_nav.test.tsx.snap | 6 ++++++ 5 files changed, 18 insertions(+) diff --git a/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap b/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap index b9da5ac37dbe..d5903379b2eb 100644 --- a/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap +++ b/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap @@ -66,6 +66,7 @@ exports[`Header handles visibility and lock changes 1`] = ` "catalogue": Object {}, "management": Object {}, "navLinks": Object {}, + "workspaces": Object {}, }, "currentActionMenu$": BehaviorSubject { "_isScalar": false, @@ -6756,6 +6757,7 @@ exports[`Header renders condensed header 1`] = ` "catalogue": Object {}, "management": Object {}, "navLinks": Object {}, + "workspaces": Object {}, }, "currentActionMenu$": BehaviorSubject { "_isScalar": false, diff --git a/src/core/server/capabilities/capabilities_service.test.ts b/src/core/server/capabilities/capabilities_service.test.ts index 80e22fdb6721..be28130afd4e 100644 --- a/src/core/server/capabilities/capabilities_service.test.ts +++ b/src/core/server/capabilities/capabilities_service.test.ts @@ -72,6 +72,7 @@ describe('CapabilitiesService', () => { "navLinks": Object { "myLink": true, }, + "workspaces": Object {}, } `); }); @@ -107,6 +108,7 @@ describe('CapabilitiesService', () => { "B": true, "C": true, }, + "workspaces": Object {}, } `); }); @@ -134,6 +136,7 @@ describe('CapabilitiesService', () => { }, "management": Object {}, "navLinks": Object {}, + "workspaces": Object {}, } `); }); @@ -192,6 +195,7 @@ describe('CapabilitiesService', () => { "b": true, "c": false, }, + "workspaces": Object {}, } `); }); diff --git a/src/core/server/capabilities/resolve_capabilities.test.ts b/src/core/server/capabilities/resolve_capabilities.test.ts index b307929d2671..88e2aa7a4f7c 100644 --- a/src/core/server/capabilities/resolve_capabilities.test.ts +++ b/src/core/server/capabilities/resolve_capabilities.test.ts @@ -76,6 +76,7 @@ describe('resolveCapabilities', () => { }, "management": Object {}, "navLinks": Object {}, + "workspaces": Object {}, } `); }); 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 2a0173850a4d..28265faad578 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 @@ -104,6 +104,7 @@ exports[`dashboard listing hideWriteControls 1`] = ` "catalogue": Object {}, "management": Object {}, "navLinks": Object {}, + "workspaces": Object {}, }, "currentAppId$": Observable { "_isScalar": false, @@ -1234,6 +1235,7 @@ exports[`dashboard listing render table listing with initial filters from URL 1` "catalogue": Object {}, "management": Object {}, "navLinks": Object {}, + "workspaces": Object {}, }, "currentAppId$": Observable { "_isScalar": false, @@ -2425,6 +2427,7 @@ exports[`dashboard listing renders call to action when no dashboards exist 1`] = "catalogue": Object {}, "management": Object {}, "navLinks": Object {}, + "workspaces": Object {}, }, "currentAppId$": Observable { "_isScalar": false, @@ -3616,6 +3619,7 @@ exports[`dashboard listing renders table rows 1`] = ` "catalogue": Object {}, "management": Object {}, "navLinks": Object {}, + "workspaces": Object {}, }, "currentAppId$": Observable { "_isScalar": false, @@ -4807,6 +4811,7 @@ exports[`dashboard listing renders warning when listingLimit is exceeded 1`] = ` "catalogue": Object {}, "management": Object {}, "navLinks": Object {}, + "workspaces": Object {}, }, "currentAppId$": Observable { "_isScalar": false, 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 7a241fe344f5..7cb635c2f2c0 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 @@ -104,6 +104,7 @@ exports[`Dashboard top nav render in embed mode 1`] = ` "catalogue": Object {}, "management": Object {}, "navLinks": Object {}, + "workspaces": Object {}, }, "currentAppId$": Observable { "_isScalar": false, @@ -1059,6 +1060,7 @@ exports[`Dashboard top nav render in embed mode, and force hide filter bar 1`] = "catalogue": Object {}, "management": Object {}, "navLinks": Object {}, + "workspaces": Object {}, }, "currentAppId$": Observable { "_isScalar": false, @@ -2014,6 +2016,7 @@ exports[`Dashboard top nav render in embed mode, components can be forced show b "catalogue": Object {}, "management": Object {}, "navLinks": Object {}, + "workspaces": Object {}, }, "currentAppId$": Observable { "_isScalar": false, @@ -2969,6 +2972,7 @@ exports[`Dashboard top nav render in full screen mode with appended URL param bu "catalogue": Object {}, "management": Object {}, "navLinks": Object {}, + "workspaces": Object {}, }, "currentAppId$": Observable { "_isScalar": false, @@ -3924,6 +3928,7 @@ exports[`Dashboard top nav render in full screen mode, no componenets should be "catalogue": Object {}, "management": Object {}, "navLinks": Object {}, + "workspaces": Object {}, }, "currentAppId$": Observable { "_isScalar": false, @@ -4879,6 +4884,7 @@ exports[`Dashboard top nav render with all components 1`] = ` "catalogue": Object {}, "management": Object {}, "navLinks": Object {}, + "workspaces": Object {}, }, "currentAppId$": Observable { "_isScalar": false, From 018395f81a62feaf5d68d1da5c6cc906d1429a87 Mon Sep 17 00:00:00 2001 From: Yulong Ruan Date: Thu, 28 Sep 2023 23:35:56 +0800 Subject: [PATCH 10/11] update failed snapshots Signed-off-by: Yulong Ruan --- .../capabilities/integration_tests/capabilities_service.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/server/capabilities/integration_tests/capabilities_service.test.ts b/src/core/server/capabilities/integration_tests/capabilities_service.test.ts index b60a067982e0..3cab62478cbc 100644 --- a/src/core/server/capabilities/integration_tests/capabilities_service.test.ts +++ b/src/core/server/capabilities/integration_tests/capabilities_service.test.ts @@ -79,6 +79,7 @@ describe('CapabilitiesService', () => { "catalogue": Object {}, "management": Object {}, "navLinks": Object {}, + "workspaces": Object {}, } `); }); @@ -101,6 +102,7 @@ describe('CapabilitiesService', () => { }, "management": Object {}, "navLinks": Object {}, + "workspaces": Object {}, } `); }); From a21adee3f513ab2b11dcd57e5cdd2f0a82540117 Mon Sep 17 00:00:00 2001 From: Yulong Ruan Date: Fri, 13 Oct 2023 16:15:23 +0800 Subject: [PATCH 11/11] update per PR comments Signed-off-by: Yulong Ruan --- src/core/public/index.ts | 2 +- src/core/public/workspace/workspaces_service.ts | 11 ++++++----- src/core/types/workspace.ts | 1 - 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/core/public/index.ts b/src/core/public/index.ts index 2967b45f1d75..084e3c246b13 100644 --- a/src/core/public/index.ts +++ b/src/core/public/index.ts @@ -348,4 +348,4 @@ export { export { __osdBootstrap__ } from './osd_bootstrap'; -export { WorkspacesStart, WorkspacesSetup, WorkspacesService } from './workspace'; +export { WorkspacesStart, WorkspacesSetup } from './workspace'; diff --git a/src/core/public/workspace/workspaces_service.ts b/src/core/public/workspace/workspaces_service.ts index a7c62a76bec2..cc19b3c79229 100644 --- a/src/core/public/workspace/workspaces_service.ts +++ b/src/core/public/workspace/workspaces_service.ts @@ -27,7 +27,8 @@ interface WorkspaceObservables { /** * The list of available workspaces. This workspace list should be set by whoever - * the workspace functionalities + * implemented the workspace functionalities, core workspace module should not be + * aware of how to populate the workspace list. */ workspaceList$: BehaviorSubject; @@ -39,7 +40,7 @@ interface WorkspaceObservables { } enum WORKSPACE_ERROR { - WORKSPACE_STALED = 'WORKSPACE_STALED', + WORKSPACE_IS_STALE = 'WORKSPACE_IS_STALE', } export type WorkspacesSetup = WorkspaceObservables; @@ -66,13 +67,13 @@ export class WorkspacesService implements CoreService