From 43acbfb6d20382491a63cd6aaa1177c8ee1416df Mon Sep 17 00:00:00 2001 From: SuZhou-Joe Date: Mon, 9 Oct 2023 14:09:54 +0800 Subject: [PATCH 01/22] feat: enable workspace id in basePath Signed-off-by: SuZhou-Joe --- src/plugins/workspace/server/plugin.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/workspace/server/plugin.ts b/src/plugins/workspace/server/plugin.ts index d926727fd569..9e9d3517593c 100644 --- a/src/plugins/workspace/server/plugin.ts +++ b/src/plugins/workspace/server/plugin.ts @@ -72,6 +72,8 @@ export class WorkspacePlugin implements Plugin<{}, {}> { this.proxyWorkspaceTrafficToRealHandler(core); + this.proxyWorkspaceTrafficToRealHandler(core); + registerRoutes({ http: core.http, logger: this.logger, From 3c128d177d38134298222c90da41fd6eb33a64fe Mon Sep 17 00:00:00 2001 From: SuZhou-Joe Date: Tue, 10 Oct 2023 14:29:01 +0800 Subject: [PATCH 02/22] feat: add unit test Signed-off-by: SuZhou-Joe --- src/core/public/http/base_path.test.ts | 32 +++ src/core/public/http/http_service.test.ts | 26 +++ src/core/public/utils/workspace.test.ts | 16 ++ .../workspace_fatal_error.test.tsx.snap | 182 ++++++++++++++++++ .../workspace_fatal_error.test.tsx | 21 ++ src/plugins/workspace/public/plugin.test.ts | 112 +++++++++++ .../workspace/public/workspace_client.mock.ts | 25 +++ 7 files changed, 414 insertions(+) create mode 100644 src/core/public/utils/workspace.test.ts create mode 100644 src/plugins/workspace/public/components/workspace_fatal_error/__snapshots__/workspace_fatal_error.test.tsx.snap create mode 100644 src/plugins/workspace/public/components/workspace_fatal_error/workspace_fatal_error.test.tsx create mode 100644 src/plugins/workspace/public/plugin.test.ts create mode 100644 src/plugins/workspace/public/workspace_client.mock.ts diff --git a/src/core/public/http/base_path.test.ts b/src/core/public/http/base_path.test.ts index 27cfa9bf0581..f80d41631b9b 100644 --- a/src/core/public/http/base_path.test.ts +++ b/src/core/public/http/base_path.test.ts @@ -110,4 +110,36 @@ describe('BasePath', () => { expect(new BasePath('/foo/bar', '/foo').serverBasePath).toEqual('/foo'); }); }); + + describe('workspaceBasePath', () => { + it('get path with workspace', () => { + expect(new BasePath('/foo/bar', '/foo/bar', '/workspace').get()).toEqual( + '/foo/bar/workspace' + ); + }); + + it('getBasePath with workspace provided', () => { + expect(new BasePath('/foo/bar', '/foo/bar', '/workspace').getBasePath()).toEqual('/foo/bar'); + }); + + it('prepend with workspace provided', () => { + expect(new BasePath('/foo/bar', '/foo/bar', '/workspace').prepend('/prepend')).toEqual( + '/foo/bar/workspace/prepend' + ); + }); + + it('prepend with workspace provided but calls without workspace', () => { + expect( + new BasePath('/foo/bar', '/foo/bar', '/workspace').prepend('/prepend', { + withoutWorkspace: true, + }) + ).toEqual('/foo/bar/prepend'); + }); + + it('remove with workspace provided', () => { + expect( + new BasePath('/foo/bar', '/foo/bar', '/workspace').remove('/foo/bar/workspace/remove') + ).toEqual('/remove'); + }); + }); }); diff --git a/src/core/public/http/http_service.test.ts b/src/core/public/http/http_service.test.ts index e60e506dfc0a..5671064e4c52 100644 --- a/src/core/public/http/http_service.test.ts +++ b/src/core/public/http/http_service.test.ts @@ -74,6 +74,32 @@ describe('#setup()', () => { // We don't verify that this Observable comes from Fetch#getLoadingCount$() to avoid complex mocking expect(loadingServiceSetup.addLoadingCountSource).toHaveBeenCalledWith(expect.any(Observable)); }); + + it('setup basePath without workspaceId provided in window.location.href', () => { + const injectedMetadata = injectedMetadataServiceMock.createSetupContract(); + const fatalErrors = fatalErrorsServiceMock.createSetupContract(); + const httpService = new HttpService(); + const setupResult = httpService.setup({ fatalErrors, injectedMetadata }); + expect(setupResult.basePath.get()).toEqual(''); + }); + + it('setup basePath with workspaceId provided in window.location.href', () => { + const windowSpy = jest.spyOn(window, 'window', 'get'); + windowSpy.mockImplementation( + () => + ({ + location: { + href: 'http://localhost/w/workspaceId/app', + }, + } as any) + ); + const injectedMetadata = injectedMetadataServiceMock.createSetupContract(); + const fatalErrors = fatalErrorsServiceMock.createSetupContract(); + const httpService = new HttpService(); + const setupResult = httpService.setup({ fatalErrors, injectedMetadata }); + expect(setupResult.basePath.get()).toEqual('/w/workspaceId'); + windowSpy.mockRestore(); + }); }); describe('#stop()', () => { diff --git a/src/core/public/utils/workspace.test.ts b/src/core/public/utils/workspace.test.ts new file mode 100644 index 000000000000..ab15152ddf04 --- /dev/null +++ b/src/core/public/utils/workspace.test.ts @@ -0,0 +1,16 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { getWorkspaceIdFromUrl } from './workspace'; + +describe('#getWorkspaceIdFromUrl', () => { + it('return workspace when there is a match', () => { + expect(getWorkspaceIdFromUrl('/w/foo')).toEqual('foo'); + }); + + it('return empty when there is not a match', () => { + expect(getWorkspaceIdFromUrl('/w2/foo')).toEqual(''); + }); +}); diff --git a/src/plugins/workspace/public/components/workspace_fatal_error/__snapshots__/workspace_fatal_error.test.tsx.snap b/src/plugins/workspace/public/components/workspace_fatal_error/__snapshots__/workspace_fatal_error.test.tsx.snap new file mode 100644 index 000000000000..df744cde09c8 --- /dev/null +++ b/src/plugins/workspace/public/components/workspace_fatal_error/__snapshots__/workspace_fatal_error.test.tsx.snap @@ -0,0 +1,182 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` render error with callout 1`] = ` +
+
+
+
+
+ +
+

+ + Something went wrong + +

+ +
+
+

+ + The workspace you want to go can not be found, try go back to home. + +

+
+ +
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+`; + +exports[` render normally 1`] = ` +
+
+
+
+
+ +
+

+ + Something went wrong + +

+ +
+
+

+ + The workspace you want to go can not be found, try go back to home. + +

+
+ +
+
+
+ +
+
+
+
+
+
+
+`; diff --git a/src/plugins/workspace/public/components/workspace_fatal_error/workspace_fatal_error.test.tsx b/src/plugins/workspace/public/components/workspace_fatal_error/workspace_fatal_error.test.tsx new file mode 100644 index 000000000000..f69007cb381a --- /dev/null +++ b/src/plugins/workspace/public/components/workspace_fatal_error/workspace_fatal_error.test.tsx @@ -0,0 +1,21 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import { WorkspaceFatalError } from './workspace_fatal_error'; +describe('', () => { + it('render normally', async () => { + const { findByText, container } = render(); + await findByText('Something went wrong'); + expect(container).toMatchSnapshot(); + }); + + it('render error with callout', async () => { + const { findByText, container } = render(); + await findByText('errorInCallout'); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/src/plugins/workspace/public/plugin.test.ts b/src/plugins/workspace/public/plugin.test.ts new file mode 100644 index 000000000000..4deb3ebce359 --- /dev/null +++ b/src/plugins/workspace/public/plugin.test.ts @@ -0,0 +1,112 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { workspaceClientMock, WorkspaceClientMock } from './workspace_client.mock'; +import { applicationServiceMock, chromeServiceMock, coreMock } from '../../../core/public/mocks'; +import { WorkspacePlugin } from './plugin'; +import { WORKSPACE_FATAL_ERROR_APP_ID, WORKSPACE_OVERVIEW_APP_ID } from '../common/constants'; +import { Observable, Subscriber } from 'rxjs'; + +describe('Workspace plugin', () => { + beforeEach(() => { + WorkspaceClientMock.mockClear(); + Object.values(workspaceClientMock).forEach((item) => item.mockClear()); + }); + it('#setup', async () => { + const setupMock = coreMock.createSetup(); + const workspacePlugin = new WorkspacePlugin(); + await workspacePlugin.setup(setupMock); + expect(setupMock.application.register).toBeCalledTimes(1); + expect(setupMock.workspaces.workspaceEnabled$.getValue()).toEqual(true); + expect(WorkspaceClientMock).toBeCalledTimes(1); + expect(workspaceClientMock.enterWorkspace).toBeCalledTimes(0); + }); + + it('#setup when workspace id is in url and enterWorkspace return error', async () => { + const windowSpy = jest.spyOn(window, 'window', 'get'); + windowSpy.mockImplementation( + () => + ({ + location: { + href: 'http://localhost/w/workspaceId/app', + }, + } as any) + ); + workspaceClientMock.enterWorkspace.mockResolvedValue({ + success: false, + error: 'error', + }); + const setupMock = coreMock.createSetup(); + const applicationStartMock = applicationServiceMock.createStartContract(); + const chromeStartMock = chromeServiceMock.createStartContract(); + setupMock.getStartServices.mockImplementation(() => { + return Promise.resolve([ + { + application: applicationStartMock, + chrome: chromeStartMock, + }, + {}, + {}, + ]) as any; + }); + + const workspacePlugin = new WorkspacePlugin(); + await workspacePlugin.setup(setupMock); + expect(setupMock.application.register).toBeCalledTimes(1); + expect(setupMock.workspaces.workspaceEnabled$.getValue()).toEqual(true); + expect(WorkspaceClientMock).toBeCalledTimes(1); + expect(workspaceClientMock.enterWorkspace).toBeCalledWith('workspaceId'); + expect(setupMock.getStartServices).toBeCalledTimes(1); + await new Promise((resolve: any) => setTimeout(resolve, 1000)); + expect(applicationStartMock.navigateToApp).toBeCalledWith(WORKSPACE_FATAL_ERROR_APP_ID, { + replace: true, + state: { + error: 'error', + }, + }); + windowSpy.mockRestore(); + }); + + it('#setup when workspace id is in url and enterWorkspace return success', async () => { + const windowSpy = jest.spyOn(window, 'window', 'get'); + windowSpy.mockImplementation( + () => + ({ + location: { + href: 'http://localhost/w/workspaceId/app', + }, + } as any) + ); + workspaceClientMock.enterWorkspace.mockResolvedValue({ + success: true, + error: 'error', + }); + const setupMock = coreMock.createSetup(); + const applicationStartMock = applicationServiceMock.createStartContract(); + let currentAppIdSubscriber: Subscriber | undefined; + setupMock.getStartServices.mockImplementation(() => { + return Promise.resolve([ + { + application: { + ...applicationStartMock, + currentAppId$: new Observable((subscriber) => { + currentAppIdSubscriber = subscriber; + }), + }, + }, + {}, + {}, + ]) as any; + }); + + const workspacePlugin = new WorkspacePlugin(); + await workspacePlugin.setup(setupMock); + await new Promise((resolve: any) => setTimeout(resolve, 0)); + currentAppIdSubscriber?.next(WORKSPACE_FATAL_ERROR_APP_ID); + await new Promise((resolve: any) => setTimeout(resolve, 0)); + expect(applicationStartMock.navigateToApp).toBeCalledWith(WORKSPACE_OVERVIEW_APP_ID); + windowSpy.mockRestore(); + }); +}); diff --git a/src/plugins/workspace/public/workspace_client.mock.ts b/src/plugins/workspace/public/workspace_client.mock.ts new file mode 100644 index 000000000000..2ceeae5627d1 --- /dev/null +++ b/src/plugins/workspace/public/workspace_client.mock.ts @@ -0,0 +1,25 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export const workspaceClientMock = { + init: jest.fn(), + enterWorkspace: jest.fn(), + getCurrentWorkspaceId: jest.fn(), + getCurrentWorkspace: jest.fn(), + create: jest.fn(), + delete: jest.fn(), + list: jest.fn(), + get: jest.fn(), + update: jest.fn(), + stop: jest.fn(), +}; + +export const WorkspaceClientMock = jest.fn(function () { + return workspaceClientMock; +}); + +jest.doMock('./workspace_client', () => ({ + WorkspaceClient: WorkspaceClientMock, +})); From c5a1a6466d8451ccb019351aeee43ef34785e46e Mon Sep 17 00:00:00 2001 From: tygao Date: Tue, 10 Oct 2023 15:13:58 +0800 Subject: [PATCH 03/22] Register Advance Settings, Data Source management,Index Pattern management and SavedObject management as standalone app, retire dashboard management (#208) * feat: init retire dashboard management Signed-off-by: tygao * move index pattern to Library (#91) * move index pattern to libaray Signed-off-by: Hailong Cui * Remove it from Dashboards management when workspace is on Signed-off-by: Hailong Cui --------- Signed-off-by: Hailong Cui index pattern always show under library Signed-off-by: Hailong Cui * functional test Signed-off-by: Hailong Cui * feat: move data source / advanced settings / saved objects management out of Dashboard management Signed-off-by: SuZhou-Joe * feat: update test Signed-off-by: SuZhou-Joe * feat: update snapshot Signed-off-by: SuZhou-Joe * feat: update snapshot Signed-off-by: SuZhou-Joe * fix: fix failed overview header ut Signed-off-by: tygao * fix: deeplink inside saved objects management page Signed-off-by: SuZhou-Joe * fix: unit test fail Signed-off-by: SuZhou-Joe * feat: add unit test for each page wrapper Signed-off-by: SuZhou-Joe * feat: some optimization Signed-off-by: SuZhou-Joe * remove management dependency Signed-off-by: Hailong Cui * test: update cypress config to use workspace branch Signed-off-by: tygao * Replace ManagementAppMountParams with AppMountParameters Signed-off-by: Hailong Cui --------- Signed-off-by: tygao Signed-off-by: Hailong Cui Signed-off-by: SuZhou-Joe Co-authored-by: Hailong Cui Co-authored-by: SuZhou-Joe --- .github/workflows/cypress_workflow.yml | 2 +- .../__snapshots__/page_wrapper.test.tsx.snap | 13 +++ .../page_wrapper/page_wrapper.test.tsx | 16 ++++ .../components/page_wrapper/page_wrapper.tsx | 4 +- .../mount_management_section.tsx | 22 ++--- .../__snapshots__/page_wrapper.test.tsx.snap | 13 +++ .../page_wrapper/page_wrapper.test.tsx | 16 ++++ .../components/page_wrapper/page_wrapper.tsx | 4 +- .../components/new_theme_modal.tsx | 4 +- .../opensearch_dashboards.json | 2 +- .../mount_management_section.tsx | 80 +++++++++++-------- .../index_pattern_management/public/plugin.ts | 11 +-- .../management_section/mount_section.tsx | 60 +++++++------- .../__snapshots__/header.test.tsx.snap | 4 +- .../objects_table/saved_objects_table.tsx | 6 +- .../__snapshots__/page_wrapper.test.tsx.snap | 13 +++ .../management_section/page_wrapper/index.ts | 6 ++ .../page_wrapper/page_wrapper.test.tsx | 16 ++++ .../page_wrapper/page_wrapper.tsx | 21 +++++ .../saved_objects_table_page.tsx | 6 -- .../saved_objects_management/public/plugin.ts | 7 +- 21 files changed, 207 insertions(+), 119 deletions(-) create mode 100644 src/plugins/advanced_settings/public/management_app/components/page_wrapper/__snapshots__/page_wrapper.test.tsx.snap create mode 100644 src/plugins/advanced_settings/public/management_app/components/page_wrapper/page_wrapper.test.tsx create mode 100644 src/plugins/data_source_management/public/components/page_wrapper/__snapshots__/page_wrapper.test.tsx.snap create mode 100644 src/plugins/data_source_management/public/components/page_wrapper/page_wrapper.test.tsx create mode 100644 src/plugins/saved_objects_management/public/management_section/page_wrapper/__snapshots__/page_wrapper.test.tsx.snap create mode 100644 src/plugins/saved_objects_management/public/management_section/page_wrapper/index.ts create mode 100644 src/plugins/saved_objects_management/public/management_section/page_wrapper/page_wrapper.test.tsx create mode 100644 src/plugins/saved_objects_management/public/management_section/page_wrapper/page_wrapper.tsx diff --git a/.github/workflows/cypress_workflow.yml b/.github/workflows/cypress_workflow.yml index 42894281fe4f..b8527eef1ed8 100644 --- a/.github/workflows/cypress_workflow.yml +++ b/.github/workflows/cypress_workflow.yml @@ -3,7 +3,7 @@ name: Run cypress tests # trigger on every PR for all branches on: pull_request: - branches: [ '**' ] + branches: ['**'] paths-ignore: - '**/*.md' diff --git a/src/plugins/advanced_settings/public/management_app/components/page_wrapper/__snapshots__/page_wrapper.test.tsx.snap b/src/plugins/advanced_settings/public/management_app/components/page_wrapper/__snapshots__/page_wrapper.test.tsx.snap new file mode 100644 index 000000000000..3c5257e2e8d1 --- /dev/null +++ b/src/plugins/advanced_settings/public/management_app/components/page_wrapper/__snapshots__/page_wrapper.test.tsx.snap @@ -0,0 +1,13 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`PageWrapper should render normally 1`] = ` +
+
+ Foo +
+
+`; diff --git a/src/plugins/advanced_settings/public/management_app/components/page_wrapper/page_wrapper.test.tsx b/src/plugins/advanced_settings/public/management_app/components/page_wrapper/page_wrapper.test.tsx new file mode 100644 index 000000000000..550eb3ee1cae --- /dev/null +++ b/src/plugins/advanced_settings/public/management_app/components/page_wrapper/page_wrapper.test.tsx @@ -0,0 +1,16 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import { PageWrapper } from './page_wrapper'; + +describe('PageWrapper', () => { + it('should render normally', async () => { + const { findByText, container } = render(Foo); + await findByText('Foo'); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/src/plugins/advanced_settings/public/management_app/components/page_wrapper/page_wrapper.tsx b/src/plugins/advanced_settings/public/management_app/components/page_wrapper/page_wrapper.tsx index e0b725edc42d..1b1949c334e4 100644 --- a/src/plugins/advanced_settings/public/management_app/components/page_wrapper/page_wrapper.tsx +++ b/src/plugins/advanced_settings/public/management_app/components/page_wrapper/page_wrapper.tsx @@ -6,10 +6,10 @@ import { EuiPageContent } from '@elastic/eui'; import React from 'react'; -export const PageWrapper = (props: { fullWidth?: boolean; children?: React.ReactChild }) => { +export const PageWrapper = (props: { children?: React.ReactChild }) => { return ( { - const wrapBreadcrumb = (item: ChromeBreadcrumb, scopedHistory: ScopedHistory) => ({ + chrome.setBreadcrumbs([ + ...crumb.map((item) => ({ ...item, - ...(item.href ? reactRouterNavigate(scopedHistory, item.href) : {}), - }); - - chrome.setBreadcrumbs([...crumbs.map((item) => wrapBreadcrumb(item, params.history))]); - }; - setBreadcrumbsScoped(crumb); + ...(item.href ? reactRouterNavigate(params.history, item.href) : {}), + })), + ]); const canSave = application.capabilities.advancedSettings.save as boolean; diff --git a/src/plugins/data_source_management/public/components/page_wrapper/__snapshots__/page_wrapper.test.tsx.snap b/src/plugins/data_source_management/public/components/page_wrapper/__snapshots__/page_wrapper.test.tsx.snap new file mode 100644 index 000000000000..3c5257e2e8d1 --- /dev/null +++ b/src/plugins/data_source_management/public/components/page_wrapper/__snapshots__/page_wrapper.test.tsx.snap @@ -0,0 +1,13 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`PageWrapper should render normally 1`] = ` +
+
+ Foo +
+
+`; diff --git a/src/plugins/data_source_management/public/components/page_wrapper/page_wrapper.test.tsx b/src/plugins/data_source_management/public/components/page_wrapper/page_wrapper.test.tsx new file mode 100644 index 000000000000..550eb3ee1cae --- /dev/null +++ b/src/plugins/data_source_management/public/components/page_wrapper/page_wrapper.test.tsx @@ -0,0 +1,16 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import { PageWrapper } from './page_wrapper'; + +describe('PageWrapper', () => { + it('should render normally', async () => { + const { findByText, container } = render(Foo); + await findByText('Foo'); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/src/plugins/data_source_management/public/components/page_wrapper/page_wrapper.tsx b/src/plugins/data_source_management/public/components/page_wrapper/page_wrapper.tsx index e0b725edc42d..1b1949c334e4 100644 --- a/src/plugins/data_source_management/public/components/page_wrapper/page_wrapper.tsx +++ b/src/plugins/data_source_management/public/components/page_wrapper/page_wrapper.tsx @@ -6,10 +6,10 @@ import { EuiPageContent } from '@elastic/eui'; import React from 'react'; -export const PageWrapper = (props: { fullWidth?: boolean; children?: React.ReactChild }) => { +export const PageWrapper = (props: { children?: React.ReactChild }) => { return ( = ({ addBasePath, onClose }) => { modes. You or your administrator can change to the previous theme by visiting {advancedSettingsLink}." values={{ advancedSettingsLink: ( - + , - params: ManagementAppMountParams, + params: AppMountParameters, getMlCardState: () => MlCardState ) { const [ @@ -75,6 +82,17 @@ export async function mountManagementSection( chrome.setBadge(readOnlyBadge); } + const setBreadcrumbsScope = (crumbs: ChromeBreadcrumb[] = [], appHistory?: ScopedHistory) => { + const wrapBreadcrumb = (item: ChromeBreadcrumb, scopedHistory: ScopedHistory) => ({ + ...item, + ...(item.href ? reactRouterNavigate(scopedHistory, item.href) : {}), + }); + + chrome.setBreadcrumbs([ + ...crumbs.map((item) => wrapBreadcrumb(item, appHistory || params.history)), + ]); + }; + const deps: IndexPatternManagmentContext = { chrome, application, @@ -86,40 +104,36 @@ export async function mountManagementSection( docLinks, data, indexPatternManagementStart: indexPatternManagementStart as IndexPatternManagementStart, - setBreadcrumbs: params.setBreadcrumbs, + setBreadcrumbs: setBreadcrumbsScope, getMlCardState, dataSourceEnabled, }; - const router = ( - - - - - - - - - - - - - - - - - ); - - const content = ( - - {router} - - ); - ReactDOM.render( - - {content} - , + + + + + + + + + + + + + + + + + + + + + + + + , params.element ); diff --git a/src/plugins/index_pattern_management/public/plugin.ts b/src/plugins/index_pattern_management/public/plugin.ts index cdac2c9d982d..758c81ee538f 100644 --- a/src/plugins/index_pattern_management/public/plugin.ts +++ b/src/plugins/index_pattern_management/public/plugin.ts @@ -47,12 +47,11 @@ import { IndexPatternManagementServiceStart, } from './service'; -import { ManagementAppMountParams, ManagementSetup } from '../../management/public'; +import { ManagementAppMountParams } from '../../management/public'; import { DEFAULT_APP_CATEGORIES } from '../../../core/public'; import { reactRouterNavigate } from '../../opensearch_dashboards_react/public'; export interface IndexPatternManagementSetupDependencies { - management: ManagementSetup; urlForwarding: UrlForwardingSetup; } @@ -85,14 +84,8 @@ export class IndexPatternManagementPlugin public setup( core: CoreSetup, - { management, urlForwarding }: IndexPatternManagementSetupDependencies + { urlForwarding }: IndexPatternManagementSetupDependencies ) { - const opensearchDashboardsSection = management.sections.section.opensearchDashboards; - - if (!opensearchDashboardsSection) { - throw new Error('`opensearchDashboards` management section not found.'); - } - const newAppPath = IPM_APP_ID; const legacyPatternsPath = 'management/opensearch-dashboards/index_patterns'; diff --git a/src/plugins/saved_objects_management/public/management_section/mount_section.tsx b/src/plugins/saved_objects_management/public/management_section/mount_section.tsx index c19f8ccdd96a..7cb48e090c27 100644 --- a/src/plugins/saved_objects_management/public/management_section/mount_section.tsx +++ b/src/plugins/saved_objects_management/public/management_section/mount_section.tsx @@ -35,6 +35,7 @@ import { I18nProvider } from '@osd/i18n/react'; import { EuiLoadingSpinner } from '@elastic/eui'; import { AppMountParameters, CoreSetup } from 'src/core/public'; import { ManagementAppMountParams } from 'src/plugins/management/public'; +import { PageWrapper } from './page_wrapper'; import { StartDependencies, SavedObjectsManagementPluginStart } from '../plugin'; import { ISavedObjectsManagementServiceRegistry } from '../services'; import { getAllowedTypes } from './../lib'; @@ -43,37 +44,27 @@ import { WORKSPACE_TYPE } from '../../../../core/public'; interface MountParams { core: CoreSetup; serviceRegistry: ISavedObjectsManagementServiceRegistry; - mountParams?: ManagementAppMountParams; appMountParams?: AppMountParameters; title: string; allowedObjectTypes?: string[]; - fullWidth?: boolean; } const SavedObjectsEditionPage = lazy(() => import('./saved_objects_edition_page')); const SavedObjectsTablePage = lazy(() => import('./saved_objects_table_page')); export const mountManagementSection = async ({ core, - mountParams, appMountParams, serviceRegistry, title, allowedObjectTypes, - fullWidth = true, }: MountParams) => { const [coreStart, { data, uiActions }, pluginStart] = await core.getStartServices(); - const usedMountParams = mountParams || appMountParams || ({} as ManagementAppMountParams); + const usedMountParams = appMountParams || ({} as ManagementAppMountParams); const { element, history } = usedMountParams; const { chrome } = coreStart; - const setBreadcrumbs = mountParams?.setBreadcrumbs || chrome.setBreadcrumbs; - let finalAllowedObjectTypes = allowedObjectTypes; - if (finalAllowedObjectTypes === undefined) { - /** - * Workspace needs to be filtered out since it is a concept with higher level than normal saved objects. - */ - finalAllowedObjectTypes = (await getAllowedTypes(coreStart.http)).filter( - (item) => item !== WORKSPACE_TYPE - ); + const setBreadcrumbs = chrome.setBreadcrumbs; + if (allowedObjectTypes === undefined) { + allowedObjectTypes = await getAllowedTypes(coreStart.http); } coreStart.chrome.docTitle.change(title); @@ -97,31 +88,34 @@ export const mountManagementSection = async ({ }> - + + + }> - + + + diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/header.test.tsx.snap b/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/header.test.tsx.snap index 6b9d1038f445..dace178024f2 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/header.test.tsx.snap +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/header.test.tsx.snap @@ -10,9 +10,7 @@ exports[`Header should render normally 1`] = ` grow={false} > -

- Saved Objects -

+

boolean; dateFormat: string; title: string; - fullWidth: boolean; } export interface SavedObjectsTableState { @@ -1080,10 +1079,7 @@ export class SavedObjectsTable extends Component + {this.renderFlyout()} {this.renderRelationships()} {this.renderDeleteConfirmModal()} diff --git a/src/plugins/saved_objects_management/public/management_section/page_wrapper/__snapshots__/page_wrapper.test.tsx.snap b/src/plugins/saved_objects_management/public/management_section/page_wrapper/__snapshots__/page_wrapper.test.tsx.snap new file mode 100644 index 000000000000..3c5257e2e8d1 --- /dev/null +++ b/src/plugins/saved_objects_management/public/management_section/page_wrapper/__snapshots__/page_wrapper.test.tsx.snap @@ -0,0 +1,13 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`PageWrapper should render normally 1`] = ` +
+
+ Foo +
+
+`; diff --git a/src/plugins/saved_objects_management/public/management_section/page_wrapper/index.ts b/src/plugins/saved_objects_management/public/management_section/page_wrapper/index.ts new file mode 100644 index 000000000000..3cf0cdd26c99 --- /dev/null +++ b/src/plugins/saved_objects_management/public/management_section/page_wrapper/index.ts @@ -0,0 +1,6 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export { PageWrapper } from './page_wrapper'; diff --git a/src/plugins/saved_objects_management/public/management_section/page_wrapper/page_wrapper.test.tsx b/src/plugins/saved_objects_management/public/management_section/page_wrapper/page_wrapper.test.tsx new file mode 100644 index 000000000000..550eb3ee1cae --- /dev/null +++ b/src/plugins/saved_objects_management/public/management_section/page_wrapper/page_wrapper.test.tsx @@ -0,0 +1,16 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import { PageWrapper } from './page_wrapper'; + +describe('PageWrapper', () => { + it('should render normally', async () => { + const { findByText, container } = render(Foo); + await findByText('Foo'); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/src/plugins/saved_objects_management/public/management_section/page_wrapper/page_wrapper.tsx b/src/plugins/saved_objects_management/public/management_section/page_wrapper/page_wrapper.tsx new file mode 100644 index 000000000000..1b1949c334e4 --- /dev/null +++ b/src/plugins/saved_objects_management/public/management_section/page_wrapper/page_wrapper.tsx @@ -0,0 +1,21 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { EuiPageContent } from '@elastic/eui'; +import React from 'react'; + +export const PageWrapper = (props: { children?: React.ReactChild }) => { + return ( + + ); +}; diff --git a/src/plugins/saved_objects_management/public/management_section/saved_objects_table_page.tsx b/src/plugins/saved_objects_management/public/management_section/saved_objects_table_page.tsx index d37ba4c7ee95..3e8121c7c2f5 100644 --- a/src/plugins/saved_objects_management/public/management_section/saved_objects_table_page.tsx +++ b/src/plugins/saved_objects_management/public/management_section/saved_objects_table_page.tsx @@ -50,7 +50,6 @@ const SavedObjectsTablePage = ({ namespaceRegistry, setBreadcrumbs, title, - fullWidth, }: { coreStart: CoreStart; dataStart: DataPublicPluginStart; @@ -61,7 +60,6 @@ const SavedObjectsTablePage = ({ namespaceRegistry: SavedObjectsManagementNamespaceServiceStart; setBreadcrumbs: (crumbs: ChromeBreadcrumb[]) => void; title: string; - fullWidth: boolean; }) => { const capabilities = coreStart.application.capabilities; const itemsPerPage = coreStart.uiSettings.get('savedObjects:perPage', 50); @@ -71,9 +69,6 @@ const SavedObjectsTablePage = ({ setBreadcrumbs([ { text: title, - /** - * There is no need to set a link for current bread crumb - */ href: undefined, }, ]); @@ -109,7 +104,6 @@ const SavedObjectsTablePage = ({ return inAppUrl ? Boolean(get(capabilities, inAppUrl.uiCapabilitiesPath)) : false; }} title={title} - fullWidth={fullWidth} /> ); }; diff --git a/src/plugins/saved_objects_management/public/plugin.ts b/src/plugins/saved_objects_management/public/plugin.ts index a7e1c0d1a1cb..dba28e05e98e 100644 --- a/src/plugins/saved_objects_management/public/plugin.ts +++ b/src/plugins/saved_objects_management/public/plugin.ts @@ -122,7 +122,6 @@ export class SavedObjectsManagementPlugin appMountParams, title, allowedObjectTypes, - fullWidth: false, }); }; @@ -130,9 +129,7 @@ export class SavedObjectsManagementPlugin * Register saved objects overview & saved search & saved query here */ core.application.register({ - id: 'objects_all', - appRoute: '/app/objects', - exactRoute: true, + id: 'objects', title: MANAGE_LIBRARY_TITLE_WORDINGS, order: 10000, category: DEFAULT_APP_CATEGORIES.opensearchDashboards, @@ -143,7 +140,6 @@ export class SavedObjectsManagementPlugin core.application.register({ id: 'objects_searches', - appRoute: '/app/objects/search', title: SAVED_SEARCHES_WORDINGS, order: 8000, category: DEFAULT_APP_CATEGORIES.opensearchDashboards, @@ -155,7 +151,6 @@ export class SavedObjectsManagementPlugin core.application.register({ id: 'objects_query', - appRoute: '/app/objects/query', title: SAVED_QUERIES_WORDINGS, order: 8001, category: DEFAULT_APP_CATEGORIES.opensearchDashboards, From 8e1310b1004ec8dbdf70ac5f57eed9aacd8027a8 Mon Sep 17 00:00:00 2001 From: SuZhou-Joe Date: Tue, 10 Oct 2023 15:23:53 +0800 Subject: [PATCH 04/22] feat: remove useless test object id Signed-off-by: SuZhou-Joe --- .../__snapshots__/workspace_fatal_error.test.tsx.snap | 2 -- .../components/workspace_fatal_error/workspace_fatal_error.tsx | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/plugins/workspace/public/components/workspace_fatal_error/__snapshots__/workspace_fatal_error.test.tsx.snap b/src/plugins/workspace/public/components/workspace_fatal_error/__snapshots__/workspace_fatal_error.test.tsx.snap index df744cde09c8..594066e959f7 100644 --- a/src/plugins/workspace/public/components/workspace_fatal_error/__snapshots__/workspace_fatal_error.test.tsx.snap +++ b/src/plugins/workspace/public/components/workspace_fatal_error/__snapshots__/workspace_fatal_error.test.tsx.snap @@ -57,7 +57,6 @@ exports[` render error with callout 1`] = ` > + + + +
+
+
+
+ + + + +`; + +exports[` render normally 1`] = ` +
+
+
+
+
+ +
+

+ + Something went wrong + +

+ +
+
+

+ + The workspace you want to go can not be found, try go back to home. + +

+
+ +
+
+
+ +
+
+
+
+
+
+
+`; diff --git a/src/plugins/workspace/public/components/workspace_fatal_error/workspace_fatal_error.test.tsx b/src/plugins/workspace/public/components/workspace_fatal_error/workspace_fatal_error.test.tsx new file mode 100644 index 000000000000..d98e0063dcfa --- /dev/null +++ b/src/plugins/workspace/public/components/workspace_fatal_error/workspace_fatal_error.test.tsx @@ -0,0 +1,71 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { IntlProvider } from 'react-intl'; +import { fireEvent, render, waitFor } from '@testing-library/react'; +import { WorkspaceFatalError } from './workspace_fatal_error'; +import { context } from '../../../../opensearch_dashboards_react/public'; +import { coreMock } from '../../../../../core/public/mocks'; + +describe('', () => { + it('render normally', async () => { + const { findByText, container } = render( + + + + ); + await findByText('Something went wrong'); + expect(container).toMatchSnapshot(); + }); + + it('render error with callout', async () => { + const { findByText, container } = render( + + + + ); + await findByText('errorInCallout'); + expect(container).toMatchSnapshot(); + }); + + it('click go back to home', async () => { + const { location } = window; + const setHrefSpy = jest.fn((href) => href); + if (window.location) { + // @ts-ignore + delete window.location; + } + window.location = {} as Location; + Object.defineProperty(window.location, 'href', { + get: () => 'http://localhost/', + set: setHrefSpy, + }); + const coreStartMock = coreMock.createStart(); + const { getByText } = render( + + + + + + ); + fireEvent.click(getByText('Go back to home')); + await waitFor( + () => { + expect(setHrefSpy).toBeCalledTimes(1); + }, + { + container: document.body, + } + ); + window.location = location; + }); +}); diff --git a/src/plugins/workspace/public/components/workspace_fatal_error/workspace_fatal_error.tsx b/src/plugins/workspace/public/components/workspace_fatal_error/workspace_fatal_error.tsx index 0509eb095bce..b1081e92237f 100644 --- a/src/plugins/workspace/public/components/workspace_fatal_error/workspace_fatal_error.tsx +++ b/src/plugins/workspace/public/components/workspace_fatal_error/workspace_fatal_error.tsx @@ -13,8 +13,9 @@ import { } from '@elastic/eui'; import React from 'react'; import { FormattedMessage } from '@osd/i18n/react'; +import { IBasePath } from 'opensearch-dashboards/public'; import { useOpenSearchDashboards } from '../../../../opensearch_dashboards_react/public'; -import { formatUrlWithWorkspaceId } from '../../utils'; +import { formatUrlWithWorkspaceId } from '../../../../../core/public/utils'; export function WorkspaceFatalError(props: { error?: string }) { const { @@ -24,7 +25,7 @@ export function WorkspaceFatalError(props: { error?: string }) { window.location.href = formatUrlWithWorkspaceId( application?.getUrlForApp('home') || '', '', - http?.basePath + http?.basePath as IBasePath ); }; return ( @@ -51,7 +52,7 @@ export function WorkspaceFatalError(props: { error?: string }) {

} actions={[ - + { + beforeEach(() => { + WorkspaceClientMock.mockClear(); + Object.values(workspaceClientMock).forEach((item) => item.mockClear()); + }); + it('#setup', async () => { + const setupMock = coreMock.createSetup(); + const workspacePlugin = new WorkspacePlugin(); + await workspacePlugin.setup(setupMock); + expect(setupMock.application.register).toBeCalledTimes(1); + expect(WorkspaceClientMock).toBeCalledTimes(1); + expect(workspaceClientMock.enterWorkspace).toBeCalledTimes(0); + }); + + it('#setup when workspace id is in url and enterWorkspace return error', async () => { + const windowSpy = jest.spyOn(window, 'window', 'get'); + windowSpy.mockImplementation( + () => + ({ + location: { + href: 'http://localhost/w/workspaceId/app', + }, + } as any) + ); + workspaceClientMock.enterWorkspace.mockResolvedValue({ + success: false, + error: 'error', + }); + const setupMock = coreMock.createSetup(); + const applicationStartMock = applicationServiceMock.createStartContract(); + const chromeStartMock = chromeServiceMock.createStartContract(); + setupMock.getStartServices.mockImplementation(() => { + return Promise.resolve([ + { + application: applicationStartMock, + chrome: chromeStartMock, + }, + {}, + {}, + ]) as any; + }); + + const workspacePlugin = new WorkspacePlugin(); + await workspacePlugin.setup(setupMock); + expect(setupMock.application.register).toBeCalledTimes(1); + expect(WorkspaceClientMock).toBeCalledTimes(1); + expect(workspaceClientMock.enterWorkspace).toBeCalledWith('workspaceId'); + expect(setupMock.getStartServices).toBeCalledTimes(1); + await waitFor( + () => { + expect(applicationStartMock.navigateToApp).toBeCalledWith(WORKSPACE_FATAL_ERROR_APP_ID, { + replace: true, + state: { + error: 'error', + }, + }); + }, + { + container: document.body, + } + ); + windowSpy.mockRestore(); + }); + + it('#setup when workspace id is in url and enterWorkspace return success', async () => { + const windowSpy = jest.spyOn(window, 'window', 'get'); + windowSpy.mockImplementation( + () => + ({ + location: { + href: 'http://localhost/w/workspaceId/app', + }, + } as any) + ); + workspaceClientMock.enterWorkspace.mockResolvedValue({ + success: true, + error: 'error', + }); + const setupMock = coreMock.createSetup(); + const applicationStartMock = applicationServiceMock.createStartContract(); + let currentAppIdSubscriber: Subscriber | undefined; + setupMock.getStartServices.mockImplementation(() => { + return Promise.resolve([ + { + application: { + ...applicationStartMock, + currentAppId$: new Observable((subscriber) => { + currentAppIdSubscriber = subscriber; + }), + }, + }, + {}, + {}, + ]) as any; + }); + + const workspacePlugin = new WorkspacePlugin(); + await workspacePlugin.setup(setupMock); + currentAppIdSubscriber?.next(WORKSPACE_FATAL_ERROR_APP_ID); + expect(applicationStartMock.navigateToApp).toBeCalledWith(WORKSPACE_OVERVIEW_APP_ID); + windowSpy.mockRestore(); + }); +}); diff --git a/src/plugins/workspace/public/workspace_client.mock.ts b/src/plugins/workspace/public/workspace_client.mock.ts new file mode 100644 index 000000000000..2ceeae5627d1 --- /dev/null +++ b/src/plugins/workspace/public/workspace_client.mock.ts @@ -0,0 +1,25 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export const workspaceClientMock = { + init: jest.fn(), + enterWorkspace: jest.fn(), + getCurrentWorkspaceId: jest.fn(), + getCurrentWorkspace: jest.fn(), + create: jest.fn(), + delete: jest.fn(), + list: jest.fn(), + get: jest.fn(), + update: jest.fn(), + stop: jest.fn(), +}; + +export const WorkspaceClientMock = jest.fn(function () { + return workspaceClientMock; +}); + +jest.doMock('./workspace_client', () => ({ + WorkspaceClient: WorkspaceClientMock, +})); diff --git a/src/plugins/workspace/public/workspace_client.test.ts b/src/plugins/workspace/public/workspace_client.test.ts new file mode 100644 index 000000000000..7d05c3f22458 --- /dev/null +++ b/src/plugins/workspace/public/workspace_client.test.ts @@ -0,0 +1,181 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { httpServiceMock, workspacesServiceMock } from '../../../core/public/mocks'; +import { WorkspaceClient } from './workspace_client'; + +const getWorkspaceClient = () => { + const httpSetupMock = httpServiceMock.createSetupContract(); + const workspaceMock = workspacesServiceMock.createSetupContract(); + return { + httpSetupMock, + workspaceMock, + workspaceClient: new WorkspaceClient(httpSetupMock, workspaceMock), + }; +}; + +describe('#WorkspaceClient', () => { + it('#init', async () => { + const { workspaceClient, httpSetupMock, workspaceMock } = getWorkspaceClient(); + await workspaceClient.init(); + expect(workspaceMock.initialized$.getValue()).toEqual(true); + expect(httpSetupMock.fetch).toBeCalledWith('/api/workspaces/_list', { + method: 'POST', + body: JSON.stringify({ + perPage: 999, + }), + }); + }); + + it('#enterWorkspace', async () => { + const { workspaceClient, httpSetupMock, workspaceMock } = getWorkspaceClient(); + httpSetupMock.fetch.mockResolvedValue({ + success: false, + }); + const result = await workspaceClient.enterWorkspace('foo'); + expect(result.success).toEqual(false); + httpSetupMock.fetch.mockResolvedValue({ + success: true, + }); + const successResult = await workspaceClient.enterWorkspace('foo'); + expect(workspaceMock.currentWorkspaceId$.getValue()).toEqual('foo'); + expect(httpSetupMock.fetch).toBeCalledWith('/api/workspaces/foo', { + method: 'GET', + }); + expect(successResult.success).toEqual(true); + }); + + it('#getCurrentWorkspaceId', async () => { + const { workspaceClient, httpSetupMock } = getWorkspaceClient(); + httpSetupMock.fetch.mockResolvedValue({ + success: true, + }); + await workspaceClient.enterWorkspace('foo'); + expect(await workspaceClient.getCurrentWorkspaceId()).toEqual({ + success: true, + result: 'foo', + }); + }); + + it('#getCurrentWorkspace', async () => { + const { workspaceClient, httpSetupMock } = getWorkspaceClient(); + httpSetupMock.fetch.mockResolvedValue({ + success: true, + result: { + name: 'foo', + }, + }); + await workspaceClient.enterWorkspace('foo'); + expect(await workspaceClient.getCurrentWorkspace()).toEqual({ + success: true, + result: { + name: 'foo', + }, + }); + }); + + it('#create', async () => { + const { workspaceClient, httpSetupMock } = getWorkspaceClient(); + httpSetupMock.fetch.mockResolvedValue({ + success: true, + result: { + name: 'foo', + workspaces: [], + }, + }); + await workspaceClient.create({ + name: 'foo', + }); + expect(httpSetupMock.fetch).toBeCalledWith('/api/workspaces', { + method: 'POST', + body: JSON.stringify({ + attributes: { + name: 'foo', + }, + }), + }); + expect(httpSetupMock.fetch).toBeCalledWith('/api/workspaces/_list', { + method: 'POST', + body: JSON.stringify({ + perPage: 999, + }), + }); + }); + + it('#delete', async () => { + const { workspaceClient, httpSetupMock } = getWorkspaceClient(); + httpSetupMock.fetch.mockResolvedValue({ + success: true, + result: { + name: 'foo', + workspaces: [], + }, + }); + await workspaceClient.delete('foo'); + expect(httpSetupMock.fetch).toBeCalledWith('/api/workspaces/foo', { + method: 'DELETE', + }); + expect(httpSetupMock.fetch).toBeCalledWith('/api/workspaces/_list', { + method: 'POST', + body: JSON.stringify({ + perPage: 999, + }), + }); + }); + + it('#list', async () => { + const { workspaceClient, httpSetupMock } = getWorkspaceClient(); + httpSetupMock.fetch.mockResolvedValue({ + success: true, + result: { + workspaces: [], + }, + }); + await workspaceClient.list({ + perPage: 999, + }); + expect(httpSetupMock.fetch).toBeCalledWith('/api/workspaces/_list', { + method: 'POST', + body: JSON.stringify({ + perPage: 999, + }), + }); + }); + + it('#get', async () => { + const { workspaceClient, httpSetupMock } = getWorkspaceClient(); + await workspaceClient.get('foo'); + expect(httpSetupMock.fetch).toBeCalledWith('/api/workspaces/foo', { + method: 'GET', + }); + }); + + it('#update', async () => { + const { workspaceClient, httpSetupMock } = getWorkspaceClient(); + httpSetupMock.fetch.mockResolvedValue({ + success: true, + result: { + workspaces: [], + }, + }); + await workspaceClient.update('foo', { + name: 'foo', + }); + expect(httpSetupMock.fetch).toBeCalledWith('/api/workspaces/foo', { + method: 'PUT', + body: JSON.stringify({ + attributes: { + name: 'foo', + }, + }), + }); + expect(httpSetupMock.fetch).toBeCalledWith('/api/workspaces/_list', { + method: 'POST', + body: JSON.stringify({ + perPage: 999, + }), + }); + }); +}); diff --git a/src/plugins/workspace/public/workspace_client.ts b/src/plugins/workspace/public/workspace_client.ts index 4dc023462fe1..9f1a106a0a50 100644 --- a/src/plugins/workspace/public/workspace_client.ts +++ b/src/plugins/workspace/public/workspace_client.ts @@ -2,6 +2,7 @@ * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 */ + import { HttpFetchError, HttpFetchOptions, @@ -102,7 +103,7 @@ export class WorkspaceClient { } }; - private getPath(path: Array): string { + private getPath(...path: Array): string { return [WORKSPACES_API_BASE_URL, join(...path)].filter((item) => item).join('/'); } @@ -179,7 +180,7 @@ export class WorkspaceClient { permissions: WorkspaceRoutePermissionItem[]; } ): Promise> { - const path = this.getPath([]); + const path = this.getPath(); const result = await this.safeFetch(path, { method: 'POST', @@ -202,7 +203,7 @@ export class WorkspaceClient { * @returns */ public async delete(id: string): Promise> { - const result = await this.safeFetch(this.getPath([id]), { method: 'DELETE' }); + const result = await this.safeFetch(this.getPath(id), { method: 'DELETE' }); if (result.success) { await this.updateWorkspaceList(); @@ -216,15 +217,15 @@ export class WorkspaceClient { * * @param {object} [options={}] * @property {string} options.search - * @property {string} options.search_fields - see OpenSearch Simple Query String + * @property {string} options.searchFields - see OpenSearch Simple Query String * Query field argument for more information * @property {integer} [options.page=1] - * @property {integer} [options.per_page=20] + * @property {integer} [options.perPage=20] * @property {array} options.fields * @property {string array} permissionModes * @returns A find result with workspaces matching the specified search. */ - public list = ( + public list( options?: WorkspaceFindOptions ): Promise< IResponse<{ @@ -233,13 +234,13 @@ export class WorkspaceClient { per_page: number; page: number; }> - > => { - const path = this.getPath(['_list']); + > { + const path = this.getPath('_list'); return this.safeFetch(path, { method: 'POST', body: JSON.stringify(options || {}), }); - }; + } /** * Fetches a single workspace @@ -247,8 +248,8 @@ export class WorkspaceClient { * @param {string} id * @returns The workspace for the given id. */ - public async get(id: string): Promise> { - const path = this.getPath([id]); + public get(id: string): Promise> { + const path = this.getPath(id); return this.safeFetch(path, { method: 'GET', }); @@ -269,7 +270,7 @@ export class WorkspaceClient { } > ): Promise> { - const path = this.getPath([id]); + const path = this.getPath(id); const body = { attributes, }; diff --git a/src/plugins/workspace/server/index.ts b/src/plugins/workspace/server/index.ts index 9447b7c6dc8c..fe44b4d71757 100644 --- a/src/plugins/workspace/server/index.ts +++ b/src/plugins/workspace/server/index.ts @@ -2,6 +2,7 @@ * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 */ + import { PluginConfigDescriptor, PluginInitializerContext } from '../../../core/server'; import { WorkspacePlugin } from './plugin'; import { configSchema } from '../config'; diff --git a/src/plugins/workspace/server/plugin.ts b/src/plugins/workspace/server/plugin.ts index 798ea103101f..411b8c586d4c 100644 --- a/src/plugins/workspace/server/plugin.ts +++ b/src/plugins/workspace/server/plugin.ts @@ -2,6 +2,7 @@ * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 */ + import { PluginInitializerContext, CoreSetup, @@ -20,6 +21,7 @@ import { SavedObjectsPermissionControlContract, } from './permission_control/client'; import { registerPermissionCheckRoutes } from './permission_control/routes'; +import { cleanWorkspaceId, getWorkspaceIdFromUrl } from '../../../core/server/utils'; export class WorkspacePlugin implements Plugin<{}, {}> { private readonly logger: Logger; @@ -31,12 +33,11 @@ export class WorkspacePlugin implements Plugin<{}, {}> { * Proxy all {basePath}/w/{workspaceId}{osdPath*} paths to {basePath}{osdPath*} */ setupDeps.http.registerOnPreRouting(async (request, response, toolkit) => { - const regexp = /\/w\/([^\/]*)/; - const matchedResult = request.url.pathname.match(regexp); + const workspaceId = getWorkspaceIdFromUrl(request.url.toString()); - if (matchedResult) { + if (workspaceId) { const requestUrl = new URL(request.url.toString()); - requestUrl.pathname = requestUrl.pathname.replace(regexp, ''); + requestUrl.pathname = cleanWorkspaceId(requestUrl.pathname); return toolkit.rewriteUrl(requestUrl.toString()); } return toolkit.next(); diff --git a/src/plugins/workspace/server/routes/index.ts b/src/plugins/workspace/server/routes/index.ts index 00ad05f3c961..2701c2a767ab 100644 --- a/src/plugins/workspace/server/routes/index.ts +++ b/src/plugins/workspace/server/routes/index.ts @@ -2,6 +2,7 @@ * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 */ + import { schema } from '@osd/config-schema'; import { ensureRawRequest } from '../../../../core/server'; diff --git a/src/plugins/workspace/server/types.ts b/src/plugins/workspace/server/types.ts index 2f46f5c7eb16..bf3337c4ae46 100644 --- a/src/plugins/workspace/server/types.ts +++ b/src/plugins/workspace/server/types.ts @@ -2,6 +2,7 @@ * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 */ + import { Logger, OpenSearchDashboardsRequest, diff --git a/src/plugins/workspace/server/workspace_client.ts b/src/plugins/workspace/server/workspace_client.ts index 56e40dfb3ebb..8fbb9ac7035e 100644 --- a/src/plugins/workspace/server/workspace_client.ts +++ b/src/plugins/workspace/server/workspace_client.ts @@ -2,6 +2,7 @@ * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 */ + import { i18n } from '@osd/i18n'; import { omit } from 'lodash'; import type { From 2653ab4c049b62e52755611bf94fb75cecde54c4 Mon Sep 17 00:00:00 2001 From: SuZhou-Joe Date: Wed, 11 Oct 2023 17:46:35 +0800 Subject: [PATCH 13/22] feat: optimization Signed-off-by: SuZhou-Joe --- src/core/public/utils/workspace.ts | 5 ++++- src/plugins/workspace/public/plugin.test.ts | 21 ++++++++++++------- .../workspace/public/workspace_client.ts | 4 ++-- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/core/public/utils/workspace.ts b/src/core/public/utils/workspace.ts index 9f6d7cdf1e24..f9c90994c8be 100644 --- a/src/core/public/utils/workspace.ts +++ b/src/core/public/utils/workspace.ts @@ -26,7 +26,10 @@ export const formatUrlWithWorkspaceId = ( /** * Patch workspace id into path */ - newUrl.pathname = basePath?.remove(newUrl.pathname) || ''; + if (basePath) { + newUrl.pathname = basePath.remove(newUrl.pathname); + } + if (workspaceId) { newUrl.pathname = `${WORKSPACE_PATH_PREFIX}/${workspaceId}${newUrl.pathname}`; } else { diff --git a/src/plugins/workspace/public/plugin.test.ts b/src/plugins/workspace/public/plugin.test.ts index e15b07ae9552..370f60caab52 100644 --- a/src/plugins/workspace/public/plugin.test.ts +++ b/src/plugins/workspace/public/plugin.test.ts @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { waitFor } from '@testing-library/dom'; import { workspaceClientMock, WorkspaceClientMock } from './workspace_client.mock'; import { applicationServiceMock, chromeServiceMock, coreMock } from '../../../core/public/mocks'; import { WorkspacePlugin } from './plugin'; @@ -57,13 +58,19 @@ describe('Workspace plugin', () => { expect(WorkspaceClientMock).toBeCalledTimes(1); expect(workspaceClientMock.enterWorkspace).toBeCalledWith('workspaceId'); expect(setupMock.getStartServices).toBeCalledTimes(1); - await new Promise((resolve: any) => setTimeout(resolve, 1000)); - expect(applicationStartMock.navigateToApp).toBeCalledWith(WORKSPACE_FATAL_ERROR_APP_ID, { - replace: true, - state: { - error: 'error', + await waitFor( + () => { + expect(applicationStartMock.navigateToApp).toBeCalledWith(WORKSPACE_FATAL_ERROR_APP_ID, { + replace: true, + state: { + error: 'error', + }, + }); }, - }); + { + container: document.body, + } + ); windowSpy.mockRestore(); }); @@ -101,9 +108,7 @@ describe('Workspace plugin', () => { const workspacePlugin = new WorkspacePlugin(); await workspacePlugin.setup(setupMock); - await new Promise((resolve: any) => setTimeout(resolve, 0)); currentAppIdSubscriber?.next(WORKSPACE_FATAL_ERROR_APP_ID); - await new Promise((resolve: any) => setTimeout(resolve, 0)); expect(applicationStartMock.navigateToApp).toBeCalledWith(WORKSPACE_OVERVIEW_APP_ID); windowSpy.mockRestore(); }); diff --git a/src/plugins/workspace/public/workspace_client.ts b/src/plugins/workspace/public/workspace_client.ts index 4dc023462fe1..f05a019733b6 100644 --- a/src/plugins/workspace/public/workspace_client.ts +++ b/src/plugins/workspace/public/workspace_client.ts @@ -216,10 +216,10 @@ export class WorkspaceClient { * * @param {object} [options={}] * @property {string} options.search - * @property {string} options.search_fields - see OpenSearch Simple Query String + * @property {string} options.searchFields - see OpenSearch Simple Query String * Query field argument for more information * @property {integer} [options.page=1] - * @property {integer} [options.per_page=20] + * @property {integer} [options.perPage=20] * @property {array} options.fields * @property {string array} permissionModes * @returns A find result with workspaces matching the specified search. From 1eb48dd366c4d01fff08fa264b2f28ac98260d13 Mon Sep 17 00:00:00 2001 From: SuZhou-Joe Date: Wed, 11 Oct 2023 17:52:31 +0800 Subject: [PATCH 14/22] feat: optimization Signed-off-by: SuZhou-Joe --- src/plugins/workspace/public/workspace_client.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/plugins/workspace/public/workspace_client.ts b/src/plugins/workspace/public/workspace_client.ts index f05a019733b6..693f40d0ab55 100644 --- a/src/plugins/workspace/public/workspace_client.ts +++ b/src/plugins/workspace/public/workspace_client.ts @@ -102,7 +102,7 @@ export class WorkspaceClient { } }; - private getPath(path: Array): string { + private getPath(...path: Array): string { return [WORKSPACES_API_BASE_URL, join(...path)].filter((item) => item).join('/'); } @@ -179,7 +179,7 @@ export class WorkspaceClient { permissions: WorkspaceRoutePermissionItem[]; } ): Promise> { - const path = this.getPath([]); + const path = this.getPath(); const result = await this.safeFetch(path, { method: 'POST', @@ -202,7 +202,7 @@ export class WorkspaceClient { * @returns */ public async delete(id: string): Promise> { - const result = await this.safeFetch(this.getPath([id]), { method: 'DELETE' }); + const result = await this.safeFetch(this.getPath(id), { method: 'DELETE' }); if (result.success) { await this.updateWorkspaceList(); @@ -234,7 +234,7 @@ export class WorkspaceClient { page: number; }> > => { - const path = this.getPath(['_list']); + const path = this.getPath('_list'); return this.safeFetch(path, { method: 'POST', body: JSON.stringify(options || {}), @@ -248,7 +248,7 @@ export class WorkspaceClient { * @returns The workspace for the given id. */ public async get(id: string): Promise> { - const path = this.getPath([id]); + const path = this.getPath(id); return this.safeFetch(path, { method: 'GET', }); @@ -269,7 +269,7 @@ export class WorkspaceClient { } > ): Promise> { - const path = this.getPath([id]); + const path = this.getPath(id); const body = { attributes, }; From a1d00ea73b384f1b9087a1fc2d2e59ccce8de35a Mon Sep 17 00:00:00 2001 From: SuZhou-Joe Date: Wed, 11 Oct 2023 18:32:25 +0800 Subject: [PATCH 15/22] feat: move workspace/utils to core Signed-off-by: SuZhou-Joe --- src/core/public/utils/index.ts | 3 ++- src/core/server/utils/index.ts | 1 + src/core/utils/index.ts | 1 + src/core/{public => }/utils/workspace.test.ts | 2 +- src/core/{public => }/utils/workspace.ts | 20 +++++++++---------- .../workspace_fatal_error.tsx | 3 ++- src/plugins/workspace/server/plugin.ts | 8 ++++---- 7 files changed, 20 insertions(+), 18 deletions(-) rename src/core/{public => }/utils/workspace.test.ts (95%) rename src/core/{public => }/utils/workspace.ts (63%) diff --git a/src/core/public/utils/index.ts b/src/core/public/utils/index.ts index b62b6ded5c83..871b71888294 100644 --- a/src/core/public/utils/index.ts +++ b/src/core/public/utils/index.ts @@ -31,10 +31,11 @@ export { shareWeakReplay } from './share_weak_replay'; export { Sha256 } from './crypto'; export { MountWrapper, mountReactNode } from './mount'; -export { getWorkspaceIdFromUrl, formatUrlWithWorkspaceId } from './workspace'; export { WORKSPACE_PATH_PREFIX, PUBLIC_WORKSPACE_ID, MANAGEMENT_WORKSPACE_ID, WORKSPACE_TYPE, + formatUrlWithWorkspaceId, + getWorkspaceIdFromUrl, } from '../../utils'; diff --git a/src/core/server/utils/index.ts b/src/core/server/utils/index.ts index d2c9e0086ad7..42b01e72b0d1 100644 --- a/src/core/server/utils/index.ts +++ b/src/core/server/utils/index.ts @@ -32,3 +32,4 @@ export * from './crypto'; export * from './from_root'; export * from './package_json'; export * from './streams'; +export { getWorkspaceIdFromUrl, cleanWorkspaceId } from '../../utils'; diff --git a/src/core/utils/index.ts b/src/core/utils/index.ts index 1b7d29406443..f2884c60581c 100644 --- a/src/core/utils/index.ts +++ b/src/core/utils/index.ts @@ -45,3 +45,4 @@ export { WORKSPACE_TYPE, PERSONAL_WORKSPACE_ID_PREFIX, } from './constants'; +export { getWorkspaceIdFromUrl, formatUrlWithWorkspaceId, cleanWorkspaceId } from './workspace'; diff --git a/src/core/public/utils/workspace.test.ts b/src/core/utils/workspace.test.ts similarity index 95% rename from src/core/public/utils/workspace.test.ts rename to src/core/utils/workspace.test.ts index a1cd4bf9a42e..7d2a1f700c5f 100644 --- a/src/core/public/utils/workspace.test.ts +++ b/src/core/utils/workspace.test.ts @@ -4,7 +4,7 @@ */ import { getWorkspaceIdFromUrl, formatUrlWithWorkspaceId } from './workspace'; -import { httpServiceMock } from '../mocks'; +import { httpServiceMock } from '../public/mocks'; describe('#getWorkspaceIdFromUrl', () => { it('return workspace when there is a match', () => { diff --git a/src/core/public/utils/workspace.ts b/src/core/utils/workspace.ts similarity index 63% rename from src/core/public/utils/workspace.ts rename to src/core/utils/workspace.ts index f9c90994c8be..46393f0bbb56 100644 --- a/src/core/public/utils/workspace.ts +++ b/src/core/utils/workspace.ts @@ -3,8 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { WORKSPACE_PATH_PREFIX } from './index'; -import { IBasePath } from '../index'; +import { WORKSPACE_PATH_PREFIX } from './constants'; +import { IBasePath } from '../public'; export const getWorkspaceIdFromUrl = (url: string): string => { const regexp = /\/w\/([^\/]*)/; @@ -17,23 +17,21 @@ export const getWorkspaceIdFromUrl = (url: string): string => { return ''; }; -export const formatUrlWithWorkspaceId = ( - url: string, - workspaceId: string, - basePath?: IBasePath -) => { +export const cleanWorkspaceId = (path: string) => { + return path.replace(/^\/w\/([^\/]*)/, ''); +}; + +export const formatUrlWithWorkspaceId = (url: string, workspaceId: string, basePath: IBasePath) => { const newUrl = new URL(url, window.location.href); /** * Patch workspace id into path */ - if (basePath) { - newUrl.pathname = basePath.remove(newUrl.pathname); - } + newUrl.pathname = basePath.remove(newUrl.pathname); if (workspaceId) { newUrl.pathname = `${WORKSPACE_PATH_PREFIX}/${workspaceId}${newUrl.pathname}`; } else { - newUrl.pathname = newUrl.pathname.replace(/^\/w\/([^\/]*)/, ''); + newUrl.pathname = cleanWorkspaceId(newUrl.pathname); } newUrl.pathname = diff --git a/src/plugins/workspace/public/components/workspace_fatal_error/workspace_fatal_error.tsx b/src/plugins/workspace/public/components/workspace_fatal_error/workspace_fatal_error.tsx index d4f187b78e56..b1081e92237f 100644 --- a/src/plugins/workspace/public/components/workspace_fatal_error/workspace_fatal_error.tsx +++ b/src/plugins/workspace/public/components/workspace_fatal_error/workspace_fatal_error.tsx @@ -13,6 +13,7 @@ import { } from '@elastic/eui'; import React from 'react'; import { FormattedMessage } from '@osd/i18n/react'; +import { IBasePath } from 'opensearch-dashboards/public'; import { useOpenSearchDashboards } from '../../../../opensearch_dashboards_react/public'; import { formatUrlWithWorkspaceId } from '../../../../../core/public/utils'; @@ -24,7 +25,7 @@ export function WorkspaceFatalError(props: { error?: string }) { window.location.href = formatUrlWithWorkspaceId( application?.getUrlForApp('home') || '', '', - http?.basePath + http?.basePath as IBasePath ); }; return ( diff --git a/src/plugins/workspace/server/plugin.ts b/src/plugins/workspace/server/plugin.ts index 798ea103101f..7c2f8f806c2a 100644 --- a/src/plugins/workspace/server/plugin.ts +++ b/src/plugins/workspace/server/plugin.ts @@ -20,6 +20,7 @@ import { SavedObjectsPermissionControlContract, } from './permission_control/client'; import { registerPermissionCheckRoutes } from './permission_control/routes'; +import { cleanWorkspaceId, getWorkspaceIdFromUrl } from '../../../core/server/utils'; export class WorkspacePlugin implements Plugin<{}, {}> { private readonly logger: Logger; @@ -31,12 +32,11 @@ export class WorkspacePlugin implements Plugin<{}, {}> { * Proxy all {basePath}/w/{workspaceId}{osdPath*} paths to {basePath}{osdPath*} */ setupDeps.http.registerOnPreRouting(async (request, response, toolkit) => { - const regexp = /\/w\/([^\/]*)/; - const matchedResult = request.url.pathname.match(regexp); + const workspaceId = getWorkspaceIdFromUrl(request.url.toString()); - if (matchedResult) { + if (workspaceId) { const requestUrl = new URL(request.url.toString()); - requestUrl.pathname = requestUrl.pathname.replace(regexp, ''); + requestUrl.pathname = cleanWorkspaceId(requestUrl.pathname); return toolkit.rewriteUrl(requestUrl.toString()); } return toolkit.next(); From 2da52460e24d384fd540b8d8e5ae015c9d9cb65f Mon Sep 17 00:00:00 2001 From: SuZhou-Joe Date: Thu, 12 Oct 2023 09:44:58 +0800 Subject: [PATCH 16/22] feat: update comment Signed-off-by: SuZhou-Joe --- src/plugins/workspace/public/workspace_client.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plugins/workspace/public/workspace_client.ts b/src/plugins/workspace/public/workspace_client.ts index 693f40d0ab55..de399950b97a 100644 --- a/src/plugins/workspace/public/workspace_client.ts +++ b/src/plugins/workspace/public/workspace_client.ts @@ -221,7 +221,6 @@ export class WorkspaceClient { * @property {integer} [options.page=1] * @property {integer} [options.perPage=20] * @property {array} options.fields - * @property {string array} permissionModes * @returns A find result with workspaces matching the specified search. */ public list = ( From 2b6764afea8c4e17c4c79f00ca0db5b249249359 Mon Sep 17 00:00:00 2001 From: SuZhou-Joe Date: Thu, 12 Oct 2023 10:47:29 +0800 Subject: [PATCH 17/22] feat: optimize code Signed-off-by: SuZhou-Joe --- src/plugins/workspace/public/workspace_client.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/workspace/public/workspace_client.ts b/src/plugins/workspace/public/workspace_client.ts index de399950b97a..2a49c6687e3f 100644 --- a/src/plugins/workspace/public/workspace_client.ts +++ b/src/plugins/workspace/public/workspace_client.ts @@ -223,7 +223,7 @@ export class WorkspaceClient { * @property {array} options.fields * @returns A find result with workspaces matching the specified search. */ - public list = ( + public list( options?: WorkspaceFindOptions ): Promise< IResponse<{ @@ -232,13 +232,13 @@ export class WorkspaceClient { per_page: number; page: number; }> - > => { + > { const path = this.getPath('_list'); return this.safeFetch(path, { method: 'POST', body: JSON.stringify(options || {}), }); - }; + } /** * Fetches a single workspace @@ -246,7 +246,7 @@ export class WorkspaceClient { * @param {string} id * @returns The workspace for the given id. */ - public async get(id: string): Promise> { + public get(id: string): Promise> { const path = this.getPath(id); return this.safeFetch(path, { method: 'GET', From bf64805ea1ddc1530ec56942cc21bae1989da52d Mon Sep 17 00:00:00 2001 From: SuZhou-Joe Date: Thu, 12 Oct 2023 10:49:31 +0800 Subject: [PATCH 18/22] feat: update unit test Signed-off-by: SuZhou-Joe --- .../workspace_fatal_error.test.tsx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/plugins/workspace/public/components/workspace_fatal_error/workspace_fatal_error.test.tsx b/src/plugins/workspace/public/components/workspace_fatal_error/workspace_fatal_error.test.tsx index 4b6f13e3a228..f681c1c9f4fd 100644 --- a/src/plugins/workspace/public/components/workspace_fatal_error/workspace_fatal_error.test.tsx +++ b/src/plugins/workspace/public/components/workspace_fatal_error/workspace_fatal_error.test.tsx @@ -5,7 +5,7 @@ import React from 'react'; import { IntlProvider } from 'react-intl'; -import { fireEvent, render } from '@testing-library/react'; +import { fireEvent, render, waitFor } from '@testing-library/react'; import { WorkspaceFatalError } from './workspace_fatal_error'; describe('', () => { @@ -47,7 +47,14 @@ describe('', () => { ); fireEvent.click(getByText('Go back to home')); - expect(setHrefSpy).toBeCalledTimes(1); + await waitFor( + () => { + expect(setHrefSpy).toBeCalledTimes(1); + }, + { + container: document.body, + } + ); window.location = location; }); }); From 6ea389117f49a3efc9d0ce2ceeb5041b687f5bee Mon Sep 17 00:00:00 2001 From: SuZhou-Joe Date: Thu, 12 Oct 2023 14:06:39 +0800 Subject: [PATCH 19/22] feat: optimization Signed-off-by: SuZhou-Joe --- src/core/utils/workspace.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/core/utils/workspace.ts b/src/core/utils/workspace.ts index 46393f0bbb56..c369f95d5817 100644 --- a/src/core/utils/workspace.ts +++ b/src/core/utils/workspace.ts @@ -34,10 +34,9 @@ export const formatUrlWithWorkspaceId = (url: string, workspaceId: string, baseP newUrl.pathname = cleanWorkspaceId(newUrl.pathname); } - newUrl.pathname = - basePath?.prepend(newUrl.pathname, { - withoutWorkspace: true, - }) || ''; + newUrl.pathname = basePath.prepend(newUrl.pathname, { + withoutWorkspace: true, + }); return newUrl.toString(); }; From d0ac2ec9328fd7601c688d30de0054322480de90 Mon Sep 17 00:00:00 2001 From: SuZhou-Joe Date: Thu, 12 Oct 2023 14:23:14 +0800 Subject: [PATCH 20/22] feat: add space under license Signed-off-by: SuZhou-Joe --- src/plugins/workspace/public/workspace_client.ts | 1 + src/plugins/workspace/server/index.ts | 1 + src/plugins/workspace/server/plugin.ts | 1 + src/plugins/workspace/server/routes/index.ts | 1 + src/plugins/workspace/server/types.ts | 1 + src/plugins/workspace/server/workspace_client.ts | 1 + 6 files changed, 6 insertions(+) diff --git a/src/plugins/workspace/public/workspace_client.ts b/src/plugins/workspace/public/workspace_client.ts index 2a49c6687e3f..6262225902e0 100644 --- a/src/plugins/workspace/public/workspace_client.ts +++ b/src/plugins/workspace/public/workspace_client.ts @@ -2,6 +2,7 @@ * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 */ + import { HttpFetchError, HttpFetchOptions, diff --git a/src/plugins/workspace/server/index.ts b/src/plugins/workspace/server/index.ts index 9447b7c6dc8c..fe44b4d71757 100644 --- a/src/plugins/workspace/server/index.ts +++ b/src/plugins/workspace/server/index.ts @@ -2,6 +2,7 @@ * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 */ + import { PluginConfigDescriptor, PluginInitializerContext } from '../../../core/server'; import { WorkspacePlugin } from './plugin'; import { configSchema } from '../config'; diff --git a/src/plugins/workspace/server/plugin.ts b/src/plugins/workspace/server/plugin.ts index 7c2f8f806c2a..411b8c586d4c 100644 --- a/src/plugins/workspace/server/plugin.ts +++ b/src/plugins/workspace/server/plugin.ts @@ -2,6 +2,7 @@ * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 */ + import { PluginInitializerContext, CoreSetup, diff --git a/src/plugins/workspace/server/routes/index.ts b/src/plugins/workspace/server/routes/index.ts index 00ad05f3c961..2701c2a767ab 100644 --- a/src/plugins/workspace/server/routes/index.ts +++ b/src/plugins/workspace/server/routes/index.ts @@ -2,6 +2,7 @@ * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 */ + import { schema } from '@osd/config-schema'; import { ensureRawRequest } from '../../../../core/server'; diff --git a/src/plugins/workspace/server/types.ts b/src/plugins/workspace/server/types.ts index 2f46f5c7eb16..bf3337c4ae46 100644 --- a/src/plugins/workspace/server/types.ts +++ b/src/plugins/workspace/server/types.ts @@ -2,6 +2,7 @@ * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 */ + import { Logger, OpenSearchDashboardsRequest, diff --git a/src/plugins/workspace/server/workspace_client.ts b/src/plugins/workspace/server/workspace_client.ts index 56e40dfb3ebb..8fbb9ac7035e 100644 --- a/src/plugins/workspace/server/workspace_client.ts +++ b/src/plugins/workspace/server/workspace_client.ts @@ -2,6 +2,7 @@ * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 */ + import { i18n } from '@osd/i18n'; import { omit } from 'lodash'; import type { From 640c45678ec154e3fc97607ec78a6f813d07d71a Mon Sep 17 00:00:00 2001 From: SuZhou-Joe Date: Thu, 12 Oct 2023 15:29:08 +0800 Subject: [PATCH 21/22] fix: unit test Signed-off-by: SuZhou-Joe --- .../workspace_fatal_error.test.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/plugins/workspace/public/components/workspace_fatal_error/workspace_fatal_error.test.tsx b/src/plugins/workspace/public/components/workspace_fatal_error/workspace_fatal_error.test.tsx index f681c1c9f4fd..d98e0063dcfa 100644 --- a/src/plugins/workspace/public/components/workspace_fatal_error/workspace_fatal_error.test.tsx +++ b/src/plugins/workspace/public/components/workspace_fatal_error/workspace_fatal_error.test.tsx @@ -7,6 +7,8 @@ import React from 'react'; import { IntlProvider } from 'react-intl'; import { fireEvent, render, waitFor } from '@testing-library/react'; import { WorkspaceFatalError } from './workspace_fatal_error'; +import { context } from '../../../../opensearch_dashboards_react/public'; +import { coreMock } from '../../../../../core/public/mocks'; describe('', () => { it('render normally', async () => { @@ -41,9 +43,18 @@ describe('', () => { get: () => 'http://localhost/', set: setHrefSpy, }); + const coreStartMock = coreMock.createStart(); const { getByText } = render( - + + + ); fireEvent.click(getByText('Go back to home')); From adc60295d24eb9cde0b59ffa3c79115f577fd38d Mon Sep 17 00:00:00 2001 From: SuZhou-Joe Date: Fri, 13 Oct 2023 11:37:17 +0800 Subject: [PATCH 22/22] feat: some sync Signed-off-by: SuZhou-Joe --- src/core/public/index.ts | 2 -- src/core/public/utils/workspace.ts | 15 --------------- src/core/public/workspace/index.ts | 1 + src/plugins/workspace/public/utils.ts | 27 +-------------------------- 4 files changed, 2 insertions(+), 43 deletions(-) delete mode 100644 src/core/public/utils/workspace.ts diff --git a/src/core/public/index.ts b/src/core/public/index.ts index 3a8358d0f2a0..1f26a26de8ef 100644 --- a/src/core/public/index.ts +++ b/src/core/public/index.ts @@ -358,5 +358,3 @@ export { MANAGEMENT_WORKSPACE_ID, WORKSPACE_TYPE, } from '../utils'; - -export { getWorkspaceIdFromUrl } from './utils'; diff --git a/src/core/public/utils/workspace.ts b/src/core/public/utils/workspace.ts deleted file mode 100644 index e93355aa00e3..000000000000 --- a/src/core/public/utils/workspace.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -export const getWorkspaceIdFromUrl = (url: string): string => { - const regexp = /\/w\/([^\/]*)/; - const urlObject = new URL(url); - const matchedResult = urlObject.pathname.match(regexp); - if (matchedResult) { - return matchedResult[1]; - } - - return ''; -}; diff --git a/src/core/public/workspace/index.ts b/src/core/public/workspace/index.ts index e46cac0b4b51..4b9b2c86f649 100644 --- a/src/core/public/workspace/index.ts +++ b/src/core/public/workspace/index.ts @@ -2,4 +2,5 @@ * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 */ + export { WorkspacesStart, WorkspacesService, WorkspacesSetup } from './workspaces_service'; diff --git a/src/plugins/workspace/public/utils.ts b/src/plugins/workspace/public/utils.ts index 09528c2b080f..f7c59dbfc53c 100644 --- a/src/plugins/workspace/public/utils.ts +++ b/src/plugins/workspace/public/utils.ts @@ -3,32 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { WORKSPACE_PATH_PREFIX } from '../../../core/public/utils'; -import { AppCategory, IBasePath } from '../../../core/public'; - -export const formatUrlWithWorkspaceId = ( - url: string, - workspaceId: string, - basePath?: IBasePath -) => { - const newUrl = new URL(url, window.location.href); - /** - * Patch workspace id into path - */ - newUrl.pathname = basePath?.remove(newUrl.pathname) || ''; - if (workspaceId) { - newUrl.pathname = `${WORKSPACE_PATH_PREFIX}/${workspaceId}${newUrl.pathname}`; - } else { - newUrl.pathname = newUrl.pathname.replace(/^\/w\/([^\/]*)/, ''); - } - - newUrl.pathname = - basePath?.prepend(newUrl.pathname, { - withoutWorkspace: true, - }) || ''; - - return newUrl.toString(); -}; +import { AppCategory } from '../../../core/public'; /** * Given a list of feature config, check if a feature matches config