Skip to content

Commit

Permalink
[Workspace] feat: update workspace server to support data connection …
Browse files Browse the repository at this point in the history
…type (opensearch-project#8200)

* feat: update worksapce server to support data connection type

Signed-off-by: tygao <tygao@amazon.com>

* Changeset file for PR opensearch-project#8200 created/updated

* test: add tests for wrapper

Signed-off-by: tygao <tygao@amazon.com>

---------

Signed-off-by: tygao <tygao@amazon.com>
Co-authored-by: opensearch-changeset-bot[bot] <154024398+opensearch-changeset-bot[bot]@users.noreply.github.com>
  • Loading branch information
raintygao and opensearch-changeset-bot[bot] committed Sep 14, 2024
1 parent 21f4218 commit f338aff
Show file tree
Hide file tree
Showing 16 changed files with 433 additions and 67 deletions.
2 changes: 2 additions & 0 deletions changelogs/fragments/8200.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
feat:
- Update workspace server to support data connection type ([#8200](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/8200))
1 change: 1 addition & 0 deletions src/plugins/data_source/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
export const PLUGIN_ID = 'dataSource';
export const PLUGIN_NAME = 'data_source';
export const DATA_SOURCE_SAVED_OBJECT_TYPE = 'data-source';
export { DATA_CONNECTION_SAVED_OBJECT_TYPE } from './data_connections';
2 changes: 1 addition & 1 deletion src/plugins/data_source/opensearch_dashboards.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
"ui": true,
"requiredPlugins": ["opensearchDashboardsUtils"],
"optionalPlugins": [],
"extraPublicDirs": ["common/data_sources", "common/util"]
"extraPublicDirs": ["common","common/data_sources", "common/util"]
}
11 changes: 10 additions & 1 deletion src/plugins/workspace/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
*/

import { i18n } from '@osd/i18n';

import {
DATA_CONNECTION_SAVED_OBJECT_TYPE,
DATA_SOURCE_SAVED_OBJECT_TYPE,
} from '../../data_source/common';
export const WORKSPACE_FATAL_ERROR_APP_ID = 'workspace_fatal_error';
export const WORKSPACE_CREATE_APP_ID = 'workspace_create';
export const WORKSPACE_LIST_APP_ID = 'workspace_list';
Expand Down Expand Up @@ -158,3 +161,9 @@ export enum AssociationDataSourceModalMode {
DirectQueryConnections = 'direction-query-connections',
}
export const USE_CASE_PREFIX = 'use-case-';

// Workspace will handle both data source and data connection type saved object.
export const WORKSPACE_DATA_SOURCE_AND_CONNECTION_OBJECT_TYPES = [
DATA_SOURCE_SAVED_OBJECT_TYPE,
DATA_CONNECTION_SAVED_OBJECT_TYPE,
];
3 changes: 2 additions & 1 deletion src/plugins/workspace/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { DataSourceAttributes } from 'src/plugins/data_source/common/data_source

export type DataSource = Pick<
DataSourceAttributes,
'title' | 'description' | 'dataSourceEngineType'
'title' | 'description' | 'dataSourceEngineType' | 'type'
> & {
// Id defined in SavedObjectAttribute could be single or array, here only should be single string.
id: string;
Expand All @@ -16,6 +16,7 @@ export type DataSource = Pick<
export enum DataSourceConnectionType {
OpenSearchConnection,
DirectQueryConnection,
DataConnection,
}

export interface DataSourceConnection {
Expand Down
5 changes: 4 additions & 1 deletion src/plugins/workspace/common/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/
import { USE_CASE_PREFIX } from './constants';
import { USE_CASE_PREFIX, WORKSPACE_DATA_SOURCE_AND_CONNECTION_OBJECT_TYPES } from './constants';

// Reference https://github.com/opensearch-project/oui/blob/main/src/services/color/is_valid_hex.ts
export const validateWorkspaceColor = (color?: string) =>
!!color && /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(color);

export const getUseCaseFeatureConfig = (useCaseId: string) => `${USE_CASE_PREFIX}${useCaseId}`;

export const validateIsWorkspaceDataSourceAndConnectionObjectType = (type: string) =>
WORKSPACE_DATA_SOURCE_AND_CONNECTION_OBJECT_TYPES.includes(type);
2 changes: 2 additions & 0 deletions src/plugins/workspace/public/workspace_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ export class WorkspaceClient implements IWorkspaceClient {
settings: {
dataSources?: string[];
permissions?: SavedObjectPermissions;
dataConnections?: string[];
}
): Promise<IResponse<Pick<WorkspaceAttributeWithPermission, 'id'>>> {
const path = this.getPath();
Expand Down Expand Up @@ -298,6 +299,7 @@ export class WorkspaceClient implements IWorkspaceClient {
settings: {
dataSources?: string[];
permissions?: SavedObjectPermissions;
dataConnections?: string[];
}
): Promise<IResponse<boolean>> {
const path = this.getPath(id);
Expand Down
5 changes: 5 additions & 0 deletions src/plugins/workspace/server/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,12 @@ const workspacePermissions = schema.recordOf(
);

const dataSourceIds = schema.arrayOf(schema.string());
const dataConnectionIds = schema.arrayOf(schema.string());

const settingsSchema = schema.object({
permissions: schema.maybe(workspacePermissions),
dataSources: schema.maybe(dataSourceIds),
dataConnections: schema.maybe(dataConnectionIds),
});

const featuresSchema = schema.arrayOf(schema.string(), {
Expand Down Expand Up @@ -203,6 +205,7 @@ export function registerRoutes({
const principals = permissionControlClient?.getPrincipalsFromRequest(req);
const createPayload: Omit<WorkspaceAttributeWithPermission, 'id'> & {
dataSources?: string[];
dataConnections?: string[];
} = attributes;

if (isPermissionControlEnabled) {
Expand All @@ -217,6 +220,7 @@ export function registerRoutes({
}

createPayload.dataSources = settings.dataSources;
createPayload.dataConnections = settings.dataConnections;

const result = await client.create(
{
Expand Down Expand Up @@ -255,6 +259,7 @@ export function registerRoutes({
...attributes,
...(isPermissionControlEnabled ? { permissions: settings.permissions } : {}),
...{ dataSources: settings.dataSources },
...{ dataConnections: settings.dataConnections },
}
);
return res.ok({ body: result });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import { SavedObject } from 'src/core/types';
import { isEqual } from 'lodash';
import packageInfo from '../../../../../../package.json';
import * as osdTestServer from '../../../../../core/test_helpers/osd_server';
import { DATA_SOURCE_SAVED_OBJECT_TYPE } from '../../../../data_source/common';
import {
DATA_SOURCE_SAVED_OBJECT_TYPE,
DATA_CONNECTION_SAVED_OBJECT_TYPE,
} from '../../../../data_source/common';

const dashboard: Omit<SavedObject, 'id'> = {
type: 'dashboard',
Expand All @@ -23,6 +26,14 @@ const dataSource: Omit<SavedObject, 'id'> = {
references: [],
};

const dataConnection: Omit<SavedObject, 'id'> = {
type: DATA_CONNECTION_SAVED_OBJECT_TYPE,
attributes: {
title: 'test data connection',
},
references: [],
};

const indexPattern: Omit<SavedObject, 'id'> = {
type: 'index-pattern',
attributes: {},
Expand Down Expand Up @@ -190,6 +201,22 @@ describe('saved_objects_wrapper_for_check_workspace_conflict integration test',
}
`);

const createDataConnectionResult = await osdTestServer.request
.post(root, `/api/saved_objects/${dataConnection.type}`)
.send({
attributes: dataConnection.attributes,
workspaces: [createdFooWorkspace.id],
})
.expect(400);

expect(createDataConnectionResult.body).toMatchInlineSnapshot(`
Object {
"error": "Bad Request",
"message": "Unsupported type in workspace: 'data-connection' is not allowed to be created in workspace.",
"statusCode": 400,
}
`);

const createConfigResult = await osdTestServer.request
.post(root, `/api/saved_objects/config`)
.send({
Expand Down Expand Up @@ -319,7 +346,7 @@ describe('saved_objects_wrapper_for_check_workspace_conflict integration test',
it('bulk create with disallowed types in workspace', async () => {
await clearFooAndBar();

// import advanced settings and data sources should throw error
// import advanced settings, data sources and data connection should throw error
const createResultFoo = await osdTestServer.request
.post(root, `/w/${createdFooWorkspace.id}/api/saved_objects/_bulk_create`)
.send([
Expand All @@ -331,6 +358,10 @@ describe('saved_objects_wrapper_for_check_workspace_conflict integration test',
...advancedSettings,
id: packageInfo.version,
},
{
...dataConnection,
id: packageInfo.version,
},
])
.expect(200);
expect(createResultFoo.body.saved_objects[0].error).toEqual(
Expand All @@ -347,6 +378,13 @@ describe('saved_objects_wrapper_for_check_workspace_conflict integration test',
statusCode: 400,
})
);
expect(createResultFoo.body.saved_objects[2].error).toEqual(
expect.objectContaining({
message:
"Unsupported type in workspace: 'data-connection' is not allowed to be imported in workspace.",
statusCode: 400,
})
);

// Data source should not be created
await osdTestServer.request
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import { SavedObject } from '../../../../core/public';
import { httpServerMock, savedObjectsClientMock, coreMock } from '../../../../core/server/mocks';
import { WorkspaceConflictSavedObjectsClientWrapper } from './saved_objects_wrapper_for_check_workspace_conflict';
import { SavedObjectsSerializer } from '../../../../core/server';
import { DATA_SOURCE_SAVED_OBJECT_TYPE } from '../../../../plugins/data_source/common';
import {
DATA_SOURCE_SAVED_OBJECT_TYPE,
DATA_CONNECTION_SAVED_OBJECT_TYPE,
} from '../../../../plugins/data_source/common';

describe('WorkspaceConflictSavedObjectsClientWrapper', () => {
const requestHandlerContext = coreMock.createRequestHandlerContext();
Expand Down Expand Up @@ -133,6 +136,19 @@ describe('WorkspaceConflictSavedObjectsClientWrapper', () => {
`[Error: Unsupported type in workspace: 'data-source' is not allowed to be created in workspace.]`
);

await expect(
wrapperClient.create(
DATA_CONNECTION_SAVED_OBJECT_TYPE,
{},

{
workspaces: ['foo'],
}
)
).rejects.toMatchInlineSnapshot(
`[Error: Unsupported type in workspace: 'data-connection' is not allowed to be created in workspace.]`
);

await expect(
wrapperClient.create(
'config',
Expand Down Expand Up @@ -336,6 +352,10 @@ describe('WorkspaceConflictSavedObjectsClientWrapper', () => {
type: DATA_SOURCE_SAVED_OBJECT_TYPE,
id: 'foo',
}),
getSavedObject({
type: DATA_CONNECTION_SAVED_OBJECT_TYPE,
id: 'foo',
}),
],
{
workspaces: ['foo'],
Expand All @@ -359,6 +379,13 @@ describe('WorkspaceConflictSavedObjectsClientWrapper', () => {
statusCode: 400,
})
);
expect(result.saved_objects[2].error).toEqual(
expect.objectContaining({
message:
"Unsupported type in workspace: 'data-connection' is not allowed to be imported in workspace.",
statusCode: 400,
})
);
});
});

Expand Down Expand Up @@ -474,6 +501,10 @@ describe('WorkspaceConflictSavedObjectsClientWrapper', () => {
type: DATA_SOURCE_SAVED_OBJECT_TYPE,
id: 'foo',
}),
getSavedObject({
type: DATA_CONNECTION_SAVED_OBJECT_TYPE,
id: 'foo',
}),
],
{
workspaces: ['foo'],
Expand All @@ -497,6 +528,13 @@ describe('WorkspaceConflictSavedObjectsClientWrapper', () => {
statusCode: 400,
})
);
expect(result.errors[2].error).toEqual(
expect.objectContaining({
message:
"Unsupported type in workspace: 'data-connection' is not allowed to be imported in workspace.",
statusCode: 400,
})
);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
SavedObjectsCheckConflictsResponse,
SavedObjectsFindOptions,
} from '../../../../core/server';
import { DATA_SOURCE_SAVED_OBJECT_TYPE } from '../../../data_source/common';
import { validateIsWorkspaceDataSourceAndConnectionObjectType } from '../../common/utils';

const UI_SETTINGS_SAVED_OBJECTS_TYPE = 'config';

Expand All @@ -42,10 +42,10 @@ export class WorkspaceConflictSavedObjectsClientWrapper {

private isDataSourceType(type: SavedObjectsFindOptions['type']): boolean {
if (Array.isArray(type)) {
return type.every((item) => item === DATA_SOURCE_SAVED_OBJECT_TYPE);
return type.every((item) => validateIsWorkspaceDataSourceAndConnectionObjectType(item));
}

return type === DATA_SOURCE_SAVED_OBJECT_TYPE;
return validateIsWorkspaceDataSourceAndConnectionObjectType(type);
}
private isConfigType(type: SavedObject['type']): boolean {
return type === UI_SETTINGS_SAVED_OBJECTS_TYPE;
Expand Down
Loading

0 comments on commit f338aff

Please sign in to comment.