公司业务系统中,API文档定义使用了Swagger,由于多人开发中每个人开发的节点不同,整体的业务过于复杂,维护一套完整的 API 文档十分繁重,且容易出现误修改,所以用 python 实现了多个 yaml 文件 OpenAPI 文档合并工具。
Writing OpenAPI (Swagger) Specification Tutorial
https://apihandyman.io/writing-openapi-swagger-specification-tutorial-part-1-introduction/
Swagger 从入门到精通 (第一个链接的译文)
https://www.gitbook.com/book/huangwenchao/swagger/details
OpenAPI Specification 2.0 (手册,无需通读)
https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md
swagger: "2.0"
info:
version: "0.0.1"
title: XXX
description: XXX
host: localhost:4010
schemes:
- http
basePath: /api/v1
produces:
- application/json
paths:
... # 路由项
definitions:
... # 定义项
responses:
... # 响应消息中定义项
parameters:
... # 路径参数定义项
def findstring(all_the_text):
head_string = re.search(r".*paths:", all_the_text, re.S).group()
# 删除 paths: 之上的头部信息
all_the_text = all_the_text.replace(head_string, '\npaths:')
# 获取模块名,以此为匹配条件
module_name_strings = re.findall(r"(\n\w*:)", all_the_text, re.S)
# print(module_name_strings)
# 新建字典存放模块内容
modules = {}
for i in range(len(module_name_strings)):
if i + 1 "\n","").replace(":",""))] = \
re.search(module_name_strings[i]+".*"+module_name_strings[i+1], all_the_text, re.S).group()\
.replace(module_name_strings[i],"").replace(module_name_strings[i+1],"")
else:
modules[(module_name_strings[i].replace("\n","").replace(":",""))] = \
re.search(module_name_strings[i]+".*", all_the_text, re.S).group().replace(module_name_strings[i],"")
# 应用平移函数
for key in modules:
modules[key] = remove(modules[key]) # remove 在 2.中实现
return modules
# 平移代码
def remove(text):
if text != '':
# 去除代码中的注释
text = re.sub(r'\n(\s*)#(.*)', "", text)
spaces = re.search(r"\n\s*", text, re.S).group()
spaces_len = len(spaces) - 1 # 计算首行 空格数
if spaces_len != 2:
text = text.replace('\n'+' '*spaces_len ,'\n ')
return text
# 去除重定义
def remove_duplicate(name,text): #name是模块名称
names = re.findall(r"(\n\s\s\w+:)", text, re.S)
names_set = set(names)
remove_module = {}
duplicate = False
for item in names_set:
if names.count(item) > 1:
print("发现重定义的%s: %s 次数:%s" %(name,item.replace("\n ","").replace(":",""),names.count(item)))
remove_module[item] = names.count(item)
duplicate = True # 发现重复,进入去重逻辑
if duplicate:
print('是否自动合并?(Y/N)')
flag = input()
if (flag == 'Y') or (flag == 'y'):
print('自动合并中...')
for i in range(len(names)):
if (names[i] in remove_module.keys() and remove_module[names[i]] > 1):
remove_string = re.search(names[i]+"(.*?)"+names[i+1], text, re.S).group()
if names[i+1] != names[i]:
remove_string = remove_string.replace(names[i+1],"")
else:
remove_string = names[i] + remove_string.replace(names[i+1],"")
text = text.replace(remove_string,"",1)
remove_module[names[i]] = remove_module[names[i]] - 1
else:
print('已忽略,请手工处理...')
return text
import re
import os
# 平移代码
def remove(text):
if text != '':
# 去除代码中的注释
text = re.sub(r'\n(\s*)#(.*)', "", text)
spaces = re.search(r"\n\s*", text, re.S).group()
spaces_len = len(spaces) - 1 # 计算首行 空格数
if spaces_len != 2:
text = text.replace('\n'+' '*spaces_len ,'\n ')
return text
# 查找指定代码段
def findstring(all_the_text):
head_string = re.search(r".*paths:", all_the_text, re.S).group()
# 删除 paths: 之上的头部信息
all_the_text = all_the_text.replace(head_string, '\npaths:')
# print(all_the_text)
# 获取模块名,以此为匹配条件
module_name_strings = re.findall(r"(\n\w*:)", all_the_text, re.S)
# print(module_name_strings)
# 新建字典存放模块内容
modules = {}
for i in range(len(module_name_strings)):
if i + 1 "\n","").replace(":",""))] = \
re.search(module_name_strings[i]+".*"+module_name_strings[i+1], all_the_text, re.S).group()\
.replace(module_name_strings[i],"").replace(module_name_strings[i+1],"")
else:
modules[(module_name_strings[i].replace("\n","").replace(":",""))] = \
re.search(module_name_strings[i]+".*", all_the_text, re.S).group().replace(module_name_strings[i],"")
# 应用平移函数
for key in modules:
modules[key] = remove(modules[key])
return modules
# 去除重定义函数
def remove_duplicate(name,text):
names = re.findall(r"(\n\s\s\w+:)", text, re.S)
names_set = set(names)
remove_module = {}
duplicate = False
for item in names_set:
if names.count(item) > 1:
print("发现重定义的%s: %s 次数:%s" %(name,item.replace("\n ","").replace(":",""),names.count(item)))
remove_module[item] = names.count(item)
duplicate = True # 发现重复,进入去重
if duplicate:
print('是否自动合并?(Y/N)')
flag = input()
if (flag == 'Y') or (flag == 'y'):
print('自动合并中...')
for i in range(len(names)):
if (names[i] in remove_module.keys() and remove_module[names[i]] > 1):
remove_string = re.search(names[i]+"(.*?)"+names[i+1], text, re.S).group()
if names[i+1] != names[i]:
remove_string = remove_string.replace(names[i+1],"")
else:
remove_string = names[i] + remove_string.replace(names[i+1],"")
text = text.replace(remove_string,"",1)
remove_module[names[i]] = remove_module[names[i]] - 1
else:
print('已忽略,请手工处理...')
return text
# 获取文件列表
def GetFileList(dir, fileList):
newDir = dir
if os.path.isfile(dir):
fileList.append(dir)
elif os.path.isdir(dir):
for s in os.listdir(dir):
#如果需要忽略某些文件夹,使用以下代码
if not (".yaml" in s) or ("api-all" in s) or ("api-combine" in s):
continue
newDir=os.path.join(dir,s)
GetFileList(newDir, fileList)
return fileList
# Main 代码
string_all = {}
string_all['paths'] = ''
string_all['definitions'] = ''
string_all['responses'] = ''
string_all['parameters'] = ''
file_names = GetFileList(os.getcwd(), [])
print("检测到当前目录以下yaml文件:")
for e in file_names:
print(e)
print('共 %s 个文件需合并'%(len(file_names)))
print('\n正在合并中...\n')
# 循环读取文件
for file_name in file_names:
file_object = open(file_name,'r',encoding= 'utf8')
try:
all_the_text = file_object.read()
finally:
file_object.close()
text_modules = findstring(all_the_text)
if 'paths' in text_modules:
string_all['paths'] += text_modules['paths']
if 'definitions' in text_modules:
string_all['definitions'] += text_modules['definitions']
if 'responses' in text_modules:
string_all['responses'] += text_modules['responses']
if 'parameters' in text_modules:
string_all['parameters'] += text_modules['parameters']
# 去重
for key in string_all:
string_all[key] = remove_duplicate(key,string_all[key])
module = '''swagger: '2.0'
info:
title: XXX
description: XXX
version: "1.0.0"
host: localhost:4010
schemes:
- http
basePath: /api/v1
produces:
- application/json
paths:%s
definitions:%s
responses:%s
parameters:%s
''' % (string_all['paths'], string_all['definitions'], string_all['responses'], string_all['parameters'])
# parameters 不存在时,去掉 parameters:
if string_all['parameters'] == '':
module = module.replace('\nparameters:','')
# 去除多余空行
module = re.sub(r"\n\s*\n", "\n", module)
result_file = open('api-combine.yaml','w+',encoding= 'utf8')
result_file.write(module)
print('api-combine.yaml生成成功!!!');
input()
将代码放到单独的 .py 文件中,置于 yaml API 文档文件夹目录下执行即可,需安装Python3.x。
笔者的python并不精通,O(∩_∩)O~ ,代码仍需改进,可能存在各种bug,仅供参考!