Skip to content

Commit

Permalink
feat: support cypher parameter
Browse files Browse the repository at this point in the history
refactor: change to  English comment

fix: remove sessionStorage

fix: wrapper safeParse

mod: add param query

mod: remove code

mod: update

mod: modify ui

mod: update style

mod: code review

mod: code review

mod: code review
  • Loading branch information
xigongdaEricyang authored and hetao92 committed Dec 29, 2021
1 parent e19daae commit 23be8c7
Show file tree
Hide file tree
Showing 8 changed files with 202 additions and 45 deletions.
3 changes: 2 additions & 1 deletion app/assets/components/CodeMirror.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export default class ReactCodeMirror extends React.PureComponent<IProps, any> {
if (stream.eatSpace()) {
return null;
}
stream.eatWhile(/[\$\w\u4e00-\u9fa5]/);
stream.eatWhile(/[\$:\w\u4e00-\u9fa5]/);
const cur = stream.current();
if (keyWords.some(item => item === cur)) {
return 'keyword';
Expand Down Expand Up @@ -127,6 +127,7 @@ export default class ReactCodeMirror extends React.PureComponent<IProps, any> {
change.preventDefault();
}
};

codemirrorValueChange = (doc, change) => {
if (change.origin !== 'setValue') {
if (this.props.onChange) {
Expand Down
36 changes: 32 additions & 4 deletions app/assets/components/OutputBox/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,22 @@ class OutputBox extends React.Component<IProps> {
};
render() {
const { value, result = {} } = this.props;
let columns = [];
let columns = [] as any;
let showSubgraphs = false;
const dataSource =
result.data && result.data.tables ? result.data.tables : [];
let dataSource = [] as any;
if (result.data && result.data.tables.length > 0) {
dataSource = result.data.tables;
} else if (result.data?.localParams) {
const params = {};
{
Object.entries(result.data?.localParams).forEach(
([k, v]) => (params[k] = JSON.stringify(v)),
);
}
dataSource = [{ ...params }];
}
if (result.code === 0) {
if (result.data && result.data.headers) {
if (result.data && result.data.headers.length > 0) {
columns = result.data.headers.map(column => {
return {
title: column,
Expand Down Expand Up @@ -121,6 +131,24 @@ class OutputBox extends React.Component<IProps> {
item._edgesParsedList ||
item._pathsParsedList,
).length > 0;
} else if (result.data?.localParams) {
columns = Object.keys(result.data?.localParams).map(column => {
return {
title: column,
dataIndex: column,
render: value => {
if (typeof value === 'boolean') {
return value.toString();
} else if (
typeof value === 'number' ||
BigNumber.isBigNumber(value)
) {
return value.toString();
}
return value;
},
};
});
}
}
return (
Expand Down
3 changes: 2 additions & 1 deletion app/assets/config/locale/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,8 @@
"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",
"deleteHistory": "Clear History"
"deleteHistory": "Clear History",
"parameterDisplay": "Custom Parameters Display"
},
"explore": {
"clear": "Clear",
Expand Down
3 changes: 2 additions & 1 deletion app/assets/config/locale/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@
"exportVertex": "请选择表中代表点VID的列",
"exportEdge": "请选择结果中分别代表边的起点(src_vid)、终点(dst_vid)和权重(rank)的列",
"showSubgraphs": "查看子图",
"deleteHistory": "清除历史"
"deleteHistory": "清除历史",
"parameterDisplay": "自定义参数 展示"
},
"explore": {
"clear": "清除",
Expand Down
5 changes: 5 additions & 0 deletions app/assets/config/nebulaQL.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,9 @@ const nebulaWordsUppercase = [
'USERS',
'UUID',
'VALUES',
'COMMENT',
':PARAM',
':PARAMS',
];

export const ban = ['use', 'USE'];
Expand Down Expand Up @@ -217,6 +220,8 @@ export const operators = [
'MINUS',
// uuid
'uuid',
// assignment
'=>',
];

const nebulaWordsLowercase = nebulaWordsUppercase.map(w => w.toLowerCase());
Expand Down
31 changes: 29 additions & 2 deletions app/assets/modules/Console/index.less
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,22 @@
flex-direction: column;
align-items: center;

> .CodeMirror {
.mirror-content {
width: 100%;
display: flex;
align-items: center;

.btn-drawer {
cursor: pointer;
}

> .CodeMirror {
min-height: 240px;
width: 100%;
}
}


.mirror-nav {
display: flex;
align-items: center;
Expand Down Expand Up @@ -79,7 +91,7 @@
flex: 1;
display: flex;
flex-direction: column;
margin: 32px 0;
margin: 15px 0;
}
}

Expand Down Expand Up @@ -124,3 +136,18 @@
}
}

.param-box {
padding: 10px;
background-color: white;
height: 100%;
margin-left: 5px;
max-width: 320px;
overflow: auto;
max-height: 240px;

p {
font-size: 12px;
word-break: break-all;
margin-bottom: 8px;
}
}
109 changes: 76 additions & 33 deletions app/assets/modules/Console/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,20 @@ import SpaceSearchInput from './SpaceSearchInput';
interface IState {
isUpDown: boolean;
history: boolean;
visible: boolean;
}

const mapState = (state: IRootState) => ({
result: state._console.result,
currentGQL: state._console.currentGQL,
paramsMap: state._console.paramsMap,
currentSpace: state.nebula.currentSpace,
runGQLLoading: state.loading.effects._console.asyncRunGQL,
});

const mapDispatch = (dispatch: IDispatch) => ({
asyncRunGQL: dispatch._console.asyncRunGQL,
asyncGetParams: dispatch._console.asyncGetParams,
updateCurrentGQL: gql =>
dispatch._console.update({
currentGQL: gql,
Expand All @@ -41,6 +44,8 @@ interface IProps
ReturnType<typeof mapDispatch>,
RouteComponentProps {}

// split from semicolon out of quotation marks
const SEMICOLON_REG = /((?:[^;'"]*(?:"(?:\\.|[^"])*"|'(?:\\.|[^'])*')[^;'"]*)+)|;/;
class Console extends React.Component<IProps, IState> {
codemirror;
editor;
Expand All @@ -51,11 +56,13 @@ class Console extends React.Component<IProps, IState> {
this.state = {
isUpDown: true,
history: false,
visible: false,
};
}

componentDidMount() {
trackPageView('/console');
this.props.asyncGetParams();
}

getLocalStorage = () => {
Expand All @@ -66,31 +73,35 @@ class Console extends React.Component<IProps, IState> {
return [];
};

handleSaveQuery = (query: string) => {
if (query !== '') {
const history = this.getLocalStorage();
history.push(query);
localStorage.setItem('history', JSON.stringify(history));
}
};

checkSwitchSpaceGql = (query: string) => {
const queryList = query.split(SEMICOLON_REG).filter(Boolean);
const reg = /^USE `?[0-9a-zA-Z_]+`?(?=[\s*;?]?)/gim;
if (queryList.some(sentence => sentence.trim().match(reg))) {
return intl.get('common.disablesUseToSwitchSpace');
}
};
handleRun = async () => {
const gql = this.editor.getValue();
if (!gql) {
const query = this.editor.getValue();
if (!query) {
message.error(intl.get('common.sorryNGQLCannotBeEmpty'));
return;
}
// Hack:
// replace the string in quotes with a constant, avoid special symbols in quotation marks from affecting regex match
// then split the gql entered by the user into sentences based on semicolons
// use regex to determine whether each sentence is a 'use space' statement
const _gql = gql
.replace(/[\r\n]/g, '')
.replace(/(["'])[^]*?\1/g, '_CONTENT_')
.toUpperCase();
const sentenceList = _gql.split(';');
const reg = /^USE `?[0-9a-zA-Z_]+`?(?=[\s*;?]?)/gm;
if (sentenceList.some(sentence => sentence.match(reg))) {
return message.error(intl.get('common.disablesUseToSwitchSpace'));
const errInfo = this.checkSwitchSpaceGql(query);
if (errInfo) {
return message.error(errInfo);
}
this.editor.execCommand('goDocEnd');
const history = this.getLocalStorage();
history.push(gql);
localStorage.setItem('history', JSON.stringify(history));

await this.props.asyncRunGQL(gql);
this.editor.execCommand('goDocEnd');
this.handleSaveQuery(query);
await this.props.asyncRunGQL(query);
this.setState({
isUpDown: true,
});
Expand Down Expand Up @@ -147,9 +158,22 @@ class Console extends React.Component<IProps, IState> {
return str.substring(0, 300) + '...';
};

toggleDrawer = () => {
const { visible } = this.state;
this.setState({
visible: !visible,
});
};

render() {
const { isUpDown, history } = this.state;
const { currentSpace, currentGQL, result, runGQLLoading } = this.props;
const {
currentSpace,
currentGQL,
result,
runGQLLoading,
paramsMap,
} = this.props;
return (
<div className="nebula-console padding-page">
<div className="ngql-content">
Expand Down Expand Up @@ -191,19 +215,38 @@ class Console extends React.Component<IProps, IState> {
</Tooltip>
</div>
</div>
<CodeMirror
value={currentGQL}
onBlur={value => this.props.updateCurrentGQL(value)}
onChangeLine={this.handleLineCount}
ref={this.getInstance}
height={isUpDown ? '240px' : 24 * maxLineNum + 'px'}
onShiftEnter={this.handleRun}
options={{
keyMap: 'sublime',
fullScreen: true,
mode: 'nebula',
}}
/>
<div className="mirror-content">
<Tooltip
title={intl.get('console.parameterDisplay')}
placement="right"
>
<Icon
type="file-search"
className="btn-drawer"
onClick={this.toggleDrawer}
/>
</Tooltip>
{this.state.visible && (
<div className="param-box">
{Object.entries(paramsMap).map(([k, v]) => (
<p key={k}>{`${k} => ${JSON.stringify(v)}`}</p>
))}
</div>
)}
<CodeMirror
value={currentGQL}
onBlur={value => this.props.updateCurrentGQL(value)}
onChangeLine={this.handleLineCount}
ref={this.getInstance}
height={isUpDown ? '240px' : 24 * maxLineNum + 'px'}
onShiftEnter={this.handleRun}
options={{
keyMap: 'sublime',
fullScreen: true,
mode: 'nebula',
}}
/>
</div>
</div>
</div>
<div className="result-wrap">
Expand Down
Loading

0 comments on commit 23be8c7

Please sign in to comment.