Skip to content

Commit

Permalink
feat: implement workspaces service
Browse files Browse the repository at this point in the history
Signed-off-by: SuZhoue-Joe <suzhou@amazon.com>
  • Loading branch information
SuZhou-Joe committed Jun 12, 2023
1 parent 3c5df9d commit f5d01c3
Show file tree
Hide file tree
Showing 10 changed files with 531 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/core/server/internal_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import { InternalStatusServiceSetup } from './status';
import { AuditTrailSetup, AuditTrailStart } from './audit_trail';
import { InternalLoggingServiceSetup } from './logging';
import { CoreUsageDataStart } from './core_usage_data';
import { InternalWorkspacesServiceSetup, InternalWorkspacesServiceStart } from './workspaces';

/** @internal */
export interface InternalCoreSetup {
Expand All @@ -64,6 +65,7 @@ export interface InternalCoreSetup {
auditTrail: AuditTrailSetup;
logging: InternalLoggingServiceSetup;
metrics: InternalMetricsServiceSetup;
workspaces: InternalWorkspacesServiceSetup;
}

/**
Expand All @@ -78,6 +80,7 @@ export interface InternalCoreStart {
uiSettings: InternalUiSettingsServiceStart;
auditTrail: AuditTrailStart;
coreUsageData: CoreUsageDataStart;
workspaces: InternalWorkspacesServiceStart;
}

/**
Expand Down
14 changes: 14 additions & 0 deletions src/core/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import { RequestHandlerContext } from '.';
import { InternalCoreSetup, InternalCoreStart, ServiceConfigDescriptor } from './internal_types';
import { CoreUsageDataService } from './core_usage_data';
import { CoreRouteHandlerContext } from './core_route_handler_context';
import { WorkspacesService } from './workspaces';

const coreId = Symbol('core');
const rootConfigPath = '';
Expand All @@ -86,6 +87,7 @@ export class Server {
private readonly coreApp: CoreApp;
private readonly auditTrail: AuditTrailService;
private readonly coreUsageData: CoreUsageDataService;
private readonly workspaces: WorkspacesService;

#pluginsInitialized?: boolean;
private coreStart?: InternalCoreStart;
Expand Down Expand Up @@ -118,6 +120,7 @@ export class Server {
this.auditTrail = new AuditTrailService(core);
this.logging = new LoggingService(core);
this.coreUsageData = new CoreUsageDataService(core);
this.workspaces = new WorkspacesService(core);
}

public async setup() {
Expand Down Expand Up @@ -172,6 +175,11 @@ export class Server {

const metricsSetup = await this.metrics.setup({ http: httpSetup });

const workspacesSetup = await this.workspaces.setup({
http: httpSetup,
savedObject: savedObjectsSetup,
});

const statusSetup = await this.status.setup({
opensearch: opensearchServiceSetup,
pluginDependencies: pluginTree.asNames,
Expand Down Expand Up @@ -212,6 +220,7 @@ export class Server {
auditTrail: auditTrailSetup,
logging: loggingSetup,
metrics: metricsSetup,
workspaces: workspacesSetup,
};

const pluginsSetup = await this.plugins.setup(coreSetup);
Expand Down Expand Up @@ -253,6 +262,9 @@ export class Server {
opensearch: opensearchStart,
savedObjects: savedObjectsStart,
});
const workspacesStart = await this.workspaces.start({
savedObjects: savedObjectsStart,
});

this.coreStart = {
capabilities: capabilitiesStart,
Expand All @@ -263,6 +275,7 @@ export class Server {
uiSettings: uiSettingsStart,
auditTrail: auditTrailStart,
coreUsageData: coreUsageDataStart,
workspaces: workspacesStart,
};

const pluginsStart = await this.plugins.start(this.coreStart);
Expand Down Expand Up @@ -295,6 +308,7 @@ export class Server {
await this.status.stop();
await this.logging.stop();
await this.auditTrail.stop();
await this.workspaces.stop();
}

private registerCoreContext(coreSetup: InternalCoreSetup) {
Expand Down
1 change: 1 addition & 0 deletions src/core/server/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@ export * from './ui_settings/types';
export * from './legacy/types';
export type { EnvironmentMode, PackageInfo } from '@osd/config';
export { Branding } from '../../core/types';
export * from './workspaces/types';
13 changes: 13 additions & 0 deletions src/core/server/workspaces/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/
export {
WorkspacesService,
InternalWorkspacesServiceSetup,
WorkspacesServiceStart,
WorkspacesServiceSetup,
InternalWorkspacesServiceStart,
} from './workspaces_service';

export { WorkspaceAttribute, WorkspaceFindOptions, WORKSPACES_API_BASE_URL } from './types';
146 changes: 146 additions & 0 deletions src/core/server/workspaces/routes/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/
import { schema } from '@osd/config-schema';
import { InternalHttpServiceSetup } from '../../http';
import { Logger } from '../../logging';
import { IWorkspaceDBImpl, WORKSPACES_API_BASE_URL } from '../types';

export function registerRoutes({
client,
logger,
http,
}: {
client: IWorkspaceDBImpl;
logger: Logger;
http: InternalHttpServiceSetup;
}) {
const router = http.createRouter(WORKSPACES_API_BASE_URL);
router.get(
{
path: '/_list',
validate: {
query: schema.object({
per_page: schema.number({ min: 0, defaultValue: 20 }),
page: schema.number({ min: 0, defaultValue: 1 }),
sort_field: schema.maybe(schema.string()),
fields: schema.maybe(schema.oneOf([schema.string(), schema.arrayOf(schema.string())])),
}),
},
},
router.handleLegacyErrors(async (context, req, res) => {
const result = await client.list(
{
context,
request: req,
logger,
},
req.query
);
return res.ok({ body: result });
})
);
router.get(
{
path: '/{id}',
validate: {
params: schema.object({
id: schema.string(),
}),
},
},
router.handleLegacyErrors(async (context, req, res) => {
const { id } = req.params;
const result = await client.get(
{
context,
request: req,
logger,
},
id
);
return res.ok({ body: result });
})
);
router.post(
{
path: '/{id?}',
validate: {
body: schema.object({
attributes: schema.object({
description: schema.maybe(schema.string()),
name: schema.string(),
}),
}),
},
},
router.handleLegacyErrors(async (context, req, res) => {
const { attributes } = req.body;

const result = await client.create(
{
context,
request: req,
logger,
},
attributes
);
return res.ok({ body: result });
})
);
router.put(
{
path: '/{id}',
validate: {
params: schema.object({
id: schema.string(),
}),
body: schema.object({
attributes: schema.object({
description: schema.maybe(schema.string()),
name: schema.string(),
}),
}),
},
},
router.handleLegacyErrors(async (context, req, res) => {
const { id } = req.params;
const { attributes } = req.body;

const result = await client.update(
{
context,
request: req,
logger,
},
id,
attributes
);
return res.ok({ body: result });
})
);
router.delete(
{
path: '/{id}',
validate: {
params: schema.object({
id: schema.string(),
}),
},
},
router.handleLegacyErrors(async (context, req, res) => {
const { id } = req.params;

const result = await client.delete(
{
context,
request: req,
logger,
},
id
);
return res.ok({ body: result });
})
);
}
6 changes: 6 additions & 0 deletions src/core/server/workspaces/saved_objects/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

export { workspace } from './workspace';
46 changes: 46 additions & 0 deletions src/core/server/workspaces/saved_objects/workspace.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { SavedObjectsType } from 'opensearch-dashboards/server';

export const workspace: SavedObjectsType = {
name: 'workspace',
namespaceType: 'agnostic',
hidden: false,
management: {
icon: 'apps', // todo: pending ux #2034
defaultSearchField: 'title',
importableAndExportable: true,
getTitle(obj) {
return obj.attributes.title;
},
getEditUrl(obj) {
return `/management/opensearch-dashboards/dataSources/${encodeURIComponent(obj.id)}`;
},
getInAppUrl(obj) {
return {
path: `/app/management/opensearch-dashboards/dataSources/${encodeURIComponent(obj.id)}`,
uiCapabilitiesPath: 'management.opensearchDashboards.dataSources',
};
},
},
mappings: {
dynamic: false,
properties: {
title: {
type: 'text',
},
description: {
type: 'text',
},
/**
* In opensearch, string[] is also mapped to text
*/
features: {
type: 'text',
},
},
},
};
71 changes: 71 additions & 0 deletions src/core/server/workspaces/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/
import {
Logger,
OpenSearchDashboardsRequest,
RequestHandlerContext,
SavedObjectsFindResponse,
} from '..';
import { WorkspacesSetupDeps } from './workspaces_service';

export interface WorkspaceAttribute {
id: string;
name: string;
description?: string;
features?: string[];
}

export interface WorkspaceFindOptions {
page?: number;
per_page?: number;
search?: string;
search_fields?: string[];
sort_field?: string;
sort_order?: string;
}

export interface IRequestDetail {
request: OpenSearchDashboardsRequest;
context: RequestHandlerContext;
logger: Logger;
}

export interface IWorkspaceDBImpl {
setup(dep: WorkspacesSetupDeps): Promise<IResponse<boolean>>;
create(
requestDetail: IRequestDetail,
payload: Omit<WorkspaceAttribute, 'id'>
): Promise<IResponse<{ id: WorkspaceAttribute['id'] }>>;
list(
requestDetail: IRequestDetail,
options: WorkspaceFindOptions
): Promise<
IResponse<
{
workspaces: WorkspaceAttribute[];
} & Pick<SavedObjectsFindResponse, 'page' | 'per_page' | 'total'>
>
>;
get(requestDetail: IRequestDetail, id: string): Promise<IResponse<WorkspaceAttribute>>;
update(
requestDetail: IRequestDetail,
id: string,
payload: Omit<WorkspaceAttribute, 'id'>
): Promise<IResponse<boolean>>;
delete(requestDetail: IRequestDetail, id: string): Promise<IResponse<boolean>>;
destroy(): Promise<IResponse<boolean>>;
}

export type IResponse<T> =
| {
result: T;
success: true;
}
| {
success: false;
error?: string;
};

export const WORKSPACES_API_BASE_URL = '/api/workspaces';
Loading

0 comments on commit f5d01c3

Please sign in to comment.