使用NodeJs将swaggerV2文档数据转成docx

结果示例

前言

最近公司一个项目需要提供一份技术文档,其中需要整理相关的接口文档。作为一个后端程序员,为了方便前端测试我们都会提供一个在线的接口文档供前端测试,例如swagger就是比较常用的一种,我用于生成这个文档的数据就是基于swagger提供的json数据。我最早的思路是打算输入json文件的地址或者在线url自动去加载数据,然后再根据swagger的版本的不同去使用不同的解析方式,但是最后因为时间原因,只实现了对swagger V2的本地数据的解析。

准备工作

swagger V2数据

node 环境

officegen office生成库

程序源码

const fs = require('fs')
const officegen = require('officegen')
// api path
const apiFilePath = "D:\\project\\nodejs\\swagger2doc\\api.json"
// controller Name
let _tag2Desc = new Map()
let _modelVo = new Map()
let _apis = []
/**
 * 读取api数据  如果是文件就读取文件,如果是链接就读取抓取接口数据
 * @param {String}} link 
 * @param {Boolean} isFile 
 */
function readApi(link, isFile) {
    let dataStr = fs.readFileSync(link).toString()
    let dataObj = JSON.parse(dataStr)
    return dataObj;
}

/**
 * 解析Api数据
 * @param {Object} api 
 */
function resolveApi(api) {
    let apiInfo = api.info
    let tags = api.tags
    let paths = api.paths
    let definitions = api.definitions
    // 聚合controller Name  
    tags.forEach(tag => {
        _tag2Desc.set(tag.name, tag.description)
    });
    // 聚合model
    for (const voName in definitions) {
        if (definitions.hasOwnProperty(voName)) {
            const vo = definitions[voName];
            _modelVo.set(voName, vo)
        }
    }
    // 解析paths
    for (let path in paths) {
        let _commonApiPath = path
        let apis = paths[path]
        resolvePaths(_commonApiPath, apis)
    }
    // fs.writeFileSync("apiattr.json",JSON.stringify(_apis))
    genDoc(apiInfo, _apis)
}

/**
 * 解析接口信息
 * @param {String} path 
 * @param {Object} apis 
 */
function resolvePaths(path, apis) {
    let basePath = path
    for (const method in apis) {
        if (apis.hasOwnProperty(method)) {
            const api = apis[method];
            let _api = {
                controller: api.tags[0],
                path: basePath,
                tag: _tag2Desc.get(api.tags[0]),
                desc: api.summary,
                method: method,
                params: resolveParam(api.parameters)
            }
            _apis.push(_api)
        }
    }
}

/**
 * 解析参数数组对象
 * @param {Object[]} params 
 */
function resolveParam(params) {
    if (!params) {
        return null
    }
    let _params = []
    let _param = {}
    params.forEach(param => {
        let position = param.in
        _param = {}
        if (position == 'query') {
            _param = {
                name: param.name,
                position: param.in,
                desc: param.description,
                required: param.required,
                type: param.type
            }
            _params.push(_param)
        } else if (position == "formData") {
            _param = {
                name: param.name,
                position: param.in,
                desc: param.description,
                required: param.required,
                type: param.type
            }
            _params.push(_param)

        } else if (position == "path") {
            _param = {
                name: param.name,
                position: param.in,
                desc: param.description,
                required: param.required,
                type: param.type
            }
            _params.push(_param)
        } else if (position == "body") {
            let voName = param.schema.originalRef
            let vo = _modelVo.get(voName)
            let requiredAttr = vo.required
            let properties = vo.properties
            for (const attrName in properties) {
                if (properties.hasOwnProperty(attrName)) {
                    const attr = properties[attrName];
                    let required = (requiredAttr && requiredAttr.indexOf(attrName) >= 0);
                    let type = attr.type
                    let desc = attr.description
                    _param = {
                        name: attrName,
                        position: position,
                        desc: desc,
                        required: required,
                        type: type
                    }
                    _params.push(_param)
                }
            }
        } else {
            console.log("未知类型", param)
        }
    })
    return _params
}

/**
 * 生成文档doc
 * @param {Object}} baseInfo 
 * @param {Object[]} apis 
 */
function genDoc(baseInfo, apis) {
    let docx = officegen('docx')
    // Officegen calling this function after finishing to generate the docx document:
    docx.on('finalize', function (written) {
        console.log(
            'Finish to create a Microsoft Word document.'
        )
    })
    // Officegen calling this function to report errors:
    docx.on('error', function (err) {
        console.log(err)
    })
    let pObj = docx.createP();
    pObj.addText(baseInfo.title, { bold: true, font_size: 20 })
    pObj = docx.createP();
    pObj.addText("版本号 ", { bold: true, font_size: 11 })
    pObj.addText(baseInfo.version)
    pObj = docx.createP();
    pObj.addText("描述 ", { bold: true, font_size: 11 })
    pObj.addText(baseInfo.description)

    var tableStyle = {
        tableAlign: "center",
        spacingLineRule: 'atLeast', // default is atLeast
        indent: 100, // table indent, default is 0
        fixedLayout: false, // default is false
        borders: true, // default is false. if true, default border size is 4
        borderSize: 2, // To use this option, the 'borders' must set as true, default is 4
        columns: [{ width: 600 }, { width: 600 }, { width: 600 }, { width: 600 }, { width: 600 }], // Table logical columns
    }
    apis.forEach(api => {
        //  创建一个段 
        pObj = docx.createP()
        pObj.addText(api.desc, { bold: true, font_size: 18 })
        pObj = docx.createP()
        pObj.addText("所属模块  ", { bold: true, font_size: 11 })
        pObj.addText(api.tag)
        pObj = docx.createP()
        pObj.addText("所属控制器  ", { bold: true, font_size: 11 })
        pObj.addText(api.controller)
        pObj = docx.createP()
        pObj.addText("Path  ", { bold: true, font_size: 11 })
        pObj.addText(api.path)
        pObj = docx.createP()
        pObj.addText("请求方法  ", { bold: true, font_size: 11 })
        pObj.addText(api.method.toUpperCase())
        var table = [
            [
                {
                    val: "属性名"
                },
                {
                    val: "类型"
                },
                {
                    val: "是否必选"
                },
                {
                    val: "参数位置"
                },
                {
                    val: "参数描述"
                }
            ]
        ]
        let params = api.params
        if (params) {
            pObj = docx.createP()
            pObj.addText("请求参数", { bold: true, font_size: 14 })
            params.forEach(param => {
                table.push([param.name, param.type, param.required ? '必选' : '可选', param.position, param.desc])
            })
            docx.createTable(table, tableStyle)
        }
        pObj = docx.createP()
        pObj.addHorizontalLine()
    })
    let out = fs.createWriteStream('doc.docx')
    docx.generate(out)
}

/**
 * 运行函数
 */
(_=>{
    let dataObj = readApi(apiFilePath,true)
    resolveApi(dataObj)
})()

总结

因为时间匆忙的关系,并没有对代码做很好的review之后就马上做了这份笔记,其中存在许多问题以及可以优化的地方,/(ㄒoㄒ)/~,只能放到后面来了

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容