openapi-ts-request
Version:
Swagger2/OpenAPI3/Apifox to TypeScript/JavaScript, request client(support any client), request mock service, enum and enum translation, react-query/vue-query, type field label, JSON Schemas
274 lines (273 loc) • 10.4 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.camelCase = exports.getOpenAPIConfig = exports.getOpenAPIConfigByApifox = exports.getImportStatement = void 0;
exports.parseSwaggerOrOpenapi = parseSwaggerOrOpenapi;
exports.translateChineseModuleNodeToEnglish = translateChineseModuleNodeToEnglish;
const tslib_1 = require("tslib");
const axios_1 = tslib_1.__importDefault(require("axios"));
const bing_translate_api_1 = require("bing-translate-api");
const http_1 = tslib_1.__importDefault(require("http"));
const https_1 = tslib_1.__importDefault(require("https"));
const yaml = tslib_1.__importStar(require("js-yaml"));
const lodash_1 = require("lodash");
const node_fs_1 = require("node:fs");
const swagger2openapi_1 = tslib_1.__importDefault(require("swagger2openapi"));
const log_1 = tslib_1.__importStar(require("./log"));
const getImportStatement = (requestLibPath) => {
if (requestLibPath) {
if (requestLibPath.startsWith('import')) {
return requestLibPath;
}
return `import request from '${requestLibPath}';`;
}
return `import { request } from 'axios';`;
};
exports.getImportStatement = getImportStatement;
const getApifoxIncludeTags = (tags) => {
let _tags_ = '*';
if (tags && Array.isArray(tags)) {
if (!tags.length) {
return '*';
}
_tags_ = [];
for (const tag of tags) {
if (typeof tag === 'string') {
if (tag === '*') {
_tags_ = '*';
break;
}
}
else if (tag instanceof RegExp) {
_tags_ = '*';
break;
// TODO:后期添加支持判断字符串是否为正则
}
else {
_tags_.push(tag);
}
}
}
else if (tags) {
_tags_ = [tags];
}
return _tags_;
};
/**
* 通过 apifox 获取 openapi 文档
* @param params {object}
* @param params.projectId {string} 项目 id
* @param params.locale {string} 语言
* @param params.apifoxVersion {string} apifox 版本 目前固定为 2024-03-28 可通过 https://api.apifox.com/v1/versions 获取最新版本
* @returns
*/
const getSchemaByApifox = (_a) => tslib_1.__awaiter(void 0, [_a], void 0, function* ({ projectId, locale = 'zh-CN', apifoxVersion = '2024-03-28', includeTags, excludeTags = [], apifoxToken, oasVersion = '3.0', exportFormat = 'JSON', includeApifoxExtensionProperties = false, addFoldersToTags = false, }) {
try {
const body = {
scope: {
excludeTags,
},
options: {
includeApifoxExtensionProperties,
addFoldersToTags,
},
oasVersion,
exportFormat,
};
const tags = getApifoxIncludeTags(includeTags);
if (tags === '*') {
body.scope.type = 'ALL';
}
else {
body.scope.type = 'SELECTED_TAGS';
body.scope.includeTags = tags;
}
const res = yield axios_1.default.post(`https://api.apifox.com/v1/projects/${projectId}/export-openapi?locale=${locale}`, body, {
headers: {
'X-Apifox-Api-Version': apifoxVersion,
Authorization: `Bearer ${apifoxToken}`,
},
});
return res.data;
}
catch (error) {
(0, log_1.logError)('fetch openapi error:', error);
return null;
}
});
function getSchema(schemaPath, authorization, timeout) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
if (schemaPath.startsWith('http')) {
const isHttps = schemaPath.startsWith('https:');
const protocol = isHttps ? https_1.default : http_1.default;
try {
const agent = new protocol.Agent({
rejectUnauthorized: false,
});
const config = isHttps ? { httpsAgent: agent } : { httpAgent: agent };
const json = yield axios_1.default
.get(schemaPath, Object.assign(Object.assign({}, config), { headers: { authorization }, timeout }))
.then((res) => res.data);
return json;
}
catch (error) {
console.log('fetch openapi error:', error);
}
return;
}
if (require.cache[schemaPath]) {
delete require.cache[schemaPath];
}
let schema = '';
try {
schema = (yield require(schemaPath));
}
catch (_a) {
try {
schema = (0, node_fs_1.readFileSync)(schemaPath, 'utf8');
}
catch (error) {
console.error('Error reading schema file:', error);
}
}
return schema;
});
}
function converterSwaggerToOpenApi(swagger) {
return new Promise((resolve, reject) => {
const convertOptions = {
patch: true,
warnOnly: true,
resolveInternal: true,
};
// options.patch = true; // fix up small errors in the source definition
// options.warnOnly = true; // Do not throw on non-patchable errors
// options.warnOnly = true; // enable resolution of internal $refs, also disables deduplication of requestBodies
swagger2openapi_1.default.convertObj(swagger, convertOptions, (err, options) => {
(0, log_1.default)(['💺 将 Swagger 转化为 openAPI']);
if (err) {
return reject(err);
}
resolve(options.openapi);
});
});
}
const getOpenAPIConfigByApifox = (props) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
const schema = yield getSchemaByApifox(props);
if (!schema) {
return;
}
return yield parseSwaggerOrOpenapi(schema);
});
exports.getOpenAPIConfigByApifox = getOpenAPIConfigByApifox;
const getOpenAPIConfig = (schemaPath, authorization, timeout) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
const schema = yield getSchema(schemaPath, authorization, timeout);
if (!schema) {
return;
}
const openAPI = yield parseSwaggerOrOpenapi(schema);
return openAPI;
});
exports.getOpenAPIConfig = getOpenAPIConfig;
function parseSwaggerOrOpenapi(content) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
let openapi = {};
if ((0, lodash_1.isObject)(content)) {
openapi = content;
// if is swagger2.0 json, covert swagger2.0 to openapi3.0
if (openapi.swagger) {
openapi = yield converterSwaggerToOpenApi(openapi);
}
}
else {
if (isJSONString(content)) {
openapi = JSON.parse(content);
}
else {
openapi = yaml.load(content);
}
if (openapi.swagger) {
openapi = yield converterSwaggerToOpenApi(openapi);
}
}
return openapi;
});
}
function isJSONString(str) {
try {
JSON.parse(str);
return true;
}
catch (error) {
return false;
}
}
function translateChineseModuleNodeToEnglish(openAPI) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
return new Promise((resolve, reject) => {
const translateMap = {};
const operations = [];
let tags = [];
(0, lodash_1.forEach)((0, lodash_1.keys)(openAPI.paths), (path) => {
const pathItemObject = openAPI.paths[path];
(0, lodash_1.forEach)((0, lodash_1.keys)(pathItemObject), (method) => {
if (pathItemObject[method]) {
const operation = pathItemObject[method];
operations.push(operation);
tags = tags.concat(operation.tags);
}
});
});
void Promise.all((0, lodash_1.map)((0, lodash_1.uniq)(tags), (tagName) => {
return new Promise((resolve) => {
if (tagName && /[\u3220-\uFA29]/.test(tagName)) {
void (0, bing_translate_api_1.translate)(tagName, null, 'en')
.then((translateRes) => {
const text = (0, exports.camelCase)(translateRes === null || translateRes === void 0 ? void 0 : translateRes.translation);
if (text) {
translateMap[tagName] = text;
resolve(text);
}
})
.catch(() => {
resolve(tagName);
});
}
else {
resolve(tagName);
}
});
}))
.then(() => {
(0, lodash_1.forEach)(operations, (operation) => {
(0, lodash_1.forEach)(operation.tags, (tagName, index) => {
if (translateMap[tagName]) {
operation.tags[index] = translateMap[tagName];
}
});
});
resolve(true);
})
.catch(() => {
reject(false);
});
});
});
}
/**
* Converts a string to camelCase format, with an option to capitalize the first letter.
* 将字符串转换为驼峰格式,并可以选择将首字母大写。
*
* @param {string} str - The string to convert.
* @param {string} str - 要转换的字符串。
*
* @param {boolean} [upper=false] - Whether to capitalize the first letter of the resulting string.
* @param {boolean} [upper=false] - 是否将结果字符串的首字母大写。
*
* @returns {string} The camelCase formatted string, optionally with a capitalized first letter.
* @returns {string} 返回驼峰格式的字符串,可选择首字母大写。
*/
const camelCase = (str, upper = false) => {
const res = (0, lodash_1.camelCase)(str);
return upper ? res.charAt(0).toUpperCase() + res.slice(1) : res;
};
exports.camelCase = camelCase;