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
291 lines (290 loc) • 11.6 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 promises_1 = require("node:fs/promises");
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;
/**
* 通过 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', selectedTags, excludedByTags = [], apifoxToken, oasVersion = '3.0', exportFormat = 'JSON', includeApifoxExtensionProperties = false, addFoldersToTags = false, branchId, moduleId, }) {
try {
const body = {
scope: {
excludedByTags,
},
options: {
includeApifoxExtensionProperties,
addFoldersToTags,
},
oasVersion,
exportFormat,
};
if (branchId !== undefined) {
body.branchId = branchId;
}
if (moduleId !== undefined) {
body.moduleId = moduleId;
}
const tags = !(0, lodash_1.isEmpty)(selectedTags) ? selectedTags : '*';
if (tags === '*') {
body.scope.type = 'ALL';
}
else {
body.scope.type = 'SELECTED_TAGS';
body.scope.selectedTags = 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 readFileSafelySync(filePath) {
if (!(0, node_fs_1.existsSync)(filePath)) {
(0, log_1.logError)(`文件 ${filePath} 不存在`);
return null;
}
try {
return (0, node_fs_1.readFileSync)(filePath, 'utf-8');
}
catch (error) {
(0, log_1.logError)(`读取文件 ${filePath} 时出错:`, error);
return null;
}
}
function writeFileAsync(filePath, content) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
try {
yield (0, promises_1.writeFile)(filePath, content, 'utf8');
}
catch (error) {
(0, log_1.logError)(`文件 ${filePath} 写入失败`);
}
});
}
function translateChineseModuleNodeToEnglish(openAPI) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const content = readFileSafelySync(process.cwd() + '/openapi-ts-request.cache.json');
let i18n = {};
if (content !== null) {
if (isJSONString(content)) {
i18n = JSON.parse(content);
}
}
return new Promise((resolve, reject) => {
const translateMap = i18n;
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) && !i18n[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(translateMap);
// 在写入前再次读取缓存,合并多个任务的翻译结果
const existingContent = readFileSafelySync(process.cwd() + '/openapi-ts-request.cache.json');
let existingCache = {};
if (existingContent !== null && isJSONString(existingContent)) {
existingCache = JSON.parse(existingContent);
}
// 合并现有缓存和新的翻译结果(新结果优先)
const mergedCache = Object.assign(Object.assign({}, existingCache), translateMap);
void writeFileAsync(process.cwd() + '/openapi-ts-request.cache.json', JSON.stringify(mergedCache, null, 2));
})
.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;