fix(接口定义): 修复swagger导入一些值缺失 & 请求体导出支持json、xml格式 (#1751)
* feat(测试跟踪): 测试用例下载模版增加标签列 * fix(接口定义): 扩大请求头键长度 * fix: schedule表对旧数据name字段兼容的补充 * fix(接口定义): 修复swagger导入一些值缺失 & 请求体导出支持json、xml格式
This commit is contained in:
parent
fc9d040579
commit
3e97f8da6c
|
@ -26,12 +26,15 @@ import io.swagger.v3.oas.models.parameters.*;
|
||||||
import io.swagger.v3.oas.models.responses.ApiResponse;
|
import io.swagger.v3.oas.models.responses.ApiResponse;
|
||||||
import io.swagger.v3.oas.models.responses.ApiResponses;
|
import io.swagger.v3.oas.models.responses.ApiResponses;
|
||||||
import io.swagger.v3.parser.core.models.SwaggerParseResult;
|
import io.swagger.v3.parser.core.models.SwaggerParseResult;
|
||||||
|
import net.sf.saxon.ma.json.XMLToJsonFn;
|
||||||
import org.apache.commons.collections.CollectionUtils;
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
import org.apache.commons.collections.MapUtils;
|
import org.apache.commons.collections.MapUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.fife.ui.rsyntaxtextarea.parser.XmlParser;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
|
||||||
|
@ -334,6 +337,16 @@ public class Swagger3Parser extends SwaggerAbstractParser {
|
||||||
} else if (schema instanceof BinarySchema) {
|
} else if (schema instanceof BinarySchema) {
|
||||||
return getDefaultValueByPropertyType(schema);
|
return getDefaultValueByPropertyType(schema);
|
||||||
} else {
|
} else {
|
||||||
|
if(schema.getType() != null) { // 特判属性不是对象的情况,直接将基本类型赋值进去
|
||||||
|
if(StringUtils.equals(schema.getType(), "string")) {
|
||||||
|
String example = (String) schema.getExample();
|
||||||
|
return example == null ? "" : example;
|
||||||
|
} else if(StringUtils.equals(schema.getType(), "boolean")) {
|
||||||
|
return schema.getExample();
|
||||||
|
} else if(StringUtils.equals(schema.getType(), "double")) {
|
||||||
|
return schema.getExample();
|
||||||
|
}
|
||||||
|
}
|
||||||
Object propertiesResult = parseSchemaProperties(schema, refSet, infoMap);
|
Object propertiesResult = parseSchemaProperties(schema, refSet, infoMap);
|
||||||
return propertiesResult == null ? getDefaultValueByPropertyType(schema) : propertiesResult;
|
return propertiesResult == null ? getDefaultValueByPropertyType(schema) : propertiesResult;
|
||||||
}
|
}
|
||||||
|
@ -432,7 +445,9 @@ public class Swagger3Parser extends SwaggerAbstractParser {
|
||||||
JSONObject requestBody = buildRequestBody(requestObject);
|
JSONObject requestBody = buildRequestBody(requestObject);
|
||||||
swaggerApiInfo.setRequestBody(requestBody);
|
swaggerApiInfo.setRequestBody(requestBody);
|
||||||
// 设置响应体
|
// 设置响应体
|
||||||
swaggerApiInfo.setResponses(new JSONObject());
|
// JSONObject reponseObject = JSON.parseObject(apiDefinition.getResponse());
|
||||||
|
// swaggerApiInfo.setResponses(buildResponseBody(reponseObject));
|
||||||
|
//.....
|
||||||
// 设置请求参数列表
|
// 设置请求参数列表
|
||||||
List<JSONObject> paramsList = buildParameters(requestObject);
|
List<JSONObject> paramsList = buildParameters(requestObject);
|
||||||
swaggerApiInfo.setParameters(paramsList);
|
swaggerApiInfo.setParameters(paramsList);
|
||||||
|
@ -459,6 +474,9 @@ public class Swagger3Parser extends SwaggerAbstractParser {
|
||||||
if(params != null) {
|
if(params != null) {
|
||||||
for(int i = 0; i < params.size(); ++i) {
|
for(int i = 0; i < params.size(); ++i) {
|
||||||
JSONObject param = params.getJSONObject(i); // 对于每个参数:
|
JSONObject param = params.getJSONObject(i); // 对于每个参数:
|
||||||
|
if(param.get("name") == null || StringUtils.isEmpty(((String) param.get("name")))) {
|
||||||
|
continue;
|
||||||
|
} // 否则无参数的情况,可能多出一行空行
|
||||||
SwaggerParams swaggerParam = new SwaggerParams();
|
SwaggerParams swaggerParam = new SwaggerParams();
|
||||||
swaggerParam.setIn(typeMap.get(type)); // 利用 map,根据 request 的 key 设置对应的参数类型
|
swaggerParam.setIn(typeMap.get(type)); // 利用 map,根据 request 的 key 设置对应的参数类型
|
||||||
swaggerParam.setDescription((String) param.get("description"));
|
swaggerParam.setDescription((String) param.get("description"));
|
||||||
|
@ -480,9 +498,20 @@ public class Swagger3Parser extends SwaggerAbstractParser {
|
||||||
put("Form Data", org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE);
|
put("Form Data", org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE);
|
||||||
put("WWW_FORM", org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED_VALUE);
|
put("WWW_FORM", org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED_VALUE);
|
||||||
}};
|
}};
|
||||||
|
JSONObject bodyInfo = new JSONObject();
|
||||||
|
if(request.getJSONObject("body") != null && request.getJSONObject("body").containsKey("raw")) {
|
||||||
|
String bodyType = request.getJSONObject("body").getString("type");
|
||||||
|
if(bodyType.equals("JSON")) {
|
||||||
|
bodyInfo = buildRequestBodyJsonInfo(request.getJSONObject("body").getJSONObject("raw"));
|
||||||
|
} else if(bodyType.equals("XML")) {
|
||||||
|
String xmlText = request.getJSONObject("body").getString("raw");
|
||||||
|
JSONObject xmlToJson = XMLUtils.XmlToJson(xmlText);
|
||||||
|
bodyInfo = buildRequestBodyJsonInfo(xmlToJson);
|
||||||
|
}
|
||||||
|
}
|
||||||
String type = request.getJSONObject("body").getString("type");
|
String type = request.getJSONObject("body").getString("type");
|
||||||
JSONObject requestBody = new JSONObject();
|
JSONObject requestBody = new JSONObject();
|
||||||
JSONObject schema = new JSONObject();
|
JSONObject schema = bodyInfo; // 需要转换导出
|
||||||
JSONObject typeName = new JSONObject();
|
JSONObject typeName = new JSONObject();
|
||||||
schema.put("type", null);
|
schema.put("type", null);
|
||||||
schema.put("format", null);
|
schema.put("format", null);
|
||||||
|
@ -494,4 +523,55 @@ public class Swagger3Parser extends SwaggerAbstractParser {
|
||||||
requestBody.put("content", content);
|
requestBody.put("content", content);
|
||||||
return requestBody;
|
return requestBody;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 将请求体中的一个 json 对象转换成 swagger 格式的 json 对象返回
|
||||||
|
private JSONObject buildRequestBodyJsonInfo(JSONObject requestBody) {
|
||||||
|
JSONObject schema = new JSONObject();
|
||||||
|
schema.put("type", "object");
|
||||||
|
JSONObject properties = buildSchema(requestBody);
|
||||||
|
schema.put("properties", properties);
|
||||||
|
return schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置一个 json 对象的属性在 swagger 格式中的类型、值
|
||||||
|
private JSONObject buildSchema(JSONObject requestBody) {
|
||||||
|
JSONObject schema = new JSONObject();
|
||||||
|
for(String key : requestBody.keySet()) {
|
||||||
|
Object param = requestBody.get(key);
|
||||||
|
JSONObject parsedParam = new JSONObject();
|
||||||
|
if(param instanceof java.lang.String) {
|
||||||
|
parsedParam.put("type", "string");
|
||||||
|
parsedParam.put("example", param == null? "" : param);
|
||||||
|
} else if(param instanceof java.lang.Integer) {
|
||||||
|
parsedParam.put("type", "integer");
|
||||||
|
parsedParam.put("format", "int64");
|
||||||
|
parsedParam.put("example", param);
|
||||||
|
} else if(param instanceof JSONObject) {
|
||||||
|
parsedParam = buildRequestBodyJsonInfo((JSONObject) param);
|
||||||
|
} else if(param instanceof java.lang.Boolean) {
|
||||||
|
parsedParam.put("type", "boolean");
|
||||||
|
parsedParam.put("example", param);
|
||||||
|
} else if(param instanceof java.math.BigDecimal) { // double 类型会被 fastJson 转换为 BigDecimal
|
||||||
|
parsedParam.put("type", "double");
|
||||||
|
parsedParam.put("example", param);
|
||||||
|
} else { // JSONOArray
|
||||||
|
parsedParam.put("type", "array");
|
||||||
|
JSONObject item = new JSONObject();
|
||||||
|
if(((JSONArray) param).size() > 0) {
|
||||||
|
if(((JSONArray) param).get(0) instanceof JSONObject) { ///
|
||||||
|
item = buildRequestBodyJsonInfo((JSONObject) ((JSONArray) param).get(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parsedParam.put("items", item);
|
||||||
|
}
|
||||||
|
schema.put(key, parsedParam);
|
||||||
|
}
|
||||||
|
return schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JSONObject buildResponseBody(JSONObject reponse) {
|
||||||
|
JSONObject responseBody = new JSONObject();
|
||||||
|
|
||||||
|
return responseBody;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,46 +2,140 @@ package io.metersphere.commons.utils;
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSONArray;
|
import com.alibaba.fastjson.JSONArray;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import io.metersphere.commons.exception.MSException;
|
||||||
|
import io.metersphere.i18n.Translator;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.*;
|
||||||
import java.util.Set;
|
import java.util.regex.*;
|
||||||
|
|
||||||
public class XMLUtils {
|
public class XMLUtils {
|
||||||
|
|
||||||
private static void jsonToXmlStr(JSONObject jObj, StringBuffer buffer) {
|
private static void jsonToXmlStr(JSONObject jObj, StringBuffer buffer, StringBuffer tab) {
|
||||||
Set<Map.Entry<String, Object>> se = jObj.entrySet();
|
Set<Map.Entry<String, Object>> se = jObj.entrySet();
|
||||||
|
StringBuffer nowTab = new StringBuffer(tab.toString());
|
||||||
for (Map.Entry<String, Object> en : se) {
|
for (Map.Entry<String, Object> en : se) {
|
||||||
if ("com.alibaba.fastjson.JSONObject".equals(en.getValue().getClass().getName())) {
|
if ("com.alibaba.fastjson.JSONObject".equals(en.getValue().getClass().getName())) {
|
||||||
buffer.append("<").append(en.getKey()).append(">");
|
buffer.append(tab).append("<").append(en.getKey()).append(">\n");
|
||||||
JSONObject jo = jObj.getJSONObject(en.getKey());
|
JSONObject jo = jObj.getJSONObject(en.getKey());
|
||||||
jsonToXmlStr(jo, buffer);
|
jsonToXmlStr(jo, buffer, nowTab.append("\t"));
|
||||||
buffer.append("</").append(en.getKey()).append(">");
|
buffer.append(tab).append("</").append(en.getKey()).append(">\n");
|
||||||
} else if ("com.alibaba.fastjson.JSONArray".equals(en.getValue().getClass().getName())) {
|
} else if ("com.alibaba.fastjson.JSONArray".equals(en.getValue().getClass().getName())) {
|
||||||
JSONArray jarray = jObj.getJSONArray(en.getKey());
|
JSONArray jarray = jObj.getJSONArray(en.getKey());
|
||||||
for (int i = 0; i < jarray.size(); i++) {
|
for (int i = 0; i < jarray.size(); i++) {
|
||||||
buffer.append("<").append(en.getKey()).append(">");
|
buffer.append(tab).append("<").append(en.getKey()).append(">\n");
|
||||||
if (StringUtils.isNotBlank(jarray.getString(i))) {
|
if (StringUtils.isNotBlank(jarray.getString(i))) {
|
||||||
JSONObject jsonobject = jarray.getJSONObject(i);
|
JSONObject jsonobject = jarray.getJSONObject(i);
|
||||||
jsonToXmlStr(jsonobject, buffer);
|
jsonToXmlStr(jsonobject, buffer, nowTab.append("\t"));
|
||||||
buffer.append("</").append(en.getKey()).append(">");
|
buffer.append(tab).append("</").append(en.getKey()).append(">\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if ("java.lang.String".equals(en.getValue().getClass().getName())) {
|
} else if ("java.lang.String".equals(en.getValue().getClass().getName())) {
|
||||||
buffer.append("<").append(en.getKey()).append(">").append(en.getValue());
|
buffer.append(tab).append("<").append(en.getKey()).append(">").append(en.getValue());
|
||||||
buffer.append("</").append(en.getKey()).append(">");
|
buffer.append("</").append(en.getKey()).append(">\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String jsonToXmlStr(JSONObject jObj) {
|
public static String jsonToXmlStr(JSONObject jObj) {
|
||||||
StringBuffer buffer = new StringBuffer();
|
StringBuffer buffer = new StringBuffer();
|
||||||
buffer.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
|
buffer.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
|
||||||
try {
|
try {
|
||||||
jsonToXmlStr(jObj, buffer);
|
jsonToXmlStr(jObj, buffer, new StringBuffer(""));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LogUtil.error(e.getMessage(), e);
|
LogUtil.error(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
return buffer.toString();
|
return buffer.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 传入完整的 xml 文本,转换成 json 对象
|
||||||
|
public static JSONObject XmlToJson(String xml) {
|
||||||
|
JSONObject result = new JSONObject();
|
||||||
|
List<String> list = preProcessXml(xml);
|
||||||
|
try {
|
||||||
|
result = (JSONObject) XmlTagToJsonObject(list);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtil.error(e.getMessage(), e);
|
||||||
|
MSException.throwException(Translator.get("illegal_xml_format"));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 预处理 xml 文本,转换成 tag + data 的列表
|
||||||
|
private static List<String> preProcessXml(String xml) {
|
||||||
|
int begin = xml.indexOf("?>");
|
||||||
|
if(begin != -1) {
|
||||||
|
if(begin + 2 >= xml.length()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
xml = xml.substring(begin + 2);
|
||||||
|
} // <?xml version="1.0" encoding="utf-8"?> 若存在,则去除
|
||||||
|
String rgex = "\\s*";
|
||||||
|
Pattern pattern = Pattern.compile(rgex);
|
||||||
|
Matcher m = pattern.matcher(xml);
|
||||||
|
xml = m.replaceAll("");
|
||||||
|
rgex = ">";
|
||||||
|
pattern = Pattern.compile(rgex);
|
||||||
|
m = pattern.matcher(xml);
|
||||||
|
xml = m.replaceAll("> ");
|
||||||
|
rgex = "\\s*</";
|
||||||
|
pattern = Pattern.compile(rgex);
|
||||||
|
m = pattern.matcher(xml);
|
||||||
|
xml = m.replaceAll(" </");
|
||||||
|
return Arrays.asList(xml.split(" "));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 传入预处理的列表,返回转换成功的 json 对象
|
||||||
|
private static Object XmlTagToJsonObject(List<String> list) {
|
||||||
|
if(list == null || list.size() == 0)
|
||||||
|
return null;
|
||||||
|
Stack<String> tagStack = new Stack<>(); // tag 栈
|
||||||
|
Stack<Object> valueStack = new Stack<>(); // 数据栈
|
||||||
|
valueStack.push(new JSONObject()); // 最终结果将存放在第一个入栈的元素中
|
||||||
|
for(String item : list) {
|
||||||
|
String beginTag = isBeginTag(item), endTag = isEndTag(item); // 判断当前 tag 是开始还是结尾
|
||||||
|
if(beginTag != null) {
|
||||||
|
tagStack.push(beginTag);
|
||||||
|
valueStack.push(new JSONObject());
|
||||||
|
} else if(endTag != null) {
|
||||||
|
if(endTag.equals(tagStack.peek())) { // 是一对 tag
|
||||||
|
Object topValue = valueStack.peek();
|
||||||
|
if(topValue instanceof String) { // 栈顶是纯数据 xml 节点
|
||||||
|
valueStack.pop();
|
||||||
|
}
|
||||||
|
valueStack.pop();
|
||||||
|
if(valueStack.peek() instanceof JSONObject) {
|
||||||
|
((JSONObject) valueStack.peek()).put(tagStack.peek(), topValue);
|
||||||
|
}
|
||||||
|
tagStack.pop();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
valueStack.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(valueStack.empty())
|
||||||
|
return null;
|
||||||
|
return valueStack.peek();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String isEndTag(String tagLine) {
|
||||||
|
String rgex = "</(\\w*)>";
|
||||||
|
Pattern pattern = Pattern.compile(rgex);// 匹配的模式
|
||||||
|
Matcher m = pattern.matcher(tagLine);
|
||||||
|
if (m.find()) {
|
||||||
|
return m.group(1);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String isBeginTag(String tagLine) {
|
||||||
|
String rgex = "<(\\w*)>";
|
||||||
|
Pattern pattern = Pattern.compile(rgex);// 匹配的模式
|
||||||
|
Matcher m = pattern.matcher(tagLine);
|
||||||
|
if (m.find()) {
|
||||||
|
return m.group(1);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,6 +75,7 @@ organization_does_not_belong_to_user=The current organization does not belong to
|
||||||
organization_id_is_null=Organization ID cannot be null
|
organization_id_is_null=Organization ID cannot be null
|
||||||
#api
|
#api
|
||||||
api_load_script_error=Load script error
|
api_load_script_error=Load script error
|
||||||
|
illegal_xml_format=illegal XML format
|
||||||
api_report_is_null="Report is null, can't update"
|
api_report_is_null="Report is null, can't update"
|
||||||
api_test_environment_already_exists="Api test environment already exists"
|
api_test_environment_already_exists="Api test environment already exists"
|
||||||
api_test=API Test
|
api_test=API Test
|
||||||
|
|
|
@ -75,6 +75,7 @@ organization_does_not_belong_to_user=当前组织不属于当前用户
|
||||||
organization_id_is_null=组织 ID 不能为空
|
organization_id_is_null=组织 ID 不能为空
|
||||||
#api
|
#api
|
||||||
api_load_script_error=读取脚本失败
|
api_load_script_error=读取脚本失败
|
||||||
|
illegal_xml_format=不合法的 XML 格式
|
||||||
api_report_is_null="测试报告是未生成,无法更新"
|
api_report_is_null="测试报告是未生成,无法更新"
|
||||||
api_test_environment_already_exists="已存在该名称的环境配置"
|
api_test_environment_already_exists="已存在该名称的环境配置"
|
||||||
api_test=接口测试
|
api_test=接口测试
|
||||||
|
|
|
@ -75,6 +75,7 @@ organization_does_not_belong_to_user=當前組織不屬於當前用戶
|
||||||
organization_id_is_null=組織 ID 不能為空
|
organization_id_is_null=組織 ID 不能為空
|
||||||
#api
|
#api
|
||||||
api_load_script_error=讀取腳本失敗
|
api_load_script_error=讀取腳本失敗
|
||||||
|
illegal_xml_format=不合法的 XML 格式
|
||||||
api_report_is_null="測試報告是未生成,無法更新"
|
api_report_is_null="測試報告是未生成,無法更新"
|
||||||
api_test_environment_already_exists="已存在該名稱的環境配置"
|
api_test_environment_already_exists="已存在該名稱的環境配置"
|
||||||
api_test=接口測試
|
api_test=接口測試
|
||||||
|
|
Loading…
Reference in New Issue