mirror of
https://github.com/YMFE/yapi.git
synced 2025-03-13 14:26:50 +08:00
Merge branch 'dev' of gitlab.corp.qunar.com:mfe/yapi into dev
This commit is contained in:
commit
765889462d
@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import moment from 'moment';
|
||||
import constants from './constants/variable';
|
||||
import Mock from 'mockjs'
|
||||
|
||||
const Roles = {
|
||||
0 : 'admin',
|
||||
@ -71,6 +72,40 @@ exports.debounce = (func, wait) => {
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
exports.simpleJsonPathParse = function (key, json){
|
||||
if(!key || typeof key !== 'string' || key.indexOf('$.') !== 0 || key.length <= 2){
|
||||
return null;
|
||||
}
|
||||
let keys = key.substr(2).split(".");
|
||||
keys = keys.filter(item=>{
|
||||
return item;
|
||||
})
|
||||
for(let i=0, l = keys.length; i< l; i++){
|
||||
try{
|
||||
let m = keys[i].match(/(.*?)\[([0-9]+)\]/)
|
||||
if(m){
|
||||
json = json[m[1]][m[2]];
|
||||
}else{
|
||||
json = json[keys[i]];
|
||||
}
|
||||
|
||||
|
||||
}catch(e){
|
||||
json = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return json;
|
||||
|
||||
}
|
||||
|
||||
exports.handleMockWord =(word) =>{
|
||||
if(!word || typeof word !== 'string' || word[0] !== '@') return word;
|
||||
return Mock.mock(word);
|
||||
}
|
||||
|
||||
// 从 Javascript 对象中选取随机属性
|
||||
exports.pickRandomProperty = (obj) => {
|
||||
let result;
|
||||
|
@ -10,6 +10,7 @@ import URL from 'url';
|
||||
const MockExtra = require('common/mock-extra.js')
|
||||
import './Postman.scss';
|
||||
import json5 from 'json5'
|
||||
import { handleMockWord } from '../../common.js'
|
||||
|
||||
function json_parse(data) {
|
||||
try {
|
||||
@ -19,23 +20,8 @@ function json_parse(data) {
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function isJsonData(headers, res) {
|
||||
if (isValidJson(res)) {
|
||||
return true;
|
||||
}
|
||||
function isJsonData(headers) {
|
||||
if (!headers || typeof headers !== 'object') return false;
|
||||
let isResJson = false;
|
||||
Object.keys(headers).map(key => {
|
||||
@ -75,8 +61,9 @@ export default class Run extends Component {
|
||||
bodyType: '',
|
||||
bodyOther: '',
|
||||
loading: false,
|
||||
validRes: null,
|
||||
hasPlugin: true
|
||||
validRes: [],
|
||||
hasPlugin: true,
|
||||
test_status: null
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
@ -136,8 +123,13 @@ export default class Run extends Component {
|
||||
req_body_form = [],
|
||||
basepath = '',
|
||||
env = [],
|
||||
case_env = ''
|
||||
case_env = '',
|
||||
test_status = '',
|
||||
test_res_body = '',
|
||||
test_report = [],
|
||||
test_res_header=''
|
||||
} = data;
|
||||
|
||||
// case 任意编辑 pathname,不管项目的 basepath
|
||||
const pathname = (type === 'inter' ? (basepath + url) : url).replace(/\/+/g, '/');
|
||||
|
||||
@ -168,12 +160,21 @@ export default class Run extends Component {
|
||||
bodyOther: req_body_other,
|
||||
caseEnv: case_env || (env[0] && env[0].name),
|
||||
bodyType: req_body_type || 'form',
|
||||
loading: false
|
||||
loading: false,
|
||||
test_status: test_status,
|
||||
validRes: test_report,
|
||||
res: test_res_body,
|
||||
resHeader: test_res_header
|
||||
}, () => {
|
||||
if (req_body_type && req_body_type !== 'file' && req_body_type !== 'form') {
|
||||
this.loadBodyEditor()
|
||||
}
|
||||
if(test_res_body){
|
||||
this.bindAceEditor();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@autobind
|
||||
@ -192,7 +193,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 ? URL.resolve(urlObj.pathname, path) : path,
|
||||
query: this.getQueryObj(query)
|
||||
});
|
||||
|
||||
@ -212,7 +213,7 @@ export default class Run extends Component {
|
||||
}
|
||||
|
||||
const { res_body, res_body_type } = that.props.data;
|
||||
let validRes = '';
|
||||
let validRes = [];
|
||||
let query = {};
|
||||
that.state.query.forEach(item => {
|
||||
query[item.name] = item.value;
|
||||
@ -233,8 +234,17 @@ export default class Run extends Component {
|
||||
validRes = Mock.valid(tpl, res)
|
||||
}
|
||||
|
||||
message.success('请求完成')
|
||||
that.setState({ res, resHeader: header, validRes })
|
||||
|
||||
if (Array.isArray(validRes) && validRes.length > 0) {
|
||||
message.warn('请求完成, 返回数据跟接口定义不匹配');
|
||||
validRes = validRes.map(item => {
|
||||
return item.message
|
||||
})
|
||||
that.setState({ res, resHeader: header, validRes, test_status: 'invalid' })
|
||||
} else if (Array.isArray(validRes) && validRes.length === 0) {
|
||||
message.success('请求完成');
|
||||
that.setState({ res, resHeader: header, validRes: ['验证通过'], test_status: 'ok' })
|
||||
}
|
||||
that.setState({ loading: false })
|
||||
that.bindAceEditor()
|
||||
} catch (e) {
|
||||
@ -250,7 +260,7 @@ export default class Run extends Component {
|
||||
message.error(e.message)
|
||||
}
|
||||
message.error('请求异常')
|
||||
that.setState({ res: err || '请求失败', resHeader: header, validRes: null })
|
||||
that.setState({ res: err || '请求失败', resHeader: header, validRes: [], test_status: 'error' })
|
||||
that.setState({ loading: false })
|
||||
that.bindAceEditor()
|
||||
}
|
||||
@ -428,11 +438,12 @@ export default class Run extends Component {
|
||||
const obj = {};
|
||||
arr.forEach(item => {
|
||||
if (item.name && item.type !== 'file') {
|
||||
obj[item.name] = item.value || '';
|
||||
obj[item.name] = handleMockWord(item.value);
|
||||
}
|
||||
})
|
||||
return obj;
|
||||
}
|
||||
|
||||
getFiles(bodyForm) {
|
||||
const files = {};
|
||||
bodyForm.forEach(item => {
|
||||
@ -446,7 +457,7 @@ export default class Run extends Component {
|
||||
const queryObj = {};
|
||||
query.forEach(item => {
|
||||
if (item.name) {
|
||||
queryObj[item.name] = item.value || '';
|
||||
queryObj[item.name] = handleMockWord(item.value);
|
||||
}
|
||||
})
|
||||
return queryObj;
|
||||
@ -498,11 +509,10 @@ export default class Run extends Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { method, domains, pathParam, pathname, query, headers, bodyForm, caseEnv, bodyType, resHeader, loading, validRes, res } = this.state;
|
||||
const { method, domains, pathParam, pathname, query, headers, bodyForm, caseEnv, bodyType, resHeader, loading, validRes } = this.state;
|
||||
HTTP_METHOD[method] = HTTP_METHOD[method] || {}
|
||||
const hasPlugin = this.state.hasPlugin;
|
||||
let isResJson = isJsonData(resHeader, res);
|
||||
|
||||
let isResJson = isJsonData(resHeader);
|
||||
let path = pathname;
|
||||
pathParam.forEach(item => {
|
||||
path = path.replace(`:${item.name}`, item.value || `:${item.name}`);
|
||||
@ -510,16 +520,10 @@ export default class Run extends Component {
|
||||
const search = decodeURIComponent(URL.format({ query: this.getQueryObj(query) }));
|
||||
|
||||
let validResView;
|
||||
if (!validRes) {
|
||||
validResView = '请定义返回json'
|
||||
}
|
||||
if (Array.isArray(validRes) && validRes.length > 0) {
|
||||
validResView = validRes.map((item, index) => {
|
||||
return <div key={index}>{item.message}</div>
|
||||
})
|
||||
} else if (Array.isArray(validRes)) {
|
||||
validResView = <p>验证通过</p>
|
||||
}
|
||||
validResView = validRes.map((item, index) => {
|
||||
return <div key={index}>{item}</div>
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
@ -569,7 +573,7 @@ export default class Run extends Component {
|
||||
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>
|
||||
|
||||
@ -587,7 +591,7 @@ export default class Run extends Component {
|
||||
onClick={this.props.save}
|
||||
type="primary"
|
||||
style={{ marginLeft: 10 }}
|
||||
>{this.props.type === 'inter' ? '保存' : '更新'}</Button>
|
||||
>{this.props.type === 'inter' ? '保存' : '保存'}</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
|
@ -49,8 +49,8 @@
|
||||
transition: all .2s;
|
||||
}
|
||||
.delete-group:hover, .edit-group:hover {
|
||||
color: $color-blue;
|
||||
background-color: $color-white;
|
||||
background-color: $color-blue;
|
||||
border: 1px solid $color-blue;
|
||||
}
|
||||
}
|
||||
.group-operate {
|
||||
|
@ -0,0 +1,93 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Row, Col, Tabs } from 'antd'
|
||||
const TabPane = Tabs.TabPane;
|
||||
function json_format(json) {
|
||||
return JSON.stringify(json, null, ' ')
|
||||
}
|
||||
|
||||
const CaseReport = function (props) {
|
||||
let body = json_format(props.body);
|
||||
let headers = json_format(props.headers, null, ' ');
|
||||
let res_header = json_format(props.res_header, null, ' ');
|
||||
let res_body = json_format(props.res_body);
|
||||
let validRes = props.validRes.map((item, index) => {
|
||||
return <div key={index}>{item.message}</div>
|
||||
})
|
||||
|
||||
return <div className="report">
|
||||
<Tabs defaultActiveKey="request" >
|
||||
<TabPane className="case-report-pane" tab="Request" key="request">
|
||||
<Row className="case-report">
|
||||
<Col className="case-report-title" span="6">Url</Col>
|
||||
<Col span="18">{props.url}</Col>
|
||||
</Row>
|
||||
{props.query ?
|
||||
<Row className="case-report">
|
||||
<Col className="case-report-title" span="6">Query</Col>
|
||||
<Col span="18">{props.query}</Col>
|
||||
</Row>
|
||||
: null
|
||||
}
|
||||
|
||||
{props.headers ?
|
||||
<Row className="case-report">
|
||||
<Col className="case-report-title" span="6">Headers</Col>
|
||||
<Col span="18"><pre>{headers}</pre></Col>
|
||||
</Row>
|
||||
: null
|
||||
}
|
||||
|
||||
{props.body ?
|
||||
<Row className="case-report">
|
||||
<Col className="case-report-title" span="6">Body</Col>
|
||||
<Col span="18"><pre>{body}</pre></Col>
|
||||
</Row>
|
||||
: null
|
||||
}
|
||||
</TabPane>
|
||||
<TabPane className="case-report-pane" tab="Response" key="response">
|
||||
{props.res_header ?
|
||||
<Row className="case-report">
|
||||
<Col className="case-report-title" span="6">Headers</Col>
|
||||
<Col span="18"><pre>{res_header}</pre></Col>
|
||||
</Row>
|
||||
: null
|
||||
}
|
||||
{props.res_body ?
|
||||
<Row className="case-report">
|
||||
<Col className="case-report-title" span="6">Body</Col>
|
||||
<Col span="18"><pre>{res_body}</pre></Col>
|
||||
</Row>
|
||||
: null
|
||||
}
|
||||
|
||||
</TabPane>
|
||||
<TabPane className="case-report-pane" tab="验证结果" key="valid">
|
||||
{props.validRes ?
|
||||
<Row className="case-report">
|
||||
<Col className="case-report-title" span="6">验证结果</Col>
|
||||
<Col span="18">{validRes}</Col>
|
||||
</Row>
|
||||
: null
|
||||
}
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
}
|
||||
|
||||
CaseReport.propTypes = {
|
||||
url: PropTypes.string,
|
||||
body: PropTypes.any,
|
||||
headers: PropTypes.object,
|
||||
res_header: PropTypes.object,
|
||||
res_body: PropTypes.any,
|
||||
query: PropTypes.string,
|
||||
validRes: PropTypes.array
|
||||
}
|
||||
|
||||
|
||||
export default CaseReport;
|
@ -96,6 +96,7 @@ export default class InterfaceCaseContent extends Component {
|
||||
}
|
||||
|
||||
updateCase = async () => {
|
||||
|
||||
const {
|
||||
caseEnv: case_env,
|
||||
pathname: path,
|
||||
@ -107,9 +108,10 @@ export default class InterfaceCaseContent extends Component {
|
||||
bodyForm: req_body_form,
|
||||
bodyOther: req_body_other
|
||||
} = this.postman.state;
|
||||
|
||||
const {editCasename: casename} = this.state;
|
||||
const {_id: id} = this.props.currCase;
|
||||
const res = await axios.post('/api/col/up_case', {
|
||||
let params = {
|
||||
id,
|
||||
casename,
|
||||
case_env,
|
||||
@ -121,7 +123,20 @@ export default class InterfaceCaseContent extends Component {
|
||||
req_body_type,
|
||||
req_body_form,
|
||||
req_body_other
|
||||
});
|
||||
};
|
||||
if(this.postman.state.test_status !== 'error'){
|
||||
params.test_res_body = this.postman.state.res;
|
||||
params.test_report = this.postman.state.validRes;
|
||||
params.test_status = this.postman.state.test_status;
|
||||
params.test_res_header = this.postman.state.resHeader;
|
||||
}
|
||||
|
||||
|
||||
if(params.test_res_body && typeof params.test_res_body === 'object'){
|
||||
params.test_res_body = JSON.stringify(params.test_res_body, null, ' ');
|
||||
}
|
||||
|
||||
const res = await axios.post('/api/col/up_case', params);
|
||||
if (this.props.currCase.casename !== casename) {
|
||||
this.props.fetchInterfaceColList(this.props.match.params.id);
|
||||
}
|
||||
|
@ -3,9 +3,31 @@ import { connect } from 'react-redux';
|
||||
import PropTypes from 'prop-types'
|
||||
import { withRouter } from 'react-router'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { Table, Tooltip } from 'antd'
|
||||
import { Tooltip, Icon, Button, Spin, Modal, message } from 'antd'
|
||||
import { fetchInterfaceColList, fetchCaseList, setColData } from '../../../../reducer/modules/interfaceCol'
|
||||
import { formatTime } from '../../../../common.js'
|
||||
import HTML5Backend from 'react-dnd-html5-backend';
|
||||
import { DragDropContext } from 'react-dnd';
|
||||
import { handleMockWord, simpleJsonPathParse } from '../../../../common.js'
|
||||
// import { formatTime } from '../../../../common.js'
|
||||
import * as Table from 'reactabular-table';
|
||||
import * as dnd from 'reactabular-dnd';
|
||||
import * as resolve from 'table-resolver';
|
||||
import axios from 'axios'
|
||||
import URL from 'url';
|
||||
import Mock from 'mockjs'
|
||||
import json5 from 'json5'
|
||||
import CaseReport from './CaseReport.js'
|
||||
const MockExtra = require('common/mock-extra.js')
|
||||
|
||||
function json_parse(data) {
|
||||
try {
|
||||
return json5.parse(data)
|
||||
} catch (e) {
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@connect(
|
||||
state => {
|
||||
@ -14,7 +36,8 @@ import { formatTime } from '../../../../common.js'
|
||||
currColId: state.interfaceCol.currColId,
|
||||
currCaseId: state.interfaceCol.currCaseId,
|
||||
isShowCol: state.interfaceCol.isShowCol,
|
||||
currCaseList: state.interfaceCol.currCaseList
|
||||
currCaseList: state.interfaceCol.currCaseList,
|
||||
currProject: state.project.currProject
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -24,7 +47,8 @@ import { formatTime } from '../../../../common.js'
|
||||
}
|
||||
)
|
||||
@withRouter
|
||||
export default class InterfaceColContent extends Component {
|
||||
@DragDropContext(HTML5Backend)
|
||||
class InterfaceColContent extends Component {
|
||||
|
||||
static propTypes = {
|
||||
match: PropTypes.object,
|
||||
@ -36,11 +60,22 @@ export default class InterfaceColContent extends Component {
|
||||
currCaseList: PropTypes.array,
|
||||
currColId: PropTypes.number,
|
||||
currCaseId: PropTypes.number,
|
||||
isShowCol: PropTypes.bool
|
||||
isShowCol: PropTypes.bool,
|
||||
currProject: PropTypes.object
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
super(props);
|
||||
this.reports = {};
|
||||
this.records = {};
|
||||
this.state = {
|
||||
rows: [],
|
||||
reports: {},
|
||||
visible: false,
|
||||
curCaseid: null
|
||||
};
|
||||
this.onRow = this.onRow.bind(this);
|
||||
this.onMoveRow = this.onMoveRow.bind(this);
|
||||
}
|
||||
|
||||
async componentWillMount() {
|
||||
@ -49,73 +84,362 @@ export default class InterfaceColContent extends Component {
|
||||
const params = this.props.match.params;
|
||||
const { actionId } = params;
|
||||
currColId = +actionId ||
|
||||
result.payload.data.data.find(item => +item._id === +currColId) && +currColId ||
|
||||
result.payload.data.data[0]._id;
|
||||
result.payload.data.data.find(item => +item._id === +currColId) && +currColId ||
|
||||
result.payload.data.data[0]._id;
|
||||
this.props.history.push('/project/' + params.id + '/interface/col/' + currColId)
|
||||
if(currColId && currColId != 0){
|
||||
this.props.fetchCaseList(currColId);
|
||||
this.props.setColData({currColId: +currColId, isShowCol: true})
|
||||
if (currColId && currColId != 0) {
|
||||
await this.props.fetchCaseList(currColId);
|
||||
this.props.setColData({ currColId: +currColId, isShowCol: true })
|
||||
this.handleColdata(this.props.currCaseList)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
handleColdata = (rows) => {
|
||||
rows = rows.map((item) => {
|
||||
item.id = item._id;
|
||||
item._test_status = item.test_status;
|
||||
return item;
|
||||
})
|
||||
rows = rows.sort((n, o) => {
|
||||
return n.index > o.index
|
||||
})
|
||||
this.setState({
|
||||
rows: rows
|
||||
})
|
||||
}
|
||||
|
||||
executeTests = async () => {
|
||||
for (let i = 0, l = this.state.rows.length, newRows, curitem; i < l; i++) {
|
||||
let { rows } = this.state;
|
||||
curitem = Object.assign({}, rows[i], { test_status: 'loading' });
|
||||
newRows = [].concat([], rows);
|
||||
newRows[i] = curitem;
|
||||
this.setState({
|
||||
rows: newRows
|
||||
})
|
||||
let status = 'error';
|
||||
try {
|
||||
let result = await this.handleTest(curitem);
|
||||
if (result.code === 400) {
|
||||
status = 'error';
|
||||
} else if (result.code === 0) {
|
||||
status = 'ok';
|
||||
} else if (result.code === 1) {
|
||||
status = 'invalid'
|
||||
}
|
||||
this.reports[curitem._id] = result;
|
||||
this.records[curitem._id] = result.res_body;
|
||||
} catch (e) {
|
||||
status = 'error';
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
curitem = Object.assign({}, rows[i], { test_status: status });
|
||||
newRows = [].concat([], rows);
|
||||
newRows[i] = curitem;
|
||||
this.setState({
|
||||
rows: newRows
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
handleTest = (interfaceData) => {
|
||||
const { currProject } = this.props;
|
||||
const { case_env } = interfaceData;
|
||||
let path = URL.resolve(currProject.basepath, interfaceData.path);
|
||||
interfaceData.req_params = interfaceData.req_params || [];
|
||||
interfaceData.req_params.forEach(item => {
|
||||
path = path.replace(`:${item.name}`, item.value || `:${item.name}`);
|
||||
});
|
||||
const domains = currProject.env.concat();
|
||||
const urlObj = URL.parse(domains.find(item => item.name === case_env).domain);
|
||||
const href = URL.format({
|
||||
protocol: urlObj.protocol || 'http',
|
||||
host: urlObj.host,
|
||||
pathname: urlObj.pathname ? URL.resolve(urlObj.pathname, path) : path,
|
||||
query: this.getQueryObj(interfaceData.req_query)
|
||||
});
|
||||
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let result = { code: 400, msg: '数据异常', validRes: [] };
|
||||
let that = this;
|
||||
window.crossRequest({
|
||||
url: href,
|
||||
method: interfaceData.method,
|
||||
headers: that.getHeadersObj(interfaceData.req_headers),
|
||||
data: interfaceData.req_body_type === 'form' ? that.arrToObj(interfaceData.req_body_form) : interfaceData.req_body_other,
|
||||
success: (res, header) => {
|
||||
res = json_parse(res);
|
||||
result.url = href;
|
||||
result.method = interfaceData.method;
|
||||
result.headers = that.getHeadersObj(interfaceData.req_headers);
|
||||
result.body = interfaceData.req_body_type === 'form' ? that.arrToObj(interfaceData.req_body_form) : interfaceData.req_body_other
|
||||
result.res_header = header;
|
||||
result.res_body = res;
|
||||
if (res && typeof res === 'object') {
|
||||
let tpl = MockExtra(json_parse(interfaceData.res_body), {
|
||||
query: interfaceData.req_query,
|
||||
body: interfaceData.req_body_form
|
||||
})
|
||||
let validRes = Mock.valid(tpl, res);
|
||||
|
||||
if (validRes.length === 0) {
|
||||
result.code = 0;
|
||||
result.validRes = [{message: '验证通过'}];
|
||||
resolve(result);
|
||||
} else if (validRes.length > 0) {
|
||||
result.code = 1;
|
||||
result.validRes = validRes;
|
||||
resolve(result)
|
||||
}
|
||||
} else {
|
||||
reject(result)
|
||||
}
|
||||
},
|
||||
error: (res) => {
|
||||
result.code = 400;
|
||||
result.msg = '请求异常'
|
||||
reject(res)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
handleVarWord(val){
|
||||
return simpleJsonPathParse(val, this.records)
|
||||
}
|
||||
|
||||
handleValue(val){
|
||||
if(!val || typeof val !== 'string'){
|
||||
return val;
|
||||
}else if(val[0] === '@'){
|
||||
return handleMockWord(val);
|
||||
}else if(val.indexOf('$.') === 0){
|
||||
return this.handleVarWord(val);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
arrToObj =(arr) =>{
|
||||
arr = arr || [];
|
||||
const obj = {};
|
||||
arr.forEach(item => {
|
||||
if (item.name && item.type !== 'file') {
|
||||
obj[item.name] = this.handleValue(item.value);
|
||||
}
|
||||
})
|
||||
return obj;
|
||||
}
|
||||
|
||||
getQueryObj =(query)=> {
|
||||
query = query || [];
|
||||
const queryObj = {};
|
||||
query.forEach(item => {
|
||||
if (item.name) {
|
||||
queryObj[item.name] = this.handleValue(item.value);
|
||||
}
|
||||
})
|
||||
return queryObj;
|
||||
}
|
||||
getHeadersObj = (headers) =>{
|
||||
headers = headers || [];
|
||||
const headersObj = {};
|
||||
headers.forEach(item => {
|
||||
if (item.name && item.value) {
|
||||
headersObj[item.name] = item.value;
|
||||
}
|
||||
})
|
||||
return headersObj;
|
||||
}
|
||||
|
||||
onRow(row) {
|
||||
return {
|
||||
rowId: row.id,
|
||||
onMove: this.onMoveRow
|
||||
};
|
||||
}
|
||||
onMoveRow({ sourceRowId, targetRowId }) {
|
||||
let rows = dnd.moveRows({
|
||||
sourceRowId,
|
||||
targetRowId
|
||||
})(this.state.rows);
|
||||
let changes = [];
|
||||
rows.forEach((item, index) => {
|
||||
changes.push({
|
||||
id: item._id,
|
||||
index: index
|
||||
})
|
||||
})
|
||||
|
||||
axios.post('/api/col/up_col_index', changes).then()
|
||||
if (rows) {
|
||||
this.setState({ rows });
|
||||
}
|
||||
}
|
||||
|
||||
async componentWillReceiveProps(nextProps) {
|
||||
const { interfaceColList } = nextProps;
|
||||
const { actionId: oldColId, id } = this.props.match.params
|
||||
let newColId = nextProps.match.params.actionId
|
||||
if (!interfaceColList.find(item => +item._id === +newColId)) {
|
||||
this.props.history.push('/project/' + id + '/interface/col/' + interfaceColList[0]._id)
|
||||
} else if (oldColId !== newColId) {
|
||||
if(newColId && newColId != 0){
|
||||
this.props.fetchCaseList(newColId);
|
||||
this.props.setColData({currColId: +newColId, isShowCol: true})
|
||||
if (newColId && newColId != 0) {
|
||||
await this.props.fetchCaseList(newColId);
|
||||
this.props.setColData({ currColId: +newColId, isShowCol: true })
|
||||
this.handleColdata(this.props.currCaseList)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
openReport = (id) => {
|
||||
if (!this.reports[id]) {
|
||||
return message.warn('还没有生成报告')
|
||||
}
|
||||
this.setState({
|
||||
visible: true,
|
||||
curCaseid: id
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
handleCancel = () => {
|
||||
this.setState({
|
||||
visible: false
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
const { currCaseList } = this.props;
|
||||
|
||||
const columns = [{
|
||||
title: '用例名称',
|
||||
dataIndex: 'casename',
|
||||
key: 'casename',
|
||||
render: (text, item)=>{
|
||||
return <Link to={"/project/" + item.project_id + "/interface/case/" + item._id} >{text}</Link>
|
||||
property: 'casename',
|
||||
header: {
|
||||
label: '用例名称'
|
||||
},
|
||||
cell: {
|
||||
formatters: [
|
||||
(text, { rowData }) => {
|
||||
let record = rowData;
|
||||
return <Link to={"/project/" + record.project_id + "/interface/case/" + record._id}>{record.casename}</Link>
|
||||
}
|
||||
]
|
||||
}
|
||||
}, {
|
||||
title: '接口路径',
|
||||
dataIndex: 'path',
|
||||
key: 'path',
|
||||
render: (path, record) => {
|
||||
return (
|
||||
<Tooltip title="跳转到对应接口">
|
||||
<Link to={`/project/${record.project_id}/interface/api/${record.interface_id}`}>{path}</Link>
|
||||
</Tooltip>
|
||||
)
|
||||
header: {
|
||||
label: 'key',
|
||||
formatters: [() => {
|
||||
return <Tooltip title="每个用例都有一个独一无二的key,可用来获取匹配的接口响应数据">
|
||||
Key</Tooltip>
|
||||
}]
|
||||
},
|
||||
cell: {
|
||||
formatters: [
|
||||
(value, { rowData }) => {
|
||||
return <span>{rowData._id}</span>
|
||||
}]
|
||||
}
|
||||
}, {
|
||||
title: '请求方法',
|
||||
dataIndex: 'method',
|
||||
key: 'method'
|
||||
}, {
|
||||
title: '更新时间',
|
||||
dataIndex: 'up_time',
|
||||
key: 'up_time',
|
||||
render: (item) => {
|
||||
return <span>{formatTime(item)}</span>
|
||||
property: 'test_status',
|
||||
header: {
|
||||
label: '状态'
|
||||
},
|
||||
cell: {
|
||||
formatters: [(value, { rowData }) => {
|
||||
switch (rowData.test_status) {
|
||||
case 'ok':
|
||||
return <div ><Icon style={{ color: '#00a854' }} type="check-circle" /></div>
|
||||
case 'error':
|
||||
return <div ><Tooltip title="请求异常"><Icon type="info-circle" style={{ color: '#f04134' }} /></Tooltip></div>
|
||||
case 'invalid':
|
||||
return <div ><Tooltip title="返回数据校验未通过"><Icon type="exclamation-circle" style={{ color: '#ffbf00' }} /></Tooltip></div>
|
||||
case 'loading':
|
||||
return <div ><Spin /></div>
|
||||
default:
|
||||
return <div ><Icon style={{ color: '#00a854' }} type="check-circle" /></div>
|
||||
}
|
||||
}]
|
||||
}
|
||||
}];
|
||||
}, {
|
||||
property: 'path',
|
||||
header: {
|
||||
label: '接口路径'
|
||||
},
|
||||
cell: {
|
||||
formatters: [
|
||||
(text, { rowData }) => {
|
||||
let record = rowData;
|
||||
return (
|
||||
<Tooltip title="跳转到对应接口">
|
||||
<Link to={`/project/${record.project_id}/interface/api/${record.interface_id}`}>{record.path}</Link>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
]
|
||||
}
|
||||
}, {
|
||||
header: {
|
||||
label: '测试报告'
|
||||
|
||||
},
|
||||
cell: {
|
||||
formatters: [(text, { rowData }) => {
|
||||
return <Button onClick={() => this.openReport(rowData.id)}>报告</Button>
|
||||
}]
|
||||
}
|
||||
}
|
||||
];
|
||||
const { rows } = this.state;
|
||||
const components = {
|
||||
header: {
|
||||
cell: dnd.Header
|
||||
},
|
||||
body: {
|
||||
row: dnd.Row
|
||||
}
|
||||
};
|
||||
|
||||
const resolvedColumns = resolve.columnChildren({ columns });
|
||||
const resolvedRows = resolve.resolve({
|
||||
columns: resolvedColumns,
|
||||
method: resolve.nested
|
||||
})(rows);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div style={{padding:"16px"}}>
|
||||
<h2 style={{marginBottom: '10px'}}>测试集合</h2>
|
||||
<Table dataSource={currCaseList} columns={columns} pagination={false} rowKey="_id"/>
|
||||
</div>
|
||||
<div className="interface-col">
|
||||
<h2 style={{ marginBottom: '10px', display: 'inline-block' }}>测试集合</h2>
|
||||
<Button type="primary" style={{ float: 'right' }} onClick={this.executeTests}>开始测试</Button>
|
||||
<Table.Provider
|
||||
components={components}
|
||||
columns={resolvedColumns}
|
||||
style={{ width: '100%', lineHeight: '36px' }}
|
||||
>
|
||||
<Table.Header
|
||||
style={{ textAlign: 'left' }}
|
||||
headerRows={resolve.headerRows({ columns })}
|
||||
/>
|
||||
|
||||
<Table.Body
|
||||
rows={resolvedRows}
|
||||
rowKey="id"
|
||||
onRow={this.onRow}
|
||||
/>
|
||||
</Table.Provider>
|
||||
<Modal
|
||||
title="测试报告"
|
||||
width="660px"
|
||||
style={{ minHeight: '500px' }}
|
||||
visible={this.state.visible}
|
||||
onCancel={this.handleCancel}
|
||||
footer={null}
|
||||
>
|
||||
<CaseReport {...this.reports[this.state.curCaseid]} />
|
||||
</Modal>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default InterfaceColContent
|
@ -30,3 +30,65 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.container {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
}
|
||||
/*
|
||||
* note that styling gu-mirror directly is a bad practice because it's too generic.
|
||||
* you're better off giving the draggable elements a unique class and styling that directly!
|
||||
*/
|
||||
.gu-mirror {
|
||||
padding: 10px;
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
transition: opacity 0.4s ease-in-out;
|
||||
}
|
||||
.container div {
|
||||
cursor: move;
|
||||
cursor: grab;
|
||||
cursor: -moz-grab;
|
||||
cursor: -webkit-grab;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.container div:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.gu-mirror {
|
||||
cursor: grabbing;
|
||||
cursor: -moz-grabbing;
|
||||
cursor: -webkit-grabbing;
|
||||
}
|
||||
.container .ex-moved {
|
||||
background-color: #e74c3c;
|
||||
}
|
||||
.container.ex-over {
|
||||
background-color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
.handle {
|
||||
padding: 0 5px;
|
||||
margin-right: 5px;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
cursor: move;
|
||||
}
|
||||
.report{
|
||||
min-height: 400px;
|
||||
.case-report-pane{
|
||||
margin-top: 10px;
|
||||
}
|
||||
.case-report{
|
||||
margin: 10px;
|
||||
|
||||
.case-report-title{
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
text-align: right;
|
||||
padding-right: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.interface-col{
|
||||
padding: 16px;
|
||||
}
|
||||
|
@ -59,7 +59,8 @@ export default class Run extends Component {
|
||||
bodyForm: req_body_form,
|
||||
bodyOther: req_body_other
|
||||
} = this.postman.state;
|
||||
const res = await axios.post('/api/col/add_case', {
|
||||
|
||||
let params = {
|
||||
interface_id,
|
||||
casename: caseName,
|
||||
col_id: colId,
|
||||
@ -73,7 +74,20 @@ export default class Run extends Component {
|
||||
req_body_type,
|
||||
req_body_form,
|
||||
req_body_other
|
||||
});
|
||||
};
|
||||
|
||||
if(this.postman.state.test_status !== 'error'){
|
||||
params.test_res_body = this.postman.state.res;
|
||||
params.test_report = this.postman.state.validRes;
|
||||
params.test_status = this.postman.state.test_status;
|
||||
params.test_res_header = this.postman.state.resHeader;
|
||||
}
|
||||
|
||||
if(params.test_res_body && typeof params.test_res_body === 'object'){
|
||||
params.test_res_body = JSON.stringify(params.test_res_body, null, ' ');
|
||||
}
|
||||
|
||||
const res = await axios.post('/api/col/add_case', params);
|
||||
if (res.data.errcode) {
|
||||
message.error(res.data.errmsg)
|
||||
} else {
|
||||
@ -85,7 +99,6 @@ export default class Run extends Component {
|
||||
render () {
|
||||
const { currInterface, currProject } = this.props;
|
||||
const data = Object.assign({}, currInterface, currProject, {_id: currInterface._id})
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Postman data={data} type="inter" saveTip="保存到集合" save={() => this.setState({saveCaseModalVisible: true})} ref={this.savePostmanRef} />
|
||||
|
@ -10,7 +10,7 @@ class advMockModel extends baseModel {
|
||||
return {
|
||||
interface_id: { type: Number, required: true },
|
||||
project_id: {type: Number, required: true},
|
||||
enable: {type: Boolean, default: false}, //1表示开启,0关闭
|
||||
enable: {type: Boolean, default: false},
|
||||
mock_script: String,
|
||||
uid: String,
|
||||
up_time: Number
|
||||
@ -25,7 +25,6 @@ class advMockModel extends baseModel {
|
||||
}
|
||||
|
||||
delByInterfaceId(interface_id) {
|
||||
console.log(interface_id);
|
||||
return this.model.deleteOne({
|
||||
interface_id: interface_id
|
||||
});
|
||||
|
@ -1,10 +1,19 @@
|
||||
const controller = require('./controller');
|
||||
const advModel = require('./model.js');
|
||||
const yapi = require('yapi.js');
|
||||
const mongoose = require('mongoose');
|
||||
|
||||
|
||||
module.exports = function(){
|
||||
|
||||
yapi.connect.then(function () {
|
||||
let Col = mongoose.connection.db.collection('adv_mock')
|
||||
Col.ensureIndex({
|
||||
interface_id: 1
|
||||
})
|
||||
Col.ensureIndex({
|
||||
project_id: 1
|
||||
})
|
||||
})
|
||||
this.bindHook('add_router', function(addRouter){
|
||||
addRouter({
|
||||
controller: controller,
|
||||
|
57
npm-shrinkwrap.json
generated
57
npm-shrinkwrap.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "yapi",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@ -3196,6 +3196,22 @@
|
||||
"randombytes": "2.0.5"
|
||||
}
|
||||
},
|
||||
"disposables": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/disposables/-/disposables-1.0.1.tgz",
|
||||
"integrity": "sha1-BkcnoltU9QK9griaot+4358bOeM="
|
||||
},
|
||||
"dnd-core": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-2.5.1.tgz",
|
||||
"integrity": "sha1-F49a1lJs4C3VlQjxFVNfe/wM6U4=",
|
||||
"requires": {
|
||||
"asap": "2.0.6",
|
||||
"invariant": "2.2.2",
|
||||
"lodash": "4.17.4",
|
||||
"redux": "3.7.2"
|
||||
}
|
||||
},
|
||||
"dns-equal": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://repo.corp.qunar.com/artifactory/api/npm/npm-qunar/dns-equal/-/dns-equal-1.0.0.tgz",
|
||||
@ -11438,6 +11454,27 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-dnd": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-2.5.1.tgz",
|
||||
"integrity": "sha1-7O8dnYR4y3av1Zdc1RI+98O+v+U=",
|
||||
"requires": {
|
||||
"disposables": "1.0.1",
|
||||
"dnd-core": "2.5.1",
|
||||
"hoist-non-react-statics": "2.3.1",
|
||||
"invariant": "2.2.2",
|
||||
"lodash": "4.17.4",
|
||||
"prop-types": "15.5.10"
|
||||
}
|
||||
},
|
||||
"react-dnd-html5-backend": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-2.5.1.tgz",
|
||||
"integrity": "sha1-02VuUUsMRpkCpIX/+nX4aE4hx3c=",
|
||||
"requires": {
|
||||
"lodash": "4.17.4"
|
||||
}
|
||||
},
|
||||
"react-dock": {
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://repo.corp.qunar.com/artifactory/api/npm/npm-qunar/react-dock/-/react-dock-0.2.4.tgz",
|
||||
@ -12182,6 +12219,19 @@
|
||||
"slick-carousel": "1.7.1"
|
||||
}
|
||||
},
|
||||
"reactabular-dnd": {
|
||||
"version": "8.9.0",
|
||||
"resolved": "https://registry.npmjs.org/reactabular-dnd/-/reactabular-dnd-8.9.0.tgz",
|
||||
"integrity": "sha1-B5LeSto2H5Qlj4pqGg1svYPSpaA="
|
||||
},
|
||||
"reactabular-table": {
|
||||
"version": "8.9.0",
|
||||
"resolved": "https://registry.npmjs.org/reactabular-table/-/reactabular-table-8.9.0.tgz",
|
||||
"integrity": "sha1-RvbO9jnNm9SyuvHxTiaG+ys14oQ=",
|
||||
"requires": {
|
||||
"classnames": "2.2.5"
|
||||
}
|
||||
},
|
||||
"read-all-stream": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://repo.corp.qunar.com/artifactory/api/npm/npm-qunar/read-all-stream/-/read-all-stream-3.1.0.tgz",
|
||||
@ -13644,6 +13694,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"table-resolver": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/table-resolver/-/table-resolver-3.2.0.tgz",
|
||||
"integrity": "sha512-DQrDHFdJPnvIhyjAcTqF4vhu/Uhp5eNRst9Url9KmBNqxYSMrPXOJoxhU7HPCd3efi1Hua7lMIDnBAphsdhPQw=="
|
||||
},
|
||||
"tapable": {
|
||||
"version": "0.2.8",
|
||||
"resolved": "https://repo.corp.qunar.com/artifactory/api/npm/npm-qunar/tapable/-/tapable-0.2.8.tgz",
|
||||
|
@ -78,10 +78,14 @@
|
||||
"rc-queue-anim": "^1.2.0",
|
||||
"rc-scroll-anim": "^1.0.7",
|
||||
"react": "^15.6.1",
|
||||
"react-dnd": "^2.5.1",
|
||||
"react-dnd-html5-backend": "^2.5.1",
|
||||
"react-dom": "^15.6.1",
|
||||
"react-redux": "^5.0.5",
|
||||
"react-router-dom": "^4.1.1",
|
||||
"react-scripts": "1.0.10",
|
||||
"reactabular-dnd": "^8.9.0",
|
||||
"reactabular-table": "^8.9.0",
|
||||
"redux": "^3.7.1",
|
||||
"redux-promise": "^0.5.3",
|
||||
"redux-thunk": "^2.2.0",
|
||||
@ -89,6 +93,7 @@
|
||||
"sha1": "^1.1.1",
|
||||
"string-replace-webpack-plugin": "^0.1.3",
|
||||
"style-loader": "^0.18.2",
|
||||
"table-resolver": "^3.2.0",
|
||||
"underscore": "^1.8.3",
|
||||
"universal-cookie": "^2.0.8",
|
||||
"url": "^0.11.0",
|
||||
|
@ -5,6 +5,7 @@ const yapi = require('./yapi.js');
|
||||
const commons = require('./utils/commons');
|
||||
yapi.commons = commons;
|
||||
const dbModule = require('./utils/db.js');
|
||||
yapi.connect = dbModule.connect();
|
||||
const mockServer = require('./middleware/mockServer.js');
|
||||
const plugins = require('./plugin.js');
|
||||
const websockify = require('koa-websocket');
|
||||
@ -17,7 +18,7 @@ const router = require('./router.js');
|
||||
|
||||
let indexFile = process.argv[2] === 'dev' ? 'dev.html' : 'index.html';
|
||||
|
||||
yapi.connect = dbModule.connect();
|
||||
|
||||
const app = websockify(new Koa());
|
||||
yapi.app = app;
|
||||
app.use(mockServer);
|
||||
@ -27,6 +28,8 @@ app.use(router.allowedMethods());
|
||||
|
||||
websocket(app);
|
||||
|
||||
|
||||
|
||||
app.use( async (ctx, next) => {
|
||||
if( /^\/(?!api)[a-zA-Z0-9\/\-_]*$/.test(ctx.path) ){
|
||||
ctx.path = "/"
|
||||
|
@ -119,7 +119,7 @@ class interfaceColController extends baseController{
|
||||
if(!id || id == 0){
|
||||
return ctx.body = yapi.commons.resReturn(null, 407, 'col_id不能为空')
|
||||
}
|
||||
let result = await this.caseModel.list(id, 'all');
|
||||
let resultList = await this.caseModel.list(id, 'all');
|
||||
let colData = await this.colModel.get(id);
|
||||
let project = await this.projectModel.getBaseInfo(colData.project_id);
|
||||
|
||||
@ -129,20 +129,27 @@ class interfaceColController extends baseController{
|
||||
}
|
||||
}
|
||||
|
||||
for(let index=0; index< result.length; index++){
|
||||
|
||||
result[index] = result[index].toObject();
|
||||
let interfaceData = await this.interfaceModel.getBaseinfo(result[index].interface_id);
|
||||
if(!interfaceData){
|
||||
await this.caseModel.del(result[index]._id);
|
||||
result[index] = undefined;
|
||||
for(let index=0; index< resultList.length; index++){
|
||||
let result = resultList[index].toObject();
|
||||
let data = await this.interfaceModel.get(result.interface_id);
|
||||
if(!data){
|
||||
await this.caseModel.del(result._id);
|
||||
continue;
|
||||
}
|
||||
let projectData = await this.projectModel.getBaseInfo(interfaceData.project_id);
|
||||
result[index].path = projectData.basepath + interfaceData.path;
|
||||
result[index].method = interfaceData.method;
|
||||
let projectData = await this.projectModel.getBaseInfo(data.project_id);
|
||||
result.path = projectData.basepath + data.path;
|
||||
result.method = data.method;
|
||||
result.req_body_type = data.req_body_type;
|
||||
result.req_headers = data.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)
|
||||
result.req_query = this.handleParamsValue(data.req_query, result.req_query)
|
||||
result.req_params = this.handleParamsValue(data.req_params, result.req_params)
|
||||
resultList[index] = result;
|
||||
}
|
||||
ctx.body = yapi.commons.resReturn(result);
|
||||
ctx.body = yapi.commons.resReturn(resultList);
|
||||
} catch (e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
@ -316,7 +323,7 @@ class interfaceColController extends baseController{
|
||||
return ctx.body = yapi.commons.resReturn(null, 400, '不存在的case');
|
||||
}
|
||||
result = result.toObject();
|
||||
let data = await this.interfaceModel.get(result.interface_id);
|
||||
let data = await this.interfaceModel.get(result.interface_id);
|
||||
if(!data){
|
||||
return ctx.body = yapi.commons.resReturn(null, 400, '找不到对应的接口,请联系管理员')
|
||||
}
|
||||
@ -327,8 +334,7 @@ class interfaceColController extends baseController{
|
||||
result.req_body_type = data.req_body_type;
|
||||
result.req_headers = data.req_headers;
|
||||
result.res_body = data.res_body;
|
||||
result.res_body_type = data.res_body_type;
|
||||
|
||||
result.res_body_type = data.res_body_type;
|
||||
result.req_body_form = this.handleParamsValue(data.req_body_form, result.req_body_form)
|
||||
result.req_query = this.handleParamsValue(data.req_query, result.req_query)
|
||||
result.req_params = this.handleParamsValue(data.req_params, result.req_params)
|
||||
@ -341,6 +347,9 @@ class interfaceColController extends baseController{
|
||||
|
||||
handleParamsValue(params, val){
|
||||
let value = {};
|
||||
try{
|
||||
params = params.toObject();
|
||||
}catch(e){ }
|
||||
if(params.length === 0 || val.length === 0){
|
||||
return params;
|
||||
}
|
||||
@ -349,7 +358,7 @@ class interfaceColController extends baseController{
|
||||
})
|
||||
params.forEach((item, index)=>{
|
||||
if(!value[item.name] || typeof value[item.name] !== 'object') return null;
|
||||
params[index].value = value[item.name].value;
|
||||
params[index].value = value[item.name].value;
|
||||
})
|
||||
return params;
|
||||
}
|
||||
@ -412,9 +421,8 @@ class interfaceColController extends baseController{
|
||||
if(!params || !Array.isArray(params)){
|
||||
ctx.body = yapi.commons.resReturn(null, 400, "请求参数必须是数组")
|
||||
}
|
||||
// let caseName = "";
|
||||
params.forEach((item) => {
|
||||
if(item.id && item.index){
|
||||
if(item.id){
|
||||
this.caseModel.upCaseIndex(item.id, item.index).then((res) => {}, (err) => {
|
||||
yapi.commons.log(err.message, 'error')
|
||||
})
|
||||
@ -422,15 +430,6 @@ class interfaceColController extends baseController{
|
||||
|
||||
});
|
||||
|
||||
// let username = this.getUsername();
|
||||
// yapi.commons.saveLog({
|
||||
// content: `用户 "${username}" 更新了接口集 "${params.col_name}"`,
|
||||
// type: 'project',
|
||||
// uid: this.getUid(),
|
||||
// username: username,
|
||||
// typeid: params.project_id
|
||||
// });
|
||||
|
||||
return ctx.body = yapi.commons.resReturn('成功!')
|
||||
}catch(e){
|
||||
ctx.body = yapi.commons.resReturn(null, 400, e.message)
|
||||
|
164
server/controllers/test.js
Normal file
164
server/controllers/test.js
Normal file
@ -0,0 +1,164 @@
|
||||
const yapi = require('../yapi.js');
|
||||
const baseController = require('./base.js');
|
||||
|
||||
class interfaceColController extends baseController{
|
||||
constructor(ctx) {
|
||||
super(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试 get
|
||||
* @interface /test/get
|
||||
* @method GET
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
async testGet(ctx){
|
||||
try {
|
||||
let query = ctx.query;
|
||||
ctx.body = yapi.commons.resReturn(query);
|
||||
} catch (e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试 post
|
||||
* @interface /test/post
|
||||
* @method POST
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
async testPost(ctx){
|
||||
try{
|
||||
let params = ctx.request.body;
|
||||
ctx.body = yapi.commons.resReturn(params);
|
||||
|
||||
}catch(e){
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试 单文件上传
|
||||
* @interface /test/single/upload
|
||||
* @method POST
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
async testSingleUpload(ctx){
|
||||
try{
|
||||
let params = ctx.request.body;
|
||||
ctx.body = yapi.commons.resReturn({res: '上传成功'});
|
||||
|
||||
}catch(e){
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试 文件上传
|
||||
* @interface /test/files/upload
|
||||
* @method POST
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
async testFilesUpload(ctx){
|
||||
try{
|
||||
let params = ctx.request.body;
|
||||
ctx.body = yapi.commons.resReturn({res: '上传成功'});
|
||||
|
||||
}catch(e){
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试 put
|
||||
* @interface /test/put
|
||||
* @method PUT
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
async testPut(ctx){
|
||||
try{
|
||||
let params = ctx.request.body;
|
||||
ctx.body = yapi.commons.resReturn(params);
|
||||
|
||||
}catch(e){
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试 delete
|
||||
* @interface /test/delete
|
||||
* @method DELETE
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
async testDelete(ctx){
|
||||
try{
|
||||
let params = ctx.request.body;
|
||||
ctx.body = yapi.commons.resReturn(params);
|
||||
|
||||
}catch(e){
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试 head
|
||||
* @interface /test/head
|
||||
* @method HEAD
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
async testHead(ctx){
|
||||
try{
|
||||
let query = ctx.query;
|
||||
ctx.body = yapi.commons.resReturn(query);
|
||||
|
||||
}catch(e){
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试 options
|
||||
* @interface /test/options
|
||||
* @method OPTIONS
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
async testOptions(ctx){
|
||||
try{
|
||||
let query = ctx.query;
|
||||
ctx.body = yapi.commons.resReturn(query);
|
||||
|
||||
}catch(e){
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试 patch
|
||||
* @interface /test/patch
|
||||
* @method PATCH
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*/
|
||||
async testPatch(ctx){
|
||||
try{
|
||||
let params = ctx.request.body;
|
||||
ctx.body = yapi.commons.resReturn(params);
|
||||
|
||||
}catch(e){
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
module.exports = interfaceColController
|
@ -1,5 +1,7 @@
|
||||
const yapi = require('../yapi.js');
|
||||
const baseModel = require('./base.js');
|
||||
var mongoose = require('mongoose');
|
||||
var Schema = mongoose.Schema;
|
||||
|
||||
class interfaceCase extends baseModel {
|
||||
getName() {
|
||||
@ -17,25 +19,21 @@ class interfaceCase extends baseModel {
|
||||
add_time: Number,
|
||||
up_time: Number,
|
||||
case_env: { type: String },
|
||||
// path: { type: String },
|
||||
// method: { type: String },
|
||||
req_params: [{
|
||||
name: String, value: String
|
||||
}],
|
||||
req_query: [{
|
||||
name: String, value: String
|
||||
}],
|
||||
// req_headers: [{
|
||||
// name: String, value: String
|
||||
// }],
|
||||
// req_body_type: {
|
||||
// type: String,
|
||||
// enum: ['form', 'json', 'text', 'xml']
|
||||
// },
|
||||
|
||||
req_body_form: [{
|
||||
name: String, value: String
|
||||
}],
|
||||
req_body_other: String
|
||||
req_body_other: String,
|
||||
test_res_body: String,
|
||||
test_status: {type: String, enum: ['ok', 'invalid', 'error', '']},
|
||||
test_report: [],
|
||||
test_res_header: Schema.Types.Mixed
|
||||
|
||||
};
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ const interfaceController = require('./controllers/interface.js');
|
||||
const groupController = require('./controllers/group.js');
|
||||
const userController = require('./controllers/user.js');
|
||||
const interfaceColController = require('./controllers/interfaceCol.js');
|
||||
const testController = require('./controllers/test.js');
|
||||
|
||||
const yapi = require('./yapi.js');
|
||||
const projectController = require('./controllers/project.js');
|
||||
@ -47,6 +48,10 @@ let INTERFACE_CONFIG = {
|
||||
col: {
|
||||
prefix: '/col/',
|
||||
controller: interfaceColController
|
||||
},
|
||||
test: {
|
||||
prefix: '/test/',
|
||||
controller: testController
|
||||
}
|
||||
};
|
||||
|
||||
@ -347,7 +352,44 @@ let routerConfig = {
|
||||
path: "del_case",
|
||||
method: "get"
|
||||
}
|
||||
]
|
||||
],
|
||||
"test": [{
|
||||
action: "testPost",
|
||||
path: "post",
|
||||
method: "post"
|
||||
}, {
|
||||
action: "testGet",
|
||||
path: "get",
|
||||
method: "get"
|
||||
}, {
|
||||
action: "testPut",
|
||||
path: "put",
|
||||
method: "put"
|
||||
}, {
|
||||
action: "testDelete",
|
||||
path: "delete",
|
||||
method: "del"
|
||||
}, {
|
||||
action: "testHead",
|
||||
path: "head",
|
||||
method: "head"
|
||||
}, {
|
||||
action: "testOptions",
|
||||
path: "options",
|
||||
method: "options"
|
||||
}, {
|
||||
action: "testPatch",
|
||||
path: "patch",
|
||||
method: "patch"
|
||||
}, {
|
||||
action: "testFilesUpload",
|
||||
path: "files/upload",
|
||||
method: "post"
|
||||
}, {
|
||||
action: "testSingleUpload",
|
||||
path: "single/upload",
|
||||
method: "post"
|
||||
}]
|
||||
}
|
||||
|
||||
let pluginsRouterPath = [];
|
||||
|
Loading…
x
Reference in New Issue
Block a user