Skip to content

Commit

Permalink
[Workspace][Feature]Setup workspace skeleton and implement basic CRUD…
Browse files Browse the repository at this point in the history
… API (opensearch-project#5075)

* feature: setup workspace skeleton and implement basic CRUD API on workspace

Signed-off-by: Zhou Su <suzhou@dev-dsk-suzhou-2a-8ce7a7a7.us-west-2.amazon.com>

* feat: remove useless required plugins and logger typo

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

* feat: setup public side skeleton

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

* temp: add unit test

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

* feat: add function test for workspace CRUD routes

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

* feat: use saved objects client instead of internal repository

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

* feat: update CHANGELOG

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

* feat: exclude permission check wrapper

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

* feat: add integration test

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

* feat: add configuration

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

* feat: enable workspace flag when run workspace related test

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

* feat: optimization according to PR comments

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

* feat: add JSDoc for workspace client

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

* Update src/plugins/workspace/server/integration_tests/routes.test.ts

Co-authored-by: Josh Romero <rmerqg@amazon.com>
Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

* feat: remove hard-coded delay

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

* feat: optimize unit test

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

* fix: unit test

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

* feat: Only allow workspace CRUD APIs to modify workspace metadata.

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

* feat: add integration test for new changes

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

---------

Signed-off-by: Zhou Su <suzhou@dev-dsk-suzhou-2a-8ce7a7a7.us-west-2.amazon.com>
Signed-off-by: SuZhou-Joe <suzhou@amazon.com>
Co-authored-by: Zhou Su <suzhou@dev-dsk-suzhou-2a-8ce7a7a7.us-west-2.amazon.com>
Co-authored-by: Josh Romero <rmerqg@amazon.com>
  • Loading branch information
3 people committed Oct 31, 2023
1 parent 8ff9e88 commit aeb826b
Show file tree
Hide file tree
Showing 10 changed files with 203 additions and 56 deletions.
8 changes: 5 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,11 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
- [Decouple] Allow plugin manifest config to define semver compatible OpenSearch plugin and verify if it is installed on the cluster([#4612](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4612))
- [Advanced Settings] Consolidate settings into new "Appearance" category and add category IDs ([#4845](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4845))
- Adds Data explorer framework and implements Discover using it ([#4806](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4806))
- [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/))
- [Workspace] Setup workspace skeleton and implement basic CRUD API ([#5075](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5075/))
- [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))
- [Workspace] Setup workspace skeleton and implement basic CRUD API ([#5075](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5075))

### 🐛 Bug Fixes

Expand Down
2 changes: 1 addition & 1 deletion src/plugins/workspace/opensearch_dashboards.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "opensearchDashboards",
"server": true,
"ui": true,
"requiredPlugins": [],
"requiredPlugins": ["savedObjects"],
"optionalPlugins": [],
"requiredBundles": ["opensearchDashboardsReact"]
}
12 changes: 11 additions & 1 deletion src/plugins/workspace/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,21 @@
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { combineLatest } from 'rxjs';
import { map } from 'rxjs/operators';
import { i18n } from '@osd/i18n';
import { featureMatchesConfig } from './utils';
import { AppMountParameters, AppNavLinkStatus, ChromeNavLink, CoreSetup, CoreStart, Plugin, WorkspaceObject, DEFAULT_APP_CATEGORIES } from '../../../core/public';
import {
AppMountParameters,
AppNavLinkStatus,
ChromeNavLink,
CoreSetup,
CoreStart,
Plugin,
WorkspaceObject,
DEFAULT_APP_CATEGORIES,
} from '../../../core/public';
import { WORKSPACE_FATAL_ERROR_APP_ID, WORKSPACE_OVERVIEW_APP_ID } from '../common/constants';
import { getWorkspaceIdFromUrl } from '../../../core/public/utils';
import { Services } from './types';
Expand Down
97 changes: 87 additions & 10 deletions src/plugins/workspace/server/integration_tests/routes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@
*/

import { WorkspaceAttribute } from 'src/core/types';
import { omit } from 'lodash';
import * as osdTestServer from '../../../../core/test_helpers/osd_server';
import { WORKSPACE_TYPE } from '../../../../core/server';

const omitId = <T extends { id?: string }>(object: T): Omit<T, 'id'> => {
const { id, ...others } = object;
return others;
};

const testWorkspace: WorkspaceAttribute = {
id: 'fake_id',
name: 'test_workspace',
Expand All @@ -32,12 +36,12 @@ describe('workspace service', () => {
opensearchServer = await startOpenSearch();
const startOSDResp = await startOpenSearchDashboards();
root = startOSDResp.root;
}, 30000);
});
afterAll(async () => {
await root.shutdown();
await opensearchServer.stop();
});
describe('Workspace CRUD apis', () => {
describe('Workspace CRUD APIs', () => {
afterEach(async () => {
const listResult = await osdTestServer.request
.post(root, `/api/workspaces/_list`)
Expand All @@ -47,6 +51,7 @@ describe('workspace service', () => {
.expect(200);
await Promise.all(
listResult.body.result.workspaces.map((item: WorkspaceAttribute) =>
// TODO delete through opensearch
// this will delete reserved workspace
osdTestServer.request
.delete(root, `/api/saved_objects/${WORKSPACE_TYPE}/${item.id}`)
Expand All @@ -65,7 +70,7 @@ describe('workspace service', () => {
const result: any = await osdTestServer.request
.post(root, `/api/workspaces`)
.send({
attributes: omit(testWorkspace, 'id'),
attributes: omitId(testWorkspace),
})
.expect(200);

Expand All @@ -76,7 +81,7 @@ describe('workspace service', () => {
const result = await osdTestServer.request
.post(root, `/api/workspaces`)
.send({
attributes: omit(testWorkspace, 'id'),
attributes: omitId(testWorkspace),
})
.expect(200);

Expand All @@ -90,15 +95,15 @@ describe('workspace service', () => {
const result: any = await osdTestServer.request
.post(root, `/api/workspaces`)
.send({
attributes: omit(testWorkspace, 'id'),
attributes: omitId(testWorkspace),
})
.expect(200);

await osdTestServer.request
.put(root, `/api/workspaces/${result.body.result.id}`)
.send({
attributes: {
...omit(testWorkspace, 'id'),
...omitId(testWorkspace),
name: 'updated',
},
})
Expand All @@ -116,7 +121,7 @@ describe('workspace service', () => {
const result: any = await osdTestServer.request
.post(root, `/api/workspaces`)
.send({
attributes: omit(testWorkspace, 'id'),
attributes: omitId(testWorkspace),
})
.expect(200);

Expand Down Expand Up @@ -151,7 +156,7 @@ describe('workspace service', () => {
const result: any = await osdTestServer.request
.post(root, `/api/workspaces`)
.send({
attributes: omit(reservedWorkspace, 'id'),
attributes: omitId(reservedWorkspace),
})
.expect(200);

Expand All @@ -168,7 +173,17 @@ describe('workspace service', () => {
await osdTestServer.request
.post(root, `/api/workspaces`)
.send({
attributes: omit(testWorkspace, 'id'),
attributes: omitId(testWorkspace),
})
.expect(200);

await osdTestServer.request
.post(root, `/api/workspaces`)
.send({
attributes: {
...omitId(testWorkspace),
name: 'another test workspace',
},
})
.expect(200);

Expand All @@ -178,6 +193,68 @@ describe('workspace service', () => {
page: 1,
})
.expect(200);
expect(listResult.body.result.total).toEqual(4);
});
it('unable to perform operations on workspace by calling saved objects APIs', async () => {
const result = await osdTestServer.request
.post(root, `/api/workspaces`)
.send({
attributes: omitId(testWorkspace),
})
.expect(200);

/**
* Can not create workspace by saved objects API
*/
await osdTestServer.request
.post(root, `/api/saved_objects/workspace`)
.send({
attributes: {
...omitId(testWorkspace),
name: 'another test workspace',
},
})
.expect(400);

/**
* Can not get workspace by saved objects API
*/
await osdTestServer.request
.get(root, `/api/saved_objects/workspace/${result.body.result.id}`)
.expect(404);

/**
* Can not update workspace by saved objects API
*/
await osdTestServer.request
.put(root, `/api/saved_objects/workspace/${result.body.result.id}`)
.send({
attributes: {
name: 'another test workspace',
},
})
.expect(404);

/**
* Can not delete workspace by saved objects API
*/
await osdTestServer.request
.delete(root, `/api/saved_objects/workspace/${result.body.result.id}`)
.expect(404);

/**
* Can not find workspace by saved objects API
*/
const findResult = await osdTestServer.request
.get(root, `/api/saved_objects/_find?type=workspace`)
.expect(200);
const listResult = await osdTestServer.request
.post(root, `/api/workspaces/_list`)
.send({
page: 1,
})
.expect(200);
expect(findResult.body.total).toEqual(0);
expect(listResult.body.result.total).toEqual(3);
});
});
Expand Down
8 changes: 4 additions & 4 deletions src/plugins/workspace/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ import {
Logger,
CoreStart,
} from '../../../core/server';
import { IWorkspaceDBImpl } from './types';
import { IWorkspaceClientImpl } from './types';
import { WorkspaceClientWithSavedObject } from './workspace_client';
import { registerRoutes } from './routes';
import { cleanWorkspaceId, getWorkspaceIdFromUrl } from '../../../core/server/utils';

export class WorkspacePlugin implements Plugin<{}, {}> {
private readonly logger: Logger;
private client?: IWorkspaceDBImpl;
private client?: IWorkspaceClientImpl;

private proxyWorkspaceTrafficToRealHandler(setupDeps: CoreSetup) {
/**
Expand Down Expand Up @@ -51,7 +51,7 @@ export class WorkspacePlugin implements Plugin<{}, {}> {
registerRoutes({
http: core.http,
logger: this.logger,
client: this.client as IWorkspaceDBImpl,
client: this.client as IWorkspaceClientImpl,
});

return {
Expand All @@ -64,7 +64,7 @@ export class WorkspacePlugin implements Plugin<{}, {}> {
this.client?.setSavedObjects(core.savedObjects);

return {
client: this.client as IWorkspaceDBImpl,
client: this.client as IWorkspaceClientImpl,
};
}

Expand Down
23 changes: 5 additions & 18 deletions src/plugins/workspace/server/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@
*/

import { schema } from '@osd/config-schema';

import { CoreSetup, Logger } from '../../../../core/server';
import { IWorkspaceDBImpl } from '../types';
import { IWorkspaceClientImpl } from '../types';

const WORKSPACES_API_BASE_URL = '/api/workspaces';

Expand All @@ -25,7 +24,7 @@ export function registerRoutes({
logger,
http,
}: {
client: IWorkspaceDBImpl;
client: IWorkspaceClientImpl;
logger: Logger;
http: CoreSetup['http'];
}) {
Expand Down Expand Up @@ -80,17 +79,9 @@ export function registerRoutes({
},
id
);
if (!result.success) {
return res.ok({ body: result });
}

return res.ok({
body: {
...result,
result: {
...result.result,
},
},
body: result,
});
})
);
Expand All @@ -112,9 +103,7 @@ export function registerRoutes({
request: req,
logger,
},
{
...attributes,
}
attributes
);
return res.ok({ body: result });
})
Expand Down Expand Up @@ -142,9 +131,7 @@ export function registerRoutes({
logger,
},
id,
{
...attributes,
}
attributes
);
return res.ok({ body: result });
})
Expand Down
8 changes: 4 additions & 4 deletions src/plugins/workspace/server/saved_objects/workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ import { SavedObjectsType, WORKSPACE_TYPE } from '../../../../core/server';
export const workspace: SavedObjectsType = {
name: WORKSPACE_TYPE,
namespaceType: 'agnostic',
hidden: false,
/**
* Disable operation by using saved objects APIs on workspace metadata
*/
hidden: true,
/**
* workspace won't appear in management page.
*/
Expand All @@ -21,9 +24,6 @@ export const workspace: SavedObjectsType = {
description: {
type: 'text',
},
/**
* In opensearch, string[] is also mapped to text
*/
features: {
type: 'keyword',
},
Expand Down
Loading

0 comments on commit aeb826b

Please sign in to comment.