feat: 重构数据导入功能

This commit is contained in:
suxiaoxin 2017-09-15 12:01:17 +08:00
parent 533b45b280
commit d08e058c4f
7 changed files with 304 additions and 212 deletions

View File

@ -29,14 +29,7 @@ module.exports = {
"error",
"unix"
],
"quotes": [
"error",
"single"
],
"semi": [
"error",
"always"
],
"strict": 0,
"comma-dangle": ["error", "never"],
"no-console": ["off"]

View File

@ -7,6 +7,10 @@ import axios from 'axios';
const Dragger = Upload.Dragger;
const Option = Select.Option;
const plugin = require('client/plugin.js');
let importDataModule = {};
@connect(
state=>{
// console.log(state);
@ -24,23 +28,24 @@ class ProjectData extends Component {
super(props);
this.state = {
selectCatid:"",
menuList:[]
menuList:[],
curImportType: null
}
}
static propTypes = {
match: PropTypes.object,
projectId: PropTypes.number,
curCatid: PropTypes.number,
basePath: PropTypes.string
}
componentWillMount(){
componentWillMount(){
axios.get(`/api/interface/getCatMenu?project_id=${this.props.match.params.id}`).then((data)=>{
let menuList = data.data.data;
this.setState({
menuList: menuList
})
});
plugin.emitHook('import_data', importDataModule, this.props);
}
selectChange(value){
this.setState({
@ -48,149 +53,7 @@ class ProjectData extends Component {
})
}
parseUrl(url){
let parser = document.createElement('a');
parser.href = url;
return {
protocol: parser.protocol,
hostname: parser.hostname,
port: parser.port,
pathname: parser.pathname,
search: parser.search,
host: parser.host
}
}
checkInterRepeat(interData){
let obj = {};
let arr = [];
for(let item in interData){
// console.log(interData[item].url + "-" + interData[item].method);
if(!obj[interData[item].url + "-" + interData[item].method+ "-"+interData[item].method]){
arr.push(interData[item]);
obj[interData[item].url + "-" + interData[item].method+ "-"+interData[item].method] = true;
}
}
return arr;
}
handleReq_query(query){
let res = [];
if(query&&query.length){
for(let item in query){
res.push({
name: query[item].key,
desc: query[item].description,
required: query[item].enable
});
}
}
return res;
}
handleReq_headers(headers){
let res = [];
if(headers&&headers.length){
for(let item in headers){
res.push({
name: headers[item].key,
desc: headers[item].description,
value: headers[item].value,
required: headers[item].enable
});
}
}
return res;
}
handleReq_body_form(body_form){
let res = [];
if(body_form&&body_form.length){
for(let item in body_form){
res.push({
name: body_form[item].key,
value: body_form[item].value,
type: body_form[item].type
});
}
}
return res;
}
handlePath(path){
path = path.replace(/{{(\w*)}}/,"");
path = this.parseUrl(path).pathname;
if(path.indexOf(this.props.basePath)>-1){
path = path.substr(this.props.basePath.length);
}
if(path.charAt(0) != "/"){
path = "/" + path;
}
if(path.charAt(path.length-1) === "/"){
path = path.substr(0,path.length-1);
}
return path;
}
importPostman(data,key){
let reflect = {//数据字段映射关系
title: "name",
path: "url",
method: "method",
desc: "description",
req_query: "queryParams",
req_headers: "headerData",
req_params: "",
req_body_type: "dataMode",
req_body_form: "data",
req_body_other: "rawModeData"
};
let allKey = ["title","path","method","desc","req_query","req_headers","req_body_type","req_body_form","req_body_other"];
key = key || allKey;
let res = {};
for(let item in key){
item = key[item];
if(item === "req_query"){
res[item] = this.handleReq_query.bind(this)(data[reflect[item]]);
}else if(item === "req_headers"){
res[item] = this.handleReq_headers.bind(this)(data[reflect[item]]);
}else if(item === "req_body_form"){
res[item] = this.handleReq_body_form.bind(this)(data[reflect[item]]);
}else if(item === "req_body_type"){
if(data.headers.indexOf('application/json')>-1){
res[item] = "json";
}else{
res[item] = "raw";
}
}else if(item === "path"){
res[item] = this.handlePath.bind(this)(data[reflect[item]]);
if(res[item] && res[item].indexOf("/:") > -1){
let params = res[item].substr(res[item].indexOf("/:")+2).split("/:");
// res[item] = res[item].substr(0,res[item].indexOf("/:"));
let arr = [];
for(let i in params){
arr.push({
name: params[i],
desc: ""
});
}
res["req_params"] = arr;
}
}else if(item === "title"){
let path = this.handlePath.bind(this)(data[reflect["path"]]);
if(data[reflect[item]].indexOf(path) > -1){
res[item] = path;
if(res[item] && res[item].indexOf("/:") > -1){
res[item] = res[item].substr(0,res[item].indexOf("/:"));
}
}else{
res[item] = data[reflect[item]];
}
}else{
res[item] = data[reflect[item]];
}
}
return res;
}
uploadChnange(info){
const status = info.file.status;
@ -206,53 +69,46 @@ class ProjectData extends Component {
handleAddInterface(info){
if(this.state.selectCatid){
let filename = info.file.name;
let filetype = filename.substr(filename.lastIndexOf(".")).toLowerCase();
// let filename = info.file.name;
// let filetype = filename.substr(filename.lastIndexOf(".")).toLowerCase();
// console.log(filename,filetype);
if(filetype != ".json") return message.error("文件格式只能为json");
//if(filetype != ".json") return message.error("文件格式只能为json");
let reader = new FileReader();
reader.readAsText(info.file);
reader.onload = (res)=>{
res = res.target.result;
try{
res = JSON.parse(res);
let interData = res.requests;
interData = this.checkInterRepeat.bind(this)(interData);
if(interData && interData.length){
let len = interData.length;
let count = 0;
let successNum = len;
for(let item in interData){
let data = this.importPostman.bind(this)(interData[item]);
data = {
...data,
project_id: this.props.projectId,
catid: this.state.selectCatid
}
axios.post('/api/interface/add',data).then((res)=>{
count++;
if(res.data.errcode){
successNum--;
}
if(count === len){
message.success(`成功导入接口 ${successNum}`);
}
});
}
res = importDataModule[this.state.curImportType].run(res.target.result);
res = res.apis;
let len = res.length;
let count = 0;
let successNum = len;
res.forEach(async (item)=>{
let data = {
...item,
project_id: this.props.match.params.id,
catid: this.state.selectCatid
}
}catch(e){
message.error("文件格式必须为JSON");
}
let result = await axios.post('/api/interface/add',data);
count++;
if(result.data.errcode){
successNum--;
}
if(count === len){
message.success(`成功导入接口 ${successNum}`);
}
})
}
}else{
message.error("请选择上传的分类");
}
}
handleImportType=(val) =>{
this.setState({
curImportType: val
})
}
/**
*
*
@ -271,9 +127,17 @@ class ProjectData extends Component {
return (
<div className="m-panel g-row" style={{paddingTop: '15px'}}>
<div className="postman-dataImport">
<h2>数据导入</h2>
<div className="dataImportCon">
<h3 className="dataImportTile">Postman 数据导入</h3>
<div className="dataImportTile">
<Select placeholder="请选择导入数据的方式" onChange={this.handleImportType}>
{Object.keys(importDataModule).map((name)=>{
return <Option key={name} value={name}>{importDataModule[name].name}</Option>
})}
</Select>
</div>
<div className="catidSelect">
<Select
showSearch
@ -295,7 +159,7 @@ class ProjectData extends Component {
<Icon type="inbox" />
</p>
<p className="ant-upload-text">点击或者拖拽文件到上传区域</p>
<p className="ant-upload-hint">注意只支持json格式数据</p>
<p className="ant-upload-hint">{this.state.curImportType?importDataModule[this.state.curImportType].desc: null}</p>
</Dragger>
</div>
</div>

View File

@ -11,5 +11,9 @@
color: #2395f1;
padding: 16px 0px;
font-weight: 500;
width: 100%;
.ant-select{
width: 100%;
}
}
}

View File

@ -1,28 +1,47 @@
const config = process.env.config;
let hooks, pluginModule, systemPlugins;
/**
* type single 只绑定一个监听函数,会返回处理结果
* mulit 绑定多个监听函数
* type component 组件
* listener 监听函数
* mulit 是否绑定多个监听函数
*
*/
var hooks = {
hooks = {
'third_login': {
type: 'single',
type: 'component',
mulit: false,
listener: null
},
'add_interface': {
type: 'mulit',
type: 'listener',
mulit: true,
listener: []
},
import_data: {
type: 'listener',
mulit: true,
listener: []
}
};
pluginModule = {
hooks: hooks,
bindHook: bindHook,
emitHook: emitHook
}
systemPlugins = ['postman']
config.plugins = config.plugins && Array.isArray(config.plugins) ? config.plugins: [];
function bindHook(name, listener) {
if (!name) throw new Error('缺少hookname');
if (name in hooks === false) {
throw new Error('不存在的hookname');
}
if (hooks[name].type === 'multi') {
if (hooks[name].mulit === true) {
hooks[name].listener.push(listener);
} else {
hooks[name].listener = listener;
@ -30,16 +49,39 @@ function bindHook(name, listener) {
}
var yapi = {
hooks: hooks,
bindHook: bindHook
function emitHook(name, ...args){
if(!hooks[name]) throw new Error('不存在的hook name');
let hook = hooks[name];
if(hook.mulit === true && hook.type === 'listener'){
if(Array.isArray(hook.listener)){
hook.listener.forEach(item=>{
if(typeof item === 'function'){
item.call(pluginModule, ...args)
}
})
}
}else if(hook.mulit === false && hook.type === 'listener'){
if(typeof hook.listener === 'function'){
hook.listener.call(pluginModule, ...args);
}
}else if( hook.type === 'component'){
return hook.listener;
}
}
if (config.plugins && Array.isArray(config.plugins)) {
config.plugins.forEach(plugin => {
let pluginModule = require(`plugins/yapi-plugin-${plugin}/client.js`);
pluginModule.call(yapi) ;
let p = require(`plugins/yapi-plugin-${plugin}/client.js`);
p.call(pluginModule) ;
})
}
module.exports = hooks;
systemPlugins.forEach(plugin => {
let p = require(`exts/yapi-plugin-${plugin}/client.js`);
p.call(pluginModule) ;
})
module.exports = pluginModule;

View File

@ -0,0 +1,185 @@
import {message} from 'antd'
function postman(obj){
function parseUrl(url){
let parser = document.createElement('a');
parser.href = url;
return {
protocol: parser.protocol,
hostname: parser.hostname,
port: parser.port,
pathname: parser.pathname,
search: parser.search,
host: parser.host
}
}
function checkInterRepeat(interData){
let obj = {};
let arr = [];
for(let item in interData){
// console.log(interData[item].url + "-" + interData[item].method);
if(!obj[interData[item].url + "-" + interData[item].method+ "-"+interData[item].method]){
arr.push(interData[item]);
obj[interData[item].url + "-" + interData[item].method+ "-"+interData[item].method] = true;
}
}
return arr;
}
function handleReq_query(query){
let res = [];
if(query&&query.length){
for(let item in query){
res.push({
name: query[item].key,
desc: query[item].description,
required: query[item].enable
});
}
}
return res;
}
function handleReq_headers(headers){
let res = [];
if(headers&&headers.length){
for(let item in headers){
res.push({
name: headers[item].key,
desc: headers[item].description,
value: headers[item].value,
required: headers[item].enable
});
}
}
return res;
}
function handleReq_body_form(body_form){
let res = [];
if(body_form&&body_form.length){
for(let item in body_form){
res.push({
name: body_form[item].key,
value: body_form[item].value,
type: body_form[item].type
});
}
}
return res;
}
function handlePath(path){
path = path.replace(/{{(\w*)}}/,"");
path = parseUrl(path).pathname;
if(path.charAt(0) != "/"){
path = "/" + path;
}
if(path.charAt(path.length-1) === "/"){
path = path.substr(0,path.length-1);
}
return path;
}
function run(res){
try{
res = JSON.parse(res);
let interData = res.requests;
let interfaceData = {apis: []};
interData = checkInterRepeat.bind(this)(interData);
if(interData && interData.length){
for(let item in interData){
let data = importPostman.bind(this)(interData[item]);
interfaceData.apis.push(data);
}
}
return interfaceData;
}catch(e){
message.error("文件格式必须为JSON");
}
}
function importPostman(data,key){
let reflect = {//数据字段映射关系
title: "name",
path: "url",
method: "method",
desc: "description",
req_query: "queryParams",
req_headers: "headerData",
req_params: "",
req_body_type: "dataMode",
req_body_form: "data",
req_body_other: "rawModeData"
};
let allKey = ["title","path","method","desc","req_query","req_headers","req_body_type","req_body_form","req_body_other"];
key = key || allKey;
let res = {};
for(let item in key){
item = key[item];
if(item === "req_query"){
res[item] = handleReq_query.bind(this)(data[reflect[item]]);
}else if(item === "req_headers"){
res[item] = handleReq_headers.bind(this)(data[reflect[item]]);
}else if(item === "req_body_form"){
res[item] = handleReq_body_form.bind(this)(data[reflect[item]]);
}else if(item === "req_body_type"){
if(data.headers.indexOf('application/json')>-1){
res[item] = "json";
}else{
res[item] = "raw";
}
}else if(item === "path"){
res[item] = handlePath.bind(this)(data[reflect[item]]);
if(res[item] && res[item].indexOf("/:") > -1){
let params = res[item].substr(res[item].indexOf("/:")+2).split("/:");
// res[item] = res[item].substr(0,res[item].indexOf("/:"));
let arr = [];
for(let i in params){
arr.push({
name: params[i],
desc: ""
});
}
res["req_params"] = arr;
}
}else if(item === "title"){
let path = handlePath.bind(this)(data[reflect["path"]]);
if(data[reflect[item]].indexOf(path) > -1){
res[item] = path;
if(res[item] && res[item].indexOf("/:") > -1){
res[item] = res[item].substr(0,res[item].indexOf("/:"));
}
}else{
res[item] = data[reflect[item]];
}
}else{
res[item] = data[reflect[item]];
}
}
return res;
}
if(!obj || typeof obj !== 'object'){
console.error('obj参数必需是一个对象');
return null;
}
obj.postman = {
name: 'Postman',
run: run,
desc: '注意只支持json格式数据'
}
}
module.exports = function(){
this.bindHook('import_data', postman)
}

View File

@ -59,6 +59,14 @@ class interfaceController extends baseController {
catid: 'number'
});
if (!params.project_id) {
return ctx.body = yapi.commons.resReturn(null, 400, '项目id不能为空');
}
if (!params.path) {
return ctx.body = yapi.commons.resReturn(null, 400, '接口请求路径不能为空');
}
let auth = await this.checkAuth(params.project_id, 'project', 'edit')
if (!auth) {
return ctx.body = yapi.commons.resReturn(null, 400, '没有权限');
@ -68,13 +76,7 @@ class interfaceController extends baseController {
params.req_params = params.req_params || [];
params.res_body_type = params.res_body_type ? params.res_body_type.toLowerCase() : 'json';
if (!params.project_id) {
return ctx.body = yapi.commons.resReturn(null, 400, '项目id不能为空');
}
if (!params.path) {
return ctx.body = yapi.commons.resReturn(null, 400, '接口请求路径不能为空');
}
let http_path = url.parse(params.path, true);
params.path = http_path.pathname;

View File

@ -103,8 +103,10 @@ module.exports = {
//初始化配置
baseConfig.devtool = 'cheap-module-eval-source-map'
baseConfig.context = path.resolve(__dirname, './client');
baseConfig.resolve.alias.client = '/client';
baseConfig.resolve.alias.common = '/common';
baseConfig.resolve.alias.plugins = '/plugins';
baseConfig.resolve.alias.exts = '/exts';
baseConfig.output.prd.path = 'static/prd';
baseConfig.output.prd.publicPath = '';
baseConfig.output.prd.filename = '[name]@[chunkhash][ext]'