-

v1.3.1 #

Bug Fixed #

    +

    v1.3.3 #

    Feature #

      +
    • 邮件功能中: 1)接口信息改动增加通知对应项目所有的成员;2)默认开启接口改动邮件提醒;3) 增加邮件内容的jsondiff信息
    +

    Bug Fixed #

      +
    • 优化接口运行页面插件提醒
    • 完善 log 记录不到的问题
    • 修复接口内容改动不发送邮件问题
    • 修复部分swagger数据导入丢失问题
    +

    v1.3.2 #

    Feature #

      +
    • 分组中新增接口自定义字段,便于用户在项目中添加额外字段数据
    • 导入数据时新增导入loading显示
    +

    v1.3.1 #

    Bug Fixed #

    1. 修复接口状态和req_params参数无法更新问题
    2. 修复搜索测试集合不展开问题
    3. 修复测试过程中全局header不存在的问题

    v1.3.0 #

    Feature #

    • yapi 默认集成 ldap 登录方式
    • yapi 做一个 sso 登录插件,基于现有的 qsso 改造成大多数公司可用的
    • 环境设置支持全局 header
    • 接口运行页面选择环境增加管理环境的弹层
    • 接口运行支持加工运行前后的 request 和 response ,主要是处理加密的接口或各种 token 参数问题
    • 自动化测试除提供自定义脚本外,还提供可视化表单形式验证一些数据,例如 statusCode、bodyContent
    • 增加查看接口详细改动
    • 支持接口运行页面 body 全屏编辑
    • 数据导出到 html 支持了分类
    diff --git a/static/doc/static/server/controllers/group.js.html b/static/doc/static/server/controllers/group.js.html index 5049b973..2c07d5cb 100644 --- a/static/doc/static/server/controllers/group.js.html +++ b/static/doc/static/server/controllers/group.js.html @@ -62,6 +62,8 @@ class groupController extends baseController { minItems: 1 } + + this.schemaMap = { get: { "*id": id @@ -77,7 +79,7 @@ class groupController extends baseController { "*member_uids": member_uids }, changeMemberRole: { - "*member_uids": member_uids, + "*member_uid": "number", "*id": id, role: role }, @@ -94,7 +96,19 @@ class groupController extends baseController { up: { "*id": id, "*group_name": group_name, - "group_desc": group_desc + "group_desc": group_desc, + "custom_field1": { + name: 'string', + enable: 'boolen' + }, + "custom_field2": { + name: 'string', + enable: 'boolen' + }, + "custom_field3": { + name: 'string', + enable: 'boolen' + } } } } diff --git a/static/doc/static/server/controllers/interface.js.html b/static/doc/static/server/controllers/interface.js.html index 528c3519..b476e844 100644 --- a/static/doc/static/server/controllers/interface.js.html +++ b/static/doc/static/server/controllers/interface.js.html @@ -29,12 +29,21 @@ const interfaceCatModel = require('../models/interfaceCat.js'); const interfaceCaseModel = require('../models/interfaceCase.js'); const followModel = require('../models/follow.js'); +const groupModel = require('../models/group.js') const _ = require('underscore'); const url = require('url'); const baseController = require('./base.js'); const yapi = require('../yapi.js'); const userModel = require('../models/user.js'); const projectModel = require('../models/project.js'); +const jsondiffpatch = require('jsondiffpatch') +const formattersHtml = jsondiffpatch.formatters.html; +const showDiffMsg = require('../../common/diff-view.js'); +const fs = require('fs-extra') +const path = require('path'); + +// const annotatedCss = require("jsondiffpatch/public/formatters-styles/annotated.css"); +// const htmlCss = require("jsondiffpatch/public/formatters-styles/html.css"); class interfaceController extends baseController { constructor(ctx) { @@ -45,6 +54,7 @@ class interfaceController extends baseController { this.caseModel = yapi.getInst(interfaceCaseModel); this.followModel = yapi.getInst(followModel); this.userModel = yapi.getInst(userModel); + this.groupModel = yapi.getInst(groupModel); const minLengthStringField = { type: 'string', @@ -69,7 +79,7 @@ class interfaceController extends baseController { required: 'string' }], req_body_type: 'string', - req_params:[{ + req_params: [{ name: 'string', example: 'string', desc: 'string' @@ -85,7 +95,8 @@ class interfaceController extends baseController { }], 'req_body_other': 'string', res_body_type: 'string', - res_body: 'string' + res_body: 'string', + custom_field_value: 'string' } this.schemaMap = { @@ -102,7 +113,9 @@ class interfaceController extends baseController { 'path': minLengthStringField, 'title': minLengthStringField, 'method': minLengthStringField, - 'catid': 'number' + 'catid': 'number', + 'switch_notice': 'boolean', + 'message': minLengthStringField }, addAndUpCommonField) } } @@ -223,9 +236,9 @@ class interfaceController extends baseController { return ctx.body = yapi.commons.resReturn(null, 400, '接口id不能为空'); } - try { let result = await this.Model.get(params.id); + // console.log('result', result); if (!result) { return ctx.body = yapi.commons.resReturn(null, 490, '不存在的'); } @@ -334,7 +347,7 @@ class interfaceController extends baseController { let result = await this.catModel.list(project_id), newResult = []; for (let i = 0, item, list; i < result.length; i++) { item = result[i].toObject() - list = await this.Model.listByCatid(item._id, '_id title method path') + list = await this.Model.listByCatid(item._id) for (let j = 0; j < list.length; j++) { list[j] = list[j].toObject() } @@ -377,7 +390,6 @@ class interfaceController extends baseController { async up(ctx) { let params = ctx.params; - // console.log('params', params.req_params); if (!_.isUndefined(params.method)) { params.method = params.method || 'GET'; @@ -440,6 +452,12 @@ class interfaceController extends baseController { let result = await this.Model.up(id, data); let username = this.getUsername(); let CurrentInterfaceData = await this.Model.get(id); + let logData = { + interface_id: id, + current: CurrentInterfaceData.toObject(), + old: interfaceData.toObject() + } + this.catModel.get(interfaceData.catid).then((cate) => { yapi.commons.saveLog({ content: `${username} @@ -449,28 +467,38 @@ class interfaceController extends baseController { uid: this.getUid(), username: username, typeid: cate.project_id, - data: { - interface_id: id, - current: CurrentInterfaceData, - old: interfaceData - } + data: logData }); }); - this.projectModel.up(interfaceData.project_id, { up_time: new Date().getTime() }).then(); if (params.switch_notice === true) { + let diffView = showDiffMsg(jsondiffpatch, formattersHtml, logData); + + let annotatedCss = fs.readFileSync(path.resolve(yapi.WEBROOT, 'node_modules/jsondiffpatch/public/formatters-styles/annotated.css'), 'utf8'); + let htmlCss = fs.readFileSync(path.resolve(yapi.WEBROOT, 'node_modules/jsondiffpatch/public/formatters-styles/html.css'), 'utf8'); + let project = await this.projectModel.getBaseInfo(interfaceData.project_id); let interfaceUrl = `http://${ctx.request.host}/project/${interfaceData.project_id}/interface/api/${id}` this.sendNotice(interfaceData.project_id, { title: `${username} 更新了接口`, - content: `

    ${username}更新了接口(${data.title})

    -

    项目名:${project.name}

    -

    修改用户: ${username}

    -

    接口名: ${data.title}

    -

    接口路径: [${data.method}]${data.path}

    -

    详细改动日志: ${params.message}

    ` + content: ` + + + + +

    ${username}更新了接口(${data.title})

    +

    项目名:${project.name}

    +

    修改用户: ${username}

    +

    接口名: ${data.title}

    +

    接口路径: [${data.method}]${data.path}

    +

    详细改动日志: ${this.diffHTML(diffView)}

    + + ` }) } @@ -479,6 +507,19 @@ class interfaceController extends baseController { } + diffHTML(html) { + if (html.length === 0) { + return `没有改动,该操作未改动Api数据` + } + + return html.map(item => { + return (`
    +

    ${item.title}

    +
    ${item.content}
    +
    `) + }) + } + /** * 删除接口 * @interface /interface/del @@ -709,24 +750,95 @@ class interfaceController extends baseController { } } - sendNotice(projectId, data) { - this.followModel.listByProjectId(projectId).then(list => { - let users = []; - list.forEach(item => { - users.push(item.uid) - }) - this.userModel.findByUids(users).then(list => { - list.forEach(item => { - yapi.commons.sendMail({ - to: item.email, - contents: data.content, - subject: data.title - }); - }) + /** + * 获取自定义接口字段数据 + * @interface /interface/get_custom_field + * @method GET + * @category interface + * @foldnumber 10 + * @param {String} app_code = '111' + * @returns {Object} + * + */ + async getCustomField(ctx) { + let params = ctx.request.query - }) + if (Object.keys(params).length !== 1) { + return ctx.body = yapi.commons.resReturn(null, 400, '参数数量错误'); + } + let customFieldName = Object.keys(params)[0]; + let customFieldValue = params[customFieldName]; - }); + try { + + // 查找有customFieldName的分组(group) + let groups = await this.groupModel.getcustomFieldName(customFieldName) + if (groups.length === 0) { + return ctx.body = yapi.commons.resReturn(null, 404, '没有找到对应自定义接口'); + } + + // 在每个分组(group)下查找对应project的id值 + let interfaces = []; + for (let i = 0; i < groups.length; i++) { + let projects = await this.projectModel.list(groups[i]._id); + + // 在每个项目(project)中查找interface下的custom_field_value + for (let j = 0; j < projects.length; j++) { + let data = {} + let inter = await this.Model.getcustomFieldValue(projects[j]._id, customFieldValue) + if (inter.length > 0) { + data.project_name = projects[j].name + inter = inter.map((item, i) => { + item = inter[i] = inter[i].toObject(); + item.res_body = yapi.commons.json_parse(item.res_body) + item.req_body_other = yapi.commons.json_parse(item.req_body_other) + + return item + }) + + data.list = inter; + interfaces.push(data); + } + } + } + return ctx.body = yapi.commons.resReturn(interfaces); + } catch (e) { + yapi.commons.resReturn(null, 400, e.message); + } + } + + + async sendNotice(projectId, data) { + const list = await this.followModel.listByProjectId(projectId); + const starUsers = list.map(item => item.uid); + + const projectList = await this.projectModel.get(projectId); + const projectMenbers = projectList.members.map(item => item.uid); + + const users = this.arrUnique(projectMenbers, starUsers); + const usersInfo = await this.userModel.findByUids(users) + const emails = usersInfo.map(item => item.email).join(','); + console.log('emails', emails); + + try { + yapi.commons.sendMail({ + to: emails, + contents: data.content, + subject: data.title + }) + } catch (e) { + yapi.commons.log('邮件发送失败:' + e, 'error') + } + + } + + arrUnique(arr1, arr2) { + + let arr = arr1.concat(arr2); + let res = arr.filter(function (item, index, arr) { + return arr.indexOf(item) === index; + }) + return res; } diff --git a/static/doc/static/server/controllers/interfaceCol.js.html b/static/doc/static/server/controllers/interfaceCol.js.html index ce33ced6..34a1e5ba 100644 --- a/static/doc/static/server/controllers/interfaceCol.js.html +++ b/static/doc/static/server/controllers/interfaceCol.js.html @@ -169,7 +169,7 @@ class interfaceColController extends baseController { result.method = data.method; result.req_body_type = data.req_body_type; result.req_headers = this.handleParamsValue(data.req_headers, result.req_headers); - result.res_body = data.res_body; + result.res_body_type = data.res_body_type; result.req_body_form = this.handleParamsValue(data.req_body_form, result.req_body_form) @@ -375,7 +375,7 @@ class interfaceColController extends baseController { } for (let i = 0; i < params.interface_list.length; i++) { - let interfaceData = await this.interfaceModel.getBaseinfo(params.interface_list[i]); + let interfaceData = await this.interfaceModel.get(params.interface_list[i]); data.interface_id = params.interface_list[i]; data.casename = interfaceData.title; data.req_body_other = interfaceData.req_body_other; diff --git a/static/doc/static/server/controllers/project.js.html b/static/doc/static/server/controllers/project.js.html index 926aebbf..6b756f65 100644 --- a/static/doc/static/server/controllers/project.js.html +++ b/static/doc/static/server/controllers/project.js.html @@ -365,6 +365,7 @@ class projectController extends baseController { async get(ctx) { let params = ctx.params; let result = await this.Model.getBaseInfo(params.id); + if (!result) { return ctx.body = yapi.commons.resReturn(null, 400, '不存在的项目'); } @@ -590,6 +591,8 @@ class projectController extends baseController { pre_script: 'string', after_script: 'string' }); + + if (!id) { return ctx.body = yapi.commons.resReturn(null, 405, '项目id不能为空'); } @@ -600,7 +603,7 @@ class projectController extends baseController { let projectData = await this.Model.get(id); - if(params.basepath){ + if (params.basepath) { if ((params.basepath = this.handleBasepath(params.basepath)) === false) { return ctx.body = yapi.commons.resReturn(null, 401, 'basepath格式有误'); } @@ -629,6 +632,7 @@ class projectController extends baseController { if (!_.isUndefined(params.desc)) data.desc = params.desc; if (!_.isUndefined(params.group_id)) data.group_id = params.group_id; if (!_.isUndefined(params.basepath)) data.basepath = params.basepath; + if (!_.isUndefined(params.switch_notice)) data.switch_notice = params.switch_notice; if (!_.isUndefined(params.color)) data.color = params.color; if (!_.isUndefined(params.icon)) data.icon = params.icon; if (!_.isUndefined(params.pre_script)) data.pre_script = params.pre_script; @@ -686,7 +690,7 @@ class projectController extends baseController { data.env = params.env; let isRepeat = this.arrRepeat(data.env, 'name'); - if(isRepeat){ + if (isRepeat) { return ctx.body = yapi.commons.resReturn(null, 405, '环境变量名重复'); } let result = await this.Model.up(id, data); @@ -704,11 +708,11 @@ class projectController extends baseController { } } - arrRepeat(arr, key){ + arrRepeat(arr, key) { const s = new Set(); - arr.forEach(item=>s.add(item[key])) + arr.forEach(item => s.add(item[key])) return s.size !== arr.length - } + } /** * 模糊搜索项目名称或者组名称