Skip to content

Commit

Permalink
[Workspace] Validate if workspace exists when setup inside a workspace (
Browse files Browse the repository at this point in the history
#6154) (#6444)

* feat: validate if workspace exists when setup inside a workspace

Signed-off-by: SuZhou-Joe <[email protected]>

* feat: add CHANGELOG

Signed-off-by: SuZhou-Joe <[email protected]>

* fix: unit test

Signed-off-by: SuZhou-Joe <[email protected]>

* feat: optimize import order

Signed-off-by: SuZhou-Joe <[email protected]>

* feat: add protection

Signed-off-by: SuZhou-Joe <[email protected]>

* Apply suggestions from code review

Co-authored-by: Yulong Ruan <[email protected]>
Signed-off-by: SuZhou-Joe <[email protected]>

* feat: jump to landing page

Signed-off-by: SuZhou-Joe <[email protected]>

---------

Signed-off-by: SuZhou-Joe <[email protected]>
Co-authored-by: Yulong Ruan <[email protected]>
(cherry picked from commit 0cc91ab)
  • Loading branch information
SuZhou-Joe authored Apr 15, 2024
1 parent d31d9de commit 57dfdf0
Show file tree
Hide file tree
Showing 10 changed files with 505 additions and 6 deletions.
2 changes: 2 additions & 0 deletions src/plugins/workspace/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
* SPDX-License-Identifier: Apache-2.0
*/

export const WORKSPACE_OVERVIEW_APP_ID = 'workspace_overview';
export const WORKSPACE_FATAL_ERROR_APP_ID = 'workspace_fatal_error';
export const WORKSPACE_SAVED_OBJECTS_CLIENT_WRAPPER_ID = 'workspace';
export const WORKSPACE_CONFLICT_CONTROL_SAVED_OBJECTS_CLIENT_WRAPPER_ID =
'workspace_conflict_control';
2 changes: 1 addition & 1 deletion src/plugins/workspace/opensearch_dashboards.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
"savedObjects"
],
"optionalPlugins": [],
"requiredBundles": []
"requiredBundles": ["opensearchDashboardsReact"]
}
26 changes: 26 additions & 0 deletions src/plugins/workspace/public/application.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import ReactDOM from 'react-dom';
import { AppMountParameters, ScopedHistory } from '../../../core/public';
import { OpenSearchDashboardsContextProvider } from '../../opensearch_dashboards_react/public';
import { WorkspaceFatalError } from './components/workspace_fatal_error';
import { Services } from './types';

export const renderFatalErrorApp = (params: AppMountParameters, services: Services) => {
const { element } = params;
const history = params.history as ScopedHistory<{ error?: string }>;
ReactDOM.render(
<OpenSearchDashboardsContextProvider services={services}>
<WorkspaceFatalError error={history.location.state?.error} />
</OpenSearchDashboardsContextProvider>,
element
);

return () => {
ReactDOM.unmountComponentAtNode(element);
};
};

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,6 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

export { WorkspaceFatalError } from './workspace_fatal_error';
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import { IntlProvider } from 'react-intl';
import { fireEvent, render, waitFor } from '@testing-library/react';
import { WorkspaceFatalError } from './workspace_fatal_error';
import { context } from '../../../../opensearch_dashboards_react/public';
import { coreMock } from '../../../../../core/public/mocks';

describe('<WorkspaceFatalError />', () => {
it('render normally', async () => {
const { findByText, container } = render(
<IntlProvider locale="en">
<WorkspaceFatalError />
</IntlProvider>
);
await findByText('Something went wrong');
expect(container).toMatchSnapshot();
});

it('render error with callout', async () => {
const { findByText, container } = render(
<IntlProvider locale="en">
<WorkspaceFatalError error="errorInCallout" />
</IntlProvider>
);
await findByText('errorInCallout');
expect(container).toMatchSnapshot();
});

it('click go back to homepage', async () => {
const { location } = window;
const setHrefSpy = jest.fn((href) => href);
if (window.location) {
// @ts-ignore
delete window.location;
}
window.location = {} as Location;
Object.defineProperty(window.location, 'href', {
get: () => 'http://localhost/',
set: setHrefSpy,
});
const coreStartMock = coreMock.createStart();
const { getByText } = render(
<IntlProvider locale="en">
<context.Provider
value={
{
services: coreStartMock,
} as any
}
>
<WorkspaceFatalError error="errorInCallout" />
</context.Provider>
</IntlProvider>
);
fireEvent.click(getByText('Go back to homepage'));
await waitFor(
() => {
expect(setHrefSpy).toBeCalledTimes(1);
},
{
container: document.body,
}
);
window.location = location;
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import {
EuiButton,
EuiEmptyPrompt,
EuiPage,
EuiPageBody,
EuiPageContent,
EuiCallOut,
} from '@elastic/eui';
import React from 'react';
import { FormattedMessage } from '@osd/i18n/react';
import { HttpSetup } from 'opensearch-dashboards/public';
import { useOpenSearchDashboards } from '../../../../opensearch_dashboards_react/public';

export function WorkspaceFatalError(props: { error?: string }) {
const {
services: { http },
} = useOpenSearchDashboards();
const goBackToHome = () => {
window.location.href = (http as HttpSetup).basePath.prepend('/', {
withoutClientBasePath: true,
});
};
return (
<EuiPage style={{ minHeight: '100vh' }}>
<EuiPageBody component="main">
<EuiPageContent verticalPosition="center" horizontalPosition="center">
<EuiEmptyPrompt
iconType="alert"
iconColor="danger"
title={
<h2>
<FormattedMessage
id="core.fatalErrors.somethingWentWrongTitle"
defaultMessage="Something went wrong"
/>
</h2>
}
body={
<p>
<FormattedMessage
id="core.fatalErrors.tryGoBackToDefaultWorkspaceDescription"
defaultMessage="The workspace you are trying to access cannot be found. Please return to the homepage and try again."
/>
</p>
}
actions={[
<EuiButton color="primary" fill onClick={goBackToHome}>
<FormattedMessage
id="core.fatalErrors.goBackToHome"
defaultMessage="Go back to homepage"
/>
</EuiButton>,
]}
/>
{props.error ? <EuiCallOut title={props.error} color="danger" iconType="alert" /> : null}
</EuiPageContent>
</EuiPageBody>
</EuiPage>
);
}
Loading

0 comments on commit 57dfdf0

Please sign in to comment.