feat(接口测试): 获取场景步骤详情接口

This commit is contained in:
AgAngle 2024-01-25 16:46:49 +08:00 committed by Craftsman
parent 478e119a2c
commit b5dca1b323
13 changed files with 204 additions and 20 deletions

View File

@ -380,6 +380,7 @@ schedule_not_exist=定时任务不存在
api_case_report_not_exist=用例报告不存在
api_scenario_report_not_exist=场景报告不存在
permission.api_plugin.name=接口插件
permission.api_step.name=步骤
#moduleApiScenarioCsv
api_scenario_csv.file_id.not_blank=文件ID不能为空
@ -398,4 +399,3 @@ api_scenario_csv.ignore_first_line.not_blank=是否忽略第一行
api_scenario_csv.allow_quoted_data.not_blank=是否允许带引号
api_scenario_csv.recycle_on_eof.not_blank=是否循环
api_scenario_csv.stop_thread_on_eof.not_blank=是否停止线程

View File

@ -392,6 +392,7 @@ schedule_not_exist=The scheduled task does not exist
api_case_report_not_exist=Api report does not exist
api_scenario_report_not_exist=Scenario report does not exist
permission.api_plugin.name=Api Plugin
permission.api_step.name=Step
#moduleApiScenarioCsv
api_scenario_csv.file_id.not_blank=File ID cannot be empty

View File

@ -361,6 +361,7 @@ schedule_not_exist=定时任务不存在
api_case_report_not_exist=用例报告不存在
api_scenario_report_not_exist=场景报告不存在
permission.api_plugin.name=接口插件
permission.api_step.name=步骤
#moduleApiScenarioCsv
api_scenario_csv.file_id.not_blank=文件ID不能为空

View File

@ -361,6 +361,7 @@ schedule_not_exist=定時任務不存在
api_case_report_not_exist=用例報告不存在
api_scenario_report_not_exist=場景報告不存在
permission.api_plugin.name=接口插件
permission.api_step.name=步驟
#moduleApiScenarioCsv 繁体
api_scenario_csv.file_id.not_blank=文件ID不能為空

View File

@ -114,10 +114,18 @@ public class ApiScenarioController {
@GetMapping("/get/{scenarioId}")
@Operation(summary = "接口测试-接口场景管理-获取场景详情")
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_READ)
@CheckOwner(resourceId = "#scenarioId", resourceType = "api_scenario")
public ApiScenarioDetail get(@PathVariable String scenarioId) {
return apiScenarioService.get(scenarioId);
}
@GetMapping("/step/get/{stepId}")
@Operation(summary = "接口测试-接口场景管理-获取场景步骤详情")
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_READ)
public Object getStepDetail(@PathVariable String stepId) {
return apiScenarioService.getStepDetail(stepId);
}
@PostMapping("/debug")
@Operation(summary = "接口测试-接口场景管理-场景调试")
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_EXECUTE)

View File

@ -1,8 +1,12 @@
package io.metersphere.api.parser.step;
import io.metersphere.api.domain.ApiScenarioStep;
import io.metersphere.api.domain.ApiTestCaseBlob;
import io.metersphere.api.dto.scenario.ApiScenarioStepCommonDTO;
import io.metersphere.api.mapper.ApiTestCaseBlobMapper;
import io.metersphere.api.utils.ApiDataUtils;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.sdk.util.CommonBeanFactory;
import org.apache.commons.lang3.StringUtils;
/**
@ -11,7 +15,7 @@ import org.apache.commons.lang3.StringUtils;
*/
public class ApiCaseStepParser extends StepParser {
@Override
public AbstractMsTestElement parse(ApiScenarioStepCommonDTO step, String resourceBlob, String stepDetail) {
public AbstractMsTestElement parseTestElement(ApiScenarioStepCommonDTO step, String resourceBlob, String stepDetail) {
if (isRef(step.getRefType())) {
return StringUtils.isBlank(resourceBlob) ? null : parse2MsTestElement(resourceBlob);
} else {
@ -21,4 +25,18 @@ public class ApiCaseStepParser extends StepParser {
return StringUtils.isBlank(stepDetail) ? null : ApiDataUtils.parseObject(stepDetail, AbstractMsTestElement.class);
}
}
@Override
public Object parseDetail(ApiScenarioStep step) {
if (isRef(step.getRefType())) {
ApiTestCaseBlobMapper apiTestCaseBlobMapper = CommonBeanFactory.getBean(ApiTestCaseBlobMapper.class);
ApiTestCaseBlob apiTestCaseBlob = apiTestCaseBlobMapper.selectByPrimaryKey(step.getResourceId());
if (apiTestCaseBlob == null) {
return null;
}
return parse2MsTestElement(new String(apiTestCaseBlob.getRequest()));
} else {
return parse2MsTestElement(getStepBlobString(step.getId()));
}
}
}

View File

@ -1,9 +1,13 @@
package io.metersphere.api.parser.step;
import io.metersphere.api.domain.ApiScenarioBlob;
import io.metersphere.api.domain.ApiScenarioStep;
import io.metersphere.api.dto.request.MsScenario;
import io.metersphere.api.dto.scenario.ApiScenarioStepCommonDTO;
import io.metersphere.api.dto.scenario.ScenarioConfig;
import io.metersphere.api.mapper.ApiScenarioBlobMapper;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.sdk.util.CommonBeanFactory;
import io.metersphere.sdk.util.JSON;
import org.apache.commons.lang3.StringUtils;
@ -13,7 +17,7 @@ import org.apache.commons.lang3.StringUtils;
*/
public class ApiScenarioStepParser extends StepParser {
@Override
public AbstractMsTestElement parse(ApiScenarioStepCommonDTO step, String resourceBlob, String stepDetail) {
public AbstractMsTestElement parseTestElement(ApiScenarioStepCommonDTO step, String resourceBlob, String stepDetail) {
MsScenario msScenario = new MsScenario();
if (isRef(step.getRefType())) {
if (StringUtils.isNotBlank(resourceBlob)) {
@ -26,4 +30,27 @@ public class ApiScenarioStepParser extends StepParser {
}
return msScenario;
}
/**
* 获取场景配置详情
* @param step
* @return
*/
@Override
public Object parseDetail(ApiScenarioStep step) {
if (isRef(step.getRefType())) {
ApiScenarioBlobMapper apiScenarioBlobMapper = CommonBeanFactory.getBean(ApiScenarioBlobMapper.class);
ApiScenarioBlob apiScenarioBlob = apiScenarioBlobMapper.selectByPrimaryKey(step.getResourceId());
if (apiScenarioBlob == null || apiScenarioBlob.getConfig() == null) {
return null;
}
return JSON.parseObject(new String(apiScenarioBlob.getConfig()), ScenarioConfig.class);
} else {
String stepDetailStr= getStepBlobString(step.getId());
if (StringUtils.isBlank(stepDetailStr)) {
return null;
}
return JSON.parseObject(stepDetailStr, ScenarioConfig.class);
}
}
}

View File

@ -1,11 +1,15 @@
package io.metersphere.api.parser.step;
import io.metersphere.api.domain.ApiDefinitionBlob;
import io.metersphere.api.domain.ApiScenarioStep;
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.ApiScenarioStepCommonDTO;
import io.metersphere.api.mapper.ApiDefinitionBlobMapper;
import io.metersphere.api.utils.ApiDataUtils;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.sdk.util.CommonBeanFactory;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
@ -17,24 +21,50 @@ import java.util.List;
*/
public class ApiStepParser extends StepParser {
@Override
public AbstractMsTestElement parse(ApiScenarioStepCommonDTO step, String resourceBlob, String stepDetail) {
public AbstractMsTestElement parseTestElement(ApiScenarioStepCommonDTO step, String resourceBlob, String stepDetail) {
if (isRef(step.getRefType())) {
if (StringUtils.isBlank(resourceBlob)) {
return null;
}
AbstractMsTestElement refResourceElement = parse2MsTestElement(resourceBlob);
if (refResourceElement instanceof MsHTTPElement && StringUtils.isNotBlank(stepDetail)) {
// 如果是 http 并且有修改请求参数则替换请求参数
AbstractMsTestElement msTestElement = ApiDataUtils.parseObject(stepDetail, AbstractMsTestElement.class);
return replaceParams((MsHTTPElement) msTestElement, (MsHTTPElement) refResourceElement);
} else {
return refResourceElement;
}
return parseRefTestElement(resourceBlob, stepDetail);
} else {
return StringUtils.isBlank(stepDetail) ? null : ApiDataUtils.parseObject(stepDetail, AbstractMsTestElement.class);
}
}
/**
* 处理引用的接口步骤
* 替换修改的参数
* @param resourceBlob 引用的接口步骤详情
* @param stepDetail 引用之后修改的步骤详情
* @return
*/
public AbstractMsTestElement parseRefTestElement(String resourceBlob, String stepDetail) {
if (StringUtils.isBlank(resourceBlob)) {
return null;
}
AbstractMsTestElement refResourceElement = parse2MsTestElement(resourceBlob);
if (refResourceElement instanceof MsHTTPElement && StringUtils.isNotBlank(stepDetail)) {
// 如果是 http 并且有修改请求参数则替换请求参数
AbstractMsTestElement stepElement = parse2MsTestElement(stepDetail);
return replaceParams((MsHTTPElement) stepElement, (MsHTTPElement) refResourceElement);
} else {
return refResourceElement;
}
}
@Override
public Object parseDetail(ApiScenarioStep step) {
if (isRef(step.getRefType())) {
ApiDefinitionBlobMapper apiDefinitionBlobMapper = CommonBeanFactory.getBean(ApiDefinitionBlobMapper.class);
ApiDefinitionBlob apiDefinitionBlob = apiDefinitionBlobMapper.selectByPrimaryKey(step.getResourceId());
if (apiDefinitionBlob == null) {
return null;
}
return parseRefTestElement(new String(apiDefinitionBlob.getRequest()), getStepBlobString(step.getId()));
} else {
return parse2MsTestElement(getStepBlobString(step.getId()));
}
}
private AbstractMsTestElement replaceParams(MsHTTPElement msTestElement, MsHTTPElement refResourceElement) {
replaceKvParam(msTestElement.getHeaders(), refResourceElement.getHeaders());

View File

@ -0,0 +1,24 @@
package io.metersphere.api.parser.step;
import io.metersphere.api.domain.ApiScenarioStep;
import io.metersphere.api.dto.scenario.ApiScenarioStepCommonDTO;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
/**
* 默认的步骤解析器
* 逻辑控制器等步骤直接将步骤详情解析为 MsTestElement
* @Author: jianxing
* @CreateTime: 2024-01-20 15:43
*/
public class DefaultStepParser extends StepParser {
@Override
public AbstractMsTestElement parseTestElement(ApiScenarioStepCommonDTO step, String resourceBlob, String stepDetail) {
return parse2MsTestElement(stepDetail);
}
@Override
public Object parseDetail(ApiScenarioStep step) {
return parse2MsTestElement(getStepBlobString(step.getId()));
}
}

View File

@ -1,10 +1,13 @@
package io.metersphere.api.parser.step;
import io.metersphere.api.constants.ApiScenarioStepRefType;
import io.metersphere.api.domain.ApiScenarioStep;
import io.metersphere.api.domain.ApiScenarioStepBlob;
import io.metersphere.api.dto.scenario.ApiScenarioStepCommonDTO;
import io.metersphere.api.dto.scenario.ApiScenarioStepRequest;
import io.metersphere.api.mapper.ApiScenarioStepBlobMapper;
import io.metersphere.api.utils.ApiDataUtils;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.sdk.util.CommonBeanFactory;
import org.apache.commons.lang3.StringUtils;
/**
@ -13,13 +16,42 @@ import org.apache.commons.lang3.StringUtils;
*/
public abstract class StepParser {
public abstract AbstractMsTestElement parse(ApiScenarioStepCommonDTO step, String resourceBlob, String stepDetail);
/**
* 将步骤详情解析为 MsTestElement
* @param step 步骤
* @param resourceBlob 关联的资源详情
* @param stepDetail 步骤详情
* @return
*/
public abstract AbstractMsTestElement parseTestElement(ApiScenarioStepCommonDTO step, String resourceBlob, String stepDetail);
/**
* 将步骤解析为步骤详情
* 场景步骤返回 ScenarioConfig
* 其余返回 MsTestElement
* @param step
* @return
*/
public abstract Object parseDetail(ApiScenarioStep step);
protected boolean isRef(String refType) {
return StringUtils.equalsAny(refType, ApiScenarioStepRefType.REF.name(), ApiScenarioStepRefType.PARTIAL_REF.name());
}
protected static AbstractMsTestElement parse2MsTestElement(String blobContent) {
if (StringUtils.isBlank(blobContent)) {
return null;
}
return ApiDataUtils.parseObject(blobContent, AbstractMsTestElement.class);
}
protected String getStepBlobString(String stepId) {
ApiScenarioStepBlobMapper apiScenarioStepBlobMapper = CommonBeanFactory.getBean(ApiScenarioStepBlobMapper.class);
ApiScenarioStepBlob apiScenarioStepBlob = apiScenarioStepBlobMapper.selectByPrimaryKey(stepId);
if (apiScenarioStepBlob == null) {
return null;
}
return new String(apiScenarioStepBlob.getContent());
}
}

View File

@ -12,6 +12,7 @@ import java.util.Map;
public abstract class StepParserFactory {
private static Map<String, StepParser> stepParserMap = new HashMap<>();
private static StepParser defaultStepParser = new DefaultStepParser();
static {
stepParserMap.put(ApiScenarioStepType.API.name(), new ApiStepParser());
@ -20,6 +21,7 @@ public abstract class StepParserFactory {
}
public static StepParser getStepParser(String stepType) {
return stepParserMap.get(stepType);
StepParser stepParser = stepParserMap.get(stepType);
return stepParser == null ? defaultStepParser : stepParser;
}
}

View File

@ -908,13 +908,13 @@ public class ApiScenarioService {
}
for (ApiScenarioStepCommonDTO step : steps) {
StepParser stepParser = StepParserFactory.getStepParser(step.getStepType());
if (stepParser == null || BooleanUtils.isFalse(step.getEnable())) {
if (BooleanUtils.isFalse(step.getEnable())) {
continue;
}
setPartialRefStepEnable(step, stepDetailMap);
// 将步骤详情解析生成对应的MsTestElement
AbstractMsTestElement msTestElement = stepParser.parse(step, resourceBlobMap.get(step.getResourceId()), stepDetailMap.get(step.getId()));
AbstractMsTestElement msTestElement = stepParser.parseTestElement(step, resourceBlobMap.get(step.getResourceId()), stepDetailMap.get(step.getId()));
if (msTestElement != null) {
parentElement.getChildren().add(msTestElement);
}
@ -1261,4 +1261,14 @@ public class ApiScenarioService {
}
return extApiScenarioStepMapper.getStepDTOByScenarioIds(scenarioIds);
}
public Object getStepDetail(String stepId) {
ApiScenarioStep step = checkStepExist(stepId);
StepParser stepParser = StepParserFactory.getStepParser(step.getStepType());
return stepParser.parseDetail(step);
}
private ApiScenarioStep checkStepExist(String id) {
return ServiceUtils.checkResourceExist(apiScenarioStepMapper.selectByPrimaryKey(id), "permission.api_step.name");
}
}

View File

@ -15,6 +15,7 @@ import io.metersphere.api.mapper.*;
import io.metersphere.api.service.BaseResourcePoolTestService;
import io.metersphere.api.service.definition.ApiDefinitionService;
import io.metersphere.api.service.definition.ApiTestCaseService;
import io.metersphere.api.service.scenario.ApiScenarioService;
import io.metersphere.api.utils.ApiDataUtils;
import io.metersphere.project.dto.filemanagement.request.FileUploadRequest;
import io.metersphere.project.mapper.ExtBaseProjectVersionMapper;
@ -65,6 +66,7 @@ public class ApiScenarioControllerTests extends BaseTest {
private static final String FOLLOW = "follow/";
protected static final String UPLOAD_TEMP_FILE = "upload/temp/file";
protected static final String DELETE_TO_GC = "delete-to-gc/{0}";
protected static final String STEP_GET = "step/get/{0}";
protected static final String DEBUG = "debug";
private static final String UPDATE_STATUS = "update-status";
private static final String UPDATE_PRIORITY = "update-priority";
@ -100,6 +102,8 @@ public class ApiScenarioControllerTests extends BaseTest {
private FileMetadataService fileMetadataService;
@Resource
private ApiScenarioCsvMapper apiScenarioCsvMapper;
@Resource
private ApiScenarioService apiScenarioService;
private static String fileMetadataId;
private static String localFileId;
private static ApiScenario addApiScenario;
@ -565,6 +569,32 @@ public class ApiScenarioControllerTests extends BaseTest {
requestGetPermissionTest(PermissionConstants.PROJECT_API_SCENARIO_READ, DEFAULT_GET, addApiScenario.getId());
}
@Test
@Order(7)
public void getStepDetail() throws Exception {
ApiScenarioDetail apiScenarioDetail = apiScenarioService.get(addApiScenario.getId());
List<ApiScenarioStepDTO> steps = apiScenarioDetail.getSteps();
requestGetStepDetail(steps);
apiScenarioDetail = apiScenarioService.get(anOtherAddApiScenario.getId());
steps = apiScenarioDetail.getSteps();
requestGetStepDetail(steps);
// @@校验权限
requestGetPermissionTest(PermissionConstants.PROJECT_API_SCENARIO_READ, STEP_GET, addApiScenario.getId());
}
private void requestGetStepDetail(List<? extends ApiScenarioStepCommonDTO> steps) throws Exception {
if (CollectionUtils.isEmpty(steps)) {
return;
}
for (ApiScenarioStepCommonDTO step : steps) {
this.requestGetWithOk(STEP_GET, step.getId());
List<? extends ApiScenarioStepCommonDTO> children = step.getChildren();
requestGetStepDetail(children);
}
}
private void asserGetApiScenarioSteps(List<? extends ApiScenarioStepCommonDTO> addApiScenarioSteps, List<? extends ApiScenarioStepCommonDTO> steps) {
if (addApiScenarioSteps == null || steps == null) {
Assertions.assertEquals(addApiScenarioSteps, null);