Skip to content

Commit

Permalink
feat: 新增插件swagger自动同步接口数据
Browse files Browse the repository at this point in the history
  • Loading branch information
shouldnotappearcalm committed Apr 23, 2019
1 parent 08cfa2d commit f04ea8d
Show file tree
Hide file tree
Showing 8 changed files with 634 additions and 0 deletions.
2 changes: 2 additions & 0 deletions common/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ module.exports = {
name: 'import-yapi-json'
},{
name: 'wiki'
}, {
name: 'swagger-auto-sync'
}
// {
// name: 'test'
Expand Down
12 changes: 12 additions & 0 deletions exts/yapi-plugin-swagger-auto-sync/client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import swaggerAutoSync from './swaggerAutoSync/swaggerAutoSync.js'

function hander(routers) {
routers.test = {
name: 'Swagger自动同步',
component: swaggerAutoSync
};
}

module.exports = function() {
this.bindHook('sub_setting_nav', hander);
};
57 changes: 57 additions & 0 deletions exts/yapi-plugin-swagger-auto-sync/controller/syncController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
const baseController = require('controllers/base.js');
const yapi = require('yapi.js');
const syncModel = require('../syncModel.js');
const projectModel = require('models/project.js');
const interfaceSyncUtils = require('../interfaceSyncUtils.js')

class syncController extends baseController {
constructor(ctx) {
super(ctx);
this.syncModel = yapi.getInst(syncModel);
this.projectModel = yapi.getInst(projectModel);
this.interfaceSyncUtils = yapi.getInst(interfaceSyncUtils);
}

/**
* 保存定时任务
* @param {*} ctx
*/
async upSync(ctx) {
let requestBody = ctx.request.body;
let projectId = requestBody.project_id;
if (!projectId) {
return (ctx.body = yapi.commons.resReturn(null, 408, '缺少项目Id'));
}
let result;
if (requestBody.id) {
result = await this.syncModel.up(requestBody);
} else {
result = await this.syncModel.save(requestBody);
}

//操作定时任务
if (requestBody.is_sync_open) {
this.interfaceSyncUtils.addSyncJob(projectId, requestBody.sync_cron, requestBody.sync_json_url, requestBody.sync_mode, requestBody.uid);
} else {
this.interfaceSyncUtils.deleteSyncJob(projectId);
}
return (ctx.body = yapi.commons.resReturn(result));
}

/**
* 查询定时任务
* @param {*} ctx
*/
async getSync(ctx) {
let projectId = ctx.query.project_id;
if (!projectId) {
return (ctx.body = yapi.commons.resReturn(null, 408, '缺少项目Id'));
}
let result = await this.syncModel.getByProjectId(projectId);
return (ctx.body = yapi.commons.resReturn(result));
}

}


module.exports = syncController;
4 changes: 4 additions & 0 deletions exts/yapi-plugin-swagger-auto-sync/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = {
server: true,
client: true
}
220 changes: 220 additions & 0 deletions exts/yapi-plugin-swagger-auto-sync/interfaceSyncUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
const schedule = require('node-schedule');
const openController = require('controllers/open.js');
const projectModel = require('models/project.js');
const syncModel = require('./syncModel.js');
const tokenModel = require('models/token.js');
const yapi = require('yapi.js')
const sha = require('sha.js');
const md5 = require('md5');
const { getToken } = require('utils/token');
const jobMap = new Map();

class syncUtils {

constructor(ctx) {
yapi.commons.log("-------------------------------------swaggerSyncUtils constructor-----------------------------------------------");
this.ctx = ctx;
this.openController = yapi.getInst(openController);
this.syncModel = yapi.getInst(syncModel);
this.tokenModel = yapi.getInst(tokenModel)
this.projectModel = yapi.getInst(projectModel);
this.init()
}

//初始化定时任务
async init() {
let allSyncJob = await this.syncModel.listAll();
for (let i = 0, len = allSyncJob.length; i < len; i++) {
let syncItem = allSyncJob[i];
if (syncItem.is_sync_open) {
this.addSyncJob(syncItem.project_id, syncItem.sync_cron, syncItem.sync_json_url, syncItem.sync_mode, syncItem.uid);
}
}
}

/**
* 新增同步任务.
* @param {*} projectId 项目id
* @param {*} cronExpression cron表达式,针对定时任务
* @param {*} swaggerUrl 获取swagger的地址
* @param {*} syncMode 同步模式
* @param {*} uid 用户id
*/
async addSyncJob(projectId, cronExpression, swaggerUrl, syncMode, uid) {
let projectToken = await this.getProjectToken(projectId, uid);
//立即执行一次
this.syncInterface(projectId, swaggerUrl, syncMode, uid, projectToken);
let scheduleItem = schedule.scheduleJob(cronExpression, async () => {
this.syncInterface(projectId, swaggerUrl, syncMode, uid, projectToken);
});

//判断是否已经存在这个任务
let jobItem = jobMap.get(projectId);
if (jobItem) {
jobItem.cancel();
}
jobMap.set(projectId, scheduleItem);
}

//同步接口
async syncInterface(projectId, swaggerUrl, syncMode, uid, projectToken) {
yapi.commons.log('定时器触发, syncJsonUrl:' + swaggerUrl + ",合并模式:" + syncMode);
let oldPorjectData;
try {
oldPorjectData = await this.projectModel.get(projectId);
} catch(e) {
yapi.commons.log('获取项目:' + projectId + '失败');
this.deleteSyncJob(projectId);
//删除数据库定时任务
await this.syncModel.delByProjectId(projectId);
return;
}
//如果项目已经删除了
if (!oldPorjectData) {
yapi.commons.log('项目:' + projectId + '不存在');
this.deleteSyncJob(projectId);
//删除数据库定时任务
await this.syncModel.delByProjectId(projectId);
return;
}
let newSwaggerJsonData;
try {
newSwaggerJsonData = await this.getSwaggerContent(swaggerUrl)
if (!newSwaggerJsonData || typeof newSwaggerJsonData !== 'object') {
yapi.commons.log('数据格式出错,请检查')
this.saveSyncLog(0, syncMode, "数据格式出错,请检查", uid, projectId);
}
newSwaggerJsonData = JSON.stringify(newSwaggerJsonData)
} catch (e) {
this.saveSyncLog(0, syncMode, "获取数据失败,请检查", uid, projectId);
yapi.commons.log('获取数据失败' + e.message)
}

let oldSyncJob = await this.syncModel.getByProjectId(projectId);

//更新之前判断本次swagger json数据是否跟上次的相同,相同则不更新
if (oldSyncJob.old_swagger_content && oldSyncJob.old_swagger_content == md5(newSwaggerJsonData)) {
//记录日志
this.saveSyncLog(0, syncMode, "接口无更新", uid, projectId);
oldSyncJob.last_sync_time = yapi.commons.time();
await this.syncModel.upById(projectId, oldSyncJob);
return;
}

let _params = {
type: 'swagger',
json: newSwaggerJsonData,
project_id: projectId,
merge: syncMode,
token: projectToken
}
let requestObj = {
params: _params
};
await this.openController.importData(requestObj);

//同步成功就更新同步表的数据
if (requestObj.body.errcode == 0) {
//修改sync_model的属性
oldSyncJob.last_sync_time = yapi.commons.time();
oldSyncJob.old_swagger_content = md5(newSwaggerJsonData);
await this.syncModel.upById(oldSyncJob._id, oldSyncJob);
}
//记录日志
this.saveSyncLog(requestObj.body.errcode, syncMode, requestObj.body.errmsg, uid, projectId);
}

getSyncJob(projectId) {
return jobMap.get(projectId);
}

deleteSyncJob(projectId) {
let jobItem = jobMap.get(projectId);
if (jobItem) {
jobItem.cancel();
}
}

/**
* 记录同步日志
* @param {*} errcode
* @param {*} syncMode
* @param {*} moremsg
* @param {*} uid
* @param {*} projectId
*/
saveSyncLog(errcode, syncMode, moremsg, uid, projectId) {
yapi.commons.saveLog({
content: '自动同步接口状态:' + (errcode == 0 ? '成功,' : '失败,') + "合并模式:" + this.getSyncModeName(syncMode) + ",更多信息:" + moremsg,
type: 'project',
uid: uid,
username: "自动同步用户",
typeid: projectId
});
}

/**
* 获取项目token,因为导入接口需要鉴权.
* @param {*} project_id 项目id
* @param {*} uid 用户id
*/
async getProjectToken(project_id, uid) {
try {
let data = await this.tokenModel.get(project_id);
let token;
if (!data) {
let passsalt = yapi.commons.randStr();
token = sha('sha1')
.update(passsalt)
.digest('hex')
.substr(0, 20);

await this.tokenModel.save({ project_id, token });
} else {
token = data.token;
}

token = getToken(token, uid);

return token;
} catch (err) {
return "";
}
}

getUid(uid) {
return parseInt(uid, 10);
}

/**
* 转换合并模式的值为中文.
* @param {*} syncMode 合并模式
*/
getSyncModeName(syncMode) {
if (syncMode == 'good') {
return '智能合并';
} else if (syncMode == 'normal') {
return '普通模式';
} else if (syncMode == 'merge') {
return '完全覆盖';
}
return '';
}

async getSwaggerContent(swaggerUrl) {
const axios = require('axios')
try {
let response = await axios.get(swaggerUrl);
if (response.status > 400) {
throw new Error(`http status "${response.status}"` + '获取数据失败,请确认 swaggerUrl 是否正确')
}
return response.data;
} catch (e) {
let response = e.response;
throw new Error(`http status "${response.status}"` + '获取数据失败,请确认 swaggerUrl 是否正确')
}
}

}

module.exports = syncUtils;
23 changes: 23 additions & 0 deletions exts/yapi-plugin-swagger-auto-sync/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const controller = require('./controller/syncController.js');
const yapi =require('yapi.js');
const interfaceSyncUtils = require('./interfaceSyncUtils.js');

module.exports = function () {
yapi.getInst(interfaceSyncUtils);

this.bindHook('add_router', function (addRouter) {
addRouter({
controller: controller,
method: 'get',
path: 'autoSync/get',
action: 'getSync'
});
addRouter({
controller: controller,
method: 'post',
path: 'autoSync/save',
action: 'upSync'
});
});

};
Loading

0 comments on commit f04ea8d

Please sign in to comment.