Merge pull request #726 from johnfwtest/master

feat:  export data as swagger json #714 #724
This commit is contained in:
sean1025 2018-12-30 12:31:02 +08:00 committed by GitHub
commit 4512765f23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 317 additions and 0 deletions

View File

@ -11,6 +11,8 @@ module.exports = {
name: 'statistics'
},{
name: 'export-data'
},{
name: 'export-swagger2-data'
},{
name: 'import-yapi-json'
},{

View File

@ -0,0 +1,11 @@
function exportData(exportDataModule, pid) {
exportDataModule.swaggerjson = {
name: 'swaggerjson',
route: `/api/plugin/exportSwagger?type=OpenAPIV2&pid=${pid}`,
desc: '导出项目接口文档为(Swagger 2.0)Json文件'
};
}
module.exports = function() {
this.bindHook('export_data', exportData);
};

View File

@ -0,0 +1,288 @@
const baseController = require('controllers/base.js');
const interfaceModel = require('models/interface.js');
const projectModel = require('models/project.js');
const interfaceCatModel = require('models/interfaceCat.js');
const yapi = require('yapi.js');
class exportSwaggerController extends baseController {
constructor(ctx) {
super(ctx);
this.catModel = yapi.getInst(interfaceCatModel);
this.interModel = yapi.getInst(interfaceModel);
this.projectModel = yapi.getInst(projectModel);
}
/*
handleListClass,handleExistId is same as the exportController(yapi-plugin-export-data).
No DRY,but i have no idea to optimize it.
*/
async handleListClass(pid, status) {
let result = await this.catModel.list(pid),
newResult = [];
for (let i = 0, item, list; i < result.length; i++) {
item = result[i].toObject();
list = await this.interModel.listByInterStatus(item._id, status);
list = list.sort((a, b) => {
return a.index - b.index;
});
if (list.length > 0) {
item.list = list;
newResult.push(item);
}
}
return newResult;
}
handleExistId(data) {
function delArrId(arr, fn) {
if (!Array.isArray(arr)) return;
arr.forEach(item => {
delete item._id;
delete item.__v;
delete item.uid;
delete item.edit_uid;
delete item.catid;
delete item.project_id;
if (typeof fn === 'function') fn(item);
});
}
delArrId(data, function (item) {
delArrId(item.list, function (api) {
delArrId(api.req_body_form);
delArrId(api.req_params);
delArrId(api.req_query);
delArrId(api.req_headers);
if (api.query_path && typeof api.query_path === 'object') {
delArrId(api.query_path.params);
}
});
});
return data;
}
async exportData(ctx) {
let pid = ctx.request.query.pid;
let type = ctx.request.query.type;
let status = ctx.request.query.status;
if (!pid) {
ctx.body = yapi.commons.resReturn(null, 200, 'pid 不为空');
}
let curProject;
let tp = '';
try {
curProject = await this.projectModel.get(pid);
ctx.set('Content-Type', 'application/octet-stream');
const list = await this.handleListClass(pid, status);
switch (type) {
case 'OpenAPIV2':
{ //in this time, only implemented OpenAPI V2.0
let data = this.handleExistId(list);
let model = await convertToSwaggerV2Model(data);
tp = JSON.stringify(model, null, 2);
ctx.set('Content-Disposition', `attachment; filename=swaggerApi.json`);
return (ctx.body = tp);
}
default:
{
ctx.body = yapi.commons.resReturn(null, 400, 'type 无效参数')
}
}
} catch (error) {
yapi.commons.log(error, 'error');
ctx.body = yapi.commons.resReturn(null, 502, '下载出错');
}
//Convert to SwaggerV2.0 (OpenAPI 2.0)
async function convertToSwaggerV2Model(list) {
const swaggerObj = {
swagger: '2.0',
info: {
title: curProject.name,
version: 'last', // last version
description: curProject.desc
},
//host: "", // No find any info of host in this point :-)
basePath: curProject.basepath ? curProject.basepath : '/', //default base path is '/'(root)
tags: (() => {
let tagArray = [];
list.forEach(t => {
tagArray.push({
name: t.name,
description: t.desc
/*externalDocs:{
descroption:"",
url:""
} */
});
});
return tagArray;
})(),
schemes: [
"http" //Only http
],
paths: (() => {
let apisObj = {};
for (let aptTag of list) { //list of category
for (let api of aptTag.list) //list of api
{
if (apisObj[api.path] == null) {
apisObj[api.path] = {};
}
apisObj[api.path][api.method.toLowerCase()] = (() => {
let apiItem = {};
apiItem['tags'] = [aptTag.name];
apiItem['summary'] = api.title;
apiItem['description'] = api.markdown;
switch (api.req_body_type) {
case 'form':
case 'file':
apiItem['consumes'] = ['multipart/form-data']; //form data required
break;
case 'json':
apiItem['consumes'] = ['application/json'];
break;
case 'raw':
apiItem['consumes'] = ['text/plain'];
break;
default:
break;
}
apiItem['parameters'] = (() => {
let paramArray = [];
for (let p of api.req_headers) //Headers parameters
{
//swagger has consumes proprety, so skip proprety "Content-Type"
if (p.name === 'Content-Type') {
continue;
}
paramArray.push({
name: p.name,
in: 'header',
description: `${p.name} (Only:${p.value})`,
required: p.required === 1,
type: 'string', //always be type string
default: p.value
});
}
for (let p of api.req_params) //Path parameters
{
paramArray.push({
name: p.name,
in: 'path',
description: p.desc,
required: true, //swagger path parameters required proprety must be always true,
type: 'string' //always be type string
});
}
for (let p of api.req_query) //Query parameters
{
paramArray.push({
name: p.name,
in: 'query',
required: p.required === 1,
description: p.desc,
type: 'string' //always be type string
});
}
switch (api.req_body_type) //Body parameters
{
case 'form':
{
for (let p of api.req_body_form) {
paramArray.push({
name: p.name,
in: 'formData',
required: p.required === 1,
description: p.desc,
type: p.type === 'text' ? 'string' : 'file' //in this time .formData type have only text or file
});
}
break;
}
case 'json':
{
if (api.req_body_other) {
let jsonParam = JSON.parse(api.req_body_other);
if (jsonParam) {
paramArray.push({
name: 'root',
in: 'body',
description: jsonParam.description,
schema: jsonParam //as same as swagger's format
});
}
}
break;
}
case 'file':
{
paramArray.push({
name: 'upfile',
in: 'formData', //use formData
description: api.req_body_other,
type: 'file'
});
break;
}
case 'raw':
{
paramArray.push({
name: 'raw',
in: 'body',
description: 'raw paramter',
schema: {
type: 'string',
format: 'binary',
default: api.req_body_other
}
});
break;
}
default:
break;
}
return paramArray;
})();
apiItem['responses'] = {
'200': {
description: 'successful operation',
schema: (() => {
let schemaObj = {};
if (api.res_body_type === 'raw') {
schemaObj['type'] = 'string';
schemaObj['format'] = 'binary';
schemaObj['default'] = api.res_body;
} else if (api.res_body_type === 'json') {
if (api.res_body) {
let resBody = JSON.parse(api.res_body);
if (resBody !== null) {
//schemaObj['type']=resBody.type;
schemaObj = resBody; //as the parameters,
}
}
}
return schemaObj;
})()
}
};
return apiItem;
})();
}
}
return apisObj;
})()
};
return swaggerObj;
}
}
}
module.exports = exportSwaggerController;

View File

@ -0,0 +1,4 @@
module.exports = {
server: true,
client: true
}

View File

@ -0,0 +1,12 @@
const exportSwaggerController = require('./controller');
module.exports = function(){
this.bindHook('add_router', function(addRouter){
addRouter({
controller: exportSwaggerController,
method: 'get',
path: 'exportSwagger',
action: 'exportData'
})
})
}