mock平台

open.js 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. const projectModel = require('../models/project.js');
  2. const interfaceColModel = require('../models/interfaceCol.js');
  3. const interfaceCaseModel = require('../models/interfaceCase.js');
  4. const interfaceModel = require('../models/interface.js');
  5. const interfaceCatModel = require('../models/interfaceCat.js');
  6. const followModel = require('../models/follow.js');
  7. const userModel = require('../models/user.js');
  8. const yapi = require('../yapi.js');
  9. const baseController = require('./base.js');
  10. const {
  11. handleParams,
  12. crossRequest,
  13. handleCurrDomain,
  14. checkNameIsExistInArray
  15. } = require('../../common/postmanLib');
  16. const { handleParamsValue, ArrayToObject } = require('../../common/utils.js');
  17. const renderToHtml = require('../utils/reportHtml');
  18. const axios = require('axios');
  19. const HanldeImportData = require('../../common/HandleImportData');
  20. const _ = require('underscore');
  21. const createContex = require('../../common/createContext')
  22. /**
  23. * {
  24. * postman: require('./m')
  25. * }
  26. */
  27. const importDataModule = {};
  28. yapi.emitHook('import_data', importDataModule);
  29. class openController extends baseController {
  30. constructor(ctx) {
  31. super(ctx);
  32. this.projectModel = yapi.getInst(projectModel);
  33. this.interfaceColModel = yapi.getInst(interfaceColModel);
  34. this.interfaceCaseModel = yapi.getInst(interfaceCaseModel);
  35. this.interfaceModel = yapi.getInst(interfaceModel);
  36. this.interfaceCatModel = yapi.getInst(interfaceCatModel);
  37. this.followModel = yapi.getInst(followModel);
  38. this.userModel = yapi.getInst(userModel);
  39. this.handleValue = this.handleValue.bind(this);
  40. this.schemaMap = {
  41. runAutoTest: {
  42. '*id': 'number',
  43. project_id: 'string',
  44. token: 'string',
  45. mode: {
  46. type: 'string',
  47. default: 'html'
  48. },
  49. email: {
  50. type: 'boolean',
  51. default: false
  52. },
  53. download: {
  54. type: 'boolean',
  55. default: false
  56. },
  57. closeRemoveAdditional: true
  58. },
  59. importData: {
  60. '*type': 'string',
  61. url: 'string',
  62. '*token': 'string',
  63. json: 'string',
  64. project_id: 'string',
  65. merge: {
  66. type: 'string',
  67. default: 'normal'
  68. }
  69. }
  70. };
  71. }
  72. async importData(ctx) {
  73. let type = ctx.params.type;
  74. let content = ctx.params.json;
  75. let project_id = ctx.params.project_id;
  76. let dataSync = ctx.params.merge;
  77. let warnMessage = ''
  78. /**
  79. * 因为以前接口文档写错了,做下兼容
  80. */
  81. try{
  82. if(!dataSync &&ctx.params.dataSync){
  83. warnMessage = 'importData Api 已废弃 dataSync 传参,请联系管理员将 dataSync 改为 merge.'
  84. dataSync = ctx.params.dataSync
  85. }
  86. }catch(e){}
  87. let token = ctx.params.token;
  88. if (!type || !importDataModule[type]) {
  89. return (ctx.body = yapi.commons.resReturn(null, 40022, '不存在的导入方式'));
  90. }
  91. if (!content && !ctx.params.url) {
  92. return (ctx.body = yapi.commons.resReturn(null, 40022, 'json 或者 url 参数,不能都为空'));
  93. }
  94. try {
  95. let request = require("request");// let Promise = require('Promise');
  96. let syncGet = function (url){
  97. return new Promise(function(resolve, reject){
  98. request.get({url : url}, function(error, response, body){
  99. if(error){
  100. reject(error);
  101. }else{
  102. resolve(body);
  103. }
  104. });
  105. });
  106. }
  107. if(ctx.params.url){
  108. content = await syncGet(ctx.params.url);
  109. }else if(content.indexOf('http://') === 0 || content.indexOf('https://') === 0){
  110. content = await syncGet(content);
  111. }
  112. content = JSON.parse(content);
  113. } catch (e) {
  114. return (ctx.body = yapi.commons.resReturn(null, 40022, 'json 格式有误:' + e));
  115. }
  116. let menuList = await this.interfaceCatModel.list(project_id);
  117. let selectCatid = menuList[0]._id;
  118. let projectData = await this.projectModel.get(project_id);
  119. let res = await importDataModule[type](content);
  120. let successMessage;
  121. let errorMessage = [];
  122. await HanldeImportData(
  123. res,
  124. project_id,
  125. selectCatid,
  126. menuList,
  127. projectData.basePath,
  128. dataSync,
  129. err => {
  130. errorMessage.push(err);
  131. },
  132. msg => {
  133. successMessage = msg;
  134. },
  135. () => {},
  136. token,
  137. yapi.WEBCONFIG.port
  138. );
  139. if (errorMessage.length > 0) {
  140. return (ctx.body = yapi.commons.resReturn(null, 404, errorMessage.join('\n')));
  141. }
  142. ctx.body = yapi.commons.resReturn(null, 0, successMessage + warnMessage);
  143. }
  144. async projectInterfaceData(ctx) {
  145. ctx.body = 'projectInterfaceData';
  146. }
  147. handleValue(val, global) {
  148. let globalValue = ArrayToObject(global);
  149. let context = Object.assign({}, {global: globalValue}, this.records);
  150. return handleParamsValue(val, context);
  151. }
  152. handleEvnParams(params) {
  153. let result = [];
  154. Object.keys(params).map(item => {
  155. if (/env_/gi.test(item)) {
  156. let curEnv = yapi.commons.trim(params[item]);
  157. let value = { curEnv, project_id: item.split('_')[1] };
  158. result.push(value);
  159. }
  160. });
  161. return result;
  162. }
  163. async runAutoTest(ctx) {
  164. if (!this.$tokenAuth) {
  165. return (ctx.body = yapi.commons.resReturn(null, 40022, 'token 验证失败'));
  166. }
  167. // console.log(1231312)
  168. const token = ctx.query.token;
  169. const projectId = ctx.params.project_id;
  170. const startTime = new Date().getTime();
  171. const records = (this.records = {});
  172. const reports = (this.reports = {});
  173. const testList = [];
  174. let id = ctx.params.id;
  175. let curEnvList = this.handleEvnParams(ctx.params);
  176. let colData = await this.interfaceColModel.get(id);
  177. if (!colData) {
  178. return (ctx.body = yapi.commons.resReturn(null, 40022, 'id值不存在'));
  179. }
  180. let projectData = await this.projectModel.get(projectId);
  181. let caseList = await yapi.commons.getCaseList(id);
  182. if (caseList.errcode !== 0) {
  183. ctx.body = caseList;
  184. }
  185. caseList = caseList.data;
  186. for (let i = 0, l = caseList.length; i < l; i++) {
  187. let item = caseList[i];
  188. let projectEvn = await this.projectModel.getByEnv(item.project_id);
  189. item.id = item._id;
  190. let curEnvItem = _.find(curEnvList, key => {
  191. return key.project_id == item.project_id;
  192. });
  193. item.case_env = curEnvItem ? curEnvItem.curEnv || item.case_env : item.case_env;
  194. item.req_headers = this.handleReqHeader(item.req_headers, projectEvn.env, item.case_env);
  195. item.pre_script = projectData.pre_script;
  196. item.after_script = projectData.after_script;
  197. item.env = projectEvn.env;
  198. let result;
  199. // console.log('item',item.case_env)
  200. try {
  201. result = await this.handleTest(item);
  202. } catch (err) {
  203. result = err;
  204. }
  205. reports[item.id] = result;
  206. records[item.id] = {
  207. params: result.params,
  208. body: result.res_body
  209. };
  210. testList.push(result);
  211. }
  212. function getMessage(testList) {
  213. let successNum = 0,
  214. failedNum = 0,
  215. len = 0,
  216. msg = '';
  217. testList.forEach(item => {
  218. len++;
  219. if (item.code === 0) {
  220. successNum++;
  221. }
  222. else {
  223. failedNum++;
  224. }
  225. });
  226. if (failedNum === 0) {
  227. msg = `一共 ${len} 测试用例,全部验证通过`;
  228. } else {
  229. msg = `一共 ${len} 测试用例,${successNum} 个验证通过, ${failedNum} 个未通过。`;
  230. }
  231. return { msg, len, successNum, failedNum };
  232. }
  233. const endTime = new Date().getTime();
  234. const executionTime = (endTime - startTime) / 1000;
  235. let reportsResult = {
  236. message: getMessage(testList),
  237. runTime: executionTime + 's',
  238. numbs: testList.length,
  239. list: testList
  240. };
  241. if (ctx.params.email === true && reportsResult.message.failedNum !== 0) {
  242. let autoTestUrl = `${
  243. ctx.request.origin
  244. }/api/open/run_auto_test?id=${id}&token=${token}&mode=${ctx.params.mode}`;
  245. yapi.commons.sendNotice(projectId, {
  246. title: `YApi自动化测试报告`,
  247. content: `
  248. <html>
  249. <head>
  250. <title>测试报告</title>
  251. <meta charset="utf-8" />
  252. <body>
  253. <div>
  254. <h3>测试结果:</h3>
  255. <p>${reportsResult.message.msg}</p>
  256. <h3>测试结果详情如下:</h3>
  257. <p>${autoTestUrl}</p>
  258. </div>
  259. </body>
  260. </html>`
  261. });
  262. }
  263. let mode = ctx.params.mode || 'html';
  264. if(ctx.params.download === true) {
  265. ctx.set('Content-Disposition', `attachment; filename=test.${mode}`);
  266. }
  267. if (ctx.params.mode === 'json') {
  268. return (ctx.body = reportsResult);
  269. } else {
  270. return (ctx.body = renderToHtml(reportsResult));
  271. }
  272. }
  273. async handleTest(interfaceData) {
  274. let requestParams = {};
  275. let options;
  276. options = handleParams(interfaceData, this.handleValue, requestParams);
  277. let result = {
  278. id: interfaceData.id,
  279. name: interfaceData.casename,
  280. path: interfaceData.path,
  281. code: 400,
  282. validRes: []
  283. };
  284. try {
  285. options.taskId = this.getUid();
  286. let data = await crossRequest(options, interfaceData.pre_script, interfaceData.after_script,createContex(
  287. this.getUid(),
  288. interfaceData.project_id,
  289. interfaceData.interface_id
  290. ));
  291. let res = data.res;
  292. result = Object.assign(result, {
  293. status: res.status,
  294. statusText: res.statusText,
  295. url: data.req.url,
  296. method: data.req.method,
  297. data: data.req.data,
  298. headers: data.req.headers,
  299. res_header: res.header,
  300. res_body: res.body
  301. });
  302. if (options.data && typeof options.data === 'object') {
  303. requestParams = Object.assign(requestParams, options.data);
  304. }
  305. let validRes = [];
  306. let responseData = Object.assign(
  307. {},
  308. {
  309. status: res.status,
  310. body: res.body,
  311. header: res.header,
  312. statusText: res.statusText
  313. }
  314. );
  315. await this.handleScriptTest(interfaceData, responseData, validRes, requestParams);
  316. result.params = requestParams;
  317. if (validRes.length === 0) {
  318. result.code = 0;
  319. result.validRes = [{ message: '验证通过' }];
  320. } else if (validRes.length > 0) {
  321. result.code = 1;
  322. result.validRes = validRes;
  323. }
  324. } catch (data) {
  325. result = Object.assign(options, result, {
  326. res_header: data.header,
  327. res_body: data.body || data.message,
  328. status: null,
  329. statusText: data.message,
  330. code: 400
  331. });
  332. }
  333. return result;
  334. }
  335. async handleScriptTest(interfaceData, response, validRes, requestParams) {
  336. try {
  337. let test = await yapi.commons.runCaseScript({
  338. response: response,
  339. records: this.records,
  340. script: interfaceData.test_script,
  341. params: requestParams
  342. }, interfaceData.col_id, interfaceData.interface_id, this.getUid());
  343. if (test.errcode !== 0) {
  344. test.data.logs.forEach(item => {
  345. validRes.push({
  346. message: item
  347. });
  348. });
  349. }
  350. } catch (err) {
  351. validRes.push({
  352. message: 'Error: ' + err.message
  353. });
  354. }
  355. }
  356. handleReqHeader(req_header, envData, curEnvName) {
  357. let currDomain = handleCurrDomain(envData, curEnvName);
  358. let header = currDomain.header;
  359. header.forEach(item => {
  360. if (!checkNameIsExistInArray(item.name, req_header)) {
  361. item.abled = true;
  362. req_header.push(item);
  363. }
  364. });
  365. req_header = req_header.filter(item => {
  366. return item && typeof item === 'object';
  367. });
  368. return req_header;
  369. }
  370. }
  371. module.exports = openController;