feat(接口测试): 场景JMX导入功能开发

This commit is contained in:
Jianguo-Genius 2024-09-10 17:50:09 +08:00 committed by Craftsman
parent bce9c9d9bb
commit d13ee84e2b
37 changed files with 1434 additions and 91 deletions

View File

@ -42,7 +42,12 @@ public enum ApiScenarioStepType {
/**
* 脚本操作
*/
SCRIPT(StepTypeGroup.REQUEST);
SCRIPT(StepTypeGroup.REQUEST),
/**
* JMeter插件
*/
JMETER_COMPONENT(StepTypeGroup.REQUEST);
private enum StepTypeGroup {

View File

@ -98,6 +98,7 @@ public class ApiScenarioBatchOperationController {
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_EXECUTE)
public void batchRun(@Validated @RequestBody ApiScenarioBatchRunRequest request) {
apiValidateService.validateApiMenuInProject(request.getProjectId(), ApiResource.PROJECT.name());
apiScenarioBatchRunService.asyncBatchRun(request, SessionUtils.getUserId());
}
}

View File

@ -6,11 +6,13 @@ import io.metersphere.api.constants.ApiResource;
import io.metersphere.api.domain.ApiScenario;
import io.metersphere.api.dto.ReferenceDTO;
import io.metersphere.api.dto.ReferenceRequest;
import io.metersphere.api.dto.definition.ApiScenarioBatchExportRequest;
import io.metersphere.api.dto.definition.ExecutePageRequest;
import io.metersphere.api.dto.definition.ExecuteReportDTO;
import io.metersphere.api.dto.request.ApiTransferRequest;
import io.metersphere.api.dto.scenario.*;
import io.metersphere.api.service.ApiFileResourceService;
import io.metersphere.api.service.ApiScenarioDataTransferService;
import io.metersphere.api.service.ApiValidateService;
import io.metersphere.api.service.scenario.ApiScenarioLogService;
import io.metersphere.api.service.scenario.ApiScenarioNoticeService;
@ -58,6 +60,8 @@ public class ApiScenarioController {
private FileModuleService fileModuleService;
@Resource
private ApiFileResourceService apiFileResourceService;
@Resource
private ApiScenarioDataTransferService apiScenarioDataTransferService;
@PostMapping("/page")
@Operation(summary = "接口测试-接口场景管理-场景列表(deleted 状态为 1 时为回收站数据)")
@ -307,4 +311,23 @@ public class ApiScenarioController {
StringUtils.isNotBlank(request.getSortString()) ? request.getSortString() : "id desc");
return PageUtils.setPageInfo(page, apiScenarioService.getReference(request));
}
@PostMapping(value = "/export/{type}")
@Operation(summary = "接口测试-接口场景管理-场景-导出场景")
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_EXPORT)
@CheckOwner(resourceId = "#request.getResourceId()", resourceType = "api_scenario")
public String export(@RequestBody ApiScenarioBatchExportRequest request, @PathVariable String type) {
return apiScenarioDataTransferService.exportScenario(request, type, SessionUtils.getUserId());
}
@PostMapping(value = "/import", consumes = {"multipart/form-data"})
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_IMPORT)
@Operation(summary = "接口测试-接口场景管理-场景-导入场景")
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
public void testCaseImport(@RequestPart(value = "file", required = false) MultipartFile file, @RequestPart("request") ApiScenarioImportRequest request) {
request.setOperator(SessionUtils.getUserId());
apiScenarioDataTransferService.importScenario(file, request);
}
}

View File

@ -14,7 +14,7 @@ import java.util.Map;
* api导入数据分析结果
*/
@Data
public class ApiImportDataAnalysisResult {
public class ApiDefinitionImportDataAnalysisResult {
// 新增接口数据
List<ApiDefinitionDetail> insertApiList = new ArrayList<>();

View File

@ -13,7 +13,7 @@ import java.util.Map;
* api导入文件解析结果
*/
@Data
public class ApiImportFileParseResult {
public class ApiDefinitionImportFileParseResult {
// 接口定义数据
private List<ApiDefinitionDetail> data = new ArrayList<>();
// 用例数据

View File

@ -0,0 +1,26 @@
package io.metersphere.api.dto.converter;
import io.metersphere.api.dto.scenario.ApiScenarioImportDetail;
import io.metersphere.system.dto.sdk.BaseTreeNode;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
* 接口导入数据准备结果
*/
@Data
public class ApiScenarioPreImportAnalysisResult {
@Schema(description = "需要创建的模块数据")
List<BaseTreeNode> insertModuleList = new ArrayList<>();
@Schema(description = "需要新增的场景")
List<ApiScenarioImportDetail> insertApiScenarioData = new ArrayList<>();
@Schema(description = "需要更新的场景")
List<ApiScenarioImportDetail> updateApiScenarioData = new ArrayList<>();
}

View File

@ -0,0 +1,48 @@
package io.metersphere.api.dto.definition;
import com.google.common.base.CaseFormat;
import io.metersphere.api.dto.scenario.ApiScenarioBatchRequest;
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 ApiScenarioBatchExportRequest extends ApiScenarioBatchRequest implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@Schema(description = "文件id")
@NotBlank
private String fileId;
@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

@ -0,0 +1,11 @@
package io.metersphere.api.dto.request;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
public class MsJMeterComponent extends AbstractMsTestElement {
}

View File

@ -0,0 +1,22 @@
package io.metersphere.api.dto.scenario;
import io.metersphere.api.domain.ApiScenario;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
import java.util.Map;
@Data
@EqualsAndHashCode(callSuper = false)
public class ApiScenarioImportDetail extends ApiScenario {
@Schema(description = "场景配置信息")
private ScenarioConfig scenarioConfig;
@Schema(description = "模块路径")
private String modulePath;
@Schema(description = "步骤详情")
private Map<String, Object> stepDetails;
@Schema(description = "步骤集合")
private List<ApiScenarioStepRequest> steps;
}

View File

@ -0,0 +1,26 @@
package io.metersphere.api.dto.scenario;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
@Data
public class ApiScenarioImportRequest {
@Schema(description = "导入的模块id")
private String moduleId;
@Schema(description = "导入的项目id")
@NotBlank
private String projectId;
@Schema(description = "导入的类型 暂定 METERSPHERE JMETER")
@NotBlank
private String type;
@Schema(description = "是否覆盖数据")
private boolean coverData;
@Schema(description = "操作人")
private String operator;
}

View File

@ -2,10 +2,6 @@ package io.metersphere.api.dto.scenario;
import lombok.Data;
/**
* @Author: jianxing
* @CreateTime: 2024-01-10 11:24
*/
@Data
public class ApiScenarioStepRequest extends ApiScenarioStepCommonDTO<ApiScenarioStepRequest> {
/**

View File

@ -94,4 +94,6 @@ public interface ExtApiScenarioMapper {
List<ApiScenario> getListBySelectModules(@Param("isRepeat") boolean isRepeat, @Param("projectId") String projectId, @Param("moduleIds") List<String> moduleIds, @Param("testPlanId") String testPlanId);
List<ApiScenario> getListBySelectIds(@Param("projectId") String projectId, @Param("ids") List<String> ids, @Param("testPlanId") String testPlanId);
List<ApiScenario> selectBaseInfoByModuleId(String id);
}

View File

@ -748,5 +748,10 @@
#{id}
</foreach>
</select>
<select id="selectBaseInfoByModuleId" resultType="io.metersphere.api.domain.ApiScenario">
SELECT id, name
FROM api_scenario
WHERE module_id = #{0}
</select>
</mapper>

View File

@ -2,8 +2,8 @@ package io.metersphere.api.parser;
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.converter.ApiDefinitionImportDataAnalysisResult;
import io.metersphere.api.dto.converter.ApiDefinitionImportFileParseResult;
import io.metersphere.api.dto.request.ImportRequest;
import java.io.InputStream;
@ -28,6 +28,6 @@ public interface ApiDefinitionImportParser<T> {
* @param existenceApiDefinitionList 数据库中已存在的数据
* @return 需要入库的模块需要入库的接口需要更新的接口
*/
ApiImportDataAnalysisResult generateInsertAndUpdateData(ApiImportFileParseResult importParser, List<ApiDefinitionDetail> existenceApiDefinitionList);
ApiDefinitionImportDataAnalysisResult generateInsertAndUpdateData(ApiDefinitionImportFileParseResult importParser, List<ApiDefinitionDetail> existenceApiDefinitionList);
}

View File

@ -0,0 +1,21 @@
package io.metersphere.api.parser;
import io.metersphere.api.dto.scenario.ApiScenarioImportDetail;
import io.metersphere.api.dto.scenario.ApiScenarioImportRequest;
import java.io.InputStream;
import java.util.List;
public interface ApiScenarioImportParser {
/**
* 解析导入文件
*
* @param source 导入文件流
* @param request 导入的请求参数
* @return 解析后的数据
*/
List<ApiScenarioImportDetail> parse(InputStream source, ApiScenarioImportRequest request) throws Exception;
}

View File

@ -1,11 +1,11 @@
package io.metersphere.api.parser;
import io.metersphere.api.constants.ApiImportPlatform;
import io.metersphere.api.parser.api.*;
import io.metersphere.api.parser.api.dataimport.*;
import org.apache.commons.lang3.StringUtils;
public class ImportParserFactory {
public static ApiDefinitionImportParser<?> getImportParser(String platform) {
public static ApiDefinitionImportParser<?> getApiDefinitionImportParser(String platform) {
if (StringUtils.equalsIgnoreCase(ApiImportPlatform.Swagger3.name(), platform)) {
return new Swagger3ParserApiDefinition();
} else if (StringUtils.equalsIgnoreCase(ApiImportPlatform.Postman.name(), platform)) {
@ -19,4 +19,13 @@ public class ImportParserFactory {
}
return null;
}
public static ApiScenarioImportParser getApiScenarioImportParser(String platform) {
if (StringUtils.equalsIgnoreCase(ApiImportPlatform.MeterSphere.name(), platform)) {
return new MetersphereParserApiScenario();
} else if (StringUtils.equalsIgnoreCase(ApiImportPlatform.Jmeter.name(), platform)) {
return new JmeterParserApiScenario();
}
return null;
}
}

View File

@ -1,10 +1,10 @@
package io.metersphere.api.parser.api;
package io.metersphere.api.parser.api.dataimport;
import io.metersphere.api.domain.ApiDefinitionBlob;
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.converter.ApiDefinitionImportDataAnalysisResult;
import io.metersphere.api.dto.converter.ApiDefinitionImportFileParseResult;
import io.metersphere.api.dto.converter.ExistenceApiDefinitionDetail;
import io.metersphere.api.dto.definition.HttpResponse;
import io.metersphere.api.dto.definition.ResponseBody;
@ -39,10 +39,10 @@ import java.util.Collections;
import java.util.List;
import java.util.Optional;
public class HarParserApiDefinition extends HttpApiDefinitionImportAbstractParser<ApiImportFileParseResult> {
public class HarParserApiDefinition extends HttpApiDefinitionImportAbstractParser<ApiDefinitionImportFileParseResult> {
@Override
public ApiImportFileParseResult parse(InputStream source, ImportRequest request) throws Exception {
public ApiDefinitionImportFileParseResult parse(InputStream source, ImportRequest request) throws Exception {
Har har = null;
try {
har = HarUtils.read(source);
@ -53,14 +53,14 @@ public class HarParserApiDefinition extends HttpApiDefinitionImportAbstractParse
if (ObjectUtils.isEmpty(har) || har.log == null) {
throw new MSException("解析失败,请确认选择的是 Har 格式!");
}
ApiImportFileParseResult definitionImport = new ApiImportFileParseResult();
ApiDefinitionImportFileParseResult definitionImport = new ApiDefinitionImportFileParseResult();
definitionImport.setData(parseRequests(har, request));
return definitionImport;
}
@Override
public ApiImportDataAnalysisResult generateInsertAndUpdateData(ApiImportFileParseResult importParser, List<ApiDefinitionDetail> existenceApiDefinitionList) {
ApiImportDataAnalysisResult insertAndUpdateData = super.generateInsertAndUpdateData(importParser, existenceApiDefinitionList);
public ApiDefinitionImportDataAnalysisResult generateInsertAndUpdateData(ApiDefinitionImportFileParseResult importParser, List<ApiDefinitionDetail> existenceApiDefinitionList) {
ApiDefinitionImportDataAnalysisResult insertAndUpdateData = super.generateInsertAndUpdateData(importParser, existenceApiDefinitionList);
ApiDefinitionBlobMapper apiDefinitionBlobMapper = CommonBeanFactory.getBean(ApiDefinitionBlobMapper.class);
for (ExistenceApiDefinitionDetail definitionDetail : insertAndUpdateData.getExistenceApiList()) {

View File

@ -1,9 +1,9 @@
package io.metersphere.api.parser.api;
package io.metersphere.api.parser.api.dataimport;
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.converter.ApiDefinitionImportDataAnalysisResult;
import io.metersphere.api.dto.converter.ApiDefinitionImportFileParseResult;
import io.metersphere.api.dto.definition.ApiDefinitionMockDTO;
import io.metersphere.api.dto.definition.ApiTestCaseDTO;
import io.metersphere.api.dto.request.ImportRequest;
@ -34,12 +34,12 @@ import java.util.stream.Collectors;
public abstract class HttpApiDefinitionImportAbstractParser<T> implements ApiDefinitionImportParser<T> {
@Override
public ApiImportDataAnalysisResult generateInsertAndUpdateData(ApiImportFileParseResult importParser, List<ApiDefinitionDetail> existenceApiDefinitionList) {
public ApiDefinitionImportDataAnalysisResult generateInsertAndUpdateData(ApiDefinitionImportFileParseResult importParser, List<ApiDefinitionDetail> existenceApiDefinitionList) {
// API类型通过 Method & Path 组合判断接口是否存在
Map<String, ApiDefinitionDetail> savedApiDefinitionMap = existenceApiDefinitionList.stream().collect(Collectors.toMap(t -> t.getMethod() + t.getPath(), t -> t, (oldValue, newValue) -> newValue));
Map<String, ApiDefinitionDetail> importDataMap = importParser.getData().stream().collect(Collectors.toMap(t -> t.getMethod() + t.getPath(), t -> t, (oldValue, newValue) -> newValue));
ApiImportDataAnalysisResult insertAndUpdateData = new ApiImportDataAnalysisResult();
ApiDefinitionImportDataAnalysisResult insertAndUpdateData = new ApiDefinitionImportDataAnalysisResult();
importDataMap.forEach((key, api) -> {
if (savedApiDefinitionMap.containsKey(key)) {

View File

@ -1,10 +1,10 @@
package io.metersphere.api.parser.api;
package io.metersphere.api.parser.api.dataimport;
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.converter.ApiDefinitionImportDataAnalysisResult;
import io.metersphere.api.dto.converter.ApiDefinitionImportFileParseResult;
import io.metersphere.api.dto.definition.ApiTestCaseDTO;
import io.metersphere.api.dto.request.ImportRequest;
import io.metersphere.api.dto.request.http.MsHTTPElement;
@ -29,11 +29,11 @@ import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;
public class JmeterParserApiDefinition implements ApiDefinitionImportParser<ApiImportFileParseResult> {
public class JmeterParserApiDefinition implements ApiDefinitionImportParser<ApiDefinitionImportFileParseResult> {
@Override
public ApiImportFileParseResult parse(InputStream inputSource, ImportRequest request) throws Exception {
public ApiDefinitionImportFileParseResult parse(InputStream inputSource, ImportRequest request) throws Exception {
try {
Object scriptWrapper = MsSaveService.loadElement(inputSource);
HashTree hashTree = this.getHashTree(scriptWrapper);
@ -49,9 +49,9 @@ public class JmeterParserApiDefinition implements ApiDefinitionImportParser<ApiI
}
}
private ApiImportFileParseResult genApiDefinitionImport(LinkedHashMap<ApiDefinitionDetail, List<ApiTestCaseDTO>> allImportDetails, String moduleName) {
private ApiDefinitionImportFileParseResult genApiDefinitionImport(LinkedHashMap<ApiDefinitionDetail, List<ApiTestCaseDTO>> allImportDetails, String moduleName) {
Map<ApiDefinitionDetail, List<ApiTestCaseDTO>> groupWithUniqueIdentification = this.mergeApiCaseWithUniqueIdentification(allImportDetails);
ApiImportFileParseResult returnDTO = new ApiImportFileParseResult();
ApiDefinitionImportFileParseResult returnDTO = new ApiDefinitionImportFileParseResult();
groupWithUniqueIdentification.forEach((definitionImportDetail, caseData) -> {
String apiID = IDGenerator.nextStr();
definitionImportDetail.setId(apiID);
@ -142,17 +142,17 @@ public class JmeterParserApiDefinition implements ApiDefinitionImportParser<ApiI
}
@Override
public ApiImportDataAnalysisResult generateInsertAndUpdateData(ApiImportFileParseResult importParser, List<ApiDefinitionDetail> existenceApiDefinitionList) {
public ApiDefinitionImportDataAnalysisResult generateInsertAndUpdateData(ApiDefinitionImportFileParseResult 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();
ApiDefinitionImportDataAnalysisResult insertAndUpdateData = new ApiDefinitionImportDataAnalysisResult();
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());
ApiDefinitionImportDataAnalysisResult httpResult = compareApiData(importList, existenceList, entry.getKey());
insertAndUpdateData.getInsertApiList().addAll(httpResult.getInsertApiList());
insertAndUpdateData.getExistenceApiList().addAll(httpResult.getExistenceApiList());
}
@ -160,8 +160,8 @@ public class JmeterParserApiDefinition implements ApiDefinitionImportParser<ApiI
return insertAndUpdateData;
}
private ApiImportDataAnalysisResult compareApiData(List<ApiDefinitionDetail> importData, List<ApiDefinitionDetail> existenceApiData, String protocol) {
ApiImportDataAnalysisResult insertAndUpdateData = new ApiImportDataAnalysisResult();
private ApiDefinitionImportDataAnalysisResult compareApiData(List<ApiDefinitionDetail> importData, List<ApiDefinitionDetail> existenceApiData, String protocol) {
ApiDefinitionImportDataAnalysisResult insertAndUpdateData = new ApiDefinitionImportDataAnalysisResult();
if (CollectionUtils.isEmpty(importData)) {
return insertAndUpdateData;

View File

@ -0,0 +1,151 @@
package io.metersphere.api.parser.api.dataimport;
import io.metersphere.api.constants.ApiScenarioStatus;
import io.metersphere.api.constants.ApiScenarioStepType;
import io.metersphere.api.dto.request.MsJMeterComponent;
import io.metersphere.api.dto.request.controller.*;
import io.metersphere.api.dto.request.http.MsHTTPElement;
import io.metersphere.api.dto.scenario.ApiScenarioImportDetail;
import io.metersphere.api.dto.scenario.ApiScenarioImportRequest;
import io.metersphere.api.dto.scenario.ApiScenarioStepRequest;
import io.metersphere.api.parser.ApiScenarioImportParser;
import io.metersphere.api.parser.jmeter.xstream.MsSaveService;
import io.metersphere.api.parser.ms.MsTestElementParser;
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.JSON;
import io.metersphere.sdk.util.LogUtils;
import io.metersphere.system.uid.IDGenerator;
import lombok.Data;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.jorphan.collections.HashTree;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.*;
public class JmeterParserApiScenario implements ApiScenarioImportParser {
@Override
public List<ApiScenarioImportDetail> parse(InputStream inputSource, ApiScenarioImportRequest request) throws Exception {
try {
Object scriptWrapper = MsSaveService.loadElement(inputSource);
HashTree hashTree = this.getHashTree(scriptWrapper);
MsTestElementParser parser = new MsTestElementParser();
AbstractMsTestElement msTestElement = parser.parse(hashTree);
Map<String, String> polymorphicNameMap = parser.getPolymorphicNameMap(request.getProjectId());
String scenarioName = StringUtils.trim(parser.parseTestPlanName(hashTree));
return Collections.singletonList(this.parseImportFile(request.getProjectId(), msTestElement, polymorphicNameMap, scenarioName));
} catch (Exception e) {
LogUtils.error(e);
throw new MSException("当前JMX版本不兼容");
}
}
private ApiScenarioImportDetail parseImportFile(String projectId, AbstractMsTestElement msElementList, Map<String, String> polymorphicNameMap, String scenarioName) {
ApiScenarioImportDetail apiScenarioDetail = new ApiScenarioImportDetail();
apiScenarioDetail.setName(scenarioName);
apiScenarioDetail.setPriority("P0");
apiScenarioDetail.setStatus(ApiScenarioStatus.UNDERWAY.name());
apiScenarioDetail.setDeleted(false);
apiScenarioDetail.setLatest(true);
apiScenarioDetail.setProjectId(projectId);
ApiScenarioStepParseResult stepParseResult = this.parseScenarioStep(msElementList.getChildren(), projectId, polymorphicNameMap);
apiScenarioDetail.setSteps(stepParseResult.getStepList());
apiScenarioDetail.setStepDetails(stepParseResult.getStepDetails());
apiScenarioDetail.setStepTotal(CollectionUtils.size(apiScenarioDetail.getSteps()));
return apiScenarioDetail;
}
private ApiScenarioStepParseResult parseScenarioStep(List<AbstractMsTestElement> msElementList, String projectId, Map<String, String> polymorphicNameMap) {
ApiScenarioStepParseResult parseResult = new ApiScenarioStepParseResult();
for (AbstractMsTestElement msTestElement : msElementList) {
ApiScenarioStepRequest apiScenarioStep = new ApiScenarioStepRequest();
apiScenarioStep.setId(IDGenerator.nextStr());
apiScenarioStep.setProjectId(projectId);
apiScenarioStep.setName(msTestElement.getName());
apiScenarioStep.setUniqueId(IDGenerator.nextStr());
byte[] stepBlobContent = null;
if (msTestElement instanceof MsHTTPElement msHTTPElement) {
apiScenarioStep.setConfig(JSON.toJSONString(new ProtocolConfig("HTTP", msHTTPElement.getMethod())));
apiScenarioStep.setStepType(ApiScenarioStepType.CUSTOM_REQUEST.name());
stepBlobContent = JSON.toJSONString(msTestElement).getBytes();
} else if (msTestElement instanceof AbstractMsProtocolTestElement) {
apiScenarioStep.setStepType(ApiScenarioStepType.CUSTOM_REQUEST.name());
String protocol = polymorphicNameMap.get(msTestElement.getClass().getSimpleName());
apiScenarioStep.setConfig(JSON.toJSONString(new ProtocolConfig(protocol, protocol)));
stepBlobContent = JSON.toJSONString(msTestElement).getBytes();
} else if (msTestElement instanceof MsJMeterComponent) {
apiScenarioStep.setStepType(this.getStepType(msTestElement));
apiScenarioStep.setConfig("{}");
} else {
apiScenarioStep.setStepType(this.getStepType(msTestElement));
apiScenarioStep.setConfig(JSON.toJSONString(msTestElement));
}
parseResult.getStepList().add(apiScenarioStep);
if (stepBlobContent != null) {
parseResult.getStepDetails().put(apiScenarioStep.getId(), stepBlobContent);
}
if (!(msTestElement instanceof AbstractMsProtocolTestElement) && CollectionUtils.isNotEmpty(msTestElement.getChildren())) {
//非请求类型组件继续处理子组件
ApiScenarioStepParseResult childParseResult = parseScenarioStep(msTestElement.getChildren(), projectId, polymorphicNameMap);
apiScenarioStep.setChildren(childParseResult.getStepList());
if (MapUtils.isNotEmpty(childParseResult.getStepDetails())) {
parseResult.getStepDetails().putAll(childParseResult.getStepDetails());
}
}
}
return parseResult;
}
private String getStepType(AbstractMsTestElement msTestElement) {
if (msTestElement instanceof MsLoopController) {
return ApiScenarioStepType.LOOP_CONTROLLER.name();
} else if (msTestElement instanceof MsOnceOnlyController) {
return ApiScenarioStepType.ONCE_ONLY_CONTROLLER.name();
} else if (msTestElement instanceof MsIfController) {
return ApiScenarioStepType.IF_CONTROLLER.name();
} else if (msTestElement instanceof MsConstantTimerController) {
return ApiScenarioStepType.CONSTANT_TIMER.name();
} else if (msTestElement instanceof MsScriptElement) {
return ApiScenarioStepType.SCRIPT.name();
} else {
return ApiScenarioStepType.JMETER_COMPONENT.name();
}
}
private HashTree getHashTree(Object scriptWrapper) throws Exception {
Field field = scriptWrapper.getClass().getDeclaredField("testPlan");
field.setAccessible(true);
return (HashTree) field.get(scriptWrapper);
}
}
@Data
class ApiScenarioStepParseResult {
private List<ApiScenarioStepRequest> stepList = new ArrayList<>();
private Map<String, Object> stepDetails = new HashMap<>();
}
class ProtocolConfig {
String id;
String name;
boolean enable = true;
String protocol;
String method;
public ProtocolConfig(String protocol, String method) {
this.protocol = protocol;
this.method = method;
}
}

View File

@ -1,10 +1,10 @@
package io.metersphere.api.parser.api;
package io.metersphere.api.parser.api.dataimport;
import io.metersphere.api.dto.converter.ApiDefinitionDetail;
import io.metersphere.api.dto.converter.ApiDefinitionExportDetail;
import io.metersphere.api.dto.converter.ApiImportDataAnalysisResult;
import io.metersphere.api.dto.converter.ApiImportFileParseResult;
import io.metersphere.api.dto.converter.ApiDefinitionImportDataAnalysisResult;
import io.metersphere.api.dto.converter.ApiDefinitionImportFileParseResult;
import io.metersphere.api.dto.definition.ApiDefinitionMockDTO;
import io.metersphere.api.dto.definition.ApiTestCaseDTO;
import io.metersphere.api.dto.export.MetersphereApiExportResponse;
@ -25,10 +25,10 @@ import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class MetersphereParserApiDefinition implements ApiDefinitionImportParser<ApiImportFileParseResult> {
public class MetersphereParserApiDefinition implements ApiDefinitionImportParser<ApiDefinitionImportFileParseResult> {
@Override
public ApiImportFileParseResult parse(InputStream source, ImportRequest request) throws Exception {
public ApiDefinitionImportFileParseResult parse(InputStream source, ImportRequest request) throws Exception {
MetersphereApiExportResponse metersphereApiExportResponse = null;
try {
metersphereApiExportResponse = ApiDataUtils.parseObject(source, MetersphereApiExportResponse.class);
@ -43,10 +43,10 @@ public class MetersphereParserApiDefinition implements ApiDefinitionImportParser
}
@Override
public ApiImportDataAnalysisResult generateInsertAndUpdateData(ApiImportFileParseResult importParser, List<ApiDefinitionDetail> existenceAllApiList) {
public ApiDefinitionImportDataAnalysisResult generateInsertAndUpdateData(ApiDefinitionImportFileParseResult importParser, List<ApiDefinitionDetail> existenceAllApiList) {
Map<String, List<ApiDefinitionDetail>> existenceProtocolMap = new HashMap<>();
ApiImportDataAnalysisResult insertAndUpdateData = new ApiImportDataAnalysisResult();
ApiDefinitionImportDataAnalysisResult insertAndUpdateData = new ApiDefinitionImportDataAnalysisResult();
for (ApiDefinitionDetail apiDefinitionDetail : existenceAllApiList) {
String protocol = apiDefinitionDetail.getProtocol().toUpperCase();
@ -109,7 +109,7 @@ public class MetersphereParserApiDefinition implements ApiDefinitionImportParser
return insertAndUpdateData;
}
private void addTestCaseAndMock(ApiImportDataAnalysisResult insertAndUpdateData, String apiId, List<ApiTestCaseDTO> caseList, List<ApiDefinitionMockDTO> mockDTOList) {
private void addTestCaseAndMock(ApiDefinitionImportDataAnalysisResult insertAndUpdateData, String apiId, List<ApiTestCaseDTO> caseList, List<ApiDefinitionMockDTO> mockDTOList) {
if (CollectionUtils.isNotEmpty(caseList)) {
insertAndUpdateData.getApiIdAndTestCaseMap().put(apiId, caseList);
}
@ -118,9 +118,9 @@ public class MetersphereParserApiDefinition implements ApiDefinitionImportParser
}
}
private ApiImportFileParseResult genApiDefinitionImport(List<ApiDefinitionExportDetail> apiDefinitions) {
private ApiDefinitionImportFileParseResult genApiDefinitionImport(List<ApiDefinitionExportDetail> apiDefinitions) {
List<ApiDefinitionExportDetail> distinctImportList = this.mergeApiCaseWithUniqueIdentification(apiDefinitions);
ApiImportFileParseResult returnDTO = new ApiImportFileParseResult();
ApiDefinitionImportFileParseResult returnDTO = new ApiDefinitionImportFileParseResult();
distinctImportList.forEach(definitionImportDetail -> {
String apiID = IDGenerator.nextStr();
definitionImportDetail.setId(apiID);

View File

@ -0,0 +1,29 @@
package io.metersphere.api.parser.api.dataimport;
import io.metersphere.api.dto.scenario.ApiScenarioImportDetail;
import io.metersphere.api.dto.scenario.ApiScenarioImportRequest;
import io.metersphere.api.parser.ApiScenarioImportParser;
import java.io.InputStream;
import java.util.List;
public class MetersphereParserApiScenario implements ApiScenarioImportParser {
@Override
public List<ApiScenarioImportDetail> parse(InputStream source, ApiScenarioImportRequest request) throws Exception {
return null;
// MetersphereApiExportResponse metersphereApiExportResponse = null;
// try {
// metersphereApiExportResponse = ApiDataUtils.parseObject(source, MetersphereApiExportResponse.class);
// } catch (Exception e) {
// LogUtils.error(e.getMessage(), e);
// throw new MSException(e.getMessage());
// }
// if (metersphereApiExportResponse == null) {
// throw new MSException("解析失败,请确认选择的是 Metersphere 格式!");
// }
// return this.genApiDefinitionImport(metersphereApiExportResponse.getApiDefinitions());
}
}

View File

@ -1,4 +1,4 @@
package io.metersphere.api.parser.api;
package io.metersphere.api.parser.api.dataimport;
import com.fasterxml.jackson.databind.JsonNode;

View File

@ -1,9 +1,9 @@
package io.metersphere.api.parser.api;
package io.metersphere.api.parser.api.dataimport;
import com.fasterxml.jackson.core.JsonProcessingException;
import io.metersphere.api.dto.converter.ApiDefinitionDetail;
import io.metersphere.api.dto.converter.ApiImportFileParseResult;
import io.metersphere.api.dto.converter.ApiDefinitionImportFileParseResult;
import io.metersphere.api.dto.definition.ApiTestCaseDTO;
import io.metersphere.api.dto.request.ImportRequest;
import io.metersphere.api.parser.api.postman.PostmanCollection;
@ -19,10 +19,10 @@ import org.apache.commons.lang3.StringUtils;
import java.io.InputStream;
import java.util.*;
public class PostmanParserApiDefinition extends PostmanAbstractParserParserApiDefinition<ApiImportFileParseResult> {
public class PostmanParserApiDefinition extends PostmanAbstractParserParserApiDefinition<ApiDefinitionImportFileParseResult> {
@Override
public ApiImportFileParseResult parse(InputStream source, ImportRequest request) throws JsonProcessingException {
public ApiDefinitionImportFileParseResult parse(InputStream source, ImportRequest request) throws JsonProcessingException {
LogUtils.info("PostmanParser parse");
String testStr = getApiTestStr(source);
PostmanCollection postmanCollection = JSON.parseObject(testStr, PostmanCollection.class);
@ -37,7 +37,7 @@ public class PostmanParserApiDefinition extends PostmanAbstractParserParserApiDe
LinkedHashMap<ApiDefinitionDetail, List<ApiDefinitionDetail>> allImportDetails = this.parseItem(postmanCollection.getItem(), modulePath, request);
// 对于postman的导入 本质就是将postman的数据导入为用例
ApiImportFileParseResult apiImport = this.genApiDefinitionImport(allImportDetails);
ApiDefinitionImportFileParseResult apiImport = this.genApiDefinitionImport(allImportDetails);
LogUtils.info("PostmanParser parse end");
return apiImport;
}
@ -61,9 +61,9 @@ public class PostmanParserApiDefinition extends PostmanAbstractParserParserApiDe
return results;
}
private ApiImportFileParseResult genApiDefinitionImport(LinkedHashMap<ApiDefinitionDetail, List<ApiDefinitionDetail>> allImportDetails) {
private ApiDefinitionImportFileParseResult genApiDefinitionImport(LinkedHashMap<ApiDefinitionDetail, List<ApiDefinitionDetail>> allImportDetails) {
Map<ApiDefinitionDetail, List<ApiDefinitionDetail>> groupWithUniqueIdentification = this.mergeApiCaseWithUniqueIdentification(allImportDetails);
ApiImportFileParseResult returnDTO = new ApiImportFileParseResult();
ApiDefinitionImportFileParseResult returnDTO = new ApiDefinitionImportFileParseResult();
groupWithUniqueIdentification.forEach((definitionImportDetail, caseData) -> {
String apiID = IDGenerator.nextStr();
definitionImportDetail.setId(apiID);

View File

@ -1,8 +1,8 @@
package io.metersphere.api.parser.api;
package io.metersphere.api.parser.api.dataimport;
import io.metersphere.api.constants.ApiConstants;
import io.metersphere.api.dto.converter.ApiDefinitionDetail;
import io.metersphere.api.dto.converter.ApiImportFileParseResult;
import io.metersphere.api.dto.converter.ApiDefinitionImportFileParseResult;
import io.metersphere.api.dto.definition.HttpResponse;
import io.metersphere.api.dto.definition.ResponseBody;
import io.metersphere.api.dto.request.ImportRequest;
@ -44,7 +44,7 @@ import java.net.URI;
import java.util.*;
public class Swagger3ParserApiDefinition extends HttpApiDefinitionImportAbstractParser<ApiImportFileParseResult> {
public class Swagger3ParserApiDefinition extends HttpApiDefinitionImportAbstractParser<ApiDefinitionImportFileParseResult> {
protected String projectId;
private Components components;
@ -72,7 +72,7 @@ public class Swagger3ParserApiDefinition extends HttpApiDefinitionImportAbstract
}
}
public ApiImportFileParseResult parse(InputStream source, ImportRequest request) throws Exception {
public ApiDefinitionImportFileParseResult parse(InputStream source, ImportRequest request) throws Exception {
//将之前在service中的swagger地址判断放在这里
if (StringUtils.isNotBlank(request.getSwaggerUrl())) {
@ -101,10 +101,10 @@ public class Swagger3ParserApiDefinition extends HttpApiDefinitionImportAbstract
throw new MSException(Translator.get("swagger_parse_error"));
}
}
ApiImportFileParseResult apiImportFileParseResult = new ApiImportFileParseResult();
ApiDefinitionImportFileParseResult apiDefinitionImportFileParseResult = new ApiDefinitionImportFileParseResult();
OpenAPI openAPI = result.getOpenAPI();
apiImportFileParseResult.setData(parseRequests(openAPI, request));
return apiImportFileParseResult;
apiDefinitionImportFileParseResult.setData(parseRequests(openAPI, request));
return apiDefinitionImportFileParseResult;
}
private List<AuthorizationValue> setAuths(ImportRequest request) {

View File

@ -3,12 +3,17 @@ package io.metersphere.api.parser.ms;
import io.metersphere.api.dto.request.MsScenario;
import io.metersphere.plugin.api.spi.AbstractMsProtocolTestElement;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.sdk.util.CommonBeanFactory;
import io.metersphere.system.dto.ProtocolDTO;
import io.metersphere.system.service.ApiPluginService;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.testelement.TestPlan;
import org.apache.jorphan.collections.HashTree;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @Author: jianxing
@ -48,4 +53,24 @@ public class MsTestElementParser {
}
return result;
}
// public List<AbstractMsTestElement> getAbstractMsTestElement(AbstractMsTestElement msTestElement) {
// List<AbstractMsTestElement> result = new ArrayList<>();
// if (msTestElement instanceof AbstractMsProtocolTestElement abstractMsProtocolTestElement) {
// result.add(abstractMsProtocolTestElement);
// } else {
// for (AbstractMsTestElement child : msTestElement.getChildren()) {
// result.addAll(this.getAbstractMsTestElement(child));
// }
// }
// return result;
// }
public Map<String, String> getPolymorphicNameMap(String projectId) {
ApiPluginService apiPluginService = CommonBeanFactory.getBean(ApiPluginService.class);
assert apiPluginService != null;
List<ProtocolDTO> protocolDTOList = apiPluginService.getProtocolsByProjectId(projectId);
return protocolDTOList.stream().collect(Collectors.toMap(ProtocolDTO::getPolymorphicName, ProtocolDTO::getProtocol));
}
}

View File

@ -1,7 +1,7 @@
package io.metersphere.api.parser.ms;
import io.metersphere.api.dto.request.MsScenario;
import io.metersphere.api.dto.request.MsJMeterComponent;
import io.metersphere.plugin.api.spi.AbstractMsElementConverter;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import org.apache.jmeter.threads.ThreadGroup;
@ -16,9 +16,9 @@ import org.apache.jorphan.collections.HashTree;
public class ThreadGroupConverter extends AbstractMsElementConverter<ThreadGroup> {
@Override
public void toMsElement(AbstractMsTestElement parent, ThreadGroup element, HashTree hashTree) {
MsScenario msScenario = new MsScenario();
// todo 解析线程组
parent.getChildren().add(msScenario);
parseChild(msScenario, element, hashTree);
MsJMeterComponent msJMeterComponent = new MsJMeterComponent();
msJMeterComponent.setName(element.getName());
parent.getChildren().add(msJMeterComponent);
parseChild(msJMeterComponent, element, hashTree);
}
}

View File

@ -0,0 +1,547 @@
package io.metersphere.api.service;
import io.metersphere.api.domain.*;
import io.metersphere.api.dto.ApiFile;
import io.metersphere.api.dto.converter.ApiScenarioPreImportAnalysisResult;
import io.metersphere.api.dto.definition.ApiScenarioBatchExportRequest;
import io.metersphere.api.dto.scenario.*;
import io.metersphere.api.mapper.*;
import io.metersphere.api.parser.ApiScenarioImportParser;
import io.metersphere.api.parser.ImportParserFactory;
import io.metersphere.api.service.scenario.ApiScenarioModuleService;
import io.metersphere.api.service.scenario.ApiScenarioService;
import io.metersphere.api.utils.ApiDefinitionImportUtils;
import io.metersphere.api.utils.ApiScenarioImportUtils;
import io.metersphere.project.domain.Project;
import io.metersphere.project.mapper.ExtBaseProjectVersionMapper;
import io.metersphere.project.mapper.ProjectMapper;
import io.metersphere.sdk.constants.DefaultRepositoryDir;
import io.metersphere.sdk.constants.ModuleConstants;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.*;
import io.metersphere.system.constants.ExportConstants;
import io.metersphere.system.domain.User;
import io.metersphere.system.dto.sdk.BaseTreeNode;
import io.metersphere.system.log.constants.OperationLogModule;
import io.metersphere.system.log.constants.OperationLogType;
import io.metersphere.system.log.dto.LogDTO;
import io.metersphere.system.log.service.OperationLogService;
import io.metersphere.system.manager.ExportTaskManager;
import io.metersphere.system.mapper.UserMapper;
import io.metersphere.system.notice.constants.NoticeConstants;
import io.metersphere.system.service.CommonNoticeSendService;
import io.metersphere.system.uid.IDGenerator;
import io.metersphere.system.utils.TreeNodeParseUtils;
import jakarta.annotation.Resource;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.ListUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import static io.metersphere.project.utils.NodeSortUtils.DEFAULT_NODE_INTERVAL_POS;
@Service
@Transactional(rollbackFor = Exception.class)
public class ApiScenarioDataTransferService {
@Resource
private ProjectMapper projectMapper;
@Resource
private UserMapper userMapper;
@Resource
private ExtBaseProjectVersionMapper extBaseProjectVersionMapper;
@Resource
private ExportTaskManager exportTaskManager;
@Resource
private ExtApiScenarioMapper extApiScenarioMapper;
@Resource
private CommonNoticeSendService commonNoticeSendService;
@Resource
private ApiScenarioModuleService apiScenarioModuleService;
@Resource
private ApiScenarioService apiScenarioService;
@Resource
private ApiFileResourceService apiFileResourceService;
@Resource
private SqlSessionFactory sqlSessionFactory;
@Resource
private OperationLogService operationLogService;
private final ThreadLocal<Long> currentApiScenarioOrder = new ThreadLocal<>();
private final ThreadLocal<Long> currentModuleOrder = new ThreadLocal<>();
public String exportScenario(ApiScenarioBatchExportRequest request, String type, String userId) {
String returnId;
try {
exportTaskManager.exportCheck(request.getProjectId(), ExportConstants.ExportType.API_SCENARIO.toString(), userId);
returnId = exportTaskManager.exportAsyncTask(
request.getProjectId(),
request.getFileId(), userId,
ExportConstants.ExportType.API_SCENARIO.name(), request, t -> {
try {
return exportApiScenarioZip(request, type, userId);
} catch (Exception e) {
throw new MSException(e);
}
}).getId();
} catch (InterruptedException e) {
LogUtils.error("导出失败:" + e);
throw new MSException(e);
}
return returnId;
}
public void importScenario(MultipartFile file, ApiScenarioImportRequest request) {
ApiScenarioImportParser parser = ImportParserFactory.getApiScenarioImportParser(request.getType());
List<ApiScenarioImportDetail> importScenarios;
try {
assert parser != null;
importScenarios = parser.parse(file.getInputStream(), request);
} catch (Exception e) {
LogUtils.error(e.getMessage(), e);
throw new MSException(Translator.get("parse_data_error"));
}
if (CollectionUtils.isEmpty(importScenarios)) {
throw new MSException(Translator.get("parse_empty_data"));
}
//解析
ApiScenarioPreImportAnalysisResult preImportAnalysisResult = this.importAnalysis(
importScenarios, request.getModuleId(), apiScenarioModuleService.getTree(request.getProjectId()));
//存储
this.save(preImportAnalysisResult, request.getProjectId(), request.getOperator(), request.isCoverData());
}
private void save(ApiScenarioPreImportAnalysisResult preImportAnalysisResult, String projectId, String operator, boolean isCoverData) {
List<LogDTO> operationLogs = new ArrayList<>();
currentModuleOrder.remove();
currentApiScenarioOrder.remove();
// 更新修改数据
{
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
this.insertModule(projectId, operator, preImportAnalysisResult.getInsertModuleList(), sqlSession);
if (isCoverData) {
this.updateScenarios(projectId, operator, preImportAnalysisResult.getUpdateApiScenarioData(), sqlSession);
}
String versionId = extBaseProjectVersionMapper.getDefaultVersion(projectId);
this.insertScenarios(projectId, operator, versionId, preImportAnalysisResult.getInsertApiScenarioData(), sqlSession);
sqlSession.flushStatements();
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
}
//记录log以及发送通知
{
Project project = projectMapper.selectByPrimaryKey(projectId);
List<ApiScenarioImportDetail> noticeCreateLists = new ArrayList<>();
List<ApiScenarioImportDetail> noticeUpdateLists = new ArrayList<>();
preImportAnalysisResult.getInsertModuleList().forEach(t ->
operationLogs.add(ApiDefinitionImportUtils.genImportLog(project, t.getId(), t.getName(), t, OperationLogModule.API_SCENARIO_MANAGEMENT_MODULE, operator, OperationLogType.ADD.name()))
);
preImportAnalysisResult.getInsertApiScenarioData().forEach(t -> {
ApiScenarioImportDetail scenarioImportDetail = new ApiScenarioImportDetail();
BeanUtils.copyBean(scenarioImportDetail, t);
noticeCreateLists.add(scenarioImportDetail);
operationLogs.add(ApiScenarioImportUtils.genImportLog(project, t.getId(), t.getName(), scenarioImportDetail, OperationLogModule.API_SCENARIO_MANAGEMENT_SCENARIO, operator, OperationLogType.IMPORT.name()));
});
preImportAnalysisResult.getUpdateApiScenarioData().forEach(t -> {
ApiScenarioImportDetail scenarioImportDetail = new ApiScenarioImportDetail();
BeanUtils.copyBean(scenarioImportDetail, t);
noticeUpdateLists.add(scenarioImportDetail);
operationLogs.add(ApiScenarioImportUtils.genImportLog(project, t.getId(), t.getName(), scenarioImportDetail, OperationLogModule.API_SCENARIO_MANAGEMENT_SCENARIO, operator, OperationLogType.UPDATE.name()));
});
//发送通知
User user = userMapper.selectByPrimaryKey(operator);
commonNoticeSendService.sendNotice(NoticeConstants.TaskType.API_SCENARIO_TASK, NoticeConstants.Event.CREATE,
new ArrayList<>(JSON.parseArray(JSON.toJSONString(noticeCreateLists), Map.class)), user, projectId);
commonNoticeSendService.sendNotice(NoticeConstants.TaskType.API_SCENARIO_TASK, NoticeConstants.Event.UPDATE,
new ArrayList<>(JSON.parseArray(JSON.toJSONString(noticeUpdateLists), Map.class)), user, projectId);
}
operationLogService.batchAdd(operationLogs);
}
private void updateScenarios(String projectId, String operator, List<ApiScenarioImportDetail> updateApiScenarioData, SqlSession sqlSession) {
// 创建场景
if (CollectionUtils.isEmpty(updateApiScenarioData)) {
return;
}
ApiScenarioMapper scenarioBatchMapper = sqlSession.getMapper(ApiScenarioMapper.class);
ApiScenarioBlobMapper scenarioBlobBatchMapper = sqlSession.getMapper(ApiScenarioBlobMapper.class);
ApiScenarioCsvMapper csvBatchMapper = sqlSession.getMapper(ApiScenarioCsvMapper.class);
ApiScenarioCsvStepMapper csvStepBatchMapper = sqlSession.getMapper(ApiScenarioCsvStepMapper.class);
ApiScenarioStepMapper stepBatchMapper = sqlSession.getMapper(ApiScenarioStepMapper.class);
ApiScenarioStepBlobMapper stepBlobBatchMapper = sqlSession.getMapper(ApiScenarioStepBlobMapper.class);
ExtApiScenarioStepMapper extStepMapper = sqlSession.getMapper(ExtApiScenarioStepMapper.class);
SubListUtils.dealForSubList(updateApiScenarioData, 100, list -> {
//首先筛选出空步骤的场景用于删除已有步骤
List<String> emptyStepScenarioIds = new ArrayList<>();
{
list.forEach(t -> {
if (t.getSteps() != null && CollectionUtils.isEmpty(t.getSteps())) {
emptyStepScenarioIds.add(t.getId());
}
});
if (!emptyStepScenarioIds.isEmpty()) {
ApiScenarioStepExample deleteStepExample = new ApiScenarioStepExample();
deleteStepExample.createCriteria().andScenarioIdIn(emptyStepScenarioIds);
stepBatchMapper.deleteByExample(deleteStepExample);
ApiScenarioStepBlobExample deleteStepBlobExample = new ApiScenarioStepBlobExample();
deleteStepBlobExample.createCriteria().andScenarioIdIn(emptyStepScenarioIds);
stepBlobBatchMapper.deleteByExample(deleteStepBlobExample);
}
}
//更新场景
list.forEach(request -> {
// 更新基础信息
ApiScenario scenario = BeanUtils.copyBean(new ApiScenario(), request);
scenario.setUpdateUser(operator);
scenario.setUpdateTime(System.currentTimeMillis());
scenario.setStepTotal(CollectionUtils.isNotEmpty(request.getSteps()) ? request.getSteps().size() : 0);
scenarioBatchMapper.updateByPrimaryKeySelective(scenario);
if (request.getScenarioConfig() != null) {
// 更新场景配置
ApiScenarioBlob apiScenarioBlob = new ApiScenarioBlob();
apiScenarioBlob.setId(scenario.getId());
apiScenarioBlob.setConfig(JSON.toJSONString(request.getScenarioConfig()).getBytes());
scenarioBlobBatchMapper.updateByPrimaryKeyWithBLOBs(apiScenarioBlob);
}
if (!emptyStepScenarioIds.contains(scenario.getId())) {
List<String> originStepDetailIds = extStepMapper.getStepIdsByScenarioId(scenario.getId());
// 更新场景步骤
this.updateApiScenarioStep(request, operator, originStepDetailIds, csvStepBatchMapper, stepBatchMapper, stepBlobBatchMapper);
}
// 处理 csv 文件
apiScenarioService.handleCsvUpdate(request.getScenarioConfig(), scenario, operator);
});
sqlSession.flushStatements();
});
}
private void updateApiScenarioStep(ApiScenarioImportDetail request, String userId, List<String> originStepDetailIds,
ApiScenarioCsvStepMapper csvStepBatchMapper, ApiScenarioStepMapper apiScenarioStepBatchMapper, ApiScenarioStepBlobMapper stepBlobBatchMapper) {
List<ApiScenarioStepRequest> steps = request.getSteps();
String scenarioId = request.getId();
String projectId = request.getProjectId();
// steps 不为 null 则修改
if (steps != null) {
if (CollectionUtils.isEmpty(steps)) {
return;
}
apiScenarioService.checkCircularRef(scenarioId, steps);
List<ApiScenarioCsvStep> scenarioCsvSteps = new ArrayList<>();
// 获取待更新的步骤
List<ApiScenarioStep> apiScenarioSteps = apiScenarioService.getApiScenarioSteps(null, steps, scenarioCsvSteps);
apiScenarioSteps.forEach(step -> step.setScenarioId(scenarioId));
scenarioCsvSteps.forEach(step -> step.setScenarioId(scenarioId));
scenarioCsvSteps = apiScenarioService.filterNotExistCsv(request.getScenarioConfig(), scenarioCsvSteps);
this.saveStepCsv(scenarioId, scenarioCsvSteps, csvStepBatchMapper);
// apiScenarioService.
// 获取待更新的步骤详情
apiScenarioService.addSpecialStepDetails(steps, request.getStepDetails());
List<ApiScenarioStepBlob> updateStepBlobs = apiScenarioService.getUpdateStepBlobs(apiScenarioSteps, request.getStepDetails());
updateStepBlobs.forEach(step -> step.setScenarioId(scenarioId));
List<String> stepIds = apiScenarioSteps.stream().map(ApiScenarioStep::getId).collect(Collectors.toList());
List<String> deleteStepIds = ListUtils.subtract(originStepDetailIds, stepIds);
// 步骤表-全部先删除再插入
ApiScenarioStepExample deleteStepExample = new ApiScenarioStepExample();
deleteStepExample.createCriteria().andScenarioIdEqualTo(scenarioId);
apiScenarioStepBatchMapper.deleteByExample(deleteStepExample);
apiScenarioStepBatchMapper.batchInsert(apiScenarioSteps);
// 详情表-删除已经删除的步骤详情
SubListUtils.dealForSubList(deleteStepIds, 100, subIds -> {
ApiScenarioStepBlobExample stepBlobExample = new ApiScenarioStepBlobExample();
stepBlobExample.createCriteria().andIdIn(subIds);
stepBlobBatchMapper.deleteByExample(stepBlobExample);
// 批量删除关联文件
String scenarioStepDirPrefix = DefaultRepositoryDir.getApiScenarioStepDir(projectId, scenarioId, StringUtils.EMPTY);
apiFileResourceService.deleteByResourceIds(scenarioStepDirPrefix, subIds, projectId, userId, OperationLogModule.API_SCENARIO_MANAGEMENT_SCENARIO);
});
// 添加新增的步骤详情
List<ApiScenarioStepBlob> addApiScenarioStepsDetails = updateStepBlobs.stream()
.filter(step -> !originStepDetailIds.contains(step.getId()))
.collect(Collectors.toList());
if (CollectionUtils.isNotEmpty(addApiScenarioStepsDetails)) {
stepBlobBatchMapper.batchInsert(addApiScenarioStepsDetails);
}
// 更新原有的步骤详情
updateStepBlobs.stream()
.filter(step -> originStepDetailIds.contains(step.getId()))
.forEach(stepBlobBatchMapper::updateByPrimaryKeySelective);
} else if (MapUtils.isNotEmpty(request.getStepDetails())) {
// steps nullstepDetails 不为 null则只更新详情
// 更新原有的步骤详情
request.getStepDetails().forEach((stepId, stepDetail) -> {
if (originStepDetailIds.contains(stepId)) {
ApiScenarioStepBlob apiScenarioStepBlob = apiScenarioService.getApiScenarioStepBlob(stepId, stepDetail);
stepBlobBatchMapper.updateByPrimaryKeySelective(apiScenarioStepBlob);
}
});
}
}
private void insertModule(String projectId, String operator, List<BaseTreeNode> insertModuleList, SqlSession sqlSession) {
if (CollectionUtils.isEmpty(insertModuleList)) {
return;
}
ApiScenarioModuleMapper batchApiDefinitionMapper = sqlSession.getMapper(ApiScenarioModuleMapper.class);
SubListUtils.dealForSubList(insertModuleList, 100, list -> {
list.forEach(t -> {
ApiScenarioModule module = new ApiScenarioModule();
module.setId(t.getId());
module.setName(t.getName());
module.setParentId(t.getParentId());
module.setProjectId(projectId);
module.setCreateUser(operator);
module.setPos(getImportNextModuleOrder(apiScenarioModuleService::getNextOrder, projectId));
module.setCreateTime(System.currentTimeMillis());
module.setUpdateUser(operator);
module.setUpdateTime(module.getCreateTime());
batchApiDefinitionMapper.insertSelective(module);
});
sqlSession.flushStatements();
});
}
private void insertScenarios(String projectId, String operator, String versionId, List<ApiScenarioImportDetail> insertScenarioList, SqlSession sqlSession) {
// 创建场景
if (CollectionUtils.isEmpty(insertScenarioList)) {
return;
}
ApiScenarioMapper apiScenarioBatchMapper = sqlSession.getMapper(ApiScenarioMapper.class);
ApiScenarioBlobMapper apiScenarioBlobBatchMapper = sqlSession.getMapper(ApiScenarioBlobMapper.class);
ApiScenarioCsvMapper csvBatchMapper = sqlSession.getMapper(ApiScenarioCsvMapper.class);
ApiScenarioCsvStepMapper csvStepBatchMapper = sqlSession.getMapper(ApiScenarioCsvStepMapper.class);
ApiScenarioStepMapper stepBatchMapper = sqlSession.getMapper(ApiScenarioStepMapper.class);
ApiScenarioStepBlobMapper stepBlobBatchMapper = sqlSession.getMapper(ApiScenarioStepBlobMapper.class);
SubListUtils.dealForSubList(insertScenarioList, 100, list -> {
list.forEach(t -> {
t.setId(IDGenerator.nextStr());
ApiScenario scenario = new ApiScenario();
BeanUtils.copyBean(scenario, t);
scenario.setNum(apiScenarioService.getNextNum(projectId));
scenario.setPos(getImportNextModuleOrder(apiScenarioService::getNextOrder, projectId));
scenario.setLatest(true);
scenario.setCreateUser(operator);
scenario.setUpdateUser(operator);
scenario.setCreateTime(System.currentTimeMillis());
scenario.setUpdateTime(System.currentTimeMillis());
scenario.setVersionId(versionId);
scenario.setRefId(scenario.getId());
scenario.setLastReportStatus(StringUtils.EMPTY);
scenario.setDeleted(false);
scenario.setRequestPassRate("0");
scenario.setStepTotal(CollectionUtils.size(t.getSteps()));
apiScenarioBatchMapper.insert(scenario);
// 更新场景配置
ApiScenarioBlob apiScenarioBlob = new ApiScenarioBlob();
apiScenarioBlob.setId(scenario.getId());
if (t.getScenarioConfig() == null) {
apiScenarioBlob.setConfig(JSON.toJSONString(new ScenarioConfig()).getBytes());
} else {
apiScenarioBlob.setConfig(JSON.toJSONString(t.getScenarioConfig()).getBytes());
}
apiScenarioBlobBatchMapper.insert(apiScenarioBlob);
// 处理csv文件
this.handCsvFilesAdd(t, operator, scenario, csvBatchMapper, csvStepBatchMapper);
// 处理添加的步骤
this.handleStepAdd(t, scenario, csvStepBatchMapper, stepBatchMapper, stepBlobBatchMapper);
});
sqlSession.flushStatements();
});
}
private void handleStepAdd(ApiScenarioImportDetail t, ApiScenario scenario,
ApiScenarioCsvStepMapper csvStepBatchMapper, ApiScenarioStepMapper stepBatchMapper, ApiScenarioStepBlobMapper stepBlobBatchMapper) {
// 插入步骤
if (CollectionUtils.isNotEmpty(t.getSteps())) {
//检测循环引用
apiScenarioService.checkCircularRef(scenario.getId(), t.getSteps());
// 获取待添加的步骤
List<ApiScenarioCsvStep> csvSteps = new ArrayList<>();
List<ApiScenarioStep> steps = apiScenarioService.getApiScenarioSteps(null, t.getSteps(), csvSteps);
steps.forEach(step -> step.setScenarioId(scenario.getId()));
// 处理特殊的步骤详情
apiScenarioService.addSpecialStepDetails(t.getSteps(), t.getStepDetails());
List<ApiScenarioStepBlob> apiScenarioStepBlobs = apiScenarioService.getUpdateStepBlobs(steps, t.getStepDetails());
apiScenarioStepBlobs.forEach(step -> step.setScenarioId(scenario.getId()));
//保存步骤
if (CollectionUtils.isNotEmpty(steps)) {
stepBatchMapper.batchInsert(steps);
}
if (CollectionUtils.isNotEmpty(apiScenarioStepBlobs)) {
stepBlobBatchMapper.batchInsert(apiScenarioStepBlobs);
}
csvSteps = apiScenarioService.filterNotExistCsv(t.getScenarioConfig(), csvSteps);
this.saveStepCsv(scenario.getId(), csvSteps, csvStepBatchMapper);
}
}
private void saveStepCsv(String scenarioId, List<ApiScenarioCsvStep> csvSteps, ApiScenarioCsvStepMapper csvStepBatchMapper) {
// 先删除
ApiScenarioCsvStepExample csvStepExample = new ApiScenarioCsvStepExample();
csvStepExample.createCriteria().andScenarioIdEqualTo(scenarioId);
csvStepBatchMapper.deleteByExample(csvStepExample);
// 再添加
if (CollectionUtils.isNotEmpty(csvSteps)) {
csvStepBatchMapper.batchInsert(csvSteps);
}
}
private void handCsvFilesAdd(ApiScenarioImportDetail t, String operator, ApiScenario scenario,
ApiScenarioCsvMapper batchCsvMapper, ApiScenarioCsvStepMapper batchCsvStepMapper) {
List<CsvVariable> csvVariables = apiScenarioService.getCsvVariables(t.getScenarioConfig());
if (CollectionUtils.isEmpty(csvVariables)) {
return;
}
// 处理 csv 相关数据表
this.handleCsvDataUpdate(csvVariables, scenario, List.of(), batchCsvMapper, batchCsvStepMapper);
// 处理文件的上传 调用流程很长目前没想到有好的批量处理方法暂时直接调用Service
apiScenarioService.handleCsvFileAdd(csvVariables, List.of(), scenario, operator);
}
private void handleCsvDataUpdate(List<CsvVariable> csvVariables, ApiScenario scenario, List<String> dbCsvIds,
ApiScenarioCsvMapper batchCsvMapper, ApiScenarioCsvStepMapper batchCsvStepMapper) {
List<String> csvIds = csvVariables.stream()
.map(CsvVariable::getId)
.toList();
List<String> deleteCsvIds = ListUtils.subtract(dbCsvIds, csvIds);
//删除不存在的数据
if (CollectionUtils.isNotEmpty(deleteCsvIds)) {
ApiScenarioCsvExample example = new ApiScenarioCsvExample();
example.createCriteria().andIdIn(deleteCsvIds);
batchCsvMapper.deleteByExample(example);
ApiScenarioCsvStepExample stepExample = new ApiScenarioCsvStepExample();
stepExample.createCriteria().andIdIn(deleteCsvIds);
batchCsvStepMapper.deleteByExample(stepExample);
}
Set<String> dbCsvIdSet = new HashSet<>(dbCsvIds);
List<ApiScenarioCsv> addCsvList = new ArrayList<>();
csvVariables.forEach(item -> {
ApiScenarioCsv scenarioCsv = new ApiScenarioCsv();
BeanUtils.copyBean(scenarioCsv, item);
scenarioCsv.setScenarioId(scenario.getId());
scenarioCsv.setProjectId(scenario.getProjectId());
ApiFile file = item.getFile();
scenarioCsv.setFileId(file.getFileId());
scenarioCsv.setFileName(file.getFileName());
scenarioCsv.setAssociation(BooleanUtils.isFalse(file.getLocal()));
if (!dbCsvIdSet.contains(item.getId())) {
addCsvList.add(scenarioCsv);
} else {
batchCsvMapper.updateByPrimaryKey(scenarioCsv);
}
});
if (CollectionUtils.isNotEmpty(addCsvList)) {
batchCsvMapper.batchInsert(addCsvList);
}
}
private Long getImportNextModuleOrder(Function<String, Long> subFunc, String projectId) {
Long order = currentModuleOrder.get();
if (order == null) {
order = subFunc.apply(projectId);
}
order = order + DEFAULT_NODE_INTERVAL_POS;
currentModuleOrder.set(order);
return order;
}
private ApiScenarioPreImportAnalysisResult importAnalysis(List<ApiScenarioImportDetail> importScenarios, String moduleId, List<BaseTreeNode> apiScenarioModules) {
ApiScenarioPreImportAnalysisResult analysisResult = new ApiScenarioPreImportAnalysisResult();
Map<String, String> moduleIdPathMap = apiScenarioModules.stream().collect(Collectors.toMap(BaseTreeNode::getId, BaseTreeNode::getPath));
Map<String, BaseTreeNode> modulePathMap = apiScenarioModules.stream().collect(Collectors.toMap(BaseTreeNode::getPath, k -> k, (k1, k2) -> k1));
for (ApiScenarioImportDetail importScenario : importScenarios) {
if (StringUtils.isBlank(moduleId) || StringUtils.equalsIgnoreCase(moduleId, ModuleConstants.DEFAULT_NODE_ID) || !moduleIdPathMap.containsKey(moduleId)) {
importScenario.setModuleId(ModuleConstants.DEFAULT_NODE_ID);
importScenario.setModulePath(moduleIdPathMap.get(ModuleConstants.DEFAULT_NODE_ID));
} else {
if (StringUtils.isBlank(importScenario.getModulePath())) {
importScenario.setModulePath(moduleIdPathMap.get(moduleId));
} else if (StringUtils.startsWith(importScenario.getModulePath(), "/")) {
importScenario.setModulePath(moduleIdPathMap.get(moduleId) + importScenario.getModulePath());
} else {
importScenario.setModulePath(moduleIdPathMap.get(moduleId) + "/" + importScenario.getModulePath());
}
}
}
//检查重复的场景模块+名称
Map<String, List<ApiScenarioImportDetail>> modulePathScenario = importScenarios.stream().collect(Collectors.groupingBy(ApiScenarioImportDetail::getModulePath));
modulePathScenario.forEach((modulePath, scenarios) -> {
if (!StringUtils.startsWith(modulePath, "/")) {
modulePath = "/" + modulePath;
}
if (modulePathMap.containsKey(modulePath)) {
List<ApiScenario> existenceScenarios = extApiScenarioMapper.selectBaseInfoByModuleId(modulePathMap.get(modulePath).getId());
Map<String, String> existenceNameIdMap = existenceScenarios.stream().collect(Collectors.toMap(ApiScenario::getName, ApiScenario::getId));
scenarios.forEach(scenario -> {
if (existenceNameIdMap.containsKey(scenario.getName())) {
scenario.setId(existenceNameIdMap.get(scenario.getName()));
analysisResult.getUpdateApiScenarioData().add(scenario);
} else {
analysisResult.getInsertApiScenarioData().add(scenario);
}
});
} else {
//模块不存在的必定是新建
analysisResult.getInsertModuleList().addAll(TreeNodeParseUtils.getInsertNodeByPath(modulePathMap, modulePath));
String finalModulePath = modulePath;
scenarios.forEach(scenario ->
scenario.setModuleId(modulePathMap.get(finalModulePath).getId())
);
analysisResult.getInsertApiScenarioData().addAll(scenarios);
}
});
return analysisResult;
}
private String exportApiScenarioZip(ApiScenarioBatchExportRequest request, String type, String userId) {
// todo 场景导出
return null;
}
}

View File

@ -118,26 +118,26 @@ public class ApiDefinitionImportService {
@Transactional(rollbackFor = Exception.class)
public void apiDefinitionImport(MultipartFile file, ImportRequest request, String projectId) {
this.initImportRequestAndCheck(file, request, projectId);
ApiDefinitionImportParser<?> runService = ImportParserFactory.getImportParser(request.getPlatform());
ApiDefinitionImportParser<?> runService = ImportParserFactory.getApiDefinitionImportParser(request.getPlatform());
assert runService != null;
ApiImportDataAnalysisResult apiImportDataAnalysisResult = new ApiImportDataAnalysisResult();
ApiDefinitionImportDataAnalysisResult apiDefinitionImportDataAnalysisResult = new ApiDefinitionImportDataAnalysisResult();
try {
//解析文件
ApiImportFileParseResult fileParseResult = (ApiImportFileParseResult) runService.parse(file == null ? null : file.getInputStream(), request);
ApiDefinitionImportFileParseResult fileParseResult = (ApiDefinitionImportFileParseResult) runService.parse(file == null ? null : file.getInputStream(), request);
if (!CollectionUtils.isEmpty(fileParseResult.getData())) {
ApiDefinitionPageRequest pageRequest = new ApiDefinitionPageRequest();
pageRequest.setProjectId(request.getProjectId());
pageRequest.setProtocols(fileParseResult.getApiProtocols());
List<ApiDefinitionDetail> existenceApiDefinitionList = extApiDefinitionMapper.importList(pageRequest);
//分析有哪些数据需要新增有哪些数据需要更新
apiImportDataAnalysisResult = runService.generateInsertAndUpdateData(fileParseResult, existenceApiDefinitionList);
apiDefinitionImportDataAnalysisResult = runService.generateInsertAndUpdateData(fileParseResult, existenceApiDefinitionList);
}
} catch (Exception e) {
LogUtils.error(e.getMessage(), e);
throw new MSException(Translator.get("parse_data_error"));
}
if (apiImportDataAnalysisResult.isEmpty()) {
if (apiDefinitionImportDataAnalysisResult.isEmpty()) {
throw new MSException(Translator.get("parse_empty_data"));
}
@ -149,7 +149,7 @@ public class ApiDefinitionImportService {
request.setVersionId(defaultVersion);
}
//通过导入配置预处理数据确定哪些要创建哪些要修改
ApiDefinitionPreImportAnalysisResult preImportAnalysisResult = this.preImportAnalysis(request, apiImportDataAnalysisResult);
ApiDefinitionPreImportAnalysisResult preImportAnalysisResult = this.preImportAnalysis(request, apiDefinitionImportDataAnalysisResult);
//入库
List<LogDTO> operationLogs = this.insertData(preImportAnalysisResult, request);
operationLogService.batchAdd(operationLogs);
@ -513,7 +513,7 @@ public class ApiDefinitionImportService {
/**
* 预导入数据分析 根据请求配置判断哪些数据新增哪些数据修改且修改它的哪部分数据
*/
private ApiDefinitionPreImportAnalysisResult preImportAnalysis(ImportRequest request, ApiImportDataAnalysisResult insertAndUpdateData) {
private ApiDefinitionPreImportAnalysisResult preImportAnalysis(ImportRequest request, ApiDefinitionImportDataAnalysisResult insertAndUpdateData) {
ApiDefinitionPreImportAnalysisResult preImportAnalysisResult = new ApiDefinitionPreImportAnalysisResult();
// api模块树查询
@ -548,7 +548,7 @@ public class ApiDefinitionImportService {
*/
private void furtherProcessingExistenceApiData(String selectModuleId, String selectModulePath,
boolean isCoverModule,
ApiImportDataAnalysisResult insertAndUpdateData,
ApiDefinitionImportDataAnalysisResult insertAndUpdateData,
ApiDefinitionPreImportAnalysisResult apiDefinitionPreImportAnalysisResult,
Map<String, BaseTreeNode> modulePathMap) {
for (ApiDefinitionDetail importApi : insertAndUpdateData.getInsertApiList()) {
@ -600,7 +600,7 @@ public class ApiDefinitionImportService {
指定了导入模块 直接塞入指定模块中
未指定导入模块 接口有模块就放在那个模块下 接口没模块就放在未规划模块内
*/
private void inertDataAnalysis(ApiDefinitionPreImportAnalysisResult apiDefinitionPreImportAnalysisResult, ImportRequest request, String selectModulePath, Map<String, BaseTreeNode> modulePathMap, ApiImportDataAnalysisResult analysisResult) {
private void inertDataAnalysis(ApiDefinitionPreImportAnalysisResult apiDefinitionPreImportAnalysisResult, ImportRequest request, String selectModulePath, Map<String, BaseTreeNode> modulePathMap, ApiDefinitionImportDataAnalysisResult analysisResult) {
for (ApiDefinitionDetail apiData : analysisResult.getInsertApiList()) {
apiDefinitionPreImportAnalysisResult.getInsertApiData().add(apiData);
//判断是否更新用例
@ -619,7 +619,7 @@ public class ApiDefinitionImportService {
}
// 已有数据处理
private void existenceDataAnalysis(ApiDefinitionPreImportAnalysisResult apiDefinitionPreImportAnalysisResult, ImportRequest request, String selectModulePath, Map<String, BaseTreeNode> modulePathMap, ApiImportDataAnalysisResult analysisResult) {
private void existenceDataAnalysis(ApiDefinitionPreImportAnalysisResult apiDefinitionPreImportAnalysisResult, ImportRequest request, String selectModulePath, Map<String, BaseTreeNode> modulePathMap, ApiDefinitionImportDataAnalysisResult analysisResult) {
//不选择覆盖接口或者数据为空终止操作
if (CollectionUtils.isEmpty(analysisResult.getExistenceApiList()) || !request.isCoverData()) {
return;

View File

@ -65,6 +65,14 @@ public class ApiScenarioModuleService extends ModuleTreeService {
return super.buildTreeAndCountResource(fileModuleList, true, Translator.get(UNPLANNED_SCENARIO));
}
public List<BaseTreeNode> getTree(String projectId) {
//接口的树结构是 模块子模块+接口 接口为非delete状态的
List<BaseTreeNode> fileModuleList = extApiScenarioModuleMapper.selectBaseByRequest(new ApiScenarioModuleRequest() {{
this.setProjectId(projectId);
}});
return super.buildTreeAndCountResource(fileModuleList, true, Translator.get(UNPLANNED_SCENARIO));
}
public List<BaseTreeNode> getTreeOnlyIdsAndResourceCount(ApiScenarioModuleRequest request, List<ModuleCountDTO> moduleCountDTOList) {
//节点内容只有Id和parentId
List<BaseTreeNode> fileModuleList = extApiScenarioModuleMapper.selectIdAndParentIdByRequest(request);

View File

@ -482,7 +482,7 @@ public class ApiScenarioService extends MoveNodeService {
}
}
private List<ApiScenarioCsvStep> filterNotExistCsv(ScenarioConfig scenarioConfig, List<ApiScenarioCsvStep> csvSteps) {
public List<ApiScenarioCsvStep> filterNotExistCsv(ScenarioConfig scenarioConfig, List<ApiScenarioCsvStep> csvSteps) {
Set<String> csvIdSet =
getCsvVariables(scenarioConfig)
.stream()
@ -560,7 +560,7 @@ public class ApiScenarioService extends MoveNodeService {
}
}
private void handleCsvUpdate(ScenarioConfig scenarioConfig, ApiScenario scenario, String userId) {
public void handleCsvUpdate(ScenarioConfig scenarioConfig, ApiScenario scenario, String userId) {
if (scenarioConfig == null) {
return;
}
@ -687,7 +687,7 @@ public class ApiScenarioService extends MoveNodeService {
}
}
private void handleCsvFileAdd(List<CsvVariable> csvVariables, List<ApiScenarioCsv> dbCsv, ApiScenario scenario, String userId) {
public void handleCsvFileAdd(List<CsvVariable> csvVariables, List<ApiScenarioCsv> dbCsv, ApiScenario scenario, String userId) {
ApiFileResourceUpdateRequest resourceUpdateRequest = getApiFileResourceUpdateRequest(scenario.getId(), scenario.getProjectId(), userId);
// 设置本地文件相关参数
setCsvLocalFileParam(csvVariables, dbCsv, resourceUpdateRequest);
@ -705,7 +705,7 @@ public class ApiScenarioService extends MoveNodeService {
apiFileResourceService.updateFileResource(resourceUpdateRequest);
}
private void setCsvLinkFileParam(List<CsvVariable> csvVariables, List<ApiScenarioCsv> dbCsv, ApiFileResourceUpdateRequest resourceUpdateRequest) {
public void setCsvLinkFileParam(List<CsvVariable> csvVariables, List<ApiScenarioCsv> dbCsv, ApiFileResourceUpdateRequest resourceUpdateRequest) {
// 获取数据库中关联的文件id
List<String> dbRefFileIds = dbCsv.stream()
.filter(c -> BooleanUtils.isTrue(c.getAssociation()) && StringUtils.isNotBlank(c.getFileId()))
@ -724,7 +724,7 @@ public class ApiScenarioService extends MoveNodeService {
resourceUpdateRequest.setLinkFileIds(linkFileIds);
}
private void setCsvLocalFileParam(List<CsvVariable> csvVariables, List<ApiScenarioCsv> dbCsv, ApiFileResourceUpdateRequest resourceUpdateRequest) {
public void setCsvLocalFileParam(List<CsvVariable> csvVariables, List<ApiScenarioCsv> dbCsv, ApiFileResourceUpdateRequest resourceUpdateRequest) {
// 获取数据库中的本地文件
List<String> dbLocalFileIds = dbCsv.stream()
.filter(c -> BooleanUtils.isFalse(c.getAssociation()))
@ -896,7 +896,7 @@ public class ApiScenarioService extends MoveNodeService {
* @param scenarioId
* @param steps
*/
private void checkCircularRef(String scenarioId, List<ApiScenarioStepRequest> steps) {
public void checkCircularRef(String scenarioId, List<ApiScenarioStepRequest> steps) {
traversalStepTree(steps, step -> {
if (isRefOrPartialRef(step.getRefType()) && StringUtils.equals(step.getResourceId(), scenarioId)) {
throw new MSException(API_SCENARIO_CIRCULAR_REFERENCE);
@ -905,7 +905,7 @@ public class ApiScenarioService extends MoveNodeService {
});
}
private ApiScenarioStepBlob getApiScenarioStepBlob(String stepId, Object stepDetail) {
public ApiScenarioStepBlob getApiScenarioStepBlob(String stepId, Object stepDetail) {
ApiScenarioStepBlob apiScenarioStepBlob = new ApiScenarioStepBlob();
apiScenarioStepBlob.setId(stepId);
apiScenarioStepBlob.setContent(JSON.toJSONString(stepDetail).getBytes());
@ -927,7 +927,7 @@ public class ApiScenarioService extends MoveNodeService {
/**
* 获取待更新的 ApiScenarioStepBlob 列表
*/
private List<ApiScenarioStepBlob> getUpdateStepBlobs(List<ApiScenarioStep> apiScenarioSteps, Map<String, Object> stepDetails) {
public List<ApiScenarioStepBlob> getUpdateStepBlobs(List<ApiScenarioStep> apiScenarioSteps, Map<String, Object> stepDetails) {
if (MapUtils.isEmpty(stepDetails)) {
return Collections.emptyList();
}
@ -989,7 +989,7 @@ public class ApiScenarioService extends MoveNodeService {
* 解析步骤树结构
* 获取待更新的 ApiScenarioStep 列表
*/
private List<ApiScenarioStep> getApiScenarioSteps(ApiScenarioStepCommonDTO parent,
public List<ApiScenarioStep> getApiScenarioSteps(ApiScenarioStepCommonDTO parent,
List<ApiScenarioStepRequest> steps, List<ApiScenarioCsvStep> csvSteps) {
if (CollectionUtils.isEmpty(steps)) {
return Collections.emptyList();
@ -1185,7 +1185,7 @@ public class ApiScenarioService extends MoveNodeService {
return partialRefStepDetail;
}
private ApiFileResourceUpdateRequest getApiFileResourceUpdateRequest(String sourceId, String projectId, String operator) {
public ApiFileResourceUpdateRequest getApiFileResourceUpdateRequest(String sourceId, String projectId, String operator) {
String apiScenarioDir = DefaultRepositoryDir.getApiScenarioDir(projectId, sourceId);
ApiFileResourceUpdateRequest resourceUpdateRequest = new ApiFileResourceUpdateRequest();
resourceUpdateRequest.setProjectId(projectId);

View File

@ -0,0 +1,25 @@
package io.metersphere.api.utils;
import io.metersphere.project.domain.Project;
import io.metersphere.sdk.constants.HttpMethodConstants;
import io.metersphere.sdk.util.JSON;
import io.metersphere.system.log.dto.LogDTO;
public class ApiScenarioImportUtils {
public static LogDTO genImportLog(Project project, String dataId, String dataName, Object importData, String module, String operator, String operationType) {
LogDTO dto = new LogDTO(
project.getId(),
project.getOrganizationId(),
dataId,
operator,
operationType,
module,
dataName);
dto.setHistory(true);
dto.setPath("/api/scenario/import");
dto.setMethod(HttpMethodConstants.POST.name());
dto.setOriginalValue(JSON.toJSONBytes(importData));
return dto;
}
}

View File

@ -1745,7 +1745,7 @@ public class ApiDefinitionControllerTests extends BaseTest {
private void importTest() throws Exception {
//测试ImportParserFactory不按规定获取会返回null
Assertions.assertNull(ImportParserFactory.getImportParser("test"));
Assertions.assertNull(ImportParserFactory.getApiDefinitionImportParser("test"));
// 创建用于导入的项目
//测试计划专用项目
AddProjectRequest initProject = new AddProjectRequest();
@ -1798,7 +1798,6 @@ public class ApiDefinitionControllerTests extends BaseTest {
//导入类型以及文件后缀
Map<String, String> importTypeAndSuffix = new LinkedHashMap<>();
// importTypeAndSuffix.put("jmeter", "jmx");
importTypeAndSuffix.put("metersphere", "json");
importTypeAndSuffix.put("postman", "json");
importTypeAndSuffix.put("har", "har");

View File

@ -0,0 +1,97 @@
package io.metersphere.api.controller;
import io.metersphere.api.dto.definition.ApiDefinitionBatchExportRequest;
import io.metersphere.api.dto.scenario.ApiScenarioImportRequest;
import io.metersphere.project.domain.Project;
import io.metersphere.sdk.util.JSON;
import io.metersphere.system.base.BaseTest;
import io.metersphere.system.dto.AddProjectRequest;
import io.metersphere.system.log.constants.OperationLogModule;
import io.metersphere.system.service.CommonProjectService;
import io.metersphere.system.uid.IDGenerator;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.*;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import java.io.File;
import java.io.FileInputStream;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class ApiScenarioControllerImportAndExportTests extends BaseTest {
private static final String URL_POST_IMPORT = "/api/scenario/import";
private static final String URL_POST_EXPORT = "/api/scenario/export/";
private static Project project;
@Resource
private CommonProjectService commonProjectService;
@BeforeEach
public void initTestData() {
//文件管理专用项目
if (project == null) {
AddProjectRequest initProject = new AddProjectRequest();
initProject.setOrganizationId("100001");
initProject.setName("场景导入专用");
initProject.setDescription("场景导入专用项目");
initProject.setEnable(true);
initProject.setUserIds(List.of("admin"));
project = commonProjectService.add(initProject, "admin", "/organization-project/add", OperationLogModule.SETTING_ORGANIZATION_PROJECT);
// ArrayList<String> moduleList = new ArrayList<>(List.of("workstation", "testPlan", "bugManagement", "caseManagement", "apiTest", "uiTest", "loadTest"));
// Project updateProject = new Project();
// updateProject.setId(importProject.getId());
// updateProject.setModuleSetting(JSON.toJSONString(moduleList));
// projectMapper.updateByPrimaryKeySelective(updateProject);
}
}
@Test
@Order(1)
public void testImport() throws Exception {
Map<String, String> importTypeAndSuffix = new LinkedHashMap<>();
// importTypeAndSuffix.put("metersphere", "json");
importTypeAndSuffix.put("jmeter", "jmx");
for (Map.Entry<String, String> entry : importTypeAndSuffix.entrySet()) {
ApiScenarioImportRequest request = new ApiScenarioImportRequest();
request.setProjectId(project.getId());
request.setType(entry.getKey());
String importType = entry.getKey();
String fileSuffix = entry.getValue();
FileInputStream inputStream = new FileInputStream(new File(Objects.requireNonNull(this.getClass().getClassLoader().getResource("file/import-scenario/" + importType + "/simple." + fileSuffix)).getPath()));
MockMultipartFile file = new MockMultipartFile("file", "simple." + fileSuffix, MediaType.APPLICATION_OCTET_STREAM_VALUE, inputStream);
MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
paramMap.add("request", JSON.toJSONString(request));
paramMap.add("file", file);
this.requestMultipartWithOkAndReturn(URL_POST_IMPORT, paramMap);
}
}
@Test
@Order(1)
public void testExport() throws Exception {
ApiDefinitionBatchExportRequest exportRequest = new ApiDefinitionBatchExportRequest();
String fileId = IDGenerator.nextStr();
exportRequest.setProjectId(project.getId());
exportRequest.setFileId(fileId);
exportRequest.setSelectAll(true);
exportRequest.setExportApiCase(true);
exportRequest.setExportApiMock(true);
MvcResult mvcResult = this.requestPostWithOkAndReturn(URL_POST_EXPORT + "metersphere", exportRequest);
String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
}
}

View File

@ -17,6 +17,6 @@ public class ParserTests {
@Test
@Order(3)
public void testImportParserMs() throws Exception {
ImportParserFactory.getImportParser(ApiImportPlatform.MeterSphere.name());
ImportParserFactory.getApiDefinitionImportParser(ApiImportPlatform.MeterSphere.name());
}
}

View File

@ -0,0 +1,267 @@
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="5.0" jmeter="5.5">
<hashTree>
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="MS-3个接口" 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="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="TestPlan.user_define_classpath"></stringProp>
</TestPlan>
<hashTree>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="测试响应断言导入ms错误" enabled="true">
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" 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>
</ThreadGroup>
<hashTree>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="getbaidu-test1111" enabled="true">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="HTTPSampler.domain">www.baidu.com</stringProp>
<stringProp name="HTTPSampler.port"></stringProp>
<stringProp name="HTTPSampler.protocol"></stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path">/test1111</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>
<ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true">
<collectionProp name="Asserion.test_strings">
<stringProp name="3556498">test</stringProp>
</collectionProp>
<stringProp name="Assertion.custom_message"></stringProp>
<stringProp name="Assertion.test_field">Assertion.response_data</stringProp>
<boolProp name="Assertion.assume_success">false</boolProp>
<intProp name="Assertion.test_type">20</intProp>
</ResponseAssertion>
<hashTree/>
<ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="View Results Tree" 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>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="测试2" enabled="true">
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" 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>
</ThreadGroup>
<hashTree>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="getbaidu-test22222" enabled="true">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="HTTPSampler.domain">www.baidu.com</stringProp>
<stringProp name="HTTPSampler.port"></stringProp>
<stringProp name="HTTPSampler.protocol"></stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path">/test2222</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>
<ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true">
<collectionProp name="Asserion.test_strings">
<stringProp name="3556498">test</stringProp>
</collectionProp>
<stringProp name="Assertion.custom_message"></stringProp>
<stringProp name="Assertion.test_field">Assertion.response_data</stringProp>
<boolProp name="Assertion.assume_success">false</boolProp>
<intProp name="Assertion.test_type">20</intProp>
</ResponseAssertion>
<hashTree/>
</hashTree>
</hashTree>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Thread Group" enabled="true">
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" 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>
</ThreadGroup>
<hashTree>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="apitester登录21" enabled="true">
<boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
<elementProp name="HTTPsampler.Arguments" elementType="Arguments">
<collectionProp name="Arguments.arguments">
<elementProp name="" elementType="HTTPArgument">
<boolProp name="HTTPArgument.always_encode">false</boolProp>
<stringProp name="Argument.value">{&#xd;
&quot;username&quot; : &quot;apitester@fit2cloud.com&quot;,&#xd;
&quot;password&quot; : &quot;apitester@fit2cloud.com&quot;,&#xd;
&quot;authenticate&quot; : &quot;local&quot;&#xd;
}</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
</collectionProp>
</elementProp>
<stringProp name="HTTPSampler.domain">qadevtest.fit2cloud.com</stringProp>
<stringProp name="HTTPSampler.port"></stringProp>
<stringProp name="HTTPSampler.protocol">https</stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path">/login</stringProp>
<stringProp name="HTTPSampler.method">POST</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>
<HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true">
<collectionProp name="HeaderManager.headers">
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Content-Type</stringProp>
<stringProp name="Header.value">application/json</stringProp>
</elementProp>
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Accept</stringProp>
<stringProp name="Header.value">*/*</stringProp>
</elementProp>
</collectionProp>
</HeaderManager>
<hashTree/>
<JSONPostProcessor guiclass="JSONPostProcessorGui" testclass="JSONPostProcessor" testname="提取token" enabled="true">
<stringProp name="JSONPostProcessor.referenceNames">csrfToken</stringProp>
<stringProp name="JSONPostProcessor.jsonPathExprs">$.data.csrfToken</stringProp>
<stringProp name="JSONPostProcessor.match_numbers"></stringProp>
</JSONPostProcessor>
<hashTree/>
<JSONPostProcessor guiclass="JSONPostProcessorGui" testclass="JSONPostProcessor" testname="提取sessionId" enabled="true">
<stringProp name="JSONPostProcessor.referenceNames">sessionId</stringProp>
<stringProp name="JSONPostProcessor.jsonPathExprs">$.data.sessionId</stringProp>
<stringProp name="JSONPostProcessor.match_numbers"></stringProp>
</JSONPostProcessor>
<hashTree/>
<BeanShellPostProcessor guiclass="TestBeanGUI" testclass="BeanShellPostProcessor" testname="BeanShell PostProcessor" enabled="true">
<stringProp name="filename"></stringProp>
<stringProp name="parameters"></stringProp>
<boolProp name="resetInterpreter">false</boolProp>
<stringProp name="script">log.info(&quot;output the token and x-auth=====&quot;+ &quot;${csrfToken}&quot;);
log.info(&quot;output the token and x-auth=====&quot;+ &quot;${sessionId}&quot;);</stringProp>
</BeanShellPostProcessor>
<hashTree/>
<ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="View Results Tree" 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/>
<BeanShellListener guiclass="TestBeanGUI" testclass="BeanShellListener" testname="BeanShell Listener" enabled="true">
<stringProp name="filename"></stringProp>
<stringProp name="parameters"></stringProp>
<boolProp name="resetInterpreter">false</boolProp>
<stringProp name="script"></stringProp>
</BeanShellListener>
<hashTree/>
</hashTree>
</hashTree>
</hashTree>
</hashTree>
</jmeterTestPlan>

View File

@ -3,7 +3,7 @@ package io.metersphere.system.constants;
public class ExportConstants {
public enum ExportType {
API_DEFINITION, CASE
API_SCENARIO, API_DEFINITION, CASE
}
public enum ExportState {