This commit is contained in:
喻希里 2017-09-19 14:15:16 +08:00
commit 98e8af85d6
10 changed files with 148 additions and 57 deletions

View File

@ -19,24 +19,24 @@ function json_parse(data) {
}
}
function isValidJson(json){
if(!json) return false;
if(typeof json === 'object') return true;
try{
if(typeof json === 'string'){
function isValidJson(json) {
if (!json) return false;
if (typeof json === 'object') return true;
try {
if (typeof json === 'string') {
json5.parse(json);
return true;
}
}catch(e){
} catch (e) {
return false;
}
}
function isJsonData(headers, res){
if(isValidJson(res)){
function isJsonData(headers, res) {
if (isValidJson(res)) {
return true;
}
if(!headers || typeof headers !== 'object') return false;
if (!headers || typeof headers !== 'object') return false;
let isResJson = false;
Object.keys(headers).map(key => {
if (/content-type/i.test(key) && /application\/json/i.test(headers[key])) {
@ -192,7 +192,7 @@ export default class Run extends Component {
const href = URL.format({
protocol: urlObj.protocol || 'http',
host: urlObj.host,
pathname: urlObj.pathname? urlObj.pathname + path : path,
pathname: urlObj.pathname ? urlObj.pathname + path : path,
query: this.getQueryObj(query)
});
@ -207,7 +207,7 @@ export default class Run extends Component {
file: bodyType === 'file' ? 'single-file' : null,
success: (res, header) => {
try {
if(isJsonData(header)){
if (isJsonData(header)) {
res = json_parse(res);
}
@ -243,9 +243,9 @@ export default class Run extends Component {
},
error: (err, header) => {
try {
if(isJsonData(header)){
if (isJsonData(header)) {
err = json_parse(err);
}
}
} catch (e) {
message.error(e.message)
}
@ -555,20 +555,24 @@ export default class Run extends Component {
}
</div>
<Card title="请求部分" noHovering className="req-part">
<Card title={<Tooltip placement="top" title="在项目设置配置domain">请求部分&nbsp;<Icon type="question-circle-o" /></Tooltip>} noHovering className="req-part">
<div className="url">
<InputGroup compact style={{ display: 'flex' }}>
<Select disabled value={method} style={{ flexBasis: 60 }} onChange={this.changeMethod} >
<Option value="GET">GET</Option>
<Option value="POST">POST</Option>
</Select>
<Select value={caseEnv} style={{ flexBasis: 180, flexGrow: 1 }} onSelect={this.selectDomain}>
{
domains.map((item, index) => (<Option value={item.name} key={index}>{item.name + '' + item.domain}</Option>))
}
</Select>
<Input disabled value={path + search} onChange={this.changePath} spellCheck="false" style={{ flexBasis: 180, flexGrow: 1 }} />
</InputGroup>
<Tooltip placement="bottom" title="请求真实接口">
<Button
disabled={!hasPlugin}
@ -636,21 +640,21 @@ export default class Run extends Component {
<Panel
header={
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<div>BODY</div>
<div>BODY</div>
</div>
}
key="3"
className={HTTP_METHOD[method].request_body?'POST':'hidden'}
className={HTTP_METHOD[method].request_body ? 'POST' : 'hidden'}
>
<div style={{ display: HTTP_METHOD[method].request_body && bodyType !== 'form' && bodyType !== 'file'? 'block': 'none' }}>
<div id="body-other-edit" style={{ marginTop: 10, minHeight:150 }} className="pretty-editor"></div>
<div style={{ display: HTTP_METHOD[method].request_body && bodyType !== 'form' && bodyType !== 'file' ? 'block' : 'none' }}>
<div id="body-other-edit" style={{ marginTop: 10, minHeight: 150 }} className="pretty-editor"></div>
</div>
{
HTTP_METHOD[method].request_body && bodyType === 'form' &&
<div>
{
{
bodyForm.map((item, index) => {
return (
<div key={index} className="key-value-wrap">

View File

@ -131,7 +131,9 @@ class ProjectList extends Component {
}]
})(
<Select>
{this.state.groupList.map((item, index) => <Option value={item._id.toString()} key={index}>{item.group_name}</Option>)}
{this.state.groupList.map((item, index) => (
<Option disabled={!(item.role === 'dev' || item.role === 'owner')} value={item._id.toString()} key={index}>{item.group_name}</Option>
))}
</Select>
)}
</FormItem>

View File

@ -247,8 +247,8 @@ export default class InterfaceColMenu extends Component {
style={{width: '100%'}}
key={'case_' + interfaceCase._id}
title={
<div className="menu-title">
<span>{interfaceCase.casename}</span>
<div className="menu-title" title={interfaceCase.casename}>
<span className="casename">{interfaceCase.casename}</span>
<Icon type='delete' className="case-delete-icon" onClick={() => { this.showDelCaseConfirm(interfaceCase._id) }} />
</div>
}

View File

@ -6,7 +6,11 @@
display: flex;
justify-content: space-between;
overflow: hidden;
.casename {
overflow: hidden;
}
.case-delete-icon{
margin-left: 5px;
display: none;
}
i:before{

View File

@ -198,6 +198,7 @@ class InterfaceEditForm extends Component {
let editor = this.editor = new Editor('#desc');
editor.create();
editor.txt.html(this.state.desc)
}
addParams = (name, data) => {
@ -281,7 +282,7 @@ class InterfaceEditForm extends Component {
{getFieldDecorator('req_query[' + index + '].desc', {
initialValue: data.desc
})(
<Input placeholder="备注" />
<Input.TextArea autosize={true} placeholder="备注" />
)}
</Col>
<Col span="1" >
@ -308,14 +309,14 @@ class InterfaceEditForm extends Component {
{getFieldDecorator('req_headers[' + index + '].value', {
initialValue: data.value
})(
<Input placeholder="参数值" />
<Input placeholder="参数值" />
)}
</Col>
<Col span="10" >
{getFieldDecorator('req_headers[' + index + '].desc', {
initialValue: data.desc
})(
<Input placeholder="备注" />
<Input.TextArea autosize={true} placeholder="备注" />
)}
</Col>
<Col span="2" >
@ -358,7 +359,7 @@ class InterfaceEditForm extends Component {
{getFieldDecorator('req_body_form[' + index + '].desc', {
initialValue: data.desc
})(
<Input placeholder="备注" />
<Input.TextArea autosize={true} placeholder="备注" />
)}
</Col>
<Col span="1" >
@ -380,7 +381,7 @@ class InterfaceEditForm extends Component {
{getFieldDecorator('req_params[' + index + '].desc', {
initialValue: data.desc
})(
<Input placeholder="备注" />
<Input.TextArea autosize={true} placeholder="备注" />
)}
</Col>

View File

@ -8,24 +8,34 @@
// .item-all-interface {
// background-color: red;
// }
.ant-tabs-bar{
border-bottom: none;
margin-bottom: 0
}
// .ant-tabs-bar{
// border-bottom: none;
// margin-bottom: 0
// }
.ant-tabs-nav{
width:100%;
background-color: #ececec
}
.ant-tabs-tab{
min-width: 50%;
}
.ant-tabs.ant-tabs-card > .ant-tabs-bar .ant-tabs-tab{
background-color: #fff;
height: 39px;
background: #fff;
border: 1px solid #d9d9d9;
border-bottom: 0;
}
.ant-tabs.ant-tabs-card > .ant-tabs-bar .ant-tabs-tab:nth-of-type(2) {
border-left: 0;
}
.ant-tabs.ant-tabs-card > .ant-tabs-bar .ant-tabs-tab:last-of-type {
border-right: 0;
}
.ant-tabs.ant-tabs-card > .ant-tabs-bar .ant-tabs-tab-active{
height: 40px;
background-color: #efefef;
color:#021b2d;
font-weight: 500;
// margin-bottom: 0;
}
.ant-tabs.ant-tabs-card > .ant-tabs-bar .ant-tabs-nav-container{
@ -34,13 +44,13 @@
.ant-tabs.ant-tabs-card > .ant-tabs-bar{
text-align: center;
line-height: 40px;
height:40px;
background: #ececec;
}
.ant-tabs-nav-wrap{
height: 40px;
line-height: 31px;
// border-bottom: 1px solid #d9d9d9;
}
.ant-input {
width: 100%;

View File

@ -1,6 +1,6 @@
{
"name": "yapi",
"version": "1.0.0",
"version": "1.0.1",
"description": "YAPI",
"main": "index.js",
"scripts": {

View File

@ -79,20 +79,22 @@ class interfaceController extends baseController {
let http_path = url.parse(params.path, true);
params.path = http_path.pathname;
if (!yapi.commons.verifyPath(http_path.pathname)) {
return ctx.body = yapi.commons.resReturn(null, 400, '接口path第一位必须是/,最后一位不能为/');
}
if (!params.req_query) {
params.req_query = [];
Object.keys(http_path.query).forEach((item) => {
params.req_query.push({
name: item
})
params.query_path = {};
params.query_path.path = http_path.pathname;
params.query_path.params = [];
Object.keys(http_path.query).forEach((item) => {
params.query_path.params.push({
name: item,
value: http_path.query[item]
})
}
})
let checkRepeat = await this.Model.checkRepeat(params.project_id, params.path, params.method);
@ -109,6 +111,7 @@ class interfaceController extends baseController {
path: params.path,
desc: params.desc,
method: params.method,
query_path: params.query_path,
req_headers: params.req_headers,
req_body_type: params.req_body_type,
res_body: params.res_body,
@ -118,11 +121,11 @@ class interfaceController extends baseController {
up_time: yapi.commons.time()
};
if (params.req_query) {
if (!_.isUndefined(params.req_query)) {
data.req_query = params.req_query;
}
if (params.req_body_form) {
if (!_.isUndefined(params.req_body_form)) {
data.req_body_form = params.req_body_form;
}
@ -147,7 +150,7 @@ class interfaceController extends baseController {
} else {
data.type = 'static'
}
if (params.req_body_other) {
if (!_.isUndefined(params.req_body_other)) {
data.req_body_other = params.req_body_other;
}
@ -350,10 +353,24 @@ class interfaceController extends baseController {
return ctx.body = yapi.commons.resReturn(null, 400, '没有权限');
}
if (params.path && !yapi.commons.verifyPath(params.path)) {
let http_path = url.parse(params.path, true);
if (!yapi.commons.verifyPath(http_path.pathname)) {
return ctx.body = yapi.commons.resReturn(null, 400, '接口path第一位必须是/,最后一位不能为/');
}
params.query_path = {};
params.query_path.path = http_path.pathname;
params.query_path.params = [];
Object.keys(http_path.query).forEach((item) => {
params.query_path.params.push({
name: item,
value: http_path.query[item]
})
})
if (params.path && (params.path !== interfaceData.path || params.method !== interfaceData.method)) {
let checkRepeat = await this.Model.checkRepeat(interfaceData.project_id, params.path, params.method);
if (checkRepeat > 0) {
@ -362,7 +379,8 @@ class interfaceController extends baseController {
}
let data = {
up_time: yapi.commons.time()
up_time: yapi.commons.time(),
query_path: params.query_path
};
if (!_.isUndefined(params.path)) {
@ -400,6 +418,7 @@ class interfaceController extends baseController {
if (!_.isUndefined(params.req_query)) {
data.req_query = params.req_query;
}
if (!_.isUndefined(params.req_body_other)) {
data.req_body_other = params.req_body_other;
}

View File

@ -8,6 +8,7 @@ const Mock = require('mockjs');
function matchApi(apiPath, apiRule) {
let apiRules = apiRule.split("/");
let apiPaths = apiPath.split("/");
if (apiPaths.length !== apiRules.length) {
return false;
}
@ -59,20 +60,51 @@ module.exports = async (ctx, next) => {
try {
newpath = path.substr(project.basepath.length);
interfaceData = await interfaceInst.getByPath(project._id, newpath, ctx.method);
//处理query_path情况
if (!interfaceData || interfaceData.length === 0) {
//非正常跨域预检请求回应
if (ctx.method === 'OPTIONS') {
ctx.set("Access-Control-Allow-Origin", "*")
ctx.set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE")
return ctx.body = 'ok'
}
let newData = await interfaceInst.getVar(project._id, ctx.method);
interfaceData = await interfaceInst.getByQueryPath(project._id, newpath, ctx.method);
let i, l, j, len, curQuery, match = false;
for (i = 0, l = interfaceData.length; i < l; i++) {
match = false;
currentInterfaceData = interfaceData[i];
curQuery = currentInterfaceData.query_path;
if (!curQuery || typeof curQuery !== 'object' || !curQuery.path) {
continue;
}
for (j = 0, len = curQuery.params.length; j < len; j++) {
if (ctx.query[curQuery.params[j].name] !== curQuery.params[j].value) {
continue;
}
if(j === len -1){
match = true;
}
}
if (match) {
interfaceData = [currentInterfaceData];
break;
}
if(i === l -1){
interfaceData = [];
}
}
}
//处理动态路由
if (!interfaceData || interfaceData.length === 0) {
let newData = await interfaceInst.getVar(project._id, ctx.method);
let findInterface = _.find(newData, (item) => {
return matchApi(newpath, item.path)
});
if (!findInterface) {
//非正常跨域预检请求回应
if (ctx.method === 'OPTIONS') {
ctx.set("Access-Control-Allow-Origin", "*")
ctx.set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE")
return ctx.body = 'ok'
}
return ctx.body = yapi.commons.resReturn(null, 404, '不存在的api');
}
interfaceData = [
@ -82,10 +114,14 @@ module.exports = async (ctx, next) => {
}
if (interfaceData.length > 1) {
return ctx.body = yapi.commons.resReturn(null, 405, '存在多个api请检查数据库');
} else {
interfaceData = interfaceData[0];
}
interfaceData = interfaceData[0];
ctx.set("Access-Control-Allow-Origin", "*")
if (interfaceData.res_body_type === 'json') {
try {

View File

@ -20,6 +20,12 @@ class interfaceModel extends baseModel {
add_time: Number,
up_time: Number,
type: {type: String, enum: ['static', 'var'], default:'static'},
query_path: {
path: String,
params: [{
name: String, value: String
}],
},
req_query:[{
name: String, value: String, desc: String, required: {
type:String,
@ -83,6 +89,15 @@ class interfaceModel extends baseModel {
}).select('_id path').exec()
}
getByQueryPath(project_id, path, method) {
return this.model.find({
project_id: project_id,
"query_path.path": path,
method: method
})
.exec();
}
getByPath(project_id, path, method) {
return this.model.find({
project_id: project_id,