feat(接口设置): 获取场景详情接口
This commit is contained in:
parent
21e3029e95
commit
b5c20b102f
|
@ -111,10 +111,17 @@ public class ApiScenarioController {
|
|||
apiScenarioService.deleteToGc(id);
|
||||
}
|
||||
|
||||
@GetMapping("/get/{scenarioId}")
|
||||
@Operation(summary = "接口测试-接口场景管理-获取场景详情")
|
||||
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_READ)
|
||||
public ApiScenarioDetail get(@PathVariable String scenarioId) {
|
||||
return apiScenarioService.get(scenarioId);
|
||||
}
|
||||
|
||||
@PostMapping("/debug")
|
||||
@Operation(summary = "接口测试-接口场景管理-场景调试")
|
||||
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_EXECUTE)
|
||||
public String debug(@RequestBody ApiScenarioDebugRequest request) {
|
||||
public String debug(@Validated @RequestBody ApiScenarioDebugRequest request) {
|
||||
return apiScenarioService.debug(request);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package io.metersphere.api.dto.scenario;
|
|||
import io.metersphere.api.constants.ApiScenarioStatus;
|
||||
import io.metersphere.system.valid.EnumValue;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
@ -57,6 +58,7 @@ public class ApiScenarioAddRequest {
|
|||
private ScenarioConfig scenarioConfig;
|
||||
|
||||
@Schema(description = "步骤集合")
|
||||
@Valid
|
||||
private List<ApiScenarioStepRequest> steps;
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package io.metersphere.api.dto.scenario;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
@ -29,6 +30,7 @@ public class ApiScenarioDebugRequest {
|
|||
@Schema(description = "场景的通用配置")
|
||||
private ScenarioConfig scenarioConfig;
|
||||
|
||||
@Valid
|
||||
@Schema(description = "步骤集合")
|
||||
private List<ApiScenarioStepRequest> steps;
|
||||
|
||||
|
|
|
@ -5,9 +5,13 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
|||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class ApiScenarioDetail extends ApiScenario {
|
||||
@Schema(description = "场景的通用配置")
|
||||
private ScenarioConfig scenarioConfig;
|
||||
@Schema(description = "步骤")
|
||||
private List<ApiScenarioStepDTO> steps;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
package io.metersphere.api.dto.scenario;
|
||||
|
||||
import io.metersphere.api.constants.ApiScenarioStepRefType;
|
||||
import io.metersphere.api.constants.ApiScenarioStepType;
|
||||
import io.metersphere.system.valid.EnumValue;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 步骤解析使用的核心数据
|
||||
* 用于步骤解析的统一处理
|
||||
* @Author: jianxing
|
||||
* @CreateTime: 2024-01-10 11:24
|
||||
*/
|
||||
@Data
|
||||
public class ApiScenarioStepCommonDTO {
|
||||
@Schema(description = "步骤id", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotBlank(message = "{api_scenario_step.id.not_blank}")
|
||||
@Size(max = 50, message = "{api_scenario_step.id.length_range}")
|
||||
private String id;
|
||||
|
||||
@Schema(description = "启用/禁用")
|
||||
private Boolean enable = true;
|
||||
|
||||
@Schema(description = "资源id")
|
||||
private String resourceId;
|
||||
|
||||
/**
|
||||
* @see ApiScenarioStepType
|
||||
*/
|
||||
@Schema(description = "步骤类型/API/CASE等")
|
||||
@NotBlank
|
||||
@EnumValue(enumClass = ApiScenarioStepType.class)
|
||||
private String stepType;
|
||||
|
||||
/**
|
||||
* 引用模式:默认完全引用
|
||||
* - 完全引用:步骤状态不可调整
|
||||
* - 部分引用:步骤状态可调整
|
||||
* @see ApiScenarioStepRefType
|
||||
*/
|
||||
@Schema(description = "引用/复制/自定义")
|
||||
@EnumValue(enumClass = ApiScenarioStepRefType.class)
|
||||
private String refType;
|
||||
|
||||
@Schema(description = "循环等组件基础数据")
|
||||
private Object config;
|
||||
|
||||
@Valid
|
||||
@Schema(description = "子步骤")
|
||||
private List<? extends ApiScenarioStepCommonDTO> children;
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package io.metersphere.api.dto.scenario;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @Author: jianxing
|
||||
* @CreateTime: 2024-01-10 11:24
|
||||
*/
|
||||
@Data
|
||||
public class ApiScenarioStepDTO extends ApiScenarioStepCommonDTO {
|
||||
|
||||
@Schema(description = "步骤名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "资源编号")
|
||||
private String resourceNum;
|
||||
|
||||
@Schema(description = "项目fk")
|
||||
private String projectId;
|
||||
|
||||
@Schema(description = "版本号")
|
||||
private String versionId;
|
||||
|
||||
@Schema(description = "场景id")
|
||||
private String scenarioId;
|
||||
|
||||
@Schema(description = "序号")
|
||||
private Long sort;
|
||||
|
||||
@Schema(description = "父级fk")
|
||||
private String parentId;
|
||||
}
|
|
@ -1,64 +1,25 @@
|
|||
package io.metersphere.api.dto.scenario;
|
||||
|
||||
import io.metersphere.api.constants.ApiScenarioStepType;
|
||||
import io.metersphere.system.valid.EnumValue;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @Author: jianxing
|
||||
* @CreateTime: 2024-01-10 11:24
|
||||
*/
|
||||
@Data
|
||||
public class ApiScenarioStepRequest {
|
||||
@Schema(description = "步骤id", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotBlank(message = "{api_scenario_step.id.not_blank}")
|
||||
@Size(max = 50, message = "{api_scenario_step.id.length_range}")
|
||||
private String id;
|
||||
|
||||
public class ApiScenarioStepRequest extends ApiScenarioStepCommonDTO {
|
||||
@Schema(description = "步骤名称")
|
||||
@NotBlank
|
||||
private String name;
|
||||
|
||||
@Schema(description = "启用/禁用")
|
||||
private Boolean enable = true;
|
||||
|
||||
@Schema(description = "资源id")
|
||||
private String resourceId;
|
||||
|
||||
@Schema(description = "资源编号")
|
||||
private String resourceNum;
|
||||
|
||||
/**
|
||||
* @see ApiScenarioStepType
|
||||
*/
|
||||
@Schema(description = "步骤类型/API/CASE等")
|
||||
@NotBlank
|
||||
@EnumValue(enumClass = ApiScenarioStepType.class)
|
||||
private String stepType;
|
||||
|
||||
@Schema(description = "项目fk")
|
||||
private String projectId;
|
||||
|
||||
@Schema(description = "版本号")
|
||||
private String versionId;
|
||||
|
||||
/**
|
||||
* 引用模式:默认完全引用
|
||||
* - 完全引用:步骤状态不可调整
|
||||
* - 部分引用:步骤状态可调整
|
||||
* @see io.metersphere.api.constants.ApiScenarioStepRefType
|
||||
*/
|
||||
@Schema(description = "引用/复制/自定义")
|
||||
private String refType;
|
||||
|
||||
@Schema(description = "循环等组件基础数据")
|
||||
private Map<Object, Object> config;
|
||||
|
||||
@Schema(description = "子步骤")
|
||||
private List<ApiScenarioStepRequest> children;
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import io.metersphere.system.valid.EnumValue;
|
|||
import io.metersphere.validation.groups.Created;
|
||||
import io.metersphere.validation.groups.Updated;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
@ -54,6 +55,7 @@ public class ApiScenarioUpdateRequest {
|
|||
@Schema(description = "场景的通用配置")
|
||||
private ScenarioConfig scenarioConfig;
|
||||
|
||||
@Valid
|
||||
@Schema(description = "步骤集合")
|
||||
private List<ApiScenarioStepRequest> steps;
|
||||
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
package io.metersphere.api.mapper;
|
||||
|
||||
import io.metersphere.api.dto.scenario.ApiScenarioStepDTO;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
@ -7,5 +10,7 @@ import java.util.List;
|
|||
* @CreateTime: 2024-01-16 19:57
|
||||
*/
|
||||
public interface ExtApiScenarioStepMapper {
|
||||
List<String> getStepIdsByScenarioId(String scenarioId);
|
||||
List<String> getStepIdsByScenarioId(@Param("scenarioId") String scenarioId);
|
||||
|
||||
List<ApiScenarioStepDTO> getStepDTOByScenarioIds(@Param("scenarioIds") List<String> scenarioIds);
|
||||
}
|
||||
|
|
|
@ -5,4 +5,12 @@
|
|||
<select id="getStepIdsByScenarioId" resultType="java.lang.String">
|
||||
SELECT id FROM api_scenario_step WHERE scenario_id = #{scenarioId}
|
||||
</select>
|
||||
<select id="getStepDTOByScenarioIds" resultType="io.metersphere.api.dto.scenario.ApiScenarioStepDTO">
|
||||
select
|
||||
<include refid="io.metersphere.api.mapper.ApiScenarioStepMapper.Base_Column_List"/>
|
||||
from api_scenario_step where scenario_id in
|
||||
<foreach collection="scenarioIds" item="scenarioId" open="(" separator="," close=")">
|
||||
#{scenarioId}
|
||||
</foreach>
|
||||
</select>
|
||||
</mapper>
|
|
@ -1,6 +1,6 @@
|
|||
package io.metersphere.api.parser.step;
|
||||
|
||||
import io.metersphere.api.dto.scenario.ApiScenarioStepRequest;
|
||||
import io.metersphere.api.dto.scenario.ApiScenarioStepCommonDTO;
|
||||
import io.metersphere.api.utils.ApiDataUtils;
|
||||
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -11,7 +11,7 @@ import org.apache.commons.lang3.StringUtils;
|
|||
*/
|
||||
public class ApiCaseStepParser extends StepParser {
|
||||
@Override
|
||||
public AbstractMsTestElement parse(ApiScenarioStepRequest step, String resourceBlob, String stepDetail) {
|
||||
public AbstractMsTestElement parse(ApiScenarioStepCommonDTO step, String resourceBlob, String stepDetail) {
|
||||
if (isRef(step.getRefType())) {
|
||||
return StringUtils.isBlank(resourceBlob) ? null : parse2MsTestElement(resourceBlob);
|
||||
} else {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package io.metersphere.api.parser.step;
|
||||
|
||||
import io.metersphere.api.dto.request.MsScenario;
|
||||
import io.metersphere.api.dto.scenario.ApiScenarioStepRequest;
|
||||
import io.metersphere.api.dto.scenario.ApiScenarioStepCommonDTO;
|
||||
import io.metersphere.api.dto.scenario.ScenarioConfig;
|
||||
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
|
||||
import io.metersphere.sdk.util.JSON;
|
||||
|
@ -13,7 +13,7 @@ import org.apache.commons.lang3.StringUtils;
|
|||
*/
|
||||
public class ApiScenarioStepParser extends StepParser {
|
||||
@Override
|
||||
public AbstractMsTestElement parse(ApiScenarioStepRequest step, String resourceBlob, String stepDetail) {
|
||||
public AbstractMsTestElement parse(ApiScenarioStepCommonDTO step, String resourceBlob, String stepDetail) {
|
||||
MsScenario msScenario = new MsScenario();
|
||||
if (isRef(step.getRefType())) {
|
||||
if (StringUtils.isNotBlank(resourceBlob)) {
|
||||
|
|
|
@ -3,7 +3,7 @@ package io.metersphere.api.parser.step;
|
|||
import io.metersphere.api.dto.request.http.KeyValueParam;
|
||||
import io.metersphere.api.dto.request.http.MsHTTPElement;
|
||||
import io.metersphere.api.dto.request.http.body.Body;
|
||||
import io.metersphere.api.dto.scenario.ApiScenarioStepRequest;
|
||||
import io.metersphere.api.dto.scenario.ApiScenarioStepCommonDTO;
|
||||
import io.metersphere.api.utils.ApiDataUtils;
|
||||
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
|
@ -17,7 +17,7 @@ import java.util.List;
|
|||
*/
|
||||
public class ApiStepParser extends StepParser {
|
||||
@Override
|
||||
public AbstractMsTestElement parse(ApiScenarioStepRequest step, String resourceBlob, String stepDetail) {
|
||||
public AbstractMsTestElement parse(ApiScenarioStepCommonDTO step, String resourceBlob, String stepDetail) {
|
||||
if (isRef(step.getRefType())) {
|
||||
if (StringUtils.isBlank(resourceBlob)) {
|
||||
return null;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package io.metersphere.api.parser.step;
|
||||
|
||||
import io.metersphere.api.constants.ApiScenarioStepRefType;
|
||||
import io.metersphere.api.dto.scenario.ApiScenarioStepCommonDTO;
|
||||
import io.metersphere.api.dto.scenario.ApiScenarioStepRequest;
|
||||
import io.metersphere.api.utils.ApiDataUtils;
|
||||
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
|
||||
|
@ -12,7 +13,7 @@ import org.apache.commons.lang3.StringUtils;
|
|||
*/
|
||||
public abstract class StepParser {
|
||||
|
||||
public abstract AbstractMsTestElement parse(ApiScenarioStepRequest step, String resourceBlob, String stepDetail);
|
||||
public abstract AbstractMsTestElement parse(ApiScenarioStepCommonDTO step, String resourceBlob, String stepDetail);
|
||||
|
||||
protected boolean isRef(String refType) {
|
||||
return StringUtils.equalsAny(refType, ApiScenarioStepRefType.REF.name(), ApiScenarioStepRefType.PARTIAL_REF.name());
|
||||
|
|
|
@ -479,7 +479,7 @@ public class ApiScenarioService {
|
|||
if (step == null) {
|
||||
return;
|
||||
}
|
||||
if (!isRef(step.getRefType()) || StringUtils.equals(step.getRefType(), ApiScenarioStepType.API.name())) {
|
||||
if (!isRef(step.getRefType()) || isRefApi(step.getStepType(), step.getRefType())) {
|
||||
// 非引用的步骤,如果有编辑内容,保存到blob表
|
||||
// 如果引用的是接口定义,也保存详情,因为应用接口定义允许修改参数值
|
||||
ApiScenarioStepBlob apiScenarioStepBlob = new ApiScenarioStepBlob();
|
||||
|
@ -503,15 +503,14 @@ public class ApiScenarioService {
|
|||
* @param steps
|
||||
* @return
|
||||
*/
|
||||
private List<ApiScenarioStep> getApiScenarioSteps(ApiScenarioStepRequest parent,
|
||||
List<ApiScenarioStepRequest> steps) {
|
||||
|
||||
private List<ApiScenarioStep> getApiScenarioSteps(ApiScenarioStepCommonDTO parent,
|
||||
List<? extends ApiScenarioStepCommonDTO> steps) {
|
||||
if (CollectionUtils.isEmpty(steps)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<ApiScenarioStep> apiScenarioSteps = new ArrayList<>();
|
||||
long sort = 1;
|
||||
for (ApiScenarioStepRequest step : steps) {
|
||||
for (ApiScenarioStepCommonDTO step : steps) {
|
||||
ApiScenarioStep apiScenarioStep = new ApiScenarioStep();
|
||||
BeanUtils.copyBean(apiScenarioStep, step);
|
||||
apiScenarioStep.setSort(sort++);
|
||||
|
@ -540,13 +539,13 @@ public class ApiScenarioService {
|
|||
* @param steps
|
||||
* @return
|
||||
*/
|
||||
private List<ApiScenarioStepBlob> getPartialRefStepDetails(List<ApiScenarioStepRequest> steps) {
|
||||
private List<ApiScenarioStepBlob> getPartialRefStepDetails(List<? extends ApiScenarioStepCommonDTO> steps) {
|
||||
if (CollectionUtils.isEmpty(steps)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<ApiScenarioStepBlob> apiScenarioStepsDetails = new ArrayList<>();
|
||||
|
||||
for (ApiScenarioStepRequest step : steps) {
|
||||
for (ApiScenarioStepCommonDTO step : steps) {
|
||||
if (StringUtils.equals(step.getRefType(), ApiScenarioStepRefType.REF.name())) {
|
||||
// 引用的步骤不解析子步骤
|
||||
continue;
|
||||
|
@ -572,12 +571,12 @@ public class ApiScenarioService {
|
|||
* @param steps
|
||||
* @return
|
||||
*/
|
||||
private Set<String> getEnableStepSet(List<ApiScenarioStepRequest> steps) {
|
||||
private Set<String> getEnableStepSet(List<? extends ApiScenarioStepCommonDTO> steps) {
|
||||
Set<String> enableSteps = new HashSet<>();
|
||||
if (CollectionUtils.isEmpty(steps)) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
for (ApiScenarioStepRequest step : steps) {
|
||||
for (ApiScenarioStepCommonDTO step : steps) {
|
||||
if (BooleanUtils.isTrue(step.getEnable())) {
|
||||
enableSteps.add(step.getId());
|
||||
}
|
||||
|
@ -708,20 +707,19 @@ public class ApiScenarioService {
|
|||
* @param stepDetailMap
|
||||
*/
|
||||
private void parseStep2MsElement(AbstractMsTestElement parentElement,
|
||||
List<ApiScenarioStepRequest> steps,
|
||||
List<? extends ApiScenarioStepCommonDTO> steps,
|
||||
Map<String, String> resourceBlobMap,
|
||||
Map<String, String> stepDetailMap) {
|
||||
if (CollectionUtils.isNotEmpty(steps)) {
|
||||
parentElement.setChildren(new LinkedList<>());
|
||||
}
|
||||
for (ApiScenarioStepRequest step : steps) {
|
||||
for (ApiScenarioStepCommonDTO step : steps) {
|
||||
StepParser stepParser = StepParserFactory.getStepParser(step.getStepType());
|
||||
if (stepParser == null || BooleanUtils.isFalse(step.getEnable())) {
|
||||
continue;
|
||||
}
|
||||
if (StringUtils.equals(step.getStepType(), ApiScenarioStepRefType.PARTIAL_REF.name())) {
|
||||
setPartialRefStepEnable(step, stepDetailMap);
|
||||
}
|
||||
setPartialRefStepEnable(step, stepDetailMap);
|
||||
|
||||
// 将步骤详情解析生成对应的MsTestElement
|
||||
AbstractMsTestElement msTestElement = stepParser.parse(step, resourceBlobMap.get(step.getResourceId()), stepDetailMap.get(step.getId()));
|
||||
if (msTestElement != null) {
|
||||
|
@ -734,18 +732,18 @@ public class ApiScenarioService {
|
|||
}
|
||||
|
||||
/**
|
||||
* 设置部分引用的步骤的启用状态
|
||||
* 设置单个部分引用的步骤的启用状态
|
||||
*
|
||||
* @param step
|
||||
* @param stepDetailMap
|
||||
*/
|
||||
private void setPartialRefStepEnable(ApiScenarioStepRequest step, Map<String, String> stepDetailMap) {
|
||||
private void setPartialRefStepEnable(ApiScenarioStepCommonDTO step, Map<String, String> stepDetailMap) {
|
||||
String stepDetail = stepDetailMap.get(step.getId());
|
||||
if (StringUtils.isBlank(stepDetail)) {
|
||||
return;
|
||||
if (!isPartialRef(step) || StringUtils.isBlank(stepDetail)) {
|
||||
return;
|
||||
}
|
||||
PartialRefStepDetail partialRefStepDetail = JSON.parseObject(stepDetail, PartialRefStepDetail.class);
|
||||
setChildPartialRefEnable(step.getChildren(), partialRefStepDetail.getEnableStepIds(), stepDetailMap);
|
||||
setChildPartialRefEnable(step.getChildren(), partialRefStepDetail.getEnableStepIds());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -753,28 +751,31 @@ public class ApiScenarioService {
|
|||
*
|
||||
* @param steps
|
||||
* @param enableStepIds
|
||||
* @param stepDetailMap
|
||||
*/
|
||||
private void setChildPartialRefEnable(List<ApiScenarioStepRequest> steps, Set<String> enableStepIds, Map<String, String> stepDetailMap) {
|
||||
for (ApiScenarioStepRequest step : steps) {
|
||||
private void setChildPartialRefEnable(List<? extends ApiScenarioStepCommonDTO> steps, Set<String> enableStepIds) {
|
||||
for (ApiScenarioStepCommonDTO step : steps) {
|
||||
if (StringUtils.equals(step.getRefType(), ApiScenarioStepRefType.REF.name())) {
|
||||
// 引用的启用不修改
|
||||
continue;
|
||||
}
|
||||
// 非完全引用的步骤,使用当前场景配置的启用状态
|
||||
step.setEnable(enableStepIds.contains(step.getId()));
|
||||
if (StringUtils.equals(step.getRefType(), ApiScenarioStepRefType.PARTIAL_REF.name())) {
|
||||
// 如果是部分引用的场景,重新获取详情再解析
|
||||
setPartialRefStepEnable(step, stepDetailMap);
|
||||
if (isPartialRef(step)) {
|
||||
// 如果是部分引用的场景,不递归解析了,上层的递归会解析
|
||||
continue;
|
||||
}
|
||||
// 非完全引用和部分引用的步骤,递归设置子步骤
|
||||
if (CollectionUtils.isNotEmpty(step.getChildren())) {
|
||||
setChildPartialRefEnable(step.getChildren(), enableStepIds, stepDetailMap);
|
||||
setChildPartialRefEnable(step.getChildren(), enableStepIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isPartialRef(ApiScenarioStepCommonDTO step) {
|
||||
return StringUtils.equals(step.getStepType(), ApiScenarioStepType.API_SCENARIO.name()) &&
|
||||
StringUtils.equals(step.getRefType(), ApiScenarioStepRefType.PARTIAL_REF.name());
|
||||
}
|
||||
|
||||
private Map<String, String> getStepDetailMap(List<ApiScenarioStepRequest> steps, Map<String, Object> stepDetailsParam) {
|
||||
List<String> needBlobStepIds = new ArrayList<>();
|
||||
for (ApiScenarioStepRequest step : steps) {
|
||||
|
@ -836,8 +837,8 @@ public class ApiScenarioService {
|
|||
return apiScenarioBlobMapper.selectByExampleWithBLOBs(example);
|
||||
}
|
||||
|
||||
private void buildRefResourceIdMap(List<ApiScenarioStepRequest> steps, Map<String, List<String>> refResourceIdMap) {
|
||||
for (ApiScenarioStepRequest step : steps) {
|
||||
private void buildRefResourceIdMap(List<? extends ApiScenarioStepCommonDTO> steps, Map<String, List<String>> refResourceIdMap) {
|
||||
for (ApiScenarioStepCommonDTO step : steps) {
|
||||
if (isRef(step.getRefType()) && BooleanUtils.isTrue(step.getEnable())) {
|
||||
// 记录引用的步骤ID
|
||||
List<String> resourceIds = refResourceIdMap.get(step.getStepType());
|
||||
|
@ -888,4 +889,165 @@ public class ApiScenarioService {
|
|||
update.setUpdateTime(System.currentTimeMillis());
|
||||
apiScenarioMapper.updateByPrimaryKeySelective(update);
|
||||
}
|
||||
|
||||
public ApiScenarioDetail get(String scenarioId) {
|
||||
checkResourceExist(scenarioId);
|
||||
ApiScenario apiScenario = apiScenarioMapper.selectByPrimaryKey(scenarioId);
|
||||
ApiScenarioDetail apiScenarioDetail = BeanUtils.copyBean(new ApiScenarioDetail(), apiScenario);
|
||||
ApiScenarioBlob apiScenarioBlob = apiScenarioBlobMapper.selectByPrimaryKey(scenarioId);
|
||||
if (apiScenarioBlob != null) {
|
||||
apiScenarioDetail.setScenarioConfig(JSON.parseObject(new String(apiScenarioBlob.getConfig()), ScenarioConfig.class));
|
||||
}
|
||||
|
||||
// 获取所有步骤
|
||||
List<ApiScenarioStepDTO> allSteps = getAllStepsByScenarioIds(List.of(scenarioId));
|
||||
// 构造 map,key 为场景ID,value 为步骤列表
|
||||
Map<String, List<ApiScenarioStepDTO>> scenarioStepMap = allSteps.stream()
|
||||
.collect(Collectors.groupingBy(step -> Optional.ofNullable(step.getScenarioId()).orElse(StringUtils.EMPTY)));
|
||||
|
||||
// 查询部分引用的步骤详情和接口定义的步骤详情
|
||||
Map<String, String> stepDetailMap = getStepDetailMap(allSteps);
|
||||
|
||||
// key 为父步骤ID,value 为子步骤列表
|
||||
Map<String, List<ApiScenarioStepDTO>> parentStepMap = scenarioStepMap.get(scenarioId)
|
||||
.stream()
|
||||
.collect(Collectors.groupingBy(step -> Optional.ofNullable(step.getParentId()).orElse(StringUtils.EMPTY)));
|
||||
List<ApiScenarioStepDTO> steps = buildStepTree(parentStepMap.get(StringUtils.EMPTY), scenarioStepMap, scenarioStepMap);
|
||||
// 设置部分引用的步骤的启用状态
|
||||
setPartialRefStepsEnable(steps, stepDetailMap);
|
||||
|
||||
apiScenarioDetail.setSteps(steps);
|
||||
|
||||
return apiScenarioDetail;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置部分引用的步骤的启用状态
|
||||
*
|
||||
* @param steps
|
||||
* @param stepDetailMap
|
||||
*/
|
||||
private void setPartialRefStepsEnable(List<? extends ApiScenarioStepCommonDTO> steps, Map<String, String> stepDetailMap) {
|
||||
if (CollectionUtils.isNotEmpty(steps)) {
|
||||
return;
|
||||
}
|
||||
for (ApiScenarioStepCommonDTO step : steps) {
|
||||
setPartialRefStepEnable(step, stepDetailMap);
|
||||
if (CollectionUtils.isNotEmpty(step.getChildren())) {
|
||||
setPartialRefStepsEnable(step.getChildren(), stepDetailMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询部分引用的步骤详情 和 接口定义的步骤详情
|
||||
*
|
||||
* @param allSteps
|
||||
*/
|
||||
private Map<String, String> getStepDetailMap(List<ApiScenarioStepDTO> allSteps) {
|
||||
List<String> stepDetailIds = allSteps.stream().filter(step -> isRefApi(step.getStepType(), step.getRefType())
|
||||
|| StringUtils.equals(step.getRefType(), ApiScenarioStepRefType.PARTIAL_REF.name()))
|
||||
.map(ApiScenarioStepDTO::getId)
|
||||
.collect(Collectors.toList());
|
||||
if (CollectionUtils.isEmpty(stepDetailIds)) {
|
||||
return new HashMap<>(0);
|
||||
}
|
||||
ApiScenarioBlobExample example = new ApiScenarioBlobExample();
|
||||
example.createCriteria().andIdIn(stepDetailIds);
|
||||
return apiScenarioBlobMapper.selectByExample(example).stream()
|
||||
.collect(Collectors.toMap(ApiScenarioBlob::getId, blob -> new String(blob.getConfig())));
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断步骤是否是引用的接口定义
|
||||
* 引用的接口定义允许修改参数值,需要特殊处理
|
||||
*
|
||||
* @param stepType
|
||||
* @param refType
|
||||
* @return
|
||||
*/
|
||||
private boolean isRefApi(String stepType, String refType) {
|
||||
return StringUtils.equals(stepType, ApiScenarioStepType.API.name()) && StringUtils.equals(refType, ApiScenarioStepRefType.REF.name());
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归构造步骤树
|
||||
*
|
||||
* @param steps 当前场景下,当前层级的步骤
|
||||
* @param parentStepMap 当前场景所有的步骤,key 为父步骤ID,value 为子步骤列表
|
||||
* @param scenarioStepMap 所有场景步骤,key 为场景ID,value 为子步骤列表
|
||||
*/
|
||||
private List<ApiScenarioStepDTO> buildStepTree(List<ApiScenarioStepDTO> steps,
|
||||
Map<String, List<ApiScenarioStepDTO>> parentStepMap,
|
||||
Map<String, List<ApiScenarioStepDTO>> scenarioStepMap) {
|
||||
if (CollectionUtils.isEmpty(steps)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
steps.forEach(step -> {
|
||||
// 获取当前步骤的子步骤
|
||||
List<ApiScenarioStepDTO> children = parentStepMap.get(step.getId());
|
||||
if (CollectionUtils.isEmpty(children)) {
|
||||
return;
|
||||
}
|
||||
if (isRefApiScenario(step)) {
|
||||
// 如果当前步骤是引用的场景,获取该场景的子步骤
|
||||
Map<String, List<ApiScenarioStepDTO>> childStepMap = scenarioStepMap.get(step.getResourceId())
|
||||
.stream()
|
||||
.collect(Collectors.groupingBy(ApiScenarioStepDTO::getParentId));
|
||||
step.setChildren(buildStepTree(children, childStepMap, scenarioStepMap));
|
||||
} else {
|
||||
step.setChildren(buildStepTree(children, parentStepMap, scenarioStepMap));
|
||||
}
|
||||
});
|
||||
// 排序
|
||||
return steps.stream()
|
||||
.sorted(Comparator.comparing(ApiScenarioStepDTO::getSort))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断步骤是否是引用的场景
|
||||
* @param step
|
||||
* @return
|
||||
*/
|
||||
private boolean isRefApiScenario(ApiScenarioStepDTO step) {
|
||||
return isRef(step.getRefType()) && StringUtils.equals(step.getStepType(), ApiScenarioStepType.API_SCENARIO.name());
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归获取所有的场景步骤
|
||||
*
|
||||
* @param scenarioIds
|
||||
* @return
|
||||
*/
|
||||
private List<ApiScenarioStepDTO> getAllStepsByScenarioIds(List<String> scenarioIds) {
|
||||
List<ApiScenarioStepDTO> steps = getStepDTOByScenarioIds(scenarioIds);
|
||||
if (CollectionUtils.isEmpty(steps)) {
|
||||
return steps;
|
||||
}
|
||||
|
||||
// 将 config 转换成对象
|
||||
steps.stream().forEach(step -> {
|
||||
if (step.getConfig() != null && StringUtils.isNotBlank(step.getConfig().toString())) {
|
||||
step.setConfig(JSON.parseObject(step.getConfig().toString()));
|
||||
}
|
||||
});
|
||||
|
||||
// 获取步骤中引用的场景ID
|
||||
List<String> childScenarioIds = steps.stream()
|
||||
.filter(step -> isRefApiScenario(step))
|
||||
.map(ApiScenarioStepDTO::getResourceId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 嵌套获取引用的场景步骤
|
||||
steps.addAll(getAllStepsByScenarioIds(childScenarioIds));
|
||||
return steps;
|
||||
}
|
||||
|
||||
private List<ApiScenarioStepDTO> getStepDTOByScenarioIds(List<String> scenarioIds) {
|
||||
if (CollectionUtils.isEmpty(scenarioIds)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return extApiScenarioStepMapper.getStepDTOByScenarioIds(scenarioIds);
|
||||
}
|
||||
}
|
|
@ -94,7 +94,9 @@ public class ApiScenarioControllerTests extends BaseTest {
|
|||
@Resource
|
||||
private BaseResourcePoolTestService baseResourcePoolTestService;
|
||||
private static ApiScenario addApiScenario;
|
||||
private static List<ApiScenarioStepRequest> addApiScenarioSteps;
|
||||
private static ApiScenario anOtherAddApiScenario;
|
||||
private static List<ApiScenarioStepRequest> anOtherAddApiScenarioSteps;
|
||||
private static ApiDefinition apiDefinition;
|
||||
private static ApiTestCase apiTestCase;
|
||||
|
||||
|
@ -229,6 +231,7 @@ public class ApiScenarioControllerTests extends BaseTest {
|
|||
stepRequest.setName(addApiScenario.getName());
|
||||
stepRequest.setResourceId(addApiScenario.getId());
|
||||
stepRequest.setRefType(ApiScenarioStepRefType.REF.name());
|
||||
stepRequest.setStepType(ApiScenarioStepType.API_SCENARIO.name());
|
||||
|
||||
ApiScenarioStepRequest stepRequest2 = new ApiScenarioStepRequest();
|
||||
stepRequest2.setId(IDGenerator.nextStr());
|
||||
|
@ -236,12 +239,14 @@ public class ApiScenarioControllerTests extends BaseTest {
|
|||
stepRequest2.setResourceId(addApiScenario.getId());
|
||||
stepRequest2.setRefType(ApiScenarioStepRefType.PARTIAL_REF.name());
|
||||
stepRequest2.setChildren(List.of(stepRequest));
|
||||
stepRequest2.setStepType(ApiScenarioStepType.API_SCENARIO.name());
|
||||
steps = List.of(stepRequest, stepRequest2);
|
||||
request.setSteps(steps);
|
||||
mvcResult = this.requestPostWithOkAndReturn(DEFAULT_ADD, request);
|
||||
this.anOtherAddApiScenario = apiScenarioMapper.selectByPrimaryKey(getResultData(mvcResult, ApiScenario.class).getId());
|
||||
assertUpdateApiScenario(request, request.getScenarioConfig(), anOtherAddApiScenario.getId());
|
||||
assertUpdateSteps(steps, steptDetailMap);
|
||||
anOtherAddApiScenarioSteps = steps;
|
||||
|
||||
// @@重名校验异常
|
||||
assertErrorCode(this.requestPost(DEFAULT_ADD, request), API_SCENARIO_EXIST);
|
||||
|
@ -314,6 +319,8 @@ public class ApiScenarioControllerTests extends BaseTest {
|
|||
stepRequest.setName(apiTestCase.getName());
|
||||
stepRequest.setResourceId(apiTestCase.getId());
|
||||
stepRequest.setRefType(ApiScenarioStepRefType.REF.name());
|
||||
stepRequest.setStepType(ApiScenarioStepType.API_CASE.name());
|
||||
stepRequest.setProjectId(DEFAULT_PROJECT_ID);
|
||||
stepRequest.setConfig(new HashMap<>());
|
||||
|
||||
ApiScenarioStepRequest stepRequest2 = new ApiScenarioStepRequest();
|
||||
|
@ -322,17 +329,22 @@ public class ApiScenarioControllerTests extends BaseTest {
|
|||
stepRequest2.setConfig(new HashMap<>());
|
||||
stepRequest2.setEnable(true);
|
||||
stepRequest2.setResourceId(apiTestCase.getId());
|
||||
stepRequest.setName(apiTestCase.getName() + "2");
|
||||
stepRequest2.setName(apiTestCase.getName() + "2");
|
||||
stepRequest2.setStepType(ApiScenarioStepType.API_CASE.name());
|
||||
stepRequest2.setRefType(ApiScenarioStepRefType.COPY.name());
|
||||
stepRequest2.setProjectId(DEFAULT_PROJECT_ID);
|
||||
|
||||
ApiScenarioStepRequest stepRequest3 = new ApiScenarioStepRequest();
|
||||
stepRequest3.setId(IDGenerator.nextStr());
|
||||
stepRequest3.setVersionId(extBaseProjectVersionMapper.getDefaultVersion(DEFAULT_PROJECT_ID));
|
||||
stepRequest3.setConfig(new HashMap<>());
|
||||
stepRequest3.setEnable(true);
|
||||
stepRequest3.setStepType(ApiScenarioStepType.API.name());
|
||||
stepRequest3.setResourceId(apiDefinition.getId());
|
||||
stepRequest.setName(apiDefinition.getName() + "3");
|
||||
stepRequest3.setName(apiDefinition.getName() + "3");
|
||||
stepRequest3.setRefType(ApiScenarioStepRefType.REF.name());
|
||||
stepRequest3.setProjectId(DEFAULT_PROJECT_ID);
|
||||
|
||||
return new ArrayList<>() {{
|
||||
add(stepRequest);
|
||||
add(stepRequest2);
|
||||
|
@ -409,6 +421,7 @@ public class ApiScenarioControllerTests extends BaseTest {
|
|||
steps.get(0).setName("test name update");
|
||||
this.requestPostWithOk(DEFAULT_UPDATE, request);
|
||||
assertUpdateSteps(steps, steptDetailMap);
|
||||
addApiScenarioSteps = steps;
|
||||
|
||||
// @@重名校验异常
|
||||
request.setName(anOtherAddApiScenario.getName());
|
||||
|
@ -475,6 +488,37 @@ public class ApiScenarioControllerTests extends BaseTest {
|
|||
requestPostPermissionTest(PermissionConstants.PROJECT_API_SCENARIO_EXECUTE, DEBUG, request);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(7)
|
||||
public void get() throws Exception {
|
||||
MvcResult mvcResult = this.requestGetWithOkAndReturn(DEFAULT_GET, addApiScenario.getId());
|
||||
ApiScenarioDetail apiScenarioDetail = getResultData(mvcResult, ApiScenarioDetail.class);
|
||||
// 验证数据
|
||||
asserGetApiScenarioSteps(this.addApiScenarioSteps, apiScenarioDetail.getSteps());
|
||||
|
||||
mvcResult = this.requestGetWithOkAndReturn(DEFAULT_GET, anOtherAddApiScenario.getId());
|
||||
apiScenarioDetail = getResultData(mvcResult, ApiScenarioDetail.class);
|
||||
// 验证数据
|
||||
Assertions.assertEquals(this.anOtherAddApiScenarioSteps.size(), apiScenarioDetail.getSteps().size());
|
||||
// @@校验权限
|
||||
requestGetPermissionTest(PermissionConstants.PROJECT_API_SCENARIO_READ, DEFAULT_GET, addApiScenario.getId());
|
||||
}
|
||||
|
||||
private void asserGetApiScenarioSteps(List<? extends ApiScenarioStepCommonDTO> addApiScenarioSteps, List<? extends ApiScenarioStepCommonDTO> steps) {
|
||||
if (addApiScenarioSteps == null || steps == null) {
|
||||
Assertions.assertEquals(addApiScenarioSteps, null);
|
||||
Assertions.assertEquals(steps, null);
|
||||
return;
|
||||
}
|
||||
Assertions.assertEquals(addApiScenarioSteps.size(), steps.size());
|
||||
for (int i = 0; i < addApiScenarioSteps.size(); i++) {
|
||||
ApiScenarioStepRequest stepRequest = (ApiScenarioStepRequest) addApiScenarioSteps.get(i);
|
||||
ApiScenarioStepDTO stepDTO = (ApiScenarioStepDTO) steps.get(i);
|
||||
Assertions.assertEquals(BeanUtils.copyBean(new ApiScenarioStepCommonDTO(), stepRequest), BeanUtils.copyBean(new ApiScenarioStepCommonDTO(), stepDTO));
|
||||
asserGetApiScenarioSteps(stepRequest.getChildren(), stepDTO.getChildren());
|
||||
}
|
||||
}
|
||||
|
||||
private String doUploadTempFile(MockMultipartFile file) throws Exception {
|
||||
return JSON.parseObject(requestUploadFileWithOkAndReturn(UPLOAD_TEMP_FILE, file)
|
||||
.getResponse()
|
||||
|
@ -497,7 +541,8 @@ public class ApiScenarioControllerTests extends BaseTest {
|
|||
public void deleteToGc() throws Exception {
|
||||
// @@请求成功
|
||||
this.requestGetWithOk(DELETE_TO_GC, addApiScenario.getId());
|
||||
// todo 校验请求成功数据
|
||||
ApiScenario apiScenario = apiScenarioMapper.selectByPrimaryKey(addApiScenario.getId());
|
||||
Assertions.assertTrue(apiScenario.getDeleted());
|
||||
// @@校验日志
|
||||
checkLog(addApiScenario.getId(), OperationLogType.DELETE);
|
||||
// @@校验权限
|
||||
|
@ -509,7 +554,10 @@ public class ApiScenarioControllerTests extends BaseTest {
|
|||
public void delete() throws Exception {
|
||||
// @@请求成功
|
||||
this.requestGetWithOk(DEFAULT_DELETE, addApiScenario.getId());
|
||||
// todo 校验请求成功数据
|
||||
ApiScenario apiScenario = apiScenarioMapper.selectByPrimaryKey(addApiScenario.getId());
|
||||
ApiScenarioBlob apiScenarioBlob = apiScenarioBlobMapper.selectByPrimaryKey(addApiScenario.getId());
|
||||
Assertions.assertNull(apiScenario);
|
||||
Assertions.assertNull(apiScenarioBlob);
|
||||
// @@校验日志
|
||||
checkLog(addApiScenario.getId(), OperationLogType.DELETE);
|
||||
// @@校验权限
|
||||
|
|
Loading…
Reference in New Issue