feat: 数据导入新增智能合并策略

This commit is contained in:
suwenxiong 2018-09-08 11:42:42 +08:00
parent 1758aab8ac
commit df98e27723
7 changed files with 235 additions and 1844 deletions

View File

@ -71,7 +71,7 @@ class ProjectData extends Component {
curImportType: null,
curExportType: null,
showLoading: false,
dataSync: false,
dataSync: 'good',
exportContent: 'all',
isSwaggerUrl: false,
swaggerUrl: '',
@ -92,8 +92,10 @@ class ProjectData extends Component {
if (data.data.errcode === 0) {
let menuList = data.data.data;
this.setState({
menuList: menuList
menuList: menuList,
selectCatid: menuList[0]._id
});
}
});
plugin.emitHook('import_data', importDataModule);
@ -143,7 +145,7 @@ class ProjectData extends Component {
reader.readAsText(info.file);
reader.onload = async res => {
res = await importDataModule[this.state.curImportType].run(res.target.result);
if (this.state.dataSync) {
if (this.state.dataSync === 'merge') {
// 开启同步
this.showConfirm(res);
} else {
@ -197,7 +199,7 @@ class ProjectData extends Component {
await that.handleAddInterface(res);
},
onCancel() {
that.setState({ showLoading: false, dataSync: false });
that.setState({ showLoading: false, dataSync: 'normal' });
ref.destroy();
}
});
@ -253,10 +255,10 @@ class ProjectData extends Component {
let content = await axios(this.state.swaggerUrl);
content = content.data;
let res = await importDataModule[this.state.curImportType].run(content);
if (this.state.dataSync) {
// 开启同步
if (this.state.dataSync === 'merge') {
// merge
this.showConfirm(res);
} else {
}else {
// 未开启同步
await this.handleAddInterface(res);
}
@ -323,7 +325,7 @@ class ProjectData extends Component {
</h3>
</div>
<div className="dataImportTile">
<Select placeholder="请选择导入数据的方式" onChange={this.handleImportType}>
<Select defaultValue="swagger" placeholder="请选择导入数据的方式" onChange={this.handleImportType}>
{Object.keys(importDataModule).map(name => {
return (
<Option key={name} value={name}>
@ -335,6 +337,7 @@ class ProjectData extends Component {
</div>
<div className="catidSelect">
<Select
value={this.state.selectCatid+''}
showSearch
style={{ width: '100%' }}
placeholder="请选择数据导入的默认分类"
@ -355,12 +358,26 @@ class ProjectData extends Component {
</div>
<div className="dataSync">
<span className="label">
开启数据同步&nbsp;<Tooltip title="开启数据同步后会覆盖项目中原本的数据">
数据同步&nbsp;<Tooltip title={<div>
<h3 style={{color: "white"}}>普通模式</h3>
<p>不导入已存在的接口</p>
<br />
<h3 style={{color: "white"}}>智能合并</h3>
<p>已存在的接口将合并返回数据的 response适用于导入了 swagger 数据保留对数据结构的改动</p>
<br />
<h3 style={{color: "white"}}>完全覆盖</h3>
<p>不保留旧数据完全使用新数据适用于接口定义完全交给后端定义</p>
</div>}>
<Icon type="question-circle-o" />
</Tooltip>{' '}
</span>
<Switch checked={this.state.dataSync} onChange={this.onChange} />
<Select value={this.state.dataSync} onChange={this.onChange}>
<Option value="normal">普通模式</Option>
<Option value="good">智能合并</Option>
<Option value="merge">完全覆盖</Option>
</Select>
{/* <Switch checked={this.state.dataSync} onChange={this.onChange} /> */}
</div>
{this.state.curImportType === 'swagger' && (
<div className="dataSync">

View File

@ -1,5 +1,7 @@
const _ = require('underscore');
const axios = require('axios');
const isNode = typeof global == 'object' && global.global === global;
async function handle(
@ -84,13 +86,14 @@ async function handle(
}
data.token = token;
if (dataSync) {
if (dataSync !== 'normal') {
// 开启同步功能
count++;
let apipath = '/api/interface/save';
if (isNode) {
apipath = 'http://127.0.0.1:' + port + apipath;
}
data.dataSync = dataSync;
let result = await axios.post(apipath, data);
if (result.data.errcode) {
successNum--;

37
common/mergeJsonSchema.js Normal file
View File

@ -0,0 +1,37 @@
function isPlainObject(obj) {
return obj ? typeof obj === 'object' && Object.getPrototypeOf(obj) === Object.prototype : false;
}
function handleProperties(sourceProperties, mergeProperties){
if(!isPlainObject(mergeProperties)){
return mergeProperties
}
if(! isPlainObject(sourceProperties)){
return mergeProperties
}
Object.keys(mergeProperties).forEach(key=>{
if(key === 'field_1'){
console.log(1)
}
mergeProperties[key]= handleSchema(sourceProperties[key], mergeProperties[key])
})
return mergeProperties;
}
function handleSchema(source, merge){
if(!isPlainObject(source)) return merge;
if(!isPlainObject(merge)) return merge;
let result = {}
Object.assign(result, source, merge)
if(merge.type === 'object'){
result.properties = handleProperties(source.properties, merge.properties);
}else if(merge.type === 'array'){
result.items = handleSchema(source.items, merge.items);
}
return result;
}
module.exports = function(sourceJsonSchema, mergeJsonSchema){
return handleSchema(sourceJsonSchema, mergeJsonSchema)
}

1846
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -55,5 +55,5 @@ app.use(koaStatic(yapi.path.join(yapi.WEBROOT, 'static'), { index: indexFile, gz
app.listen(yapi.WEBCONFIG.port);
commons.log(
`the server is start at 127.0.0.1${yapi.WEBCONFIG.port == '80' ? '' : ':' + yapi.WEBCONFIG.port}`
`服务已启动,请打开下面链接访问: \nhttp://127.0.0.1${yapi.WEBCONFIG.port == '80' ? '' : ':' + yapi.WEBCONFIG.port}/`
);

View File

@ -12,6 +12,7 @@ const projectModel = require('../models/project.js');
const jsondiffpatch = require('jsondiffpatch');
const formattersHtml = jsondiffpatch.formatters.html;
const showDiffMsg = require('../../common/diff-view.js');
const mergeJsonSchema = require('../../common/mergeJsonSchema');
const fs = require('fs-extra');
const path = require('path');
@ -115,7 +116,8 @@ class interfaceController extends baseController {
title: minLengthStringField,
path: minLengthStringField,
method: minLengthStringField,
message: minLengthStringField
message: minLengthStringField,
dataSync: 'string'
},
addAndUpCommonField
)
@ -281,7 +283,7 @@ class interfaceController extends baseController {
async save(ctx) {
let params = ctx.params;
if (!this.$tokenAuth) {
let auth = await this.checkAuth(params.project_id, 'project', 'edit');
if (!auth) {
@ -301,16 +303,25 @@ class interfaceController extends baseController {
));
}
let result = await this.Model.getByPath(params.project_id, params.path, params.method, '_id');
let result = await this.Model.getByPath(params.project_id, params.path, params.method, '_id res_body');
if (result.length > 0) {
result.forEach(async item => {
params.id = item._id;
// console.log(this.schemaMap['up'])
let validResult = yapi.commons.validateParams(this.schemaMap['up'], params);
let validParams = Object.assign({}, params)
let validResult = yapi.commons.validateParams(this.schemaMap['up'], validParams);
if (validResult.valid) {
let data = {};
data.params = params;
data.params = validParams;
if(params.res_body_is_json_schema && params.dataSync === 'good'){
try{
let new_res_body = yapi.commons.json_parse(params.res_body)
let old_res_body = yapi.commons.json_parse(item.res_body)
data.params.res_body = JSON.stringify(mergeJsonSchema(old_res_body, new_res_body),null,2);
}catch(err){}
}
await this.up(data);
} else {
return (ctx.body = yapi.commons.resReturn(null, 400, validResult.message));

View File

@ -0,0 +1,129 @@
import test from 'ava';
import mergeJsonSchema from '../../common/mergeJsonSchema';
test('base', t=>{
let schema1 = {
type: 'string',
default: 'xxx'
}
let schema2 = {
type: 'string',
format: 'email'
}
let result = mergeJsonSchema(schema1, schema2)
t.deepEqual(result, {
type:'string',
default: 'xxx',
format: 'email'
})
})
test('object', t=>{
let schema1 = {
"type": "object",
"title": "empty object",
"xxx": 1,
"properties": {
"field_1": {
"type": "string",
"format": "email"
}
}
}
let schema2 = {
"type": "object",
"title": "empty object",
"properties": {
"field_1": {
"type": "string",
"description": "dd"
}
}
}
let result = mergeJsonSchema(schema1, schema2)
t.deepEqual(result, {
"type": "object",
"title": "empty object",
"xxx": 1,
"properties": {
"field_1": {
"type": "string",
"format": "email",
"description": "dd"
}
}
})
})
test('array', t=>{
let schema1 = {
"type": "object",
"title": "empty object",
"properties": {
"field_1": {
"type": "array",
"tt":1,
"items": {
"type": "object",
"xxx": "2",
"properties": {
"field_3": {
"format": 'ttt',
"type": "string"
}
}
}
}
}
}
let schema2 = {
"type": "object",
"title": "empty object",
"properties": {
"field_1": {
"type": "array",
"items": {
"type": "object",
"properties": {
"field_3": {
"type": "string",
"enum": [1,2]
}
}
}
}
}
}
let result = mergeJsonSchema(schema1, schema2)
t.deepEqual(result, {
"type": "object",
"title": "empty object",
"properties": {
"field_1": {
"type": "array",
"tt":1,
"items": {
"type": "object",
"xxx": "2",
"properties": {
"field_3": {
"format": 'ttt',
"type": "string",
"enum": [1,2]
}
}
}
}
}
})
})