Skip to content

Commit

Permalink
[Workspace] Add a workspace client in workspace plugin (opensearch-pr…
Browse files Browse the repository at this point in the history
…oject#6094) (opensearch-project#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 <suzhou@amazon.com>
  • Loading branch information
SuZhou-Joe authored Mar 25, 2024
1 parent 0f44eff commit efe6254
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 16 deletions.
36 changes: 33 additions & 3 deletions src/plugins/workspace/public/workspace_client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
});
Expand Down Expand Up @@ -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', {
Expand All @@ -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({
Expand Down Expand Up @@ -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([]);
});
});
60 changes: 47 additions & 13 deletions src/plugins/workspace/public/workspace_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

import { i18n } from '@osd/i18n';
import {
HttpFetchError,
HttpFetchOptions,
Expand Down Expand Up @@ -64,15 +65,20 @@ 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();
this.workspaces.initialized$.next(true);
}

/**
* 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 <T = any>(
path: string,
Expand Down Expand Up @@ -102,20 +108,38 @@ 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 | undefined>): 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<void> {
const result = await this.list({
perPage: 999,
});

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<IResponse<null>>} result for this operation
*/
public async enterWorkspace(id: string): Promise<IResponse<null>> {
const workspaceResp = await this.get(id);
if (workspaceResp.success) {
Expand All @@ -129,12 +153,17 @@ export class WorkspaceClient {
}
}

public async getCurrentWorkspaceId(): Promise<IResponse<WorkspaceAttribute['id']>> {
/**
* A bypass layer to get current workspace id
*/
public getCurrentWorkspaceId(): IResponse<WorkspaceAttribute['id']> {
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.',
}),
};
}

Expand All @@ -144,8 +173,11 @@ export class WorkspaceClient {
};
}

/**
* Do a find in the latest workspace list with current workspace id
*/
public async getCurrentWorkspace(): Promise<IResponse<WorkspaceAttribute>> {
const currentWorkspaceIdResp = await this.getCurrentWorkspaceId();
const currentWorkspaceIdResp = this.getCurrentWorkspaceId();
if (currentWorkspaceIdResp.success) {
const currentWorkspaceResp = await this.get(currentWorkspaceIdResp.result);
return currentWorkspaceResp;
Expand All @@ -155,15 +187,16 @@ export class WorkspaceClient {
}

/**
* Persists an workspace
* Create a workspace
*
* @param attributes
* @returns
* @param permissions
* @returns {Promise<IResponse<Pick<WorkspaceAttribute, 'id'>>>} id of the new created workspace
*/
public async create(
attributes: Omit<WorkspaceAttribute, 'id'>,
permissions?: WorkspacePermissionItem[]
): Promise<IResponse<WorkspaceAttribute>> {
): Promise<IResponse<Pick<WorkspaceAttribute, 'id'>>> {
const path = this.getPath();

const result = await this.safeFetch<WorkspaceAttribute>(path, {
Expand All @@ -182,10 +215,10 @@ export class WorkspaceClient {
}

/**
* Deletes a workspace
* Deletes a workspace by workspace id
*
* @param id
* @returns
* @returns {Promise<IResponse<null>>} result for this operation
*/
public async delete(id: string): Promise<IResponse<null>> {
const result = await this.safeFetch<null>(this.getPath(id), { method: 'DELETE' });
Expand Down Expand Up @@ -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<IResponse<WorkspaceAttribute>>} The metadata of the workspace for the given id.
*/
public get(id: string): Promise<IResponse<WorkspaceAttribute>> {
const path = this.getPath(id);
Expand All @@ -244,7 +277,8 @@ export class WorkspaceClient {
*
* @param {string} id
* @param {object} attributes
* @returns
* @param {WorkspacePermissionItem[]} permissions
* @returns {Promise<IResponse<boolean>>} result for this operation
*/
public async update(
id: string,
Expand Down

0 comments on commit efe6254

Please sign in to comment.