前言
当前公司开发,后端通过swagger提供接口文档,开发某些模块时,一下子可能生成二三十个接口,前端使用的时候,需要一个一个copy到对应的api文件中,定义类型,引入接口等等,实在是麻烦!于是花了点时间,搞了下自动导出
github项目地址,欢迎提出问题,求个star😁
思路
-
通过 swagger-parser这个库,解析swagger文档地址,下面是swagger内容的一些说明,可参考
// swagger文档 // info:API的基本信息,包括标题、版本号、描述等。 // host:API的主机名和端口号。 // basePath:API的基本路径,通常是API的前缀。 // schemes:API的协议,通常是http或https。 // tags:API的标签,可以用于对API进行分类。 // paths:API的路径和操作,包括HTTP方法、请求参数、响应内容等。 // operationId: 用于指定操作的唯一标识符。 // summary: 用于提供操作的简要说明。 // description: 用于提供操作的详细说明。 // tags: 用于对操作进行分类和组织。 // parameters: 用于定义在操作中使用的参数,例如查询参数,请求体参数等。 // requestBody: 用于定义操作的请求体。 // responses: 用于定义操作的响应,包括响应代码和响应消息。 // security: 用于指定操作所需的安全方案。 // deprecated: 用于指定操作是否已被弃用。 // parameters:API的公共参数,可以在多个API中共用。 // responses:API的响应定义,包括响应码、响应内容等。 // securityDefinitions:API的安全定义,包括认证方式、访问授权等。 // security:API的安全配置,指定哪些API需要认证或授权。
-
获取解析中需要的信息,如接口类型、标题、描述、地址、参数等等
-
遍历所有接口,根据配置的规则,生成内容,写入文件
核心代码
const fs = require("fs");
const path = require("path");
const chalk = require("chalk");
const beautify = require("js-beautify").js_beautify;
const parse = require("swagger-parser");
const { swaggerUrl, topConfig, apiConfig,dirIdx } = require("./config");
const demoData = require("./demo.json");
// demo or swaggerUrl
let demo = process.env.rundemo;
if (demo) {
console.log(
"========================================测试demo========================================"
);
} else {
console.log(
"========================================swaggerUrl========================================"
);
}
// api接口方法存放目录
const API_PATH = path.resolve(__dirname, "../api");
// 判断目录是否存在
const isExist = (lastPath = "") => {
const privatePath = `${lastPath ? API_PATH + "/" + lastPath : API_PATH}`;
const stat = fs.existsSync(privatePath);
if (!stat) {
fs.mkdirSync(privatePath);
}
};
// 清空文件夹
const rmDir = (dir) => {
if (fs.existsSync(dir)) {
const files = fs.readdirSync(dir);
if (files.length > 0) {
fs.rmdirSync(dir, { recursive: true });
fs.mkdirSync(dir);
}
}
};
// 接口名称处理 getColumnInfosUsingGET-->getColumnInfos saveProjectUsingPOST-->saveProject
const moduleName = (operationId) => {
return operationId.replace(/Using(POST|GET)/g, "");
};
// 数据类型
const dataType = (key) => {
const type = {
string: "string",
integer: "number",
int: "number",
long: "string",
Array: "array",
file: "Blob",
boolean: "boolean",
};
return type[key] ? type[key] : "any";
};
// 获取模块
const getModules = (map) => {
let moduleList = [];
map.forEach((value, key) => {
const module = writeFileApi(key, value);
moduleList = [...moduleList, ...module];
});
console.log(
chalk.green(
"---------------------------------------------------------------"
)
);
console.log(chalk.green("导出成功!"));
return moduleList;
};
// 参数item类型
const interfaceParamsList = (params) => {
let str = "";
params.forEach((item) => {
str = `${str}
/** ${item.description ? item.description : ""} **/
${item.name}${item.required ? "?" : ""}: ${dataType(item.type)};
`;
});
return str;
};
// 定义参数类型
const interfaceParamsTpl = (params, interfaceName) => {
if (!params || params.length === 0) {
return "";
} else {
return `interface ${interfaceName} {
${interfaceParamsList(params)}
}`;
}
};
// 设置单个接口写入模板
const tplInsertApi = (apiInfo) => {
const keys = Object.keys(apiInfo);
const method = keys[0];
const methodParams = apiInfo[method];
const parameters = methodParams.parameters; // 接口参数
const operationId = methodParams.operationId;
const allPath = apiInfo.allPath; // 接口地址
const summary = methodParams.summary; // 接口描述
const requestName = moduleName(operationId); // 接口名称格式化
let interfaceName = "any"; // 定义接口name
let interfaceParams = "data?:any"; // 定义参数及类型
let parametersType = "data"; // 请求类型
if (parameters && parameters.length > 0) {
interfaceName = `${requestName}Ife`;
interfaceParams = `data?: ${interfaceName}`;
}
// get请求默认为json传参
if (method.toLocaleLowerCase() === "get") {
parametersType = "params";
interfaceParams = `params?: ${interfaceName}`;
}
return apiConfig(
summary,
interfaceParamsTpl(parameters, interfaceName),
requestName,
interfaceParams,
method,
allPath,
parametersType
);
};
// 接口名称(使用operationId)
const getModulesName = (apiInfo) => {
const keys = Object.keys(apiInfo);
const method = keys[0];
const methodParams = apiInfo[method];
const operationId = methodParams.operationId;
return operationId;
};
// 单个文件顶部配置
const setTopConfig = () => {
let config = topConfig;
return config;
};
// 写入文件
const writeFileApi = (fileName, apiData) => {
// 设置文件顶部配置(如引入axios/定义响应类型等)
let tplIndex = setTopConfig();
const apiDataLen = apiData.length;
let moduleList = [];
for (let i = 0; i < apiDataLen; i++) {
const item = apiData[i];
tplIndex = `${tplIndex}\n${tplInsertApi(item)}\n`;
moduleList.push(getModulesName(item));
}
tplIndex = beautify(tplIndex, {
indent_size: 2, // 缩进宽度,默认为4
max_preserve_newlines: 2, // 保留的最大换行符数量,默认为10
space_in_paren: true, // 圆括号内是否保留空格,默认为false
preserve_newlines: false, // 是否保留换行符,默认为true
// jslint_happy: true, // 是否启用JSLint风格,默认为false
});
fs.writeFileSync(`${API_PATH}/${fileName}.ts`, tplIndex);
console.log(
chalk.blue(
`${fileName}.ts` +
chalk.green(" ------------ ") +
chalk.black("[" + apiDataLen + "]") +
chalk.green("个接口写入完成")
)
);
return moduleList;
};
const gen = async () => {
rmDir(API_PATH);
isExist();
try {
let parsed;
if (demo) {
parsed = demoData;
} else {
// 解析url 获得
parsed = await parse.parse(swaggerUrl);
}
const paths = parsed.paths;
const pathsKeys = Object.keys(paths);
const pathsKeysLen = pathsKeys.length;
console.log(
chalk.blue("开始解析,总共接口数量:") + chalk.yellow(pathsKeysLen)
);
console.log(
chalk.red(
"---------------------------------------------------------------"
)
);
const modulesMap = new Map();
for (let i = 0; i < pathsKeysLen; i++) {
const item = pathsKeys[i];
const itemAry = item.split("/");
const pathsItem = paths[item];
let fileName = itemAry[dirIdx || 2];
if (!fileName) continue;
fileName = fileName.toLowerCase();
pathsItem.allPath = item;
if (modulesMap.has(fileName)) {
// 继续添加到当前 fileName 文件内
const fileNameAry = modulesMap.get(fileName);
fileNameAry.push(pathsItem);
modulesMap.set(fileName, fileNameAry);
} else {
modulesMap.set(fileName, [pathsItem]);
}
}
// 获取模块,并写入文件
getModules(modulesMap);
} catch (e) {
console.log(e);
}
};
module.exports = {
gen,
};
其他
初次提交,在本公司项目中,使用是完全支持了,但是不可避免,可能存在问题,如有发现,欢迎提出,感谢!(后期有时间的话,打算搞成vscode插件)