swagger 文档自动生成接口代码+ts类型

递归获取 interface 中指定类型

interface Path {
  "/v1/user/update": {
    post: {
      a: string;
      response: {
        "200": {
          data: string;
        };
      };
    };
  };
}

type GetResponseType<T extends keyof Path, K extends keyof Path[T]> = K extends string
  ? Path[T][K]
  : never;

// 递归定义类型
type GetPathType<T extends keyof Path, P extends string[]> = P extends [infer Head, ...infer Tail]
  ? Head extends keyof Path
    ? GetPathType<Path[Head], Tail>
    : never
  : T extends keyof Path
  ? Path[T]
  : never;

// getPath 方法
function getPath<T extends keyof Path, P extends string[]>(path: T, ...keys: P): GetPathType<T, P> {
  return keys.reduce((acc, key) => acc[key], path) as GetPathType<T, P>;
}

// 测试
const pathType: GetPathType<"/v1/user/update", ["post", "response", "200"]> = {
  data: 'example',
}; // 此时 pathType 的类型即为 Path["/v1/user/update"]["post"]["response"]["200"]
console.log(pathType);

const specificResponseType: GetResponseType<"/v1/user/update", "200"> = {
  data: 'example',
}; // 此时 specificResponseType 的类型即为 Path["/v1/user/update"]["post"]["response"]["200"]
console.log(specificResponseType);

const dynamicPathType = getPath("/v1/user/update", "post", "response", "200");
console.log(dynamicPathType);

实现

const fs = require('fs');
const axios = require('axios');
// const apiJson = require('./testDemo/openapi.json');
// const apiJson = require('./api.json');

const { spawn } = require('child_process');

// 命令和参数
const command = 'npx';
const args = ['openapi-typescript', './testDemo/openapi.json', '-o', './testDemo/schema.d.ts'];

// 输出文件夹
const outputDir = './testDemo';

console.log('Command executing......');

if (!fs.existsSync(outputDir)) {
    // 如果不存在,则创建文件夹
    fs.mkdirSync(outputDir);
    console.log(`Folder "${outputDir}" created.`);
}

async function fetchSwagger() {
    const url = 'swagger文档地址';

    const res = await axios.get(url);
    const data = JSON.stringify(res.data);
    fs.writeFile(`./${outputDir}/openapi.json`, data, (err) => {
        if (err) {
            console.error('Failed to generate file:', err);
        } else {
            console.log('File generated successfully.');
            // 执行命令
            const process = spawn(command, args, { stdio: 'inherit' });
            // 监听命令的退出事件
            process.on('close', (code) => {
                if (code === 0) {
                    console.log('Command executed successfully.');
                    // 生成所有axios方法内容

                    collectTagsAndGenerateCode(res.data);
                } else {
                    console.error(`Command failed with code ${code}.`);
                }
            });
        }
    });
}

async function collectTagsAndGenerateCode(apiJson) {
    const tagsMap = collectTags(apiJson);

    for (const modelKey in tagsMap) {
        const model = tagsMap[modelKey];
        const axiosFunctionsContent = generateAxiosFunctions(model.name, model.paths);
        if (fs.existsSync(outputDir)) {
            fs.writeFile(`./${outputDir}/${model.name}.ts`, axiosFunctionsContent, (err) => {
                if (err) {
                    console.error('Failed to generate file:', err);
                } else {
                    console.log('File generated successfully.');
                }
            });
        } else {
            console.log(`Folder "${outputDir}" created.`);
        }
    }
}

// 接口代码生成,按照文件为维度
function generateAxiosFunctions(modelName, apiPaths) {
    let axiosFunctions = '';

    const header = 'import axios from "../src/service/tools.ts" \n';
    const dts = "import type {paths} from '../schema.d.ts' \n";

    // const ReturnValidType = 'type ReturnValidType<T> = T extends { data?: infer D } ? D : T; \n';
    const ReturnValidType = `type ReturnValidType<T> = T[keyof T] extends { content: infer D }
    ? D extends { '*/*': infer U }
        ? U
        : unknown
    : unknown;
    `;
    axiosFunctions += header;
    axiosFunctions += dts;
    axiosFunctions += ReturnValidType;

    for (const path of apiPaths) {
        const method = path.method;
        const url = path.path;

        // for (const method of methods) {
        const tags = '';
        const axiosFunction = generateAxiosFunction(url, method, tags);

        axiosFunctions += axiosFunction + '\n\n';
        // }
    }

    return axiosFunctions;
}

function generateFunctionName(url) {
    // 可根据需要自定义函数名称的生成逻辑,这里使用简单的处理方式
    const parts = url.split('/');
    const functionName = parts
        .filter((part) => part !== '')
        .map((part) => part.replace(/[{}]/g, ''))
        .map((part) => part.replace(/(\b\w)/gi, (match) => match.toUpperCase()))
        .join('');

    return functionName;
}

function generateAxiosFunction(url, method, tags) {
    const functionName = generateFunctionName(url);

    return `
        export type IProps_${functionName} = paths['${url}']['${method}']['requestBody']['content']['application/json']['data']
        // export type IRes_${functionName}_prefix =paths['${url}']['${method}']['responses'][200]['content']['*/*']
        export type IRes_${functionName}_prefix =paths['${url}']['${method}']['responses']
        export type IRes_${functionName} =ReturnValidType<IRes_${functionName}_prefix>

        export async function ${functionName}(requestData: IProps_${functionName}) {
        try {
            const response = await axios({
            method: '${method}',
            url: '${url}',
            data: requestData
            });

            console.log('API response:', response.data);
            return response.data as IRes_${functionName} ;
        } catch (error) {
            console.error('API error:', error);
            throw error;
        }
        }
    `;
}

function collectTags(apiJson) {
    const tagsMap = {};
    const tags = apiJson.tags;
    const paths = apiJson.paths;

    for (const pathKey in paths) {
        const path = paths[pathKey];

        for (const method in path) {
            const pathTags = path[method]?.tags || [];

            pathTags.forEach((tag) => {
                const item = {
                    [pathKey]: {
                        [method]: path[method],
                    },
                    method,
                    path: pathKey,
                };

                if (tagsMap[tag]) {
                    tagsMap[tag].paths.push(item);
                } else {
                    tagsMap[tag] = {
                        name: tag,
                        paths: [item],
                    };
                }
            });
        }
    }

    return tagsMap;
}
fetchSwagger();

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值