feat(接口测试): 接口定义导入
This commit is contained in:
parent
f5e0177de8
commit
df16810485
|
@ -7,6 +7,7 @@ import lombok.Data;
|
||||||
|
|
||||||
import java.io.Serial;
|
import java.io.Serial;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -35,10 +36,10 @@ public class HttpResponse implements Serializable {
|
||||||
|
|
||||||
@Schema(description = "响应请求头")
|
@Schema(description = "响应请求头")
|
||||||
@Valid
|
@Valid
|
||||||
private List<MsHeader> headers;
|
private List<MsHeader> headers = new ArrayList<>();
|
||||||
|
|
||||||
@Schema(description = "响应请求体")
|
@Schema(description = "响应请求体")
|
||||||
@Valid
|
@Valid
|
||||||
private ResponseBody body;
|
private ResponseBody body = new ResponseBody();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,15 +23,15 @@ public class ResponseBody implements Serializable {
|
||||||
private String bodyType;
|
private String bodyType;
|
||||||
|
|
||||||
@Valid
|
@Valid
|
||||||
private JsonBody jsonBody;
|
private JsonBody jsonBody = new JsonBody();
|
||||||
|
|
||||||
@Valid
|
@Valid
|
||||||
private XmlBody xmlBody;
|
private XmlBody xmlBody = new XmlBody();
|
||||||
|
|
||||||
@Valid
|
@Valid
|
||||||
private RawBody rawBody;
|
private RawBody rawBody = new RawBody();
|
||||||
|
|
||||||
@Valid
|
@Valid
|
||||||
private ResponseBinaryBody binaryBody;
|
private ResponseBinaryBody binaryBody = new ResponseBinaryBody();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
package io.metersphere.api.parser.api;
|
||||||
|
|
||||||
|
|
||||||
|
import io.metersphere.api.dto.converter.ApiDefinitionImportDetail;
|
||||||
|
import io.metersphere.api.dto.request.ImportRequest;
|
||||||
|
import io.metersphere.api.dto.request.http.MsHTTPConfig;
|
||||||
|
import io.metersphere.api.dto.request.http.MsHTTPElement;
|
||||||
|
import io.metersphere.api.dto.request.http.body.*;
|
||||||
|
import io.metersphere.api.parser.ImportParser;
|
||||||
|
import io.metersphere.project.dto.environment.auth.NoAuth;
|
||||||
|
import io.metersphere.sdk.exception.MSException;
|
||||||
|
import io.metersphere.sdk.util.LogUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public abstract class ApiImportAbstractParser<T> implements ImportParser<T> {
|
||||||
|
|
||||||
|
protected String projectId;
|
||||||
|
|
||||||
|
protected String getApiTestStr(InputStream source) {
|
||||||
|
StringBuilder testStr = null;
|
||||||
|
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(source, StandardCharsets.UTF_8))) {
|
||||||
|
testStr = new StringBuilder();
|
||||||
|
String inputStr;
|
||||||
|
while ((inputStr = bufferedReader.readLine()) != null) {
|
||||||
|
testStr.append(inputStr);
|
||||||
|
}
|
||||||
|
source.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtils.error(e.getMessage(), e);
|
||||||
|
throw new MSException(e.getMessage());
|
||||||
|
}
|
||||||
|
return StringUtils.isNotBlank(testStr) ? testStr.toString() : StringUtils.EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
protected ApiDefinitionImportDetail buildApiDefinition(String name, String path, String method,String modulePath, ImportRequest importRequest) {
|
||||||
|
ApiDefinitionImportDetail apiDefinition = new ApiDefinitionImportDetail();
|
||||||
|
apiDefinition.setName(name);
|
||||||
|
apiDefinition.setPath(formatPath(path));
|
||||||
|
apiDefinition.setProtocol(importRequest.getProtocol());
|
||||||
|
apiDefinition.setMethod(method);
|
||||||
|
apiDefinition.setProjectId(this.projectId);
|
||||||
|
apiDefinition.setCreateUser(importRequest.getUserId());
|
||||||
|
apiDefinition.setModulePath(modulePath);
|
||||||
|
apiDefinition.setResponse(new ArrayList<>());
|
||||||
|
return apiDefinition;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected MsHTTPElement buildHttpRequest(String name, String path, String method) {
|
||||||
|
MsHTTPElement request = new MsHTTPElement();
|
||||||
|
request.setName(name);
|
||||||
|
// 路径去掉域名/IP 地址,保留方法名称及参数
|
||||||
|
request.setPath(formatPath(path));
|
||||||
|
request.setMethod(method);
|
||||||
|
request.setHeaders(new ArrayList<>());
|
||||||
|
request.setQuery(new ArrayList<>());
|
||||||
|
request.setRest(new ArrayList<>());
|
||||||
|
request.setBody(new Body());
|
||||||
|
MsHTTPConfig httpConfig = new MsHTTPConfig();
|
||||||
|
httpConfig.setConnectTimeout(60000L);
|
||||||
|
httpConfig.setResponseTimeout(60000L);
|
||||||
|
request.setOtherConfig(httpConfig);
|
||||||
|
request.setAuthConfig(new NoAuth());
|
||||||
|
Body body = new Body();
|
||||||
|
body.setBinaryBody(new BinaryBody());
|
||||||
|
body.setFormDataBody(new FormDataBody());
|
||||||
|
body.setXmlBody(new XmlBody());
|
||||||
|
body.setRawBody(new RawBody());
|
||||||
|
body.setNoneBody(new NoneBody());
|
||||||
|
body.setJsonBody(new JsonBody());
|
||||||
|
body.setWwwFormBody(new WWWFormBody());
|
||||||
|
body.setNoneBody(new NoneBody());
|
||||||
|
body.setBodyType(Body.BodyType.NONE.name());
|
||||||
|
request.setBody(body);
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String formatPath(String url) {
|
||||||
|
try {
|
||||||
|
URI urlObject = new URI(url);
|
||||||
|
return StringUtils.isBlank(urlObject.getPath()) ? url : urlObject.getPath();
|
||||||
|
} catch (Exception ex) {
|
||||||
|
//只需要返回?前的路径
|
||||||
|
return url.split("\\?")[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,329 @@
|
||||||
|
package io.metersphere.api.parser.api;
|
||||||
|
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
|
import com.fasterxml.jackson.databind.node.BooleanNode;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
import com.fasterxml.jackson.databind.node.TextNode;
|
||||||
|
import io.metersphere.api.dto.ApiFile;
|
||||||
|
import io.metersphere.api.dto.converter.ApiDefinitionImportDetail;
|
||||||
|
import io.metersphere.api.dto.definition.HttpResponse;
|
||||||
|
import io.metersphere.api.dto.request.ImportRequest;
|
||||||
|
import io.metersphere.api.dto.request.http.MsHTTPElement;
|
||||||
|
import io.metersphere.api.dto.request.http.MsHeader;
|
||||||
|
import io.metersphere.api.dto.request.http.QueryParam;
|
||||||
|
import io.metersphere.api.dto.request.http.RestParam;
|
||||||
|
import io.metersphere.api.dto.request.http.body.Body;
|
||||||
|
import io.metersphere.api.dto.request.http.body.BodyParamType;
|
||||||
|
import io.metersphere.api.dto.request.http.body.FormDataKV;
|
||||||
|
import io.metersphere.api.dto.request.http.body.WWWFormKV;
|
||||||
|
import io.metersphere.api.parser.api.postman.PostmanItem;
|
||||||
|
import io.metersphere.api.parser.api.postman.PostmanKeyValue;
|
||||||
|
import io.metersphere.api.parser.api.postman.PostmanRequest;
|
||||||
|
import io.metersphere.api.parser.api.postman.PostmanResponse;
|
||||||
|
import io.metersphere.project.dto.environment.auth.BasicAuth;
|
||||||
|
import io.metersphere.project.dto.environment.auth.DigestAuth;
|
||||||
|
import io.metersphere.project.dto.environment.auth.HTTPAuthConfig;
|
||||||
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
|
import org.apache.commons.lang3.BooleanUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public abstract class PostmanAbstractParserParser<T> extends ApiImportAbstractParser<T> {
|
||||||
|
|
||||||
|
|
||||||
|
private static final String POSTMAN_AUTH_TYPE_BASIC = "basic";
|
||||||
|
private static final String POSTMAN_AUTH_TYPE_DIGEST = "digest";
|
||||||
|
|
||||||
|
private static final String QUERY = "query";
|
||||||
|
private static final String PATH = "path";
|
||||||
|
private static final String VARIABLE = "variable";
|
||||||
|
private static final String KEY = "key";
|
||||||
|
private static final String VALUE = "value";
|
||||||
|
private static final String RAW = "raw";
|
||||||
|
private static final String TYPE = "type";
|
||||||
|
private static final String DESCRIPTION = "description";
|
||||||
|
private static final String USERNAME = "username";
|
||||||
|
private static final String PASSWORD = "password";
|
||||||
|
private static final String DISABLED = "disabled";
|
||||||
|
private static final String MODE = "mode";
|
||||||
|
private static final String FORM_DATA = "formdata";
|
||||||
|
private static final String URL_ENCODED = "urlencoded";
|
||||||
|
private static final String FILE = "file";
|
||||||
|
private static final String OPTIONS = "options";
|
||||||
|
private static final String La = "language";
|
||||||
|
private static final String JSON = "json";
|
||||||
|
private static final String XML = "xml";
|
||||||
|
|
||||||
|
|
||||||
|
protected ApiDefinitionImportDetail parsePostman(PostmanItem requestItem, String modulePath, ImportRequest importRequest) {
|
||||||
|
PostmanRequest requestDesc = requestItem.getRequest();
|
||||||
|
if (requestDesc == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String url = StringUtils.EMPTY;
|
||||||
|
JsonNode urlNode = requestDesc.getUrl();
|
||||||
|
//获取url
|
||||||
|
url = getUrl(urlNode, url);
|
||||||
|
ApiDefinitionImportDetail detail = buildApiDefinition(requestItem.getName(), url, requestDesc.getMethod(), modulePath, importRequest);
|
||||||
|
MsHTTPElement request = buildHttpRequest(requestItem.getName(), url, requestDesc.getMethod());
|
||||||
|
//设置认证
|
||||||
|
setAuth(requestDesc, request);
|
||||||
|
//设置基础参数 请求头
|
||||||
|
setHeader(requestDesc, request);
|
||||||
|
if (urlNode instanceof ObjectNode urlObject) {
|
||||||
|
//设置query参数
|
||||||
|
setQuery(urlObject, request);
|
||||||
|
//设置rest参数
|
||||||
|
setRest(urlObject, request);
|
||||||
|
}
|
||||||
|
//设置body参数
|
||||||
|
setBody(requestDesc, request);
|
||||||
|
// 其他设置
|
||||||
|
PostmanItem.ProtocolProfileBehavior protocolProfileBehavior = requestItem.getProtocolProfileBehavior();
|
||||||
|
request.getOtherConfig().setFollowRedirects(protocolProfileBehavior != null &&
|
||||||
|
BooleanUtils.isTrue(protocolProfileBehavior.getFollowRedirects()));
|
||||||
|
request.getOtherConfig().setAutoRedirects(!request.getOtherConfig().getFollowRedirects());
|
||||||
|
|
||||||
|
detail.setRequest(request);
|
||||||
|
|
||||||
|
//设置response
|
||||||
|
setResponseData(requestItem, detail);
|
||||||
|
|
||||||
|
return detail;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setResponseData(PostmanItem requestItem, ApiDefinitionImportDetail detail) {
|
||||||
|
List<PostmanResponse> response = requestItem.getResponse();
|
||||||
|
if (CollectionUtils.isNotEmpty(response)) {
|
||||||
|
response.forEach(r -> {
|
||||||
|
HttpResponse httpResponse = new HttpResponse();
|
||||||
|
//httpResponse.setId(IDGenerator.nextStr());
|
||||||
|
httpResponse.setName(r.getName());
|
||||||
|
httpResponse.setStatusCode(r.getCode().toString());
|
||||||
|
if (CollectionUtils.isNotEmpty(r.getHeader())) {
|
||||||
|
r.getHeader().forEach(h -> {
|
||||||
|
MsHeader msHeader = getMsHeader(h);
|
||||||
|
httpResponse.getHeaders().add(msHeader);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
httpResponse.getBody().getJsonBody().setJsonValue(r.getBody() != null && r.getBody() instanceof TextNode ? r.getBody().asText() : StringUtils.EMPTY);
|
||||||
|
httpResponse.getBody().setBodyType(Body.BodyType.RAW.name());
|
||||||
|
detail.getResponse().add(httpResponse);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static MsHeader getMsHeader(PostmanKeyValue h) {
|
||||||
|
MsHeader msHeader = new MsHeader();
|
||||||
|
msHeader.setKey(h.getKey());
|
||||||
|
msHeader.setValue(h.getValue());
|
||||||
|
msHeader.setDescription(h.getDescription() != null ? h.getDescription().asText() : StringUtils.EMPTY);
|
||||||
|
msHeader.setEnable(BooleanUtils.isFalse(h.isDisabled()));
|
||||||
|
return msHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setBody(PostmanRequest requestDesc, MsHTTPElement request) {
|
||||||
|
JsonNode bodyNode = requestDesc.getBody();
|
||||||
|
if (bodyNode != null) {
|
||||||
|
JsonNode modeNode = bodyNode.get(MODE);
|
||||||
|
if (modeNode != null) {
|
||||||
|
switch (modeNode.asText()) {
|
||||||
|
case FORM_DATA:
|
||||||
|
setFormData(bodyNode, request);
|
||||||
|
break;
|
||||||
|
case URL_ENCODED:
|
||||||
|
setWwwData(bodyNode, request);
|
||||||
|
break;
|
||||||
|
case RAW:
|
||||||
|
setRaw(bodyNode, request);
|
||||||
|
break;
|
||||||
|
case FILE:
|
||||||
|
setBinary(bodyNode, request);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setRaw(JsonNode bodyNode, MsHTTPElement request) {
|
||||||
|
JsonNode rawNode = bodyNode.get(RAW);
|
||||||
|
JsonNode optionNode = bodyNode.get(OPTIONS);
|
||||||
|
if (optionNode != null) {
|
||||||
|
if (optionNode instanceof ObjectNode optionObject) {
|
||||||
|
JsonNode languageNode = optionObject.get(RAW).get(La);
|
||||||
|
if (languageNode instanceof TextNode languageText) {
|
||||||
|
if (StringUtils.equals(languageText.asText(), JSON)) {
|
||||||
|
request.getBody().setBodyType(Body.BodyType.JSON.name());
|
||||||
|
request.getBody().getJsonBody().setJsonValue(rawNode instanceof TextNode ? rawNode.asText() : StringUtils.EMPTY);
|
||||||
|
} else if (StringUtils.equals(languageText.asText(), XML)) {
|
||||||
|
request.getBody().setBodyType(Body.BodyType.XML.name());
|
||||||
|
request.getBody().getXmlBody().setValue(rawNode instanceof TextNode ? rawNode.asText() : StringUtils.EMPTY);
|
||||||
|
} else {
|
||||||
|
request.getBody().setBodyType(Body.BodyType.RAW.name());
|
||||||
|
request.getBody().getRawBody().setValue(rawNode instanceof TextNode ? rawNode.asText() : StringUtils.EMPTY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setBinary(JsonNode bodyNode, MsHTTPElement request) {
|
||||||
|
JsonNode fileNode = bodyNode.get(FILE);
|
||||||
|
if (fileNode != null) {
|
||||||
|
request.getBody().getBinaryBody().setFile(new ApiFile());
|
||||||
|
}
|
||||||
|
request.getBody().setBodyType(Body.BodyType.BINARY.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setWwwData(JsonNode bodyNode, MsHTTPElement request) {
|
||||||
|
JsonNode modeNode = bodyNode.get(URL_ENCODED);
|
||||||
|
if (modeNode instanceof ArrayNode urlEncodedArray) {
|
||||||
|
urlEncodedArray.forEach(u -> {
|
||||||
|
WWWFormKV wwwFormKV = new WWWFormKV();
|
||||||
|
wwwFormKV.setKey(u.get(KEY).asText());
|
||||||
|
wwwFormKV.setValue(u.get(VALUE).asText());
|
||||||
|
wwwFormKV.setDescription(u.get(DESCRIPTION) instanceof TextNode ? u.get(DESCRIPTION).asText() : StringUtils.EMPTY);
|
||||||
|
wwwFormKV.setEnable(!(u.get(DISABLED) instanceof BooleanNode) || !u.get(DISABLED).asBoolean());
|
||||||
|
request.getBody().getWwwFormBody().getFormValues().add(wwwFormKV);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
request.getBody().setBodyType(Body.BodyType.WWW_FORM.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setFormData(JsonNode bodyNode, MsHTTPElement request) {
|
||||||
|
JsonNode modeNode = bodyNode.get(FORM_DATA);
|
||||||
|
if (modeNode instanceof ArrayNode formArray) {
|
||||||
|
formArray.forEach(f -> {
|
||||||
|
FormDataKV formDataKV = new FormDataKV();
|
||||||
|
formDataKV.setKey(f.get(KEY).asText());
|
||||||
|
String type = f.get(TYPE).asText();
|
||||||
|
if (StringUtils.equals(type, FILE)) {
|
||||||
|
formDataKV.setParamType(BodyParamType.FILE.getValue());
|
||||||
|
formDataKV.setFiles(new ArrayList<>());
|
||||||
|
} else {
|
||||||
|
formDataKV.setParamType(BodyParamType.STRING.getValue());
|
||||||
|
formDataKV.setValue(f.get(VALUE).asText());
|
||||||
|
}
|
||||||
|
formDataKV.setDescription(f.get(DESCRIPTION) instanceof TextNode ? f.get(DESCRIPTION).asText() : StringUtils.EMPTY);
|
||||||
|
formDataKV.setEnable(!(f.get(DISABLED) instanceof BooleanNode) || !f.get(DISABLED).asBoolean());
|
||||||
|
request.getBody().getFormDataBody().getFormValues().add(formDataKV);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
request.getBody().setBodyType(Body.BodyType.FORM_DATA.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setRest(JsonNode urlObject, MsHTTPElement request) {
|
||||||
|
JsonNode restNode = urlObject.get(VARIABLE);
|
||||||
|
if (restNode instanceof ArrayNode restArray) {
|
||||||
|
restArray.forEach(r -> {
|
||||||
|
RestParam restParam = new RestParam();
|
||||||
|
restParam.setKey(r.get(KEY).asText());
|
||||||
|
restParam.setValue(r.get(VALUE).asText());
|
||||||
|
restParam.setDescription(r.get(DESCRIPTION) instanceof TextNode ? r.get(DESCRIPTION).asText() : StringUtils.EMPTY);
|
||||||
|
restParam.setEnable(!(r.get(DISABLED) instanceof BooleanNode) || !r.get(DISABLED).asBoolean());
|
||||||
|
request.getRest().add(restParam);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setQuery(JsonNode urlObject, MsHTTPElement request) {
|
||||||
|
JsonNode queryNode = urlObject.get(QUERY);
|
||||||
|
if (queryNode instanceof ArrayNode queryArray) {
|
||||||
|
queryArray.forEach(q -> {
|
||||||
|
QueryParam queryParam = new QueryParam();
|
||||||
|
queryParam.setKey(q.get(KEY).asText());
|
||||||
|
queryParam.setValue(q.get(VALUE).asText());
|
||||||
|
queryParam.setDescription(q.get(DESCRIPTION) instanceof TextNode ? q.get(DESCRIPTION).asText() : StringUtils.EMPTY);
|
||||||
|
queryParam.setEnable(!(q.get(DISABLED) instanceof BooleanNode) || !q.get(DISABLED).asBoolean());
|
||||||
|
request.getQuery().add(queryParam);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setHeader(PostmanRequest requestDesc, MsHTTPElement request) {
|
||||||
|
if (CollectionUtils.isNotEmpty(requestDesc.getHeader())) {
|
||||||
|
requestDesc.getHeader().forEach(h -> {
|
||||||
|
MsHeader msHeader = getMsHeader(h);
|
||||||
|
request.getHeaders().add(msHeader);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getUrl(JsonNode urlNode, String url) {
|
||||||
|
if (urlNode instanceof ObjectNode urlObject) {
|
||||||
|
JsonNode pathNode = urlObject.get(PATH);
|
||||||
|
if (pathNode instanceof ArrayNode pathArray) {
|
||||||
|
StringBuilder path = new StringBuilder();
|
||||||
|
pathArray.forEach(p -> {
|
||||||
|
if (p instanceof TextNode pathString) {
|
||||||
|
if (pathString.asText().startsWith(":")) {
|
||||||
|
path.append(StringUtils.join("/{", pathString.asText().substring(1), "}"));
|
||||||
|
} else {
|
||||||
|
path.append(StringUtils.join("/", pathString.asText()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
url = path.toString();
|
||||||
|
}
|
||||||
|
} else if (urlNode instanceof TextNode urlText) {
|
||||||
|
url = urlText.asText();
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static void setAuth(PostmanRequest requestDesc, MsHTTPElement request) {
|
||||||
|
JsonNode auth = requestDesc.getAuth();
|
||||||
|
if (auth != null) {
|
||||||
|
JsonNode authNode = auth.get(TYPE);
|
||||||
|
HTTPAuthConfig authConfig = request.getAuthConfig();
|
||||||
|
if (authNode != null && StringUtils.equals(authNode.asText(), POSTMAN_AUTH_TYPE_BASIC)) {
|
||||||
|
BasicAuth basicAuth = new BasicAuth();
|
||||||
|
authConfig.setAuthType(HTTPAuthConfig.HTTPAuthType.BASIC.name());
|
||||||
|
DigestAuth digestAuth = buildAuth(POSTMAN_AUTH_TYPE_BASIC, auth);
|
||||||
|
basicAuth.setUserName(digestAuth.getUserName());
|
||||||
|
basicAuth.setPassword(digestAuth.getPassword());
|
||||||
|
authConfig.setBasicAuth(basicAuth);
|
||||||
|
} else if (authNode != null && StringUtils.equals(authNode.asText(), POSTMAN_AUTH_TYPE_DIGEST)) {
|
||||||
|
DigestAuth digestAuth = buildAuth(POSTMAN_AUTH_TYPE_DIGEST, auth);
|
||||||
|
authConfig.setAuthType(HTTPAuthConfig.HTTPAuthType.DIGEST.name());
|
||||||
|
authConfig.setDigestAuth(digestAuth);
|
||||||
|
}
|
||||||
|
request.setAuthConfig(authConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DigestAuth buildAuth(String type, JsonNode authNode) {
|
||||||
|
DigestAuth digestAuth = new DigestAuth();
|
||||||
|
JsonNode digestNode = authNode.get(type);
|
||||||
|
if (digestNode != null) {
|
||||||
|
if (digestNode instanceof ObjectNode digestObject) {
|
||||||
|
JsonNode usernameNode = digestObject.get(USERNAME);
|
||||||
|
JsonNode passwordNode = digestObject.get(PASSWORD);
|
||||||
|
digestAuth.setUserName(usernameNode instanceof TextNode ? usernameNode.asText() : StringUtils.EMPTY);
|
||||||
|
digestAuth.setPassword(passwordNode instanceof TextNode ?passwordNode.asText() : StringUtils.EMPTY);
|
||||||
|
} else if (digestNode instanceof ArrayNode digestArray) {
|
||||||
|
digestArray.forEach(node -> {
|
||||||
|
JsonNode keyNode = node.get(KEY);
|
||||||
|
JsonNode valueNode = node.get(VALUE);
|
||||||
|
if (StringUtils.equals(keyNode.asText(), USERNAME)) {
|
||||||
|
digestAuth.setUserName(valueNode.asText());
|
||||||
|
} else if (StringUtils.equals(keyNode.asText(), PASSWORD)) {
|
||||||
|
digestAuth.setPassword(valueNode.asText());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return digestAuth;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -1,17 +1,60 @@
|
||||||
package io.metersphere.api.parser.api;
|
package io.metersphere.api.parser.api;
|
||||||
|
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import io.metersphere.api.dto.converter.ApiDefinitionImport;
|
||||||
|
import io.metersphere.api.dto.converter.ApiDefinitionImportDetail;
|
||||||
import io.metersphere.api.dto.request.ImportRequest;
|
import io.metersphere.api.dto.request.ImportRequest;
|
||||||
import io.metersphere.api.parser.ImportParser;
|
import io.metersphere.api.parser.api.postman.PostmanCollection;
|
||||||
|
import io.metersphere.api.parser.api.postman.PostmanItem;
|
||||||
|
import io.metersphere.sdk.exception.MSException;
|
||||||
|
import io.metersphere.sdk.util.JSON;
|
||||||
import io.metersphere.sdk.util.LogUtils;
|
import io.metersphere.sdk.util.LogUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class PostmanParser<T> implements ImportParser<T> {
|
public class PostmanParser<T> extends PostmanAbstractParserParser<ApiDefinitionImport> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public T parse(InputStream source, ImportRequest request) {
|
public ApiDefinitionImport parse(InputStream source, ImportRequest request) throws JsonProcessingException {
|
||||||
LogUtils.info("PostmanParser parse");
|
LogUtils.info("PostmanParser parse");
|
||||||
return null;
|
String testStr = getApiTestStr(source);
|
||||||
|
this.projectId = request.getProjectId();
|
||||||
|
PostmanCollection postmanCollection = JSON.parseObject(testStr, PostmanCollection.class);
|
||||||
|
if (postmanCollection == null || postmanCollection.getItem() == null || postmanCollection.getItem().isEmpty() || postmanCollection.getInfo() == null){
|
||||||
|
throw new MSException("Postman collection is empty");
|
||||||
|
}
|
||||||
|
ApiDefinitionImport apiImport = new ApiDefinitionImport();
|
||||||
|
List<ApiDefinitionImportDetail> results = new ArrayList<>();
|
||||||
|
|
||||||
|
String modulePath = null;
|
||||||
|
if (StringUtils.isNotBlank(postmanCollection.getInfo().getName())) {
|
||||||
|
modulePath = "/" + postmanCollection.getInfo().getName();
|
||||||
|
}
|
||||||
|
parseItem(postmanCollection.getItem(), modulePath, results, request);
|
||||||
|
apiImport.setData(results);
|
||||||
|
//apiImport.setCases(cases);
|
||||||
|
LogUtils.info("PostmanParser parse end");
|
||||||
|
return apiImport;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void parseItem(List<PostmanItem> items, String modulePath, List<ApiDefinitionImportDetail> results, ImportRequest request) {
|
||||||
|
for (PostmanItem item : items) {
|
||||||
|
List<PostmanItem> childItems = item.getItem();
|
||||||
|
if (childItems != null) {
|
||||||
|
String itemModulePath;
|
||||||
|
if (StringUtils.isNotBlank(modulePath) && StringUtils.isNotBlank(item.getName())) {
|
||||||
|
itemModulePath = modulePath + "/" + item.getName();
|
||||||
|
} else {
|
||||||
|
itemModulePath = item.getName();
|
||||||
|
}
|
||||||
|
parseItem(childItems, itemModulePath, results, request);
|
||||||
|
} else {
|
||||||
|
results.add(parsePostman(item, modulePath, request));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,22 @@
|
||||||
package io.metersphere.api.parser.api;
|
package io.metersphere.api.parser.api;
|
||||||
|
|
||||||
import io.metersphere.api.constants.ApiConstants;
|
|
||||||
import io.metersphere.api.dto.converter.ApiDefinitionImport;
|
import io.metersphere.api.dto.converter.ApiDefinitionImport;
|
||||||
import io.metersphere.api.dto.converter.ApiDefinitionImportDetail;
|
import io.metersphere.api.dto.converter.ApiDefinitionImportDetail;
|
||||||
import io.metersphere.api.dto.definition.HttpResponse;
|
import io.metersphere.api.dto.definition.HttpResponse;
|
||||||
import io.metersphere.api.dto.definition.ResponseBody;
|
import io.metersphere.api.dto.definition.ResponseBody;
|
||||||
import io.metersphere.api.dto.request.ImportRequest;
|
import io.metersphere.api.dto.request.ImportRequest;
|
||||||
import io.metersphere.api.dto.request.MsCommonElement;
|
import io.metersphere.api.dto.request.MsCommonElement;
|
||||||
import io.metersphere.api.dto.request.http.*;
|
import io.metersphere.api.dto.request.http.MsHTTPElement;
|
||||||
import io.metersphere.project.dto.environment.auth.NoAuth;
|
import io.metersphere.api.dto.request.http.MsHeader;
|
||||||
|
import io.metersphere.api.dto.request.http.QueryParam;
|
||||||
|
import io.metersphere.api.dto.request.http.RestParam;
|
||||||
import io.metersphere.api.dto.request.http.body.*;
|
import io.metersphere.api.dto.request.http.body.*;
|
||||||
import io.metersphere.api.dto.schema.JsonSchemaItem;
|
import io.metersphere.api.dto.schema.JsonSchemaItem;
|
||||||
import io.metersphere.api.parser.ImportParser;
|
|
||||||
import io.metersphere.api.utils.ApiDataUtils;
|
import io.metersphere.api.utils.ApiDataUtils;
|
||||||
import io.metersphere.api.utils.JsonSchemaBuilder;
|
import io.metersphere.api.utils.JsonSchemaBuilder;
|
||||||
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
|
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
|
||||||
import io.metersphere.project.constants.PropertyConstant;
|
import io.metersphere.project.constants.PropertyConstant;
|
||||||
|
import io.metersphere.project.dto.environment.auth.NoAuth;
|
||||||
import io.metersphere.sdk.exception.MSException;
|
import io.metersphere.sdk.exception.MSException;
|
||||||
import io.metersphere.sdk.util.JSON;
|
import io.metersphere.sdk.util.JSON;
|
||||||
import io.metersphere.sdk.util.LogUtils;
|
import io.metersphere.sdk.util.LogUtils;
|
||||||
|
@ -35,15 +36,11 @@ import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.net.URI;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
|
||||||
public class Swagger3Parser<T> implements ImportParser<ApiDefinitionImport> {
|
public class Swagger3Parser<T> extends ApiImportAbstractParser<ApiDefinitionImport> {
|
||||||
|
|
||||||
protected String projectId;
|
protected String projectId;
|
||||||
private Components components;
|
private Components components;
|
||||||
|
@ -53,7 +50,6 @@ public class Swagger3Parser<T> implements ImportParser<ApiDefinitionImport> {
|
||||||
public static final String COOKIE = "cookie";
|
public static final String COOKIE = "cookie";
|
||||||
public static final String QUERY = "query";
|
public static final String QUERY = "query";
|
||||||
|
|
||||||
@Override
|
|
||||||
public ApiDefinitionImport parse(InputStream source, ImportRequest request) throws Exception {
|
public ApiDefinitionImport parse(InputStream source, ImportRequest request) throws Exception {
|
||||||
LogUtils.info("Swagger3Parser parse");
|
LogUtils.info("Swagger3Parser parse");
|
||||||
List<AuthorizationValue> auths = setAuths(request);
|
List<AuthorizationValue> auths = setAuths(request);
|
||||||
|
@ -90,22 +86,6 @@ public class Swagger3Parser<T> implements ImportParser<ApiDefinitionImport> {
|
||||||
return CollectionUtils.size(auths) == 0 ? null : auths;
|
return CollectionUtils.size(auths) == 0 ? null : auths;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String getApiTestStr(InputStream source) {
|
|
||||||
StringBuilder testStr = null;
|
|
||||||
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(source, StandardCharsets.UTF_8))) {
|
|
||||||
testStr = new StringBuilder();
|
|
||||||
String inputStr;
|
|
||||||
while ((inputStr = bufferedReader.readLine()) != null) {
|
|
||||||
testStr.append(inputStr);
|
|
||||||
}
|
|
||||||
source.close();
|
|
||||||
} catch (Exception e) {
|
|
||||||
LogUtils.error(e.getMessage(), e);
|
|
||||||
throw new MSException(e.getMessage());
|
|
||||||
}
|
|
||||||
return StringUtils.isNotBlank(testStr) ? testStr.toString() : StringUtils.EMPTY;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<ApiDefinitionImportDetail> parseRequests(OpenAPI openAPI, ImportRequest importRequest) {
|
private List<ApiDefinitionImportDetail> parseRequests(OpenAPI openAPI, ImportRequest importRequest) {
|
||||||
|
|
||||||
Paths paths = openAPI.getPaths();
|
Paths paths = openAPI.getPaths();
|
||||||
|
@ -133,9 +113,9 @@ public class Swagger3Parser<T> implements ImportParser<ApiDefinitionImport> {
|
||||||
Operation operation = operationsMap.get(method);
|
Operation operation = operationsMap.get(method);
|
||||||
if (operation != null) {
|
if (operation != null) {
|
||||||
//构建基本请求
|
//构建基本请求
|
||||||
ApiDefinitionImportDetail apiDefinitionDTO = buildApiDefinition(operation, pathName, method, importRequest);
|
ApiDefinitionImportDetail apiDefinitionDTO = buildSwaggerApiDefinition(operation, pathName, method, importRequest);
|
||||||
//构建请求参数
|
//构建请求参数
|
||||||
MsHTTPElement request = buildRequest(apiDefinitionDTO.getName(), pathName, method);
|
MsHTTPElement request = buildHttpRequest(apiDefinitionDTO.getName(), pathName, method);
|
||||||
parseParameters(operation, request);
|
parseParameters(operation, request);
|
||||||
parseParameters(pathItem, request);
|
parseParameters(pathItem, request);
|
||||||
//构建请求体
|
//构建请求体
|
||||||
|
@ -330,7 +310,7 @@ public class Swagger3Parser<T> implements ImportParser<ApiDefinitionImport> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ApiDefinitionImportDetail buildApiDefinition(Operation operation, String path, String
|
private ApiDefinitionImportDetail buildSwaggerApiDefinition(Operation operation, String path, String
|
||||||
method, ImportRequest importRequest) {
|
method, ImportRequest importRequest) {
|
||||||
String name;
|
String name;
|
||||||
if (StringUtils.isNotBlank(operation.getSummary())) {
|
if (StringUtils.isNotBlank(operation.getSummary())) {
|
||||||
|
@ -340,44 +320,8 @@ public class Swagger3Parser<T> implements ImportParser<ApiDefinitionImport> {
|
||||||
} else {
|
} else {
|
||||||
name = path;
|
name = path;
|
||||||
}
|
}
|
||||||
ApiDefinitionImportDetail apiDefinition = new ApiDefinitionImportDetail();
|
String modulePath = CollectionUtils.isNotEmpty(operation.getTags()) ? StringUtils.join("/", operation.getTags().get(0)) : StringUtils.EMPTY;
|
||||||
apiDefinition.setName(name);
|
return buildApiDefinition(name, path, method, modulePath, importRequest);
|
||||||
apiDefinition.setPath(formatPath(path));
|
|
||||||
apiDefinition.setProtocol(ApiConstants.HTTP_PROTOCOL);
|
|
||||||
apiDefinition.setMethod(method);
|
|
||||||
apiDefinition.setProjectId(this.projectId);
|
|
||||||
apiDefinition.setCreateUser(importRequest.getUserId());
|
|
||||||
apiDefinition.setModulePath(CollectionUtils.isNotEmpty(operation.getTags()) ? StringUtils.join("/", operation.getTags().get(0)) : StringUtils.EMPTY);
|
|
||||||
apiDefinition.setResponse(new ArrayList<>());
|
|
||||||
return apiDefinition;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected MsHTTPElement buildRequest(String name, String path, String method) {
|
|
||||||
MsHTTPElement request = new MsHTTPElement();
|
|
||||||
request.setName(name);
|
|
||||||
// 路径去掉域名/IP 地址,保留方法名称及参数
|
|
||||||
request.setPath(formatPath(path));
|
|
||||||
request.setMethod(method);
|
|
||||||
request.setHeaders(new ArrayList<>());
|
|
||||||
request.setQuery(new ArrayList<>());
|
|
||||||
request.setRest(new ArrayList<>());
|
|
||||||
request.setBody(new Body());
|
|
||||||
MsHTTPConfig httpConfig = new MsHTTPConfig();
|
|
||||||
httpConfig.setConnectTimeout(60000L);
|
|
||||||
httpConfig.setResponseTimeout(60000L);
|
|
||||||
request.setOtherConfig(httpConfig);
|
|
||||||
request.setAuthConfig(new NoAuth());
|
|
||||||
Body body = new Body();
|
|
||||||
body.setBinaryBody(new BinaryBody());
|
|
||||||
body.setFormDataBody(new FormDataBody());
|
|
||||||
body.setXmlBody(new XmlBody());
|
|
||||||
body.setRawBody(new RawBody());
|
|
||||||
body.setNoneBody(new NoneBody());
|
|
||||||
body.setJsonBody(new JsonBody());
|
|
||||||
body.setWwwFormBody(new WWWFormBody());
|
|
||||||
body.setNoneBody(new NoneBody());
|
|
||||||
request.setBody(body);
|
|
||||||
return request;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void parseParameters(Operation operation, MsHTTPElement request) {
|
private void parseParameters(Operation operation, MsHTTPElement request) {
|
||||||
|
@ -754,17 +698,4 @@ public class Swagger3Parser<T> implements ImportParser<ApiDefinitionImport> {
|
||||||
return jsonSchemaArray;
|
return jsonSchemaArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String formatPath(String url) {
|
|
||||||
try {
|
|
||||||
URI urlObject = new URI(url);
|
|
||||||
String path = StringUtils.isBlank(urlObject.getPath()) ? url : urlObject.getPath();
|
|
||||||
StringBuilder pathBuffer = new StringBuilder(path);
|
|
||||||
if (StringUtils.isNotEmpty(urlObject.getQuery())) {
|
|
||||||
pathBuffer.append("?").append(urlObject.getQuery());
|
|
||||||
}
|
|
||||||
return pathBuffer.toString();
|
|
||||||
} catch (Exception ex) {
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
package io.metersphere.api.parser.api.postman;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class PostmanCollection {
|
||||||
|
|
||||||
|
private PostmanCollectionInfo info;
|
||||||
|
private List<PostmanItem> item;
|
||||||
|
private List<PostmanKeyValue> variable;
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package io.metersphere.api.parser.api.postman;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class PostmanCollectionInfo {
|
||||||
|
private String postmanId;
|
||||||
|
private String name;
|
||||||
|
private String schema;
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package io.metersphere.api.parser.api.postman;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class PostmanItem {
|
||||||
|
private String name;
|
||||||
|
private PostmanRequest request;
|
||||||
|
private List<PostmanResponse> response;
|
||||||
|
private List<PostmanItem> item;
|
||||||
|
private ProtocolProfileBehavior protocolProfileBehavior;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ProtocolProfileBehavior {
|
||||||
|
private Boolean followRedirects = true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package io.metersphere.api.parser.api.postman;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class PostmanKeyValue {
|
||||||
|
private String key;
|
||||||
|
private String value;
|
||||||
|
private String type;
|
||||||
|
private JsonNode description;
|
||||||
|
private String contentType;
|
||||||
|
private boolean disabled;
|
||||||
|
|
||||||
|
public PostmanKeyValue() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public PostmanKeyValue(String key, String value) {
|
||||||
|
this.key = key;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package io.metersphere.api.parser.api.postman;
|
||||||
|
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class PostmanRequest {
|
||||||
|
|
||||||
|
private String method;
|
||||||
|
private String schema;
|
||||||
|
private List<PostmanKeyValue> header;
|
||||||
|
private JsonNode body;
|
||||||
|
private JsonNode auth;
|
||||||
|
private JsonNode url;
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package io.metersphere.api.parser.api.postman;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class PostmanResponse {
|
||||||
|
|
||||||
|
private Integer code;
|
||||||
|
private String name;
|
||||||
|
private String status;
|
||||||
|
private List<PostmanKeyValue> header;
|
||||||
|
private JsonNode body;
|
||||||
|
}
|
|
@ -504,12 +504,15 @@ public class ApiDefinitionImportUtilService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断数据唯一性 通过method和path判断 有重复的数据 需要覆盖
|
||||||
|
*/
|
||||||
public void methodAndPath(List<ApiDefinitionImportDetail> importData,
|
public void methodAndPath(List<ApiDefinitionImportDetail> importData,
|
||||||
List<ApiDefinitionImportDetail> lists,
|
List<ApiDefinitionImportDetail> lists,
|
||||||
ApiDetailWithData apiDetailWithData) {
|
ApiDetailWithData apiDetailWithData) {
|
||||||
|
|
||||||
Map<String, ApiDefinitionImportDetail> apiDateMap = lists.stream().collect(Collectors.toMap(t -> t.getMethod() + t.getPath(), t -> t));
|
Map<String, ApiDefinitionImportDetail> apiDateMap = lists.stream().collect(Collectors.toMap(t -> t.getMethod() + t.getPath(), t -> t, (oldValue, newValue) -> newValue));
|
||||||
Map<String, ApiDefinitionImportDetail> importDataMap = importData.stream().collect(Collectors.toMap(t -> t.getMethod() + t.getPath(), t -> t));
|
Map<String, ApiDefinitionImportDetail> importDataMap = importData.stream().collect(Collectors.toMap(t -> t.getMethod() + t.getPath(), t -> t, (oldValue, newValue) -> newValue));
|
||||||
//判断是否重复
|
//判断是否重复
|
||||||
List<String> orgList = apiDateMap.keySet().stream().toList();
|
List<String> orgList = apiDateMap.keySet().stream().toList();
|
||||||
List<String> importList = importDataMap.keySet().stream().toList();
|
List<String> importList = importDataMap.keySet().stream().toList();
|
||||||
|
|
|
@ -5,10 +5,12 @@ import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||||
import com.fasterxml.jackson.core.JsonParser;
|
import com.fasterxml.jackson.core.JsonParser;
|
||||||
import com.fasterxml.jackson.core.json.JsonReadFeature;
|
import com.fasterxml.jackson.core.json.JsonReadFeature;
|
||||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
import com.fasterxml.jackson.databind.json.JsonMapper;
|
import com.fasterxml.jackson.databind.json.JsonMapper;
|
||||||
import com.fasterxml.jackson.databind.jsontype.NamedType;
|
import com.fasterxml.jackson.databind.jsontype.NamedType;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import com.fasterxml.jackson.databind.type.CollectionType;
|
import com.fasterxml.jackson.databind.type.CollectionType;
|
||||||
import io.metersphere.api.dto.request.MsCommonElement;
|
import io.metersphere.api.dto.request.MsCommonElement;
|
||||||
import io.metersphere.api.dto.request.controller.*;
|
import io.metersphere.api.dto.request.controller.*;
|
||||||
|
@ -53,6 +55,8 @@ public class ApiDataUtils {
|
||||||
// 如果一个对象中没有任何的属性,那么在序列化的时候就会报错
|
// 如果一个对象中没有任何的属性,那么在序列化的时候就会报错
|
||||||
objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
|
objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
|
||||||
objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
|
objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
|
||||||
|
// 设置默认使用 BigDecimal 解析浮点数
|
||||||
|
objectMapper.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String toJSONString(Object value) {
|
public static String toJSONString(Object value) {
|
||||||
|
@ -133,4 +137,32 @@ public class ApiDataUtils {
|
||||||
// 加入新的动态组件
|
// 加入新的动态组件
|
||||||
clazzList.forEach(objectMapper::registerSubtypes);
|
clazzList.forEach(objectMapper::registerSubtypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static JsonNode readTree(String content) {
|
||||||
|
try {
|
||||||
|
return objectMapper.readTree(content);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new MSException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String writerWithDefaultPrettyPrinter(Object value) {
|
||||||
|
try {
|
||||||
|
return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(value);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new MSException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ObjectNode createObjectNode() {
|
||||||
|
return objectMapper.createObjectNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T convertValue(JsonNode jsonNode, Class<T> valueType) {
|
||||||
|
try {
|
||||||
|
return objectMapper.convertValue(jsonNode, valueType);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new MSException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,6 @@
|
||||||
package io.metersphere.api.utils;
|
package io.metersphere.api.utils;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonParser;
|
|
||||||
import com.fasterxml.jackson.core.json.JsonReadFeature;
|
|
||||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
|
||||||
import com.fasterxml.jackson.databind.json.JsonMapper;
|
|
||||||
import com.fasterxml.jackson.databind.node.*;
|
import com.fasterxml.jackson.databind.node.*;
|
||||||
import io.metersphere.jmeter.mock.Mock;
|
import io.metersphere.jmeter.mock.Mock;
|
||||||
import io.metersphere.project.constants.PropertyConstant;
|
import io.metersphere.project.constants.PropertyConstant;
|
||||||
|
@ -22,29 +16,14 @@ import java.util.regex.Pattern;
|
||||||
|
|
||||||
public class JsonSchemaBuilder {
|
public class JsonSchemaBuilder {
|
||||||
|
|
||||||
private static final ObjectMapper objectMapper = JsonMapper.builder()
|
|
||||||
.enable(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
static {
|
|
||||||
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
|
||||||
// 支持json字符中带注释符
|
|
||||||
objectMapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
|
|
||||||
// 如果一个对象中没有任何的属性,那么在序列化的时候就会报错
|
|
||||||
objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
|
|
||||||
objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
|
|
||||||
// 设置默认使用 BigDecimal 解析浮点数
|
|
||||||
objectMapper.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String jsonSchemaToJson(String jsonSchemaString) {
|
public static String jsonSchemaToJson(String jsonSchemaString) {
|
||||||
try {
|
try {
|
||||||
// 解析 JSON Schema 字符串为 JsonNode
|
// 解析 JSON Schema 字符串为 JsonNode
|
||||||
JsonNode jsonSchemaNode = objectMapper.readTree(jsonSchemaString);
|
JsonNode jsonSchemaNode = ApiDataUtils.readTree(jsonSchemaString);
|
||||||
Map<String, String> processMap = new HashMap<>();
|
Map<String, String> processMap = new HashMap<>();
|
||||||
// 生成符合 JSON Schema 的 JSON
|
// 生成符合 JSON Schema 的 JSON
|
||||||
JsonNode jsonNode = generateJson(jsonSchemaNode, processMap);
|
JsonNode jsonNode = generateJson(jsonSchemaNode, processMap);
|
||||||
String jsonString = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonNode);
|
String jsonString = ApiDataUtils.writerWithDefaultPrettyPrinter(jsonNode);
|
||||||
if (MapUtils.isNotEmpty(processMap)) {
|
if (MapUtils.isNotEmpty(processMap)) {
|
||||||
for (String str : processMap.keySet()) {
|
for (String str : processMap.keySet()) {
|
||||||
jsonString = jsonString.replace(str, processMap.get(str));
|
jsonString = jsonString.replace(str, processMap.get(str));
|
||||||
|
@ -58,7 +37,7 @@ public class JsonSchemaBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static JsonNode generateJson(JsonNode jsonSchemaNode, Map<String, String> processMap) {
|
private static JsonNode generateJson(JsonNode jsonSchemaNode, Map<String, String> processMap) {
|
||||||
ObjectNode jsonNode = objectMapper.createObjectNode();
|
ObjectNode jsonNode = ApiDataUtils.createObjectNode();
|
||||||
|
|
||||||
if (jsonSchemaNode instanceof NullNode) {
|
if (jsonSchemaNode instanceof NullNode) {
|
||||||
return NullNode.getInstance();
|
return NullNode.getInstance();
|
||||||
|
|
|
@ -3,6 +3,7 @@ package io.metersphere.api.controller;
|
||||||
import io.metersphere.api.constants.ApiConstants;
|
import io.metersphere.api.constants.ApiConstants;
|
||||||
import io.metersphere.api.constants.ApiDefinitionDocType;
|
import io.metersphere.api.constants.ApiDefinitionDocType;
|
||||||
import io.metersphere.api.constants.ApiDefinitionStatus;
|
import io.metersphere.api.constants.ApiDefinitionStatus;
|
||||||
|
import io.metersphere.api.constants.ApiImportPlatform;
|
||||||
import io.metersphere.api.controller.result.ApiResultCode;
|
import io.metersphere.api.controller.result.ApiResultCode;
|
||||||
import io.metersphere.api.domain.*;
|
import io.metersphere.api.domain.*;
|
||||||
import io.metersphere.api.dto.ApiFile;
|
import io.metersphere.api.dto.ApiFile;
|
||||||
|
@ -1587,6 +1588,29 @@ public class ApiDefinitionControllerTests extends BaseTest {
|
||||||
paramMap.add("request", JSON.toJSONString(request));
|
paramMap.add("request", JSON.toJSONString(request));
|
||||||
this.requestMultipart(IMPORT, paramMap, status().is5xxServerError());
|
this.requestMultipart(IMPORT, paramMap, status().is5xxServerError());
|
||||||
|
|
||||||
|
request.setPlatform(ApiImportPlatform.Postman.name());
|
||||||
|
request.setCoverModule(true);
|
||||||
|
request.setCoverData(true);
|
||||||
|
paramMap.clear();
|
||||||
|
inputStream = new FileInputStream(new File(
|
||||||
|
Objects.requireNonNull(this.getClass().getClassLoader().getResource("file/postman.json"))
|
||||||
|
.getPath()));
|
||||||
|
file = new MockMultipartFile("file", "postman.json", MediaType.APPLICATION_OCTET_STREAM_VALUE, inputStream);
|
||||||
|
paramMap.add("file", file);
|
||||||
|
paramMap.add("request", JSON.toJSONString(request));
|
||||||
|
this.requestMultipartWithOkAndReturn(IMPORT, paramMap);
|
||||||
|
paramMap.clear();
|
||||||
|
request.setCoverModule(true);
|
||||||
|
request.setCoverData(true);
|
||||||
|
inputStream = new FileInputStream(new File(
|
||||||
|
Objects.requireNonNull(this.getClass().getClassLoader().getResource("file/postman2.json"))
|
||||||
|
.getPath()));
|
||||||
|
file = new MockMultipartFile("file", "postman2.json", MediaType.APPLICATION_OCTET_STREAM_VALUE, inputStream);
|
||||||
|
paramMap.add("file", file);
|
||||||
|
paramMap.add("request", JSON.toJSONString(request));
|
||||||
|
this.requestMultipartWithOkAndReturn(IMPORT, paramMap);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected MvcResult requestMultipart(String url, MultiValueMap<String, Object> paramMap, ResultMatcher resultMatcher) throws Exception {
|
protected MvcResult requestMultipart(String url, MultiValueMap<String, Object> paramMap, ResultMatcher resultMatcher) throws Exception {
|
||||||
|
|
|
@ -8,20 +8,12 @@ import org.junit.jupiter.api.TestMethodOrder;
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
|
|
||||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||||
@AutoConfigureMockMvc
|
@AutoConfigureMockMvc
|
||||||
public class ParserTests {
|
public class ParserTests {
|
||||||
|
|
||||||
@Test
|
|
||||||
@Order(2)
|
|
||||||
public void testImportParserPostman() throws Exception {
|
|
||||||
Objects.requireNonNull(ImportParserFactory.getImportParser(ApiImportPlatform.Postman.name())).parse(null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Order(3)
|
@Order(3)
|
||||||
public void testImportParserMs() throws Exception {
|
public void testImportParserMs() throws Exception {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue