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

feat: support open in explorer #141

Merged
merged 1 commit into from
Mar 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ name: Studio nightly Package
on:
push:
branches:
- master
- v3.3.0-dev
jobs:
call-workflow:
uses: vesoft-inc/nebula-studio/.github/workflows/build.yml@master
uses: vesoft-inc/nebula-studio/.github/workflows/build.yml@v3.3.0-dev
secrets:
oss_endpoint: ${{ secrets.OSS_ENDPOINT }}
oss_id: ${{ secrets.OSS_ID }}
Expand Down
3 changes: 3 additions & 0 deletions app/app.less
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@
border: none;
white-space: nowrap;
color: @darkBlue;
&.ant-radio-button-wrapper-checked {
color: #fff;
}

&:not(.ant-radio-button-wrapper-checked) {
background: none;
Expand Down
4 changes: 2 additions & 2 deletions app/config/locale/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"import": "Import",
"ask": "Are you sure to proceed?",
"output": "Export CSV File",
"openInExplore": "Open In Explore",
"openInExplore": "Open In Explorer",
"schema": "Schema",
"create": "Create",
"serialNumber": "No.",
Expand Down Expand Up @@ -137,7 +137,7 @@
"execTime": "Execution Time",
"exportVertex": "Please choose the column representing vertex IDs from the table",
"exportEdge": "Please choose the columns representing source vertex ID, destination vertex ID, and rank of an edge",
"showSubgraphs": "View Subgraphs",
"showSubgraphs": "Open in Explorer",
"deleteHistory": "Clear History",
"cypherParam": "Cypher Parameter",
"favorites": "Favorites",
Expand Down
148 changes: 148 additions & 0 deletions app/pages/Console/ExportModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import { Button, Form, Input, Modal, Radio, Select } from 'antd';
import React from 'react';
import intl from 'react-intl-universal';
import { observer } from 'mobx-react-lite';
import { useStore } from '@app/stores';

import './index.less';
const Option = Select.Option;

const layout = {
labelCol: { span: 10 },
wrapperCol: { span: 8 },
};

interface IProps {
data: any;
visible: boolean;
onClose: () => void;
onExplorer: (params: {
space: string;
vertexes: any[],
edges: any[]
}) => void
}
const ExportModal = (props: IProps) => {
const { data, visible, onClose, onExplorer } = props;
const { schema: { currentSpace } } = useStore();
const { headers, tables } = data;
const handleExport = (values) => {
const { type, vertexId, srcId, dstId, edgeType, rank } = values;
const vertexes =
type === 'vertex'
? tables
.map(vertex => {
if (vertex.type === 'vertex') {
return vertex.vid;
} else {
return vertex[vertexId].toString();
}
})
.filter(vertexId => vertexId !== '')
: tables
.map(edge => [edge[srcId], edge[dstId]])
.flat()
.filter(id => id !== '');
const edges =
type === 'edge'
? tables
.map(edge => ({
srcId: edge[srcId],
dstId: edge[dstId],
rank: rank !== '' && rank !== undefined ? edge[rank] : 0,
edgeType,
}))
.filter(edge => edge.srcId !== '' && edge.dstId !== '')
: [];
onExplorer({
space: currentSpace,
vertexes,
edges
});
};
if(!data) {
return;
}
return (
<Modal
className="export-node-modal"
footer={null}
width="650px"
visible={visible}
onCancel={onClose}
>
<Form {...layout} onFinish={handleExport} initialValues={{
type: 'vertex'
}}>
<Form.Item name="type" className="select-type">
<Radio.Group className="nebula-tab-group" buttonStyle="solid">
<Radio.Button value="vertex">
{intl.get('import.vertexText')}
</Radio.Button>
<Radio.Button value="edge">
{intl.get('common.edge')}
</Radio.Button>
</Radio.Group>
</Form.Item>
<Form.Item noStyle={true} dependencies={['type']}>
{({ getFieldValue }) => {
const type = getFieldValue('type');
return type === 'vertex' ? <>
<p>{intl.get('console.exportVertex')}</p>
<Form.Item className="select-component" label="vid" name="vertexId" rules={[{ required: true }]}>
<Select>
{headers.map(i => (
<Option value={i} key={i}>
{i}
</Option>
))}
</Select>
</Form.Item>
</> : <>
<p>{intl.get('console.exportEdge')}</p>
<Form.Item className="select-component" label="Edge Type" name="edgeType" rules={[{ required: true }]}>
<Input />
</Form.Item>
<Form.Item className="select-component" label="Src ID" name="srcId" rules={[{ required: true }]}>
<Select>
{headers.map(i => (
<Option value={i} key={i}>
{i}
</Option>
))}
</Select>
</Form.Item>
<Form.Item className="select-component" label="Dst ID" name="dstId" rules={[{ required: true }]}>
<Select>
{headers.map(i => (
<Option value={i} key={i}>
{i}
</Option>
))}
</Select>
</Form.Item>
<Form.Item className="select-component" label="Rank" name="rank">
<Select allowClear={true}>
{headers.map(i => (
<Option value={i} key={i}>
{i}
</Option>
))}
</Select>
</Form.Item>
</>;
}}
</Form.Item>
<Form.Item noStyle={true}>
<Button
htmlType="submit"
type="primary"
>
{intl.get('common.import')}
</Button>
</Form.Item>
</Form>
</Modal>
);
};
export default observer(ExportModal);
1 change: 1 addition & 0 deletions app/pages/Console/OutputBox/index.less
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@
.output-footer {
display: flex;
align-items: center;
justify-content: space-between;
padding: 15px 18px;
width: 100%;
height: 65px;
Expand Down
40 changes: 37 additions & 3 deletions app/pages/Console/OutputBox/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Table, Tabs, Tooltip } from 'antd';
import { Button, Table, Tabs, Tooltip } from 'antd';
import { BigNumber } from 'bignumber.js';
import React, { useCallback, useEffect, useState } from 'react';
import intl from 'react-intl-universal';
Expand All @@ -8,6 +8,7 @@ import { trackEvent } from '@app/utils/stat';
import { v4 as uuidv4 } from 'uuid';
import Icon from '@app/components/Icon';
import Graphviz from './Graphviz';
import { parseSubGraph } from '@app/utils/parseData';

import './index.less';
import classNames from 'classnames';
Expand All @@ -17,14 +18,21 @@ interface IProps {
gql: string;
result: any;
onHistoryItem: (gql: string) => void;
onExplorer?: (params: {
space: string;
vertexes: any[],
edges: any[]
}) => void;
onResultConfig?: (data: any) => void
}

const OutputBox = (props: IProps) => {
const { gql, result: { code, data, message }, onHistoryItem, index } = props;
const { global, console } = useStore();
const { gql, result: { code, data, message }, onHistoryItem, index, onExplorer, onResultConfig } = props;
const { global, console, schema } = useStore();
const [visible, setVisible] = useState(true);
const { username, host } = global;
const { results, update, favorites, updateFavorites } = console;
const { spaceVidType, currentSpace } = schema;
const [columns, setColumns] = useState<any>([]);
const [dataSource, setDataSource] = useState<any>([]);
const [isFavorited, setIsFavorited] = useState(false);
Expand Down Expand Up @@ -165,6 +173,31 @@ const OutputBox = (props: IProps) => {
link.click();
document.body.removeChild(link);
};

const handleExplore = () => {
if (
data.tables.filter(
item =>
item._verticesParsedList ||
item._edgesParsedList ||
item._pathsParsedList,
).length > 0
) {
parseToGraph();
} else {
onResultConfig!(data);
}
};

const parseToGraph = () => {
const { vertexes, edges } = parseSubGraph(data.tables, spaceVidType);
onExplorer!({
space: currentSpace,
vertexes,
edges
});
};

return <div className="output-box">
<div className="output-header">
<p className={classNames('gql', { 'error-info': code !== 0 })} onClick={() => onHistoryItem(gql)}>
Expand Down Expand Up @@ -266,6 +299,7 @@ const OutputBox = (props: IProps) => {
{`${intl.get('console.execTime')} ${data.timeCost /
1000000} (s)`}
</span>
{onExplorer && <Button className="primary-btn" type="text" onClick={handleExplore}>{intl.get('common.openInExplore')}</Button>}
</div>
)}
</>}
Expand Down
9 changes: 9 additions & 0 deletions app/pages/Console/index.less
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,15 @@
}
}

.export-node-modal {
.select-type {
display: inline-flex;
}
.ant-modal-body {
text-align: center;
}
}

.historyList, .favoriteList {
height: 80%;
overflow: hidden;
Expand Down
36 changes: 34 additions & 2 deletions app/pages/Console/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Button, Select, Tooltip, message } from 'antd';
import React, { useEffect, useRef, useState } from 'react';
import intl from 'react-intl-universal';
import { observer } from 'mobx-react-lite';
import { trackPageView } from '@app/utils/stat';
import { trackPageView, trackEvent } from '@app/utils/stat';
import { useStore } from '@app/stores';
import Instruction from '@app/components/Instruction';
import Icon from '@app/components/Icon';
Expand All @@ -12,6 +12,7 @@ import { maxLineNum } from '@app/config/nebulaQL';
import HistoryBtn from './HistoryBtn';
import FavoriteBtn from './FavoriteBtn';
import CypherParameterBox from './CypherParameterBox';
import ExportModal from './ExportModal';
import './index.less';
const Option = Select.Option;

Expand All @@ -26,12 +27,22 @@ const getHistory = () => {
return [];
};

const Console = () => {
interface IProps {
onExplorer?: (params: {
space: string;
vertexes: any[],
edges: any[]
}) => void
}
const Console = (props: IProps) => {
const { schema, console, global } = useStore();
const { onExplorer } = props;
const { spaces, getSpaces, switchSpace, currentSpace } = schema;
const { runGQL, currentGQL, results, runGQLLoading, getParams, update, paramsMap } = console;
const { username, host } = global;
const [isUpDown, setUpDown] = useState(false);
const [modalVisible, setModalVisible] = useState(false);
const [modalData, setModalData] = useState<any>(null);
const editor = useRef<any>(null);
useEffect(() => {
trackPageView('/console');
Expand Down Expand Up @@ -93,6 +104,20 @@ const Console = () => {
const addParam = (param: string) => {
update({ currentGQL: currentGQL + ` $${param}` });
};

const handleResultConfig = (data: any) => {
setModalData(data);
setModalVisible(true);
};

const handleExplorer = (data) => {
if(!onExplorer) {
return;
}
onExplorer!(data);
!modalVisible && setModalVisible(false);
trackEvent('navigation', 'view_explore', 'from_console_btn');
};
return (
<div className="nebula-console">
<div className="space-select">
Expand Down Expand Up @@ -146,7 +171,9 @@ const Console = () => {
index={index}
result={item}
gql={item.gql}
onExplorer={onExplorer ? handleExplorer : undefined}
onHistoryItem={gql => updateGql(gql)}
onResultConfig={handleResultConfig}
/>
)) : <OutputBox
key="empty"
Expand All @@ -157,6 +184,11 @@ const Console = () => {
/>}
</div>
</div>
{modalVisible && <ExportModal
visible={modalVisible}
data={modalData}
onClose={() => setModalVisible(false)}
onExplorer={handleExplorer} />}
</div>
);
};
Expand Down
1 change: 1 addition & 0 deletions scripts/deb/start.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#!/bin/bash
mkdir -p /usr/lib/systemd/system
cd /usr/local/nebula-graph-studio/
cp ./lib/nebula-graph-studio.service /usr/lib/systemd/system/
sudo systemctl daemon-reload && sudo systemctl enable nebula-graph-studio.service && sudo systemctl start nebula-graph-studio.service