Skip to content

Commit 0af3a00

Browse files
opensearch-trigger-bot[bot]github-actions[bot]SuZhou-Joeopensearch-changeset-bot[bot]Hailong-am
authored
[Workspace]Import sample data to current workspace (#6105) (#6826)
* Import sample data to workspace * Enable workspace ui plugin * Add changelog for import sample data to current workspace * feat: register sample data as standalone app (#8) * feat: register sample data as standalone app * feat: optimize code * feat: add comment * feat: use props to pass homeLink * feat: add unit test --------- * Retrieve workspace id from request * Remove workspace id in query * Move changelog to fragments * Fix sample data list unit tests * Remove no need workspaces deps * Remove manual created changelogs * Changeset file for PR #6105 created/updated * Enable sample data in workspace overview page (#9) * enable sample data in workspace overview page * add comments for empty id --------- * Add unit tests for getFinalSavedObjects in data sets util file * Add unit tests for renderImportSampleDataApp destroy * Address PR comments * Remove history listen in renderImportSampleDataApp * Remove Route for workspace import sample data entry point --------- (cherry picked from commit 3e9a159) Signed-off-by: Lin Wang <wonglam@amazon.com> Signed-off-by: SuZhou-Joe <suzhou@amazon.com> Signed-off-by: Hailong Cui <ihailong@amazon.com> 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: SuZhou-Joe <suzhou@amazon.com> Co-authored-by: opensearch-changeset-bot[bot] <154024398+opensearch-changeset-bot[bot]@users.noreply.github.com> Co-authored-by: Hailong Cui <ihailong@amazon.com>
1 parent fad6971 commit 0af3a00

26 files changed

+849
-118
lines changed

changelogs/fragments/6105.yml

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
feat:
2+
- [Workspace]Import sample data to current workspace ([#6105](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6105))

src/plugins/home/common/constants.ts

+1
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,4 @@
3131
export const PLUGIN_ID = 'home';
3232
export const HOME_APP_BASE_PATH = `/app/${PLUGIN_ID}`;
3333
export const USE_NEW_HOME_PAGE = 'home:useNewHomePage';
34+
export const IMPORT_SAMPLE_DATA_APP_ID = 'import_sample_data';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import React, { useEffect, useRef } from 'react';
7+
import { render } from '@testing-library/react';
8+
import { coreMock } from '../../../../core/public/mocks';
9+
import { renderImportSampleDataApp } from './application';
10+
11+
jest.mock('./components/home_app', () => ({
12+
HomeApp: () => 'HomeApp',
13+
ImportSampleDataApp: () => 'ImportSampleDataApp',
14+
}));
15+
16+
const coreStartMocks = coreMock.createStart();
17+
18+
const ComponentForRender = (props: { renderFn: typeof renderImportSampleDataApp }) => {
19+
const container = useRef<HTMLDivElement>(null);
20+
useEffect(() => {
21+
if (container.current) {
22+
const destroyFn = props.renderFn(container.current, coreStartMocks);
23+
return () => {
24+
destroyFn.then((res) => res());
25+
};
26+
}
27+
}, [props]);
28+
29+
return <div ref={container} />;
30+
};
31+
32+
describe('renderImportSampleDataApp', () => {
33+
it('should render ImportSampleDataApp when calling renderImportSampleDataApp', async () => {
34+
const { container } = render(<ComponentForRender renderFn={renderImportSampleDataApp} />);
35+
expect(container).toMatchInlineSnapshot(`
36+
<div>
37+
<div>
38+
ImportSampleDataApp
39+
</div>
40+
</div>
41+
`);
42+
});
43+
});

src/plugins/home/public/application/application.tsx

+14-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import { i18n } from '@osd/i18n';
3434
import { ScopedHistory, CoreStart } from 'opensearch-dashboards/public';
3535
import { OpenSearchDashboardsContextProvider } from '../../../opensearch_dashboards_react/public';
3636
// @ts-ignore
37-
import { HomeApp } from './components/home_app';
37+
import { HomeApp, ImportSampleDataApp } from './components/home_app';
3838
import { getServices } from './opensearch_dashboards_services';
3939

4040
import './index.scss';
@@ -77,3 +77,16 @@ export const renderApp = async (
7777
unlisten();
7878
};
7979
};
80+
81+
export const renderImportSampleDataApp = async (element: HTMLElement, coreStart: CoreStart) => {
82+
render(
83+
<OpenSearchDashboardsContextProvider services={{ ...coreStart }}>
84+
<ImportSampleDataApp />
85+
</OpenSearchDashboardsContextProvider>,
86+
element
87+
);
88+
89+
return () => {
90+
unmountComponentAtNode(element);
91+
};
92+
};

src/plugins/home/public/application/components/home_app.js

+29-10
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,35 @@ const RedirectToDefaultApp = () => {
5151
return null;
5252
};
5353

54+
const renderTutorialDirectory = (props) => {
55+
const { addBasePath, environmentService } = getServices();
56+
const environment = environmentService.getEnvironment();
57+
const isCloudEnabled = environment.cloud;
58+
59+
return (
60+
<TutorialDirectory
61+
addBasePath={addBasePath}
62+
openTab={props.match.params.tab}
63+
isCloudEnabled={isCloudEnabled}
64+
withoutHomeBreadCrumb={props.withoutHomeBreadCrumb}
65+
/>
66+
);
67+
};
68+
69+
export function ImportSampleDataApp() {
70+
return (
71+
<I18nProvider>
72+
{renderTutorialDirectory({
73+
// Pass a fixed tab to avoid TutorialDirectory missing openTab property
74+
match: {
75+
params: { tab: 'sampleData' },
76+
},
77+
withoutHomeBreadCrumb: true,
78+
})}
79+
</I18nProvider>
80+
);
81+
}
82+
5483
export function HomeApp({ directories, solutions }) {
5584
const {
5685
savedObjectsClient,
@@ -63,16 +92,6 @@ export function HomeApp({ directories, solutions }) {
6392
const environment = environmentService.getEnvironment();
6493
const isCloudEnabled = environment.cloud;
6594

66-
const renderTutorialDirectory = (props) => {
67-
return (
68-
<TutorialDirectory
69-
addBasePath={addBasePath}
70-
openTab={props.match.params.tab}
71-
isCloudEnabled={isCloudEnabled}
72-
/>
73-
);
74-
};
75-
7695
const renderTutorial = (props) => {
7796
return (
7897
<Tutorial
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import React from 'react';
7+
import { render } from '@testing-library/react';
8+
import { setServices } from '../opensearch_dashboards_services';
9+
import { getMockedServices } from '../opensearch_dashboards_services.mock';
10+
import { ImportSampleDataApp, HomeApp } from './home_app';
11+
12+
jest.mock('./legacy/home', () => ({
13+
Home: () => <div>Home</div>,
14+
}));
15+
16+
jest.mock('../load_tutorials', () => ({
17+
getTutorial: () => {},
18+
}));
19+
20+
jest.mock('./tutorial_directory', () => ({
21+
TutorialDirectory: (props: { withoutHomeBreadCrumb?: boolean }) => (
22+
<div
23+
data-test-subj="tutorial_directory"
24+
data-without-home-bread-crumb={!!props.withoutHomeBreadCrumb}
25+
/>
26+
),
27+
}));
28+
29+
describe('<HomeApp />', () => {
30+
let currentService: ReturnType<typeof getMockedServices>;
31+
beforeEach(() => {
32+
currentService = getMockedServices();
33+
setServices(currentService);
34+
});
35+
36+
it('should not pass withoutHomeBreadCrumb to TutorialDirectory component', async () => {
37+
const originalHash = window.location.hash;
38+
const { findByTestId } = render(<HomeApp />);
39+
window.location.hash = '/tutorial_directory';
40+
const tutorialRenderResult = await findByTestId('tutorial_directory');
41+
expect(tutorialRenderResult.dataset.withoutHomeBreadCrumb).toEqual('false');
42+
43+
// revert to original hash
44+
window.location.hash = originalHash;
45+
});
46+
});
47+
48+
describe('<ImportSampleDataApp />', () => {
49+
let currentService: ReturnType<typeof getMockedServices>;
50+
beforeEach(() => {
51+
currentService = getMockedServices();
52+
setServices(currentService);
53+
});
54+
55+
it('should pass withoutHomeBreadCrumb to TutorialDirectory component', async () => {
56+
const { findByTestId } = render(<ImportSampleDataApp />);
57+
const tutorialRenderResult = await findByTestId('tutorial_directory');
58+
expect(tutorialRenderResult.dataset.withoutHomeBreadCrumb).toEqual('true');
59+
});
60+
});

src/plugins/home/public/application/components/tutorial_directory.js

+10-6
Original file line numberDiff line numberDiff line change
@@ -93,14 +93,17 @@ class TutorialDirectoryUi extends React.Component {
9393

9494
async componentDidMount() {
9595
this._isMounted = true;
96-
97-
getServices().chrome.setBreadcrumbs([
98-
{
96+
const { chrome } = getServices();
97+
const { withoutHomeBreadCrumb } = this.props;
98+
const breadcrumbs = [{ text: addDataTitle }];
99+
if (!withoutHomeBreadCrumb) {
100+
breadcrumbs.splice(0, 0, {
99101
text: homeTitle,
100102
href: '#/',
101-
},
102-
{ text: addDataTitle },
103-
]);
103+
});
104+
}
105+
106+
chrome.setBreadcrumbs(breadcrumbs);
104107

105108
const tutorialConfigs = await getTutorials();
106109

@@ -322,6 +325,7 @@ TutorialDirectoryUi.propTypes = {
322325
addBasePath: PropTypes.func.isRequired,
323326
openTab: PropTypes.string,
324327
isCloudEnabled: PropTypes.bool.isRequired,
328+
withoutHomeBreadCrumb: PropTypes.bool,
325329
};
326330

327331
export const TutorialDirectory = injectI18n(TutorialDirectoryUi);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import React from 'react';
7+
import { render } from '@testing-library/react';
8+
import { IntlProvider } from 'react-intl';
9+
import { coreMock } from '../../../../../core/public/mocks';
10+
import { setServices } from '../opensearch_dashboards_services';
11+
import { getMockedServices } from '../opensearch_dashboards_services.mock';
12+
13+
const makeProps = () => {
14+
const coreMocks = coreMock.createStart();
15+
return {
16+
addBasePath: coreMocks.http.basePath.prepend,
17+
openTab: 'foo',
18+
isCloudEnabled: false,
19+
};
20+
};
21+
22+
describe('<TutorialDirectory />', () => {
23+
let currentService: ReturnType<typeof getMockedServices>;
24+
beforeEach(() => {
25+
currentService = getMockedServices();
26+
setServices(currentService);
27+
});
28+
it('should render home breadcrumbs when withoutHomeBreadCrumb is undefined', async () => {
29+
const finalProps = makeProps();
30+
currentService.http.get.mockResolvedValueOnce([]);
31+
// @ts-ignore
32+
const { TutorialDirectory } = await import('./tutorial_directory');
33+
render(
34+
<IntlProvider locale="en">
35+
<TutorialDirectory {...finalProps} />
36+
</IntlProvider>
37+
);
38+
expect(currentService.chrome.setBreadcrumbs).toBeCalledWith([
39+
{
40+
href: '#/',
41+
text: 'Home',
42+
},
43+
{
44+
text: 'Add data',
45+
},
46+
]);
47+
});
48+
49+
it('should not render home breadcrumbs when withoutHomeBreadCrumb is true', async () => {
50+
const finalProps = makeProps();
51+
currentService.http.get.mockResolvedValueOnce([]);
52+
// @ts-ignore
53+
const { TutorialDirectory } = await import('./tutorial_directory');
54+
render(
55+
<IntlProvider locale="en">
56+
<TutorialDirectory {...finalProps} withoutHomeBreadCrumb />
57+
</IntlProvider>
58+
);
59+
expect(currentService.chrome.setBreadcrumbs).toBeCalledWith([
60+
{
61+
text: 'Add data',
62+
},
63+
]);
64+
});
65+
});

src/plugins/home/public/application/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,4 @@
2828
* under the License.
2929
*/
3030

31-
export { renderApp } from './application';
31+
export { renderApp, renderImportSampleDataApp } from './application';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import { coreMock } from '../../../../core/public/mocks';
7+
import { urlForwardingPluginMock } from '../../../url_forwarding/public/mocks';
8+
import { homePluginMock } from '../mocks';
9+
import {
10+
EnvironmentService,
11+
FeatureCatalogueRegistry,
12+
SectionTypeService,
13+
TutorialService,
14+
} from '../services';
15+
import { telemetryPluginMock } from '../../../telemetry/public/mocks';
16+
17+
export const getMockedServices = () => {
18+
const coreMocks = coreMock.createStart();
19+
const urlForwarding = urlForwardingPluginMock.createStartContract();
20+
const homePlugin = homePluginMock.createSetupContract();
21+
return {
22+
...coreMocks,
23+
...homePlugin,
24+
telemetry: telemetryPluginMock.createStartContract(),
25+
indexPatternService: jest.fn(),
26+
dataSource: {
27+
dataSourceEnabled: false,
28+
hideLocalCluster: false,
29+
noAuthenticationTypeEnabled: false,
30+
usernamePasswordAuthEnabled: false,
31+
awsSigV4AuthEnabled: false,
32+
},
33+
opensearchDashboardsVersion: '',
34+
urlForwarding,
35+
savedObjectsClient: coreMocks.savedObjects.client,
36+
toastNotifications: coreMocks.notifications.toasts,
37+
banners: coreMocks.overlays.banners,
38+
trackUiMetric: jest.fn(),
39+
getBasePath: jest.fn(),
40+
addBasePath: jest.fn(),
41+
environmentService: new EnvironmentService(),
42+
tutorialService: new TutorialService(),
43+
homeConfig: homePlugin.config,
44+
featureCatalogue: new FeatureCatalogueRegistry(),
45+
sectionTypes: new SectionTypeService(),
46+
};
47+
};

src/plugins/home/public/plugin.test.ts

+8
Original file line numberDiff line numberDiff line change
@@ -96,5 +96,13 @@ describe('HomePublicPlugin', () => {
9696
expect(setup).toHaveProperty('tutorials');
9797
expect(setup.tutorials).toHaveProperty('setVariable');
9898
});
99+
100+
test('wires up and register applications', async () => {
101+
const coreMocks = coreMock.createSetup();
102+
await new HomePublicPlugin(mockInitializerContext).setup(coreMocks, {
103+
urlForwarding: urlForwardingPluginMock.createSetupContract(),
104+
});
105+
expect(coreMocks.application.register).toBeCalledTimes(2);
106+
});
99107
});
100108
});

0 commit comments

Comments
 (0)