mirror of
https://github.com/YMFE/yapi.git
synced 2025-03-31 14:50:26 +08:00
feat: pre-script 支持持久化数据存储,兼容浏览器和服务端
This commit is contained in:
parent
708d88a5a8
commit
dedb73bad9
@ -1,4 +1,5 @@
|
||||
import React, { PureComponent as Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
Button,
|
||||
@ -35,6 +36,8 @@ const {
|
||||
checkNameIsExistInArray
|
||||
} = require('common/postmanLib.js');
|
||||
|
||||
const createContext = require('common/createContext')
|
||||
|
||||
const HTTP_METHOD = constants.HTTP_METHOD;
|
||||
const InputGroup = Input.Group;
|
||||
const Option = Select.Option;
|
||||
@ -105,11 +108,21 @@ ParamsNameComponent.propTypes = {
|
||||
name: PropTypes.string
|
||||
};
|
||||
|
||||
@connect(
|
||||
state => {
|
||||
return {
|
||||
curUid: state.user.uid
|
||||
};
|
||||
}
|
||||
)
|
||||
export default class Run extends Component {
|
||||
static propTypes = {
|
||||
data: PropTypes.object, //接口原有数据
|
||||
save: PropTypes.func, //保存回调方法
|
||||
type: PropTypes.string //enum[case, inter], 判断是在接口页面使用还是在测试集
|
||||
type: PropTypes.string, //enum[case, inter], 判断是在接口页面使用还是在测试集
|
||||
curUid: PropTypes.number.isRequired,
|
||||
interfaceId: PropTypes.number.isRequired,
|
||||
projectId: PropTypes.number.isRequired
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
@ -300,7 +313,12 @@ export default class Run extends Component {
|
||||
result;
|
||||
|
||||
try {
|
||||
result = await crossRequest(options, this.state.pre_script, this.state.after_script);
|
||||
options.taskId = this.props.curUid;
|
||||
result = await crossRequest(options, this.state.pre_script, this.state.after_script, createContext(
|
||||
this.props.curUid,
|
||||
this.props.projectId,
|
||||
this.props.interfaceId
|
||||
));
|
||||
result = {
|
||||
header: result.res.header,
|
||||
body: result.res.body,
|
||||
|
@ -221,6 +221,8 @@ export default class InterfaceCaseContent extends Component {
|
||||
saveTip="更新保存修改"
|
||||
save={this.updateCase}
|
||||
ref={this.savePostmanRef}
|
||||
interfaceId={currCase.interface_id}
|
||||
projectId={currCase.project_id}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
@ -36,6 +36,7 @@ import CaseEnv from 'client/components/CaseEnv';
|
||||
import Label from '../../../../components/Label/Label.js';
|
||||
|
||||
const Option = Select.Option;
|
||||
const createContext = require('common/createContext')
|
||||
|
||||
import copy from 'copy-to-clipboard';
|
||||
|
||||
@ -64,7 +65,8 @@ function handleReport(json) {
|
||||
token: state.project.token,
|
||||
envList: state.interfaceCol.envList,
|
||||
curProjectRole: state.project.currProject.role,
|
||||
projectEnv: state.project.projectEnv
|
||||
projectEnv: state.project.projectEnv,
|
||||
curUid: state.user.uid
|
||||
};
|
||||
},
|
||||
{
|
||||
@ -98,7 +100,8 @@ class InterfaceColContent extends Component {
|
||||
getEnv: PropTypes.func,
|
||||
projectEnv: PropTypes.object,
|
||||
fetchCaseEnvList: PropTypes.func,
|
||||
envList: PropTypes.array
|
||||
envList: PropTypes.array,
|
||||
curUid: PropTypes.number
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
@ -309,7 +312,12 @@ class InterfaceColContent extends Component {
|
||||
};
|
||||
|
||||
try {
|
||||
let data = await crossRequest(options, interfaceData.pre_script, interfaceData.after_script);
|
||||
let data = await crossRequest(options, interfaceData.pre_script, interfaceData.after_script, createContext(
|
||||
this.props.curUid,
|
||||
this.props.match.params.id,
|
||||
interfaceData.interface_id
|
||||
));
|
||||
options.taskId = this.props.curUid;
|
||||
let res = (data.res.body = json_parse(data.res.body));
|
||||
result = {
|
||||
...options,
|
||||
|
@ -95,6 +95,8 @@ export default class Run extends Component {
|
||||
saveTip="保存到集合"
|
||||
save={() => this.setState({ saveCaseModalVisible: true })}
|
||||
ref={this.savePostmanRef}
|
||||
interfaceId={currInterface._id}
|
||||
projectId={currInterface.project_id}
|
||||
/>
|
||||
<AddColModal
|
||||
visible={this.state.saveCaseModalVisible}
|
||||
|
14
common/createContext.js
Normal file
14
common/createContext.js
Normal file
@ -0,0 +1,14 @@
|
||||
module.exports = function (uid, projectId,interfaceId) {
|
||||
if(!uid || !projectId || !interfaceId){
|
||||
console.error('uid projectId interfaceId 不能为空', uid, projectId,interfaceId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一转换为number
|
||||
*/
|
||||
return {
|
||||
uid: +uid,
|
||||
projectId: +projectId,
|
||||
interfaceId: +interfaceId
|
||||
}
|
||||
}
|
@ -20,6 +20,32 @@ const ContentTypeMap = {
|
||||
other: 'text'
|
||||
};
|
||||
|
||||
const getStorage = async (id)=>{
|
||||
try{
|
||||
if(isNode){
|
||||
let storage = global.storageCreator(id);
|
||||
let data = await storage.getItem();
|
||||
return {
|
||||
getItem: (name)=> data[name],
|
||||
setItem: (name, value)=>{
|
||||
data[name] = value;
|
||||
storage.setItem(name, value)
|
||||
}
|
||||
}
|
||||
}else{
|
||||
return {
|
||||
getItem: (name)=> window.localStorage.getItem(name),
|
||||
setItem: (name, value)=> window.localStorage.setItem(name, value)
|
||||
}
|
||||
}
|
||||
}catch(e){
|
||||
console.error(e)
|
||||
return (...args)=>{
|
||||
console.error(...args)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function httpRequestByNode(options) {
|
||||
function handleRes(response) {
|
||||
if (!response || typeof response !== 'object') {
|
||||
@ -207,12 +233,21 @@ function sandboxByBrowser(context = {}, script) {
|
||||
return context;
|
||||
}
|
||||
|
||||
async function crossRequest(defaultOptions, preScript, afterScript) {
|
||||
/**
|
||||
*
|
||||
* @param {*} defaultOptions
|
||||
* @param {*} preScript
|
||||
* @param {*} afterScript
|
||||
* @param {*} commonContext 负责传递一些业务信息,crossRequest 不关注具体传什么,只负责当中间人
|
||||
*/
|
||||
async function crossRequest(defaultOptions, preScript, afterScript, commonContext = {}) {
|
||||
let options = Object.assign({}, defaultOptions);
|
||||
const taskId = options.taskId || Math.random() + '';
|
||||
let urlObj = URL.parse(options.url, true),
|
||||
query = {};
|
||||
query = Object.assign(query, urlObj.query);
|
||||
let context = {
|
||||
...commonContext,
|
||||
get href() {
|
||||
return urlObj.href;
|
||||
},
|
||||
@ -239,7 +274,8 @@ async function crossRequest(defaultOptions, preScript, afterScript) {
|
||||
query: query,
|
||||
requestHeader: options.headers || {},
|
||||
requestBody: options.data,
|
||||
promise: false
|
||||
promise: false,
|
||||
storage: await getStorage(taskId)
|
||||
};
|
||||
|
||||
context.utils = Object.freeze({
|
||||
@ -395,7 +431,7 @@ function handleParams(interfaceData, handleValue, requestParams) {
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('err', e);
|
||||
console.error('err', e);
|
||||
}
|
||||
|
||||
if (HTTP_METHOD[interfaceRunData.method].request_body) {
|
||||
|
@ -10,6 +10,7 @@ const mockServer = require('./middleware/mockServer.js');
|
||||
const plugins = require('./plugin.js');
|
||||
const websockify = require('koa-websocket');
|
||||
const websocket = require('./websocket.js');
|
||||
const storageCreator = require('./utils/storage')
|
||||
|
||||
const Koa = require('koa');
|
||||
const koaStatic = require('koa-static');
|
||||
@ -17,6 +18,7 @@ const koaStatic = require('koa-static');
|
||||
const koaBody = require('koa-body');
|
||||
const router = require('./router.js');
|
||||
|
||||
global.storageCreator = storageCreator;
|
||||
let indexFile = process.argv[2] === 'dev' ? 'dev.html' : 'index.html';
|
||||
|
||||
const app = websockify(new Koa());
|
||||
|
@ -18,6 +18,7 @@ const renderToHtml = require('../utils/reportHtml');
|
||||
const axios = require('axios');
|
||||
const HanldeImportData = require('../../common/HandleImportData');
|
||||
const _ = require('underscore');
|
||||
const createContex = require('../../common/createContext')
|
||||
|
||||
/**
|
||||
* {
|
||||
@ -307,7 +308,12 @@ class openController extends baseController {
|
||||
validRes: []
|
||||
};
|
||||
try {
|
||||
let data = await crossRequest(options, interfaceData.pre_script, interfaceData.after_script);
|
||||
options.taskId = this.getUid();
|
||||
let data = await crossRequest(options, interfaceData.pre_script, interfaceData.after_script,createContex(
|
||||
this.getUid(),
|
||||
interfaceData.project_id,
|
||||
interfaceData.interface_id
|
||||
));
|
||||
let res = data.res;
|
||||
|
||||
result = Object.assign(result, {
|
||||
|
70
server/models/storage.js
Normal file
70
server/models/storage.js
Normal file
@ -0,0 +1,70 @@
|
||||
const baseModel = require('./base.js');
|
||||
const mongoose = require('mongoose');
|
||||
|
||||
class stroageModel extends baseModel {
|
||||
constructor() {
|
||||
super()
|
||||
let storageCol = mongoose.connection.db.collection('storage');
|
||||
storageCol.createIndex(
|
||||
{
|
||||
key: 1
|
||||
},
|
||||
{
|
||||
unique: true
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
getName() {
|
||||
return 'storage';
|
||||
}
|
||||
|
||||
getSchema() {
|
||||
return {
|
||||
key: { type: Number, required: true },
|
||||
data: {
|
||||
type: String,
|
||||
default: ''
|
||||
} //用于原始数据存储
|
||||
};
|
||||
}
|
||||
save(key, data = {}, isInsert = false) {
|
||||
|
||||
let saveData = {
|
||||
key,
|
||||
data: JSON.stringify(data, null, 2)
|
||||
};
|
||||
if(isInsert){
|
||||
let r = new this.model(saveData);
|
||||
return r.save();
|
||||
}
|
||||
return this.model.updateOne({
|
||||
key
|
||||
}, saveData)
|
||||
}
|
||||
|
||||
del(key) {
|
||||
return this.model.remove({
|
||||
key
|
||||
});
|
||||
}
|
||||
|
||||
get(key) {
|
||||
return this.model
|
||||
.findOne({
|
||||
key
|
||||
})
|
||||
.exec().then(data => {
|
||||
this.save(key, {})
|
||||
if (!data) return null;
|
||||
data = data.toObject().data;
|
||||
try {
|
||||
return JSON.parse(data)
|
||||
} catch (e) {
|
||||
return {}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = stroageModel;
|
@ -521,33 +521,10 @@ function convertString(variable) {
|
||||
}
|
||||
}
|
||||
|
||||
const _cache = {}
|
||||
|
||||
function creater(key){
|
||||
const _storage ={
|
||||
getItem(key){
|
||||
return _storage[key]
|
||||
},
|
||||
setItem(key, value){
|
||||
_storage[key] = value;
|
||||
}
|
||||
}
|
||||
if(!_cache[key]){
|
||||
_cache[key] = _storage;
|
||||
|
||||
setTimeout(()=>{
|
||||
delete _cache[key];
|
||||
}, 1000* 3600) //一个小时后释放掉,防止长时间调用内存导致内存占用过大
|
||||
return _storage;
|
||||
}else{
|
||||
return _cache[key]
|
||||
}
|
||||
}
|
||||
|
||||
exports.runCaseScript = async function runCaseScript(params, colId, interfaceId, userId) {
|
||||
exports.runCaseScript = async function runCaseScript(params, colId, interfaceId) {
|
||||
const colInst = yapi.getInst(interfaceColModel);
|
||||
let colData = await colInst.get(colId);
|
||||
let localStorage = creater(colId + '/' +userId);
|
||||
const logs = [];
|
||||
const context = {
|
||||
assert: require('assert'),
|
||||
@ -556,7 +533,6 @@ exports.runCaseScript = async function runCaseScript(params, colId, interfaceId,
|
||||
header: params.response.header,
|
||||
records: params.records,
|
||||
params: params.params,
|
||||
localStorage,
|
||||
log: msg => {
|
||||
logs.push('log: ' + convertString(msg));
|
||||
}
|
||||
|
27
server/utils/storage.js
Normal file
27
server/utils/storage.js
Normal file
@ -0,0 +1,27 @@
|
||||
module.exports = function storageCreator(id) {
|
||||
const storageModel = require('../models/storage.js');
|
||||
const yapi = require('../yapi.js');
|
||||
const defaultData = {}
|
||||
return {
|
||||
getItem: async (name = '') => {
|
||||
let inst = yapi.getInst(storageModel);
|
||||
let data = await inst.get(id);
|
||||
data = data || defaultData;
|
||||
if (name) return data[name];
|
||||
return data;
|
||||
},
|
||||
setItem: async (name, value) => {
|
||||
let inst = yapi.getInst(storageModel);
|
||||
let data = await inst.get(id) || defaultData;
|
||||
let result;
|
||||
data[name] = value;
|
||||
if(!data){
|
||||
result = await inst.save(id, data, true)
|
||||
}else{
|
||||
result = await inst.save(id, data, false)
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user