From efe62545b48e356cf532995541ad048522fb00f6 Mon Sep 17 00:00:00 2001 From: SuZhou-Joe Date: Mon, 25 Mar 2024 09:52:47 +0800 Subject: [PATCH] [Workspace] Add a workspace client in workspace plugin (#6094) (#308) * feat: add comment * feat: update unit test * feat: add CHANGELOG * feat: optimize comment * feat: optimize comment * feat: optimize code * feat: optimize code --------- Signed-off-by: SuZhou-Joe --- .../workspace/public/workspace_client.test.ts | 36 ++++++++++- .../workspace/public/workspace_client.ts | 60 +++++++++++++++---- 2 files changed, 80 insertions(+), 16 deletions(-) diff --git a/src/plugins/workspace/public/workspace_client.test.ts b/src/plugins/workspace/public/workspace_client.test.ts index 5ffcf9cfb6f8..ce51ddc1e501 100644 --- a/src/plugins/workspace/public/workspace_client.test.ts +++ b/src/plugins/workspace/public/workspace_client.test.ts @@ -54,7 +54,7 @@ describe('#WorkspaceClient', () => { success: true, }); await workspaceClient.enterWorkspace('foo'); - expect(await workspaceClient.getCurrentWorkspaceId()).toEqual({ + expect(workspaceClient.getCurrentWorkspaceId()).toEqual({ success: true, result: 'foo', }); @@ -188,11 +188,15 @@ describe('#WorkspaceClient', () => { }); it('#update', async () => { - const { workspaceClient, httpSetupMock } = getWorkspaceClient(); + const { workspaceClient, httpSetupMock, workspaceMock } = getWorkspaceClient(); httpSetupMock.fetch.mockResolvedValue({ success: true, result: { - workspaces: [], + workspaces: [ + { + id: 'foo', + }, + ], }, }); await workspaceClient.update('foo', { @@ -206,6 +210,11 @@ describe('#WorkspaceClient', () => { }, }), }); + expect(workspaceMock.workspaceList$.getValue()).toEqual([ + { + id: 'foo', + }, + ]); expect(httpSetupMock.fetch).toBeCalledWith('/api/workspaces/_list', { method: 'POST', body: JSON.stringify({ @@ -247,4 +256,25 @@ describe('#WorkspaceClient', () => { }), }); }); + it('#update with list gives error', async () => { + const { workspaceClient, httpSetupMock, workspaceMock } = getWorkspaceClient(); + let callTimes = 0; + httpSetupMock.fetch.mockImplementation(async () => { + callTimes++; + if (callTimes > 1) { + return { + success: false, + error: 'Something went wrong', + }; + } + + return { + success: true, + }; + }); + await workspaceClient.update('foo', { + name: 'foo', + }); + expect(workspaceMock.workspaceList$.getValue()).toEqual([]); + }); }); diff --git a/src/plugins/workspace/public/workspace_client.ts b/src/plugins/workspace/public/workspace_client.ts index b7800563ce43..31a08b6ae9c2 100644 --- a/src/plugins/workspace/public/workspace_client.ts +++ b/src/plugins/workspace/public/workspace_client.ts @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { i18n } from '@osd/i18n'; import { HttpFetchError, HttpFetchOptions, @@ -64,7 +65,9 @@ export class WorkspaceClient { } /** - * Initialize workspace list + * Initialize workspace list: + * 1. Retrieve the list of workspaces + * 2. Change the initialized flag to true */ public async init() { await this.updateWorkspaceList(); @@ -72,7 +75,10 @@ export class WorkspaceClient { } /** - * Add a non-throw-error fetch method for internal use. + * Add a non-throw-error fetch method, + * so that consumers only need to care about + * if the success is false instead of wrapping the call with a try catch + * and judge the error both in catch clause and if(!success) cluase. */ private safeFetch = async ( path: string, @@ -102,10 +108,19 @@ export class WorkspaceClient { } }; + /** + * Filter empty sub path and join all of the sub paths into a standard http path + * + * @param path + * @returns path + */ private getPath(...path: Array): string { return [WORKSPACES_API_BASE_URL, join(...path)].filter((item) => item).join('/'); } + /** + * Fetch latest list of workspaces and update workspaceList$ to notify subscriptions + */ private async updateWorkspaceList(): Promise { const result = await this.list({ perPage: 999, @@ -113,9 +128,18 @@ export class WorkspaceClient { if (result?.success) { this.workspaces.workspaceList$.next(result.result.workspaces); + } else { + this.workspaces.workspaceList$.next([]); } } + /** + * This method will check if a valid workspace can be found by the given workspace id, + * If so, perform a side effect of updating the core.workspace.currentWorkspaceId$. + * + * @param id workspace id + * @returns {Promise>} result for this operation + */ public async enterWorkspace(id: string): Promise> { const workspaceResp = await this.get(id); if (workspaceResp.success) { @@ -129,12 +153,17 @@ export class WorkspaceClient { } } - public async getCurrentWorkspaceId(): Promise> { + /** + * A bypass layer to get current workspace id + */ + public getCurrentWorkspaceId(): IResponse { const currentWorkspaceId = this.workspaces.currentWorkspaceId$.getValue(); if (!currentWorkspaceId) { return { success: false, - error: 'You are not in any workspace yet.', + error: i18n.translate('workspace.error.notInWorkspace', { + defaultMessage: 'You are not in any workspace yet.', + }), }; } @@ -144,8 +173,11 @@ export class WorkspaceClient { }; } + /** + * Do a find in the latest workspace list with current workspace id + */ public async getCurrentWorkspace(): Promise> { - const currentWorkspaceIdResp = await this.getCurrentWorkspaceId(); + const currentWorkspaceIdResp = this.getCurrentWorkspaceId(); if (currentWorkspaceIdResp.success) { const currentWorkspaceResp = await this.get(currentWorkspaceIdResp.result); return currentWorkspaceResp; @@ -155,15 +187,16 @@ export class WorkspaceClient { } /** - * Persists an workspace + * Create a workspace * * @param attributes - * @returns + * @param permissions + * @returns {Promise>>} id of the new created workspace */ public async create( attributes: Omit, permissions?: WorkspacePermissionItem[] - ): Promise> { + ): Promise>> { const path = this.getPath(); const result = await this.safeFetch(path, { @@ -182,10 +215,10 @@ export class WorkspaceClient { } /** - * Deletes a workspace + * Deletes a workspace by workspace id * * @param id - * @returns + * @returns {Promise>} result for this operation */ public async delete(id: string): Promise> { const result = await this.safeFetch(this.getPath(id), { method: 'DELETE' }); @@ -227,10 +260,10 @@ export class WorkspaceClient { } /** - * Fetches a single workspace + * Fetches a single workspace by a workspace id * * @param {string} id - * @returns The workspace for the given id. + * @returns {Promise>} The metadata of the workspace for the given id. */ public get(id: string): Promise> { const path = this.getPath(id); @@ -244,7 +277,8 @@ export class WorkspaceClient { * * @param {string} id * @param {object} attributes - * @returns + * @param {WorkspacePermissionItem[]} permissions + * @returns {Promise>} result for this operation */ public async update( id: string,