Skip to content

Commit

Permalink
[Workspace] Integrate workspace front end with data connection type s…
Browse files Browse the repository at this point in the history
…aved object (#8013) (#8269)

* feat: integrate workspace with data connections



* update workspace pages and hooks to integrate with data connection



* Changeset file for PR #8013 created/updated

* remove extra comments



* update data source import



* test: update tests



* Changeset file for PR #8013 created/updated

* unify connection type icon and connectionType



* test: add tests



* display text directly instead of link for data connection in table



* Apply suggestions from code review




* update



---------





(cherry picked from commit c894584)

Signed-off-by: tygao <[email protected]>
Signed-off-by: Tianyu Gao <[email protected]>
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: opensearch-changeset-bot[bot] <154024398+opensearch-changeset-bot[bot]@users.noreply.github.com>
Co-authored-by: SuZhou-Joe <[email protected]>
  • Loading branch information
4 people committed Sep 24, 2024
1 parent 6971d8b commit 6ea0cb4
Show file tree
Hide file tree
Showing 18 changed files with 485 additions and 55 deletions.
2 changes: 2 additions & 0 deletions changelogs/fragments/8013.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
feat:
- Integrate workspace with data connections in front end ([#8013](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/8013))
2 changes: 1 addition & 1 deletion src/plugins/data_source/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +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';
export { DATA_CONNECTION_SAVED_OBJECT_TYPE, DataConnectionType } from './data_connections';
10 changes: 10 additions & 0 deletions src/plugins/workspace/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
*/

import { DataSourceAttributes } from 'src/plugins/data_source/common/data_sources';
import { DataConnectionSavedObjectAttributes } from 'src/plugins/data_source/common/data_connections';
import { DataConnectionType } from '../../data_source/common';

export type DataSource = Pick<
DataSourceAttributes,
Expand All @@ -13,6 +15,14 @@ export type DataSource = Pick<
id: string;
};

export type DataConnection = Pick<DataConnectionSavedObjectAttributes, 'title'> & {
type: string;
id: string;
connectionType: DataConnectionType;
description?: string;
title: string;
};

export enum DataSourceConnectionType {
OpenSearchConnection,
DirectQueryConnection,
Expand Down
9 changes: 9 additions & 0 deletions src/plugins/workspace/public/assets/cloudwatch_logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions src/plugins/workspace/public/assets/security_lake_logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,19 @@ const dataSourcesList = [
return 'ds2';
},
},
{
id: 'id3',
title: 'dqs1',
description: 'Description of data connection 1',
auth: '',
dataSourceEngineType: '' as DataSourceEngineType,
workspaces: [],
type: 'data-connection',
connectionType: 'AWS Security Lake',
get: () => {
return 'ds2';
},
},
];

const dataSourceConnectionsList = [
Expand All @@ -71,6 +84,13 @@ const dataSourceConnectionsList = [
connectionType: DataSourceConnectionType.OpenSearchConnection,
type: 'OpenSearch',
},
{
id: 'id3',
name: 'dqs1',
description: 'Description of data connection 1',
connectionType: DataSourceConnectionType.DataConnection,
type: 'AWS Security Lake',
},
];

const mockCoreStart = coreMock.createStart();
Expand Down Expand Up @@ -243,6 +263,7 @@ describe('WorkspaceCreator', () => {
}),
{
dataSources: [],
dataConnections: [],
permissions: {
library_write: { users: ['%me%'] },
write: { users: ['%me%'] },
Expand Down Expand Up @@ -318,6 +339,7 @@ describe('WorkspaceCreator', () => {
name: 'test workspace name',
}),
{
dataConnections: [],
dataSources: [],
permissions: {
write: {
Expand Down Expand Up @@ -375,6 +397,7 @@ describe('WorkspaceCreator', () => {
name: 'test workspace name',
}),
{
dataConnections: [],
dataSources: ['id1'],
permissions: {
library_write: {
Expand All @@ -392,6 +415,64 @@ describe('WorkspaceCreator', () => {
expect(notificationToastsAddDanger).not.toHaveBeenCalled();
});

it('create workspace with customized selected data connections', async () => {
Object.defineProperty(HTMLElement.prototype, 'offsetHeight', {
configurable: true,
value: 600,
});
Object.defineProperty(HTMLElement.prototype, 'offsetWidth', {
configurable: true,
value: 600,
});
const { getByTestId, getAllByText, getByText } = render(
<WorkspaceCreator isDashboardAdmin={true} />
);

// Ensure workspace create form rendered
await waitFor(() => {
expect(getByTestId('workspaceForm-bottomBar-createButton')).toBeInTheDocument();
});
const nameInput = getByTestId('workspaceForm-workspaceDetails-nameInputText');
fireEvent.input(nameInput, {
target: { value: 'test workspace name' },
});
fireEvent.click(getByTestId('workspaceUseCase-observability'));
fireEvent.click(getByTestId('workspace-creator-dqc-assign-button'));
await waitFor(() => {
expect(
getByText(
'Add data sources that will be available in the workspace. If a selected data source has related Direct Query connection, they will also be available in the workspace.'
)
).toBeInTheDocument();
expect(getByText(dataSourcesList[2].title)).toBeInTheDocument();
});
fireEvent.click(getByText(dataSourcesList[2].title));
fireEvent.click(getAllByText('Associate data sources')[1]);

fireEvent.click(getByTestId('workspaceForm-bottomBar-createButton'));
expect(workspaceClientCreate).toHaveBeenCalledWith(
expect.objectContaining({
name: 'test workspace name',
}),
{
dataConnections: ['id3'],
dataSources: [],
permissions: {
library_write: {
users: ['%me%'],
},
write: {
users: ['%me%'],
},
},
}
);
await waitFor(() => {
expect(notificationToastsAddSuccess).toHaveBeenCalled();
});
expect(notificationToastsAddDanger).not.toHaveBeenCalled();
});

it('should not create workspace API when submitting', async () => {
workspaceClientCreate.mockImplementationOnce(
() =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,16 @@ export const WorkspaceCreator = (props: WorkspaceCreatorProps) => {
.map(({ id }) => {
return id;
});
const selectedDataConnectionIds = (selectedDataSourceConnections ?? [])
.filter(
({ connectionType }) => connectionType === DataSourceConnectionType.DataConnection
)
.map(({ id }) => {
return id;
});
result = await workspaceClient.create(attributes, {
dataSources: selectedDataSourceIds,
dataConnections: selectedDataConnectionIds,
permissions: convertPermissionSettingsToPermissions(permissionSettings),
});
if (result?.success) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ const setupAssociationDataSourceModal = ({
connectionType: DataSourceConnectionType.OpenSearchConnection,
type: 'OpenSearch',
},
{
id: 'dqs1',
name: 'Data Connection 1',
connectionType: DataSourceConnectionType.DataConnection,
type: 'AWS Security Lake',
},
]);
const { logos } = chromeServiceMock.createStartContract();
render(
Expand Down Expand Up @@ -178,4 +184,26 @@ describe('AssociationDataSourceModal', () => {
},
]);
});

it('should call handleAssignDataSourceConnections with data connections after assigned', async () => {
const handleAssignDataSourceConnectionsMock = jest.fn();
setupAssociationDataSourceModal({
handleAssignDataSourceConnections: handleAssignDataSourceConnectionsMock,
mode: AssociationDataSourceModalMode.DirectQueryConnections,
});

await waitFor(() => {
fireEvent.click(screen.getByRole('option', { name: 'Data Connection 1' }));
fireEvent.click(screen.getByRole('button', { name: 'Associate data sources' }));
});

expect(handleAssignDataSourceConnectionsMock).toHaveBeenCalledWith([
{
id: 'dqs1',
name: 'Data Connection 1',
connectionType: DataSourceConnectionType.DataConnection,
type: 'AWS Security Lake',
},
]);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { DataSourceConnection, DataSourceConnectionType } from '../../../common/
import { HttpStart, NotificationsStart, SavedObjectsStart } from '../../../../../core/public';
import { AssociationDataSourceModalMode } from '../../../common/constants';
import { Logos } from '../../../../../core/common';
import { DirectQueryConnectionIcon } from '../workspace_form';
import { ConnectionTypeIcon } from '../workspace_form';

const ConnectionIcon = ({
connection: { connectionType, type },
Expand All @@ -42,9 +42,13 @@ const ConnectionIcon = ({
if (connectionType === DataSourceConnectionType.OpenSearchConnection) {
return <EuiIcon type={logos.Mark.url} />;
}
if (connectionType === DataSourceConnectionType.DirectQueryConnection) {
return <DirectQueryConnectionIcon type={type} />;
if (
connectionType === DataSourceConnectionType.DirectQueryConnection ||
connectionType === DataSourceConnectionType.DataConnection
) {
return <ConnectionTypeIcon type={type} />;
}

return null;
};

Expand Down Expand Up @@ -143,6 +147,14 @@ const convertConnectionsToOptions = ({
) {
return [];
}

if (connection.connectionType === DataSourceConnectionType.DataConnection) {
if (showDirectQueryConnections) {
return [connection];
}
return [];
}

if (showDirectQueryConnections) {
if (!connection.relatedConnections || connection.relatedConnections.length === 0) {
return [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ export const WorkspaceDetailConnectionTable = ({
return dataSourceConnections.filter((dsc) =>
connectionType === AssociationDataSourceModalMode.OpenSearchConnections
? dsc.connectionType === DataSourceConnectionType.OpenSearchConnection
: dsc?.relatedConnections && dsc.relatedConnections?.length > 0
: dsc.connectionType === DataSourceConnectionType.DataConnection ||
(dsc?.relatedConnections && dsc.relatedConnections?.length > 0)
);
}, [connectionType, dataSourceConnections]);

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import { mount } from 'enzyme';

import { ConnectionTypeIcon } from './connection_type_icon';

describe('ConnectionTypeIcon', () => {
it('should render normally', () => {
expect(mount(<ConnectionTypeIcon />)).toMatchSnapshot();
expect(mount(<ConnectionTypeIcon type="Amazon S3" />)).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import { EuiIcon } from '@elastic/eui';

import prometheusLogo from '../../assets/prometheus_logo.svg';
import s3Logo from '../../assets/s3_logo.svg';
import cloudWatchLogo from '../../assets/cloudwatch_logo.svg';
import securityLakeLogo from '../../assets/security_lake_logo.svg';
import { DataConnectionType } from '../../../../data_source/common/';

// Direct query connection and data connection both have different types, each type has a corresponding icon
export const ConnectionTypeIcon = ({ type }: { type?: string }) => {
switch (type) {
case 'Amazon S3':
return <EuiIcon type={s3Logo} />;
case 'Prometheus':
return <EuiIcon type={prometheusLogo} />;
case DataConnectionType.CloudWatch:
return <EuiIcon type={cloudWatchLogo} />;
case DataConnectionType.SecurityLake:
return <EuiIcon type={securityLakeLogo} />;
default:
return null;
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
import { i18n } from '@osd/i18n';
import { DataSourceConnection, DataSourceConnectionType } from '../../../common/types';
import { AssociationDataSourceModalMode } from '../../../common/constants';
import { DirectQueryConnectionIcon } from './direct_query_connection_icon';
import { ConnectionTypeIcon } from './connection_type_icon';
import { useOpenSearchDashboards } from '../../../../opensearch_dashboards_react/public';
import { CoreStart } from '../../../../../core/public';

Expand Down Expand Up @@ -115,6 +115,10 @@ export const DataSourceConnectionTable = forwardRef<
}),
truncateText: true,
render: (name: string, record) => {
// There is not a detail page for data connection, so we won't display a link here.
if (record.connectionType === DataSourceConnectionType.DataConnection) {
return name;
}
let url: string;
if (record.connectionType === DataSourceConnectionType.OpenSearchConnection) {
url = http.basePath.prepend(`/app/dataSources/${record.id}`);
Expand Down Expand Up @@ -167,7 +171,7 @@ export const DataSourceConnectionTable = forwardRef<
<EuiButtonEmpty
data-test-subj={`workspace-detail-dataSources-table-dqc-${record.id}-related-button`}
size="xs"
flush="right"
flush="left"
color="text"
onClick={() => togglePopover(record.id)}
>
Expand All @@ -191,7 +195,7 @@ export const DataSourceConnectionTable = forwardRef<
key={item.id}
size="xs"
label={item.name}
icon={<DirectQueryConnectionIcon type={item.type} />}
icon={<ConnectionTypeIcon type={item.type} />}
style={{ maxHeight: '30px' }}
/>
))}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export { WorkspaceUseCase } from './workspace_use_case';
export { WorkspacePermissionSettingPanel } from './workspace_permission_setting_panel';
export { WorkspaceCancelModal } from './workspace_cancel_modal';
export { WorkspaceNameField, WorkspaceDescriptionField } from './fields';
export { DirectQueryConnectionIcon } from './direct_query_connection_icon';
export { ConnectionTypeIcon } from './connection_type_icon';
export { DataSourceConnectionTable } from './data_source_connection_table';

export { WorkspaceFormSubmitData, WorkspaceFormProps, WorkspaceFormDataState } from './types';
Expand Down
Loading

0 comments on commit 6ea0cb4

Please sign in to comment.