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 import task editing, re-running, draft saving #545

Merged
merged 2 commits into from
Apr 24, 2023
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
11 changes: 9 additions & 2 deletions app/config/locale/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@
"continue": "Continue",
"update": "Update",
"prev": "Previous",
"createTime": "Create Time"
"createTime": "Create Time",
"rerun": "Rerun"
},
"doc": {
"welcome": "Welcome to",
Expand Down Expand Up @@ -316,7 +317,13 @@
"sftpUsernameRequired": "Please enter the sftp username in the configuration file",
"sftpPasswordRequired": "Please enter the sftp password in the configuration file",
"ossAccessKeyRequired": "Please enter the oss accessKey in the configuration file",
"ossSecretKeyRequired": "Please enter the oss secretKey in the configuration file"
"ossSecretKeyRequired": "Please enter the oss secretKey in the configuration file",
"draft": "Draft",
"saveDraft": "Save Draft",
"modifyTime": "Modify time",
"taskNameRequired": "Please enter the task name and select space",
"fileMissing": "{files} does not exist, please re-upload the file",
"datasourceMissing": "The related data source of {files} is not found, please re-add the related datasource and reconfigure the task"
},
"schema": {
"spaceList": "Graph Space List",
Expand Down
11 changes: 9 additions & 2 deletions app/config/locale/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@
"continue": "继续",
"update": "更新",
"prev": "上一步",
"createTime": "创建时间"
"createTime": "创建时间",
"rerun": "重新导入"
},
"doc": {
"welcome": "欢迎使用",
Expand Down Expand Up @@ -316,7 +317,13 @@
"sftpUsernameRequired": "请在配置文件中输入 sftp 的用户名",
"sftpPasswordRequired": "请在配置文件中输入 sftp 的密码",
"ossAccessKeyRequired": "请在配置文件中输入 oss 的 accessKey",
"ossSecretKeyRequired": "请在配置文件中输入 oss 的 secretKey"
"ossSecretKeyRequired": "请在配置文件中输入 oss 的 secretKey",
"draft": "草稿",
"saveDraft": "保存草稿",
"modifyTime": "编辑时间",
"taskNameRequired": "请填写任务名称并选择图空间",
"fileMissing": "{files} 文件不存在,请重新上传文件或添加相关数据源",
"datasourceMissing": "{files} 所在数据源未找到,请重新添加相关数据源并重新配置任务"
},
"schema": {
"spaceList": "图空间列表",
Expand Down
3 changes: 3 additions & 0 deletions app/config/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ const service = {
stopImportTask: (id: number, config?) => {
return get(`/api/import-tasks/${id}/stop`)(undefined, config);
},
saveTaskDraft: (params, config?) => {
return post('/api/import-tasks/draft')(params, config);
},
deleteImportTask: (id: number, config) => {
return _delete(`/api/import-tasks/${id}`)(undefined, config);
},
Expand Down
3 changes: 3 additions & 0 deletions app/interfaces/import.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export enum ITaskStatus {
'NotExisted' = 'NotExisted',
'Aborted' = 'Failed',
'Pending' = 'Pending',
'Draft' = 'Draft',
}

export interface ITaskStats {
Expand Down Expand Up @@ -34,6 +35,7 @@ export interface ITaskItem {
status: ITaskStatus;
message: string;
stats: ITaskStats;
rawConfig: string;
}

export interface IPropertyProps {
Expand Down Expand Up @@ -78,6 +80,7 @@ export interface ISftpConfig {
passPhrase?: string;
}
export interface IBasicConfig {
id?: number;
taskName: string;
address: string[];
batchSize?: string;
Expand Down
1 change: 1 addition & 0 deletions app/pages/Import/DatasourceList/LocalFileList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ const FileList = () => {
dataSource={fileList}
rowSelection={{
type: 'checkbox',
selectedRowKeys: selectFiles,
onChange: (selectedRowKeys) => setSelectFiles(selectedRowKeys as string[]),
}}
columns={columns}
Expand Down
12 changes: 8 additions & 4 deletions app/pages/Import/TaskCreate/ConfigConfirmModal/index.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import { useStore } from '@app/stores';
import { ITagItem, IEdgeItem } from '@app/stores/import';
import { useI18n } from '@vesoft-inc/i18n';
import { Button, Input, Modal } from 'antd';
import { observer } from 'mobx-react-lite';
import React, { useState } from 'react';
import React, { useMemo, useState } from 'react';

import styles from './index.module.less';
interface IProps {
visible: boolean;
onConfirm: (password: string) => void
onCancel: () => void;
needPwdConfirm?: boolean;
config?: { tagConfig: ITagItem[], edgeConfig: IEdgeItem[] }
}
const ConfigConfirmModal = (props: IProps) => {
const { dataImport: { tagConfig, edgeConfig } } = useStore();
const { dataImport } = useStore();
const [password, setPassword] = useState('');
const { visible, onConfirm, onCancel } = props;
const { visible, onConfirm, onCancel, config } = props;
const { intl } = useI18n();
const handleConfirm = (password?: string) => {
onConfirm(password);
Expand All @@ -24,11 +25,14 @@ const ConfigConfirmModal = (props: IProps) => {
setPassword('');
onCancel();
};
const tagConfig = useMemo(() => config ? config.tagConfig : dataImport.tagConfig, [config]);
const edgeConfig = useMemo(() => config ? config.edgeConfig : dataImport.edgeConfig, [config]);
return (
<Modal
title={intl.get('import.importConfirm')}
open={visible}
onCancel={() => handleCancel()}
destroyOnClose
className={styles.importConfirmModal}
footer={false}
>
Expand Down
12 changes: 11 additions & 1 deletion app/pages/Import/TaskCreate/SchemaConfig/FileMapping/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ const VIDSetting = observer((props: {
data.update({ [idKey]: index });
index.length > 1 && setKey(['default']);
}, [data]);
useEffect(() => {
([idPrefix, idSuffix, idFunction].some(i => !!data[i]) || data[idKey].length > 1) && setKey(['default']);
}, []);
return <div className={styles.row}>
<Collapse bordered={false} ghost className={styles.vidCollapse} onChange={handleChange} activeKey={key}>
<Panel header={<div className={cls(styles.panelTitle, styles.spaceBetween)}>
Expand Down Expand Up @@ -156,6 +159,10 @@ const FileMapping = (props: IProps) => {
});

useEffect(() => {
item.file && setSelectFile({
file: item.file,
cachedState: null
});
cachedStore && setSelectFile({
file: null,
cachedState: cachedStore,
Expand Down Expand Up @@ -220,7 +227,10 @@ const FileMapping = (props: IProps) => {
<Button type="link" onClick={() => setVisible(true)}>{selectFile.file ? selectFile.file.name : intl.get('import.selectFile')}</Button>
{selectFile.file && <div className={styles.pathRow}>
<span className={styles.pathLabel}>{intl.get('import.filePath')}</span>
<span className={styles.pathValue}>{selectFile.cachedState.path + selectFile.file.name}</span>
<span className={styles.pathValue}>{selectFile.cachedState
? selectFile.cachedState.path + selectFile.file.name
: selectFile.file.path || selectFile.file.name
}</span>
</div>}
</div>
<CloseOutlined className={styles.btnClose} onClick={() => onRemove(item)} />
Expand Down
73 changes: 53 additions & 20 deletions app/pages/Import/TaskCreate/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { observer } from 'mobx-react-lite';
import { useStore } from '@app/stores';
import { trackEvent, trackPageView } from '@app/utils/stat';
import cls from 'classnames';
import { useHistory } from 'react-router-dom';
import { useHistory, useLocation } from 'react-router-dom';
import { DEFAULT_IMPORT_CONFIG, POSITIVE_INTEGER_REGEX } from '@app/utils/constant';
import { useI18n } from '@vesoft-inc/i18n';
import { ISchemaEnum, ISchemaType } from '@app/interfaces/schema';
Expand All @@ -23,11 +23,6 @@ const formItemLayout = {
},
};

interface IProps {
needPwdConfirm?: boolean;
}


const AddMappingBtn = (props: { type: ISchemaType }) => {
const { intl } = useI18n();
const { type } = props;
Expand All @@ -39,16 +34,17 @@ const AddMappingBtn = (props: { type: ISchemaType }) => {
</Button>;
};

const TaskCreate = (props: IProps) => {
const TaskCreate = () => {
const { dataImport, schema, files, global, datasource } = useStore();
const { intl, currentLocale } = useI18n();
const { basicConfig, tagConfig, edgeConfig, updateBasicConfig, importTask } = dataImport;
const { spaces, getSpaces, updateSpaceInfo, currentSpace } = schema;
const { basicConfig, tagConfig, edgeConfig, updateBasicConfig, importTask, saveTaskDraft, setDraft, envCfg } = dataImport;
const { needPwdConfirm } = envCfg;
const { spaces, getSpaces, updateSpaceInfo, currentSpace, spaceVidType } = schema;
const { getGraphAddress, _host } = global;
const { getFiles } = files;
const [modalVisible, setVisible] = useState(false);
const history = useHistory();
const { needPwdConfirm = true } = props;
const location = useLocation();
const [address, setAddress] = useState([]);
const [loading, setLoading] = useState(false);
const [showMoreConfig, setShowMoreConfig] = useState(false);
Expand All @@ -64,10 +60,15 @@ const TaskCreate = (props: IProps) => {
]), [currentLocale]);

useEffect(() => {
const { state } = location;
initTaskDir();
getSpaces();
getFiles();
if(currentSpace) {
if(state) {
setShowMoreConfig(true);
updateSpaceInfo(state.space);
setDraft(JSON.parse(state.cfg));
} else if (currentSpace) {
updateSpaceInfo(currentSpace);
}
trackPageView('/import/create');
Expand All @@ -88,10 +89,27 @@ const TaskCreate = (props: IProps) => {
const handleStartImport = async (password?: string) => {
setVisible(false);
setLoading(true);
const code = await importTask({
const { state } = location;
const payload = {
name: basicConfig.taskName,
password
});
password,
config: {
basicConfig,
tagConfig,
edgeConfig,
space: currentSpace,
spaceVidType
},
type: 'create'
} as any;
if(state?.id !== undefined) {
if(state?.isDraft) {
payload.id = state.id;
} else {
payload.type = 'rebuild';
}
}
const code = await importTask(payload);
setLoading(false);
if(code === 0) {
message.success(intl.get('import.startImporting'));
Expand Down Expand Up @@ -159,11 +177,26 @@ const TaskCreate = (props: IProps) => {
clearConfig();
updateSpaceInfo(space);
}, []);

const saveDraft = async () => {
if(!currentSpace || !basicConfig.taskName) {
message.error(intl.get('import.taskNameRequired'));
return;
}
setLoading(true);
const id = location.state?.isDraft ? location.state?.id : undefined;
const code = await saveTaskDraft(id);
setLoading(false);
if(code === 0) {
message.success(intl.get('sketch.saveSuccess'));
history.push('/import/tasks');
}
};
const initTaskDir = useCallback(async () => {
updateBasicConfig({ 'taskName': `task-${Date.now()}` });
const { state } = location;
const graphs = await getGraphAddress();
updateBasicConfig({ 'address': graphs });
if(!state) {
updateBasicConfig({ 'taskName': `task-${Date.now()}`, 'address': graphs });
}
setAddress(graphs.map(item => ({
label: <>
{item}
Expand Down Expand Up @@ -283,7 +316,7 @@ const TaskCreate = (props: IProps) => {
<Form.Item label={<>
<span className={styles.label}>{item.label}</span>
<Instruction description={item.description} />
</>} name={item.key} rules={item.rules}>
</>} name={item.key} rules={item.rules} initialValue={basicConfig[item.key]}>
<Input placeholder={item.placeholder.toString()} value={basicConfig[item.key]} onChange={e => {
updateBasicConfig({ [item.key]: e.target.value });
trackEvent('import', `update_config_${item.key}`);
Expand Down Expand Up @@ -327,13 +360,13 @@ const TaskCreate = (props: IProps) => {
</div>
<div className="studioFormFooter">
<Button onClick={() => history.push('/import/tasks')}>{intl.get('common.cancel')}</Button>
<Button onClick={saveDraft} loading={loading}>{intl.get('import.saveDraft')}</Button>
<Button type="primary" disabled={
basicConfig.taskName === ''
|| (!tagConfig.length && !edgeConfig.length)
} onClick={checkConfig} loading={!needPwdConfirm && loading}>{intl.get('import.runImport')}</Button>
} onClick={checkConfig} loading={loading}>{intl.get('import.runImport')}</Button>
</div>
{modalVisible && <ConfigConfirmModal
needPwdConfirm={needPwdConfirm}
visible={modalVisible}
onConfirm={handleStartImport}
onCancel={() => setVisible(false)}
Expand Down
14 changes: 7 additions & 7 deletions app/pages/Import/TaskList/TaskItem/LogModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ interface IProps {
logDimension: ILogDimension;
visible: boolean;
onCancel: () => void;
showLogDownload: boolean
}

let isMounted = true;

const LogModal = (props: IProps) => {
const { visible, onCancel, showLogDownload, logDimension: { space, id, status } } = props;
const { dataImport: { getLogs, downloadTaskLog, getLogDetail } } = useStore();
const { visible, onCancel, logDimension: { space, id, status } } = props;
const { dataImport: { getLogs, downloadTaskLog, getLogDetail, envCfg } } = useStore();
const { supportLogDownload } = envCfg;
const { intl } = useI18n();
const logRef = useRef<HTMLDivElement>(null);
const timer = useRef<any>(null);
Expand Down Expand Up @@ -93,7 +93,7 @@ const LogModal = (props: IProps) => {
};
useEffect(() => {
isMounted = true;
if(showLogDownload) {
if(supportLogDownload) {
getAllLogs();
} else {
initLog();
Expand Down Expand Up @@ -128,7 +128,7 @@ const LogModal = (props: IProps) => {
<span>{`${space} ${intl.get('import.task')} - ${intl.get('common.log')}`}</span>
{(loading || [ITaskStatus.Processing, ITaskStatus.Pending].includes(status)) && <Button type="text" loading={true} />}
</div>
{showLogDownload && <Button className="studioAddBtn primaryBtn" onClick={handleLogDownload}>
{supportLogDownload && <Button className="studioAddBtn primaryBtn" onClick={handleLogDownload}>
<Icon type="icon-studio-btn-download" />
{intl.get('import.downloadLog')}
</Button>}
Expand All @@ -140,8 +140,8 @@ const LogModal = (props: IProps) => {
destroyOnClose={true}
footer={false}
>
{showLogDownload && <Tabs className={styles.logTab} tabBarGutter={0} tabPosition="left" onChange={handleTabChange} items={items} />}
<div className={classnames(styles.logContainer, !showLogDownload && styles.full)} ref={logRef}/>
{supportLogDownload && <Tabs className={styles.logTab} tabBarGutter={0} tabPosition="left" onChange={handleTabChange} items={items} />}
<div className={classnames(styles.logContainer, !supportLogDownload && styles.full)} ref={logRef}/>
</Modal>
);
};
Expand Down
11 changes: 10 additions & 1 deletion app/pages/Import/TaskList/TaskItem/index.module.less
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,20 @@
margin-bottom: 12px;
padding-right: calc(2em + 8px);
.taskName {
.draftLabel {
background: #DBEFFF;
border-radius: 4px;
padding: 6px 12px;
font-weight: 500;
font-size: 14px;
color: @darkBlue;
margin-right: 10px;
}
font-family: Roboto-Bold, serif;
font-style: normal;
font-size: 14px;
color: @lightBlack;
& > span {
& > span:not(.draftLabel) {
margin-left: 12px;
}
.completeInfo {
Expand Down
Loading