feat(接口测试): jmx导入

This commit is contained in:
Jianguo-Genius 2024-08-27 14:58:47 +08:00 committed by Craftsman
parent 3710ed3370
commit 68276ef05d
45 changed files with 2366 additions and 142 deletions

View File

@ -123,6 +123,7 @@ module_not_null=所属模块不能为空格
user_not_exists=该项目下无该用户 user_not_exists=该项目下无该用户
test_case_already_exists=该项目下已存在该测试用例 test_case_already_exists=该项目下已存在该测试用例
parse_data_error=解析数据出错 parse_data_error=解析数据出错
parse_empty_data=未解析到数据
missing_header_information=缺少头部信息 missing_header_information=缺少头部信息
test_case_exist=该项目下已存在用例: test_case_exist=该项目下已存在用例:
node_deep_limit=节点深度不超过8层 node_deep_limit=节点深度不超过8层

View File

@ -123,6 +123,7 @@ module_starts_with=The module must start with '/'
user_not_exists=The user in this project is not exists user_not_exists=The user in this project is not exists
test_case_already_exists=The test case in this project is exists test_case_already_exists=The test case in this project is exists
parse_data_error=Parse data error parse_data_error=Parse data error
parse_empty_data=Parse empty data
missing_header_information=Missing header information missing_header_information=Missing header information
test_case_exist=A test case already exists under this project: test_case_exist=A test case already exists under this project:
node_deep_limit=The node depth does not exceed 8 layers! node_deep_limit=The node depth does not exceed 8 layers!

View File

@ -124,6 +124,7 @@ module_starts_with=所属模块必须以'/'开始
user_not_exists=该项目下无该用户 user_not_exists=该项目下无该用户
test_case_already_exists=该项目下已存在该测试用例 test_case_already_exists=该项目下已存在该测试用例
parse_data_error=解析数据出错 parse_data_error=解析数据出错
parse_empty_data=未解析到数据
missing_header_information=缺少头部信息 missing_header_information=缺少头部信息
test_case_exist=该项目下已存在用例: test_case_exist=该项目下已存在用例:
node_deep_limit=节点深度不超过8层 node_deep_limit=节点深度不超过8层

View File

@ -124,6 +124,7 @@ module_starts_with=所屬模塊必須以'/'開始
user_not_exists=該項目下無該用戶 user_not_exists=該項目下無該用戶
test_case_already_exists=該項目下已存在該測試用例 test_case_already_exists=該項目下已存在該測試用例
parse_data_error=解析數據出錯 parse_data_error=解析數據出錯
parse_empty_data=未解析到数据
missing_header_information=缺少頭部信息 missing_header_information=缺少頭部信息
test_case_exist=該項目下已存在用例: test_case_exist=該項目下已存在用例:
node_deep_limit=節點深度不超過8層 node_deep_limit=節點深度不超過8層

View File

@ -312,7 +312,7 @@ public class ApiDefinitionController {
@PostMapping(value = "/export/{type}") @PostMapping(value = "/export/{type}")
@Operation(summary = "接口测试-接口管理-导出接口定义") @Operation(summary = "接口测试-接口管理-导出接口定义")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_EXPORT) @RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_EXPORT)
public ApiExportResponse export(@RequestBody ApiDefinitionBatchRequest request, @PathVariable String type) { public ApiExportResponse export(@RequestBody ApiDefinitionBatchExportRequest request, @PathVariable String type) {
return apiDefinitionExportService.export(request, type, SessionUtils.getUserId()); return apiDefinitionExportService.export(request, type, SessionUtils.getUserId());
} }
} }

View File

@ -3,6 +3,7 @@ package io.metersphere.api.dto.converter;
import io.metersphere.api.dto.definition.ApiDefinitionMockDTO; import io.metersphere.api.dto.definition.ApiDefinitionMockDTO;
import io.metersphere.api.dto.definition.ApiTestCaseDTO; import io.metersphere.api.dto.definition.ApiTestCaseDTO;
import lombok.Data; import lombok.Data;
import org.apache.commons.collections4.CollectionUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@ -17,7 +18,7 @@ public class ApiImportDataAnalysisResult {
// 新增接口数据 // 新增接口数据
List<ApiDefinitionDetail> insertApiList = new ArrayList<>(); List<ApiDefinitionDetail> insertApiList = new ArrayList<>();
// 存在的接口数据. Map<导入的接口 , 已存在的接口> // 存在的接口数据
List<ExistenceApiDefinitionDetail> existenceApiList = new ArrayList<>(); List<ExistenceApiDefinitionDetail> existenceApiList = new ArrayList<>();
// 接口的用例数据 // 接口的用例数据
Map<String, List<ApiTestCaseDTO>> apiIdAndTestCaseMap = new HashMap<>(); Map<String, List<ApiTestCaseDTO>> apiIdAndTestCaseMap = new HashMap<>();
@ -27,4 +28,8 @@ public class ApiImportDataAnalysisResult {
public void addExistenceApi(ApiDefinitionDetail importApi, ApiDefinitionDetail exportApi) { public void addExistenceApi(ApiDefinitionDetail importApi, ApiDefinitionDetail exportApi) {
this.existenceApiList.add(new ExistenceApiDefinitionDetail(importApi, exportApi)); this.existenceApiList.add(new ExistenceApiDefinitionDetail(importApi, exportApi));
} }
public boolean isEmpty() {
return CollectionUtils.isEmpty(insertApiList) && CollectionUtils.isEmpty(existenceApiList);
}
} }

View File

@ -20,4 +20,14 @@ public class ApiImportFileParseResult {
private Map<String, List<ApiTestCaseDTO>> caseMap = new HashMap<>(); private Map<String, List<ApiTestCaseDTO>> caseMap = new HashMap<>();
// mock数据 // mock数据
private Map<String, List<ApiDefinitionMockDTO>> mockMap = new HashMap<>(); private Map<String, List<ApiDefinitionMockDTO>> mockMap = new HashMap<>();
public List<String> getApiProtocols() {
List<String> protocols = new ArrayList<>();
for (ApiDefinitionDetail apiDefinitionDetail : data) {
if (!protocols.contains(apiDefinitionDetail.getProtocol())) {
protocols.add(apiDefinitionDetail.getProtocol());
}
}
return protocols;
}
} }

View File

@ -0,0 +1,50 @@
package io.metersphere.api.dto.definition;
import com.google.common.base.CaseFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang3.StringUtils;
import java.io.Serial;
import java.io.Serializable;
import java.util.Map;
/**
* @author lan
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class ApiDefinitionBatchExportRequest extends ApiDefinitionBatchRequest implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@Schema(description = "是否同步导出接口用例")
private boolean exportApiCase;
@Schema(description = "是否同步导出接口Mock")
private boolean exportApiMock;
@Schema(description = "排序字段model中的字段 : asc/desc")
private Map<@Valid @Pattern(regexp = "^[A-Za-z]+$") String, @Valid @NotBlank String> sort;
public String getSortString() {
if (sort == null || sort.isEmpty()) {
return null;
}
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> entry : sort.entrySet()) {
String column = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, entry.getKey());
sb.append(column)
.append(StringUtils.SPACE)
.append(StringUtils.equalsIgnoreCase(entry.getValue(), "DESC") ? "DESC" : "ASC")
.append(",");
}
return sb.substring(0, sb.length() - 1);
}
}

View File

@ -32,10 +32,4 @@ public class ApiDefinitionBatchRequest extends TableBatchProcessDTO implements S
@Schema(description = "模块ID(根据模块树查询时要把当前节点以及子节点都放在这里。)") @Schema(description = "模块ID(根据模块树查询时要把当前节点以及子节点都放在这里。)")
private List<@NotBlank String> moduleIds; private List<@NotBlank String> moduleIds;
@Schema(description = "是否同步导出接口用例")
private boolean exportApiCase;
@Schema(description = "是否同步导出接口Mock")
private boolean exportApiMock;
} }

View File

@ -30,6 +30,8 @@ public interface ExtApiDefinitionMapper {
List<String> getIds(@Param("request") TableBatchProcessDTO request, @Param("projectId") String projectId, @Param("protocols") List<String> protocols, @Param("deleted") boolean deleted); List<String> getIds(@Param("request") TableBatchProcessDTO request, @Param("projectId") String projectId, @Param("protocols") List<String> protocols, @Param("deleted") boolean deleted);
List<String> getIdsBySort(@Param("request") TableBatchProcessDTO request, @Param("projectId") String projectId, @Param("protocols") List<String> protocols, @Param("orderColumns") String orderColumns);
List<String> getRefIds(@Param("ids") List<String> ids, @Param("deleted") boolean deleted); List<String> getRefIds(@Param("ids") List<String> ids, @Param("deleted") boolean deleted);
List<String> getIdsByRefId(@Param("refIds") List<String> refIds, @Param("deleted") boolean deleted); List<String> getIdsByRefId(@Param("refIds") List<String> refIds, @Param("deleted") boolean deleted);

View File

@ -89,6 +89,28 @@
<include refid="queryWhereConditionByBaseQueryRequest"/> <include refid="queryWhereConditionByBaseQueryRequest"/>
</select> </select>
<select id="getIdsBySort" resultType="java.lang.String">
SELECT id
FROM api_definition
where project_id = #{projectId} and deleted is false
<if test="protocols != null and protocols.size() > 0">
AND protocol in
<foreach collection="protocols" item="protocol" separator="," open="(" close=")">
#{protocol}
</foreach>
</if>
<if test="request.moduleIds != null and request.moduleIds.size() != 0">
AND module_id IN
<foreach collection="request.moduleIds" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<include refid="queryWhereConditionByBaseQueryRequest"/>
<if test="orderColumns != null">
ORDER BY #{orderColumns}
</if>
</select>
<select id="getRefIds" resultType="java.lang.String"> <select id="getRefIds" resultType="java.lang.String">
SELECT SELECT

View File

@ -30,8 +30,4 @@ public interface ApiDefinitionImportParser<T> {
*/ */
ApiImportDataAnalysisResult generateInsertAndUpdateData(ApiImportFileParseResult importParser, List<ApiDefinitionDetail> existenceApiDefinitionList); ApiImportDataAnalysisResult generateInsertAndUpdateData(ApiImportFileParseResult importParser, List<ApiDefinitionDetail> existenceApiDefinitionList);
/**
* 获取解析协议类型
*/
String getParseProtocol();
} }

View File

@ -1,10 +1,7 @@
package io.metersphere.api.parser; package io.metersphere.api.parser;
import io.metersphere.api.constants.ApiImportPlatform; import io.metersphere.api.constants.ApiImportPlatform;
import io.metersphere.api.parser.api.HarParserApiDefinition; import io.metersphere.api.parser.api.*;
import io.metersphere.api.parser.api.MetersphereParserApiDefinition;
import io.metersphere.api.parser.api.PostmanParserApiDefinition;
import io.metersphere.api.parser.api.Swagger3ParserApiDefinition;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
public class ImportParserFactory { public class ImportParserFactory {
@ -17,6 +14,8 @@ public class ImportParserFactory {
return new MetersphereParserApiDefinition(); return new MetersphereParserApiDefinition();
} else if (StringUtils.equalsIgnoreCase(ApiImportPlatform.Har.name(), platform)) { } else if (StringUtils.equalsIgnoreCase(ApiImportPlatform.Har.name(), platform)) {
return new HarParserApiDefinition(); return new HarParserApiDefinition();
} else if (StringUtils.equalsIgnoreCase(ApiImportPlatform.Jmeter.name(), platform)) {
return new JmeterParserApiDefinition();
} }
return null; return null;
} }

View File

@ -270,34 +270,7 @@ public class HarParserApiDefinition extends HttpApiDefinitionImportAbstractParse
return resultList; return resultList;
} }
// private void addBodyHeader(MsHTTPElement request) {
// String contentType = StringUtils.EMPTY;
// if (request.getBody() != null && StringUtils.isNotBlank(request.getBody().getType())) {
// switch (request.getBody().getType()) {
// case Body.WWW_FROM:
// contentType = "application/x-www-form-urlencoded";
// break;
// case Body.JSON_STR:
// contentType = "application/json";
// break;
// case Body.XML:
// contentType = "application/xml";
// break;
// case Body.BINARY:
// contentType = "application/octet-stream";
// break;
// }
// List<KeyValue> headers = request.getHeaders();
// if (headers == null) {
// headers = new ArrayList<>();
// request.setHeaders(headers);
// }
// if (StringUtils.isNotEmpty(contentType)) {
// addContentType(request.getHeaders(), contentType);
// }
// }
// }
private void parseParameters(HarRequest harRequest, MsHTTPElement request) { private void parseParameters(HarRequest harRequest, MsHTTPElement request) {
List<HarQueryParam> queryStringList = harRequest.queryString; List<HarQueryParam> queryStringList = harRequest.queryString;
queryStringList.forEach(harQueryParam -> { queryStringList.forEach(harQueryParam -> {

View File

@ -12,9 +12,9 @@ import io.metersphere.api.dto.request.http.MsHTTPElement;
import io.metersphere.api.dto.request.http.body.*; import io.metersphere.api.dto.request.http.body.*;
import io.metersphere.api.parser.ApiDefinitionImportParser; import io.metersphere.api.parser.ApiDefinitionImportParser;
import io.metersphere.project.dto.environment.auth.NoAuth; import io.metersphere.project.dto.environment.auth.NoAuth;
import io.metersphere.project.dto.environment.http.HttpConfig;
import io.metersphere.sdk.exception.MSException; import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.LogUtils; import io.metersphere.sdk.util.LogUtils;
import io.metersphere.system.uid.IDGenerator;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -30,11 +30,6 @@ import java.util.stream.Collectors;
public abstract class HttpApiDefinitionImportAbstractParser<T> implements ApiDefinitionImportParser<T> { public abstract class HttpApiDefinitionImportAbstractParser<T> implements ApiDefinitionImportParser<T> {
@Override
public String getParseProtocol() {
return HttpConfig.HttpProtocolType.HTTP.name();
}
@Override @Override
public ApiImportDataAnalysisResult generateInsertAndUpdateData(ApiImportFileParseResult importParser, List<ApiDefinitionDetail> existenceApiDefinitionList) { public ApiImportDataAnalysisResult generateInsertAndUpdateData(ApiImportFileParseResult importParser, List<ApiDefinitionDetail> existenceApiDefinitionList) {
// API类型通过 Method & Path 组合判断接口是否存在 // API类型通过 Method & Path 组合判断接口是否存在
@ -62,16 +57,6 @@ public abstract class HttpApiDefinitionImportAbstractParser<T> implements ApiDef
return insertAndUpdateData; return insertAndUpdateData;
} }
public String getUniqueName(String originalName, List<String> existenceNameList) {
String returnName = originalName;
int index = 1;
while (existenceNameList.contains(returnName)) {
returnName = originalName + " - " + index;
index++;
}
return returnName;
}
protected String getApiTestStr(InputStream source) { protected String getApiTestStr(InputStream source) {
StringBuilder testStr = null; StringBuilder testStr = null;
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(source, StandardCharsets.UTF_8))) { try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(source, StandardCharsets.UTF_8))) {
@ -91,6 +76,7 @@ public abstract class HttpApiDefinitionImportAbstractParser<T> implements ApiDef
protected ApiDefinitionDetail buildApiDefinition(String name, String path, String method, String modulePath, ImportRequest importRequest) { protected ApiDefinitionDetail buildApiDefinition(String name, String path, String method, String modulePath, ImportRequest importRequest) {
ApiDefinitionDetail apiDefinition = new ApiDefinitionDetail(); ApiDefinitionDetail apiDefinition = new ApiDefinitionDetail();
apiDefinition.setId(IDGenerator.nextStr());
apiDefinition.setName(name); apiDefinition.setName(name);
apiDefinition.setPath(formatPath(path)); apiDefinition.setPath(formatPath(path));
apiDefinition.setProtocol(importRequest.getProtocol()); apiDefinition.setProtocol(importRequest.getProtocol());

View File

@ -0,0 +1,194 @@
package io.metersphere.api.parser.api;
import io.metersphere.api.constants.ApiConstants;
import io.metersphere.api.domain.ApiDefinition;
import io.metersphere.api.dto.converter.ApiDefinitionDetail;
import io.metersphere.api.dto.converter.ApiImportDataAnalysisResult;
import io.metersphere.api.dto.converter.ApiImportFileParseResult;
import io.metersphere.api.dto.definition.ApiTestCaseDTO;
import io.metersphere.api.dto.request.ImportRequest;
import io.metersphere.api.dto.request.http.MsHTTPElement;
import io.metersphere.api.parser.ApiDefinitionImportParser;
import io.metersphere.api.parser.ms.MsTestElementParser;
import io.metersphere.api.utils.ApiDefinitionImportUtils;
import io.metersphere.plugin.api.spi.AbstractMsProtocolTestElement;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.CommonBeanFactory;
import io.metersphere.sdk.util.LogUtils;
import io.metersphere.system.dto.ProtocolDTO;
import io.metersphere.system.service.ApiPluginService;
import io.metersphere.system.uid.IDGenerator;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.save.SaveService;
import org.apache.jorphan.collections.HashTree;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;
public class JmeterParserApiDefinition implements ApiDefinitionImportParser<ApiImportFileParseResult> {
@Override
public ApiImportFileParseResult parse(InputStream inputSource, ImportRequest request) throws Exception {
try {
Object scriptWrapper = SaveService.loadElement(inputSource);
HashTree hashTree = this.getHashTree(scriptWrapper);
MsTestElementParser parser = new MsTestElementParser();
AbstractMsTestElement msTestElement = parser.parse(hashTree);
List<AbstractMsProtocolTestElement> msElement = parser.getAbstractMsProtocolTestElement(msTestElement);
LinkedHashMap<ApiDefinitionDetail, List<ApiTestCaseDTO>> allImportDetails = this.parseImportFile(request.getProjectId(), msElement);
return this.genApiDefinitionImport(allImportDetails);
} catch (Exception e) {
LogUtils.error(e);
throw new MSException("当前JMX版本不兼容");
}
}
private ApiImportFileParseResult genApiDefinitionImport(LinkedHashMap<ApiDefinitionDetail, List<ApiTestCaseDTO>> allImportDetails) {
Map<ApiDefinitionDetail, List<ApiTestCaseDTO>> groupWithUniqueIdentification = this.mergeApiCaseWithUniqueIdentification(allImportDetails);
ApiImportFileParseResult returnDTO = new ApiImportFileParseResult();
groupWithUniqueIdentification.forEach((definitionImportDetail, caseData) -> {
String apiID = IDGenerator.nextStr();
definitionImportDetail.setId(apiID);
returnDTO.getData().add(definitionImportDetail);
caseData.forEach(item -> {
item.setId(IDGenerator.nextStr());
item.setApiDefinitionId(apiID);
});
if (CollectionUtils.isNotEmpty(caseData)) {
returnDTO.getCaseMap().put(apiID, caseData);
}
});
return returnDTO;
}
private Map<ApiDefinitionDetail, List<ApiTestCaseDTO>> mergeApiCaseWithUniqueIdentification(LinkedHashMap<ApiDefinitionDetail, List<ApiTestCaseDTO>> allImportDetails) {
Map<ApiDefinitionDetail, List<ApiTestCaseDTO>> returnMap = new HashMap<>();
Map<String, ApiDefinitionDetail> filterApiMap = new HashMap<>();
Map<String, List<ApiTestCaseDTO>> uniqueCaseMap = new HashMap<>();
allImportDetails.forEach((api, apiCase) -> {
String key = api.getMethod() + StringUtils.SPACE + api.getPath();
if (!filterApiMap.containsKey(key)) {
filterApiMap.put(key, api);
}
if (uniqueCaseMap.containsKey(key)) {
uniqueCaseMap.get(key).addAll(apiCase);
} else {
uniqueCaseMap.put(key, apiCase);
}
});
filterApiMap.forEach((key, api) -> {
returnMap.put(api, ApiDefinitionImportUtils.apiCaseRename(uniqueCaseMap.get(key)));
});
return returnMap;
}
private LinkedHashMap<ApiDefinitionDetail, List<ApiTestCaseDTO>> parseImportFile(String projectId, List<AbstractMsProtocolTestElement> msElement) {
LinkedHashMap<ApiDefinitionDetail, List<ApiTestCaseDTO>> returnMap = new LinkedHashMap<>();
ApiPluginService apiPluginService = CommonBeanFactory.getBean(ApiPluginService.class);
assert apiPluginService != null;
List<ProtocolDTO> protocolDTOList = apiPluginService.getProtocolsByProjectId(projectId);
Map<String, String> polymorphicNameMap = protocolDTOList.stream().collect(Collectors.toMap(ProtocolDTO::getPolymorphicName, ProtocolDTO::getProtocol));
for (AbstractMsProtocolTestElement protocolTestElement : msElement) {
ApiDefinitionDetail definition = new ApiDefinitionDetail();
definition.setName(protocolTestElement.getName());
if (protocolTestElement instanceof MsHTTPElement msHTTPElement) {
definition.setMethod(msHTTPElement.getMethod());
definition.setPath(msHTTPElement.getPath());
definition.setProtocol(ApiConstants.HTTP_PROTOCOL);
} else {
definition.setProtocol(polymorphicNameMap.get(protocolTestElement.getClass().getSimpleName()));
definition.setMethod(definition.getProtocol());
}
if (StringUtils.isBlank(definition.getProtocol())) {
continue;
}
definition.setRequest(protocolTestElement);
definition.setResponse(new ArrayList<>());
ApiTestCaseDTO apiTestCaseDTO = new ApiTestCaseDTO();
apiTestCaseDTO.setName(definition.getName());
apiTestCaseDTO.setPriority("P0");
apiTestCaseDTO.setStatus(definition.getStatus());
apiTestCaseDTO.setProjectId(definition.getProjectId());
apiTestCaseDTO.setFollow(false);
apiTestCaseDTO.setMethod(definition.getMethod());
apiTestCaseDTO.setPath(definition.getPath());
apiTestCaseDTO.setRequest(definition.getRequest());
apiTestCaseDTO.setProtocol(definition.getProtocol());
apiTestCaseDTO.setProjectId(definition.getProjectId());
returnMap.put(definition, new ArrayList<>(Collections.singletonList(apiTestCaseDTO)));
}
return returnMap;
}
private HashTree getHashTree(Object scriptWrapper) throws Exception {
Field field = scriptWrapper.getClass().getDeclaredField("testPlan");
field.setAccessible(true);
return (HashTree) field.get(scriptWrapper);
}
@Override
public ApiImportDataAnalysisResult generateInsertAndUpdateData(ApiImportFileParseResult importParser, List<ApiDefinitionDetail> existenceApiDefinitionList) {
List<ApiDefinitionDetail> importDataList = importParser.getData();
Map<String, List<ApiDefinitionDetail>> protocolImportMap = importDataList.stream().collect(Collectors.groupingBy(ApiDefinitionDetail::getProtocol));
Map<String, List<ApiDefinitionDetail>> existenceProtocolMap = existenceApiDefinitionList.stream().collect(Collectors.groupingBy(ApiDefinitionDetail::getProtocol));
ApiImportDataAnalysisResult insertAndUpdateData = new ApiImportDataAnalysisResult();
for (Map.Entry<String, List<ApiDefinitionDetail>> entry : protocolImportMap.entrySet()) {
List<ApiDefinitionDetail> importList = entry.getValue();
List<ApiDefinitionDetail> existenceList = existenceProtocolMap.get(entry.getKey());
ApiImportDataAnalysisResult httpResult = compareApiData(importList, existenceList, entry.getKey());
insertAndUpdateData.getInsertApiList().addAll(httpResult.getInsertApiList());
insertAndUpdateData.getExistenceApiList().addAll(httpResult.getExistenceApiList());
}
insertAndUpdateData.getApiIdAndTestCaseMap().putAll(importParser.getCaseMap());
return insertAndUpdateData;
}
private ApiImportDataAnalysisResult compareApiData(List<ApiDefinitionDetail> importData, List<ApiDefinitionDetail> existenceApiData, String protocol) {
ApiImportDataAnalysisResult insertAndUpdateData = new ApiImportDataAnalysisResult();
if (CollectionUtils.isEmpty(importData)) {
return insertAndUpdateData;
}
if (CollectionUtils.isEmpty(existenceApiData)) {
insertAndUpdateData.setInsertApiList(importData);
return insertAndUpdateData;
}
// API类型通过 Method & Path 组合判断接口是否存在
Map<String, ApiDefinitionDetail> savedApiDefinitionMap = null;
Map<String, ApiDefinitionDetail> importDataMap = null;
if (StringUtils.equalsIgnoreCase(protocol, ApiConstants.HTTP_PROTOCOL)) {
savedApiDefinitionMap = existenceApiData.stream().collect(Collectors.toMap(t -> t.getMethod() + t.getPath(), t -> t, (oldValue, newValue) -> newValue));
importDataMap = importData.stream().collect(Collectors.toMap(t -> t.getMethod() + t.getPath(), t -> t, (oldValue, newValue) -> newValue));
} else {
savedApiDefinitionMap = existenceApiData.stream().collect(Collectors.toMap(ApiDefinition::getName, t -> t, (oldValue, newValue) -> newValue));
importDataMap = importData.stream().collect(Collectors.toMap(ApiDefinition::getName, t -> t, (oldValue, newValue) -> newValue));
}
for (Map.Entry<String, ApiDefinitionDetail> entry : importDataMap.entrySet()) {
if (savedApiDefinitionMap.containsKey(entry.getKey())) {
insertAndUpdateData.addExistenceApi(entry.getValue(), savedApiDefinitionMap.get(entry.getKey()));
} else {
insertAndUpdateData.getInsertApiList().add(entry.getValue());
}
}
return insertAndUpdateData;
}
}

View File

@ -8,6 +8,7 @@ import io.metersphere.api.dto.definition.ApiTestCaseDTO;
import io.metersphere.api.dto.export.MetersphereApiExportResponse; import io.metersphere.api.dto.export.MetersphereApiExportResponse;
import io.metersphere.api.dto.request.ImportRequest; import io.metersphere.api.dto.request.ImportRequest;
import io.metersphere.api.utils.ApiDataUtils; import io.metersphere.api.utils.ApiDataUtils;
import io.metersphere.api.utils.ApiDefinitionImportUtils;
import io.metersphere.sdk.exception.MSException; import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.LogUtils; import io.metersphere.sdk.util.LogUtils;
import io.metersphere.system.uid.IDGenerator; import io.metersphere.system.uid.IDGenerator;
@ -60,44 +61,16 @@ public class MetersphereParserApiDefinition extends HttpApiDefinitionImportAbstr
} }
returnDTO.getData().add(definitionImportDetail); returnDTO.getData().add(definitionImportDetail);
if (CollectionUtils.isNotEmpty(caseList)) { if (CollectionUtils.isNotEmpty(caseList)) {
returnDTO.getCaseMap().put(apiID, this.apiCaseRename(caseList)); returnDTO.getCaseMap().put(apiID, ApiDefinitionImportUtils.apiCaseRename(caseList));
} }
if (CollectionUtils.isNotEmpty(mockList)) { if (CollectionUtils.isNotEmpty(mockList)) {
returnDTO.getMockMap().put(apiID, this.apiMockRename(mockList)); returnDTO.getMockMap().put(apiID, ApiDefinitionImportUtils.apiMockRename(mockList));
} }
}); });
return returnDTO; return returnDTO;
} }
private List<ApiTestCaseDTO> apiCaseRename(List<ApiTestCaseDTO> caseList) {
List<ApiTestCaseDTO> returnList = new ArrayList<>();
if (CollectionUtils.isNotEmpty(caseList)) {
List<String> caseNameList = new ArrayList<>();
for (ApiTestCaseDTO apiCase : caseList) {
String uniqueName = this.getUniqueName(apiCase.getName(), caseNameList);
apiCase.setName(uniqueName);
caseNameList.add(uniqueName);
returnList.add(apiCase);
}
}
return returnList;
}
private List<ApiDefinitionMockDTO> apiMockRename(List<ApiDefinitionMockDTO> caseList) {
List<ApiDefinitionMockDTO> returnList = new ArrayList<>();
if (CollectionUtils.isNotEmpty(caseList)) {
List<String> caseNameList = new ArrayList<>();
for (ApiDefinitionMockDTO apiMock : caseList) {
String uniqueName = this.getUniqueName(apiMock.getName(), caseNameList);
apiMock.setName(uniqueName);
caseNameList.add(uniqueName);
returnList.add(apiMock);
}
}
return returnList;
}
//合并相同路径下的用例和mock //合并相同路径下的用例和mock
private List<ApiDefinitionExportDetail> mergeApiCaseWithUniqueIdentification(List<ApiDefinitionExportDetail> apiDefinitions) { private List<ApiDefinitionExportDetail> mergeApiCaseWithUniqueIdentification(List<ApiDefinitionExportDetail> apiDefinitions) {
List<ApiDefinitionExportDetail> returnList = new ArrayList<>(); List<ApiDefinitionExportDetail> returnList = new ArrayList<>();

View File

@ -8,6 +8,7 @@ import io.metersphere.api.dto.definition.ApiTestCaseDTO;
import io.metersphere.api.dto.request.ImportRequest; import io.metersphere.api.dto.request.ImportRequest;
import io.metersphere.api.parser.api.postman.PostmanCollection; import io.metersphere.api.parser.api.postman.PostmanCollection;
import io.metersphere.api.parser.api.postman.PostmanItem; import io.metersphere.api.parser.api.postman.PostmanItem;
import io.metersphere.api.utils.ApiDefinitionImportUtils;
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;
@ -109,23 +110,9 @@ public class PostmanParserApiDefinition extends PostmanAbstractParserParserApiDe
} }
}); });
filterApiMap.forEach((key, api) -> { filterApiMap.forEach((key, api) -> {
returnMap.put(api, this.apiCaseRename(uniqueCaseMap.get(key))); returnMap.put(api, ApiDefinitionImportUtils.apiRename(uniqueCaseMap.get(key)));
}); });
return returnMap; return returnMap;
} }
private List<ApiDefinitionDetail> apiCaseRename(List<ApiDefinitionDetail> caseList) {
List<ApiDefinitionDetail> returnList = new ArrayList<>();
if (CollectionUtils.isNotEmpty(caseList)) {
List<String> caseNameList = new ArrayList<>();
for (ApiDefinitionDetail apiCase : caseList) {
String uniqueName = this.getUniqueName(apiCase.getName(), caseNameList);
apiCase.setName(uniqueName);
caseNameList.add(uniqueName);
returnList.add(apiCase);
}
}
return returnList;
}
} }

View File

@ -1,10 +1,30 @@
package io.metersphere.api.parser.ms; package io.metersphere.api.parser.ms;
import io.metersphere.api.dto.ApiFile;
import io.metersphere.api.dto.request.http.MsHTTPElement; 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.body.*;
import io.metersphere.plugin.api.spi.AbstractMsElementConverter; import io.metersphere.plugin.api.spi.AbstractMsElementConverter;
import io.metersphere.plugin.api.spi.AbstractMsTestElement; import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.project.api.KeyValueEnableParam;
import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.sdk.util.LogUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.protocol.http.control.HeaderManager;
import org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy; import org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy;
import org.apache.jmeter.protocol.http.util.HTTPArgument;
import org.apache.jmeter.protocol.http.util.HTTPFileArg;
import org.apache.jmeter.testelement.property.JMeterProperty;
import org.apache.jorphan.collections.HashTree; import org.apache.jorphan.collections.HashTree;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
/** /**
* @Author: jianxing * @Author: jianxing
@ -13,9 +33,151 @@ import org.apache.jorphan.collections.HashTree;
public class HTTPSamplerConverter extends AbstractMsElementConverter<HTTPSamplerProxy> { public class HTTPSamplerConverter extends AbstractMsElementConverter<HTTPSamplerProxy> {
@Override @Override
public void toMsElement(AbstractMsTestElement parent, HTTPSamplerProxy httpSampler, HashTree hashTree) { public void toMsElement(AbstractMsTestElement parent, HTTPSamplerProxy httpSampler, HashTree hashTree) {
MsHTTPElement msHTTPElement = new MsHTTPElement(); MsHTTPElement msHTTPElement = this.convertHttpSampler(httpSampler);
// todo 解析HTTP请求
parent.getChildren().add(msHTTPElement); parent.getChildren().add(msHTTPElement);
parseChild(msHTTPElement, httpSampler, hashTree); parseChild(msHTTPElement, httpSampler, hashTree);
} }
private MsHTTPElement convertHttpSampler(HTTPSamplerProxy source) {
MsHTTPElement samplerProxy = new MsHTTPElement();
samplerProxy.setQuery(new ArrayList<>());
samplerProxy.setRest(new ArrayList<>());
try {
BeanUtils.copyBean(samplerProxy, source);
// 处理HTTP协议的请求头
List<MsHeader> headerKvList = new LinkedList<>();
HeaderManager headerManager = source.getHeaderManager();
if (headerManager != null && headerManager.getHeaders() != null) {
for (int i = 0; i < headerManager.getHeaders().size(); i++) {
String headerKey = headerManager.getHeader(i).getName();
String value = headerManager.getHeader(i).getValue();
headerKvList.add(new MsHeader() {{
this.setKey(headerKey);
this.setValue(value);
}});
}
}
samplerProxy.setHeaders(headerKvList);
// 初始化body
Body body = new Body();
body.setJsonBody(new JsonBody());
body.setFormDataBody(new FormDataBody());
body.setWwwFormBody(new WWWFormBody());
body.setRawBody(new RawBody());
body.setXmlBody(new XmlBody());
samplerProxy.setBody(body);
if (source.getHTTPFiles().length > 0) {
samplerProxy.getBody().setBodyType(Body.BodyType.FORM_DATA.name());
List<FormDataKV> keyValues = new LinkedList<>();
for (HTTPFileArg arg : source.getHTTPFiles()) {
FormDataKV keyValue = getFormDataKV(arg);
keyValues.add(keyValue);
}
samplerProxy.getBody().setFormDataBody(new FormDataBody() {{
this.setFormValues(keyValues);
}});
}
samplerProxy.getOtherConfig().setConnectTimeout((long) source.getConnectTimeout());
samplerProxy.getOtherConfig().setResponseTimeout((long) source.getResponseTimeout());
samplerProxy.getOtherConfig().setFollowRedirects(source.getFollowRedirects());
samplerProxy.getOtherConfig().setAutoRedirects(source.getAutoRedirects());
if (source.getArguments() != null) {
String bodyType = this.getBodyType(samplerProxy.getHeaders());
if (source.getPostBodyRaw()) {
List<MsHeader> headers = samplerProxy.getHeaders();
boolean jsonType = false;
if (CollectionUtils.isNotEmpty(headers)) {
for (MsHeader header : headers) {
if (StringUtils.equals(header.getKey(), "Content-Type") && StringUtils.equals(header.getValue(), "application/json")) {
samplerProxy.getBody().setBodyType(Body.BodyType.JSON.name());
jsonType = true;
break;
}
}
}
if (!jsonType) {
samplerProxy.getBody().setBodyType(Body.BodyType.RAW.name());
}
source.getArguments().getArgumentsAsMap().forEach((k, v) -> samplerProxy.getBody().setRawBody(new RawBody() {{
this.setValue(v);
}}));
} else if (StringUtils.isNotEmpty(bodyType) || ("POST".equalsIgnoreCase(source.getMethod()) && source.getArguments().getArgumentsAsMap().size() > 0)) {
samplerProxy.getBody().setBodyType(Body.BodyType.WWW_FORM.name());
List<WWWFormKV> keyValues = new LinkedList<>();
source.getArguments().getArguments().forEach(params -> {
WWWFormKV keyValue = new WWWFormKV();
parseParams(params, keyValue);
keyValues.add(keyValue);
});
samplerProxy.getBody().setWwwFormBody(new WWWFormBody() {{
this.setFormValues(keyValues);
}});
} else if (samplerProxy.getBody() != null && samplerProxy.getBody().getBodyType().equals(Body.BodyType.FORM_DATA.name())) {
source.getArguments().getArguments().forEach(params -> {
FormDataKV keyValue = new FormDataKV();
parseParams(params, keyValue);
samplerProxy.getBody().getFormDataBody().getFormValues().add(keyValue);
});
} else {
List<QueryParam> keyValues = new LinkedList<>();
source.getArguments().getArguments().forEach(params -> {
QueryParam keyValue = new QueryParam();
parseParams(params, keyValue);
keyValues.add(keyValue);
});
if (CollectionUtils.isNotEmpty(keyValues)) {
samplerProxy.setQuery(keyValues);
}
}
}
samplerProxy.setPath(source.getPath());
samplerProxy.setMethod(source.getMethod());
} catch (Exception e) {
LogUtils.error(e);
}
return samplerProxy;
}
private String getBodyType(List<MsHeader> headers) {
if (CollectionUtils.isNotEmpty(headers)) {
List<MsHeader> keyValues = headers.stream().filter(keyValue -> "Content-Type".equals(keyValue.getKey()) && "application/x-www-form-urlencoded".equals(keyValue.getValue())).collect(Collectors.toList());
if (CollectionUtils.isNotEmpty(keyValues)) {
return keyValues.getFirst().getValue();
}
}
return null;
}
@NotNull
private static FormDataKV getFormDataKV(HTTPFileArg arg) {
ApiFile file = new ApiFile();
file.setFileId(arg.getParamName());
String fileName = arg.getPath();
if (fileName.contains("/")) {
fileName = fileName.substring(fileName.lastIndexOf("/") + 1);
}
file.setFileName(fileName);
FormDataKV keyValue = new FormDataKV();
keyValue.setKey(arg.getParamName());
keyValue.setValue(arg.getParamName());
keyValue.setContentType(arg.getMimeType());
keyValue.setContentType("file");
keyValue.setFiles(Collections.singletonList(file));
return keyValue;
}
private void parseParams(JMeterProperty params, KeyValueEnableParam keyValue) {
if (params == null || keyValue == null) {
return;
}
Object objValue = params.getObjectValue();
if (objValue instanceof HTTPArgument) {
HTTPArgument argument = (HTTPArgument) objValue;
keyValue.setKey(argument.getName());
keyValue.setValue(argument.getValue());
}
}
} }

View File

@ -1,5 +1,10 @@
package io.metersphere.api.parser.ms; package io.metersphere.api.parser.ms;
import io.metersphere.api.parser.ms.http.HeaderManagerConverter;
import io.metersphere.api.parser.ms.http.post.*;
import io.metersphere.api.parser.ms.http.pre.BeanShellPreProcessConverter;
import io.metersphere.api.parser.ms.http.pre.JDBCPreProcessConverter;
import io.metersphere.api.parser.ms.http.pre.JSR223PreProcessConverter;
import io.metersphere.plugin.api.spi.AbstractMsElementConverter; import io.metersphere.plugin.api.spi.AbstractMsElementConverter;
import io.metersphere.plugin.sdk.util.PluginLogUtils; import io.metersphere.plugin.sdk.util.PluginLogUtils;
import org.apache.jmeter.testelement.TestElement; import org.apache.jmeter.testelement.TestElement;
@ -30,6 +35,22 @@ public class MsElementConverterRegister {
register(TestPlanConverter.class); register(TestPlanConverter.class);
register(ThreadGroupConverter.class); register(ThreadGroupConverter.class);
register(HTTPSamplerConverter.class); register(HTTPSamplerConverter.class);
register(HeaderManagerConverter.class);
register(BeanShellPostProcessConverter.class);
register(ConstantTimerConverter.class);
register(JDBCPostProcessConverter.class);
register(JSONPostProcessorConverter.class);
register(JSR223PostProcessConverter.class);
register(RegexExtractorConverter.class);
register(XPath2ExtractorConverter.class);
register(XPathExtractorConverter.class);
register(BeanShellPreProcessConverter.class);
register(JDBCPreProcessConverter.class);
register(JSR223PreProcessConverter.class);
} }
/** /**

View File

@ -1,10 +1,14 @@
package io.metersphere.api.parser.ms; package io.metersphere.api.parser.ms;
import io.metersphere.api.dto.request.MsScenario; import io.metersphere.api.dto.request.MsScenario;
import io.metersphere.plugin.api.spi.AbstractMsProtocolTestElement;
import io.metersphere.plugin.api.spi.AbstractMsTestElement; import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import org.apache.jmeter.testelement.TestElement; import org.apache.jmeter.testelement.TestElement;
import org.apache.jorphan.collections.HashTree; import org.apache.jorphan.collections.HashTree;
import java.util.ArrayList;
import java.util.List;
/** /**
* @Author: jianxing * @Author: jianxing
* @CreateTime: 2023-10-27 10:07 * @CreateTime: 2023-10-27 10:07
@ -22,4 +26,16 @@ public class MsTestElementParser {
} }
return msScenario; return msScenario;
} }
public List<AbstractMsProtocolTestElement> getAbstractMsProtocolTestElement(AbstractMsTestElement msTestElement) {
List<AbstractMsProtocolTestElement> result = new ArrayList<>();
if (msTestElement instanceof AbstractMsProtocolTestElement abstractMsProtocolTestElement) {
result.add(abstractMsProtocolTestElement);
} else {
for (AbstractMsTestElement child : msTestElement.getChildren()) {
result.addAll(this.getAbstractMsProtocolTestElement(child));
}
}
return result;
}
} }

View File

@ -0,0 +1,36 @@
package io.metersphere.api.parser.ms.http;
import io.metersphere.api.dto.request.http.MsHTTPElement;
import io.metersphere.api.dto.request.http.MsHeader;
import io.metersphere.plugin.api.spi.AbstractMsElementConverter;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import org.apache.jmeter.protocol.http.control.HeaderManager;
import org.apache.jmeter.testelement.property.CollectionProperty;
import org.apache.jmeter.testelement.property.JMeterProperty;
import org.apache.jorphan.collections.HashTree;
import java.util.LinkedList;
import java.util.List;
public class HeaderManagerConverter extends AbstractMsElementConverter<HeaderManager> {
@Override
public void toMsElement(AbstractMsTestElement parent, HeaderManager element, HashTree hashTree) {
if (parent instanceof MsHTTPElement msHTTPElement) {
// 处理HTTP协议的请求头
List<MsHeader> headerKvList = msHTTPElement.getHeaders() == null ? new LinkedList<>() : msHTTPElement.getHeaders();
CollectionProperty collectionProperty = element.getHeaders();
List<String> extendsHeaderKey = headerKvList.stream().map(MsHeader::getKey).toList();
for (int i = 0; i < collectionProperty.size(); i++) {
JMeterProperty jMeterProperty = collectionProperty.get(i);
String key = jMeterProperty.getName();
if (!extendsHeaderKey.contains(key)) {
headerKvList.add(new MsHeader() {{
this.setKey(key);
this.setValue(jMeterProperty.getStringValue());
}});
}
}
msHTTPElement.setHeaders(headerKvList);
}
}
}

View File

@ -0,0 +1,21 @@
package io.metersphere.api.parser.ms.http.post;
import io.metersphere.api.utils.ConverterUtils;
import io.metersphere.plugin.api.spi.AbstractMsElementConverter;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.project.api.processor.ScriptProcessor;
import io.metersphere.project.constants.ScriptLanguageType;
import org.apache.jmeter.extractor.BeanShellPostProcessor;
import org.apache.jorphan.collections.HashTree;
public class BeanShellPostProcessConverter extends AbstractMsElementConverter<BeanShellPostProcessor> {
@Override
public void toMsElement(AbstractMsTestElement parent, BeanShellPostProcessor element, HashTree hashTree) {
ScriptProcessor msScriptElement = new ScriptProcessor();
msScriptElement.setEnable(element.isEnabled());
msScriptElement.setScriptLanguage(ScriptLanguageType.BEANSHELL.name());
msScriptElement.setName(element.getPropertyAsString("TestElement.name"));
msScriptElement.setScript(element.getPropertyAsString("script"));
ConverterUtils.addPostProcess(parent, msScriptElement);
}
}

View File

@ -0,0 +1,19 @@
package io.metersphere.api.parser.ms.http.post;
import io.metersphere.api.utils.ConverterUtils;
import io.metersphere.plugin.api.spi.AbstractMsElementConverter;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.project.api.processor.TimeWaitingProcessor;
import org.apache.jmeter.timers.ConstantTimer;
import org.apache.jorphan.collections.HashTree;
public class ConstantTimerConverter extends AbstractMsElementConverter<ConstantTimer> {
@Override
public void toMsElement(AbstractMsTestElement parent, ConstantTimer element, HashTree hashTree) {
TimeWaitingProcessor msProcessor = new TimeWaitingProcessor();
msProcessor.setDelay(Long.parseLong(element.getDelay()));
msProcessor.setEnable(element.isEnabled());
msProcessor.setName(element.getName());
ConverterUtils.addPreProcess(parent, msProcessor);
}
}

View File

@ -0,0 +1,14 @@
package io.metersphere.api.parser.ms.http.post;
import io.metersphere.api.utils.ConverterUtils;
import io.metersphere.plugin.api.spi.AbstractMsElementConverter;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import org.apache.jmeter.protocol.jdbc.processor.JDBCPostProcessor;
import org.apache.jorphan.collections.HashTree;
public class JDBCPostProcessConverter extends AbstractMsElementConverter<JDBCPostProcessor> {
@Override
public void toMsElement(AbstractMsTestElement parent, JDBCPostProcessor element, HashTree hashTree) {
ConverterUtils.addPostProcess(parent, ConverterUtils.genJDBCProcessor(element));
}
}

View File

@ -0,0 +1,35 @@
package io.metersphere.api.parser.ms.http.post;
import io.metersphere.api.utils.ConverterUtils;
import io.metersphere.plugin.api.spi.AbstractMsElementConverter;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.project.api.processor.extract.JSONPathExtract;
import io.metersphere.project.api.processor.extract.ResultMatchingExtract;
import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.extractor.json.jsonpath.JSONPostProcessor;
import org.apache.jorphan.collections.HashTree;
public class JSONPostProcessorConverter extends AbstractMsElementConverter<JSONPostProcessor> {
@Override
public void toMsElement(AbstractMsTestElement parent, JSONPostProcessor element, HashTree hashTree) {
JSONPathExtract jsonPathExtract = new JSONPathExtract();
jsonPathExtract.setVariableName(element.getRefNames());
jsonPathExtract.setExpression(element.getJsonPathExpressions());
jsonPathExtract.setEnable(element.isEnabled());
if (StringUtils.equalsIgnoreCase(element.getMatchNumbers(), "-1")) {
jsonPathExtract.setResultMatchingRule(ResultMatchingExtract.ResultMatchingRuleType.ALL.name());
jsonPathExtract.setResultMatchingRuleNum(-1);
} else if (StringUtils.equalsIgnoreCase(element.getMatchNumbers(), "0")) {
jsonPathExtract.setResultMatchingRule(ResultMatchingExtract.ResultMatchingRuleType.RANDOM.name());
jsonPathExtract.setResultMatchingRuleNum(0);
} else {
jsonPathExtract.setResultMatchingRule(ResultMatchingExtract.ResultMatchingRuleType.SPECIFIC.name());
try {
jsonPathExtract.setResultMatchingRuleNum(Integer.parseInt(element.getMatchNumbers()));
} catch (Exception ignore) {
}
}
ConverterUtils.addPostExtract(parent, jsonPathExtract);
}
}

View File

@ -0,0 +1,20 @@
package io.metersphere.api.parser.ms.http.post;
import io.metersphere.api.utils.ConverterUtils;
import io.metersphere.plugin.api.spi.AbstractMsElementConverter;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.project.api.processor.ScriptProcessor;
import org.apache.jmeter.extractor.JSR223PostProcessor;
import org.apache.jorphan.collections.HashTree;
public class JSR223PostProcessConverter extends AbstractMsElementConverter<JSR223PostProcessor> {
@Override
public void toMsElement(AbstractMsTestElement parent, JSR223PostProcessor element, HashTree hashTree) {
ScriptProcessor msScriptElement = new ScriptProcessor();
msScriptElement.setScriptLanguage(element.getScriptLanguage());
msScriptElement.setEnable(element.isEnabled());
msScriptElement.setName(element.getPropertyAsString("TestElement.name"));
msScriptElement.setScript(element.getPropertyAsString("script"));
ConverterUtils.addPostProcess(parent, msScriptElement);
}
}

View File

@ -0,0 +1,45 @@
package io.metersphere.api.parser.ms.http.post;
import io.metersphere.api.utils.ConverterUtils;
import io.metersphere.plugin.api.spi.AbstractMsElementConverter;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.project.api.processor.extract.RegexExtract;
import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.extractor.RegexExtractor;
import org.apache.jorphan.collections.HashTree;
public class RegexExtractorConverter extends AbstractMsElementConverter<RegexExtractor> {
@Override
public void toMsElement(AbstractMsTestElement parent, RegexExtractor element, HashTree hashTree) {
RegexExtract regexExtract = new RegexExtract();
regexExtract.setVariableName(element.getRefName());
regexExtract.setExpression(element.getRegex());
regexExtract.setEnable(element.isEnabled());
regexExtract.setExtractScope(this.getUseField(element));
regexExtract.setResultMatchingRuleNum(element.getMatchNumber());
ConverterUtils.addPostExtract(parent, regexExtract);
}
private String getUseField(RegexExtractor element) {
String useHeaders = element.getPropertyAsString("RegexExtractor.useHeaders");
if (StringUtils.equalsIgnoreCase(useHeaders, "false")) {
return RegexExtract.ExtractScope.BODY.name();
} else if (StringUtils.equalsIgnoreCase(useHeaders, "unescaped")) {
return RegexExtract.ExtractScope.UNESCAPED_BODY.name();
} else if (StringUtils.equalsIgnoreCase(useHeaders, "as_document")) {
return RegexExtract.ExtractScope.BODY_AS_DOCUMENT.name();
} else if (StringUtils.equalsIgnoreCase(useHeaders, "true")) {
return RegexExtract.ExtractScope.RESPONSE_HEADERS.name();
} else if (StringUtils.equalsIgnoreCase(useHeaders, "request_headers")) {
return RegexExtract.ExtractScope.REQUEST_HEADERS.name();
} else if (StringUtils.equalsIgnoreCase(useHeaders, "URL")) {
return RegexExtract.ExtractScope.URL.name();
} else if (StringUtils.equalsIgnoreCase(useHeaders, "code")) {
return RegexExtract.ExtractScope.RESPONSE_CODE.name();
} else if (StringUtils.equalsIgnoreCase(useHeaders, "message")) {
return RegexExtract.ExtractScope.RESPONSE_MESSAGE.name();
}
return useHeaders;
}
}

View File

@ -0,0 +1,13 @@
package io.metersphere.api.parser.ms.http.post;
import io.metersphere.plugin.api.spi.AbstractMsElementConverter;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import org.apache.jmeter.assertions.ResponseAssertion;
import org.apache.jorphan.collections.HashTree;
public class ResponseAssertionConverter extends AbstractMsElementConverter<ResponseAssertion> {
@Override
public void toMsElement(AbstractMsTestElement parent, ResponseAssertion element, HashTree hashTree) {
}
}

View File

@ -0,0 +1,35 @@
package io.metersphere.api.parser.ms.http.post;
import io.metersphere.api.utils.ConverterUtils;
import io.metersphere.plugin.api.spi.AbstractMsElementConverter;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.project.api.processor.extract.ResultMatchingExtract;
import io.metersphere.project.api.processor.extract.XPathExtract;
import org.apache.jmeter.extractor.XPath2Extractor;
import org.apache.jorphan.collections.HashTree;
public class XPath2ExtractorConverter extends AbstractMsElementConverter<XPath2Extractor> {
@Override
public void toMsElement(AbstractMsTestElement parent, XPath2Extractor element, HashTree hashTree) {
XPathExtract xPathExtract = new XPathExtract();
xPathExtract.setEnable(element.isEnabled());
xPathExtract.setResponseFormat(XPathExtract.ResponseFormat.XML.name());
xPathExtract.setVariableName(element.getRefName());
xPathExtract.setExpression(element.getXPathQuery());
if (element.getMatchNumber() == -1) {
xPathExtract.setResultMatchingRule(ResultMatchingExtract.ResultMatchingRuleType.ALL.name());
xPathExtract.setResultMatchingRuleNum(-1);
} else if (element.getMatchNumber() == 0) {
xPathExtract.setResultMatchingRule(ResultMatchingExtract.ResultMatchingRuleType.RANDOM.name());
xPathExtract.setResultMatchingRuleNum(0);
} else {
xPathExtract.setResultMatchingRule(ResultMatchingExtract.ResultMatchingRuleType.SPECIFIC.name());
xPathExtract.setResultMatchingRuleNum(element.getMatchNumber());
}
// xPathExtract.setVariableType(element.getPropertyAsString("type"));
// xPathExtract.setExpression(element.getPropertyAsString("expression"));
ConverterUtils.addPostExtract(parent, xPathExtract);
}
}

View File

@ -0,0 +1,38 @@
package io.metersphere.api.parser.ms.http.post;
import io.metersphere.api.utils.ConverterUtils;
import io.metersphere.plugin.api.spi.AbstractMsElementConverter;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.project.api.processor.extract.JSONPathExtract;
import io.metersphere.project.api.processor.extract.RegexExtract;
import io.metersphere.project.api.processor.extract.ResultMatchingExtract;
import io.metersphere.project.api.processor.extract.XPathExtract;
import org.apache.jmeter.extractor.XPathExtractor;
import org.apache.jorphan.collections.HashTree;
public class XPathExtractorConverter extends AbstractMsElementConverter<XPathExtractor> {
@Override
public void toMsElement(AbstractMsTestElement parent, XPathExtractor element, HashTree hashTree) {
JSONPathExtract jsonPathExtract = new JSONPathExtract();
RegexExtract regexExtract = new RegexExtract();
XPathExtract xPathExtract = new XPathExtract();
xPathExtract.setEnable(element.isEnabled());
xPathExtract.setResponseFormat(XPathExtract.ResponseFormat.HTML.name());
xPathExtract.setVariableName(element.getRefName());
xPathExtract.setExpression(element.getXPathQuery());
if (element.getMatchNumber() == -1) {
xPathExtract.setResultMatchingRule(ResultMatchingExtract.ResultMatchingRuleType.ALL.name());
xPathExtract.setResultMatchingRuleNum(-1);
} else if (element.getMatchNumber() == 0) {
xPathExtract.setResultMatchingRule(ResultMatchingExtract.ResultMatchingRuleType.RANDOM.name());
xPathExtract.setResultMatchingRuleNum(0);
} else {
xPathExtract.setResultMatchingRule(ResultMatchingExtract.ResultMatchingRuleType.SPECIFIC.name());
xPathExtract.setResultMatchingRuleNum(element.getMatchNumber());
}
ConverterUtils.addPostExtract(parent, xPathExtract);
}
}

View File

@ -0,0 +1,21 @@
package io.metersphere.api.parser.ms.http.pre;
import io.metersphere.api.utils.ConverterUtils;
import io.metersphere.plugin.api.spi.AbstractMsElementConverter;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.project.api.processor.ScriptProcessor;
import io.metersphere.project.constants.ScriptLanguageType;
import org.apache.jmeter.modifiers.BeanShellPreProcessor;
import org.apache.jorphan.collections.HashTree;
public class BeanShellPreProcessConverter extends AbstractMsElementConverter<BeanShellPreProcessor> {
@Override
public void toMsElement(AbstractMsTestElement parent, BeanShellPreProcessor element, HashTree hashTree) {
ScriptProcessor msScriptElement = new ScriptProcessor();
msScriptElement.setEnable(element.isEnabled());
msScriptElement.setScriptLanguage(ScriptLanguageType.BEANSHELL.name());
msScriptElement.setName(element.getPropertyAsString("TestElement.name"));
msScriptElement.setScript(element.getPropertyAsString("script"));
ConverterUtils.addPreProcess(parent, msScriptElement);
}
}

View File

@ -0,0 +1,14 @@
package io.metersphere.api.parser.ms.http.pre;
import io.metersphere.api.utils.ConverterUtils;
import io.metersphere.plugin.api.spi.AbstractMsElementConverter;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import org.apache.jmeter.protocol.jdbc.processor.JDBCPreProcessor;
import org.apache.jorphan.collections.HashTree;
public class JDBCPreProcessConverter extends AbstractMsElementConverter<JDBCPreProcessor> {
@Override
public void toMsElement(AbstractMsTestElement parent, JDBCPreProcessor element, HashTree hashTree) {
ConverterUtils.addPreProcess(parent, ConverterUtils.genJDBCProcessor(element));
}
}

View File

@ -0,0 +1,22 @@
package io.metersphere.api.parser.ms.http.pre;
import io.metersphere.api.utils.ConverterUtils;
import io.metersphere.plugin.api.spi.AbstractMsElementConverter;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.project.api.processor.ScriptProcessor;
import org.apache.jmeter.modifiers.JSR223PreProcessor;
import org.apache.jorphan.collections.HashTree;
public class JSR223PreProcessConverter extends AbstractMsElementConverter<JSR223PreProcessor> {
@Override
public void toMsElement(AbstractMsTestElement parent, JSR223PreProcessor element, HashTree hashTree) {
ScriptProcessor msScriptElement = new ScriptProcessor();
msScriptElement.setScriptLanguage(element.getScriptLanguage());
msScriptElement.setEnable(element.isEnabled());
msScriptElement.setName(element.getPropertyAsString("TestElement.name"));
msScriptElement.setScript(element.getPropertyAsString("script"));
ConverterUtils.addPreProcess(parent, msScriptElement);
}
}

View File

@ -1,10 +1,8 @@
package io.metersphere.api.service.definition; package io.metersphere.api.service.definition;
import io.metersphere.api.domain.*; import io.metersphere.api.domain.ApiDefinitionModule;
import io.metersphere.api.dto.definition.ApiDefinitionBatchRequest; import io.metersphere.api.domain.ApiDefinitionModuleExample;
import io.metersphere.api.dto.definition.ApiDefinitionWithBlob; import io.metersphere.api.dto.definition.*;
import io.metersphere.api.dto.definition.ApiMockWithBlob;
import io.metersphere.api.dto.definition.ApiTestCaseWithBlob;
import io.metersphere.api.dto.export.ApiExportResponse; import io.metersphere.api.dto.export.ApiExportResponse;
import io.metersphere.api.mapper.*; import io.metersphere.api.mapper.*;
import io.metersphere.api.parser.api.MetersphereExportParser; import io.metersphere.api.parser.api.MetersphereExportParser;
@ -16,6 +14,7 @@ import io.metersphere.sdk.exception.MSException;
import io.metersphere.system.utils.CustomFieldUtils; import io.metersphere.system.utils.CustomFieldUtils;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.ArrayList; import java.util.ArrayList;
@ -45,12 +44,12 @@ public class ApiDefinitionExportService {
private ApiDefinitionMapper apiDefinitionMapper; private ApiDefinitionMapper apiDefinitionMapper;
public ApiExportResponse export(ApiDefinitionBatchRequest request, String type, String userId) { public ApiExportResponse export(ApiDefinitionBatchExportRequest request, String type, String userId) {
List<String> ids = getBatchApiIds(request, request.getProjectId(), List.of(ModuleConstants.NODE_PROTOCOL_HTTP), false, userId); List<String> ids = this.getBatchExportApiIds(request, request.getProjectId(), userId);
if (CollectionUtils.isEmpty(ids)) { if (CollectionUtils.isEmpty(ids)) {
return null; return null;
} }
List<ApiDefinitionWithBlob> list = extApiDefinitionMapper.selectApiDefinitionWithBlob(ids); List<ApiDefinitionWithBlob> list = this.selectAndSortByIds(ids);
List<String> moduleIds = list.stream().map(ApiDefinitionWithBlob::getModuleId).toList(); List<String> moduleIds = list.stream().map(ApiDefinitionWithBlob::getModuleId).toList();
ApiDefinitionModuleExample example = new ApiDefinitionModuleExample(); ApiDefinitionModuleExample example = new ApiDefinitionModuleExample();
example.createCriteria().andIdIn(moduleIds); example.createCriteria().andIdIn(moduleIds);
@ -63,20 +62,26 @@ public class ApiDefinitionExportService {
}; };
} }
private List<String> getBatchApiIds(ApiDefinitionBatchRequest request, String projectId, List<String> protocols, boolean deleted, String userId) { private List<ApiDefinitionWithBlob> selectAndSortByIds(List<String> ids) {
Map<String, ApiDefinitionWithBlob> apiMap = extApiDefinitionMapper.selectApiDefinitionWithBlob(ids).stream().collect(Collectors.toMap(ApiDefinitionWithBlob::getId, v -> v));
return ids.stream().map(apiMap::get).toList();
}
private List<String> getBatchExportApiIds(ApiDefinitionBatchExportRequest request, String exportType, String userId) {
List<String> protocols = request.getProtocols();
if (StringUtils.equalsIgnoreCase(exportType, "swagger")) {
protocols = List.of(ModuleConstants.NODE_PROTOCOL_HTTP);
}
if (request.isSelectAll()) { if (request.isSelectAll()) {
CustomFieldUtils.setBaseQueryRequestCustomMultipleFields(request.getCondition(), userId); CustomFieldUtils.setBaseQueryRequestCustomMultipleFields(request.getCondition(), userId);
List<String> ids = extApiDefinitionMapper.getIds(request, projectId, protocols, deleted); List<String> ids = extApiDefinitionMapper.getIdsBySort(request, request.getProjectId(), protocols, request.getSortString());
if (CollectionUtils.isNotEmpty(request.getExcludeIds())) { if (CollectionUtils.isNotEmpty(request.getExcludeIds())) {
ids.removeAll(request.getExcludeIds()); ids.removeAll(request.getExcludeIds());
} }
return ids; return ids;
} else { } else {
request.getSelectIds().removeAll(request.getExcludeIds()); return request.getSelectIds();
ApiDefinitionExample definitionExample = new ApiDefinitionExample();
definitionExample.createCriteria().andIdIn(request.getSelectIds()).andProtocolIn(protocols).andDeletedEqualTo(deleted);
List<ApiDefinition> apiDefinitions = apiDefinitionMapper.selectByExample(definitionExample);
return apiDefinitions.stream().map(ApiDefinition::getId).toList();
} }
} }
@ -90,15 +95,11 @@ public class ApiDefinitionExportService {
} }
} }
@Resource private ApiExportResponse exportMetersphere(ApiDefinitionBatchExportRequest request, List<ApiDefinitionWithBlob> list, Map<String, String> moduleMap) {
private ApiTestCaseBlobMapper apiTestCaseBlobMapper;
private ApiExportResponse exportMetersphere(ApiDefinitionBatchRequest request, List<ApiDefinitionWithBlob> list, Map<String, String> moduleMap) {
try { try {
List<String> apiIds = list.stream().map(ApiDefinitionWithBlob::getId).toList(); List<String> apiIds = list.stream().map(ApiDefinitionWithBlob::getId).toList();
List<ApiTestCaseWithBlob> apiTestCaseWithBlobs = new ArrayList<>(); List<ApiTestCaseWithBlob> apiTestCaseWithBlobs = new ArrayList<>();
List<ApiMockWithBlob> apiMockWithBlobs = new ArrayList<>(); List<ApiMockWithBlob> apiMockWithBlobs = new ArrayList<>();
List<ApiTestCaseBlob> apiTestCaseBlobs = new ArrayList<>();
if (request.isExportApiCase()) { if (request.isExportApiCase()) {
apiTestCaseWithBlobs = extApiTestCaseMapper.selectAllDetailByApiIds(apiIds); apiTestCaseWithBlobs = extApiTestCaseMapper.selectAllDetailByApiIds(apiIds);
} }

View File

@ -52,7 +52,10 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import java.util.*; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static io.metersphere.project.utils.NodeSortUtils.DEFAULT_NODE_INTERVAL_POS; import static io.metersphere.project.utils.NodeSortUtils.DEFAULT_NODE_INTERVAL_POS;
@ -99,6 +102,9 @@ public class ApiDefinitionImportService {
if (StringUtils.isBlank(request.getProjectId())) { if (StringUtils.isBlank(request.getProjectId())) {
request.setProjectId(projectId); request.setProjectId(projectId);
} }
if (StringUtils.equalsIgnoreCase(request.getType(), ApiImportPlatform.Jmeter.name())) {
request.setSyncCase(true);
}
//判断是否是定时任务进入 //判断是否是定时任务进入
if (StringUtils.equals(request.getType(), "SCHEDULE")) { if (StringUtils.equals(request.getType(), "SCHEDULE")) {
request.setProtocol(ModuleConstants.NODE_PROTOCOL_HTTP); request.setProtocol(ModuleConstants.NODE_PROTOCOL_HTTP);
@ -118,22 +124,26 @@ public class ApiDefinitionImportService {
this.initImportRequestAndCheck(file, request, projectId); this.initImportRequestAndCheck(file, request, projectId);
ApiDefinitionImportParser<?> runService = ImportParserFactory.getImportParser(request.getPlatform()); ApiDefinitionImportParser<?> runService = ImportParserFactory.getImportParser(request.getPlatform());
assert runService != null; assert runService != null;
ApiImportDataAnalysisResult apiImportDataAnalysisResult; ApiImportDataAnalysisResult apiImportDataAnalysisResult = new ApiImportDataAnalysisResult();
try { try {
//解析文件 //解析文件
ApiImportFileParseResult fileParseResult = (ApiImportFileParseResult) runService.parse(file == null ? null : file.getInputStream(), request); ApiImportFileParseResult fileParseResult = (ApiImportFileParseResult) runService.parse(file == null ? null : file.getInputStream(), request);
if (!CollectionUtils.isEmpty(fileParseResult.getData())) {
ApiDefinitionPageRequest pageRequest = new ApiDefinitionPageRequest(); ApiDefinitionPageRequest pageRequest = new ApiDefinitionPageRequest();
pageRequest.setProjectId(request.getProjectId()); pageRequest.setProjectId(request.getProjectId());
pageRequest.setProtocols(Collections.singletonList(runService.getParseProtocol())); pageRequest.setProtocols(fileParseResult.getApiProtocols());
List<ApiDefinitionDetail> existenceApiDefinitionList = extApiDefinitionMapper.importList(pageRequest); List<ApiDefinitionDetail> existenceApiDefinitionList = extApiDefinitionMapper.importList(pageRequest);
//分析有哪些数据需要新增有哪些数据需要更新 //分析有哪些数据需要新增有哪些数据需要更新
apiImportDataAnalysisResult = runService.generateInsertAndUpdateData(fileParseResult, existenceApiDefinitionList); apiImportDataAnalysisResult = runService.generateInsertAndUpdateData(fileParseResult, existenceApiDefinitionList);
}
} catch (Exception e) { } catch (Exception e) {
LogUtils.error(e.getMessage(), e); LogUtils.error(e.getMessage(), e);
throw new MSException(Translator.get("parse_data_error")); throw new MSException(Translator.get("parse_data_error"));
} }
if (apiImportDataAnalysisResult.isEmpty()) {
throw new MSException(Translator.get("parse_empty_data"));
}
try { try {
//初始化版本信息用于保存以及以后真对具体版本导入进行拓展 //初始化版本信息用于保存以及以后真对具体版本导入进行拓展
String defaultVersion = extBaseProjectVersionMapper.getDefaultVersion(request.getProjectId()); String defaultVersion = extBaseProjectVersionMapper.getDefaultVersion(request.getProjectId());

View File

@ -1,6 +1,9 @@
package io.metersphere.api.utils; package io.metersphere.api.utils;
import io.metersphere.api.constants.ApiImportPlatform; import io.metersphere.api.constants.ApiImportPlatform;
import io.metersphere.api.dto.converter.ApiDefinitionDetail;
import io.metersphere.api.dto.definition.ApiDefinitionMockDTO;
import io.metersphere.api.dto.definition.ApiTestCaseDTO;
import io.metersphere.api.dto.request.ImportRequest; import io.metersphere.api.dto.request.ImportRequest;
import io.metersphere.project.domain.Project; import io.metersphere.project.domain.Project;
import io.metersphere.sdk.constants.HttpMethodConstants; import io.metersphere.sdk.constants.HttpMethodConstants;
@ -8,6 +11,10 @@ import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.JSON; import io.metersphere.sdk.util.JSON;
import io.metersphere.sdk.util.Translator; import io.metersphere.sdk.util.Translator;
import io.metersphere.system.log.dto.LogDTO; import io.metersphere.system.log.dto.LogDTO;
import org.apache.commons.collections4.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
public class ApiDefinitionImportUtils { public class ApiDefinitionImportUtils {
@ -48,4 +55,56 @@ public class ApiDefinitionImportUtils {
dto.setOriginalValue(JSON.toJSONBytes(importData)); dto.setOriginalValue(JSON.toJSONBytes(importData));
return dto; return dto;
} }
public static List<ApiDefinitionDetail> apiRename(List<ApiDefinitionDetail> caseList) {
List<ApiDefinitionDetail> returnList = new ArrayList<>();
if (CollectionUtils.isNotEmpty(caseList)) {
List<String> caseNameList = new ArrayList<>();
for (ApiDefinitionDetail apiCase : caseList) {
String uniqueName = getUniqueName(apiCase.getName(), caseNameList);
apiCase.setName(uniqueName);
caseNameList.add(uniqueName);
returnList.add(apiCase);
}
}
return returnList;
}
public static List<ApiTestCaseDTO> apiCaseRename(List<ApiTestCaseDTO> caseList) {
List<ApiTestCaseDTO> returnList = new ArrayList<>();
if (CollectionUtils.isNotEmpty(caseList)) {
List<String> caseNameList = new ArrayList<>();
for (ApiTestCaseDTO apiCase : caseList) {
String uniqueName = getUniqueName(apiCase.getName(), caseNameList);
apiCase.setName(uniqueName);
caseNameList.add(uniqueName);
returnList.add(apiCase);
}
}
return returnList;
}
public static List<ApiDefinitionMockDTO> apiMockRename(List<ApiDefinitionMockDTO> caseList) {
List<ApiDefinitionMockDTO> returnList = new ArrayList<>();
if (CollectionUtils.isNotEmpty(caseList)) {
List<String> caseNameList = new ArrayList<>();
for (ApiDefinitionMockDTO apiMock : caseList) {
String uniqueName = ApiDefinitionImportUtils.getUniqueName(apiMock.getName(), caseNameList);
apiMock.setName(uniqueName);
caseNameList.add(uniqueName);
returnList.add(apiMock);
}
}
return returnList;
}
private static String getUniqueName(String originalName, List<String> existenceNameList) {
String returnName = originalName;
int index = 1;
while (existenceNameList.contains(returnName)) {
returnName = originalName + " - " + index;
index++;
}
return returnName;
}
} }

View File

@ -0,0 +1,104 @@
package io.metersphere.api.utils;
import io.metersphere.api.dto.request.MsCommonElement;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.project.api.processor.ExtractPostProcessor;
import io.metersphere.project.api.processor.MsProcessor;
import io.metersphere.project.api.processor.SQLProcessor;
import io.metersphere.project.api.processor.extract.MsExtract;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.jmeter.testelement.AbstractTestElement;
import java.util.LinkedList;
public class ConverterUtils {
public static void addPreProcess(AbstractMsTestElement parent, MsProcessor msProcessor) {
if (CollectionUtils.isEmpty(parent.getChildren())) {
MsCommonElement msCommonElement = new MsCommonElement();
msCommonElement.getPreProcessorConfig().getProcessors().add(msProcessor);
LinkedList<AbstractMsTestElement> children = new LinkedList<>();
children.add(msCommonElement);
parent.setChildren(children);
} else {
AbstractMsTestElement child = parent.getChildren().getFirst();
if (child instanceof MsCommonElement msCommonElement) {
msCommonElement.getPreProcessorConfig().getProcessors().add(msProcessor);
} else {
MsCommonElement msCommonElement = new MsCommonElement();
msCommonElement.getPreProcessorConfig().getProcessors().add(msProcessor);
parent.getChildren().add(msCommonElement);
}
}
}
public static void addPostProcess(AbstractMsTestElement parent, MsProcessor msProcessor) {
if (CollectionUtils.isEmpty(parent.getChildren())) {
MsCommonElement msCommonElement = new MsCommonElement();
msCommonElement.getPostProcessorConfig().getProcessors().add(msProcessor);
LinkedList<AbstractMsTestElement> children = new LinkedList<>();
children.add(msCommonElement);
parent.setChildren(children);
} else {
AbstractMsTestElement child = parent.getChildren().getFirst();
if (child instanceof MsCommonElement msCommonElement) {
msCommonElement.getPostProcessorConfig().getProcessors().add(msProcessor);
} else {
MsCommonElement msCommonElement = new MsCommonElement();
msCommonElement.getPostProcessorConfig().getProcessors().add(msProcessor);
parent.getChildren().add(msCommonElement);
}
}
}
public static void addPostExtract(AbstractMsTestElement parent, MsExtract msExtract) {
if (CollectionUtils.isEmpty(parent.getChildren())) {
ExtractPostProcessor extractPostProcessor = new ExtractPostProcessor();
extractPostProcessor.getExtractors().add(msExtract);
MsCommonElement msCommonElement = new MsCommonElement();
msCommonElement.getPostProcessorConfig().getProcessors().add(extractPostProcessor);
LinkedList<AbstractMsTestElement> children = new LinkedList<>();
children.add(msCommonElement);
parent.setChildren(children);
} else {
AbstractMsTestElement child = parent.getChildren().getFirst();
if (child instanceof MsCommonElement msCommonElement) {
ExtractPostProcessor extractPostProcessor = null;
for (Object processor : msCommonElement.getPostProcessorConfig().getProcessors()) {
if (processor instanceof ExtractPostProcessor) {
extractPostProcessor = (ExtractPostProcessor) processor;
break;
}
}
if (extractPostProcessor == null) {
extractPostProcessor = new ExtractPostProcessor();
extractPostProcessor.getExtractors().add(msExtract);
msCommonElement.getPostProcessorConfig().getProcessors().add(extractPostProcessor);
} else {
extractPostProcessor.getExtractors().add(msExtract);
}
} else {
ExtractPostProcessor extractPostProcessor = new ExtractPostProcessor();
extractPostProcessor.getExtractors().add(msExtract);
MsCommonElement msCommonElement = new MsCommonElement();
msCommonElement.getPostProcessorConfig().getProcessors().add(extractPostProcessor);
parent.getChildren().add(msCommonElement);
}
}
}
public static SQLProcessor genJDBCProcessor(AbstractTestElement element) {
SQLProcessor msScriptElement = new SQLProcessor();
msScriptElement.setEnable(element.isEnabled());
msScriptElement.setName(element.getName());
msScriptElement.setScript(element.getPropertyAsString("query"));
try {
msScriptElement.setQueryTimeout(element.getPropertyAsLong("queryTimeout"));
} catch (Exception ignore) {
}
msScriptElement.setResultVariable(element.getPropertyAsString("resultVariable"));
msScriptElement.setVariableNames(element.getPropertyAsString("variableNames"));
return msScriptElement;
}
}

View File

@ -1759,6 +1759,7 @@ public class ApiDefinitionControllerTests extends BaseTest {
//导入类型以及文件后缀 //导入类型以及文件后缀
Map<String, String> importTypeAndSuffix = new LinkedHashMap<>(); Map<String, String> importTypeAndSuffix = new LinkedHashMap<>();
// importTypeAndSuffix.put("jmeter", "jmx");
importTypeAndSuffix.put("metersphere", "json"); importTypeAndSuffix.put("metersphere", "json");
importTypeAndSuffix.put("postman", "json"); importTypeAndSuffix.put("postman", "json");
importTypeAndSuffix.put("har", "har"); importTypeAndSuffix.put("har", "har");
@ -1997,11 +1998,40 @@ public class ApiDefinitionControllerTests extends BaseTest {
Assertions.assertEquals(newApiDefinition.size(), 0); Assertions.assertEquals(newApiDefinition.size(), 0);
Assertions.assertEquals(newApiBlobList.size(), 0); Assertions.assertEquals(newApiBlobList.size(), 0);
Assertions.assertEquals(newApiTestCaseList.size(), 0); Assertions.assertEquals(newApiTestCaseList.size(), 0);
} }
// 最后测试一把JMeter 5个请求其中有4个重复的 导入之后应该是2个请求5个用例其中1个请求有4个用例
File httpJmx = new File(
this.getClass().getClassLoader().getResource("file/import/jmeter/post-page.jmx")
.getPath()
);
FileInputStream inputStream = new FileInputStream(new File(Objects.requireNonNull(this.getClass().getClassLoader().getResource("file/import/jmeter/post-page.jmx")).getPath()));
MockMultipartFile file = new MockMultipartFile("file", "post-page.jmx", MediaType.APPLICATION_OCTET_STREAM_VALUE, inputStream);
ImportRequest request = new ImportRequest();
request.setProjectId(importProject.getId());
request.setUserId("admin");
request.setPlatform("Jmeter");
request.setSyncCase(true);
MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
paramMap.add("request", JSON.toJSONString(request));
paramMap.add("file", file);
this.requestMultipartWithOkAndReturn(IMPORT, paramMap);
List<ApiDefinitionModule> apiDefinitionModuleList = apiDefinitionModuleMapper.selectByExample(moduleExample);
Assertions.assertEquals(0, apiDefinitionModuleList.size());
List<ApiDefinitionBlob> apiDefinitionBlobs = apiDefinitionImportTestService.selectBlobByProjectId(importProject.getId());
Assertions.assertEquals(2, apiDefinitionBlobs.size());
List<ApiTestCase> newApiTestCaseList = apiTestCaseMapper.selectByExample(apiTestCaseExample);
Assertions.assertEquals(5, newApiTestCaseList.size());
//去重处理
List<String> apiDefinitionIdList = newApiTestCaseList.stream().map(ApiTestCase::getApiDefinitionId).distinct().collect(Collectors.toList());
Assertions.assertEquals(2, apiDefinitionIdList.size());
} }
private void testExportAndImport(String exportProjectId, List<ApiDefinitionBlob> exportApiBlobs) throws Exception { private void testExportAndImport(String exportProjectId, List<ApiDefinitionBlob> exportApiBlobs) throws Exception {
ApiDefinitionBatchRequest exportRequest = new ApiDefinitionBatchRequest(); ApiDefinitionBatchExportRequest exportRequest = new ApiDefinitionBatchExportRequest();
exportRequest.setProjectId(exportProjectId); exportRequest.setProjectId(exportProjectId);
exportRequest.setSelectAll(true); exportRequest.setSelectAll(true);
exportRequest.setExportApiCase(true); exportRequest.setExportApiCase(true);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,88 @@
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="5.0" jmeter="5.4.1">
<hashTree>
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="测试计划" enabled="true">
<stringProp name="TestPlan.comments"></stringProp>
<boolProp name="TestPlan.functional_mode">false</boolProp>
<boolProp name="TestPlan.tearDown_on_shutdown">true</boolProp>
<boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
<elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="用户定义的变量" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="TestPlan.user_define_classpath"></stringProp>
</TestPlan>
<hashTree>
<PostThreadGroup guiclass="PostThreadGroupGui" testclass="PostThreadGroup" testname="tearDown线程组" enabled="true">
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="循环控制器" enabled="true">
<boolProp name="LoopController.continue_forever">false</boolProp>
<stringProp name="LoopController.loops">1</stringProp>
</elementProp>
<stringProp name="ThreadGroup.num_threads">1</stringProp>
<stringProp name="ThreadGroup.ramp_time">1</stringProp>
<boolProp name="ThreadGroup.scheduler">false</boolProp>
<stringProp name="ThreadGroup.duration"></stringProp>
<stringProp name="ThreadGroup.delay"></stringProp>
<boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp>
</PostThreadGroup>
<hashTree>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="HTTP请求" enabled="true">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="用户定义的变量" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="HTTPSampler.domain">mbd.baidu.com</stringProp>
<stringProp name="HTTPSampler.port"></stringProp>
<stringProp name="HTTPSampler.protocol">https</stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path"></stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
</HTTPSamplerProxy>
<hashTree/>
<ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="察看结果树" enabled="true">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>false</xml>
<fieldNames>true</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
<sentBytes>true</sentBytes>
<url>true</url>
<threadCounts>true</threadCounts>
<idleTime>true</idleTime>
<connectTime>true</connectTime>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
</hashTree>
</hashTree>
</hashTree>
</jmeterTestPlan>

View File

@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonTypeName;
import io.metersphere.project.api.processor.extract.MsExtract; import io.metersphere.project.api.processor.extract.MsExtract;
import lombok.Data; import lombok.Data;
import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
@ -17,5 +18,5 @@ public class ExtractPostProcessor extends MsProcessor {
/** /**
* 提取器列表 * 提取器列表
*/ */
private List<MsExtract> extractors; private List<MsExtract> extractors = new ArrayList<>();
} }

View File

@ -7,6 +7,7 @@ import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size; import jakarta.validation.constraints.Size;
import lombok.Data; import lombok.Data;
import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
@ -52,5 +53,5 @@ public class SQLProcessor extends MsProcessor {
* 提取参数 * 提取参数
*/ */
@Valid @Valid
private List<KeyValueParam> extractParams; private List<KeyValueParam> extractParams = new ArrayList<>();
} }

View File

@ -4,6 +4,7 @@ import io.metersphere.plugin.api.spi.AbstractApiPlugin;
import io.metersphere.plugin.api.spi.AbstractMsTestElement; import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.plugin.api.spi.AbstractProtocolPlugin; import io.metersphere.plugin.api.spi.AbstractProtocolPlugin;
import io.metersphere.plugin.api.spi.MsTestElement; import io.metersphere.plugin.api.spi.MsTestElement;
import io.metersphere.project.domain.Project;
import io.metersphere.project.mapper.ProjectMapper; import io.metersphere.project.mapper.ProjectMapper;
import io.metersphere.sdk.constants.PluginScenarioType; import io.metersphere.sdk.constants.PluginScenarioType;
import io.metersphere.sdk.dto.api.task.ApiExecuteFileInfo; import io.metersphere.sdk.dto.api.task.ApiExecuteFileInfo;
@ -64,6 +65,15 @@ public class ApiPluginService {
return protocols; return protocols;
} }
public List<ProtocolDTO> getProtocolsByProjectId(String projectId) {
Project project = projectMapper.selectByPrimaryKey(projectId);
if (project == null) {
return new ArrayList<>();
} else {
return this.getProtocols(project.getOrganizationId());
}
}
private List<PluginWrapper> getOrgProtocolPluginWrappers(String orgId) { private List<PluginWrapper> getOrgProtocolPluginWrappers(String orgId) {
return getOrgApiPluginWrappers(orgId).stream() return getOrgApiPluginWrappers(orgId).stream()
.filter(plugin -> plugin.getPlugin() instanceof AbstractProtocolPlugin) .filter(plugin -> plugin.getPlugin() instanceof AbstractProtocolPlugin)