Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Workspace]Feat add use cases to workspace form #6887

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
d27b2f2
Add workspace use case to workspace form
wanglam May 20, 2024
81f069a
Remove feature selector in workspace form
wanglam May 20, 2024
2db0264
Show use cases in workspace list page
wanglam May 20, 2024
2463602
Change direction for workspace use case selector
wanglam May 21, 2024
0c3991d
Modify test cases for match use case
wanglam May 21, 2024
a9cc376
Make use cases as a required field
wanglam May 22, 2024
136c9db
Update ui according feedbacks
wanglam May 24, 2024
e0f624c
Add management feature to dashboards and visualize use cases
wanglam May 24, 2024
47ca6f7
Update latest feature relationships
wanglam May 31, 2024
bafdc05
Changeset file for PR #6887 created/updated
opensearch-changeset-bot[bot] Jun 3, 2024
e3f2756
Changeset file for PR #6887 created/updated
opensearch-changeset-bot[bot] Jun 3, 2024
dc2f49e
Update test case for workspace creator and updater
wanglam Jun 3, 2024
a597f59
Address unit test
wanglam Jun 4, 2024
f3e52ca
Add discover feature to all use case
wanglam Jun 4, 2024
8f6f45b
Add missing features to security analytics
wanglam Jun 4, 2024
44b1b4c
Merge branch 'main' into feat-add-use-cases-to-workspace-form
wanglam Jun 5, 2024
91f5dd1
Address PR comments
wanglam Jun 6, 2024
6c21761
Merge branch 'main' into feat-add-use-cases-to-workspace-form
wanglam Jun 6, 2024
b122d33
Merge branch 'main' into feat-add-use-cases-to-workspace-form
ruanyl Jun 6, 2024
1565a01
Add comment for workspace use cases map
wanglam Jun 6, 2024
55e9839
Update use case UI
wanglam Jun 6, 2024
9cfa436
Remove the permissions tab
wanglam Jun 6, 2024
9bc93db
Update breadcrum to Create a workspace
wanglam Jun 6, 2024
adc2b54
Merge branch 'main' into feat-add-use-cases-to-workspace-form
wanglam Jun 7, 2024
4caf213
Address ut failed
wanglam Jun 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelogs/fragments/6887.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
feat:
- [Workspace]Add use cases to workspace form ([#6887](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6887))
103 changes: 103 additions & 0 deletions src/plugins/workspace/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,106 @@ export const WORKSPACE_APP_CATEGORIES: Record<string, AppCategory> = Object.free
order: 14000,
},
});
/**
*
* This is a temp solution to store relationships between use cases and features.
* The relationship should be provided by plugin itself. The workspace plugin should
* provide some method to register single feature to the use case map instead of
* store a static map in workspace.
*
*/
export const WORKSPACE_USE_CASES = Object.freeze({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we add a comment to mention this is a temporary solution, and also briefly describe the future plan?

observability: {
id: 'observability',
title: i18n.translate('workspace.usecase.observability.title', {
defaultMessage: 'Observability',
}),
description: i18n.translate('workspace.usecase.observability.description', {
defaultMessage:
'Gain visibility into system health, performance, and reliability through monitoring and analysis of logs, metrics, and traces.',
}),
features: [
'discover',
'dashboards',
'visualize',
'maps-dashboards',
'observability-notebooks',
'reports-dashboards',
'integrations',
'alerting',
'anomaly-detection-dashboards',
'observability-metrics',
'observability-traces',
'observability-applications',
// Add management avoid index patterns application not found for dashboards or visualize
'management',
] as string[],
},
'security-analytics': {
id: 'security-analytics',
title: i18n.translate('workspace.usecase.security.analytics.title', {
defaultMessage: 'Security Analytics',
}),
description: i18n.translate('workspace.usecase.analytics.description', {
defaultMessage:
'Detect and investigate potential security threats and vulnerabilities across your systems and data.',
}),
features: [
'discover',
'dashboards',
'visualize',
'maps-dashboards',
'observability-notebooks',
'reports-dashboards',
'integrations',
'alerting',
'anomaly-detection-dashboards',
'opensearch_security_analytics_dashboards',
// Add management avoid index patterns application not found for dashboards or visualize
'management',
] as string[],
},
analytics: {
id: 'analytics',
title: i18n.translate('workspace.usecase.analytics.title', {
defaultMessage: 'Analytics',
}),
description: i18n.translate('workspace.usecase.analytics.description', {
defaultMessage:
'Analyze data to derive insights, identify patterns and trends, and make data-driven decisions.',
}),
features: [
'discover',
'dashboards',
'visualize',
'maps-dashboards',
'observability-notebooks',
'reports-dashboards',
'integrations',
'alerting',
'anomaly-detection-dashboards',
// Add management avoid index patterns application not found for dashboards or visualize
'management',
] as string[],
},
search: {
id: 'search',
title: i18n.translate('workspace.usecase.search.title', {
defaultMessage: 'Search',
}),
description: i18n.translate('workspace.usecase.search.description', {
defaultMessage:
"Quickly find and explore relevant information across your organization's data sources.",
}),
features: [
'discover',
'dashboards',
'visualize',
'maps-dashboards',
'reports-dashboards',
'searchRelevance',
// Add management avoid index patterns application not found for dashboards or visualize
'management',
] as string[],
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,8 @@ const navigateToApp = jest.fn();
const notificationToastsAddSuccess = jest.fn();
const notificationToastsAddDanger = jest.fn();
const PublicAPPInfoMap = new Map([
['app1', { id: 'app1', title: 'app1' }],
['app2', { id: 'app2', title: 'app2', category: { id: 'category1', label: 'category1' } }],
['app3', { id: 'app3', category: { id: 'category1', label: 'category1' } }],
['app4', { id: 'app4', category: { id: 'category2', label: 'category2' } }],
['app5', { id: 'app5', category: { id: 'category2', label: 'category2' } }],
['data-explorer', { id: 'data-explorer', title: 'Data Explorer' }],
['dashboards', { id: 'dashboards', title: 'Dashboards' }],
]);

const mockCoreStart = coreMock.createStart();
Expand Down Expand Up @@ -116,6 +113,22 @@ describe('WorkspaceCreator', () => {
expect(workspaceClientCreate).not.toHaveBeenCalled();
});

it('should not create workspace without use cases', async () => {
setHrefSpy.mockReset();
const { getByTestId } = render(
<WorkspaceCreator
workspaceConfigurableApps$={new BehaviorSubject([...PublicAPPInfoMap.values()])}
/>
);
const nameInput = getByTestId('workspaceForm-workspaceDetails-nameInputText');
fireEvent.input(nameInput, {
target: { value: 'test workspace name' },
});
expect(setHrefSpy).not.toHaveBeenCalled();
fireEvent.click(getByTestId('workspaceForm-bottomBar-createButton'));
expect(workspaceClientCreate).not.toHaveBeenCalled();
});

it('cancel create workspace', async () => {
const { findByText, getByTestId } = render(
<WorkspaceCreator
Expand Down Expand Up @@ -148,12 +161,14 @@ describe('WorkspaceCreator', () => {
fireEvent.input(colorSelector, {
target: { value: '#000000' },
});
fireEvent.click(getByTestId('workspaceUseCase-observability'));
fireEvent.click(getByTestId('workspaceForm-bottomBar-createButton'));
expect(workspaceClientCreate).toHaveBeenCalledWith(
expect.objectContaining({
name: 'test workspace name',
color: '#000000',
description: 'test workspace description',
features: expect.arrayContaining(['use-case-observability']),
}),
undefined
);
Expand All @@ -163,37 +178,6 @@ describe('WorkspaceCreator', () => {
expect(notificationToastsAddDanger).not.toHaveBeenCalled();
});

it('create workspace with customized features', async () => {
setHrefSpy.mockReset();
const { getByTestId } = render(
<WorkspaceCreator
workspaceConfigurableApps$={new BehaviorSubject([...PublicAPPInfoMap.values()])}
/>
);
const nameInput = getByTestId('workspaceForm-workspaceDetails-nameInputText');
fireEvent.input(nameInput, {
target: { value: 'test workspace name' },
});
fireEvent.click(getByTestId('workspaceForm-workspaceFeatureVisibility-app1'));
fireEvent.click(getByTestId('workspaceForm-workspaceFeatureVisibility-category1'));
expect(setHrefSpy).not.toHaveBeenCalled();
fireEvent.click(getByTestId('workspaceForm-bottomBar-createButton'));
expect(workspaceClientCreate).toHaveBeenCalledWith(
expect.objectContaining({
name: 'test workspace name',
features: expect.arrayContaining(['app1', 'app2', 'app3']),
}),
undefined
);
await waitFor(() => {
expect(notificationToastsAddSuccess).toHaveBeenCalled();
});
expect(notificationToastsAddDanger).not.toHaveBeenCalled();
await waitFor(() => {
expect(setHrefSpy).toHaveBeenCalledWith(expect.stringMatching(/workspace_overview$/));
});
});

it('should show danger toasts after create workspace failed', async () => {
workspaceClientCreate.mockReturnValueOnce({ result: { id: 'failResult' }, success: false });
const { getByTestId } = render(
Expand All @@ -205,6 +189,7 @@ describe('WorkspaceCreator', () => {
fireEvent.input(nameInput, {
target: { value: 'test workspace name' },
});
fireEvent.click(getByTestId('workspaceUseCase-observability'));
fireEvent.click(getByTestId('workspaceForm-bottomBar-createButton'));
expect(workspaceClientCreate).toHaveBeenCalled();
await waitFor(() => {
Expand All @@ -226,6 +211,7 @@ describe('WorkspaceCreator', () => {
fireEvent.input(nameInput, {
target: { value: 'test workspace name' },
});
fireEvent.click(getByTestId('workspaceUseCase-observability'));
fireEvent.click(getByTestId('workspaceForm-bottomBar-createButton'));
expect(workspaceClientCreate).toHaveBeenCalled();
await waitFor(() => {
Expand All @@ -235,12 +221,16 @@ describe('WorkspaceCreator', () => {
});

it('create workspace with customized permissions', async () => {
const { getByTestId, getByText, getAllByText } = render(<WorkspaceCreator />);
const { getByTestId, getAllByText } = render(
<WorkspaceCreator
workspaceConfigurableApps$={new BehaviorSubject([...PublicAPPInfoMap.values()])}
/>
);
const nameInput = getByTestId('workspaceForm-workspaceDetails-nameInputText');
fireEvent.input(nameInput, {
target: { value: 'test workspace name' },
});
fireEvent.click(getByText('Users & Permissions'));
fireEvent.click(getByTestId('workspaceUseCase-observability'));
fireEvent.click(getByTestId('workspaceForm-permissionSettingPanel-user-addNew'));
const userIdInput = getAllByText('Select')[0];
fireEvent.click(userIdInput);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export const WorkspaceCreator = (props: WorkspaceCreatorProps) => {
return (
<EuiPage paddingSize="none">
<EuiPageBody>
<EuiPageHeader restrictWidth pageTitle="Create Workspace" />
<EuiPageHeader restrictWidth pageTitle="Create a workspace" />
<EuiSpacer />
<EuiPageContent
verticalPosition="center"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const WorkspaceCreatorApp = (props: WorkspaceCreatorProps) => {
chrome?.setBreadcrumbs([
{
text: i18n.translate('workspace.workspaceCreateTitle', {
defaultMessage: 'Create workspace',
defaultMessage: 'Create a workspace',
}),
},
]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,6 @@ export enum WorkspaceOperationType {
Update = 'update',
}

export enum WorkspaceFormTabs {
NotSelected,
FeatureVisibility,
UsersAndPermissions,
}

export enum WorkspacePermissionItemType {
User = 'user',
Group = 'group',
Expand Down
10 changes: 0 additions & 10 deletions src/plugins/workspace/public/components/workspace_form/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,6 @@ export interface WorkspaceFormData extends WorkspaceFormSubmitData {
reserved?: boolean;
}

export interface WorkspaceFeature {
id: string;
name: string;
}

export interface WorkspaceFeatureGroup {
name: string;
features: WorkspaceFeature[];
}

export type WorkspaceFormErrors = {
[key in keyof Omit<WorkspaceFormData, 'permissionSettings'>]?: string;
} & {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,35 @@ describe('useWorkspaceForm', () => {
act(() => {
renderResult.result.current.handleFormSubmit({ preventDefault: jest.fn() });
});
expect(renderResult.result.current.formErrors).toEqual({
name: 'Invalid workspace name',
expect(renderResult.result.current.formErrors).toEqual(
expect.objectContaining({
name: 'Invalid workspace name',
})
);
expect(onSubmitMock).not.toHaveBeenCalled();
});
it('should return "Use case is required. Select a use case." and not call onSubmit', async () => {
const { renderResult, onSubmitMock } = setup({
id: 'foo',
name: 'test-workspace-name',
});
expect(renderResult.result.current.formErrors).toEqual({});

act(() => {
renderResult.result.current.handleFormSubmit({ preventDefault: jest.fn() });
});
expect(renderResult.result.current.formErrors).toEqual(
expect.objectContaining({
features: 'Use case is required. Select a use case.',
})
);
expect(onSubmitMock).not.toHaveBeenCalled();
});
it('should call onSubmit with workspace name and features', async () => {
const { renderResult, onSubmitMock } = setup({
id: 'foo',
name: 'test-workspace-name',
features: ['use-case-observability'],
});
expect(renderResult.result.current.formErrors).toEqual({});

Expand All @@ -53,7 +73,7 @@ describe('useWorkspaceForm', () => {
expect(onSubmitMock).toHaveBeenCalledWith(
expect.objectContaining({
name: 'test-workspace-name',
features: ['workspace_update', 'workspace_overview'],
features: ['use-case-observability', 'workspace_update', 'workspace_overview'],
})
);
});
Expand Down
Loading
Loading