From 11c8e894d81958417cde216b26d22818f5e565dc Mon Sep 17 00:00:00 2001 From: suxiaoxin Date: Sun, 17 Sep 2017 13:36:51 +0800 Subject: [PATCH 1/4] feat: add advance-mock --- .eslintrc.js | 7 +- .eslintrc.json | 1 - client/common.js | 19 +++ .../InterfaceList/InterfaceContent.js | 43 +++-- client/plugin.js | 72 +++++--- common/config.js | 19 +++ common/lib.js | 26 +++ exts/yapi-plugin-advanced-mock/AdvMock.js | 118 +++++++++++++ exts/yapi-plugin-advanced-mock/client.js | 10 ++ exts/yapi-plugin-advanced-mock/controller.js | 54 ++++++ exts/yapi-plugin-advanced-mock/model.js | 62 +++++++ exts/yapi-plugin-advanced-mock/server.js | 55 ++++++ nodemon.json | 2 +- server/app.js | 11 +- server/controllers/base.js | 3 +- server/controllers/interface.js | 16 +- server/controllers/project.js | 2 +- server/middleware/mockServer.js | 14 +- server/models/avatar.js | 1 - server/models/interface.js | 5 +- server/plugin.js | 156 ++++++++++++------ server/router.js | 37 ++++- server/utils/commons.js | 11 +- 23 files changed, 625 insertions(+), 119 deletions(-) create mode 100644 common/config.js create mode 100644 common/lib.js create mode 100644 exts/yapi-plugin-advanced-mock/AdvMock.js create mode 100644 exts/yapi-plugin-advanced-mock/client.js create mode 100644 exts/yapi-plugin-advanced-mock/controller.js create mode 100644 exts/yapi-plugin-advanced-mock/model.js create mode 100644 exts/yapi-plugin-advanced-mock/server.js diff --git a/.eslintrc.js b/.eslintrc.js index 6ebe7542..5b0f7575 100755 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -5,7 +5,7 @@ module.exports = { "es6": true, "node": true }, - "extends": "eslint:recommended", + extends: ["eslint:recommended", "plugin:react/recommended"], "parser": "babel-eslint", "parserOptions": { "ecmaFeatures": { @@ -14,8 +14,9 @@ module.exports = { }, "sourceType": "module" }, - "plugins": [ - "react" + plugins: [ + "react", + "import" ], "rules": { "indent": [ diff --git a/.eslintrc.json b/.eslintrc.json index 2ef48aad..8f036860 100755 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -31,7 +31,6 @@ "/fekit_modules/*", "/node_modules/*", "/bower_components/*", - "/plugins/*", "/dev/*", "/prd/*" ] diff --git a/client/common.js b/client/common.js index d99ab1ff..b46f1b2b 100755 --- a/client/common.js +++ b/client/common.js @@ -2,6 +2,25 @@ import React from 'react'; import moment from 'moment'; import constants from './constants/variable'; +const Roles = { + 0 : 'admin', + 10: 'owner', + 20: 'dev', + 30: 'guest', + 40: 'member' +} + +const roleAction = { + 'manageUserlist' : 'admin', + 'changeMemberRole': 'owner', + 'editInterface': 'dev', + 'viewPrivateInterface': 'guest', + 'viewGroup': 'guest' +} + +exports.checkAuth = (action, role)=>{ + return Roles[roleAction[action]] <= Roles[role]; +} exports.formatTime = (timestamp) => { return moment.unix(timestamp).format("YYYY-MM-DD HH:mm:ss") diff --git a/client/containers/Project/Interface/InterfaceList/InterfaceContent.js b/client/containers/Project/Interface/InterfaceList/InterfaceContent.js index acf8fc07..a5b1ccda 100755 --- a/client/containers/Project/Interface/InterfaceList/InterfaceContent.js +++ b/client/containers/Project/Interface/InterfaceList/InterfaceContent.js @@ -8,7 +8,7 @@ import View from './View.js' import { fetchInterfaceData } from '../../../../reducer/modules/interface.js'; import { withRouter } from 'react-router-dom'; import Run from './Run/Run.js' - +const plugin = require('client/plugin.js'); const TabPane = Tabs.TabPane; @connect( @@ -72,24 +72,33 @@ class Content extends Component { } render() { - const tabs = - - {/* */} - - + let InterfaceTabs = { + view: { + component: View, + name: '预览' + }, + edit: { + component: Edit, + name: '编辑' + }, + run: { + component: Run, + name: '运行' + } + } - - - {/* */} - + plugin.emitHook('interface_tab', InterfaceTabs); + + const tabs = + {Object.keys(InterfaceTabs).map(key=>{ + let item = InterfaceTabs[key]; + return + })} ; - let tabContent; - if (this.state.curtab === 'view') { - tabContent = ; - } else if (this.state.curtab === 'edit') { - tabContent = - } else if (this.state.curtab === 'run') { - tabContent = + let tabContent = null; + if (this.state.curtab) { + let C = InterfaceTabs[this.state.curtab].component; + tabContent = ; } return
diff --git a/client/plugin.js b/client/plugin.js index 13e50982..c7cc96b6 100755 --- a/client/plugin.js +++ b/client/plugin.js @@ -1,3 +1,4 @@ +let initPlugins = require('common/lib.js').initPlugins; const config = process.env.config; let hooks, pluginModule, systemPlugins; @@ -9,12 +10,12 @@ let hooks, pluginModule, systemPlugins; */ hooks = { - 'third_login': { + third_login: { type: 'component', mulit: false, listener: null }, - 'add_interface': { + add_interface: { type: 'listener', mulit: true, listener: [] @@ -23,18 +24,21 @@ hooks = { type: 'listener', mulit: true, listener: [] + }, + interface_tab: { + type: 'listener', + mulit: true, + listener: [] } }; -pluginModule = { - hooks: hooks, - bindHook: bindHook, - emitHook: emitHook -} -systemPlugins = ['import-postman', 'import-har'] +//初始化配置 +systemPlugins = require('common/config.js').exts; +systemPlugins = initPlugins(systemPlugins); -config.plugins = config.plugins && Array.isArray(config.plugins) ? config.plugins: []; +config.plugins = config.plugins && Array.isArray(config.plugins) ? config.plugins : []; +config.plugins = initPlugins(config.plugins); function bindHook(name, listener) { if (!name) throw new Error('缺少hookname'); @@ -49,38 +53,52 @@ function bindHook(name, listener) { } -function emitHook(name, ...args){ - if(!hooks[name]) throw new Error('不存在的hook name'); +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'){ + 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'){ + } else if (hook.mulit === false && hook.type === 'listener') { + if (typeof hook.listener === 'function') { hook.listener.call(pluginModule, ...args); } - }else if( hook.type === 'component'){ + } else if (hook.type === 'component') { return hook.listener; } - + } - -if (config.plugins && Array.isArray(config.plugins)) { - config.plugins.forEach(plugin => { - let p = require(`plugins/yapi-plugin-${plugin}/client.js`); - p.call(pluginModule) ; - }) +pluginModule = { + hooks: hooks, + bindHook: bindHook, + emitHook: emitHook } +config.plugins.forEach(plugin => { + if (!plugin) return null; + if (!plugin.enable) return null; + if (plugin.client) { + let p = require(`plugins/yapi-plugin-${plugin.name}/client.js`); + p.call(pluginModule, plugin); + } + +}) + + systemPlugins.forEach(plugin => { - let p = require(`exts/yapi-plugin-${plugin}/client.js`); - p.call(pluginModule) ; + if (!plugin) return null; + if (!plugin.enable) return null; + if (plugin.client) { + let p = require(`exts/yapi-plugin-${plugin.name}/client.js`); + p.call(pluginModule, plugin); + } + }) module.exports = pluginModule; \ No newline at end of file diff --git a/common/config.js b/common/config.js new file mode 100644 index 00000000..b42291c2 --- /dev/null +++ b/common/config.js @@ -0,0 +1,19 @@ +module.exports = { + exts: [ + { + name: 'import-postman', + server: false, + client: true + }, + { + name: 'import-har', + server: false, + client: true + }, + { + name: 'advanced-mock', + server: true, + client: true + } + ] +} \ No newline at end of file diff --git a/common/lib.js b/common/lib.js new file mode 100644 index 00000000..4795599a --- /dev/null +++ b/common/lib.js @@ -0,0 +1,26 @@ +const defaultPluginConfig = { + name: null, + server: true, + client: true, + enable: true +} + +module.exports = { + initPlugins: function (plugins) { + if (!plugins) { + return []; + } + if (typeof plugins !== 'object' || !Array.isArray(plugins)) { + console.error('插件配置有误,请检查', plugins); + return []; + } + + return plugins.map(item => { + if (item && typeof item === 'string') { + return Object.assign({}, defaultPluginConfig, { name: item }) + } else if (item && typeof item === 'object') { + return Object.assign({}, defaultPluginConfig, item) + } + }) + } +} \ No newline at end of file diff --git a/exts/yapi-plugin-advanced-mock/AdvMock.js b/exts/yapi-plugin-advanced-mock/AdvMock.js new file mode 100644 index 00000000..d62872c9 --- /dev/null +++ b/exts/yapi-plugin-advanced-mock/AdvMock.js @@ -0,0 +1,118 @@ +import React, { Component } from 'react' +// import { connect } from 'react-redux' +import axios from 'axios' +import PropTypes from 'prop-types' +import { withRouter } from 'react-router-dom'; +import { Form, Switch, Button, message } from 'antd'; +import mockEditor from 'client/containers/Project/Interface/InterfaceList/mockEditor'; +const FormItem = Form.Item; + + +class AdvMock extends Component { + static propTypes = { + form: PropTypes.object, + match: PropTypes.object + } + + constructor(props) { + super(props); + this.state = { + enable: false, + mock_script: '' + } + } + + handleSubmit = (e) => { + e.preventDefault(); + let projectId = this.props.match.params.id; + let interfaceId = this.props.match.params.actionId; + let params = { + project_id: projectId, + interface_id: interfaceId, + mock_script: this.state.mock_script, + enable: this.state.enable + } + axios.post('/api/plugin/advmock/save', params).then(res => { + if (res.data.errcode === 0) { + message.success('保存成功'); + } else { + message.error(res.data.errmsg); + } + }) + } + + componentWillMount() { + this.getAdvMockData(); + } + + async getAdvMockData() { + let interfaceId = this.props.match.params.actionId; + let result = await axios.get('/api/plugin/advmock/get?interface_id=' + interfaceId); + if (result.data.errcode === 0) { + let mockData = result.data.data; + this.setState({ + enable: mockData.enable, + mock_script: mockData.mock_script + }) + } + + let that = this; + mockEditor({ + container: 'mock-script', + data: that.state.mock_script, + onChange: function (d) { + that.setState({ + mock_script: d.text + }) + } + }) + } + + onChange = (v) => { + this.setState({ + enable: v + }) + } + + render() { + const formItemLayout = { + labelCol: { + sm: { span: 4 } + }, + wrapperCol: { + sm: { span: 16 } + } + }; + const tailFormItemLayout = { + wrapperCol: { + sm: { + span: 16, + offset: 11 + } + } + }; + return
+
+ + + + + +
+
+ + + + +
+
+ } +} + +module.exports = Form.create()(withRouter(AdvMock)); \ No newline at end of file diff --git a/exts/yapi-plugin-advanced-mock/client.js b/exts/yapi-plugin-advanced-mock/client.js new file mode 100644 index 00000000..26a7d7db --- /dev/null +++ b/exts/yapi-plugin-advanced-mock/client.js @@ -0,0 +1,10 @@ +import AdvMock from './AdvMock.js' + +module.exports = function(){ + this.bindHook('interface_tab', function(tabs){ + tabs.advMock = { + name: '高级Mock', + component: AdvMock + } + }) +} \ No newline at end of file diff --git a/exts/yapi-plugin-advanced-mock/controller.js b/exts/yapi-plugin-advanced-mock/controller.js new file mode 100644 index 00000000..72f41e43 --- /dev/null +++ b/exts/yapi-plugin-advanced-mock/controller.js @@ -0,0 +1,54 @@ +const baseController = require('controllers/base.js'); +const advModel = require('./model.js'); +const yapi = require('yapi.js'); + +class advMockController extends baseController{ + constructor(ctx){ + super(ctx); + this.Model = yapi.getInst(advModel); + } + + async getMock(ctx){ + let id = ctx.query.interface_id; + let mockData = await this.Model.get(id); + if(!mockData){ + return ctx.body = yapi.commons.resReturn(null, 408, 'mock脚本不存在'); + } + return ctx.body = yapi.commons.resReturn(mockData); + } + + async upMock(ctx){ + let params = ctx.request.body; + try{ + if(!params.interface_id){ + return yapi.commons.resReturn(null, 408, '缺少interface_id'); + } + if(!params.project_id){ + return yapi.commons.resReturn(null, 408, '缺少project_id'); + } + if(!params.mock_script){ + return yapi.commons.resReturn(null, 408, '缺少mock_script'); + } + + let data = { + interface_id: params.interface_id, + mock_script: params.mock_script, + project_id: params.project_id, + uid: this.getUid(), + enable: params.enable === true ? true : false + } + let result; + let mockData = await this.Model.get(data.interface_id); + if(mockData){ + result = await this.Model.up(data); + }else{ + result = await this.Model.save(data); + } + return ctx.body = yapi.commons.resReturn(result); + }catch(e){ + return ctx.body = yapi.commons.resReturn(null, 400, e.message); + } + } +} + +module.exports = advMockController; \ No newline at end of file diff --git a/exts/yapi-plugin-advanced-mock/model.js b/exts/yapi-plugin-advanced-mock/model.js new file mode 100644 index 00000000..6de524f6 --- /dev/null +++ b/exts/yapi-plugin-advanced-mock/model.js @@ -0,0 +1,62 @@ +const yapi = require('yapi.js'); +const baseModel = require('models/base.js'); + +class advMockModel extends baseModel { + getName() { + return 'adv_mock'; + } + + getSchema() { + return { + interface_id: { type: Number, required: true }, + project_id: {type: Number, required: true}, + enable: {type: Boolean, default: false}, //1表示开启,0关闭 + mock_script: String, + uid: String, + up_time: Number + }; + } + + get(interface_id) { + + return this.model.findOne({ + interface_id: interface_id + }); + } + + delByInterfaceId(interface_id) { + console.log(interface_id); + return this.model.deleteOne({ + interface_id: interface_id + }); + } + + delByProjectId(project_id){ + return this.model.deleteMany({ + project_id: project_id + }) + } + + save(data) { + data.up_time = yapi.commons.time(); + let m = new this.model(data); + return m.save(); + } + + up(data) { + data.up_time = yapi.commons.time(); + return this.model.update({ + interface_id: data.interface_id + }, { + uid: data.uid, + up_time: data.up_time, + mock_script: data.mock_script, + enable: data.enable + }, { + upsert: true + }) + } + +} + +module.exports = advMockModel; \ No newline at end of file diff --git a/exts/yapi-plugin-advanced-mock/server.js b/exts/yapi-plugin-advanced-mock/server.js new file mode 100644 index 00000000..1dc8670e --- /dev/null +++ b/exts/yapi-plugin-advanced-mock/server.js @@ -0,0 +1,55 @@ +const controller = require('./controller'); +const advModel = require('./model.js'); +const yapi = require('yapi.js'); + + +module.exports = function(){ + + this.bindHook('add_router', function(addRouter){ + addRouter({ + controller: controller, + method: 'get', + path: 'advmock/get', + action: 'getMock' + }) + addRouter({ + controller: controller, + method: 'post', + path: 'advmock/save', + action: 'upMock' + }) + }) + this.bindHook('interface_del', async function(id){ + let inst = yapi.getInst(advModel); + await inst.delByInterfaceId(id); + }) + this.bindHook('project_del', async function(id){ + let inst = yapi.getInst(advModel); + await inst.delByProjectId(id); + }) + /** + * let context = { + projectData: project, + interfaceData: interfaceData, + ctx: ctx, + mockJson: res + } + */ + this.bindHook('mock_after', async function(context){ + let interfaceId = context.interfaceData._id; + let inst = yapi.getInst(advModel); + let data = await inst.get(interfaceId); + if(!data || !data.enable || !data.mock_script){ + return context; + } + let script = data.mock_script; + let sandbox = { + query: context.ctx.query, + body: context.ctx.request.body, + mockJson: context.mockJson + } + sandbox = yapi.commons.sandbox(sandbox, script); + context.mockJson = sandbox.mockJson; + console.log(11111, context.mockJson) + }) +} \ No newline at end of file diff --git a/nodemon.json b/nodemon.json index 9e6af8b9..c20763b6 100755 --- a/nodemon.json +++ b/nodemon.json @@ -1,3 +1,3 @@ { - "watch": ["server/", "common/", "plugins"] + "watch": ["server/", "common/", "plugins", "exts/"] } \ No newline at end of file diff --git a/server/app.js b/server/app.js index 24bf1c45..489761d3 100755 --- a/server/app.js +++ b/server/app.js @@ -1,22 +1,25 @@ +process.env.NODE_PATH = __dirname; +require('module').Module._initPaths(); + const yapi = require('./yapi.js'); const commons = require('./utils/commons'); yapi.commons = commons; const dbModule = require('./utils/db.js'); const mockServer = require('./middleware/mockServer.js'); +const plugins = require('./plugin.js'); +const websockify = require('koa-websocket'); +const websocket = require('./websocket.js'); + const Koa = require('koa'); const koaStatic = require('koa-static'); const bodyParser = require('koa-bodyparser'); const router = require('./router.js'); -const websockify = require('koa-websocket'); -const websocket = require('./websocket.js'); -const plugins = require('./plugin.js'); let indexFile = process.argv[2] === 'dev' ? 'dev.html' : 'index.html'; yapi.connect = dbModule.connect(); const app = websockify(new Koa()); yapi.app = app; -plugins(); app.use(mockServer); app.use(bodyParser({multipart: true})); app.use(router.routes()); diff --git a/server/controllers/base.js b/server/controllers/base.js index f11dfb1d..5e64a724 100755 --- a/server/controllers/base.js +++ b/server/controllers/base.js @@ -24,7 +24,8 @@ class baseController { '/api/user/login', '/api/user/reg', '/api/user/status', - '/api/user/logout' + '/api/user/logout', + '/api/user/avatar' ]; if (ignoreRouter.indexOf(ctx.path) > -1) { this.$auth = true; diff --git a/server/controllers/interface.js b/server/controllers/interface.js index 4556048a..bef86e67 100755 --- a/server/controllers/interface.js +++ b/server/controllers/interface.js @@ -225,6 +225,9 @@ class interfaceController extends baseController { async list(ctx) { let project_id = ctx.request.query.project_id; let project = await this.projectModel.getBaseInfo(project_id); + if(!project){ + return ctx.body = yapi.commons.resReturn(null, 407, '不存在的项目'); + } if (project.project_type === 'private') { if (await this.checkAuth(project._id, 'project', 'edit') !== true) { return ctx.body = yapi.commons.resReturn(null, 406, '没有权限'); @@ -497,6 +500,7 @@ class interfaceController extends baseController { let inter = await this.Model.get(id); let result = await this.Model.del(id); + yapi.emitHook('interface_del', id).then(); await this.caseModel.delByInterfaceId(id); let username = this.getUsername(); this.catModel.get(inter.catid).then((cate) => { @@ -645,7 +649,17 @@ class interfaceController extends baseController { username: username, typeid: catData.project_id }); - + + let interfaceData = await this.Model.listByCatid(id); + interfaceData.forEach(async item=>{ + try{ + yapi.emitHook('interface_del', item._id).then(); + await this.caseModel.delByInterfaceId(item._id); + }catch(e){ + yapi.commons.log(e.message, 'error'); + } + + }) await this.catModel.del(id); let r = await this.Model.delByCatid(id); return ctx.body = yapi.commons.resReturn(r); diff --git a/server/controllers/project.js b/server/controllers/project.js index ed72a231..17e40fb6 100755 --- a/server/controllers/project.js +++ b/server/controllers/project.js @@ -403,7 +403,7 @@ class projectController extends baseController { await interfaceInst.delByProjectId(id) await interfaceCaseInst.delByProjectId(id) await interfaceColInst.delByProjectId(id) - + yapi.emitHook('project_del', id).then(); let result = await this.Model.del(id); ctx.body = yapi.commons.resReturn(result); } catch (err) { diff --git a/server/middleware/mockServer.js b/server/middleware/mockServer.js index 64e03a11..37dee7a4 100755 --- a/server/middleware/mockServer.js +++ b/server/middleware/mockServer.js @@ -89,19 +89,27 @@ module.exports = async (ctx, next) => { ctx.set("Access-Control-Allow-Origin", "*") if (interfaceData.res_body_type === 'json') { try { - const res = mockExtra( + let res = mockExtra( yapi.commons.json_parse(interfaceData.res_body), { query: ctx.request.query, body: ctx.request.body } ); - return ctx.body = Mock.mock(res); + res = Mock.mock(res); + let context = { + projectData: project, + interfaceData: interfaceData, + ctx: ctx, + mockJson: res + } + await yapi.emitHook('mock_after', context); + return ctx.body = context.mockJson; } catch (e) { yapi.commons.log(e, 'error') return ctx.body = { errcode: 400, - errmsg: 'mock json数据格式有误', + errmsg: 'mock json数据格式有误, '+ e.message, data: interfaceData.res_body } } diff --git a/server/models/avatar.js b/server/models/avatar.js index 1545db91..233797f6 100755 --- a/server/models/avatar.js +++ b/server/models/avatar.js @@ -1,6 +1,5 @@ const yapi = require('../yapi.js'); const baseModel = require('./base.js'); -// const userModel = require('../models/user.js'); class avatarModel extends baseModel { getName() { diff --git a/server/models/interface.js b/server/models/interface.js index 69314498..73425b7d 100755 --- a/server/models/interface.js +++ b/server/models/interface.js @@ -116,11 +116,10 @@ class interfaceModel extends baseModel { .exec(); } - listByCatid(catid, select){ - select = select || '_id title uid path method project_id catid edit_uid status desc add_time up_time' + listByCatid(catid){ return this.model.find({ catid: catid - }).select(select).exec(); + }).exec(); } del(id) { diff --git a/server/plugin.js b/server/plugin.js index 4d3163ad..4c1fccce 100755 --- a/server/plugin.js +++ b/server/plugin.js @@ -1,60 +1,124 @@ const yapi = require('./yapi.js'); -const plugin_path = yapi.path.join(yapi.WEBROOT, 'plugins') +const plugin_path = yapi.path.join(yapi.WEBROOT, 'plugins'); +const plugin_system_path = yapi.path.join(yapi.WEBROOT, 'exts'); +const initPlugins = require('../common/lib.js').initPlugins; +var extConfig = require('../common/config.js').exts; var hooks = { - 'third_login': { - type: 'single', - listener: null - }, - 'add_interface': { - type: 'mulit', - listener: [] - } + //第三方sso登录钩子,暂只支持设置一个 + 'third_login': { + type: 'single', + listener: null + }, + //增加接口成功后触发 + 'interface_add': { + type: 'multi', + listener: [] + }, + //删除接口成功后触发 + 'interface_del': { + type: 'multi', + listener: [] + }, + 'project_add':{ + type: 'multi', + listener: [] + }, + 'project_del':{ + type: 'multi', + listener: [] + }, + //MockServer生成mock数据后触发 + mock_after: { + type: 'multi', + listener: [] + }, + //增加路由的钩子 + add_router: { + type: 'multi', + listener: [] + } }; -function bindHook(name, listener){ - if(!name) throw new Error('缺少hookname'); - if(name in hooks === false){ - throw new Error('不存在的hookname'); - } - if(hooks[name].type === 'multi'){ - hooks[name].listener.push(listener); - }else{ - hooks[name].listener = listener; - } +function bindHook(name, listener) { + if (!name) throw new Error('缺少hookname'); + if (name in hooks === false) { + throw new Error('不存在的hookname'); + } + if (hooks[name].type === 'multi') { + hooks[name].listener.push(listener); + } else { + if (typeof hooks[name].listener === 'function') { + throw new Error('重复绑定singleHook(' + name + '), 请检查'); + } + hooks[name].listener = listener; + } } -function emitHook(name){ - if(!name) throw new Error('缺少hookname'); - if(name in hooks === false){ - throw new Error('不存在的hookname'); - } - - if(hooks[name] && typeof hooks[name] === 'object'){ - if(hooks[name].type === 'single' && typeof hooks[name].listener === 'function'){ - return hooks[name].listener.apply(yapi, Array.prototype.slice.call(arguments, 1)); - } - if(Array.isArray(hooks[name.listener])){ - hooks[name].listener.forEach(listener=>{ - listener.apply(yapi, Array.prototype.slice.call(arguments,1)) - }) - } - } +async function emitHook(name) { + if (hooks[name] && typeof hooks[name] === 'object') { + let args = Array.prototype.slice.call(arguments, 1); + if (hooks[name].type === 'single' && typeof hooks[name].listener === 'function') { + return await hooks[name].listener.apply(yapi, args); + } + if (Array.isArray(hooks[name].listener)) { + let listenerList = hooks[name].listener; + for(let i=0, l = listenerList.length; i< l; i++){ + await listenerList[i].apply(yapi, args); + } + } + } } +function emitHookSync(name) { + if (hooks[name] && typeof hooks[name] === 'object') { + let args = Array.prototype.slice.call(arguments, 1); + if (hooks[name].type === 'single' && typeof hooks[name].listener === 'function') { + return hooks[name].listener.apply(yapi, args); + } + if (Array.isArray(hooks[name].listener)) { + hooks[name].listener.forEach((listener) => { + listener.apply(yapi, args) + }) + } + } +} + + yapi.bindHook = bindHook; yapi.emitHook = emitHook; +yapi.emitHookSync = emitHookSync; -module.exports = function(){ - if(yapi.WEBCONFIG.plugins && Array.isArray(yapi.WEBCONFIG.plugins)){ - yapi.WEBCONFIG.plugins.forEach(plugin=>{ - if(!yapi.commons.fileExist(yapi.path.join(plugin_path, 'yapi-plugin-' + plugin + '/server.js'))){ - console.error(`config.json配置了插件${plugin},但plugins目录没有找到此插件,请安装此插件?`); + +let pluginsConfig = initPlugins(yapi.WEBCONFIG.plugins); + +pluginsConfig.forEach(plugin => { + if (!plugin || plugin.enable === false || plugin.server === false) return null; + + if (!yapi.commons.fileExist(yapi.path.join(plugin_path, 'yapi-plugin-' + plugin.name + '/server.js'))) { + console.error(`config.json配置了插件${plugin},但plugins目录没有找到此插件,请安装此插件`); process.exit(); - } - let pluginModule = require(yapi.path.join(plugin_path, 'yapi-plugin-' + plugin + '/server.js')); - pluginModule.apply(yapi) - }) - } -} + } + let pluginModule = require(yapi.path.join(plugin_path, 'yapi-plugin-' + plugin.name + '/server.js')); + pluginModule.call(yapi, plugin) +}) + +extConfig = initPlugins(extConfig); + +extConfig.forEach(plugin => { + if (!plugin || plugin.enable === false || plugin.server === false) return null; + + if (!yapi.commons.fileExist(yapi.path.join(plugin_system_path, 'yapi-plugin-' + plugin.name + '/server.js'))) { + console.error(`config.json配置了插件${plugin},但plugins目录没有找到此插件,请安装此插件`); + process.exit(); + } + let pluginModule = require(yapi.path.join(plugin_system_path, 'yapi-plugin-' + plugin.name + '/server.js')); + pluginModule.call(yapi, plugin) + yapi.commons.log('init plugins success...') +}) + +//delete bindHook方法,避免误操作 +delete yapi.bindHook + + diff --git a/server/router.js b/server/router.js index 1756c3f4..e274b8cf 100755 --- a/server/router.js +++ b/server/router.js @@ -19,7 +19,7 @@ const authLevel = { guest:100 } -const INTERFACE_CONFIG = { +let INTERFACE_CONFIG = { interface: { prefix: '/interface/', controller: interfaceController @@ -50,7 +50,7 @@ const INTERFACE_CONFIG = { } }; -const routerConfig = { +let routerConfig = { "group": [ { "action": "list", @@ -350,23 +350,42 @@ const routerConfig = { ] } +let pluginsRouterPath = []; + +function addPluginRouter(config){ + if(!config.path || !config.controller || !config.action){ + throw new Error('Plugin Route config Error'); + } + let method = config.method || 'GET'; + let routerPath = '/plugin/' + config.path; + if(pluginsRouterPath.indexOf(routerPath) > -1){ + throw new Error('Plugin Route path conflict, please try rename the path') + } + pluginsRouterPath.push(routerPath); + createAction(config.controller, config.action, routerPath, method); +} + +yapi.emitHookSync('add_router', addPluginRouter); + for(let ctrl in routerConfig){ let actions = routerConfig[ctrl]; actions.forEach( (item) => { - createAction(ctrl, item.action, item.path, item.method); + let routerController = INTERFACE_CONFIG[ctrl].controller; + let routerPath = INTERFACE_CONFIG[ctrl].prefix + item.path; + createAction(routerController, item.action, routerPath, item.method); } ) } /** * - * @param {*} controller controller_name - * @param {*} path request_path + * @param {*} routerController controller + * @param {*} path routerPath * @param {*} method request_method , post get put delete ... - * @param {*} action controller_action_name + * @param {*} action controller action_name */ -function createAction(controller, action, path, method) { - router[method]("/api" + INTERFACE_CONFIG[controller].prefix + path, async (ctx) => { - let inst = new INTERFACE_CONFIG[controller].controller(ctx); +function createAction(routerController, action, path, method) { + router[method]("/api" + path, async (ctx) => { + let inst = new routerController(ctx); await inst.init(ctx); diff --git a/server/utils/commons.js b/server/utils/commons.js index 228132f6..7964daf5 100755 --- a/server/utils/commons.js +++ b/server/utils/commons.js @@ -172,6 +172,15 @@ exports.verifyPath = (path) => { } }; +exports.sandbox = (sandbox, script) => { + const vm = require('vm'); + sandbox = sandbox || {}; + script = new vm.Script(script); + const context = new vm.createContext(sandbox); + script.runInContext(context); + return sandbox; +} + function trim(str) { if (!str) { return str; @@ -241,7 +250,7 @@ exports.saveLog = (logData) => { logInst.save(data).then( ); - } catch(e) { + } catch (e) { yapi.commons.log(e, 'error'); // eslint-disable-line } }; From 2ccc653671a4d3e771b0083aef6e4a00d25e6aac Mon Sep 17 00:00:00 2001 From: suxiaoxin Date: Sun, 17 Sep 2017 19:22:13 +0800 Subject: [PATCH 2/4] fix: mockServer bug --- server/middleware/mockServer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/middleware/mockServer.js b/server/middleware/mockServer.js index 37dee7a4..5dc94954 100755 --- a/server/middleware/mockServer.js +++ b/server/middleware/mockServer.js @@ -109,8 +109,8 @@ module.exports = async (ctx, next) => { yapi.commons.log(e, 'error') return ctx.body = { errcode: 400, - errmsg: 'mock json数据格式有误, '+ e.message, - data: interfaceData.res_body + errmsg: '解析出错,请检查。Error: '+ e.message, + data: null } } } From 7e00de16d2e36c839ec0dbd511f923eb406502e7 Mon Sep 17 00:00:00 2001 From: suxiaoxin Date: Tue, 19 Sep 2017 17:49:59 +0800 Subject: [PATCH 3/4] fix: interfaceContent go back view bug --- .../Project/Interface/InterfaceList/InterfaceContent.js | 2 +- exts/yapi-plugin-advanced-mock/server.js | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/client/containers/Project/Interface/InterfaceList/InterfaceContent.js b/client/containers/Project/Interface/InterfaceList/InterfaceContent.js index a5b1ccda..41b355d4 100755 --- a/client/containers/Project/Interface/InterfaceList/InterfaceContent.js +++ b/client/containers/Project/Interface/InterfaceList/InterfaceContent.js @@ -98,7 +98,7 @@ class Content extends Component { let tabContent = null; if (this.state.curtab) { let C = InterfaceTabs[this.state.curtab].component; - tabContent = ; + tabContent = ; } return
diff --git a/exts/yapi-plugin-advanced-mock/server.js b/exts/yapi-plugin-advanced-mock/server.js index 1dc8670e..75af1d1b 100644 --- a/exts/yapi-plugin-advanced-mock/server.js +++ b/exts/yapi-plugin-advanced-mock/server.js @@ -44,12 +44,18 @@ module.exports = function(){ } let script = data.mock_script; let sandbox = { + header: context.ctx.header, query: context.ctx.query, body: context.ctx.request.body, mockJson: context.mockJson } + sandbox.cookie = {}; + + context.ctx.header.cookie && context.ctx.header.cookie.split(';').forEach(function( Cookie ) { + var parts = Cookie.split('='); + sandbox.cookie[ parts[ 0 ].trim() ] = ( parts[ 1 ] || '' ).trim(); + }); sandbox = yapi.commons.sandbox(sandbox, script); context.mockJson = sandbox.mockJson; - console.log(11111, context.mockJson) }) } \ No newline at end of file From ca995144271f403e584b0516b253bb4a64284505 Mon Sep 17 00:00:00 2001 From: zwjamnsss Date: Tue, 19 Sep 2017 17:59:36 +0800 Subject: [PATCH 4/4] =?UTF-8?q?opti:=20=E4=BF=AE=E6=94=B9=20tree=20?= =?UTF-8?q?=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Interface/InterfaceCol/InterfaceColMenu.js | 4 ++-- .../Interface/InterfaceCol/InterfaceColMenu.scss | 13 ++++++++++--- .../Interface/InterfaceList/InterfaceMenu.js | 5 ++--- client/containers/Project/Interface/interface.scss | 6 ++++++ 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/client/containers/Project/Interface/InterfaceCol/InterfaceColMenu.js b/client/containers/Project/Interface/InterfaceCol/InterfaceColMenu.js index 77097b53..ba66e30a 100755 --- a/client/containers/Project/Interface/InterfaceCol/InterfaceColMenu.js +++ b/client/containers/Project/Interface/InterfaceCol/InterfaceColMenu.js @@ -235,8 +235,8 @@ export default class InterfaceColMenu extends Component { title={
{col.name} - - + e.stopPropagation()}> +
} diff --git a/client/containers/Project/Interface/InterfaceCol/InterfaceColMenu.scss b/client/containers/Project/Interface/InterfaceCol/InterfaceColMenu.scss index 4fb8a4ab..2d489328 100755 --- a/client/containers/Project/Interface/InterfaceCol/InterfaceColMenu.scss +++ b/client/containers/Project/Interface/InterfaceCol/InterfaceColMenu.scss @@ -1,7 +1,17 @@ .col-list-tree { + line-height: 25px; .ant-tree-node-content-wrapper { width: calc(100% - 24px); } + .opts-icon, .case-delete-icon { + line-height: 25px; + width: 30px; + } + .opts-icon:hover, .case-delete-icon:hover { + color: #2395f1; + border-radius: 4px; + box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); + } .menu-title { display: flex; justify-content: space-between; @@ -13,9 +23,6 @@ margin-left: 5px; display: none; } - i:before{ - line-height: 17px; - } } .menu-title:hover { .case-delete-icon { diff --git a/client/containers/Project/Interface/InterfaceList/InterfaceMenu.js b/client/containers/Project/Interface/InterfaceList/InterfaceMenu.js index d755c07d..793e6b4c 100755 --- a/client/containers/Project/Interface/InterfaceList/InterfaceMenu.js +++ b/client/containers/Project/Interface/InterfaceList/InterfaceMenu.js @@ -12,7 +12,6 @@ import { Link, withRouter } from 'react-router-dom'; const confirm = Modal.confirm; const TreeNode = Tree.TreeNode; - @connect( state => { @@ -384,8 +383,8 @@ class InterfaceMenu extends Component { {menuList.map((item) => { return {item.name} - - + e.stopPropagation()}> +
} key={'cat_' + item._id} diff --git a/client/containers/Project/Interface/interface.scss b/client/containers/Project/Interface/interface.scss index bec79253..de39e312 100755 --- a/client/containers/Project/Interface/interface.scss +++ b/client/containers/Project/Interface/interface.scss @@ -94,6 +94,7 @@ overflow: hidden; top: 0px; line-height: 100%; + text-decoration: none; } .interface-item-nav{ line-height:25px; @@ -111,6 +112,11 @@ line-height: 25px; width: 30px; } + .interface-delete-icon:hover { + color: #2395f1; + border-radius: 4px; + box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); + } } }