feat(接口测试): 场景JMX导入功能开发
This commit is contained in:
parent
bce9c9d9bb
commit
d13ee84e2b
|
@ -42,7 +42,12 @@ public enum ApiScenarioStepType {
|
||||||
/**
|
/**
|
||||||
* 脚本操作
|
* 脚本操作
|
||||||
*/
|
*/
|
||||||
SCRIPT(StepTypeGroup.REQUEST);
|
SCRIPT(StepTypeGroup.REQUEST),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JMeter插件
|
||||||
|
*/
|
||||||
|
JMETER_COMPONENT(StepTypeGroup.REQUEST);
|
||||||
|
|
||||||
|
|
||||||
private enum StepTypeGroup {
|
private enum StepTypeGroup {
|
||||||
|
|
|
@ -98,6 +98,7 @@ public class ApiScenarioBatchOperationController {
|
||||||
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
|
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
|
||||||
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_EXECUTE)
|
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_EXECUTE)
|
||||||
public void batchRun(@Validated @RequestBody ApiScenarioBatchRunRequest request) {
|
public void batchRun(@Validated @RequestBody ApiScenarioBatchRunRequest request) {
|
||||||
|
apiValidateService.validateApiMenuInProject(request.getProjectId(), ApiResource.PROJECT.name());
|
||||||
apiScenarioBatchRunService.asyncBatchRun(request, SessionUtils.getUserId());
|
apiScenarioBatchRunService.asyncBatchRun(request, SessionUtils.getUserId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,11 +6,13 @@ import io.metersphere.api.constants.ApiResource;
|
||||||
import io.metersphere.api.domain.ApiScenario;
|
import io.metersphere.api.domain.ApiScenario;
|
||||||
import io.metersphere.api.dto.ReferenceDTO;
|
import io.metersphere.api.dto.ReferenceDTO;
|
||||||
import io.metersphere.api.dto.ReferenceRequest;
|
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.ExecutePageRequest;
|
||||||
import io.metersphere.api.dto.definition.ExecuteReportDTO;
|
import io.metersphere.api.dto.definition.ExecuteReportDTO;
|
||||||
import io.metersphere.api.dto.request.ApiTransferRequest;
|
import io.metersphere.api.dto.request.ApiTransferRequest;
|
||||||
import io.metersphere.api.dto.scenario.*;
|
import io.metersphere.api.dto.scenario.*;
|
||||||
import io.metersphere.api.service.ApiFileResourceService;
|
import io.metersphere.api.service.ApiFileResourceService;
|
||||||
|
import io.metersphere.api.service.ApiScenarioDataTransferService;
|
||||||
import io.metersphere.api.service.ApiValidateService;
|
import io.metersphere.api.service.ApiValidateService;
|
||||||
import io.metersphere.api.service.scenario.ApiScenarioLogService;
|
import io.metersphere.api.service.scenario.ApiScenarioLogService;
|
||||||
import io.metersphere.api.service.scenario.ApiScenarioNoticeService;
|
import io.metersphere.api.service.scenario.ApiScenarioNoticeService;
|
||||||
|
@ -58,6 +60,8 @@ public class ApiScenarioController {
|
||||||
private FileModuleService fileModuleService;
|
private FileModuleService fileModuleService;
|
||||||
@Resource
|
@Resource
|
||||||
private ApiFileResourceService apiFileResourceService;
|
private ApiFileResourceService apiFileResourceService;
|
||||||
|
@Resource
|
||||||
|
private ApiScenarioDataTransferService apiScenarioDataTransferService;
|
||||||
|
|
||||||
@PostMapping("/page")
|
@PostMapping("/page")
|
||||||
@Operation(summary = "接口测试-接口场景管理-场景列表(deleted 状态为 1 时为回收站数据)")
|
@Operation(summary = "接口测试-接口场景管理-场景列表(deleted 状态为 1 时为回收站数据)")
|
||||||
|
@ -307,4 +311,23 @@ public class ApiScenarioController {
|
||||||
StringUtils.isNotBlank(request.getSortString()) ? request.getSortString() : "id desc");
|
StringUtils.isNotBlank(request.getSortString()) ? request.getSortString() : "id desc");
|
||||||
return PageUtils.setPageInfo(page, apiScenarioService.getReference(request));
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ import java.util.Map;
|
||||||
* api导入数据分析结果
|
* api导入数据分析结果
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
public class ApiImportDataAnalysisResult {
|
public class ApiDefinitionImportDataAnalysisResult {
|
||||||
|
|
||||||
// 新增接口数据
|
// 新增接口数据
|
||||||
List<ApiDefinitionDetail> insertApiList = new ArrayList<>();
|
List<ApiDefinitionDetail> insertApiList = new ArrayList<>();
|
|
@ -13,7 +13,7 @@ import java.util.Map;
|
||||||
* api导入文件解析结果
|
* api导入文件解析结果
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
public class ApiImportFileParseResult {
|
public class ApiDefinitionImportFileParseResult {
|
||||||
// 接口定义数据
|
// 接口定义数据
|
||||||
private List<ApiDefinitionDetail> data = new ArrayList<>();
|
private List<ApiDefinitionDetail> data = new ArrayList<>();
|
||||||
// 用例数据
|
// 用例数据
|
|
@ -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<>();
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -2,10 +2,6 @@ package io.metersphere.api.dto.scenario;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
/**
|
|
||||||
* @Author: jianxing
|
|
||||||
* @CreateTime: 2024-01-10 11:24
|
|
||||||
*/
|
|
||||||
@Data
|
@Data
|
||||||
public class ApiScenarioStepRequest extends ApiScenarioStepCommonDTO<ApiScenarioStepRequest> {
|
public class ApiScenarioStepRequest extends ApiScenarioStepCommonDTO<ApiScenarioStepRequest> {
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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> 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> getListBySelectIds(@Param("projectId") String projectId, @Param("ids") List<String> ids, @Param("testPlanId") String testPlanId);
|
||||||
|
|
||||||
|
List<ApiScenario> selectBaseInfoByModuleId(String id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -748,5 +748,10 @@
|
||||||
#{id}
|
#{id}
|
||||||
</foreach>
|
</foreach>
|
||||||
</select>
|
</select>
|
||||||
|
<select id="selectBaseInfoByModuleId" resultType="io.metersphere.api.domain.ApiScenario">
|
||||||
|
SELECT id, name
|
||||||
|
FROM api_scenario
|
||||||
|
WHERE module_id = #{0}
|
||||||
|
</select>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|
|
@ -2,8 +2,8 @@ package io.metersphere.api.parser;
|
||||||
|
|
||||||
|
|
||||||
import io.metersphere.api.dto.converter.ApiDefinitionDetail;
|
import io.metersphere.api.dto.converter.ApiDefinitionDetail;
|
||||||
import io.metersphere.api.dto.converter.ApiImportDataAnalysisResult;
|
import io.metersphere.api.dto.converter.ApiDefinitionImportDataAnalysisResult;
|
||||||
import io.metersphere.api.dto.converter.ApiImportFileParseResult;
|
import io.metersphere.api.dto.converter.ApiDefinitionImportFileParseResult;
|
||||||
import io.metersphere.api.dto.request.ImportRequest;
|
import io.metersphere.api.dto.request.ImportRequest;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
@ -28,6 +28,6 @@ public interface ApiDefinitionImportParser<T> {
|
||||||
* @param existenceApiDefinitionList 数据库中已存在的数据
|
* @param existenceApiDefinitionList 数据库中已存在的数据
|
||||||
* @return 需要入库的模块、需要入库的接口、需要更新的接口
|
* @return 需要入库的模块、需要入库的接口、需要更新的接口
|
||||||
*/
|
*/
|
||||||
ApiImportDataAnalysisResult generateInsertAndUpdateData(ApiImportFileParseResult importParser, List<ApiDefinitionDetail> existenceApiDefinitionList);
|
ApiDefinitionImportDataAnalysisResult generateInsertAndUpdateData(ApiDefinitionImportFileParseResult importParser, List<ApiDefinitionDetail> existenceApiDefinitionList);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
}
|
|
@ -1,11 +1,11 @@
|
||||||
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.*;
|
import io.metersphere.api.parser.api.dataimport.*;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
public class ImportParserFactory {
|
public class ImportParserFactory {
|
||||||
public static ApiDefinitionImportParser<?> getImportParser(String platform) {
|
public static ApiDefinitionImportParser<?> getApiDefinitionImportParser(String platform) {
|
||||||
if (StringUtils.equalsIgnoreCase(ApiImportPlatform.Swagger3.name(), platform)) {
|
if (StringUtils.equalsIgnoreCase(ApiImportPlatform.Swagger3.name(), platform)) {
|
||||||
return new Swagger3ParserApiDefinition();
|
return new Swagger3ParserApiDefinition();
|
||||||
} else if (StringUtils.equalsIgnoreCase(ApiImportPlatform.Postman.name(), platform)) {
|
} else if (StringUtils.equalsIgnoreCase(ApiImportPlatform.Postman.name(), platform)) {
|
||||||
|
@ -19,4 +19,13 @@ public class ImportParserFactory {
|
||||||
}
|
}
|
||||||
return null;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -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.domain.ApiDefinitionBlob;
|
||||||
import io.metersphere.api.dto.converter.ApiDefinitionDetail;
|
import io.metersphere.api.dto.converter.ApiDefinitionDetail;
|
||||||
import io.metersphere.api.dto.converter.ApiImportDataAnalysisResult;
|
import io.metersphere.api.dto.converter.ApiDefinitionImportDataAnalysisResult;
|
||||||
import io.metersphere.api.dto.converter.ApiImportFileParseResult;
|
import io.metersphere.api.dto.converter.ApiDefinitionImportFileParseResult;
|
||||||
import io.metersphere.api.dto.converter.ExistenceApiDefinitionDetail;
|
import io.metersphere.api.dto.converter.ExistenceApiDefinitionDetail;
|
||||||
import io.metersphere.api.dto.definition.HttpResponse;
|
import io.metersphere.api.dto.definition.HttpResponse;
|
||||||
import io.metersphere.api.dto.definition.ResponseBody;
|
import io.metersphere.api.dto.definition.ResponseBody;
|
||||||
|
@ -39,10 +39,10 @@ import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public class HarParserApiDefinition extends HttpApiDefinitionImportAbstractParser<ApiImportFileParseResult> {
|
public class HarParserApiDefinition extends HttpApiDefinitionImportAbstractParser<ApiDefinitionImportFileParseResult> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ApiImportFileParseResult parse(InputStream source, ImportRequest request) throws Exception {
|
public ApiDefinitionImportFileParseResult parse(InputStream source, ImportRequest request) throws Exception {
|
||||||
Har har = null;
|
Har har = null;
|
||||||
try {
|
try {
|
||||||
har = HarUtils.read(source);
|
har = HarUtils.read(source);
|
||||||
|
@ -53,14 +53,14 @@ public class HarParserApiDefinition extends HttpApiDefinitionImportAbstractParse
|
||||||
if (ObjectUtils.isEmpty(har) || har.log == null) {
|
if (ObjectUtils.isEmpty(har) || har.log == null) {
|
||||||
throw new MSException("解析失败,请确认选择的是 Har 格式!");
|
throw new MSException("解析失败,请确认选择的是 Har 格式!");
|
||||||
}
|
}
|
||||||
ApiImportFileParseResult definitionImport = new ApiImportFileParseResult();
|
ApiDefinitionImportFileParseResult definitionImport = new ApiDefinitionImportFileParseResult();
|
||||||
definitionImport.setData(parseRequests(har, request));
|
definitionImport.setData(parseRequests(har, request));
|
||||||
return definitionImport;
|
return definitionImport;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ApiImportDataAnalysisResult generateInsertAndUpdateData(ApiImportFileParseResult importParser, List<ApiDefinitionDetail> existenceApiDefinitionList) {
|
public ApiDefinitionImportDataAnalysisResult generateInsertAndUpdateData(ApiDefinitionImportFileParseResult importParser, List<ApiDefinitionDetail> existenceApiDefinitionList) {
|
||||||
ApiImportDataAnalysisResult insertAndUpdateData = super.generateInsertAndUpdateData(importParser, existenceApiDefinitionList);
|
ApiDefinitionImportDataAnalysisResult insertAndUpdateData = super.generateInsertAndUpdateData(importParser, existenceApiDefinitionList);
|
||||||
ApiDefinitionBlobMapper apiDefinitionBlobMapper = CommonBeanFactory.getBean(ApiDefinitionBlobMapper.class);
|
ApiDefinitionBlobMapper apiDefinitionBlobMapper = CommonBeanFactory.getBean(ApiDefinitionBlobMapper.class);
|
||||||
|
|
||||||
for (ExistenceApiDefinitionDetail definitionDetail : insertAndUpdateData.getExistenceApiList()) {
|
for (ExistenceApiDefinitionDetail definitionDetail : insertAndUpdateData.getExistenceApiList()) {
|
|
@ -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.ApiDefinitionDetail;
|
||||||
import io.metersphere.api.dto.converter.ApiImportDataAnalysisResult;
|
import io.metersphere.api.dto.converter.ApiDefinitionImportDataAnalysisResult;
|
||||||
import io.metersphere.api.dto.converter.ApiImportFileParseResult;
|
import io.metersphere.api.dto.converter.ApiDefinitionImportFileParseResult;
|
||||||
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 io.metersphere.api.dto.request.ImportRequest;
|
import io.metersphere.api.dto.request.ImportRequest;
|
||||||
|
@ -34,12 +34,12 @@ import java.util.stream.Collectors;
|
||||||
public abstract class HttpApiDefinitionImportAbstractParser<T> implements ApiDefinitionImportParser<T> {
|
public abstract class HttpApiDefinitionImportAbstractParser<T> implements ApiDefinitionImportParser<T> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ApiImportDataAnalysisResult generateInsertAndUpdateData(ApiImportFileParseResult importParser, List<ApiDefinitionDetail> existenceApiDefinitionList) {
|
public ApiDefinitionImportDataAnalysisResult generateInsertAndUpdateData(ApiDefinitionImportFileParseResult importParser, List<ApiDefinitionDetail> existenceApiDefinitionList) {
|
||||||
// API类型,通过 Method & Path 组合判断,接口是否存在
|
// 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> 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));
|
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) -> {
|
importDataMap.forEach((key, api) -> {
|
||||||
if (savedApiDefinitionMap.containsKey(key)) {
|
if (savedApiDefinitionMap.containsKey(key)) {
|
|
@ -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.constants.ApiConstants;
|
||||||
import io.metersphere.api.domain.ApiDefinition;
|
import io.metersphere.api.domain.ApiDefinition;
|
||||||
import io.metersphere.api.dto.converter.ApiDefinitionDetail;
|
import io.metersphere.api.dto.converter.ApiDefinitionDetail;
|
||||||
import io.metersphere.api.dto.converter.ApiImportDataAnalysisResult;
|
import io.metersphere.api.dto.converter.ApiDefinitionImportDataAnalysisResult;
|
||||||
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.definition.ApiTestCaseDTO;
|
||||||
import io.metersphere.api.dto.request.ImportRequest;
|
import io.metersphere.api.dto.request.ImportRequest;
|
||||||
import io.metersphere.api.dto.request.http.MsHTTPElement;
|
import io.metersphere.api.dto.request.http.MsHTTPElement;
|
||||||
|
@ -29,11 +29,11 @@ import java.lang.reflect.Field;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class JmeterParserApiDefinition implements ApiDefinitionImportParser<ApiImportFileParseResult> {
|
public class JmeterParserApiDefinition implements ApiDefinitionImportParser<ApiDefinitionImportFileParseResult> {
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ApiImportFileParseResult parse(InputStream inputSource, ImportRequest request) throws Exception {
|
public ApiDefinitionImportFileParseResult parse(InputStream inputSource, ImportRequest request) throws Exception {
|
||||||
try {
|
try {
|
||||||
Object scriptWrapper = MsSaveService.loadElement(inputSource);
|
Object scriptWrapper = MsSaveService.loadElement(inputSource);
|
||||||
HashTree hashTree = this.getHashTree(scriptWrapper);
|
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);
|
Map<ApiDefinitionDetail, List<ApiTestCaseDTO>> groupWithUniqueIdentification = this.mergeApiCaseWithUniqueIdentification(allImportDetails);
|
||||||
ApiImportFileParseResult returnDTO = new ApiImportFileParseResult();
|
ApiDefinitionImportFileParseResult returnDTO = new ApiDefinitionImportFileParseResult();
|
||||||
groupWithUniqueIdentification.forEach((definitionImportDetail, caseData) -> {
|
groupWithUniqueIdentification.forEach((definitionImportDetail, caseData) -> {
|
||||||
String apiID = IDGenerator.nextStr();
|
String apiID = IDGenerator.nextStr();
|
||||||
definitionImportDetail.setId(apiID);
|
definitionImportDetail.setId(apiID);
|
||||||
|
@ -142,17 +142,17 @@ public class JmeterParserApiDefinition implements ApiDefinitionImportParser<ApiI
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ApiImportDataAnalysisResult generateInsertAndUpdateData(ApiImportFileParseResult importParser, List<ApiDefinitionDetail> existenceApiDefinitionList) {
|
public ApiDefinitionImportDataAnalysisResult generateInsertAndUpdateData(ApiDefinitionImportFileParseResult importParser, List<ApiDefinitionDetail> existenceApiDefinitionList) {
|
||||||
List<ApiDefinitionDetail> importDataList = importParser.getData();
|
List<ApiDefinitionDetail> importDataList = importParser.getData();
|
||||||
Map<String, List<ApiDefinitionDetail>> protocolImportMap = importDataList.stream().collect(Collectors.groupingBy(ApiDefinitionDetail::getProtocol));
|
Map<String, List<ApiDefinitionDetail>> protocolImportMap = importDataList.stream().collect(Collectors.groupingBy(ApiDefinitionDetail::getProtocol));
|
||||||
Map<String, List<ApiDefinitionDetail>> existenceProtocolMap = existenceApiDefinitionList.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()) {
|
for (Map.Entry<String, List<ApiDefinitionDetail>> entry : protocolImportMap.entrySet()) {
|
||||||
List<ApiDefinitionDetail> importList = entry.getValue();
|
List<ApiDefinitionDetail> importList = entry.getValue();
|
||||||
List<ApiDefinitionDetail> existenceList = existenceProtocolMap.get(entry.getKey());
|
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.getInsertApiList().addAll(httpResult.getInsertApiList());
|
||||||
insertAndUpdateData.getExistenceApiList().addAll(httpResult.getExistenceApiList());
|
insertAndUpdateData.getExistenceApiList().addAll(httpResult.getExistenceApiList());
|
||||||
}
|
}
|
||||||
|
@ -160,8 +160,8 @@ public class JmeterParserApiDefinition implements ApiDefinitionImportParser<ApiI
|
||||||
return insertAndUpdateData;
|
return insertAndUpdateData;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ApiImportDataAnalysisResult compareApiData(List<ApiDefinitionDetail> importData, List<ApiDefinitionDetail> existenceApiData, String protocol) {
|
private ApiDefinitionImportDataAnalysisResult compareApiData(List<ApiDefinitionDetail> importData, List<ApiDefinitionDetail> existenceApiData, String protocol) {
|
||||||
ApiImportDataAnalysisResult insertAndUpdateData = new ApiImportDataAnalysisResult();
|
ApiDefinitionImportDataAnalysisResult insertAndUpdateData = new ApiDefinitionImportDataAnalysisResult();
|
||||||
|
|
||||||
if (CollectionUtils.isEmpty(importData)) {
|
if (CollectionUtils.isEmpty(importData)) {
|
||||||
return insertAndUpdateData;
|
return insertAndUpdateData;
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.ApiDefinitionDetail;
|
||||||
import io.metersphere.api.dto.converter.ApiDefinitionExportDetail;
|
import io.metersphere.api.dto.converter.ApiDefinitionExportDetail;
|
||||||
import io.metersphere.api.dto.converter.ApiImportDataAnalysisResult;
|
import io.metersphere.api.dto.converter.ApiDefinitionImportDataAnalysisResult;
|
||||||
import io.metersphere.api.dto.converter.ApiImportFileParseResult;
|
import io.metersphere.api.dto.converter.ApiDefinitionImportFileParseResult;
|
||||||
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 io.metersphere.api.dto.export.MetersphereApiExportResponse;
|
import io.metersphere.api.dto.export.MetersphereApiExportResponse;
|
||||||
|
@ -25,10 +25,10 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class MetersphereParserApiDefinition implements ApiDefinitionImportParser<ApiImportFileParseResult> {
|
public class MetersphereParserApiDefinition implements ApiDefinitionImportParser<ApiDefinitionImportFileParseResult> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ApiImportFileParseResult parse(InputStream source, ImportRequest request) throws Exception {
|
public ApiDefinitionImportFileParseResult parse(InputStream source, ImportRequest request) throws Exception {
|
||||||
MetersphereApiExportResponse metersphereApiExportResponse = null;
|
MetersphereApiExportResponse metersphereApiExportResponse = null;
|
||||||
try {
|
try {
|
||||||
metersphereApiExportResponse = ApiDataUtils.parseObject(source, MetersphereApiExportResponse.class);
|
metersphereApiExportResponse = ApiDataUtils.parseObject(source, MetersphereApiExportResponse.class);
|
||||||
|
@ -43,10 +43,10 @@ public class MetersphereParserApiDefinition implements ApiDefinitionImportParser
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ApiImportDataAnalysisResult generateInsertAndUpdateData(ApiImportFileParseResult importParser, List<ApiDefinitionDetail> existenceAllApiList) {
|
public ApiDefinitionImportDataAnalysisResult generateInsertAndUpdateData(ApiDefinitionImportFileParseResult importParser, List<ApiDefinitionDetail> existenceAllApiList) {
|
||||||
Map<String, List<ApiDefinitionDetail>> existenceProtocolMap = new HashMap<>();
|
Map<String, List<ApiDefinitionDetail>> existenceProtocolMap = new HashMap<>();
|
||||||
|
|
||||||
ApiImportDataAnalysisResult insertAndUpdateData = new ApiImportDataAnalysisResult();
|
ApiDefinitionImportDataAnalysisResult insertAndUpdateData = new ApiDefinitionImportDataAnalysisResult();
|
||||||
|
|
||||||
for (ApiDefinitionDetail apiDefinitionDetail : existenceAllApiList) {
|
for (ApiDefinitionDetail apiDefinitionDetail : existenceAllApiList) {
|
||||||
String protocol = apiDefinitionDetail.getProtocol().toUpperCase();
|
String protocol = apiDefinitionDetail.getProtocol().toUpperCase();
|
||||||
|
@ -109,7 +109,7 @@ public class MetersphereParserApiDefinition implements ApiDefinitionImportParser
|
||||||
return insertAndUpdateData;
|
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)) {
|
if (CollectionUtils.isNotEmpty(caseList)) {
|
||||||
insertAndUpdateData.getApiIdAndTestCaseMap().put(apiId, 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);
|
List<ApiDefinitionExportDetail> distinctImportList = this.mergeApiCaseWithUniqueIdentification(apiDefinitions);
|
||||||
ApiImportFileParseResult returnDTO = new ApiImportFileParseResult();
|
ApiDefinitionImportFileParseResult returnDTO = new ApiDefinitionImportFileParseResult();
|
||||||
distinctImportList.forEach(definitionImportDetail -> {
|
distinctImportList.forEach(definitionImportDetail -> {
|
||||||
String apiID = IDGenerator.nextStr();
|
String apiID = IDGenerator.nextStr();
|
||||||
definitionImportDetail.setId(apiID);
|
definitionImportDetail.setId(apiID);
|
|
@ -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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package io.metersphere.api.parser.api;
|
package io.metersphere.api.parser.api.dataimport;
|
||||||
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
|
@ -1,9 +1,9 @@
|
||||||
package io.metersphere.api.parser.api;
|
package io.metersphere.api.parser.api.dataimport;
|
||||||
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import io.metersphere.api.dto.converter.ApiDefinitionDetail;
|
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.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;
|
||||||
|
@ -19,10 +19,10 @@ import org.apache.commons.lang3.StringUtils;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public class PostmanParserApiDefinition extends PostmanAbstractParserParserApiDefinition<ApiImportFileParseResult> {
|
public class PostmanParserApiDefinition extends PostmanAbstractParserParserApiDefinition<ApiDefinitionImportFileParseResult> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ApiImportFileParseResult parse(InputStream source, ImportRequest request) throws JsonProcessingException {
|
public ApiDefinitionImportFileParseResult parse(InputStream source, ImportRequest request) throws JsonProcessingException {
|
||||||
LogUtils.info("PostmanParser parse");
|
LogUtils.info("PostmanParser parse");
|
||||||
String testStr = getApiTestStr(source);
|
String testStr = getApiTestStr(source);
|
||||||
PostmanCollection postmanCollection = JSON.parseObject(testStr, PostmanCollection.class);
|
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);
|
LinkedHashMap<ApiDefinitionDetail, List<ApiDefinitionDetail>> allImportDetails = this.parseItem(postmanCollection.getItem(), modulePath, request);
|
||||||
|
|
||||||
// 对于postman的导入: 本质就是将postman的数据导入为用例
|
// 对于postman的导入: 本质就是将postman的数据导入为用例
|
||||||
ApiImportFileParseResult apiImport = this.genApiDefinitionImport(allImportDetails);
|
ApiDefinitionImportFileParseResult apiImport = this.genApiDefinitionImport(allImportDetails);
|
||||||
LogUtils.info("PostmanParser parse end");
|
LogUtils.info("PostmanParser parse end");
|
||||||
return apiImport;
|
return apiImport;
|
||||||
}
|
}
|
||||||
|
@ -61,9 +61,9 @@ public class PostmanParserApiDefinition extends PostmanAbstractParserParserApiDe
|
||||||
return results;
|
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);
|
Map<ApiDefinitionDetail, List<ApiDefinitionDetail>> groupWithUniqueIdentification = this.mergeApiCaseWithUniqueIdentification(allImportDetails);
|
||||||
ApiImportFileParseResult returnDTO = new ApiImportFileParseResult();
|
ApiDefinitionImportFileParseResult returnDTO = new ApiDefinitionImportFileParseResult();
|
||||||
groupWithUniqueIdentification.forEach((definitionImportDetail, caseData) -> {
|
groupWithUniqueIdentification.forEach((definitionImportDetail, caseData) -> {
|
||||||
String apiID = IDGenerator.nextStr();
|
String apiID = IDGenerator.nextStr();
|
||||||
definitionImportDetail.setId(apiID);
|
definitionImportDetail.setId(apiID);
|
|
@ -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.constants.ApiConstants;
|
||||||
import io.metersphere.api.dto.converter.ApiDefinitionDetail;
|
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.HttpResponse;
|
||||||
import io.metersphere.api.dto.definition.ResponseBody;
|
import io.metersphere.api.dto.definition.ResponseBody;
|
||||||
import io.metersphere.api.dto.request.ImportRequest;
|
import io.metersphere.api.dto.request.ImportRequest;
|
||||||
|
@ -44,7 +44,7 @@ import java.net.URI;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
|
||||||
public class Swagger3ParserApiDefinition extends HttpApiDefinitionImportAbstractParser<ApiImportFileParseResult> {
|
public class Swagger3ParserApiDefinition extends HttpApiDefinitionImportAbstractParser<ApiDefinitionImportFileParseResult> {
|
||||||
|
|
||||||
protected String projectId;
|
protected String projectId;
|
||||||
private Components components;
|
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地址判断放在这里。
|
//将之前在service中的swagger地址判断放在这里。
|
||||||
if (StringUtils.isNotBlank(request.getSwaggerUrl())) {
|
if (StringUtils.isNotBlank(request.getSwaggerUrl())) {
|
||||||
|
@ -101,10 +101,10 @@ public class Swagger3ParserApiDefinition extends HttpApiDefinitionImportAbstract
|
||||||
throw new MSException(Translator.get("swagger_parse_error"));
|
throw new MSException(Translator.get("swagger_parse_error"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ApiImportFileParseResult apiImportFileParseResult = new ApiImportFileParseResult();
|
ApiDefinitionImportFileParseResult apiDefinitionImportFileParseResult = new ApiDefinitionImportFileParseResult();
|
||||||
OpenAPI openAPI = result.getOpenAPI();
|
OpenAPI openAPI = result.getOpenAPI();
|
||||||
apiImportFileParseResult.setData(parseRequests(openAPI, request));
|
apiDefinitionImportFileParseResult.setData(parseRequests(openAPI, request));
|
||||||
return apiImportFileParseResult;
|
return apiDefinitionImportFileParseResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<AuthorizationValue> setAuths(ImportRequest request) {
|
private List<AuthorizationValue> setAuths(ImportRequest request) {
|
|
@ -3,12 +3,17 @@ 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.AbstractMsProtocolTestElement;
|
||||||
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
|
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.TestElement;
|
||||||
import org.apache.jmeter.testelement.TestPlan;
|
import org.apache.jmeter.testelement.TestPlan;
|
||||||
import org.apache.jorphan.collections.HashTree;
|
import org.apache.jorphan.collections.HashTree;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author: jianxing
|
* @Author: jianxing
|
||||||
|
@ -48,4 +53,24 @@ public class MsTestElementParser {
|
||||||
}
|
}
|
||||||
return result;
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
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.MsJMeterComponent;
|
||||||
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 org.apache.jmeter.threads.ThreadGroup;
|
import org.apache.jmeter.threads.ThreadGroup;
|
||||||
|
@ -16,9 +16,9 @@ import org.apache.jorphan.collections.HashTree;
|
||||||
public class ThreadGroupConverter extends AbstractMsElementConverter<ThreadGroup> {
|
public class ThreadGroupConverter extends AbstractMsElementConverter<ThreadGroup> {
|
||||||
@Override
|
@Override
|
||||||
public void toMsElement(AbstractMsTestElement parent, ThreadGroup element, HashTree hashTree) {
|
public void toMsElement(AbstractMsTestElement parent, ThreadGroup element, HashTree hashTree) {
|
||||||
MsScenario msScenario = new MsScenario();
|
MsJMeterComponent msJMeterComponent = new MsJMeterComponent();
|
||||||
// todo 解析线程组
|
msJMeterComponent.setName(element.getName());
|
||||||
parent.getChildren().add(msScenario);
|
parent.getChildren().add(msJMeterComponent);
|
||||||
parseChild(msScenario, element, hashTree);
|
parseChild(msJMeterComponent, element, hashTree);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 为 null,stepDetails 不为 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -118,26 +118,26 @@ public class ApiDefinitionImportService {
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void apiDefinitionImport(MultipartFile file, ImportRequest request, String projectId) {
|
public void apiDefinitionImport(MultipartFile file, ImportRequest request, String projectId) {
|
||||||
this.initImportRequestAndCheck(file, request, projectId);
|
this.initImportRequestAndCheck(file, request, projectId);
|
||||||
ApiDefinitionImportParser<?> runService = ImportParserFactory.getImportParser(request.getPlatform());
|
ApiDefinitionImportParser<?> runService = ImportParserFactory.getApiDefinitionImportParser(request.getPlatform());
|
||||||
assert runService != null;
|
assert runService != null;
|
||||||
ApiImportDataAnalysisResult apiImportDataAnalysisResult = new ApiImportDataAnalysisResult();
|
ApiDefinitionImportDataAnalysisResult apiDefinitionImportDataAnalysisResult = new ApiDefinitionImportDataAnalysisResult();
|
||||||
try {
|
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())) {
|
if (!CollectionUtils.isEmpty(fileParseResult.getData())) {
|
||||||
ApiDefinitionPageRequest pageRequest = new ApiDefinitionPageRequest();
|
ApiDefinitionPageRequest pageRequest = new ApiDefinitionPageRequest();
|
||||||
pageRequest.setProjectId(request.getProjectId());
|
pageRequest.setProjectId(request.getProjectId());
|
||||||
pageRequest.setProtocols(fileParseResult.getApiProtocols());
|
pageRequest.setProtocols(fileParseResult.getApiProtocols());
|
||||||
List<ApiDefinitionDetail> existenceApiDefinitionList = extApiDefinitionMapper.importList(pageRequest);
|
List<ApiDefinitionDetail> existenceApiDefinitionList = extApiDefinitionMapper.importList(pageRequest);
|
||||||
//分析有哪些数据需要新增、有哪些数据需要更新
|
//分析有哪些数据需要新增、有哪些数据需要更新
|
||||||
apiImportDataAnalysisResult = runService.generateInsertAndUpdateData(fileParseResult, existenceApiDefinitionList);
|
apiDefinitionImportDataAnalysisResult = 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()) {
|
if (apiDefinitionImportDataAnalysisResult.isEmpty()) {
|
||||||
throw new MSException(Translator.get("parse_empty_data"));
|
throw new MSException(Translator.get("parse_empty_data"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,7 +149,7 @@ public class ApiDefinitionImportService {
|
||||||
request.setVersionId(defaultVersion);
|
request.setVersionId(defaultVersion);
|
||||||
}
|
}
|
||||||
//通过导入配置,预处理数据,确定哪些要创建、哪些要修改
|
//通过导入配置,预处理数据,确定哪些要创建、哪些要修改
|
||||||
ApiDefinitionPreImportAnalysisResult preImportAnalysisResult = this.preImportAnalysis(request, apiImportDataAnalysisResult);
|
ApiDefinitionPreImportAnalysisResult preImportAnalysisResult = this.preImportAnalysis(request, apiDefinitionImportDataAnalysisResult);
|
||||||
//入库
|
//入库
|
||||||
List<LogDTO> operationLogs = this.insertData(preImportAnalysisResult, request);
|
List<LogDTO> operationLogs = this.insertData(preImportAnalysisResult, request);
|
||||||
operationLogService.batchAdd(operationLogs);
|
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();
|
ApiDefinitionPreImportAnalysisResult preImportAnalysisResult = new ApiDefinitionPreImportAnalysisResult();
|
||||||
|
|
||||||
// api模块树查询
|
// api模块树查询
|
||||||
|
@ -548,7 +548,7 @@ public class ApiDefinitionImportService {
|
||||||
*/
|
*/
|
||||||
private void furtherProcessingExistenceApiData(String selectModuleId, String selectModulePath,
|
private void furtherProcessingExistenceApiData(String selectModuleId, String selectModulePath,
|
||||||
boolean isCoverModule,
|
boolean isCoverModule,
|
||||||
ApiImportDataAnalysisResult insertAndUpdateData,
|
ApiDefinitionImportDataAnalysisResult insertAndUpdateData,
|
||||||
ApiDefinitionPreImportAnalysisResult apiDefinitionPreImportAnalysisResult,
|
ApiDefinitionPreImportAnalysisResult apiDefinitionPreImportAnalysisResult,
|
||||||
Map<String, BaseTreeNode> modulePathMap) {
|
Map<String, BaseTreeNode> modulePathMap) {
|
||||||
for (ApiDefinitionDetail importApi : insertAndUpdateData.getInsertApiList()) {
|
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()) {
|
for (ApiDefinitionDetail apiData : analysisResult.getInsertApiList()) {
|
||||||
apiDefinitionPreImportAnalysisResult.getInsertApiData().add(apiData);
|
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()) {
|
if (CollectionUtils.isEmpty(analysisResult.getExistenceApiList()) || !request.isCoverData()) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -65,6 +65,14 @@ public class ApiScenarioModuleService extends ModuleTreeService {
|
||||||
return super.buildTreeAndCountResource(fileModuleList, true, Translator.get(UNPLANNED_SCENARIO));
|
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) {
|
public List<BaseTreeNode> getTreeOnlyIdsAndResourceCount(ApiScenarioModuleRequest request, List<ModuleCountDTO> moduleCountDTOList) {
|
||||||
//节点内容只有Id和parentId
|
//节点内容只有Id和parentId
|
||||||
List<BaseTreeNode> fileModuleList = extApiScenarioModuleMapper.selectIdAndParentIdByRequest(request);
|
List<BaseTreeNode> fileModuleList = extApiScenarioModuleMapper.selectIdAndParentIdByRequest(request);
|
||||||
|
|
|
@ -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 =
|
Set<String> csvIdSet =
|
||||||
getCsvVariables(scenarioConfig)
|
getCsvVariables(scenarioConfig)
|
||||||
.stream()
|
.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) {
|
if (scenarioConfig == null) {
|
||||||
return;
|
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);
|
ApiFileResourceUpdateRequest resourceUpdateRequest = getApiFileResourceUpdateRequest(scenario.getId(), scenario.getProjectId(), userId);
|
||||||
// 设置本地文件相关参数
|
// 设置本地文件相关参数
|
||||||
setCsvLocalFileParam(csvVariables, dbCsv, resourceUpdateRequest);
|
setCsvLocalFileParam(csvVariables, dbCsv, resourceUpdateRequest);
|
||||||
|
@ -705,7 +705,7 @@ public class ApiScenarioService extends MoveNodeService {
|
||||||
apiFileResourceService.updateFileResource(resourceUpdateRequest);
|
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
|
// 获取数据库中关联的文件id
|
||||||
List<String> dbRefFileIds = dbCsv.stream()
|
List<String> dbRefFileIds = dbCsv.stream()
|
||||||
.filter(c -> BooleanUtils.isTrue(c.getAssociation()) && StringUtils.isNotBlank(c.getFileId()))
|
.filter(c -> BooleanUtils.isTrue(c.getAssociation()) && StringUtils.isNotBlank(c.getFileId()))
|
||||||
|
@ -724,7 +724,7 @@ public class ApiScenarioService extends MoveNodeService {
|
||||||
resourceUpdateRequest.setLinkFileIds(linkFileIds);
|
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()
|
List<String> dbLocalFileIds = dbCsv.stream()
|
||||||
.filter(c -> BooleanUtils.isFalse(c.getAssociation()))
|
.filter(c -> BooleanUtils.isFalse(c.getAssociation()))
|
||||||
|
@ -896,7 +896,7 @@ public class ApiScenarioService extends MoveNodeService {
|
||||||
* @param scenarioId
|
* @param scenarioId
|
||||||
* @param steps
|
* @param steps
|
||||||
*/
|
*/
|
||||||
private void checkCircularRef(String scenarioId, List<ApiScenarioStepRequest> steps) {
|
public void checkCircularRef(String scenarioId, List<ApiScenarioStepRequest> steps) {
|
||||||
traversalStepTree(steps, step -> {
|
traversalStepTree(steps, step -> {
|
||||||
if (isRefOrPartialRef(step.getRefType()) && StringUtils.equals(step.getResourceId(), scenarioId)) {
|
if (isRefOrPartialRef(step.getRefType()) && StringUtils.equals(step.getResourceId(), scenarioId)) {
|
||||||
throw new MSException(API_SCENARIO_CIRCULAR_REFERENCE);
|
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 apiScenarioStepBlob = new ApiScenarioStepBlob();
|
||||||
apiScenarioStepBlob.setId(stepId);
|
apiScenarioStepBlob.setId(stepId);
|
||||||
apiScenarioStepBlob.setContent(JSON.toJSONString(stepDetail).getBytes());
|
apiScenarioStepBlob.setContent(JSON.toJSONString(stepDetail).getBytes());
|
||||||
|
@ -927,7 +927,7 @@ public class ApiScenarioService extends MoveNodeService {
|
||||||
/**
|
/**
|
||||||
* 获取待更新的 ApiScenarioStepBlob 列表
|
* 获取待更新的 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)) {
|
if (MapUtils.isEmpty(stepDetails)) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
@ -989,7 +989,7 @@ public class ApiScenarioService extends MoveNodeService {
|
||||||
* 解析步骤树结构
|
* 解析步骤树结构
|
||||||
* 获取待更新的 ApiScenarioStep 列表
|
* 获取待更新的 ApiScenarioStep 列表
|
||||||
*/
|
*/
|
||||||
private List<ApiScenarioStep> getApiScenarioSteps(ApiScenarioStepCommonDTO parent,
|
public List<ApiScenarioStep> getApiScenarioSteps(ApiScenarioStepCommonDTO parent,
|
||||||
List<ApiScenarioStepRequest> steps, List<ApiScenarioCsvStep> csvSteps) {
|
List<ApiScenarioStepRequest> steps, List<ApiScenarioCsvStep> csvSteps) {
|
||||||
if (CollectionUtils.isEmpty(steps)) {
|
if (CollectionUtils.isEmpty(steps)) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
|
@ -1185,7 +1185,7 @@ public class ApiScenarioService extends MoveNodeService {
|
||||||
return partialRefStepDetail;
|
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);
|
String apiScenarioDir = DefaultRepositoryDir.getApiScenarioDir(projectId, sourceId);
|
||||||
ApiFileResourceUpdateRequest resourceUpdateRequest = new ApiFileResourceUpdateRequest();
|
ApiFileResourceUpdateRequest resourceUpdateRequest = new ApiFileResourceUpdateRequest();
|
||||||
resourceUpdateRequest.setProjectId(projectId);
|
resourceUpdateRequest.setProjectId(projectId);
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1745,7 +1745,7 @@ public class ApiDefinitionControllerTests extends BaseTest {
|
||||||
|
|
||||||
private void importTest() throws Exception {
|
private void importTest() throws Exception {
|
||||||
//测试ImportParserFactory不按规定获取会返回null
|
//测试ImportParserFactory不按规定获取会返回null
|
||||||
Assertions.assertNull(ImportParserFactory.getImportParser("test"));
|
Assertions.assertNull(ImportParserFactory.getApiDefinitionImportParser("test"));
|
||||||
// 创建用于导入的项目
|
// 创建用于导入的项目
|
||||||
//测试计划专用项目
|
//测试计划专用项目
|
||||||
AddProjectRequest initProject = new AddProjectRequest();
|
AddProjectRequest initProject = new AddProjectRequest();
|
||||||
|
@ -1798,7 +1798,6 @@ 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");
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,6 +17,6 @@ public class ParserTests {
|
||||||
@Test
|
@Test
|
||||||
@Order(3)
|
@Order(3)
|
||||||
public void testImportParserMs() throws Exception {
|
public void testImportParserMs() throws Exception {
|
||||||
ImportParserFactory.getImportParser(ApiImportPlatform.MeterSphere.name());
|
ImportParserFactory.getApiDefinitionImportParser(ApiImportPlatform.MeterSphere.name());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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">{
|
||||||
|
"username" : "apitester@fit2cloud.com",
|
||||||
|
"password" : "apitester@fit2cloud.com",
|
||||||
|
"authenticate" : "local"
|
||||||
|
}</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("output the token and x-auth====="+ "${csrfToken}");
|
||||||
|
log.info("output the token and x-auth====="+ "${sessionId}");</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>
|
|
@ -3,7 +3,7 @@ package io.metersphere.system.constants;
|
||||||
public class ExportConstants {
|
public class ExportConstants {
|
||||||
|
|
||||||
public enum ExportType {
|
public enum ExportType {
|
||||||
API_DEFINITION, CASE
|
API_SCENARIO, API_DEFINITION, CASE
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ExportState {
|
public enum ExportState {
|
||||||
|
|
Loading…
Reference in New Issue