Skip to content

Commit

Permalink
[Data Explorer] Initial commit (#4292)
Browse files Browse the repository at this point in the history
* create legacy plugin and new data explorer plugin

Signed-off-by: Ashwin P Chandran <ashwinpc@amazon.com>

* Adds license header

Signed-off-by: Ashwin P Chandran <ashwinpc@amazon.com>

* adds legacy toggle

Signed-off-by: Ashwin P Chandran <ashwinpc@amazon.com>

* fixes nav link

Signed-off-by: Ashwin P Chandran <ashwinpc@amazon.com>

* working data explorer routing basic

Signed-off-by: Ashwin P Chandran <ashwinpc@amazon.com>

* sets the toggle to true for functional tests

Signed-off-by: Ashwin P Chandran <ashwinpc@amazon.com>

* removes components that fail tests and are not necessary

Signed-off-by: Ashwin P Chandran <ashwinpc@amazon.com>

---------

Signed-off-by: Ashwin P Chandran <ashwinpc@amazon.com>
  • Loading branch information
ashwin-pc committed Jun 28, 2023
1 parent 487cd36 commit d4acb33
Show file tree
Hide file tree
Showing 274 changed files with 11,373 additions and 129 deletions.
7 changes: 7 additions & 0 deletions src/plugins/data_explorer/.i18nrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"prefix": "dataExplorer",
"paths": {
"dataExplorer": "."
},
"translations": ["translations/ja-JP.json"]
}
11 changes: 11 additions & 0 deletions src/plugins/data_explorer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# dataExplorer

A OpenSearch Dashboards plugin

---

## Development

See the [OpenSearch Dashboards contributing
guide](https://github.com/opensearch-project/OpenSearch-Dashboards/blob/main/CONTRIBUTING.md) for instructions
setting up your development environment.
7 changes: 7 additions & 0 deletions src/plugins/data_explorer/common/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

export const PLUGIN_ID = 'data-explorer';
export const PLUGIN_NAME = 'Data Explorer';
10 changes: 10 additions & 0 deletions src/plugins/data_explorer/opensearch_dashboards.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"id": "dataExplorer",
"version": "1.0.0",
"opensearchDashboardsVersion": "opensearchDashboards",
"server": true,
"ui": true,
"requiredPlugins": ["navigation"],
"optionalPlugins": [],
"requiredBundles": ["opensearchDashboardsReact"]
}
40 changes: 40 additions & 0 deletions src/plugins/data_explorer/public/application.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import ReactDOM from 'react-dom';
import { Router, Route, Switch } from 'react-router-dom';
import { AppMountParameters, CoreStart } from '../../../core/public';
import { OpenSearchDashboardsContextProvider } from '../../opensearch_dashboards_react/public';
import { DataExplorerServices } from './types';
import { DataExplorerApp } from './components/app';

export const renderApp = (
{ notifications, http }: CoreStart,
services: DataExplorerServices,
{ appBasePath, element, history }: AppMountParameters
) => {
ReactDOM.render(
<Router history={history}>
<OpenSearchDashboardsContextProvider services={services}>
<services.i18n.Context>
<Switch>
<Route path={[`/:appId`, '/']} exact={false}>
<DataExplorerApp
basename={appBasePath}
notifications={notifications}
http={http}
history={history}
/>
</Route>
</Switch>
</services.i18n.Context>
</OpenSearchDashboardsContextProvider>
</Router>,
element
);

return () => ReactDOM.unmountComponentAtNode(element);
};
41 changes: 41 additions & 0 deletions src/plugins/data_explorer/public/components/app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

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

import { CoreStart, ScopedHistory } from '../../../../core/public';
import { Sidebar } from './sidebar';
import { useView } from '../utils/use';
import { NoView } from './no_view';

interface DataExplorerAppDeps {
basename: string;
notifications: CoreStart['notifications'];
http: CoreStart['http'];
history: ScopedHistory;
}

export const DataExplorerApp = ({ basename, history }: DataExplorerAppDeps) => {
const { view } = useView();

if (!view) {
return <NoView />;
}

// Render the application DOM.
// Note that `navigation.ui.TopNavMenu` is a stateful component exported on the `navigation` plugin's start contract.
return (
<EuiPageTemplate
pageSideBar={<Sidebar />}
className="dePageTemplate"
template="default"
restrictWidth={false}
paddingSize="none"
>
{view.ui.canvas}
</EuiPageTemplate>
);
};
32 changes: 32 additions & 0 deletions src/plugins/data_explorer/public/components/no_view.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import { EuiPageTemplate, EuiEmptyPrompt } from '@elastic/eui';

export const NoView = () => {
return (
<EuiPageTemplate
template="centeredContent"
className="dePageTemplate"
pageContentProps={{
role: 'alertdialog',
color: 'plain',
hasBorder: false,
}}
>
<EuiEmptyPrompt
iconType="alert"
iconColor="danger"
title={<h2>View not found</h2>}
body={
<p>
The view you are trying to access does not exist. Please check the URL and try again.
</p>
}
/>
</EuiPageTemplate>
);
};
40 changes: 40 additions & 0 deletions src/plugins/data_explorer/public/components/sidebar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React, { useMemo } from 'react';
import { EuiPanel, EuiComboBox, EuiSelect, EuiSelectOption } from '@elastic/eui';
import { useView } from '../utils/use';

export const Sidebar = () => {
const { view, viewRegistry } = useView();
const views = viewRegistry.all();
const viewOptions: EuiSelectOption[] = useMemo(
() =>
views.map(({ id, title }) => ({
value: id,
text: title,
})),
[views]
);
return (
<>
<EuiPanel borderRadius="none" hasShadow={false}>
<EuiComboBox
placeholder="Select a datasource"
singleSelection={{ asPlainText: true }}
options={[
{
label: 'Select a datasource',
},
]}
selectedOptions={[]}
onChange={() => {}}
/>
<EuiSelect options={viewOptions} value={view?.id} />
</EuiPanel>
{view?.ui.panel}
</>
);
};
5 changes: 5 additions & 0 deletions src/plugins/data_explorer/public/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
$osdHeaderOffset: $euiHeaderHeightCompensation;

.dePageTemplate {
height: calc(100vh - #{$osdHeaderOffset});
}
15 changes: 15 additions & 0 deletions src/plugins/data_explorer/public/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import './index.scss';

import { DataExplorerPlugin } from './plugin';

// This exports static code and TypeScript types,
// as well as, OpenSearch Dashboards Platform `plugin()` initializer.
export function plugin() {
return new DataExplorerPlugin();
}
export { DataExplorerPluginSetup, DataExplorerPluginStart, ViewRedirectParams } from './types';
54 changes: 54 additions & 0 deletions src/plugins/data_explorer/public/plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import {
AppMountParameters,
CoreSetup,
CoreStart,
Plugin,
AppNavLinkStatus,
} from '../../../core/public';
import { DataExplorerPluginSetup, DataExplorerPluginStart, DataExplorerServices } from './types';
import { PLUGIN_ID, PLUGIN_NAME } from '../common';
import { ViewService } from './services/view_service';

export class DataExplorerPlugin
implements Plugin<DataExplorerPluginSetup, DataExplorerPluginStart> {
private viewService = new ViewService();

public setup(core: CoreSetup): DataExplorerPluginSetup {
const viewService = this.viewService;
// Register an application into the side navigation menu
core.application.register({
id: PLUGIN_ID,
title: PLUGIN_NAME,
navLinkStatus: AppNavLinkStatus.hidden,
async mount(params: AppMountParameters) {
const [coreStart, depsStart] = await core.getStartServices();

const services: DataExplorerServices = {
...coreStart,
viewRegistry: viewService.start(),
};

// Load application bundle
const { renderApp } = await import('./application');
// Get start services as specified in opensearch_dashboards.json
// Render the application
return renderApp(coreStart, services, params);
},
});

return {
...this.viewService.setup(),
};
}

public start(core: CoreStart): DataExplorerPluginStart {
return {};
}

public stop() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

export * from './view_service';
export * from './types';
31 changes: 31 additions & 0 deletions src/plugins/data_explorer/public/services/view_service/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/
import { ReactElement } from 'react';

// TODO: Correctly type this file.

interface ViewListItem {
id: string;
label: string;
}

export interface ViewDefinition<T = any> {
readonly id: string;
readonly title: string;
readonly ui: {
panel: ReactElement;
canvas: ReactElement;
defaults: T;
reducer: (state: T, action: any) => T;
};
readonly defaultPath: string;
readonly appExtentions: {
savedObject: {
docTypes: [string];
toListItem: (obj: { id: string; title: string }) => ViewListItem;
};
};
readonly shouldShow?: (state: any) => boolean;
}
25 changes: 25 additions & 0 deletions src/plugins/data_explorer/public/services/view_service/view.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/
import { ViewDefinition } from './types';

type IView = ViewDefinition;

export class View implements IView {
public readonly id: string;
public readonly title: string;
public readonly ui: IView['ui'];
public readonly defaultPath: string;
public readonly appExtentions: IView['appExtentions'];
readonly shouldShow?: (state: any) => boolean;

constructor(options: ViewDefinition) {
this.id = options.id;
this.title = options.title;
this.ui = options.ui;
this.defaultPath = options.defaultPath;
this.appExtentions = options.appExtentions;
this.shouldShow = options.shouldShow;
}
}
Loading

0 comments on commit d4acb33

Please sign in to comment.