Skip to content

Commit 86d8b01

Browse files
[Workspace][Feature]Setup workspace skeleton and implement basic CRUD API (#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> (cherry picked from commit eeb3251) Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> # Conflicts: # CHANGELOG.md
1 parent f489da8 commit 86d8b01

File tree

20 files changed

+1084
-2
lines changed

20 files changed

+1084
-2
lines changed

src/core/server/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -345,8 +345,8 @@ export {
345345
MetricsServiceStart,
346346
} from './metrics';
347347

348-
export { AppCategory } from '../types';
349-
export { DEFAULT_APP_CATEGORIES } from '../utils';
348+
export { AppCategory, WorkspaceAttribute } from '../types';
349+
export { DEFAULT_APP_CATEGORIES, WORKSPACE_TYPE } from '../utils';
350350

351351
export {
352352
SavedObject,

src/core/utils/constants.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
export const WORKSPACE_TYPE = 'workspace';

src/core/utils/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,4 @@ export {
3737
IContextProvider,
3838
} from './context';
3939
export { DEFAULT_APP_CATEGORIES } from './default_app_categories';
40+
export { WORKSPACE_TYPE } from './constants';
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
export const WORKSPACE_SAVED_OBJECTS_CLIENT_WRAPPER_ID = 'workspace';

src/plugins/workspace/config.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import { schema, TypeOf } from '@osd/config-schema';
7+
8+
export const configSchema = schema.object({
9+
enabled: schema.boolean({ defaultValue: false }),
10+
});
11+
12+
export type ConfigSchema = TypeOf<typeof configSchema>;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"id": "workspace",
3+
"version": "opensearchDashboards",
4+
"server": true,
5+
"ui": false,
6+
"requiredPlugins": [
7+
"savedObjects"
8+
],
9+
"optionalPlugins": [],
10+
"requiredBundles": []
11+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import { WorkspacePlugin } from './plugin';
7+
8+
export function plugin() {
9+
return new WorkspacePlugin();
10+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import { Plugin } from '../../../core/public';
7+
8+
export class WorkspacePlugin implements Plugin<{}, {}, {}> {
9+
public async setup() {
10+
return {};
11+
}
12+
13+
public start() {
14+
return {};
15+
}
16+
17+
public stop() {}
18+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import { PluginConfigDescriptor, PluginInitializerContext } from '../../../core/server';
7+
import { WorkspacePlugin } from './plugin';
8+
import { configSchema } from '../config';
9+
10+
// This exports static code and TypeScript types,
11+
// as well as, OpenSearch Dashboards Platform `plugin()` initializer.
12+
13+
export function plugin(initializerContext: PluginInitializerContext) {
14+
return new WorkspacePlugin(initializerContext);
15+
}
16+
17+
export const config: PluginConfigDescriptor = {
18+
schema: configSchema,
19+
};
20+
21+
export { WorkspaceFindOptions } from './types';
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import { WorkspaceAttribute } from 'src/core/types';
7+
import * as osdTestServer from '../../../../core/test_helpers/osd_server';
8+
9+
const omitId = <T extends { id?: string }>(object: T): Omit<T, 'id'> => {
10+
const { id, ...others } = object;
11+
return others;
12+
};
13+
14+
const testWorkspace: WorkspaceAttribute = {
15+
id: 'fake_id',
16+
name: 'test_workspace',
17+
description: 'test_workspace_description',
18+
};
19+
20+
describe('workspace service', () => {
21+
let root: ReturnType<typeof osdTestServer.createRoot>;
22+
let opensearchServer: osdTestServer.TestOpenSearchUtils;
23+
beforeAll(async () => {
24+
const { startOpenSearch, startOpenSearchDashboards } = osdTestServer.createTestServers({
25+
adjustTimeout: (t: number) => jest.setTimeout(t),
26+
settings: {
27+
osd: {
28+
workspace: {
29+
enabled: true,
30+
},
31+
},
32+
},
33+
});
34+
opensearchServer = await startOpenSearch();
35+
const startOSDResp = await startOpenSearchDashboards();
36+
root = startOSDResp.root;
37+
});
38+
afterAll(async () => {
39+
await root.shutdown();
40+
await opensearchServer.stop();
41+
});
42+
describe('Workspace CRUD APIs', () => {
43+
afterEach(async () => {
44+
const listResult = await osdTestServer.request
45+
.post(root, `/api/workspaces/_list`)
46+
.send({
47+
page: 1,
48+
})
49+
.expect(200);
50+
await Promise.all(
51+
listResult.body.result.workspaces.map((item: WorkspaceAttribute) =>
52+
osdTestServer.request.delete(root, `/api/workspaces/${item.id}`).expect(200)
53+
)
54+
);
55+
});
56+
it('create', async () => {
57+
await osdTestServer.request
58+
.post(root, `/api/workspaces`)
59+
.send({
60+
attributes: testWorkspace,
61+
})
62+
.expect(400);
63+
64+
const result: any = await osdTestServer.request
65+
.post(root, `/api/workspaces`)
66+
.send({
67+
attributes: omitId(testWorkspace),
68+
})
69+
.expect(200);
70+
71+
expect(result.body.success).toEqual(true);
72+
expect(typeof result.body.result.id).toBe('string');
73+
});
74+
it('get', async () => {
75+
const result = await osdTestServer.request
76+
.post(root, `/api/workspaces`)
77+
.send({
78+
attributes: omitId(testWorkspace),
79+
})
80+
.expect(200);
81+
82+
const getResult = await osdTestServer.request.get(
83+
root,
84+
`/api/workspaces/${result.body.result.id}`
85+
);
86+
expect(getResult.body.result.name).toEqual(testWorkspace.name);
87+
});
88+
it('update', async () => {
89+
const result: any = await osdTestServer.request
90+
.post(root, `/api/workspaces`)
91+
.send({
92+
attributes: omitId(testWorkspace),
93+
})
94+
.expect(200);
95+
96+
await osdTestServer.request
97+
.put(root, `/api/workspaces/${result.body.result.id}`)
98+
.send({
99+
attributes: {
100+
...omitId(testWorkspace),
101+
name: 'updated',
102+
},
103+
})
104+
.expect(200);
105+
106+
const getResult = await osdTestServer.request.get(
107+
root,
108+
`/api/workspaces/${result.body.result.id}`
109+
);
110+
111+
expect(getResult.body.success).toEqual(true);
112+
expect(getResult.body.result.name).toEqual('updated');
113+
});
114+
it('delete', async () => {
115+
const result: any = await osdTestServer.request
116+
.post(root, `/api/workspaces`)
117+
.send({
118+
attributes: omitId(testWorkspace),
119+
})
120+
.expect(200);
121+
122+
await osdTestServer.request
123+
.delete(root, `/api/workspaces/${result.body.result.id}`)
124+
.expect(200);
125+
126+
const getResult = await osdTestServer.request.get(
127+
root,
128+
`/api/workspaces/${result.body.result.id}`
129+
);
130+
131+
expect(getResult.body.success).toEqual(false);
132+
});
133+
it('list', async () => {
134+
await osdTestServer.request
135+
.post(root, `/api/workspaces`)
136+
.send({
137+
attributes: omitId(testWorkspace),
138+
})
139+
.expect(200);
140+
141+
await osdTestServer.request
142+
.post(root, `/api/workspaces`)
143+
.send({
144+
attributes: {
145+
...omitId(testWorkspace),
146+
name: 'another test workspace',
147+
},
148+
})
149+
.expect(200);
150+
151+
const listResult = await osdTestServer.request
152+
.post(root, `/api/workspaces/_list`)
153+
.send({
154+
page: 1,
155+
})
156+
.expect(200);
157+
expect(listResult.body.result.total).toEqual(2);
158+
});
159+
it('unable to perform operations on workspace by calling saved objects APIs', async () => {
160+
const result = await osdTestServer.request
161+
.post(root, `/api/workspaces`)
162+
.send({
163+
attributes: omitId(testWorkspace),
164+
})
165+
.expect(200);
166+
167+
/**
168+
* Can not create workspace by saved objects API
169+
*/
170+
await osdTestServer.request
171+
.post(root, `/api/saved_objects/workspace`)
172+
.send({
173+
attributes: {
174+
...omitId(testWorkspace),
175+
name: 'another test workspace',
176+
},
177+
})
178+
.expect(400);
179+
180+
/**
181+
* Can not get workspace by saved objects API
182+
*/
183+
await osdTestServer.request
184+
.get(root, `/api/saved_objects/workspace/${result.body.result.id}`)
185+
.expect(404);
186+
187+
/**
188+
* Can not update workspace by saved objects API
189+
*/
190+
await osdTestServer.request
191+
.put(root, `/api/saved_objects/workspace/${result.body.result.id}`)
192+
.send({
193+
attributes: {
194+
name: 'another test workspace',
195+
},
196+
})
197+
.expect(404);
198+
199+
/**
200+
* Can not delete workspace by saved objects API
201+
*/
202+
await osdTestServer.request
203+
.delete(root, `/api/saved_objects/workspace/${result.body.result.id}`)
204+
.expect(404);
205+
206+
/**
207+
* Can not find workspace by saved objects API
208+
*/
209+
const findResult = await osdTestServer.request
210+
.get(root, `/api/saved_objects/_find?type=workspace`)
211+
.expect(200);
212+
const listResult = await osdTestServer.request
213+
.post(root, `/api/workspaces/_list`)
214+
.send({
215+
page: 1,
216+
})
217+
.expect(200);
218+
expect(findResult.body.total).toEqual(0);
219+
expect(listResult.body.result.total).toEqual(1);
220+
});
221+
});
222+
});

0 commit comments

Comments
 (0)