refactor(接口测试): 重构接口执行

This commit is contained in:
AgAngle 2024-06-05 11:49:06 +08:00 committed by Craftsman
parent 18c713238d
commit 8a307bb87d
28 changed files with 1826 additions and 1392 deletions

View File

@ -0,0 +1,41 @@
package io.metersphere.sdk.dto.api.task;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 获取执行脚本请求参数
*/
@Data
public class GetRunScriptRequest implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 任务项
*/
private TaskItem taskItem;
/**
* 操作人
*/
private String userId;
/**
* {@link io.metersphere.sdk.constants.ApiExecuteRunMode}
*/
@NotBlank
private String runMode;
/**
* 运行配置
*/
@Valid
private ApiRunModeConfigDTO runModeConfig = new ApiRunModeConfigDTO();
/**
* 资源类型
*
* @see io.metersphere.sdk.constants.ApiExecuteResourceType
*/
private String resourceType;
}

View File

@ -0,0 +1,34 @@
package io.metersphere.sdk.dto.api.task;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 获取执行脚本请求结果
*/
@Data
public class GetRunScriptResult implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 执行脚本
*/
private String script;
/**
* 要执行的请求总量用于计算执行各种指标
*/
private Long requestCount;
/**
* 执行所需的文件
*/
private TaskResourceFile taskResourceFile;
/**
* 场景执行时关联的其他项目的步骤所需的资源
*/
private TaskProjectResource refProjectResource = new TaskProjectResource();
}

View File

@ -0,0 +1,29 @@
package io.metersphere.sdk.dto.api.task;
import jakarta.validation.Valid;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
* 任务请求参数数据
*/
@Data
public class TaskBatchRequestDTO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 任务的基本信息
*/
@Valid
private TaskInfo taskInfo = new TaskInfo();
/**
* 任务项
*/
@Valid
private List<TaskItem> taskItems;
}

View File

@ -0,0 +1,89 @@
package io.metersphere.sdk.dto.api.task;
import io.metersphere.sdk.dto.api.result.MsRegexDTO;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
* 任务请求参数数据
*/
@Data
public class TaskInfo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private String msUrl;
private String kafkaConfig;
private String minioConfig;
private int poolSize;
/**
* 批量执行时的队列ID
*/
private String queueId;
/**
* 是否需要实时接收单个步骤的结果
*/
private Boolean realTime = false;
/**
* 是否保存执行结果
*/
private Boolean saveResult = true;
/**
* 是否需要解析脚本
* 接口详情页面需要传试试详情会其他解析脚本needParseScript false
* 不传详情执行时通过 task-runner 发起解析脚本请求needParseScript true
*/
private Boolean needParseScript = true;
/**
* 操作人
*/
private String userId;
/**
* 触发方式
* 手动执行批量执行API执行定时任务
* {@link io.metersphere.sdk.constants.TaskTriggerMode}
*/
private String triggerMode;
/**
* 资源类型
*
* @see io.metersphere.sdk.constants.ApiExecuteResourceType
*/
private String resourceType;
/**
* 当前项目执行时所需的资源
*/
private TaskProjectResource projectResource = new TaskProjectResource();
/**
* 误报规则
*/
private List<MsRegexDTO> msRegexList;
/**
* 项目id
*/
@NotBlank
private String projectId;
/**
* {@link io.metersphere.sdk.constants.ApiExecuteRunMode}
*/
@NotBlank
private String runMode;
/**
* 运行配置
*/
@Valid
private ApiRunModeConfigDTO runModeConfig = new ApiRunModeConfigDTO();
/**
* 记录执行时的环境变量
*/
private List<String> environmentVariables;
}

View File

@ -0,0 +1,36 @@
package io.metersphere.sdk.dto.api.task;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 任务项
*/
@Data
public class TaskItem implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@NotBlank
private String reportId;
/**
* 执行的资源ID
*/
@NotBlank
private String resourceId;
/**
* 待执行任务所需要的文件资源
*/
private TaskResourceFile taskResourceFile = new TaskResourceFile();
/**
* 场景执行时关联的其他项目的步骤所需的资源
*/
private TaskProjectResource refProjectResource = new TaskProjectResource();
/**
* 要执行的请求总量用于计算执行各种指标
*/
private Long requestCount;
}

View File

@ -0,0 +1,28 @@
package io.metersphere.sdk.dto.api.task;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* 任务项
*/
@Data
public class TaskProjectResource implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 插件文件列表
* id 为插件的 id + 更新时间戳
* 任务中心批量执行时会有并发操作使用 CopyOnWriteArrayList
*/
private CopyOnWriteArrayList<ApiExecuteFileInfo> pluginFiles;
/**
* 接口测试函数包
*/
private CopyOnWriteArrayList<ApiExecuteFileInfo> funcJars;
}

View File

@ -1,13 +1,10 @@
package io.metersphere.sdk.dto.api.task;
import io.metersphere.sdk.dto.api.result.MsRegexDTO;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
* 任务请求参数数据
@ -17,97 +14,15 @@ public class TaskRequestDTO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@NotBlank
private String reportId;
private String msUrl;
private String kafkaConfig;
private String minioConfig;
private int poolSize;
/**
* 批量执行时的队列ID
*/
private String queueId;
/**
* 是否需要实时接收单个步骤的结果
*/
private Boolean realTime = false;
/**
* 是否保存执行结果
*/
private Boolean saveResult = true;
/**
* 执行的资源ID
*/
@NotBlank
private String resourceId;
/**
* 触发方式
* 手动执行批量执行API执行定时任务
* {@link io.metersphere.sdk.constants.TaskTriggerMode}
*/
private String triggerMode;
/**
* 资源类型
*
* @see io.metersphere.sdk.constants.ApiExecuteResourceType
*/
private String resourceType;
/**
* 点击调试时尚未保存的本地上传的文件列表
*/
private List<ApiExecuteFileInfo> localTempFiles;
/**
* 通过本地上传的文件ID列表
*/
private List<ApiExecuteFileInfo> localFiles;
/**
* 关联文件管理的文件列表
* 这里记录文件名mino存的文件名是id
* 执行时下载文件后按原文件命名
*/
private List<ApiExecuteFileInfo> refFiles;
/**
* 插件文件列表
* id 为插件的 id + 更新时间戳
*/
private List<ApiExecuteFileInfo> pluginFiles;
/**
* 接口测试函数包
*/
private List<ApiExecuteFileInfo> funcJars;
/**
* 误报规则
*/
List<MsRegexDTO> msRegexList;
/**
* 项目id
*/
@NotBlank
private String projectId;
/**
* {@link io.metersphere.sdk.constants.ApiExecuteRunMode}
*/
@NotBlank
private String runMode;
/**
* 运行配置
* 任务的基本信息
*/
@Valid
private ApiRunModeConfigDTO runModeConfig = new ApiRunModeConfigDTO();
private TaskInfo taskInfo = new TaskInfo();
/**
* TODO 要执行的请求总量用于计算执行各种指标
* 任务项
*/
private Long requestCount;
/**
* 记录执行时的环境变量
*/
private List<String> environmentVariables;
// TODO 其它执行参数
@Valid
private TaskItem taskItem;
}

View File

@ -0,0 +1,31 @@
package io.metersphere.sdk.dto.api.task;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
* @Author: jianxing
* @CreateTime: 2024-06-03 16:06
*/
@Data
public class TaskResourceFile implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 点击调试时尚未保存的本地上传的文件列表
*/
private List<ApiExecuteFileInfo> localTempFiles;
/**
* 通过本地上传的文件ID列表
*/
private List<ApiExecuteFileInfo> localFiles;
/**
* 关联文件管理的文件列表
* 这里记录文件名mino存的文件名是id
* 执行时下载文件后按原文件命名
*/
private List<ApiExecuteFileInfo> refFiles;
}

View File

@ -1,21 +1,16 @@
package io.metersphere.api.controller;
import io.metersphere.api.service.ApiExecuteResourceService;
import io.metersphere.api.service.ApiExecuteService;
import io.metersphere.api.service.definition.ApiReportService;
import io.metersphere.api.service.scenario.ApiScenarioReportService;
import io.metersphere.sdk.constants.ExecStatus;
import io.metersphere.sdk.dto.api.task.GetRunScriptRequest;
import io.metersphere.sdk.dto.api.task.GetRunScriptResult;
import io.metersphere.sdk.file.FileRequest;
import io.metersphere.sdk.util.LogUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.*;
import java.util.Optional;
/**
* @Author: jianxing
* @CreateTime: 2023-12-05 17:52
@ -24,15 +19,10 @@ import java.util.Optional;
@RequestMapping("/api/execute/resource")
@Tag(name = "接口测试-执行-资源")
public class ApiExecuteResourceController {
@Resource
private StringRedisTemplate stringRedisTemplate;
@Resource
private ApiExecuteService apiExecuteService;
@Resource
private ApiReportService apiReportService;
@Resource
private ApiScenarioReportService apiScenarioReportService;
private ApiExecuteResourceService apiExecuteResourceService;
/**
* 获取执行脚本
@ -44,13 +34,18 @@ public class ApiExecuteResourceController {
@GetMapping("script")
@Operation(summary = "获取执行脚本")
public String getScript(@RequestParam("reportId") String reportId, @RequestParam("testId") String testId) {
String key = apiExecuteService.getScriptRedisKey(reportId, testId);
LogUtils.info("获取执行脚本: {}", key);
String script = stringRedisTemplate.opsForValue().get(key);
stringRedisTemplate.delete(key);
apiReportService.updateReportStatus(reportId, ExecStatus.RUNNING.name());
apiScenarioReportService.updateReportStatus(reportId, ExecStatus.RUNNING.name());
return Optional.ofNullable(script).orElse(StringUtils.EMPTY);
return apiExecuteResourceService.getRunScript(reportId, testId);
}
/**
* 获取执行脚本
*
* @return
*/
@PostMapping("script")
@Operation(summary = "获取执行脚本")
public GetRunScriptResult getScript(@RequestBody GetRunScriptRequest request) {
return apiExecuteResourceService.getRunScript(request);
}
/**

View File

@ -14,6 +14,7 @@ import io.metersphere.api.service.ApiFileResourceService;
import io.metersphere.api.service.ApiValidateService;
import io.metersphere.api.service.scenario.ApiScenarioLogService;
import io.metersphere.api.service.scenario.ApiScenarioNoticeService;
import io.metersphere.api.service.scenario.ApiScenarioRunService;
import io.metersphere.api.service.scenario.ApiScenarioService;
import io.metersphere.project.service.FileModuleService;
import io.metersphere.sdk.constants.PermissionConstants;
@ -49,6 +50,8 @@ public class ApiScenarioController {
@Resource
private ApiScenarioService apiScenarioService;
@Resource
private ApiScenarioRunService apiScenarioRunService;
@Resource
private ApiValidateService apiValidateService;
@Resource
private FileModuleService fileModuleService;
@ -166,14 +169,14 @@ public class ApiScenarioController {
@Operation(summary = "接口测试-接口场景管理-场景调试")
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_EXECUTE)
public TaskRequestDTO debug(@Validated @RequestBody ApiScenarioDebugRequest request) {
return apiScenarioService.debug(request);
return apiScenarioRunService.debug(request);
}
@PostMapping("/run")
@Operation(summary = "接口测试-接口场景管理-场景执行")
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_EXECUTE)
public TaskRequestDTO run(@Validated @RequestBody ApiScenarioDebugRequest request) {
return apiScenarioService.run(request, SessionUtils.getUserId());
return apiScenarioRunService.run(request, SessionUtils.getUserId());
}
@GetMapping("/run/{id}")
@ -181,7 +184,7 @@ public class ApiScenarioController {
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_EXECUTE)
@CheckOwner(resourceId = "#id", resourceType = "api_scenario")
public TaskRequestDTO run(@PathVariable String id, @RequestParam(required = false) String reportId) {
return apiScenarioService.run(id, reportId, SessionUtils.getUserId());
return apiScenarioRunService.run(id, reportId, SessionUtils.getUserId());
}
@GetMapping(value = "/update-status/{id}/{status}")

View File

@ -8,11 +8,13 @@ import io.metersphere.api.dto.request.MsScenario;
import io.metersphere.api.dto.scenario.ApiScenarioDetail;
import io.metersphere.api.dto.scenario.ApiScenarioParseParam;
import io.metersphere.api.service.ApiExecuteService;
import io.metersphere.api.service.scenario.ApiScenarioService;
import io.metersphere.api.service.scenario.ApiScenarioRunService;
import io.metersphere.sdk.constants.ApiBatchRunMode;
import io.metersphere.sdk.constants.ApiExecuteRunMode;
import io.metersphere.sdk.constants.TaskTriggerMode;
import io.metersphere.sdk.dto.api.task.ApiRunModeConfigDTO;
import io.metersphere.sdk.dto.api.task.TaskInfo;
import io.metersphere.sdk.dto.api.task.TaskItem;
import io.metersphere.sdk.dto.api.task.TaskRequestDTO;
import io.metersphere.sdk.util.CommonBeanFactory;
import io.metersphere.sdk.util.JSON;
@ -27,17 +29,17 @@ import org.quartz.TriggerKey;
public class ApiScenarioScheduleJob extends BaseScheduleJob {
@Override
protected void businessExecute(JobExecutionContext context) {
ApiScenarioService apiScenarioService = CommonBeanFactory.getBean(ApiScenarioService.class);
ApiExecuteService apiExecuteService = CommonBeanFactory.getBean(ApiExecuteService.class);
ApiScenarioRunService apiScenarioRunService = CommonBeanFactory.getBean(ApiScenarioRunService.class);
ApiRunModeConfigDTO apiRunModeConfigDTO = JSON.parseObject(context.getJobDetail().getJobDataMap().get("config").toString(), ApiRunModeConfigDTO.class);
ApiScenarioDetail apiScenarioDetail = apiScenarioService.getForRun(resourceId);
ApiScenarioDetail apiScenarioDetail = apiScenarioRunService.getForRun(resourceId);
if (apiScenarioDetail == null) {
LogUtils.info("当前定时任务的场景已删除 {}", resourceId);
return;
}
MsScenario msScenario = apiScenarioService.getMsScenario(apiScenarioDetail);
ApiScenarioParseParam parseParam = apiScenarioService.getApiScenarioParseParam(apiScenarioDetail);
MsScenario msScenario = apiScenarioRunService.getMsScenario(apiScenarioDetail);
ApiScenarioParseParam parseParam = apiScenarioRunService.getApiScenarioParseParam(apiScenarioDetail);
parseParam.setEnvironmentId(apiRunModeConfigDTO.getEnvironmentId());
parseParam.setGrouped(apiRunModeConfigDTO.getGrouped());
@ -52,33 +54,35 @@ public class ApiScenarioScheduleJob extends BaseScheduleJob {
msScenario.setResourceId(apiScenarioDetail.getId());
// 解析生成场景树并保存临时变量
ApiScenarioParseTmpParam tmpParam = apiScenarioService.parse(msScenario, apiScenarioDetail.getSteps(), parseParam);
ApiScenarioParseTmpParam tmpParam = apiScenarioRunService.parse(msScenario, apiScenarioDetail.getSteps(), parseParam);
ApiResourceRunRequest runRequest = apiScenarioService.getApiResourceRunRequest(msScenario, tmpParam);
ApiResourceRunRequest runRequest = apiScenarioRunService.getApiResourceRunRequest(msScenario, tmpParam);
TaskRequestDTO taskRequest = apiScenarioService.getTaskRequest(IDGenerator.nextStr(), apiScenarioDetail.getId(), apiScenarioDetail.getProjectId(), ApiExecuteRunMode.SCENARIO.name());
taskRequest.getRunModeConfig().setPoolId(apiRunModeConfigDTO.getPoolId());
taskRequest.setSaveResult(true);
taskRequest.setRealTime(false);
taskRequest.getRunModeConfig().setEnvironmentId(parseParam.getEnvironmentId());
taskRequest.setRequestCount(tmpParam.getRequestCount().get());
TaskRequestDTO taskRequest = apiScenarioRunService.getTaskRequest(IDGenerator.nextStr(), apiScenarioDetail.getId(), apiScenarioDetail.getProjectId(), ApiExecuteRunMode.SCENARIO.name());
TaskInfo taskInfo = taskRequest.getTaskInfo();
TaskItem taskItem = taskRequest.getTaskItem();
taskInfo.getRunModeConfig().setPoolId(apiRunModeConfigDTO.getPoolId());
taskInfo.setSaveResult(true);
taskInfo.setRealTime(false);
taskInfo.getRunModeConfig().setEnvironmentId(parseParam.getEnvironmentId());
taskItem.setRequestCount(tmpParam.getRequestCount().get());
ApiScenarioParamConfig parseConfig = apiScenarioService.getApiScenarioParamConfig(parseParam, tmpParam.getScenarioParseEnvInfo());
parseConfig.setReportId(taskRequest.getReportId());
ApiScenarioParamConfig parseConfig = apiScenarioRunService.getApiScenarioParamConfig(msScenario.getProjectId(), parseParam, tmpParam.getScenarioParseEnvInfo());
parseConfig.setReportId(taskItem.getReportId());
// 初始化报告
ApiScenarioReport scenarioReport = apiScenarioService.getScenarioReport(userId);
scenarioReport.setId(taskRequest.getReportId());
ApiScenarioReport scenarioReport = apiScenarioRunService.getScenarioReport(userId);
scenarioReport.setId(taskItem.getReportId());
scenarioReport.setTriggerMode(TaskTriggerMode.SCHEDULE.name());
scenarioReport.setRunMode(ApiBatchRunMode.PARALLEL.name());
scenarioReport.setPoolId(apiRunModeConfigDTO.getPoolId());
scenarioReport.setEnvironmentId(parseParam.getEnvironmentId());
scenarioReport.setWaitingTime(apiScenarioService.getGlobalWaitTime(parseParam.getScenarioConfig()));
scenarioReport.setWaitingTime(apiScenarioRunService.getGlobalWaitTime(parseParam.getScenarioConfig()));
apiScenarioService.initApiReport(apiScenarioDetail, scenarioReport);
apiScenarioRunService.initApiReport(apiScenarioDetail, scenarioReport);
// 初始化报告步骤
apiScenarioService.initScenarioReportSteps(apiScenarioDetail.getSteps(), taskRequest.getReportId());
apiScenarioRunService.initScenarioReportSteps(apiScenarioDetail.getSteps(), taskItem.getReportId());
apiExecuteService.execute(runRequest, taskRequest, parseConfig);
}

View File

@ -0,0 +1,70 @@
package io.metersphere.api.service;
import io.metersphere.api.service.definition.ApiReportService;
import io.metersphere.api.service.definition.ApiTestCaseService;
import io.metersphere.api.service.scenario.ApiScenarioReportService;
import io.metersphere.api.service.scenario.ApiScenarioRunService;
import io.metersphere.sdk.constants.ApiExecuteResourceType;
import io.metersphere.sdk.constants.ExecStatus;
import io.metersphere.sdk.dto.api.task.GetRunScriptRequest;
import io.metersphere.sdk.dto.api.task.GetRunScriptResult;
import io.metersphere.sdk.dto.api.task.TaskItem;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.EnumValidator;
import io.metersphere.sdk.util.LogUtils;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Optional;
@Service
@Transactional(rollbackFor = Exception.class)
public class ApiExecuteResourceService {
@Resource
private ApiExecuteService apiExecuteService;
@Resource
private ApiReportService apiReportService;
@Resource
private ApiScenarioReportService apiScenarioReportService;
@Resource
private ApiScenarioRunService apiScenarioRunService;
@Resource
private ApiTestCaseService apiTestCaseService;
@Resource
private StringRedisTemplate stringRedisTemplate;
public GetRunScriptResult getRunScript(GetRunScriptRequest request) {
TaskItem taskItem = request.getTaskItem();
String reportId = taskItem.getReportId();
String key = apiExecuteService.getTaskKey(reportId, taskItem.getResourceId());
LogUtils.info("生成并获取执行脚本: {}", key);
ApiExecuteResourceType apiExecuteResourceType = EnumValidator.validateEnum(ApiExecuteResourceType.class, request.getResourceType());
switch (apiExecuteResourceType) {
case API_SCENARIO -> {
apiScenarioReportService.updateReportStatus(reportId, ExecStatus.RUNNING.name());
return apiScenarioRunService.getRunScript(request);
}
case API_CASE -> {
apiReportService.updateReportStatus(reportId, ExecStatus.RUNNING.name());
return apiTestCaseService.getRunScript(request);
}
default -> throw new MSException("不支持的资源类型: " + request.getResourceType());
}
}
public String getRunScript(String reportId, String testId) {
String key = apiExecuteService.getTaskKey(reportId, testId);
LogUtils.info("获取执行脚本: {}", key);
String script = stringRedisTemplate.opsForValue().get(key);
stringRedisTemplate.delete(key);
apiReportService.updateReportStatus(reportId, ExecStatus.RUNNING.name());
apiScenarioReportService.updateReportStatus(reportId, ExecStatus.RUNNING.name());
return Optional.ofNullable(script).orElse(StringUtils.EMPTY);
}
}

View File

@ -20,9 +20,7 @@ import io.metersphere.project.dto.environment.GlobalParams;
import io.metersphere.project.dto.environment.GlobalParamsDTO;
import io.metersphere.project.service.*;
import io.metersphere.sdk.constants.*;
import io.metersphere.sdk.dto.api.task.ApiExecuteFileInfo;
import io.metersphere.sdk.dto.api.task.ApiRunModeConfigDTO;
import io.metersphere.sdk.dto.api.task.TaskRequestDTO;
import io.metersphere.sdk.dto.api.task.*;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.exception.TaskRunnerResultCode;
import io.metersphere.sdk.file.FileCenter;
@ -56,6 +54,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@ -121,87 +120,75 @@ public class ApiExecuteService {
}
}
public String getScriptRedisKey(String reportId, String testId) {
public String getTaskKey(String reportId, String testId) {
return reportId + "_" + testId;
}
public TaskRequestDTO execute(ApiResourceRunRequest runRequest, TaskRequestDTO taskRequest, ApiParamConfig parameterConfig) {
// 置minio kafka ms 等信息
setServerInfoParam(taskRequest);
// 设置执行文件参数
setTaskFileParam(runRequest, taskRequest);
// 误报处理 todo 多项目
taskRequest.setMsRegexList(projectApplicationService.get(Collections.singletonList(taskRequest.getProjectId())));
if (!StringUtils.equals(taskRequest.getResourceType(), ApiExecuteResourceType.API_DEBUG.name())) {
// 设置全局参数接口调试不使用全局参数
parameterConfig.setGlobalParams(getGlobalParam(taskRequest.getProjectId()));
}
TaskInfo taskInfo = taskRequest.getTaskInfo();
TaskItem taskItem = taskRequest.getTaskItem();
// 解析执行脚本
String executeScript = parseExecuteScript(runRequest.getTestElement(), parameterConfig);
// 设置插件文件信息 todo 多项目
List<ApiExecuteFileInfo> pluginFiles = new ArrayList<>();
pluginFiles.addAll(apiPluginService.getFileInfoByProjectId(taskRequest.getProjectId()));
pluginFiles.addAll(jdbcDriverPluginService.getFileInfoByProjectId(taskRequest.getProjectId()));
taskRequest.setPluginFiles(pluginFiles);
// 脚本已经解析不需要再解析
taskInfo.setNeedParseScript(false);
// 将测试脚本缓存到 redis
String scriptRedisKey = getScriptRedisKey(taskRequest.getReportId(), taskRequest.getResourceId());
String scriptRedisKey = getTaskKey(taskItem.getReportId(), taskItem.getResourceId());
stringRedisTemplate.opsForValue().set(scriptRedisKey, executeScript, 1, TimeUnit.DAYS);
if (StringUtils.equals(taskRequest.getRunMode(), ApiExecuteRunMode.FRONTEND_DEBUG.name())) {
setTaskItemFileParam(runRequest, taskItem);
if (StringUtils.equals(taskInfo.getRunMode(), ApiExecuteRunMode.FRONTEND_DEBUG.name())) {
taskInfo = setTaskRequestParams(taskInfo);
// 清空mino和kafka配置信息避免前端获取
taskRequest.setMinioConfig(null);
taskRequest.setKafkaConfig(null);
taskInfo.setMinioConfig(null);
taskInfo.setKafkaConfig(null);
// 前端调试返回执行参数由前端调用本地资源池执行
return taskRequest;
}
try {
return doExecute(taskRequest);
} catch (HttpServerErrorException e) {
handleDoExecuteException(scriptRedisKey, e);
int errorCode = e.getResponseBodyAs(ResultHolder.class).getCode();
for (TaskRunnerResultCode taskRunnerResultCode : TaskRunnerResultCode.values()) {
// 匹配资源池的错误代码抛出相应异常
if (taskRunnerResultCode.getCode() == errorCode) {
throw new MSException(taskRunnerResultCode, e.getMessage());
}
}
throw new MSException(RESOURCE_POOL_EXECUTE_ERROR, e.getMessage());
} catch (MSException e) {
handleDoExecuteException(scriptRedisKey, e);
// 集合报告对应的资源池集合移除
removeCollectionReport(taskRequest);
throw e;
return execute(taskRequest);
} catch (Exception e) {
handleDoExecuteException(scriptRedisKey, e);
// 集合报告对应的资源池集合移除
removeCollectionReport(taskRequest);
throw new MSException(RESOURCE_POOL_EXECUTE_ERROR, e.getMessage());
// 调用失败清理脚本
stringRedisTemplate.delete(scriptRedisKey);
throw e;
}
}
private void handleDoExecuteException(String scriptRedisKey, Exception e) {
LogUtils.error(e);
// 调用失败清理脚本
stringRedisTemplate.delete(scriptRedisKey);
private TaskInfo setTaskRequestParams(TaskInfo taskInfo) {
// 置minio kafka ms 等信息
setServerInfoParam(taskInfo);
// 设置项目执行所需要的文件信息
setProjectTaskFileParam(taskInfo);
// 误报处理
taskInfo.setMsRegexList(projectApplicationService.get(Collections.singletonList(taskInfo.getProjectId())));
return taskInfo;
}
/**
* taskRequest 设置当前项目相关的文件信息
*
* @param taskInfo 执行参数
*/
private void setProjectTaskFileParam(TaskInfo taskInfo) {
// 设置jar包信息
setTaskFuncJarParam(taskInfo);
// 设置插件文件信息
setProjectPluginParam(taskInfo);
}
private void removeCollectionReport(TaskRequestDTO taskRequest) {
// 集合报告对应的资源池集合移除
if (taskRequest.getRunModeConfig().getIntegratedReport()) {
String SET_PREFIX = "set:" + taskRequest.getRunModeConfig().getCollectionReport().getReportId();
stringRedisTemplate.opsForSet().remove(SET_PREFIX, taskRequest.getResourceId());
}
private void setProjectPluginParam(TaskInfo taskInfo) {
// 设置插件文件信息
CopyOnWriteArrayList<ApiExecuteFileInfo> pluginFiles = new CopyOnWriteArrayList<>();
pluginFiles.addAll(apiPluginService.getFileInfoByProjectId(taskInfo.getProjectId()));
pluginFiles.addAll(jdbcDriverPluginService.getFileInfoByProjectId(taskInfo.getProjectId()));
taskInfo.getProjectResource().setPluginFiles(pluginFiles);
}
private GlobalParams getGlobalParam(String projectId) {
public GlobalParams getGlobalParam(String projectId) {
GlobalParamsDTO globalParamsDTO = globalParamsService.get(projectId);
if (globalParamsDTO != null) {
return globalParamsDTO.getGlobalParams();
@ -214,37 +201,110 @@ public class ApiExecuteService {
*
* @param taskRequest 执行参数
*/
private TaskRequestDTO doExecute(TaskRequestDTO taskRequest) throws Exception {
// 获取资源池
TestResourcePoolReturnDTO testResourcePoolDTO = getGetResourcePoolNodeDTO(taskRequest.getRunModeConfig(), taskRequest.getProjectId());
if (testResourcePoolDTO == null || CollectionUtils.isEmpty(testResourcePoolDTO.getTestResourceReturnDTO().getNodesList())) {
throw new MSException(ApiResultCode.EXECUTE_RESOURCE_POOL_NOT_CONFIG);
}
TestResourceNodeDTO testResourceNodeDTO = getProjectExecuteNode(testResourcePoolDTO);
public TaskRequestDTO execute(TaskRequestDTO taskRequest) {
TaskInfo taskInfo = taskRequest.getTaskInfo();
TaskItem taskItem = taskRequest.getTaskItem();
if (StringUtils.isNotBlank(testResourcePoolDTO.getServerUrl())) {
// 如果资源池配置了当前站点则使用资源池的
taskRequest.setMsUrl(testResourcePoolDTO.getServerUrl());
}
taskRequest.setPoolSize(testResourceNodeDTO.getConcurrentNumber());
try {
String endpoint = TaskRunnerClient.getEndpoint(testResourceNodeDTO.getIp(), testResourceNodeDTO.getPort());
LogUtils.info("开始发送请求【 {}_{} 】到 {} 节点执行", taskRequest.getReportId(), taskRequest.getResourceId(), endpoint);
if (StringUtils.equalsAny(taskRequest.getRunMode(), ApiExecuteRunMode.FRONTEND_DEBUG.name(), ApiExecuteRunMode.BACKEND_DEBUG.name())) {
TaskRunnerClient.debugApi(endpoint, taskRequest);
} else {
TaskRunnerClient.runApi(endpoint, taskRequest);
taskInfo = setTaskRequestParams(taskInfo);
// 获取资源池
TestResourcePoolReturnDTO testResourcePoolDTO = getGetResourcePoolNodeDTO(taskInfo.getRunModeConfig(), taskInfo.getProjectId());
TestResourceNodeDTO testResourceNodeDTO = getNextExecuteNode(testResourcePoolDTO);
if (StringUtils.isNotBlank(testResourcePoolDTO.getServerUrl())) {
// 如果资源池配置了当前站点则使用资源池的
taskInfo.setMsUrl(testResourcePoolDTO.getServerUrl());
}
taskInfo.setPoolSize(testResourceNodeDTO.getConcurrentNumber());
String endpoint = TaskRunnerClient.getEndpoint(testResourceNodeDTO.getIp(), testResourceNodeDTO.getPort());
LogUtils.info("开始发送请求【 {}_{} 】到 {} 节点执行", taskItem.getReportId(), taskItem.getResourceId(), endpoint);
if (StringUtils.equalsAny(taskInfo.getRunMode(), ApiExecuteRunMode.FRONTEND_DEBUG.name(), ApiExecuteRunMode.BACKEND_DEBUG.name())) {
TaskRunnerClient.debugApi(endpoint, taskRequest);
} else {
TaskRunnerClient.runApi(endpoint, taskRequest);
}
} catch (HttpServerErrorException e) {
LogUtils.error(e);
int errorCode = e.getResponseBodyAs(ResultHolder.class).getCode();
for (TaskRunnerResultCode taskRunnerResultCode : TaskRunnerResultCode.values()) {
// 匹配资源池的错误代码抛出相应异常
if (taskRunnerResultCode.getCode() == errorCode) {
throw new MSException(taskRunnerResultCode, e.getMessage());
}
}
throw new MSException(RESOURCE_POOL_EXECUTE_ERROR, e.getMessage());
} catch (MSException e) {
LogUtils.error(e);
throw e;
} catch (Exception e) {
LogUtils.error(e);
throw new MSException(RESOURCE_POOL_EXECUTE_ERROR, e.getMessage());
}
// 清空mino和kafka配置信息避免前端获取
taskRequest.setMinioConfig(null);
taskRequest.setKafkaConfig(null);
taskInfo.setMinioConfig(null);
taskInfo.setKafkaConfig(null);
return taskRequest;
}
private TestResourceNodeDTO getProjectExecuteNode(TestResourcePoolReturnDTO resourcePoolDTO) {
/**
* 发送执行任务
*
*/
public void batchExecute(TaskBatchRequestDTO taskRequest) {
setTaskRequestParams(taskRequest.getTaskInfo());
TaskInfo taskInfo = taskRequest.getTaskInfo();
// 获取资源池
TestResourcePoolReturnDTO testResourcePool = getGetResourcePoolNodeDTO(taskInfo.getRunModeConfig(), taskInfo.getProjectId());
if (StringUtils.isNotBlank(testResourcePool.getServerUrl())) {
// 如果资源池配置了当前站点则使用资源池的
taskInfo.setMsUrl(testResourcePool.getServerUrl());
}
// 将任务按资源池的数量拆分
List<TestResourceNodeDTO> nodesList = testResourcePool.getTestResourceReturnDTO().getNodesList();
List<TaskBatchRequestDTO> distributeTasks = new ArrayList<>(nodesList.size());
for (int i = 0; i < taskRequest.getTaskItems().size(); i++) {
TaskBatchRequestDTO distributeTask;
int nodeIndex = i % nodesList.size();
if (distributeTasks.size() < nodesList.size()) {
distributeTask = BeanUtils.copyBean(new TaskBatchRequestDTO(), taskRequest);
distributeTask.setTaskItems(new ArrayList<>());
distributeTasks.add(distributeTask);
} else {
distributeTask = distributeTasks.get(nodeIndex);
}
distributeTask.getTaskInfo().setPoolSize(nodesList.get(nodeIndex).getConcurrentNumber());
distributeTask.getTaskItems().add(taskRequest.getTaskItems().get(i));
}
for (int i = 0; i < nodesList.size(); i++) {
// todo 优化某个资源池不可用的情况以及清理 executionSet
TestResourceNodeDTO testResourceNode = nodesList.get(i);
TaskBatchRequestDTO subTaskRequest = distributeTasks.get(i);
String endpoint = TaskRunnerClient.getEndpoint(testResourceNode.getIp(), testResourceNode.getPort());
try {
List<String> taskKeys = subTaskRequest.getTaskItems().stream()
.map(taskItem -> taskItem.getReportId() + "_" + taskItem.getResourceId())
.toList();
LogUtils.info("开始发送批量任务到 {} 节点执行:\n" + taskKeys, endpoint);
TaskRunnerClient.batchRunApi(endpoint, subTaskRequest);
} catch (Exception e) {
LogUtils.error("发送批量任务到 {} 节点执行失败", endpoint);
LogUtils.error(e);
}
}
}
private TestResourceNodeDTO getNextExecuteNode(TestResourcePoolReturnDTO resourcePoolDTO) {
roundRobinService.initializeNodes(resourcePoolDTO.getId(), resourcePoolDTO.getTestResourceReturnDTO().getNodesList());
try {
TestResourceNodeDTO node = roundRobinService.getNextNode(resourcePoolDTO.getId());
@ -263,18 +323,23 @@ public class ApiExecuteService {
if (StringUtils.isBlank(poolId)) {
poolId = getProjectApiResourcePoolId(projectId);
}
return getAvailableResourcePoolDTO(projectId, poolId);
TestResourcePoolReturnDTO resourcePool = getAvailableResourcePoolDTO(projectId, poolId);
if (resourcePool == null || CollectionUtils.isEmpty(resourcePool.getTestResourceReturnDTO().getNodesList())) {
throw new MSException(ApiResultCode.EXECUTE_RESOURCE_POOL_NOT_CONFIG);
}
return resourcePool;
}
/**
* 设置minio kafka ms 等信息
*
* @param taskRequest 执行参数
* @param taskInfo 执行参数
*/
private void setServerInfoParam(TaskRequestDTO taskRequest) {
taskRequest.setKafkaConfig(EncryptUtils.aesEncrypt(JSON.toJSONString(KafkaConfig.getKafkaConfig())));
taskRequest.setMinioConfig(EncryptUtils.aesEncrypt(JSON.toJSONString(getMinio())));
taskRequest.setMsUrl(systemParameterService.getBaseInfo().getUrl());
private void setServerInfoParam(TaskInfo taskInfo) {
taskInfo.setKafkaConfig(EncryptUtils.aesEncrypt(JSON.toJSONString(KafkaConfig.getKafkaConfig())));
taskInfo.setMinioConfig(EncryptUtils.aesEncrypt(JSON.toJSONString(getMinio())));
taskInfo.setMsUrl(systemParameterService.getBaseInfo().getUrl());
}
/**
@ -305,11 +370,12 @@ public class ApiExecuteService {
// 设置执行参数
TaskRequestDTO taskRequest = getTaskRequest(reportId, testId, runRequest.getProjectId());
setServerInfoParam(taskRequest);
taskRequest.setRealTime(true);
taskRequest.setSaveResult(false);
taskRequest.setResourceType(ApiExecuteResourceType.API_DEBUG.name());
taskRequest.setRunMode(ApiExecuteRunMode.BACKEND_DEBUG.name());
TaskInfo taskInfo = taskRequest.getTaskInfo();
setServerInfoParam(taskInfo);
taskInfo.setRealTime(true);
taskInfo.setSaveResult(false);
taskInfo.setResourceType(ApiExecuteResourceType.API_DEBUG.name());
taskInfo.setRunMode(ApiExecuteRunMode.BACKEND_DEBUG.name());
return execute(apiRunRequest, taskRequest, new ApiParamConfig());
}
@ -318,57 +384,77 @@ public class ApiExecuteService {
* taskRequest 设置文件相关参数
*
* @param runRequest 请求参数
* @param taskRequest 执行参数
*/
private void setTaskFileParam(ApiResourceRunRequest runRequest, TaskRequestDTO taskRequest) {
setTaskRefFileParam(runRequest, taskRequest);
setTaskTmpFileParam(runRequest, taskRequest);
setTaskFuncJarParam(runRequest, taskRequest);
public void setTaskItemFileParam(ApiResourceRunRequest runRequest, TaskItem taskItem) {
// 接口执行相关的文件
setTaskRefFileParam(runRequest, taskItem.getTaskResourceFile(), taskItem.getResourceId());
// 调试时未保存的文件
setTaskTmpFileParam(runRequest, taskItem.getTaskResourceFile());
// 场景引用跨项目的用例所需要的jar包
setTaskFuncJarParam(taskItem, runRequest.getRefProjectIds().stream().toList());
}
/**
* taskRequest 设置文件相关参数
*/
public void setTaskItemFileParam(TaskItem taskItem) {
setTaskItemFileParam(new ApiResourceRunRequest(), taskItem);
}
/**
* 处理脚本执行所需要的jar包
*
* @param runRequest
* @param taskRequest
* @param taskInfo
*/
private void setTaskFuncJarParam(ApiResourceRunRequest runRequest, TaskRequestDTO taskRequest) {
Set<String> projectIdsSet = runRequest.getRefProjectIds();
projectIdsSet.add(taskRequest.getProjectId());
List<String> projectIds = projectIdsSet.stream().collect(Collectors.toList());
private void setTaskFuncJarParam(TaskInfo taskInfo) {
// 获取函数jar包
List<FileMetadata> fileMetadataList = fileManagementService.findJarByProjectId(projectIds);
taskRequest.setFuncJars(getApiExecuteFileInfo(fileMetadataList));
List<FileMetadata> fileMetadataList = fileManagementService.findJarByProjectId(List.of(taskInfo.getProjectId()));
taskInfo.getProjectResource().setFuncJars(getApiExecuteFileInfo(fileMetadataList));
// TODO 当前项目没有包分两种情况1 之前存在被删除2 一直不存在
// 为了兼容1 这种情况需要初始化一条空的数据由执行机去做卸载
if (CollectionUtils.isEmpty(taskRequest.getFuncJars())) {
if (CollectionUtils.isEmpty(taskInfo.getProjectResource().getFuncJars())) {
ApiExecuteFileInfo tempFileInfo = new ApiExecuteFileInfo();
tempFileInfo.setProjectId(taskRequest.getProjectId());
taskRequest.setFuncJars(List.of(tempFileInfo));
tempFileInfo.setProjectId(taskInfo.getProjectId());
CopyOnWriteArrayList copyOnWriteArrayList = new CopyOnWriteArrayList();
copyOnWriteArrayList.add(tempFileInfo);
taskInfo.getProjectResource().setFuncJars(copyOnWriteArrayList);
}
}
/**
* 处理脚本执行所需要的jar包
*
* @param taskItem
*/
private void setTaskFuncJarParam(TaskItem taskItem, List<String> projectIds) {
if (CollectionUtils.isEmpty(projectIds)) {
return;
}
TaskProjectResource projectResource = taskItem.getRefProjectResource();
// 获取函数jar包
List<FileMetadata> fileMetadataList = fileManagementService.findJarByProjectId(projectIds);
projectResource.setFuncJars(getApiExecuteFileInfo(fileMetadataList));
}
/**
* 处理没有保存的临时文件
*
* @param runRequest
* @param taskRequest
*/
private void setTaskTmpFileParam(ApiResourceRunRequest runRequest, TaskRequestDTO taskRequest) {
private void setTaskTmpFileParam(ApiResourceRunRequest runRequest, TaskResourceFile taskResourceFile) {
// 没有保存的本地临时文件
List<String> uploadFileIds = runRequest.getUploadFileIds();
if (CollectionUtils.isNotEmpty(uploadFileIds)) {
List<ApiExecuteFileInfo> localTempFiles = uploadFileIds.stream()
.map(tempFileId -> {
String fileName = apiFileResourceService.getTempFileNameByFileId(tempFileId);
return getApiExecuteFileInfo(tempFileId, fileName, taskRequest.getProjectId());
return getApiExecuteFileInfo(tempFileId, fileName, null);
})
// uploadFileIds 查不到则已经移动到正式目录
.filter(i -> StringUtils.isNotBlank(i.getFileName()))
.collect(Collectors.toList());
taskRequest.setLocalTempFiles(localTempFiles);
taskResourceFile.setLocalTempFiles(localTempFiles);
}
List<String> linkFileIds = runRequest.getLinkFileIds();
@ -376,7 +462,7 @@ public class ApiExecuteService {
if (CollectionUtils.isNotEmpty(linkFileIds)) {
List<FileMetadata> fileMetadataList = fileMetadataService.getByFileIds(linkFileIds);
// 添加临时的文件管理的文件
taskRequest.getRefFiles().addAll(getApiExecuteFileInfo(fileMetadataList));
taskResourceFile.getRefFiles().addAll(getApiExecuteFileInfo(fileMetadataList));
}
}
@ -384,12 +470,11 @@ public class ApiExecuteService {
* 处理运行的资源所关联的文件信息
*
* @param runRequest
* @param taskRequest
*/
private void setTaskRefFileParam(ApiResourceRunRequest runRequest, TaskRequestDTO taskRequest) {
private void setTaskRefFileParam(ApiResourceRunRequest runRequest, TaskResourceFile taskResourceFile, String resourceId) {
// 查询包括资源所需的文件
Set<String> resourceIdsSet = runRequest.getFileResourceIds();
resourceIdsSet.add(taskRequest.getResourceId());
resourceIdsSet.add(resourceId);
List<String> resourceIds = resourceIdsSet.stream().collect(Collectors.toList());
SubListUtils.dealForSubList(resourceIds, 50, subResourceIds -> {
// 查询通过本地上传的文件
@ -406,7 +491,7 @@ public class ApiExecuteService {
return apiExecuteFileInfo;
})
.collect(Collectors.toList());
taskRequest.setLocalFiles(localFiles);
taskResourceFile.setLocalFiles(localFiles);
// 查询关联的文件管理的文件
List<ApiExecuteFileInfo> refFiles = fileAssociationService.getFiles(subResourceIds).
@ -421,11 +506,11 @@ public class ApiExecuteService {
}
return refFileInfo;
}).collect(Collectors.toList());
taskRequest.setRefFiles(refFiles);
taskResourceFile.setRefFiles(refFiles);
});
}
private List<ApiExecuteFileInfo> getApiExecuteFileInfo(List<FileMetadata> fileMetadataList) {
private CopyOnWriteArrayList<ApiExecuteFileInfo> getApiExecuteFileInfo(List<FileMetadata> fileMetadataList) {
return fileMetadataList.stream()
.map(file -> {
ApiExecuteFileInfo tempFileInfo = getApiExecuteFileInfo(file.getId(), file.getOriginalName(),
@ -436,7 +521,7 @@ public class ApiExecuteService {
tempFileInfo.setFileModuleRepositoryDTO(fileManagementService.getFileModuleRepositoryDTO(file.getModuleId()));
}
return tempFileInfo;
}).toList();
}).collect(Collectors.toCollection(CopyOnWriteArrayList::new));
}
private ApiExecuteFileInfo getApiExecuteFileInfo(String fileId, String fileName, String projectId) {
@ -459,7 +544,7 @@ public class ApiExecuteService {
* @param config 参数配置
* @return 执行脚本
*/
private String parseExecuteScript(AbstractMsTestElement msTestElement, ParameterConfig config) {
public String parseExecuteScript(AbstractMsTestElement msTestElement, ParameterConfig config) {
// 解析生成脚本
TestElementParser defaultParser = TestElementParserFactory.getDefaultParser();
return defaultParser.parse(msTestElement, config);
@ -510,7 +595,7 @@ public class ApiExecuteService {
}
public void downloadFile(String reportId, String testId, FileRequest fileRequest, HttpServletResponse response) throws Exception {
String key = getScriptRedisKey(reportId, testId);
String key = getTaskKey(reportId, testId);
if (BooleanUtils.isTrue(stringRedisTemplate.hasKey(key))) {
FileRepository repository = StringUtils.isBlank(fileRequest.getStorage()) ? FileCenter.getDefaultRepository()
: FileCenter.getRepository(fileRequest.getStorage());
@ -546,14 +631,17 @@ public class ApiExecuteService {
*/
public TaskRequestDTO apiExecute(ApiResourceRunRequest runRequest, TaskRequestDTO taskRequest, ApiParamConfig apiParamConfig) {
// 设置使用脚本前后置的公共脚本信息
AbstractMsTestElement testElement = runRequest.getTestElement();
apiCommonService.setEnableCommonScriptProcessorInfo(testElement);
testElement.setResourceId(taskRequest.getResourceId());
testElement.setStepId(taskRequest.getResourceId());
testElement.setProjectId(taskRequest.getProjectId());
setTestElementParam(runRequest.getTestElement(), taskRequest.getTaskInfo().getProjectId(), taskRequest.getTaskItem());
return execute(runRequest, taskRequest, apiParamConfig);
}
public void setTestElementParam(AbstractMsTestElement testElement, String projectId, TaskItem taskItem) {
apiCommonService.setEnableCommonScriptProcessorInfo(testElement);
testElement.setResourceId(taskItem.getResourceId());
testElement.setStepId(taskItem.getResourceId());
testElement.setProjectId(projectId);
}
public ApiParamConfig getApiParamConfig(String reportId) {
ApiParamConfig paramConfig = new ApiParamConfig();
paramConfig.setTestElementClassPluginIdMap(apiPluginService.getTestElementPluginMap());
@ -562,14 +650,37 @@ public class ApiExecuteService {
return paramConfig;
}
public ApiParamConfig getApiParamConfig(String reportId, String projectId) {
ApiParamConfig paramConfig = new ApiParamConfig();
paramConfig.setTestElementClassPluginIdMap(apiPluginService.getTestElementPluginMap());
paramConfig.setTestElementClassProtocolMap(apiPluginService.getTestElementProtocolMap());
paramConfig.setReportId(reportId);
paramConfig.setGlobalParams(getGlobalParam(projectId));
return paramConfig;
}
public TaskRequestDTO getTaskRequest(String reportId, String resourceId, String projectId) {
TaskRequestDTO taskRequest = new TaskRequestDTO();
taskRequest.setReportId(reportId);
taskRequest.setResourceId(resourceId);
taskRequest.setProjectId(projectId);
TaskInfo taskInfo = getTaskInfo(projectId);
TaskItem taskItem = getTaskItem(reportId, resourceId);
taskRequest.setTaskInfo(taskInfo);
taskRequest.setTaskItem(taskItem);
return taskRequest;
}
public TaskInfo getTaskInfo(String projectId) {
TaskInfo taskInfo = new TaskInfo();
taskInfo.setProjectId(projectId);
return taskInfo;
}
public TaskItem getTaskItem(String reportId, String resourceId) {
TaskItem taskItem = new TaskItem();
taskItem.setReportId(reportId);
taskItem.setResourceId(resourceId);
return taskItem;
}
public String getDebugRunModule(boolean isFrontendDebug) {
return isFrontendDebug ? ApiExecuteRunMode.FRONTEND_DEBUG.name() : ApiExecuteRunMode.BACKEND_DEBUG.name();
}

View File

@ -11,6 +11,8 @@ import io.metersphere.project.mapper.ProjectMapper;
import io.metersphere.sdk.constants.*;
import io.metersphere.sdk.dto.api.result.ProcessResultDTO;
import io.metersphere.sdk.dto.api.result.TaskResultDTO;
import io.metersphere.sdk.dto.api.task.TaskInfo;
import io.metersphere.sdk.dto.api.task.TaskItem;
import io.metersphere.sdk.dto.api.task.TaskRequestDTO;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.*;
@ -293,11 +295,14 @@ public class ApiTaskCenterService {
LogUtils.error(e);
} finally {
subList.forEach(reportId -> {
taskRequestDTO.setReportId(reportId);
taskRequestDTO.setResourceType(request.getModuleType());
taskRequestDTO.getRunModeConfig().setIntegratedReport(integrationMap.get(reportId));
TaskInfo taskInfo = taskRequestDTO.getTaskInfo();
TaskItem taskItem = new TaskItem();
taskRequestDTO.setTaskItem(taskItem);
taskItem.setReportId(reportId);
taskInfo.setResourceType(request.getModuleType());
taskInfo.getRunModeConfig().setIntegratedReport(integrationMap.get(reportId));
if (BooleanUtils.isTrue(integrationMap.get(reportId))) {
taskRequestDTO.getRunModeConfig().getCollectionReport().setReportId(reportId);
taskInfo.getRunModeConfig().getCollectionReport().setReportId(reportId);
}
result.setRequest(taskRequestDTO);
kafkaTemplate.send(KafkaTopicConstants.API_REPORT_TOPIC, JSON.toJSONString(result));

View File

@ -25,6 +25,7 @@ import io.metersphere.project.service.MoveNodeService;
import io.metersphere.project.service.ProjectService;
import io.metersphere.sdk.constants.ApiFileResourceType;
import io.metersphere.sdk.constants.DefaultRepositoryDir;
import io.metersphere.sdk.dto.api.task.TaskInfo;
import io.metersphere.sdk.dto.api.task.TaskRequestDTO;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.BeanUtils;
@ -213,10 +214,11 @@ public class ApiDebugService extends MoveNodeService {
ApiParamConfig apiParamConfig = apiExecuteService.getApiParamConfig(request.getReportId());
TaskRequestDTO taskRequest = apiExecuteService.getTaskRequest(request.getReportId(), request.getId(), request.getProjectId());
taskRequest.setSaveResult(false);
taskRequest.setRealTime(true);
taskRequest.setResourceType(ApiResourceType.API_DEBUG.name());
taskRequest.setRunMode(apiExecuteService.getDebugRunModule(request.getFrontendDebug()));
TaskInfo taskInfo = taskRequest.getTaskInfo();
taskInfo.setSaveResult(false);
taskInfo.setRealTime(true);
taskInfo.setResourceType(ApiResourceType.API_DEBUG.name());
taskInfo.setRunMode(apiExecuteService.getDebugRunModule(request.getFrontendDebug()));
return apiExecuteService.apiExecute(runRequest, taskRequest, apiParamConfig);
}

View File

@ -30,6 +30,7 @@ import io.metersphere.sdk.constants.ApplicationNumScope;
import io.metersphere.sdk.constants.DefaultRepositoryDir;
import io.metersphere.sdk.constants.ModuleConstants;
import io.metersphere.sdk.domain.OperationLogBlob;
import io.metersphere.sdk.dto.api.task.TaskInfo;
import io.metersphere.sdk.dto.api.task.TaskRequestDTO;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.mapper.OperationLogBlobMapper;
@ -1145,13 +1146,14 @@ public class ApiDefinitionService extends MoveNodeService {
public TaskRequestDTO debug(ApiDefinitionRunRequest request) {
ApiResourceRunRequest runRequest = apiExecuteService.getApiResourceRunRequest(request);
EnvironmentInfoDTO environmentInfoDTO = environmentService.get(request.getEnvironmentId());
ApiParamConfig apiParamConfig = apiExecuteService.getApiParamConfig(request.getReportId());
ApiParamConfig apiParamConfig = apiExecuteService.getApiParamConfig(request.getReportId(), request.getProjectId());
TaskRequestDTO taskRequest = apiExecuteService.getTaskRequest(request.getReportId(), request.getId(), request.getProjectId());
taskRequest.setSaveResult(false);
taskRequest.setRealTime(true);
taskRequest.setResourceType(ApiResourceType.API.name());
taskRequest.setRunMode(apiExecuteService.getDebugRunModule(request.getFrontendDebug()));
TaskInfo taskInfo = taskRequest.getTaskInfo();
taskInfo.setSaveResult(false);
taskInfo.setRealTime(true);
taskInfo.setResourceType(ApiResourceType.API.name());
taskInfo.setRunMode(apiExecuteService.getDebugRunModule(request.getFrontendDebug()));
AbstractMsTestElement msTestElement = runRequest.getTestElement();

View File

@ -1,23 +1,14 @@
package io.metersphere.api.service.definition;
import io.metersphere.api.domain.*;
import io.metersphere.api.dto.ApiDefinitionExecuteInfo;
import io.metersphere.api.dto.ApiParamConfig;
import io.metersphere.api.dto.debug.ApiResourceRunRequest;
import io.metersphere.api.dto.definition.ApiTestCaseBatchRunRequest;
import io.metersphere.api.mapper.*;
import io.metersphere.api.service.ApiBatchRunBaseService;
import io.metersphere.api.service.ApiCommonService;
import io.metersphere.api.service.ApiExecuteService;
import io.metersphere.api.service.queue.ApiExecutionQueueService;
import io.metersphere.api.service.queue.ApiExecutionSetService;
import io.metersphere.api.utils.ApiDataUtils;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.project.service.EnvironmentService;
import io.metersphere.sdk.constants.*;
import io.metersphere.sdk.dto.api.task.ApiRunModeConfigDTO;
import io.metersphere.sdk.dto.api.task.CollectionReportDTO;
import io.metersphere.sdk.dto.api.task.TaskRequestDTO;
import io.metersphere.sdk.dto.api.task.*;
import io.metersphere.sdk.dto.queue.ExecutionQueue;
import io.metersphere.sdk.dto.queue.ExecutionQueueDetail;
import io.metersphere.sdk.util.BeanUtils;
@ -34,7 +25,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
import java.util.stream.Collectors;
@ -47,14 +37,10 @@ public class ApiTestCaseBatchRunService {
@Resource
private ExtApiTestCaseMapper extApiTestCaseMapper;
@Resource
private ApiTestCaseBlobMapper apiTestCaseBlobMapper;
@Resource
private ApiTestCaseService apiTestCaseService;
@Resource
private ApiExecuteService apiExecuteService;
@Resource
private EnvironmentService environmentService;
@Resource
private ApiExecutionQueueService apiExecutionQueueService;
@Resource
private ApiExecutionSetService apiExecutionSetService;
@ -63,10 +49,6 @@ public class ApiTestCaseBatchRunService {
@Resource
private ApiBatchRunBaseService apiBatchRunBaseService;
@Resource
private ApiCommonService apiCommonService;
@Resource
private ApiDefinitionMapper apiDefinitionMapper;
@Resource
private ApiReportMapper apiReportMapper;
@Resource
private ApiReportDetailMapper apiReportDetailMapper;
@ -168,49 +150,32 @@ public class ApiTestCaseBatchRunService {
// 集成报告执行前先设置成 RUNNING
setRunningIntegrateReport(runModeConfig);
List<TaskItem> taskItems = new ArrayList<>(ids.size());
// 分批查询
SubListUtils.dealForSubList(ids, 100, subIds -> {
// 这里ID顺序和队列的ID顺序保持一致
for (String id : ids) {
ApiTestCase apiTestCase = apiCaseMap.get(id);
AtomicInteger errorCount = new AtomicInteger();
Map<String, ApiTestCaseBlob> apiTestCaseBlobMap = apiTestCaseService.getBlobByIds(subIds).stream()
.collect(Collectors.toMap(ApiTestCaseBlob::getId, Function.identity()));
// 获取用例和定义信息的mapkey 为用例IDvalue 为接口定义信息
Map<String, ApiDefinitionExecuteInfo> definitionExecuteInfoMap = apiTestCaseService.getModuleInfoByIds(subIds).stream()
.collect(Collectors.toMap(ApiDefinitionExecuteInfo::getResourceId, Function.identity()));
// 这里ID顺序和队列的ID顺序保持一致
for (String id : subIds) {
String reportId = null;
try {
ApiTestCase apiTestCase = apiCaseMap.get(id);
ApiTestCaseBlob apiTestCaseBlob = apiTestCaseBlobMap.get(id);
if (apiTestCase == null) {
if (runModeConfig.isIntegratedReport()) {
// 用例不存在则在执行集合中删除
apiExecutionSetService.removeItem(runModeConfig.getCollectionReport().getReportId(), id);
}
LogUtils.info("当前执行任务的用例已删除 {}", id);
break;
}
// 如果是集成报告则生成唯一的虚拟ID非集成报告使用单用例的报告ID
reportId = runModeConfig.isIntegratedReport() ? UUID.randomUUID().toString() : caseReportMap.get(id);
TaskRequestDTO taskRequest = getTaskRequestDTO(reportId, apiTestCase, runModeConfig);
taskRequest.setRequestCount(1L);
execute(taskRequest, apiTestCase, apiTestCaseBlob, definitionExecuteInfoMap.get(apiTestCase.getId()));
} catch (Exception e) {
LogUtils.error("执行用例失败 {}-{}", reportId, id);
LogUtils.error(e);
if (errorCount.getAndIncrement() > 10) {
LogUtils.error("批量执行用例失败错误次数超过10次停止执行");
return;
}
if (apiTestCase == null) {
if (runModeConfig.isIntegratedReport()) {
// 用例不存在则在执行集合中删除
apiExecutionSetService.removeItem(runModeConfig.getCollectionReport().getReportId(), id);
}
LogUtils.info("当前执行任务的用例已删除 {}", id);
break;
}
});
// 如果是集成报告则生成唯一的虚拟ID非集成报告使用单用例的报告ID
String reportId = runModeConfig.isIntegratedReport() ? UUID.randomUUID().toString() : caseReportMap.get(id);
TaskItem taskItem = apiExecuteService.getTaskItem(reportId, id);
taskItem.setRequestCount(1L);
taskItems.add(taskItem);
}
TaskBatchRequestDTO taskRequest = getTaskBatchRequestDTO(request.getProjectId(), runModeConfig);
taskRequest.setTaskItems(taskItems);
apiExecuteService.batchExecute(taskRequest);
}
/**
@ -309,7 +274,6 @@ public class ApiTestCaseBatchRunService {
String resourceId = queueDetail.getResourceId();
ApiTestCase apiTestCase = apiTestCaseMapper.selectByPrimaryKey(resourceId);
ApiTestCaseBlob apiTestCaseBlob = apiTestCaseBlobMapper.selectByPrimaryKey(resourceId);
String reportId;
if (runModeConfig.isIntegratedReport()) {
@ -323,41 +287,39 @@ public class ApiTestCaseBatchRunService {
LogUtils.info("当前执行任务的用例已删除 {}", resourceId);
return;
}
ApiDefinition apiDefinition = apiDefinitionMapper.selectByPrimaryKey(apiTestCase.getApiDefinitionId());
TaskRequestDTO taskRequest = getTaskRequestDTO(reportId, apiTestCase, runModeConfig);
taskRequest.setQueueId(queue.getQueueId());
taskRequest.setRequestCount(1L);
execute(taskRequest, apiTestCase, apiTestCaseBlob, BeanUtils.copyBean(new ApiDefinitionExecuteInfo(), apiDefinition));
}
taskRequest.getTaskInfo().setQueueId(queue.getQueueId());
taskRequest.getTaskItem().setRequestCount(1L);
/**
* 执行批量的单个任务
*
* @param apiTestCase
* @param apiTestCaseBlob
*/
public void execute(TaskRequestDTO taskRequest, ApiTestCase apiTestCase, ApiTestCaseBlob apiTestCaseBlob, ApiDefinitionExecuteInfo definitionExecuteInfo) {
ApiParamConfig apiParamConfig = apiExecuteService.getApiParamConfig(taskRequest.getReportId());
ApiResourceRunRequest runRequest = new ApiResourceRunRequest();
runRequest.setTestElement(ApiDataUtils.parseObject(new String(apiTestCaseBlob.getRequest()), AbstractMsTestElement.class));
// 设置环境信息
apiParamConfig.setEnvConfig(environmentService.get(getEnvId(taskRequest.getRunModeConfig(), apiTestCase)));
// 设置 method 等信息
apiCommonService.setApiDefinitionExecuteInfo(runRequest.getTestElement(), definitionExecuteInfo);
apiExecuteService.apiExecute(runRequest, taskRequest, apiParamConfig);
apiExecuteService.execute(taskRequest);
}
private TaskRequestDTO getTaskRequestDTO(String reportId, ApiTestCase apiTestCase, ApiRunModeConfigDTO runModeConfig) {
TaskRequestDTO taskRequest = apiTestCaseService.getTaskRequest(reportId, apiTestCase.getId(), apiTestCase.getProjectId(), ApiExecuteRunMode.RUN.name());
taskRequest.setSaveResult(true);
taskRequest.setRealTime(false);
taskRequest.setRunModeConfig(runModeConfig);
TaskRequestDTO taskRequest = new TaskRequestDTO();
TaskItem taskItem = apiExecuteService.getTaskItem(reportId, apiTestCase.getId());
TaskInfo taskInfo = getTaskInfo(apiTestCase.getProjectId(), runModeConfig);
taskRequest.setTaskInfo(taskInfo);
taskRequest.setTaskItem(taskItem);
return taskRequest;
}
private TaskBatchRequestDTO getTaskBatchRequestDTO(String projectId, ApiRunModeConfigDTO runModeConfig) {
TaskBatchRequestDTO taskRequest = new TaskBatchRequestDTO();
TaskInfo taskInfo = getTaskInfo(projectId, runModeConfig);
taskRequest.setTaskInfo(taskInfo);
return taskRequest;
}
private TaskInfo getTaskInfo(String projectId, ApiRunModeConfigDTO runModeConfig) {
TaskInfo taskInfo = apiTestCaseService.getTaskInfo(projectId, ApiExecuteRunMode.RUN.name());
taskInfo.setSaveResult(true);
taskInfo.setRealTime(false);
taskInfo.setNeedParseScript(true);
taskInfo.setRunModeConfig(runModeConfig);
return taskInfo;
}
/**
* 预生成用例的执行报告
@ -387,7 +349,7 @@ public class ApiTestCaseBatchRunService {
private ApiReport getApiReport(ApiRunModeConfigDTO runModeConfig, ApiTestCase apiTestCase, String userId) {
ApiReport apiReport = getApiReport(runModeConfig, userId);
apiReport.setEnvironmentId(getEnvId(runModeConfig, apiTestCase));
apiReport.setEnvironmentId(apiTestCaseService.getEnvId(runModeConfig, apiTestCase));
apiReport.setName(apiTestCase.getName() + "_" + DateUtils.getTimeString(System.currentTimeMillis()));
apiReport.setProjectId(apiTestCase.getProjectId());
apiReport.setTriggerMode(TaskTriggerMode.BATCH.name());
@ -403,18 +365,6 @@ public class ApiTestCaseBatchRunService {
return apiReport;
}
/**
* 获取执行的环境ID
* 优先使用运行配置的环境
* 没有则使用用例自身的环境
*
* @param runModeConfig
* @param apiTestCase
* @return
*/
public String getEnvId(ApiRunModeConfigDTO runModeConfig, ApiTestCase apiTestCase) {
return StringUtils.isBlank(runModeConfig.getEnvironmentId()) ? apiTestCase.getEnvironmentId() : runModeConfig.getEnvironmentId();
}
public void updateStopOnFailureApiReport(ExecutionQueue queue) {
ApiRunModeConfigDTO runModeConfig = queue.getRunModeConfig();

View File

@ -25,7 +25,7 @@ import io.metersphere.project.service.MoveNodeService;
import io.metersphere.sdk.constants.*;
import io.metersphere.sdk.domain.Environment;
import io.metersphere.sdk.domain.EnvironmentExample;
import io.metersphere.sdk.dto.api.task.TaskRequestDTO;
import io.metersphere.sdk.dto.api.task.*;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.mapper.EnvironmentMapper;
import io.metersphere.sdk.util.*;
@ -693,16 +693,18 @@ public class ApiTestCaseService extends MoveNodeService {
String poolId = apiExecuteService.getProjectApiResourcePoolId(apiTestCase.getProjectId());
TaskRequestDTO taskRequest = getTaskRequest(reportId, apiTestCase.getId(), apiTestCase.getProjectId(), ApiExecuteRunMode.RUN.name());
taskRequest.getRunModeConfig().setPoolId(poolId);
taskRequest.setSaveResult(true);
TaskItem taskItem = taskRequest.getTaskItem();
TaskInfo taskInfo = taskRequest.getTaskInfo();
taskInfo.getRunModeConfig().setPoolId(poolId);
taskInfo.setSaveResult(true);
if (StringUtils.isEmpty(taskRequest.getReportId())) {
taskRequest.setRealTime(false);
if (StringUtils.isEmpty(taskItem.getReportId())) {
taskInfo.setRealTime(false);
reportId = IDGenerator.nextStr();
taskRequest.setReportId(reportId);
taskItem.setReportId(reportId);
} else {
// 如果传了报告ID则实时获取结果
taskRequest.setRealTime(true);
taskInfo.setRealTime(true);
}
// 初始化报告
@ -721,8 +723,8 @@ public class ApiTestCaseService extends MoveNodeService {
public TaskRequestDTO debug(ApiCaseRunRequest request) {
TaskRequestDTO taskRequest = getTaskRequest(request.getReportId(), request.getId(),
request.getProjectId(), apiExecuteService.getDebugRunModule(request.getFrontendDebug()));
taskRequest.setSaveResult(false);
taskRequest.setRealTime(true);
taskRequest.getTaskInfo().setSaveResult(false);
taskRequest.getTaskInfo().setRealTime(true);
ApiResourceRunRequest runRequest = apiExecuteService.getApiResourceRunRequest(request);
@ -731,7 +733,7 @@ public class ApiTestCaseService extends MoveNodeService {
private TaskRequestDTO doExecute(TaskRequestDTO taskRequest, ApiResourceRunRequest runRequest, String apiDefinitionId, String envId) {
ApiParamConfig apiParamConfig = apiExecuteService.getApiParamConfig(taskRequest.getReportId());
ApiParamConfig apiParamConfig = apiExecuteService.getApiParamConfig(taskRequest.getTaskItem().getReportId(), taskRequest.getTaskInfo().getProjectId());
ApiDefinition apiDefinition = apiDefinitionMapper.selectByPrimaryKey(apiDefinitionId);
@ -743,6 +745,49 @@ public class ApiTestCaseService extends MoveNodeService {
return apiExecuteService.apiExecute(runRequest, taskRequest, apiParamConfig);
}
/**
* 获取执行脚本
*/
public GetRunScriptResult getRunScript(GetRunScriptRequest request) {
TaskItem taskItem = request.getTaskItem();
ApiTestCase apiTestCase = apiTestCaseMapper.selectByPrimaryKey(taskItem.getResourceId());
ApiDefinition apiDefinition = apiDefinitionMapper.selectByPrimaryKey(apiTestCase.getApiDefinitionId());
ApiTestCaseBlob apiTestCaseBlob = apiTestCaseBlobMapper.selectByPrimaryKey(taskItem.getResourceId());
ApiParamConfig apiParamConfig = apiExecuteService.getApiParamConfig(taskItem.getReportId(), apiTestCase.getProjectId());
AbstractMsTestElement msTestElement = ApiDataUtils.parseObject(new String(apiTestCaseBlob.getRequest()), AbstractMsTestElement.class);
// 设置 method 等信息
apiCommonService.setApiDefinitionExecuteInfo(msTestElement, BeanUtils.copyBean(new ApiDefinitionExecuteInfo(), apiDefinition));
apiExecuteService.setTestElementParam(msTestElement, apiTestCase.getProjectId(), request.getTaskItem());
// 设置环境信息
apiParamConfig.setEnvConfig(environmentService.get(getEnvId(request.getRunModeConfig(), apiTestCase)));
GetRunScriptResult runScriptResult = new GetRunScriptResult();
// 记录请求数量
runScriptResult.setRequestCount(1L);
runScriptResult.setScript(apiExecuteService.parseExecuteScript(msTestElement, apiParamConfig));
// 设置资源关联的文件信息
apiExecuteService.setTaskItemFileParam(taskItem);
runScriptResult.setTaskResourceFile(taskItem.getTaskResourceFile());
runScriptResult.setRefProjectResource(taskItem.getRefProjectResource());
return runScriptResult;
}
/**
* 获取执行的环境ID
* 优先使用运行配置的环境
* 没有则使用用例自身的环境
*
* @param runModeConfig
* @param apiTestCase
* @return
*/
public String getEnvId(ApiRunModeConfigDTO runModeConfig, ApiTestCase apiTestCase) {
return StringUtils.isBlank(runModeConfig.getEnvironmentId()) ? apiTestCase.getEnvironmentId() : runModeConfig.getEnvironmentId();
}
/**
* 预生成用例的执行报告
*
@ -802,12 +847,22 @@ public class ApiTestCaseService extends MoveNodeService {
}
public TaskRequestDTO getTaskRequest(String reportId, String resourceId, String projectId, String runModule) {
TaskRequestDTO taskRequest = apiExecuteService.getTaskRequest(reportId, resourceId, projectId);
taskRequest.setResourceType(ApiResourceType.API_CASE.name());
taskRequest.setRunMode(runModule);
TaskRequestDTO taskRequest = new TaskRequestDTO();
TaskItem taskItem = apiExecuteService.getTaskItem(reportId, resourceId);
TaskInfo taskInfo = getTaskInfo(projectId, runModule);
taskRequest.setTaskInfo(taskInfo);
taskRequest.setTaskItem(taskItem);
return taskRequest;
}
public TaskInfo getTaskInfo(String projectId, String runModule) {
TaskInfo taskInfo = apiExecuteService.getTaskInfo(projectId);
taskInfo.setResourceType(ApiResourceType.API_CASE.name());
taskInfo.setRunMode(runModule);
taskInfo.setNeedParseScript(false);
return taskInfo;
}
@Override
public void updatePos(String id, long pos) {
extApiTestCaseMapper.updatePos(id, pos);

View File

@ -23,12 +23,16 @@ public class ApiExecutionSetService {
*/
public void initSet(String setId, List<String> resourceIds) {
resourceIds.forEach(resourceId -> {
String key = SET_PREFIX + setId;
String key = getKey(setId);
redisTemplate.opsForSet().add(key, resourceId);
redisTemplate.expire(key, 1, TimeUnit.DAYS);
});
}
private String getKey(String setId) {
return SET_PREFIX + setId;
}
/**
* 从执行集合中去除选项
*/

View File

@ -4,39 +4,27 @@ import io.metersphere.api.domain.ApiScenario;
import io.metersphere.api.domain.ApiScenarioRecord;
import io.metersphere.api.domain.ApiScenarioReport;
import io.metersphere.api.domain.ApiScenarioReportStep;
import io.metersphere.api.dto.ApiScenarioParamConfig;
import io.metersphere.api.dto.ApiScenarioParseTmpParam;
import io.metersphere.api.dto.debug.ApiResourceRunRequest;
import io.metersphere.api.dto.request.MsScenario;
import io.metersphere.api.dto.scenario.ApiScenarioBatchRunRequest;
import io.metersphere.api.dto.scenario.ApiScenarioDetail;
import io.metersphere.api.dto.scenario.ApiScenarioParseParam;
import io.metersphere.api.dto.scenario.ApiScenarioStepDTO;
import io.metersphere.api.mapper.ApiScenarioMapper;
import io.metersphere.api.mapper.ApiScenarioReportMapper;
import io.metersphere.api.mapper.ExtApiScenarioMapper;
import io.metersphere.api.service.ApiBatchRunBaseService;
import io.metersphere.api.service.ApiExecuteService;
import io.metersphere.api.service.queue.ApiExecutionQueueService;
import io.metersphere.api.service.queue.ApiExecutionSetService;
import io.metersphere.api.utils.ExecTask;
import io.metersphere.api.utils.TaskRunnerUtils;
import io.metersphere.sdk.constants.*;
import io.metersphere.sdk.dto.api.task.ApiRunModeConfigDTO;
import io.metersphere.sdk.dto.api.task.CollectionReportDTO;
import io.metersphere.sdk.dto.api.task.TaskRequestDTO;
import io.metersphere.sdk.dto.api.task.*;
import io.metersphere.sdk.dto.queue.ExecutionQueue;
import io.metersphere.sdk.dto.queue.ExecutionQueueDetail;
import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.sdk.util.DateUtils;
import io.metersphere.sdk.util.LogUtils;
import io.metersphere.sdk.util.SubListUtils;
import io.metersphere.system.dto.pool.TestResourcePoolReturnDTO;
import io.metersphere.system.uid.IDGenerator;
import jakarta.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -45,7 +33,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
import java.util.stream.Collectors;
@ -68,9 +55,10 @@ public class ApiScenarioBatchRunService {
private ApiBatchRunBaseService apiBatchRunBaseService;
@Resource
private ExtApiScenarioMapper extApiScenarioMapper;
@Value("${spring.datasource.hikari.maximum-pool-size}")
private int maximumPoolSize;
@Resource
private ApiScenarioMapper apiScenarioMapper;
@Resource
private ApiScenarioRunService apiScenarioRunService;
/**
* 异步批量执行
@ -148,31 +136,17 @@ public class ApiScenarioBatchRunService {
// 集成报告执行前先设置成 RUNNING
setRunningIntegrateReport(runModeConfig);
TestResourcePoolReturnDTO testResourcePoolDTO = apiExecuteService.getGetResourcePoolNodeDTO(runModeConfig, request.getProjectId());
List<ApiScenarioDetail> apiScenarioDetails = apiScenarioService.getForRuns(ids);
List<TaskItem> taskItems = ids.stream()
.map(id -> {
String reportId = runModeConfig.isIntegratedReport() ? IDGenerator.nextStr() : scenarioReportMap.get(id);
return apiExecuteService.getTaskItem(reportId, id);
}).toList();
TaskBatchRequestDTO taskRequest = getTaskBatchRequestDTO(request.getProjectId(), runModeConfig);
taskRequest.setTaskItems(taskItems);
if (StringUtils.isNotBlank(testResourcePoolDTO.getServerUrl())) {
// 独立部署执行专属服务,线程池执行
TaskRunnerUtils.setThreadPoolSize(maximumPoolSize / 2 - 10);
apiScenarioDetails.forEach(apiScenarioDetail -> {
ExecTask execTask = new ExecTask(this, apiScenarioDetail, scenarioReportMap, runModeConfig);
TaskRunnerUtils.executeThreadPool(execTask);
});
} else {
// 未独立部署执行专属引用则使用默认循环分发任务
apiScenarioDetails.forEach(apiScenarioDetail -> execute(apiScenarioDetail, scenarioReportMap, runModeConfig));
}
apiExecuteService.batchExecute(taskRequest);
}
public void execute(ApiScenarioDetail apiScenarioDetail, Map<String, String> scenarioReportMap, ApiRunModeConfigDTO runModeConfig) {
try {
String reportId = runModeConfig.isIntegratedReport() ? IDGenerator.nextStr() : scenarioReportMap.get(apiScenarioDetail.getId());
TaskRequestDTO taskRequest = getTaskRequestDTO(reportId, apiScenarioDetail, runModeConfig);
execute(taskRequest, apiScenarioDetail);
} catch (Exception e) {
LogUtils.error("执行用例失败 {}", apiScenarioDetail.getId(), e);
}
}
private Map<String, String> initReport(List<String> ids, ApiRunModeConfigDTO runModeConfig, String userId) {
Map<String, String> scenarioReportMap = new HashMap<>();
@ -214,17 +188,6 @@ public class ApiScenarioBatchRunService {
}
private Long getRequestCount(List<ApiScenarioStepDTO> steps) {
AtomicLong requestCount = new AtomicLong();
apiScenarioService.traversalStepTree(steps, step -> {
if (BooleanUtils.isTrue(step.getEnable()) && apiScenarioService.isRequestStep(step)) {
requestCount.getAndIncrement();
}
return true;
});
return requestCount.get();
}
/**
* 集成报告执行前先设置成 RUNNING
*
@ -288,81 +251,53 @@ public class ApiScenarioBatchRunService {
*/
public void executeNextTask(ExecutionQueue queue, ExecutionQueueDetail queueDetail) {
ApiRunModeConfigDTO runModeConfig = queue.getRunModeConfig();
ApiScenarioDetail apiScenarioDetail = apiScenarioService.getForRun(queueDetail.getResourceId());
ApiScenario apiScenario = apiScenarioMapper.selectByPrimaryKey(queueDetail.getResourceId());
ApiScenarioDetail apiScenarioDetail = apiScenarioRunService.getForRun(queueDetail.getResourceId());
if (apiScenarioDetail == null) {
LogUtils.info("当前执行任务的用例已删除 {}", queueDetail.getResourceId());
return;
}
String reportId;
if (runModeConfig.isIntegratedReport()) {
reportId = IDGenerator.nextStr();
} else {
// 独立报告执行到当前任务时初始化报告
reportId = initScenarioReport(runModeConfig, apiScenarioDetail, queue.getUserId()).getApiScenarioReportId();
reportId = initScenarioReport(runModeConfig, apiScenario, queue.getUserId()).getApiScenarioReportId();
}
TaskRequestDTO taskRequest = getTaskRequestDTO(reportId, apiScenarioDetail, queue.getRunModeConfig());
taskRequest.setQueueId(queue.getQueueId());
execute(taskRequest, apiScenarioDetail);
TaskRequestDTO taskRequest = getTaskRequestDTO(apiScenario.getProjectId(), queue.getRunModeConfig());
TaskItem taskItem = apiExecuteService.getTaskItem(reportId, queueDetail.getResourceId());
taskRequest.setTaskItem(taskItem);
taskRequest.getTaskInfo().setQueueId(queue.getQueueId());
apiExecuteService.execute(taskRequest);
}
/**
* 执行批量的单个任务
*/
public void execute(TaskRequestDTO taskRequest, ApiScenarioDetail apiScenarioDetail) {
ApiRunModeConfigDTO runModeConfig = taskRequest.getRunModeConfig();
String reportId = taskRequest.getReportId();
String envId = getEnvId(runModeConfig, apiScenarioDetail);
boolean envGroup = getEnvGroup(runModeConfig, apiScenarioDetail);
// 解析生成待执行的场景树
MsScenario msScenario = apiScenarioService.getMsScenario(apiScenarioDetail);
ApiScenarioParseParam parseParam = apiScenarioService.getApiScenarioParseParam(apiScenarioDetail);
parseParam.setEnvironmentId(envId);
parseParam.setGrouped(envGroup);
// 初始化报告步骤
if (runModeConfig.isIntegratedReport()) {
apiScenarioService.initScenarioReportSteps(apiScenarioDetail.getId(), apiScenarioDetail.getSteps(), runModeConfig.getCollectionReport().getReportId());
} else {
updateReportWaitTime(reportId, parseParam);
apiScenarioService.initScenarioReportSteps(apiScenarioDetail.getSteps(), reportId);
}
taskRequest.setReportId(reportId);
// 记录请求数量
taskRequest.setRequestCount(getRequestCount(apiScenarioDetail.getSteps()));
msScenario.setResourceId(apiScenarioDetail.getId());
ApiScenarioParseTmpParam tmpParam = apiScenarioService.parse(msScenario, apiScenarioDetail.getSteps(), parseParam);
ApiResourceRunRequest runRequest = apiScenarioService.getApiResourceRunRequest(msScenario, tmpParam);
ApiScenarioParamConfig parseConfig = apiScenarioService.getApiScenarioParamConfig(parseParam, tmpParam.getScenarioParseEnvInfo());
parseConfig.setReportId(reportId);
apiExecuteService.execute(runRequest, taskRequest, parseConfig);
}
private void updateReportWaitTime(String reportId, ApiScenarioParseParam parseParam) {
Long globalWaitTime = apiScenarioService.getGlobalWaitTime(parseParam.getScenarioConfig());
if (globalWaitTime != null) {
ApiScenarioReport apiScenarioReport = new ApiScenarioReport();
apiScenarioReport.setId(reportId);
apiScenarioReport.setWaitingTime(globalWaitTime);
apiScenarioReportMapper.updateByPrimaryKeySelective(apiScenarioReport);
}
}
private TaskRequestDTO getTaskRequestDTO(String reportId, ApiScenarioDetail apiScenarioDetail, ApiRunModeConfigDTO runModeConfig) {
TaskRequestDTO taskRequest = apiScenarioService.getTaskRequest(reportId, apiScenarioDetail.getId(), apiScenarioDetail.getProjectId(), ApiExecuteRunMode.RUN.name());
taskRequest.setSaveResult(true);
taskRequest.setRealTime(false);
taskRequest.setRunModeConfig(runModeConfig);
private TaskRequestDTO getTaskRequestDTO(String projectId, ApiRunModeConfigDTO runModeConfig) {
TaskRequestDTO taskRequest = new TaskRequestDTO();
TaskInfo taskInfo = getTaskInfo(projectId, runModeConfig);
taskRequest.setTaskInfo(taskInfo);
return taskRequest;
}
private TaskBatchRequestDTO getTaskBatchRequestDTO(String projectId, ApiRunModeConfigDTO runModeConfig) {
TaskBatchRequestDTO taskRequest = new TaskBatchRequestDTO();
TaskInfo taskInfo = getTaskInfo(projectId, runModeConfig);
taskRequest.setTaskInfo(taskInfo);
return taskRequest;
}
private TaskInfo getTaskInfo(String projectId, ApiRunModeConfigDTO runModeConfig) {
TaskInfo taskInfo = apiScenarioRunService.getTaskInfo(projectId, ApiExecuteRunMode.RUN.name());
taskInfo.setSaveResult(true);
taskInfo.setRealTime(false);
taskInfo.setNeedParseScript(true);
taskInfo.setRunModeConfig(runModeConfig);
return taskInfo;
}
/**
* 预生成用例的执行报告
*
@ -375,7 +310,7 @@ public class ApiScenarioBatchRunService {
ApiScenarioReport apiScenarioReport = getScenarioReport(runModeConfig, apiScenario, userId);
apiScenarioReport.setId(IDGenerator.nextStr());
// 创建报告和用例的关联关系
ApiScenarioRecord apiScenarioRecord = apiScenarioService.getApiScenarioRecord(apiScenario, apiScenarioReport);
ApiScenarioRecord apiScenarioRecord = apiScenarioRunService.getApiScenarioRecord(apiScenario, apiScenarioReport);
apiScenarioReportService.insertApiScenarioReport(List.of(apiScenarioReport), List.of(apiScenarioRecord));
return apiScenarioRecord;
}
@ -383,7 +318,7 @@ public class ApiScenarioBatchRunService {
private ApiScenarioReport getScenarioReport(ApiRunModeConfigDTO runModeConfig, ApiScenario apiScenario, String userId) {
ApiScenarioReport apiScenarioReport = getScenarioReport(runModeConfig, userId);
apiScenarioReport.setEnvironmentId(getEnvId(runModeConfig, apiScenario));
apiScenarioReport.setEnvironmentId(apiScenarioRunService.getEnvId(runModeConfig, apiScenario));
apiScenarioReport.setName(apiScenario.getName() + "_" + DateUtils.getTimeString(System.currentTimeMillis()));
apiScenarioReport.setProjectId(apiScenario.getProjectId());
apiScenarioReport.setTriggerMode(TaskTriggerMode.BATCH.name());
@ -391,7 +326,7 @@ public class ApiScenarioBatchRunService {
}
public ApiScenarioReport getScenarioReport(ApiRunModeConfigDTO runModeConfig, String userId) {
ApiScenarioReport apiScenarioReport = apiScenarioService.getScenarioReport(userId);
ApiScenarioReport apiScenarioReport = apiScenarioRunService.getScenarioReport(userId);
apiScenarioReport.setEnvironmentId(runModeConfig.getEnvironmentId());
apiScenarioReport.setRunMode(runModeConfig.getRunMode());
apiScenarioReport.setPoolId(runModeConfig.getPoolId());
@ -399,22 +334,7 @@ public class ApiScenarioBatchRunService {
return apiScenarioReport;
}
/**
* 获取执行的环境ID
* 优先使用运行配置的环境
* 没有则使用用例自身的环境
*
* @param runModeConfig
* @param apiScenario
* @return
*/
public String getEnvId(ApiRunModeConfigDTO runModeConfig, ApiScenario apiScenario) {
return StringUtils.isBlank(runModeConfig.getEnvironmentId()) ? apiScenario.getEnvironmentId() : runModeConfig.getEnvironmentId();
}
public boolean getEnvGroup(ApiRunModeConfigDTO runModeConfig, ApiScenario apiScenario) {
return StringUtils.isBlank(runModeConfig.getEnvironmentId()) ? apiScenario.getGrouped() : runModeConfig.getGrouped();
}
public void updateStopOnFailureReport(ExecutionQueue queue) {
ApiRunModeConfigDTO runModeConfig = queue.getRunModeConfig();
@ -425,18 +345,18 @@ public class ApiScenarioBatchRunService {
}
long requestCount = 0L;
while (queueDetail != null) {
ApiScenarioDetail apiScenarioDetail = apiScenarioService.getForRun(queueDetail.getResourceId());
ApiScenarioDetail apiScenarioDetail = apiScenarioRunService.getForRun(queueDetail.getResourceId());
if (apiScenarioDetail == null) {
LogUtils.info("当前场景已删除 {}", queueDetail.getResourceId());
continue;
}
Long requestCountItem = getRequestCount(apiScenarioDetail.getSteps());
Long requestCountItem = apiScenarioRunService.getRequestCount(apiScenarioDetail.getSteps());
requestCount += requestCountItem;
// 初始化报告步骤
if (runModeConfig.isIntegratedReport()) {
apiScenarioService.initScenarioReportSteps(apiScenarioDetail.getId(), apiScenarioDetail.getSteps(), runModeConfig.getCollectionReport().getReportId());
apiScenarioRunService.initScenarioReportSteps(apiScenarioDetail.getId(), apiScenarioDetail.getSteps(), runModeConfig.getCollectionReport().getReportId());
}
queueDetail = apiExecutionQueueService.getNextDetail(queue.getQueueId());

View File

@ -0,0 +1,961 @@
package io.metersphere.api.service.scenario;
import io.metersphere.api.constants.ApiResourceType;
import io.metersphere.api.constants.ApiScenarioStepRefType;
import io.metersphere.api.constants.ApiScenarioStepType;
import io.metersphere.api.domain.*;
import io.metersphere.api.dto.*;
import io.metersphere.api.dto.debug.ApiResourceRunRequest;
import io.metersphere.api.dto.request.MsScenario;
import io.metersphere.api.dto.request.controller.MsScriptElement;
import io.metersphere.api.dto.request.http.MsHTTPElement;
import io.metersphere.api.dto.scenario.*;
import io.metersphere.api.mapper.ApiScenarioBlobMapper;
import io.metersphere.api.mapper.ApiScenarioMapper;
import io.metersphere.api.mapper.ApiScenarioReportMapper;
import io.metersphere.api.parser.step.StepParser;
import io.metersphere.api.parser.step.StepParserFactory;
import io.metersphere.api.service.ApiCommonService;
import io.metersphere.api.service.ApiExecuteService;
import io.metersphere.api.service.definition.ApiDefinitionModuleService;
import io.metersphere.api.service.definition.ApiDefinitionService;
import io.metersphere.api.service.definition.ApiTestCaseService;
import io.metersphere.api.service.queue.ApiExecutionSetService;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.project.api.processor.MsProcessor;
import io.metersphere.project.api.processor.TimeWaitingProcessor;
import io.metersphere.project.dto.environment.EnvironmentInfoDTO;
import io.metersphere.project.dto.environment.http.HttpConfig;
import io.metersphere.project.dto.environment.http.HttpConfigModuleMatchRule;
import io.metersphere.project.dto.environment.http.SelectModule;
import io.metersphere.project.service.EnvironmentGroupService;
import io.metersphere.project.service.EnvironmentService;
import io.metersphere.sdk.constants.ApiBatchRunMode;
import io.metersphere.sdk.constants.ApiExecuteRunMode;
import io.metersphere.sdk.constants.ExecStatus;
import io.metersphere.sdk.constants.TaskTriggerMode;
import io.metersphere.sdk.dto.api.task.*;
import io.metersphere.sdk.util.DateUtils;
import io.metersphere.sdk.util.JSON;
import io.metersphere.sdk.util.LogUtils;
import io.metersphere.system.service.ApiPluginService;
import io.metersphere.system.uid.IDGenerator;
import jakarta.annotation.Resource;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
import java.util.stream.Collectors;
@Service
@Transactional(rollbackFor = Exception.class)
public class ApiScenarioRunService {
@Resource
private ApiScenarioMapper apiScenarioMapper;
@Resource
private ApiScenarioService apiScenarioService;
@Resource
private ApiExecuteService apiExecuteService;
@Resource
private ApiDefinitionService apiDefinitionService;
@Resource
private ApiTestCaseService apiTestCaseService;
@Resource
private EnvironmentService environmentService;
@Resource
private EnvironmentGroupService environmentGroupService;
@Resource
private ApiPluginService apiPluginService;
@Resource
private ApiDefinitionModuleService apiDefinitionModuleService;
@Resource
private ApiCommonService apiCommonService;
@Resource
private ApiScenarioReportService apiScenarioReportService;
@Resource
private ApiScenarioReportMapper apiScenarioReportMapper;
@Resource
private ApiScenarioBlobMapper apiScenarioBlobMapper;
@Resource
private ApiExecutionSetService apiExecutionSetService;
public TaskRequestDTO run(String id, String reportId, String userId) {
ApiScenarioDetail apiScenarioDetail = getForRun(id);
// 解析生成待执行的场景树
MsScenario msScenario = getMsScenario(apiScenarioDetail);
ApiScenarioParseParam parseParam = getApiScenarioParseParam(apiScenarioDetail);
return executeRun(apiScenarioDetail, msScenario, apiScenarioDetail.getSteps(), parseParam, new ApiResourceRunRequest(), reportId, userId);
}
public TaskRequestDTO run(ApiScenarioDebugRequest request, String userId) {
ApiScenario apiScenario = apiScenarioMapper.selectByPrimaryKey(request.getId());
// 解析生成待执行的场景树
MsScenario msScenario = new MsScenario();
msScenario.setRefType(ApiScenarioStepRefType.DIRECT.name());
msScenario.setScenarioConfig(getScenarioConfig(request, true));
msScenario.setProjectId(request.getProjectId());
// 处理特殊的步骤详情
apiScenarioService.addSpecialStepDetails(request.getSteps(), request.getStepDetails());
ApiResourceRunRequest runRequest = new ApiResourceRunRequest();
runRequest = setFileParam(request, runRequest);
return executeRun(apiScenario, msScenario, request.getSteps(), request, runRequest, request.getReportId(), userId);
}
private ScenarioConfig getScenarioConfig(ApiScenarioDebugRequest request, boolean hasSave) {
if (request.getScenarioConfig() != null) {
// 优先使用前端传的配置
return request.getScenarioConfig();
} else if (hasSave) {
// 没传并且保存过则从数据库获取
ApiScenarioBlob apiScenarioBlob = apiScenarioBlobMapper.selectByPrimaryKey(request.getId());
if (apiScenarioBlob != null) {
return JSON.parseObject(new String(apiScenarioBlob.getConfig()), ScenarioConfig.class);
}
}
return new ScenarioConfig();
}
public ApiScenarioParseParam getApiScenarioParseParam(ApiScenarioDetail apiScenarioDetail) {
ApiScenarioParseParam parseParam = new ApiScenarioParseParam();
parseParam.setScenarioConfig(apiScenarioDetail.getScenarioConfig());
parseParam.setStepDetails(Map.of());
parseParam.setEnvironmentId(apiScenarioDetail.getEnvironmentId());
parseParam.setGrouped(apiScenarioDetail.getGrouped());
return parseParam;
}
/**
* 执行时设置临时文件的相关参数
*
* @param request
* @param runRequest
*/
private ApiResourceRunRequest setFileParam(ApiScenarioDebugRequest request, ApiResourceRunRequest runRequest) {
runRequest.getLinkFileIds().addAll(getLinkFileIds(request.getFileParam()));
runRequest.getUploadFileIds().addAll(getUploadFileIds(request.getFileParam()));
Map<String, ResourceAddFileParam> stepFileParam = request.getStepFileParam();
if (MapUtils.isNotEmpty(stepFileParam)) {
stepFileParam.values().forEach(fileParam -> {
runRequest.getLinkFileIds().addAll(getLinkFileIds(fileParam));
runRequest.getUploadFileIds().addAll(getUploadFileIds(fileParam));
});
}
return runRequest;
}
public List<String> getLinkFileIds(ResourceAddFileParam fileParam) {
if (fileParam != null && CollectionUtils.isNotEmpty(fileParam.getLinkFileIds())) {
return fileParam.getLinkFileIds();
}
return List.of();
}
public List<String> getUploadFileIds(ResourceAddFileParam fileParam) {
if (fileParam != null && CollectionUtils.isNotEmpty(fileParam.getUploadFileIds())) {
return fileParam.getUploadFileIds();
}
return List.of();
}
public TaskRequestDTO executeRun(ApiScenario apiScenario,
MsScenario msScenario,
List<? extends ApiScenarioStepCommonDTO> steps,
ApiScenarioParseParam parseParam,
ApiResourceRunRequest runRequest,
String reportId,
String userId) {
msScenario.setResourceId(apiScenario.getId());
// 解析生成场景树并保存临时变量
ApiScenarioParseTmpParam tmpParam = parse(msScenario, steps, parseParam);
runRequest = setApiResourceRunRequestParam(msScenario, tmpParam, runRequest);
String poolId = apiExecuteService.getProjectApiResourcePoolId(apiScenario.getProjectId());
TaskRequestDTO taskRequest = getTaskRequest(reportId, apiScenario.getId(), apiScenario.getProjectId(), ApiExecuteRunMode.RUN.name());
TaskInfo taskInfo = taskRequest.getTaskInfo();
TaskItem taskItem = taskRequest.getTaskItem();
taskInfo.getRunModeConfig().setPoolId(poolId);
taskInfo.setSaveResult(true);
taskInfo.getRunModeConfig().setEnvironmentId(parseParam.getEnvironmentId());
taskRequest.getTaskItem().setRequestCount(tmpParam.getRequestCount().get());
if (StringUtils.isEmpty(taskItem.getReportId())) {
taskInfo.setRealTime(false);
reportId = IDGenerator.nextStr();
taskItem.setReportId(reportId);
} else {
// 如果传了报告ID则实时获取结果
taskInfo.setRealTime(true);
}
ApiScenarioParamConfig parseConfig = getApiScenarioParamConfig(apiScenario.getProjectId(), parseParam, tmpParam.getScenarioParseEnvInfo());
parseConfig.setReportId(reportId);
// 初始化报告
ApiScenarioReport scenarioReport = getScenarioReport(userId);
scenarioReport.setId(reportId);
scenarioReport.setTriggerMode(TaskTriggerMode.MANUAL.name());
scenarioReport.setRunMode(ApiBatchRunMode.PARALLEL.name());
scenarioReport.setPoolId(poolId);
scenarioReport.setEnvironmentId(parseParam.getEnvironmentId());
scenarioReport.setWaitingTime(getGlobalWaitTime(parseParam.getScenarioConfig()));
initApiReport(apiScenario, scenarioReport);
// 初始化报告步骤
initScenarioReportSteps(steps, taskItem.getReportId());
return apiExecuteService.execute(runRequest, taskRequest, parseConfig);
}
public MsScenario getMsScenario(ApiScenarioDetail apiScenarioDetail) {
MsScenario msScenario = new MsScenario();
msScenario.setRefType(ApiScenarioStepRefType.DIRECT.name());
msScenario.setScenarioConfig(apiScenarioDetail.getScenarioConfig());
msScenario.setProjectId(apiScenarioDetail.getProjectId());
return msScenario;
}
public ApiScenarioDetail getForRun(String scenarioId) {
ApiScenarioDetail apiScenarioDetail = apiScenarioService.get(scenarioId);
apiScenarioDetail.setSteps(filerDisableSteps(apiScenarioDetail.getSteps()));
return apiScenarioDetail;
}
/**
* 过滤掉禁用的步骤
*/
public List<ApiScenarioStepDTO> filerDisableSteps(List<ApiScenarioStepDTO> steps) {
if (CollectionUtils.isEmpty(steps)) {
return List.of();
}
return steps.stream()
.filter(step -> {
boolean isEnable = BooleanUtils.isTrue(step.getEnable());
if (isEnable) {
step.setChildren(filerDisableSteps(step.getChildren()));
}
return isEnable;
})
.toList();
}
/**
* 解析并返回执行脚本等信息
*/
public GetRunScriptResult getRunScript(GetRunScriptRequest request) {
TaskItem taskItem = request.getTaskItem();
String id = taskItem.getResourceId();
ApiRunModeConfigDTO runModeConfig = request.getRunModeConfig();
String reportId = taskItem.getReportId();
ApiScenarioDetail apiScenarioDetail = getForRun(id);
if (apiScenarioDetail == null) {
if (runModeConfig.isIntegratedReport()) {
// 用例不存在则在执行集合中删除
apiExecutionSetService.removeItem(runModeConfig.getCollectionReport().getReportId(), id);
}
LogUtils.info("当前执行任务的用例已删除 {}", id);
return null;
}
String envId = getEnvId(runModeConfig, apiScenarioDetail);
boolean envGroup = getEnvGroup(runModeConfig, apiScenarioDetail);
// 解析生成待执行的场景树
MsScenario msScenario = getMsScenario(apiScenarioDetail);
ApiScenarioParseParam parseParam = getApiScenarioParseParam(apiScenarioDetail);
parseParam.setEnvironmentId(envId);
parseParam.setGrouped(envGroup);
// 初始化报告步骤
if (runModeConfig.isIntegratedReport()) {
initScenarioReportSteps(apiScenarioDetail.getId(), apiScenarioDetail.getSteps(), runModeConfig.getCollectionReport().getReportId());
} else {
updateReportWaitTime(reportId, parseParam);
initScenarioReportSteps(apiScenarioDetail.getSteps(), reportId);
}
GetRunScriptResult runScriptResult = new GetRunScriptResult();
// 记录请求数量
runScriptResult.setRequestCount(getRequestCount(apiScenarioDetail.getSteps()));
msScenario.setResourceId(apiScenarioDetail.getId());
ApiScenarioParseTmpParam tmpParam = parse(msScenario, apiScenarioDetail.getSteps(), parseParam);
ApiResourceRunRequest runRequest = getApiResourceRunRequest(msScenario, tmpParam);
ApiScenarioParamConfig parseConfig = getApiScenarioParamConfig(apiScenarioDetail.getProjectId(), parseParam, tmpParam.getScenarioParseEnvInfo());
parseConfig.setReportId(reportId);
String script = apiExecuteService.parseExecuteScript(runRequest.getTestElement(), parseConfig);
runScriptResult.setScript(script);
apiExecuteService.setTaskItemFileParam(runRequest, taskItem);
runScriptResult.setTaskResourceFile(taskItem.getTaskResourceFile());
runScriptResult.setRefProjectResource(taskItem.getRefProjectResource());
return runScriptResult;
}
/**
* 获取执行的环境ID
* 优先使用运行配置的环境
* 没有则使用用例自身的环境
*
* @param runModeConfig
* @param apiScenario
* @return
*/
public String getEnvId(ApiRunModeConfigDTO runModeConfig, ApiScenario apiScenario) {
return StringUtils.isBlank(runModeConfig.getEnvironmentId()) ? apiScenario.getEnvironmentId() : runModeConfig.getEnvironmentId();
}
public boolean getEnvGroup(ApiRunModeConfigDTO runModeConfig, ApiScenario apiScenario) {
return StringUtils.isBlank(runModeConfig.getEnvironmentId()) ? apiScenario.getGrouped() : runModeConfig.getGrouped();
}
private void updateReportWaitTime(String reportId, ApiScenarioParseParam parseParam) {
Long globalWaitTime = getGlobalWaitTime(parseParam.getScenarioConfig());
if (globalWaitTime != null) {
ApiScenarioReport apiScenarioReport = new ApiScenarioReport();
apiScenarioReport.setId(reportId);
apiScenarioReport.setWaitingTime(globalWaitTime);
apiScenarioReportMapper.updateByPrimaryKeySelective(apiScenarioReport);
}
}
public TaskRequestDTO debug(ApiScenarioDebugRequest request) {
ApiScenario apiScenario = apiScenarioMapper.selectByPrimaryKey(request.getId());
boolean hasSave = apiScenario != null;
// 解析生成待执行的场景树
MsScenario msScenario = new MsScenario();
msScenario.setRefType(ApiScenarioStepRefType.DIRECT.name());
msScenario.setScenarioConfig(getScenarioConfig(request, hasSave));
msScenario.setProjectId(request.getProjectId());
msScenario.setResourceId(request.getId());
// 处理特殊的步骤详情
apiScenarioService.addSpecialStepDetails(request.getSteps(), request.getStepDetails());
ApiScenarioParseTmpParam tmpParam = parse(msScenario, request.getSteps(), request);
ApiResourceRunRequest runRequest = getApiResourceRunRequest(msScenario, tmpParam);
runRequest = setFileParam(request, runRequest);
TaskRequestDTO taskRequest = getTaskRequest(request.getReportId(), request.getId(), request.getProjectId(),
apiExecuteService.getDebugRunModule(request.getFrontendDebug()));
TaskInfo taskInfo = taskRequest.getTaskInfo();
TaskItem taskItem = taskRequest.getTaskItem();
taskInfo.setSaveResult(false);
taskInfo.setRealTime(true);
taskItem.setRequestCount(tmpParam.getRequestCount().get());
ApiScenarioParamConfig parseConfig = getApiScenarioParamConfig(request.getProjectId(), request, tmpParam.getScenarioParseEnvInfo());
parseConfig.setReportId(request.getReportId());
return apiExecuteService.execute(runRequest, taskRequest, parseConfig);
}
/**
* 获取场景前置的总等待时间
*
* @param scenarioConfig
* @return
*/
public Long getGlobalWaitTime(ScenarioConfig scenarioConfig) {
Long waitTime = null;
if (scenarioConfig != null
&& scenarioConfig.getPreProcessorConfig() != null
&& scenarioConfig.getPreProcessorConfig().getProcessors() != null) {
waitTime = 0L;
for (MsProcessor processor : scenarioConfig
.getPreProcessorConfig()
.getProcessors()) {
if (processor instanceof TimeWaitingProcessor timeWaitingProcessor
&& timeWaitingProcessor.getEnable()
&& timeWaitingProcessor.getDelay() != null) {
waitTime += timeWaitingProcessor.getDelay();
}
}
waitTime = waitTime > 0 ? waitTime : null;
}
return waitTime;
}
/**
* 预生成用例的执行报告
*
* @param apiScenario
* @return
*/
public ApiScenarioRecord initApiReport(ApiScenario apiScenario, ApiScenarioReport scenarioReport) {
// 初始化报告
scenarioReport.setName(apiScenario.getName() + "_" + DateUtils.getTimeString(System.currentTimeMillis()));
scenarioReport.setProjectId(apiScenario.getProjectId());
// 创建报告和用例的关联关系
ApiScenarioRecord scenarioRecord = getApiScenarioRecord(apiScenario, scenarioReport);
apiScenarioReportService.insertApiScenarioReport(List.of(scenarioReport), List.of(scenarioRecord));
return scenarioRecord;
}
public Long getRequestCount(List<ApiScenarioStepDTO> steps) {
AtomicLong requestCount = new AtomicLong();
apiScenarioService.traversalStepTree(steps, step -> {
if (BooleanUtils.isTrue(step.getEnable()) && apiScenarioService.isRequestStep(step)) {
requestCount.getAndIncrement();
}
return true;
});
return requestCount.get();
}
/**
* 初始化场景报告步骤
*
* @param steps
* @param reportId
*/
public void initScenarioReportSteps(List<? extends ApiScenarioStepCommonDTO> steps, String reportId) {
initScenarioReportSteps(null, steps, reportId);
}
public void initScenarioReportSteps(String parentId, List<? extends ApiScenarioStepCommonDTO> steps, String reportId) {
List<ApiScenarioReportStep> scenarioReportSteps = getScenarioReportSteps(parentId, steps, reportId);
apiScenarioReportService.insertApiScenarioReportStep(scenarioReportSteps);
}
/**
* 获取场景报告步骤
*
* @param steps
* @param reportId
*/
public List<ApiScenarioReportStep> getScenarioReportSteps(String parentId, List<? extends ApiScenarioStepCommonDTO> steps, String reportId) {
AtomicLong sort = new AtomicLong(1);
List<ApiScenarioReportStep> scenarioReportSteps = new ArrayList<>();
for (ApiScenarioStepCommonDTO step : steps) {
if (StringUtils.isBlank(step.getUniqueId())) {
// 如果没有步骤唯一ID则生成唯一ID
step.setUniqueId(IDGenerator.nextStr());
}
ApiScenarioReportStep scenarioReportStep = getScenarioReportStep(step, reportId, sort.getAndIncrement());
scenarioReportStep.setParentId(parentId);
scenarioReportSteps.add(scenarioReportStep);
List<? extends ApiScenarioStepCommonDTO> children = step.getChildren();
if (CollectionUtils.isNotEmpty(children)) {
scenarioReportSteps.addAll(getScenarioReportSteps(step.getUniqueId(), children, reportId));
}
}
return scenarioReportSteps;
}
private ApiScenarioReportStep getScenarioReportStep(ApiScenarioStepCommonDTO step, String reportId, long sort) {
ApiScenarioReportStep scenarioReportStep = new ApiScenarioReportStep();
scenarioReportStep.setReportId(reportId);
scenarioReportStep.setStepId(step.getUniqueId());
scenarioReportStep.setSort(sort);
scenarioReportStep.setName(step.getName());
scenarioReportStep.setStepType(step.getStepType());
return scenarioReportStep;
}
public ApiScenarioRecord getApiScenarioRecord(ApiScenario apiScenario, ApiScenarioReport scenarioReport) {
ApiScenarioRecord scenarioRecord = new ApiScenarioRecord();
scenarioRecord.setApiScenarioId(apiScenario.getId());
scenarioRecord.setApiScenarioReportId(scenarioReport.getId());
return scenarioRecord;
}
public ApiScenarioReport getScenarioReport(String userId) {
ApiScenarioReport scenarioReport = new ApiScenarioReport();
scenarioReport.setId(IDGenerator.nextStr());
scenarioReport.setDeleted(false);
scenarioReport.setIntegrated(false);
scenarioReport.setExecStatus(ExecStatus.PENDING.name());
scenarioReport.setStartTime(System.currentTimeMillis());
scenarioReport.setUpdateTime(System.currentTimeMillis());
scenarioReport.setUpdateUser(userId);
scenarioReport.setCreateUser(userId);
return scenarioReport;
}
public ApiScenarioParamConfig getApiScenarioParamConfig(String projectId, ApiScenarioParseParam request, ApiScenarioParseEnvInfo scenarioParseEnvInfo) {
ApiScenarioParamConfig parseConfig = new ApiScenarioParamConfig();
parseConfig.setTestElementClassPluginIdMap(apiPluginService.getTestElementPluginMap());
parseConfig.setTestElementClassProtocolMap(apiPluginService.getTestElementProtocolMap());
parseConfig.setGrouped(request.getGrouped());
parseConfig.setRootScenarioConfig(request.getScenarioConfig());
if (BooleanUtils.isTrue(request.getGrouped())) {
// 设置环境组 map
parseConfig.setProjectEnvMap(getProjectEnvMap(scenarioParseEnvInfo, request.getEnvironmentId()));
} else {
// 设置环境
parseConfig.setEnvConfig(scenarioParseEnvInfo.getEnvMap().get(request.getEnvironmentId()));
}
// 设置全局参数接口调试不使用全局参数
parseConfig.setGlobalParams(apiExecuteService.getGlobalParam(projectId));
return parseConfig;
}
public ApiResourceRunRequest getApiResourceRunRequest(MsScenario msScenario, ApiScenarioParseTmpParam tmpParam) {
ApiResourceRunRequest runRequest = new ApiResourceRunRequest();
return setApiResourceRunRequestParam(msScenario, tmpParam, runRequest);
}
private ApiResourceRunRequest setApiResourceRunRequestParam(MsScenario msScenario, ApiScenarioParseTmpParam tmpParam, ApiResourceRunRequest runRequest) {
runRequest.setFileResourceIds(tmpParam.getFileResourceIds());
runRequest.setFileStepScenarioMap(tmpParam.getFileStepScenarioMap());
runRequest.setRefProjectIds(tmpParam.getRefProjectIds());
runRequest.setTestElement(msScenario);
return runRequest;
}
/**
* 将步骤转换成场景树
* 并保存临时变量
*
* @param msScenario
* @param steps
* @param parseParam
* @return
*/
public ApiScenarioParseTmpParam parse(MsScenario msScenario,
List<? extends ApiScenarioStepCommonDTO> steps,
ApiScenarioParseParam parseParam) {
// 记录引用的资源ID
Map<String, List<String>> refResourceMap = new HashMap<>();
buildRefResourceIdMap(steps, refResourceMap);
ApiScenarioParseTmpParam tmpParam = new ApiScenarioParseTmpParam();
// 查询引用的资源详情
tmpParam.setResourceDetailMap(getResourceDetailMap(refResourceMap));
// 查询复制的步骤详情
tmpParam.setStepDetailMap(getStepDetailMap(steps, parseParam.getStepDetails()));
// 获取场景环境相关配置
tmpParam.setScenarioParseEnvInfo(getScenarioParseEnvInfo(refResourceMap, parseParam.getEnvironmentId(), parseParam.getGrouped()));
parseStep2MsElement(msScenario, steps, tmpParam, msScenario.getResourceId());
// 设置 HttpElement 的模块信息
setApiDefinitionExecuteInfo(tmpParam.getUniqueIdStepMap(), tmpParam.getStepTypeHttpElementMap());
// 设置使用脚本前后置的公共脚本信息
apiCommonService.setCommonElementEnableCommonScriptInfo(tmpParam.getCommonElements());
apiCommonService.setScriptElementEnableCommonScriptInfo(tmpParam.getScriptElements());
return tmpParam;
}
private void buildRefResourceIdMap(List<? extends ApiScenarioStepCommonDTO> steps, Map<String, List<String>> refResourceIdMap) {
for (ApiScenarioStepCommonDTO step : steps) {
if (apiScenarioService.isRefOrPartialRef(step.getRefType()) && BooleanUtils.isTrue(step.getEnable())) {
// 记录引用的步骤ID
List<String> resourceIds = refResourceIdMap.computeIfAbsent(step.getStepType(), k -> new ArrayList<>());
resourceIds.add(step.getResourceId());
}
if (CollectionUtils.isNotEmpty(step.getChildren())) {
buildRefResourceIdMap(step.getChildren(), refResourceIdMap);
}
}
}
private Map<String, String> getStepDetailMap(List<? extends ApiScenarioStepCommonDTO> steps, Map<String, Object> stepDetailsParam) {
List<String> needBlobStepIds = getHasDetailStepIds(steps, stepDetailsParam);
Map<String, String> stepDetails = apiScenarioService.getStepBlobByIds(needBlobStepIds).stream()
.collect(Collectors.toMap(ApiScenarioStepBlob::getId, blob -> new String(blob.getContent())));
// 前端有传就用前端传的
if (stepDetailsParam != null) {
stepDetailsParam.forEach((stepId, detail) -> stepDetails.put(stepId, detail instanceof byte[] bytes ? new String(bytes) : JSON.toJSONString(detail)));
}
return stepDetails;
}
private List<String> getHasDetailStepIds(List<? extends ApiScenarioStepCommonDTO> steps, Map<String, Object> stepDetailsParam) {
List<String> needBlobStepIds = new ArrayList<>();
for (ApiScenarioStepCommonDTO step : steps) {
List<? extends ApiScenarioStepCommonDTO> children = step.getChildren();
if (CollectionUtils.isNotEmpty(children)) {
needBlobStepIds.addAll(getHasDetailStepIds(children, stepDetailsParam));
}
if (BooleanUtils.isFalse(step.getEnable())) {
continue;
}
if (!hasStepDetail(step.getStepType())) {
continue;
}
if (stepDetailsParam != null && stepDetailsParam.containsKey(step.getId())) {
// 前端传了blob不需要再查
continue;
}
needBlobStepIds.add(step.getId());
}
return needBlobStepIds;
}
/**
* 非完全引用的步骤和接口定义的步骤才需要查 blob
*
* @param stepType
* @return
*/
private boolean hasStepDetail(String stepType) {
return !StringUtils.equals(stepType, ApiScenarioStepRefType.REF.name())
|| apiScenarioService.isApi(stepType);
}
private Map<String, String> getResourceDetailMap(Map<String, List<String>> refResourceMap) {
Map<String, String> resourceBlobMap = new HashMap<>();
List<String> apiIds = refResourceMap.get(ApiScenarioStepType.API.name());
List<ApiDefinitionBlob> apiDefinitionBlobs = apiDefinitionService.getBlobByIds(apiIds);
apiDefinitionBlobs.forEach(blob -> resourceBlobMap.put(blob.getId(), new String(blob.getRequest())));
List<String> apiCaseIds = refResourceMap.get(ApiScenarioStepType.API_CASE.name());
List<ApiTestCaseBlob> apiTestCaseBlobs = apiTestCaseService.getBlobByIds(apiCaseIds);
apiTestCaseBlobs.forEach(blob -> resourceBlobMap.put(blob.getId(), new String(blob.getRequest())));
List<String> apiScenarioIds = refResourceMap.get(ApiScenarioStepType.API_SCENARIO.name());
List<ApiScenarioBlob> apiScenarioBlobs = getBlobByIds(apiScenarioIds);
apiScenarioBlobs.forEach(blob -> resourceBlobMap.put(blob.getId(), new String(blob.getConfig())));
return resourceBlobMap;
}
private List<ApiScenarioBlob> getBlobByIds(List<String> apiScenarioIds) {
if (CollectionUtils.isEmpty(apiScenarioIds)) {
return Collections.emptyList();
}
ApiScenarioBlobExample example = new ApiScenarioBlobExample();
example.createCriteria().andIdIn(apiScenarioIds);
return apiScenarioBlobMapper.selectByExampleWithBLOBs(example);
}
public TaskRequestDTO getTaskRequest(String reportId, String resourceId, String projectId, String runModule) {
TaskRequestDTO taskRequest = new TaskRequestDTO();
TaskInfo taskInfo = getTaskInfo(projectId, runModule);
TaskItem taskItem = apiExecuteService.getTaskItem(reportId, resourceId);
taskRequest.setTaskInfo(taskInfo);
taskRequest.setTaskItem(taskItem);
return taskRequest;
}
public TaskInfo getTaskInfo(String projectId, String runModule) {
TaskInfo taskInfo = apiExecuteService.getTaskInfo(projectId);
taskInfo.setResourceType(ApiResourceType.API_SCENARIO.name());
taskInfo.setRunMode(runModule);
return taskInfo;
}
/**
* 设置 HttpElement 的模块信息
* 用户环境中的模块过滤
*
* @param uniqueIdStepMap
* @param stepTypeHttpElementMap
*/
private void setApiDefinitionExecuteInfo(Map<String, ApiScenarioStepCommonDTO> uniqueIdStepMap, Map<String, List<MsHTTPElement>> stepTypeHttpElementMap) {
setApiDefinitionExecuteInfo(uniqueIdStepMap, stepTypeHttpElementMap.get(ApiScenarioStepType.API.name()), apiDefinitionService::getModuleInfoByIds);
setApiDefinitionExecuteInfo(uniqueIdStepMap, stepTypeHttpElementMap.get(ApiScenarioStepType.API_CASE.name()), apiTestCaseService::getModuleInfoByIds);
}
/**
* 设置 MsHTTPElement 中的 method 等信息
*
* @param httpElements
* @param getDefinitionInfoFunc
*/
public void setApiDefinitionExecuteInfo(Map<String, ApiScenarioStepCommonDTO> uniqueIdStepMap, List<MsHTTPElement> httpElements, Function<List<String>, List<ApiDefinitionExecuteInfo>> getDefinitionInfoFunc) {
if (org.apache.commons.collections.CollectionUtils.isNotEmpty(httpElements)) {
List<String> resourceIds = httpElements.stream().map(MsHTTPElement::getResourceId).collect(Collectors.toList());
// 获取接口模块信息
Map<String, ApiDefinitionExecuteInfo> resourceModuleMap = apiCommonService.getApiDefinitionExecuteInfoMap(getDefinitionInfoFunc, resourceIds);
httpElements.forEach(httpElement -> {
ApiDefinitionExecuteInfo definitionExecuteInfo = resourceModuleMap.get(httpElement.getResourceId());
String path = httpElement.getPath();
String method = httpElement.getMethod();
// httpElement 设置模块,请求方法等信息
apiCommonService.setApiDefinitionExecuteInfo(httpElement, definitionExecuteInfo);
ApiScenarioStepCommonDTO step = uniqueIdStepMap.get(httpElement.getStepId());
if (step != null && apiScenarioService.isCopyApi(step.getStepType(), step.getRefType())) {
// 复制的接口定义不使用源接口定义的path和method
httpElement.setPath(path);
httpElement.setMethod(method);
}
});
}
}
/**
* 设置脚本解析-环境相关参数
*/
private ApiScenarioParseEnvInfo getScenarioParseEnvInfo(Map<String, List<String>> refResourceMap, String currentEnvId, Boolean isCurrentEnvGrouped) {
List<String> apiScenarioIds = refResourceMap.get(ApiScenarioStepType.API_SCENARIO.name());
List<String> envIds = new ArrayList<>();
List<String> envGroupIds = new ArrayList<>();
ApiScenarioParseEnvInfo envInfo = new ApiScenarioParseEnvInfo();
if (BooleanUtils.isTrue(isCurrentEnvGrouped)) {
envGroupIds.add(currentEnvId);
} else {
envIds.add(currentEnvId);
}
if (CollectionUtils.isNotEmpty(apiScenarioIds)) {
Map<String, EnvironmentModeDTO> refScenarioEnvMap = new HashMap<>();
List<ApiScenario> apiScenarios = getApiScenarioByIds(apiScenarioIds);
for (ApiScenario scenario : apiScenarios) {
EnvironmentModeDTO envMode = new EnvironmentModeDTO();
envMode.setEnvironmentId(scenario.getEnvironmentId());
envMode.setGrouped(scenario.getGrouped());
if (BooleanUtils.isTrue(scenario.getGrouped())) {
// 记录环境组ID
envGroupIds.add(scenario.getEnvironmentId());
} else {
// 记录环境ID
envIds.add(scenario.getEnvironmentId());
}
// 保存场景的环境配置信息
refScenarioEnvMap.put(scenario.getId(), envMode);
}
envInfo.setRefScenarioEnvMap(refScenarioEnvMap);
}
// 查询环境组中的环境ID列表
Map<String, List<String>> envGroupMap = new HashMap<>();
environmentGroupService.getEnvironmentGroupRelations(envGroupIds).forEach(environmentGroupRelation -> {
envGroupMap.putIfAbsent(environmentGroupRelation.getEnvironmentGroupId(), new ArrayList<>());
envGroupMap.get(environmentGroupRelation.getEnvironmentGroupId()).add(environmentGroupRelation.getEnvironmentId());
envIds.add(environmentGroupRelation.getEnvironmentId());
});
// 获取环境的配置信息
List<String> distinctEnvIds = envIds.stream().distinct().toList();
Map<String, EnvironmentInfoDTO> envMap = environmentService.getByIds(distinctEnvIds)
.stream()
.collect(Collectors.toMap(EnvironmentInfoDTO::getId, Function.identity()));
envInfo.setEnvGroupMap(envGroupMap);
envInfo.setEnvMap(envMap);
envMap.forEach((envId, envInfoDTO) -> handleHttpModuleMatchRule(envInfoDTO));
return envInfo;
}
/**
* 处理环境的 HTTP 配置模块匹配规则
* 查询新增子模块
*
* @param envInfoDTO
*/
private void handleHttpModuleMatchRule(EnvironmentInfoDTO envInfoDTO) {
List<HttpConfig> httpConfigs = envInfoDTO.getConfig().getHttpConfig();
for (HttpConfig httpConfig : httpConfigs) {
if (!httpConfig.isModuleMatchRule()) {
continue;
}
// 获取勾选了包含子模块的模块ID
HttpConfigModuleMatchRule moduleMatchRule = httpConfig.getModuleMatchRule();
List<SelectModule> selectModules = moduleMatchRule.getModules();
List<String> containChildModuleIds = selectModules.stream()
.filter(SelectModule::getContainChildModule)
.map(SelectModule::getModuleId)
.toList();
// 查询子模块ID, 并去重
Set<String> moduleIds = apiDefinitionModuleService.getModuleIdsByParentIds(containChildModuleIds)
.stream()
.collect(Collectors.toSet());
selectModules.forEach(selectModule -> moduleIds.add(selectModule.getModuleId()));
// 重新设置选中的模块ID
moduleMatchRule.setModules(null);
List<SelectModule> allSelectModules = moduleIds.stream().map(moduleId -> {
SelectModule module = new SelectModule();
module.setModuleId(moduleId);
return module;
}).collect(Collectors.toList());
moduleMatchRule.setModules(allSelectModules);
}
}
private List<ApiScenario> getApiScenarioByIds(List<String> apiScenarioIds) {
ApiScenarioExample example = new ApiScenarioExample();
example.createCriteria().andIdIn(apiScenarioIds);
return apiScenarioMapper.selectByExample(example);
}
/**
* 将步骤解析成 MsTestElement 树结构
*/
private void parseStep2MsElement(AbstractMsTestElement parentElement,
List<? extends ApiScenarioStepCommonDTO> steps,
ApiScenarioParseTmpParam parseParam,
String scenarioId) {
if (CollectionUtils.isNotEmpty(steps)) {
parentElement.setChildren(new LinkedList<>());
}
Map<String, String> stepDetailMap = parseParam.getStepDetailMap();
Map<String, String> resourceDetailMap = parseParam.getResourceDetailMap();
Map<String, List<MsHTTPElement>> stepTypeHttpElementMap = parseParam.getStepTypeHttpElementMap();
for (ApiScenarioStepCommonDTO step : steps) {
StepParser stepParser = StepParserFactory.getStepParser(step.getStepType());
if (BooleanUtils.isFalse(step.getEnable())) {
continue;
}
apiScenarioService.setPartialRefStepEnable(step, stepDetailMap);
if (apiScenarioService.isRequestStep(step) && BooleanUtils.isTrue(step.getEnable())) {
// 记录待执行的请求总数
parseParam.getRequestCount().getAndIncrement();
}
if (StringUtils.isBlank(step.getUniqueId())) {
// 如果调试的时候前端没有传步骤唯一ID则生成唯一ID
step.setUniqueId(IDGenerator.nextStr());
}
parseParam.getUniqueIdStepMap().put(step.getUniqueId(), step);
// 将步骤详情解析生成对应的MsTestElement
AbstractMsTestElement msTestElement = stepParser.parseTestElement(step,
MapUtils.isNotEmpty(resourceDetailMap) ? resourceDetailMap.getOrDefault(step.getResourceId(), StringUtils.EMPTY) : StringUtils.EMPTY, stepDetailMap.get(step.getId()));
if (msTestElement != null) {
if (msTestElement instanceof MsHTTPElement msHTTPElement) {
// 暂存http类型的步骤
stepTypeHttpElementMap.putIfAbsent(step.getStepType(), new LinkedList<>());
stepTypeHttpElementMap.get(step.getStepType()).add(msHTTPElement);
} else if (msTestElement instanceof MsScriptElement msScriptElement) {
parseParam.getScriptElements().add(msScriptElement);
}
msTestElement.setProjectId(step.getProjectId());
msTestElement.setResourceId(step.getResourceId());
msTestElement.setName(step.getName());
// 步骤ID设置为唯一ID
msTestElement.setStepId(step.getUniqueId());
msTestElement.setCsvIds(step.getCsvIds());
// 记录引用的资源ID和项目ID下载执行文件时需要使用
parseParam.getRefProjectIds().add(step.getProjectId());
if (apiScenarioService.isRefOrPartialRef(step.getRefType())) {
// 引用的步骤记录引用的资源ID
parseParam.getFileResourceIds().add(step.getResourceId());
} else if (msTestElement instanceof MsHTTPElement) {
// 非引用的步骤记录步骤ID
parseParam.getFileResourceIds().add(step.getId());
parseParam.getFileStepScenarioMap().put(step.getId(), scenarioId);
}
// 设置环境等运行时场景参数
setMsScenarioParam(parseParam.getScenarioParseEnvInfo(), step, msTestElement);
// 记录 msCommonElement
Optional.ofNullable(apiCommonService.getMsCommonElement(msTestElement))
.ifPresent(msCommonElement -> parseParam.getCommonElements().add(msCommonElement));
// 组装树结构
parentElement.getChildren().add(msTestElement);
if (CollectionUtils.isNotEmpty(step.getChildren())) {
if (apiScenarioService.isScenarioStep(step.getStepType()) && apiScenarioService.isRefOrPartialRef(step.getRefType())) {
scenarioId = step.getResourceId();
}
parseStep2MsElement(msTestElement, step.getChildren(), parseParam, scenarioId);
}
}
}
}
/**
* 设置运行时场景参数
*
* @param scenarioParseEnvInfo
* @param step
* @param msTestElement
*/
private void setMsScenarioParam(ApiScenarioParseEnvInfo scenarioParseEnvInfo,
ApiScenarioStepCommonDTO step,
AbstractMsTestElement msTestElement) {
// 引用的场景设置场景参数
if (!apiScenarioService.isScenarioStep(step.getStepType()) || !apiScenarioService.isRefOrPartialRef(step.getRefType())
|| !(msTestElement instanceof MsScenario msScenario)) {
return;
}
if (step.getConfig() != null) {
// 设置场景步骤的运行参数
msScenario.setScenarioStepConfig(JSON.parseObject(JSON.toJSONString(step.getConfig()), ScenarioStepConfig.class));
}
// 获取当前场景配置的环境信息
EnvironmentModeDTO environmentModeDTO = scenarioParseEnvInfo.getRefScenarioEnvMap().get(step.getResourceId());
if (environmentModeDTO != null) {
String environmentId = environmentModeDTO.getEnvironmentId();
// 设置是否是环境组
Boolean isGrouped = environmentModeDTO.getGrouped();
msScenario.setGrouped(isGrouped);
Map<String, EnvironmentInfoDTO> envMap = scenarioParseEnvInfo.getEnvMap();
if (BooleanUtils.isTrue(isGrouped)) {
// 设置环境组 map
msScenario.setProjectEnvMap(getProjectEnvMap(scenarioParseEnvInfo, environmentId));
} else {
// 设置环境
msScenario.setEnvironmentInfo(envMap.get(environmentId));
}
}
}
/**
* scenarioParseEnvInfo 获取对应环境组的 projectEnvMap
*
* @param scenarioParseEnvInfo 环境信息
* @param environmentId 环境ID
* @return projectEnvMap
*/
private Map<String, EnvironmentInfoDTO> getProjectEnvMap(ApiScenarioParseEnvInfo scenarioParseEnvInfo, String environmentId) {
Map<String, List<String>> envGroupMap = scenarioParseEnvInfo.getEnvGroupMap();
List<String> envIds = envGroupMap.get(environmentId);
Map<String, EnvironmentInfoDTO> projectEnvMap = new HashMap<>();
for (String envId : envIds) {
EnvironmentInfoDTO environmentInfoDTO = scenarioParseEnvInfo.getEnvMap().get(envId);
projectEnvMap.put(environmentInfoDTO.getProjectId(), environmentInfoDTO);
}
return projectEnvMap;
}
}

View File

@ -6,11 +6,9 @@ import io.metersphere.api.constants.ApiScenarioStepType;
import io.metersphere.api.domain.*;
import io.metersphere.api.dto.*;
import io.metersphere.api.dto.debug.ApiFileResourceUpdateRequest;
import io.metersphere.api.dto.debug.ApiResourceRunRequest;
import io.metersphere.api.dto.definition.ExecutePageRequest;
import io.metersphere.api.dto.definition.ExecuteReportDTO;
import io.metersphere.api.dto.request.ApiTransferRequest;
import io.metersphere.api.dto.request.MsScenario;
import io.metersphere.api.dto.request.controller.MsScriptElement;
import io.metersphere.api.dto.request.http.MsHTTPElement;
import io.metersphere.api.dto.response.ApiScenarioBatchOperationResponse;
@ -20,31 +18,23 @@ import io.metersphere.api.mapper.*;
import io.metersphere.api.parser.step.StepParser;
import io.metersphere.api.parser.step.StepParserFactory;
import io.metersphere.api.service.ApiCommonService;
import io.metersphere.api.service.ApiExecuteService;
import io.metersphere.api.service.ApiFileResourceService;
import io.metersphere.api.service.definition.ApiDefinitionModuleService;
import io.metersphere.api.service.definition.ApiDefinitionService;
import io.metersphere.api.service.definition.ApiTestCaseService;
import io.metersphere.api.service.queue.ApiExecutionSetService;
import io.metersphere.api.utils.ApiDataUtils;
import io.metersphere.api.utils.ApiScenarioBatchOperationUtils;
import io.metersphere.functional.domain.FunctionalCaseTestExample;
import io.metersphere.functional.mapper.FunctionalCaseTestMapper;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.project.api.processor.MsProcessor;
import io.metersphere.project.api.processor.TimeWaitingProcessor;
import io.metersphere.project.domain.FileAssociation;
import io.metersphere.project.domain.FileMetadata;
import io.metersphere.project.domain.Project;
import io.metersphere.project.domain.ProjectExample;
import io.metersphere.project.dto.MoveNodeSortDTO;
import io.metersphere.project.dto.environment.EnvironmentInfoDTO;
import io.metersphere.project.dto.environment.http.HttpConfig;
import io.metersphere.project.dto.environment.http.HttpConfigModuleMatchRule;
import io.metersphere.project.dto.environment.http.SelectModule;
import io.metersphere.project.mapper.ExtBaseProjectVersionMapper;
import io.metersphere.project.mapper.ProjectMapper;
import io.metersphere.project.service.EnvironmentGroupService;
import io.metersphere.project.service.EnvironmentService;
import io.metersphere.project.service.MoveNodeService;
import io.metersphere.sdk.constants.*;
import io.metersphere.sdk.domain.Environment;
@ -52,7 +42,6 @@ import io.metersphere.sdk.domain.EnvironmentExample;
import io.metersphere.sdk.domain.EnvironmentGroup;
import io.metersphere.sdk.domain.EnvironmentGroupExample;
import io.metersphere.sdk.dto.api.task.ApiRunModeConfigDTO;
import io.metersphere.sdk.dto.api.task.TaskRequestDTO;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.file.FileCopyRequest;
import io.metersphere.sdk.file.MinioRepository;
@ -72,7 +61,6 @@ import io.metersphere.system.log.constants.OperationLogType;
import io.metersphere.system.mapper.ScheduleMapper;
import io.metersphere.system.notice.constants.NoticeConstants;
import io.metersphere.system.schedule.ScheduleService;
import io.metersphere.system.service.ApiPluginService;
import io.metersphere.system.service.OperationHistoryService;
import io.metersphere.system.service.UserLoginService;
import io.metersphere.system.uid.IDGenerator;
@ -149,8 +137,6 @@ public class ApiScenarioService extends MoveNodeService {
@Resource
private ApiScenarioBlobMapper apiScenarioBlobMapper;
@Resource
private ApiExecuteService apiExecuteService;
@Resource
private ApiDefinitionService apiDefinitionService;
@Resource
private ApiTestCaseService apiTestCaseService;
@ -163,12 +149,6 @@ public class ApiScenarioService extends MoveNodeService {
@Resource
private ScheduleMapper scheduleMapper;
@Resource
private EnvironmentService environmentService;
@Resource
private EnvironmentGroupService environmentGroupService;
@Resource
private ApiPluginService apiPluginService;
@Resource
private ProjectMapper projectMapper;
@Resource
private ExtApiDefinitionMapper extApiDefinitionMapper;
@ -179,14 +159,10 @@ public class ApiScenarioService extends MoveNodeService {
@Resource
private ExtApiTestCaseMapper extApiTestCaseMapper;
@Resource
private ApiDefinitionModuleService apiDefinitionModuleService;
@Resource
private OperationHistoryService operationHistoryService;
@Resource
private ApiCommonService apiCommonService;
@Resource
private ApiScenarioReportService apiScenarioReportService;
@Resource
private ApiScenarioReportMapper apiScenarioReportMapper;
@Resource
private ApiScenarioNoticeService apiScenarioNoticeService;
@ -930,7 +906,7 @@ public class ApiScenarioService extends MoveNodeService {
return StringUtils.equals(refType, ApiScenarioStepRefType.PARTIAL_REF.name());
}
private boolean isRefOrPartialRef(String refType) {
public boolean isRefOrPartialRef(String refType) {
return StringUtils.equalsAny(refType, ApiScenarioStepRefType.REF.name(), ApiScenarioStepRefType.PARTIAL_REF.name());
}
@ -1005,7 +981,7 @@ public class ApiScenarioService extends MoveNodeService {
* copyFromStepId detail
* isNew 的资源的 detail
*/
private void addSpecialStepDetails(List<ApiScenarioStepRequest> steps, Map<String, Object> stepDetails) {
public void addSpecialStepDetails(List<ApiScenarioStepRequest> steps, Map<String, Object> stepDetails) {
if (CollectionUtils.isEmpty(steps)) {
return;
}
@ -1338,625 +1314,19 @@ public class ApiScenarioService extends MoveNodeService {
return ServiceUtils.checkResourceExist(apiScenarioMapper.selectByPrimaryKey(id), "permission.system_api_scenario.name");
}
public TaskRequestDTO debug(ApiScenarioDebugRequest request) {
ApiScenario apiScenario = apiScenarioMapper.selectByPrimaryKey(request.getId());
boolean hasSave = apiScenario != null;
// 解析生成待执行的场景树
MsScenario msScenario = new MsScenario();
msScenario.setRefType(ApiScenarioStepRefType.DIRECT.name());
msScenario.setScenarioConfig(getScenarioConfig(request, hasSave));
msScenario.setProjectId(request.getProjectId());
msScenario.setResourceId(request.getId());
// 处理特殊的步骤详情
addSpecialStepDetails(request.getSteps(), request.getStepDetails());
ApiScenarioParseTmpParam tmpParam = parse(msScenario, request.getSteps(), request);
ApiResourceRunRequest runRequest = getApiResourceRunRequest(msScenario, tmpParam);
runRequest = setFileParam(request, runRequest);
TaskRequestDTO taskRequest = getTaskRequest(request.getReportId(), request.getId(), request.getProjectId(),
apiExecuteService.getDebugRunModule(request.getFrontendDebug()));
taskRequest.setSaveResult(false);
taskRequest.setRealTime(true);
taskRequest.setRequestCount(tmpParam.getRequestCount().get());
ApiScenarioParamConfig parseConfig = getApiScenarioParamConfig(request, tmpParam.getScenarioParseEnvInfo());
parseConfig.setReportId(request.getReportId());
return apiExecuteService.execute(runRequest, taskRequest, parseConfig);
}
/**
* 执行时设置临时文件的相关参数
*
* @param request
* @param runRequest
*/
private ApiResourceRunRequest setFileParam(ApiScenarioDebugRequest request, ApiResourceRunRequest runRequest) {
runRequest.getLinkFileIds().addAll(getLinkFileIds(request.getFileParam()));
runRequest.getUploadFileIds().addAll(getUploadFileIds(request.getFileParam()));
Map<String, ResourceAddFileParam> stepFileParam = request.getStepFileParam();
if (MapUtils.isNotEmpty(stepFileParam)) {
stepFileParam.values().forEach(fileParam -> {
runRequest.getLinkFileIds().addAll(getLinkFileIds(fileParam));
runRequest.getUploadFileIds().addAll(getUploadFileIds(fileParam));
});
}
return runRequest;
}
public List<String> getLinkFileIds(ResourceAddFileParam fileParam) {
if (fileParam != null && CollectionUtils.isNotEmpty(fileParam.getLinkFileIds())) {
return fileParam.getLinkFileIds();
}
return List.of();
}
public List<String> getUploadFileIds(ResourceAddFileParam fileParam) {
if (fileParam != null && CollectionUtils.isNotEmpty(fileParam.getUploadFileIds())) {
return fileParam.getUploadFileIds();
}
return List.of();
}
public TaskRequestDTO run(String id, String reportId, String userId) {
ApiScenarioDetail apiScenarioDetail = getForRun(id);
// 解析生成待执行的场景树
MsScenario msScenario = getMsScenario(apiScenarioDetail);
ApiScenarioParseParam parseParam = getApiScenarioParseParam(apiScenarioDetail);
return executeRun(apiScenarioDetail, msScenario, apiScenarioDetail.getSteps(), parseParam, new ApiResourceRunRequest(), reportId, userId);
}
public ApiScenarioParseParam getApiScenarioParseParam(ApiScenarioDetail apiScenarioDetail) {
ApiScenarioParseParam parseParam = new ApiScenarioParseParam();
parseParam.setScenarioConfig(apiScenarioDetail.getScenarioConfig());
parseParam.setStepDetails(Map.of());
parseParam.setEnvironmentId(apiScenarioDetail.getEnvironmentId());
parseParam.setGrouped(apiScenarioDetail.getGrouped());
return parseParam;
}
public MsScenario getMsScenario(ApiScenarioDetail apiScenarioDetail) {
MsScenario msScenario = new MsScenario();
msScenario.setRefType(ApiScenarioStepRefType.DIRECT.name());
msScenario.setScenarioConfig(apiScenarioDetail.getScenarioConfig());
msScenario.setProjectId(apiScenarioDetail.getProjectId());
return msScenario;
}
public TaskRequestDTO run(ApiScenarioDebugRequest request, String userId) {
ApiScenario apiScenario = apiScenarioMapper.selectByPrimaryKey(request.getId());
// 解析生成待执行的场景树
MsScenario msScenario = new MsScenario();
msScenario.setRefType(ApiScenarioStepRefType.DIRECT.name());
msScenario.setScenarioConfig(getScenarioConfig(request, true));
msScenario.setProjectId(request.getProjectId());
// 处理特殊的步骤详情
addSpecialStepDetails(request.getSteps(), request.getStepDetails());
ApiResourceRunRequest runRequest = new ApiResourceRunRequest();
runRequest = setFileParam(request, runRequest);
return executeRun(apiScenario, msScenario, request.getSteps(), request, runRequest, request.getReportId(), userId);
}
public TaskRequestDTO executeRun(ApiScenario apiScenario,
MsScenario msScenario,
List<? extends ApiScenarioStepCommonDTO> steps,
ApiScenarioParseParam parseParam,
ApiResourceRunRequest runRequest,
String reportId,
String userId) {
msScenario.setResourceId(apiScenario.getId());
// 解析生成场景树并保存临时变量
ApiScenarioParseTmpParam tmpParam = parse(msScenario, steps, parseParam);
runRequest = setApiResourceRunRequestParam(msScenario, tmpParam, runRequest);
String poolId = apiExecuteService.getProjectApiResourcePoolId(apiScenario.getProjectId());
TaskRequestDTO taskRequest = getTaskRequest(reportId, apiScenario.getId(), apiScenario.getProjectId(), ApiExecuteRunMode.RUN.name());
taskRequest.getRunModeConfig().setPoolId(poolId);
taskRequest.setSaveResult(true);
taskRequest.getRunModeConfig().setEnvironmentId(parseParam.getEnvironmentId());
taskRequest.setRequestCount(tmpParam.getRequestCount().get());
if (StringUtils.isEmpty(taskRequest.getReportId())) {
taskRequest.setRealTime(false);
reportId = IDGenerator.nextStr();
taskRequest.setReportId(reportId);
} else {
// 如果传了报告ID则实时获取结果
taskRequest.setRealTime(true);
}
ApiScenarioParamConfig parseConfig = getApiScenarioParamConfig(parseParam, tmpParam.getScenarioParseEnvInfo());
parseConfig.setReportId(reportId);
// 初始化报告
ApiScenarioReport scenarioReport = getScenarioReport(userId);
scenarioReport.setId(reportId);
scenarioReport.setTriggerMode(TaskTriggerMode.MANUAL.name());
scenarioReport.setRunMode(ApiBatchRunMode.PARALLEL.name());
scenarioReport.setPoolId(poolId);
scenarioReport.setEnvironmentId(parseParam.getEnvironmentId());
scenarioReport.setWaitingTime(getGlobalWaitTime(parseParam.getScenarioConfig()));
initApiReport(apiScenario, scenarioReport);
// 初始化报告步骤
initScenarioReportSteps(steps, taskRequest.getReportId());
return apiExecuteService.execute(runRequest, taskRequest, parseConfig);
}
public boolean isRequestStep(ApiScenarioStepCommonDTO step) {
ApiScenarioStepType scenarioStepType = EnumValidator.validateEnum(ApiScenarioStepType.class, step.getStepType());
return scenarioStepType == null ? false : scenarioStepType.isRequest();
}
/**
* 预生成用例的执行报告
*
* @param apiScenario
* @return
*/
public ApiScenarioRecord initApiReport(ApiScenario apiScenario, ApiScenarioReport scenarioReport) {
// 初始化报告
scenarioReport.setName(apiScenario.getName() + "_" + DateUtils.getTimeString(System.currentTimeMillis()));
scenarioReport.setProjectId(apiScenario.getProjectId());
// 创建报告和用例的关联关系
ApiScenarioRecord scenarioRecord = getApiScenarioRecord(apiScenario, scenarioReport);
apiScenarioReportService.insertApiScenarioReport(List.of(scenarioReport), List.of(scenarioRecord));
return scenarioRecord;
}
/**
* 初始化场景报告步骤
*
* @param steps
* @param reportId
*/
public void initScenarioReportSteps(List<? extends ApiScenarioStepCommonDTO> steps, String reportId) {
initScenarioReportSteps(null, steps, reportId);
}
public void initScenarioReportSteps(String parentId, List<? extends ApiScenarioStepCommonDTO> steps, String reportId) {
List<ApiScenarioReportStep> scenarioReportSteps = getScenarioReportSteps(parentId, steps, reportId);
apiScenarioReportService.insertApiScenarioReportStep(scenarioReportSteps);
}
/**
* 获取场景报告步骤
*
* @param steps
* @param reportId
*/
public List<ApiScenarioReportStep> getScenarioReportSteps(String parentId, List<? extends ApiScenarioStepCommonDTO> steps, String reportId) {
AtomicLong sort = new AtomicLong(1);
List<ApiScenarioReportStep> scenarioReportSteps = new ArrayList<>();
for (ApiScenarioStepCommonDTO step : steps) {
if (StringUtils.isBlank(step.getUniqueId())) {
// 如果没有步骤唯一ID则生成唯一ID
step.setUniqueId(IDGenerator.nextStr());
}
ApiScenarioReportStep scenarioReportStep = getScenarioReportStep(step, reportId, sort.getAndIncrement());
scenarioReportStep.setParentId(parentId);
scenarioReportSteps.add(scenarioReportStep);
List<? extends ApiScenarioStepCommonDTO> children = step.getChildren();
if (CollectionUtils.isNotEmpty(children)) {
scenarioReportSteps.addAll(getScenarioReportSteps(step.getUniqueId(), children, reportId));
}
}
return scenarioReportSteps;
}
private ApiScenarioReportStep getScenarioReportStep(ApiScenarioStepCommonDTO step, String reportId, long sort) {
ApiScenarioReportStep scenarioReportStep = new ApiScenarioReportStep();
scenarioReportStep.setReportId(reportId);
scenarioReportStep.setStepId(step.getUniqueId());
scenarioReportStep.setSort(sort);
scenarioReportStep.setName(step.getName());
scenarioReportStep.setStepType(step.getStepType());
return scenarioReportStep;
}
public ApiScenarioRecord getApiScenarioRecord(ApiScenario apiScenario, ApiScenarioReport scenarioReport) {
ApiScenarioRecord scenarioRecord = new ApiScenarioRecord();
scenarioRecord.setApiScenarioId(apiScenario.getId());
scenarioRecord.setApiScenarioReportId(scenarioReport.getId());
return scenarioRecord;
}
public ApiScenarioReport getScenarioReport(String userId) {
ApiScenarioReport scenarioReport = new ApiScenarioReport();
scenarioReport.setId(IDGenerator.nextStr());
scenarioReport.setDeleted(false);
scenarioReport.setIntegrated(false);
scenarioReport.setExecStatus(ExecStatus.PENDING.name());
scenarioReport.setStartTime(System.currentTimeMillis());
scenarioReport.setUpdateTime(System.currentTimeMillis());
scenarioReport.setUpdateUser(userId);
scenarioReport.setCreateUser(userId);
return scenarioReport;
}
public ApiScenarioParamConfig getApiScenarioParamConfig(ApiScenarioParseParam request, ApiScenarioParseEnvInfo scenarioParseEnvInfo) {
ApiScenarioParamConfig parseConfig = new ApiScenarioParamConfig();
parseConfig.setTestElementClassPluginIdMap(apiPluginService.getTestElementPluginMap());
parseConfig.setTestElementClassProtocolMap(apiPluginService.getTestElementProtocolMap());
parseConfig.setGrouped(request.getGrouped());
parseConfig.setRootScenarioConfig(request.getScenarioConfig());
if (BooleanUtils.isTrue(request.getGrouped())) {
// 设置环境组 map
parseConfig.setProjectEnvMap(getProjectEnvMap(scenarioParseEnvInfo, request.getEnvironmentId()));
} else {
// 设置环境
parseConfig.setEnvConfig(scenarioParseEnvInfo.getEnvMap().get(request.getEnvironmentId()));
}
return parseConfig;
}
public ApiResourceRunRequest getApiResourceRunRequest(MsScenario msScenario, ApiScenarioParseTmpParam tmpParam) {
ApiResourceRunRequest runRequest = new ApiResourceRunRequest();
return setApiResourceRunRequestParam(msScenario, tmpParam, runRequest);
}
private ApiResourceRunRequest setApiResourceRunRequestParam(MsScenario msScenario, ApiScenarioParseTmpParam tmpParam, ApiResourceRunRequest runRequest) {
runRequest.setFileResourceIds(tmpParam.getFileResourceIds());
runRequest.setFileStepScenarioMap(tmpParam.getFileStepScenarioMap());
runRequest.setRefProjectIds(tmpParam.getRefProjectIds());
runRequest.setTestElement(msScenario);
return runRequest;
}
/**
* 将步骤转换成场景树
* 并保存临时变量
*
* @param msScenario
* @param steps
* @param parseParam
* @return
*/
public ApiScenarioParseTmpParam parse(MsScenario msScenario,
List<? extends ApiScenarioStepCommonDTO> steps,
ApiScenarioParseParam parseParam) {
// 记录引用的资源ID
Map<String, List<String>> refResourceMap = new HashMap<>();
buildRefResourceIdMap(steps, refResourceMap);
ApiScenarioParseTmpParam tmpParam = new ApiScenarioParseTmpParam();
// 查询引用的资源详情
tmpParam.setResourceDetailMap(getResourceDetailMap(refResourceMap));
// 查询复制的步骤详情
tmpParam.setStepDetailMap(getStepDetailMap(steps, parseParam.getStepDetails()));
// 获取场景环境相关配置
tmpParam.setScenarioParseEnvInfo(getScenarioParseEnvInfo(refResourceMap, parseParam.getEnvironmentId(), parseParam.getGrouped()));
parseStep2MsElement(msScenario, steps, tmpParam, msScenario.getResourceId());
// 设置 HttpElement 的模块信息
setApiDefinitionExecuteInfo(tmpParam.getUniqueIdStepMap(), tmpParam.getStepTypeHttpElementMap());
// 设置使用脚本前后置的公共脚本信息
apiCommonService.setCommonElementEnableCommonScriptInfo(tmpParam.getCommonElements());
apiCommonService.setScriptElementEnableCommonScriptInfo(tmpParam.getScriptElements());
return tmpParam;
}
public TaskRequestDTO getTaskRequest(String reportId, String resourceId, String projectId, String runModule) {
TaskRequestDTO taskRequest = apiExecuteService.getTaskRequest(reportId, resourceId, projectId);
taskRequest.setResourceType(ApiResourceType.API_SCENARIO.name());
taskRequest.setRunMode(runModule);
return taskRequest;
}
/**
* 设置 HttpElement 的模块信息
* 用户环境中的模块过滤
*
* @param uniqueIdStepMap
* @param stepTypeHttpElementMap
*/
private void setApiDefinitionExecuteInfo(Map<String, ApiScenarioStepCommonDTO> uniqueIdStepMap, Map<String, List<MsHTTPElement>> stepTypeHttpElementMap) {
setApiDefinitionExecuteInfo(uniqueIdStepMap, stepTypeHttpElementMap.get(ApiScenarioStepType.API.name()), apiDefinitionService::getModuleInfoByIds);
setApiDefinitionExecuteInfo(uniqueIdStepMap, stepTypeHttpElementMap.get(ApiScenarioStepType.API_CASE.name()), apiTestCaseService::getModuleInfoByIds);
}
/**
* 设置 MsHTTPElement 中的 method 等信息
*
* @param httpElements
* @param getDefinitionInfoFunc
*/
public void setApiDefinitionExecuteInfo(Map<String, ApiScenarioStepCommonDTO> uniqueIdStepMap, List<MsHTTPElement> httpElements, Function<List<String>, List<ApiDefinitionExecuteInfo>> getDefinitionInfoFunc) {
if (org.apache.commons.collections.CollectionUtils.isNotEmpty(httpElements)) {
List<String> resourceIds = httpElements.stream().map(MsHTTPElement::getResourceId).collect(Collectors.toList());
// 获取接口模块信息
Map<String, ApiDefinitionExecuteInfo> resourceModuleMap = apiCommonService.getApiDefinitionExecuteInfoMap(getDefinitionInfoFunc, resourceIds);
httpElements.forEach(httpElement -> {
ApiDefinitionExecuteInfo definitionExecuteInfo = resourceModuleMap.get(httpElement.getResourceId());
String path = httpElement.getPath();
String method = httpElement.getMethod();
// httpElement 设置模块,请求方法等信息
apiCommonService.setApiDefinitionExecuteInfo(httpElement, definitionExecuteInfo);
ApiScenarioStepCommonDTO step = uniqueIdStepMap.get(httpElement.getStepId());
if (step != null && isCopyApi(step.getStepType(), step.getRefType())) {
// 复制的接口定义不使用源接口定义的path和method
httpElement.setPath(path);
httpElement.setMethod(method);
}
});
}
}
/**
* 设置脚本解析-环境相关参数
*/
private ApiScenarioParseEnvInfo getScenarioParseEnvInfo(Map<String, List<String>> refResourceMap, String currentEnvId, Boolean isCurrentEnvGrouped) {
List<String> apiScenarioIds = refResourceMap.get(ApiScenarioStepType.API_SCENARIO.name());
List<String> envIds = new ArrayList<>();
List<String> envGroupIds = new ArrayList<>();
ApiScenarioParseEnvInfo envInfo = new ApiScenarioParseEnvInfo();
if (BooleanUtils.isTrue(isCurrentEnvGrouped)) {
envGroupIds.add(currentEnvId);
} else {
envIds.add(currentEnvId);
}
if (CollectionUtils.isNotEmpty(apiScenarioIds)) {
Map<String, EnvironmentModeDTO> refScenarioEnvMap = new HashMap<>();
List<ApiScenario> apiScenarios = getApiScenarioByIds(apiScenarioIds);
for (ApiScenario scenario : apiScenarios) {
EnvironmentModeDTO envMode = new EnvironmentModeDTO();
envMode.setEnvironmentId(scenario.getEnvironmentId());
envMode.setGrouped(scenario.getGrouped());
if (BooleanUtils.isTrue(scenario.getGrouped())) {
// 记录环境组ID
envGroupIds.add(scenario.getEnvironmentId());
} else {
// 记录环境ID
envIds.add(scenario.getEnvironmentId());
}
// 保存场景的环境配置信息
refScenarioEnvMap.put(scenario.getId(), envMode);
}
envInfo.setRefScenarioEnvMap(refScenarioEnvMap);
}
// 查询环境组中的环境ID列表
Map<String, List<String>> envGroupMap = new HashMap<>();
environmentGroupService.getEnvironmentGroupRelations(envGroupIds).forEach(environmentGroupRelation -> {
envGroupMap.putIfAbsent(environmentGroupRelation.getEnvironmentGroupId(), new ArrayList<>());
envGroupMap.get(environmentGroupRelation.getEnvironmentGroupId()).add(environmentGroupRelation.getEnvironmentId());
envIds.add(environmentGroupRelation.getEnvironmentId());
});
// 获取环境的配置信息
List<String> distinctEnvIds = envIds.stream().distinct().toList();
Map<String, EnvironmentInfoDTO> envMap = environmentService.getByIds(distinctEnvIds)
.stream()
.collect(Collectors.toMap(EnvironmentInfoDTO::getId, Function.identity()));
envInfo.setEnvGroupMap(envGroupMap);
envInfo.setEnvMap(envMap);
envMap.forEach((envId, envInfoDTO) -> handleHttpModuleMatchRule(envInfoDTO));
return envInfo;
}
/**
* 处理环境的 HTTP 配置模块匹配规则
* 查询新增子模块
*
* @param envInfoDTO
*/
private void handleHttpModuleMatchRule(EnvironmentInfoDTO envInfoDTO) {
List<HttpConfig> httpConfigs = envInfoDTO.getConfig().getHttpConfig();
for (HttpConfig httpConfig : httpConfigs) {
if (!httpConfig.isModuleMatchRule()) {
continue;
}
// 获取勾选了包含子模块的模块ID
HttpConfigModuleMatchRule moduleMatchRule = httpConfig.getModuleMatchRule();
List<SelectModule> selectModules = moduleMatchRule.getModules();
List<String> containChildModuleIds = selectModules.stream()
.filter(SelectModule::getContainChildModule)
.map(SelectModule::getModuleId)
.toList();
// 查询子模块ID, 并去重
Set<String> moduleIds = apiDefinitionModuleService.getModuleIdsByParentIds(containChildModuleIds)
.stream()
.collect(Collectors.toSet());
selectModules.forEach(selectModule -> moduleIds.add(selectModule.getModuleId()));
// 重新设置选中的模块ID
moduleMatchRule.setModules(null);
List<SelectModule> allSelectModules = moduleIds.stream().map(moduleId -> {
SelectModule module = new SelectModule();
module.setModuleId(moduleId);
return module;
}).collect(Collectors.toList());
moduleMatchRule.setModules(allSelectModules);
}
}
private List<ApiScenario> getApiScenarioByIds(List<String> apiScenarioIds) {
ApiScenarioExample example = new ApiScenarioExample();
example.createCriteria().andIdIn(apiScenarioIds);
return apiScenarioMapper.selectByExample(example);
}
/**
* 将步骤解析成 MsTestElement 树结构
*/
private void parseStep2MsElement(AbstractMsTestElement parentElement,
List<? extends ApiScenarioStepCommonDTO> steps,
ApiScenarioParseTmpParam parseParam,
String scenarioId) {
if (CollectionUtils.isNotEmpty(steps)) {
parentElement.setChildren(new LinkedList<>());
}
Map<String, String> stepDetailMap = parseParam.getStepDetailMap();
Map<String, String> resourceDetailMap = parseParam.getResourceDetailMap();
Map<String, List<MsHTTPElement>> stepTypeHttpElementMap = parseParam.getStepTypeHttpElementMap();
for (ApiScenarioStepCommonDTO step : steps) {
StepParser stepParser = StepParserFactory.getStepParser(step.getStepType());
if (BooleanUtils.isFalse(step.getEnable())) {
continue;
}
setPartialRefStepEnable(step, stepDetailMap);
if (isRequestStep(step) && BooleanUtils.isTrue(step.getEnable())) {
// 记录待执行的请求总数
parseParam.getRequestCount().getAndIncrement();
}
if (StringUtils.isBlank(step.getUniqueId())) {
// 如果调试的时候前端没有传步骤唯一ID则生成唯一ID
step.setUniqueId(IDGenerator.nextStr());
}
parseParam.getUniqueIdStepMap().put(step.getUniqueId(), step);
// 将步骤详情解析生成对应的MsTestElement
AbstractMsTestElement msTestElement = stepParser.parseTestElement(step,
MapUtils.isNotEmpty(resourceDetailMap) ? resourceDetailMap.getOrDefault(step.getResourceId(), StringUtils.EMPTY) : StringUtils.EMPTY, stepDetailMap.get(step.getId()));
if (msTestElement != null) {
if (msTestElement instanceof MsHTTPElement msHTTPElement) {
// 暂存http类型的步骤
stepTypeHttpElementMap.putIfAbsent(step.getStepType(), new LinkedList<>());
stepTypeHttpElementMap.get(step.getStepType()).add(msHTTPElement);
} else if (msTestElement instanceof MsScriptElement msScriptElement) {
parseParam.getScriptElements().add(msScriptElement);
}
msTestElement.setProjectId(step.getProjectId());
msTestElement.setResourceId(step.getResourceId());
msTestElement.setName(step.getName());
// 步骤ID设置为唯一ID
msTestElement.setStepId(step.getUniqueId());
msTestElement.setCsvIds(step.getCsvIds());
// 记录引用的资源ID和项目ID下载执行文件时需要使用
parseParam.getRefProjectIds().add(step.getProjectId());
if (isRefOrPartialRef(step.getRefType())) {
// 引用的步骤记录引用的资源ID
parseParam.getFileResourceIds().add(step.getResourceId());
} else if (msTestElement instanceof MsHTTPElement) {
// 非引用的步骤记录步骤ID
parseParam.getFileResourceIds().add(step.getId());
parseParam.getFileStepScenarioMap().put(step.getId(), scenarioId);
}
// 设置环境等运行时场景参数
setMsScenarioParam(parseParam.getScenarioParseEnvInfo(), step, msTestElement);
// 记录 msCommonElement
Optional.ofNullable(apiCommonService.getMsCommonElement(msTestElement))
.ifPresent(msCommonElement -> parseParam.getCommonElements().add(msCommonElement));
// 组装树结构
parentElement.getChildren().add(msTestElement);
if (CollectionUtils.isNotEmpty(step.getChildren())) {
if (isScenarioStep(step.getStepType()) && isRefOrPartialRef(step.getRefType())) {
scenarioId = step.getResourceId();
}
parseStep2MsElement(msTestElement, step.getChildren(), parseParam, scenarioId);
}
}
}
}
/**
* 设置运行时场景参数
*
* @param scenarioParseEnvInfo
* @param step
* @param msTestElement
*/
private void setMsScenarioParam(ApiScenarioParseEnvInfo scenarioParseEnvInfo,
ApiScenarioStepCommonDTO step,
AbstractMsTestElement msTestElement) {
// 引用的场景设置场景参数
if (!isScenarioStep(step.getStepType()) || !isRefOrPartialRef(step.getRefType()) || !(msTestElement instanceof MsScenario msScenario)) {
return;
}
if (step.getConfig() != null) {
// 设置场景步骤的运行参数
msScenario.setScenarioStepConfig(JSON.parseObject(JSON.toJSONString(step.getConfig()), ScenarioStepConfig.class));
}
// 获取当前场景配置的环境信息
EnvironmentModeDTO environmentModeDTO = scenarioParseEnvInfo.getRefScenarioEnvMap().get(step.getResourceId());
if (environmentModeDTO != null) {
String environmentId = environmentModeDTO.getEnvironmentId();
// 设置是否是环境组
Boolean isGrouped = environmentModeDTO.getGrouped();
msScenario.setGrouped(isGrouped);
Map<String, EnvironmentInfoDTO> envMap = scenarioParseEnvInfo.getEnvMap();
if (BooleanUtils.isTrue(isGrouped)) {
// 设置环境组 map
msScenario.setProjectEnvMap(getProjectEnvMap(scenarioParseEnvInfo, environmentId));
} else {
// 设置环境
msScenario.setEnvironmentInfo(envMap.get(environmentId));
}
}
}
/**
* scenarioParseEnvInfo 获取对应环境组的 projectEnvMap
*
* @param scenarioParseEnvInfo 环境信息
* @param environmentId 环境ID
* @return projectEnvMap
*/
private Map<String, EnvironmentInfoDTO> getProjectEnvMap(ApiScenarioParseEnvInfo scenarioParseEnvInfo, String environmentId) {
Map<String, List<String>> envGroupMap = scenarioParseEnvInfo.getEnvGroupMap();
List<String> envIds = envGroupMap.get(environmentId);
Map<String, EnvironmentInfoDTO> projectEnvMap = new HashMap<>();
for (String envId : envIds) {
EnvironmentInfoDTO environmentInfoDTO = scenarioParseEnvInfo.getEnvMap().get(envId);
projectEnvMap.put(environmentInfoDTO.getProjectId(), environmentInfoDTO);
}
return projectEnvMap;
}
private static boolean isScenarioStep(String stepType) {
public static boolean isScenarioStep(String stepType) {
return StringUtils.equals(stepType, ApiScenarioStepType.API_SCENARIO.name());
}
/**
* 设置单个部分引用的步骤的启用状态
*/
private void setPartialRefStepEnable(ApiScenarioStepCommonDTO step, Map<String, String> stepDetailMap) {
public void setPartialRefStepEnable(ApiScenarioStepCommonDTO step, Map<String, String> stepDetailMap) {
String stepDetail = stepDetailMap.get(step.getId());
if (!isPartialRef(step) || StringUtils.isBlank(stepDetail)) {
return;
@ -1998,17 +1368,6 @@ public class ApiScenarioService extends MoveNodeService {
StringUtils.equals(step.getRefType(), ApiScenarioStepRefType.PARTIAL_REF.name());
}
private Map<String, String> getStepDetailMap(List<? extends ApiScenarioStepCommonDTO> steps, Map<String, Object> stepDetailsParam) {
List<String> needBlobStepIds = getHasDetailStepIds(steps, stepDetailsParam);
Map<String, String> stepDetails = getStepBlobByIds(needBlobStepIds).stream()
.collect(Collectors.toMap(ApiScenarioStepBlob::getId, blob -> new String(blob.getContent())));
// 前端有传就用前端传的
if (stepDetailsParam != null) {
stepDetailsParam.forEach((stepId, detail) -> stepDetails.put(stepId, detail instanceof byte[] bytes ? new String(bytes) : JSON.toJSONString(detail)));
}
return stepDetails;
}
/**
* 查询部分引用的步骤的详情
*
@ -2031,56 +1390,7 @@ public class ApiScenarioService extends MoveNodeService {
.collect(Collectors.toMap(ApiScenarioStepBlob::getId, blob -> new String(blob.getContent())));
}
private List<String> getHasDetailStepIds(List<? extends ApiScenarioStepCommonDTO> steps, Map<String, Object> stepDetailsParam) {
List<String> needBlobStepIds = new ArrayList<>();
for (ApiScenarioStepCommonDTO step : steps) {
List<? extends ApiScenarioStepCommonDTO> children = step.getChildren();
if (CollectionUtils.isNotEmpty(children)) {
needBlobStepIds.addAll(getHasDetailStepIds(children, stepDetailsParam));
}
if (BooleanUtils.isFalse(step.getEnable())) {
continue;
}
if (!hasStepDetail(step.getStepType())) {
continue;
}
if (stepDetailsParam != null && stepDetailsParam.containsKey(step.getId())) {
// 前端传了blob不需要再查
continue;
}
needBlobStepIds.add(step.getId());
}
return needBlobStepIds;
}
/**
* 非完全引用的步骤和接口定义的步骤才需要查 blob
*
* @param stepType
* @return
*/
private boolean hasStepDetail(String stepType) {
return !StringUtils.equals(stepType, ApiScenarioStepRefType.REF.name())
|| isApi(stepType);
}
private Map<String, String> getResourceDetailMap(Map<String, List<String>> refResourceMap) {
Map<String, String> resourceBlobMap = new HashMap<>();
List<String> apiIds = refResourceMap.get(ApiScenarioStepType.API.name());
List<ApiDefinitionBlob> apiDefinitionBlobs = apiDefinitionService.getBlobByIds(apiIds);
apiDefinitionBlobs.forEach(blob -> resourceBlobMap.put(blob.getId(), new String(blob.getRequest())));
List<String> apiCaseIds = refResourceMap.get(ApiScenarioStepType.API_CASE.name());
List<ApiTestCaseBlob> apiTestCaseBlobs = apiTestCaseService.getBlobByIds(apiCaseIds);
apiTestCaseBlobs.forEach(blob -> resourceBlobMap.put(blob.getId(), new String(blob.getRequest())));
List<String> apiScenarioIds = refResourceMap.get(ApiScenarioStepType.API_SCENARIO.name());
List<ApiScenarioBlob> apiScenarioBlobs = getBlobByIds(apiScenarioIds);
apiScenarioBlobs.forEach(blob -> resourceBlobMap.put(blob.getId(), new String(blob.getConfig())));
return resourceBlobMap;
}
private List<ApiScenarioStepBlob> getStepBlobByIds(List<String> stepIds) {
public List<ApiScenarioStepBlob> getStepBlobByIds(List<String> stepIds) {
if (CollectionUtils.isEmpty(stepIds)) {
return Collections.emptyList();
}
@ -2089,43 +1399,6 @@ public class ApiScenarioService extends MoveNodeService {
return apiScenarioStepBlobMapper.selectByExampleWithBLOBs(example);
}
private List<ApiScenarioBlob> getBlobByIds(List<String> apiScenarioIds) {
if (CollectionUtils.isEmpty(apiScenarioIds)) {
return Collections.emptyList();
}
ApiScenarioBlobExample example = new ApiScenarioBlobExample();
example.createCriteria().andIdIn(apiScenarioIds);
return apiScenarioBlobMapper.selectByExampleWithBLOBs(example);
}
private void buildRefResourceIdMap(List<? extends ApiScenarioStepCommonDTO> steps, Map<String, List<String>> refResourceIdMap) {
for (ApiScenarioStepCommonDTO step : steps) {
if (isRefOrPartialRef(step.getRefType()) && BooleanUtils.isTrue(step.getEnable())) {
// 记录引用的步骤ID
List<String> resourceIds = refResourceIdMap.computeIfAbsent(step.getStepType(), k -> new ArrayList<>());
resourceIds.add(step.getResourceId());
}
if (CollectionUtils.isNotEmpty(step.getChildren())) {
buildRefResourceIdMap(step.getChildren(), refResourceIdMap);
}
}
}
private ScenarioConfig getScenarioConfig(ApiScenarioDebugRequest request, boolean hasSave) {
if (request.getScenarioConfig() != null) {
// 优先使用前端传的配置
return request.getScenarioConfig();
} else if (hasSave) {
// 没传并且保存过则从数据库获取
ApiScenarioBlob apiScenarioBlob = apiScenarioBlobMapper.selectByPrimaryKey(request.getId());
if (apiScenarioBlob != null) {
return JSON.parseObject(new String(apiScenarioBlob.getConfig()), ScenarioConfig.class);
}
}
return new ScenarioConfig();
}
public void updateStatus(String id, String status, String userId) {
checkResourceExist(id);
ApiScenario update = new ApiScenario();
@ -2181,36 +1454,6 @@ public class ApiScenarioService extends MoveNodeService {
return apiScenarios.getFirst();
}
public ApiScenarioDetail getForRun(String scenarioId) {
ApiScenarioDetail apiScenarioDetail = get(scenarioId);
apiScenarioDetail.setSteps(filerDisableSteps(apiScenarioDetail.getSteps()));
return apiScenarioDetail;
}
public List<ApiScenarioDetail> getForRuns(List<String> scenarioIds) {
List<ApiScenarioDetail> apiScenarioDetails = list(scenarioIds);
apiScenarioDetails.forEach(apiScenarioDetail -> apiScenarioDetail.setSteps(filerDisableSteps(apiScenarioDetail.getSteps())));
return apiScenarioDetails;
}
/**
* 过滤掉禁用的步骤
*/
public List<ApiScenarioStepDTO> filerDisableSteps(List<ApiScenarioStepDTO> steps) {
if (CollectionUtils.isEmpty(steps)) {
return List.of();
}
return steps.stream()
.filter(step -> {
boolean isEnable = BooleanUtils.isTrue(step.getEnable());
if (isEnable) {
step.setChildren(filerDisableSteps(step.getChildren()));
}
return isEnable;
})
.toList();
}
public ApiScenarioDetail get(String scenarioId) {
ApiScenario apiScenario = checkResourceIsNoDeleted(scenarioId);
ApiScenarioDetail apiScenarioDetail = BeanUtils.copyBean(new ApiScenarioDetail(), apiScenario);
@ -2397,19 +1640,19 @@ public class ApiScenarioService extends MoveNodeService {
* 判断步骤是否是引用的接口定义
* 引用的接口定义允许修改参数值需要特殊处理
*/
private boolean isCopyApi(String stepType, String refType) {
public boolean isCopyApi(String stepType, String refType) {
return isApi(stepType) && isCopy(refType);
}
private boolean isApi(String stepType) {
public boolean isApi(String stepType) {
return StringUtils.equals(stepType, ApiScenarioStepType.API.name());
}
private boolean isCopy(String refType) {
public boolean isCopy(String refType) {
return StringUtils.equals(refType, ApiScenarioStepRefType.COPY.name());
}
private boolean isApiCase(String stepType) {
public boolean isApiCase(String stepType) {
return StringUtils.equals(stepType, ApiScenarioStepType.API_CASE.name());
}
@ -3110,6 +2353,7 @@ public class ApiScenarioService extends MoveNodeService {
resourceInfo.setDelete(apiTestCase.getDeleted());
resourceInfo.setProjectId(apiTestCase.getProjectId());
});
default -> {}
}
Optional.ofNullable(apiStepResourceInfo).ifPresent(resourceInfo -> {
Project project = projectMapper.selectByPrimaryKey(resourceInfo.getProjectId());
@ -3128,30 +2372,4 @@ public class ApiScenarioService extends MoveNodeService {
setParamFunc.accept(apiStepResourceInfo, resource);
return apiStepResourceInfo;
}
/**
* 获取场景前置的总等待时间
*
* @param scenarioConfig
* @return
*/
public Long getGlobalWaitTime(ScenarioConfig scenarioConfig) {
Long waitTime = null;
if (scenarioConfig != null
&& scenarioConfig.getPreProcessorConfig() != null
&& scenarioConfig.getPreProcessorConfig().getProcessors() != null) {
waitTime = 0L;
for (MsProcessor processor : scenarioConfig
.getPreProcessorConfig()
.getProcessors()) {
if (processor instanceof TimeWaitingProcessor timeWaitingProcessor
&& timeWaitingProcessor.getEnable()
&& timeWaitingProcessor.getDelay() != null) {
waitTime += timeWaitingProcessor.getDelay();
}
}
waitTime = waitTime > 0 ? waitTime : null;
}
return waitTime;
}
}

View File

@ -1,23 +0,0 @@
package io.metersphere.api.utils;
import io.metersphere.api.dto.scenario.ApiScenarioDetail;
import io.metersphere.api.service.scenario.ApiScenarioBatchRunService;
import io.metersphere.sdk.dto.api.task.ApiRunModeConfigDTO;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.Map;
@Data
@AllArgsConstructor
public class ExecTask implements Runnable {
private ApiScenarioBatchRunService apiScenarioBatchRunService;
private ApiScenarioDetail detail;
private Map<String, String> scenarioReportMap;
private ApiRunModeConfigDTO runModeConfig;
@Override
public void run() {
apiScenarioBatchRunService.execute(detail, scenarioReportMap, runModeConfig);
}
}

View File

@ -1,52 +0,0 @@
package io.metersphere.api.utils;
import io.metersphere.sdk.util.LogUtils;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class TaskRunnerUtils {
// 线程池维护线程的最大数量
private final static int MAX_POOL_SIZE = 10;
// 线程池维护线程所允许的空闲时间
private final static int KEEP_ALIVE_TIME = 1;
// 线程池所使用的缓冲队列大小
private final static int WORK_QUEUE_SIZE = 50000;
private static final ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
MAX_POOL_SIZE,
MAX_POOL_SIZE,
KEEP_ALIVE_TIME,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(WORK_QUEUE_SIZE));
public static void executeThreadPool(ExecTask task) {
try {
// 开始执行任务
threadPool.execute(task);
LogUtils.info("当前线程池活跃线程数量:{},当前线程池线程数量:{},当前线程池队列数量:{}",
threadPool.getActiveCount(),
threadPool.getPoolSize(),
threadPool.getQueue().size());
} catch (Exception e) {
LogUtils.error("KAFKA消费失败", e);
}
}
public static void setThreadPoolSize(int poolSize) {
try {
if (poolSize > 10 && poolSize < 500 && poolSize != threadPool.getMaximumPoolSize()) {
threadPool.setMaximumPoolSize(poolSize);
threadPool.setCorePoolSize(poolSize);
threadPool.allowCoreThreadTimeOut(true);
LogUtils.info("Set successfully: " + threadPool.prestartAllCoreThreads());
}
LogUtils.info("Invalid thread pool size: " + poolSize);
} catch (Exception e) {
LogUtils.error("设置线程参数异常", e);
}
}
}

View File

@ -543,7 +543,7 @@ public class ApiDebugControllerTests extends BaseTest {
request.setFrontendDebug(true);
MvcResult mvcResult = this.requestPostWithOkAndReturn(DEBUG, request);
TaskRequestDTO taskRequestDTO = getResultData(mvcResult, TaskRequestDTO.class);
Assertions.assertEquals(taskRequestDTO.getReportId(), request.getReportId());
Assertions.assertEquals(taskRequestDTO.getTaskItem().getReportId(), request.getReportId());
// 测试请求体
MockMultipartFile file = getMockMultipartFile();

View File

@ -67,7 +67,7 @@ public class ApiExecuteResourceControllerTest extends BaseTest {
String reportId = UUID.randomUUID().toString();
String testId = UUID.randomUUID().toString();
String scriptRedisKey = apiExecuteService.getScriptRedisKey(reportId, testId);
String scriptRedisKey = apiExecuteService.getTaskKey(reportId, testId);
stringRedisTemplate.opsForValue().set(scriptRedisKey, "aaa");
mockMvc.perform(getPostRequestBuilder(FILE, fileRequest, reportId, testId))
.andExpect(status().isOk());

View File

@ -166,7 +166,7 @@ public class KubernetesEngineTests extends BaseTest {
TaskRequestDTO request = new TaskRequestDTO();
ApiRunModeConfigDTO runModeConfig = new ApiRunModeConfigDTO();
runModeConfig.setPoolId(id);
request.setRunModeConfig(runModeConfig);
request.getTaskInfo().setRunModeConfig(runModeConfig);
final ApiEngine engine = EngineFactory.createApiEngine(request);
engine.start();

View File

@ -2,6 +2,7 @@ package io.metersphere.system.utils;
import com.bastiaanjansen.otp.TOTPGenerator;
import io.metersphere.sdk.constants.MsHttpHeaders;
import io.metersphere.sdk.dto.api.task.TaskBatchRequestDTO;
import io.metersphere.sdk.dto.api.task.TaskRequestDTO;
import io.metersphere.sdk.util.LogUtils;
import io.metersphere.system.controller.handler.ResultHolder;
@ -28,6 +29,7 @@ public class TaskRunnerClient {
private static final String API_DEBUG = "/api/debug";
private static final String API_RUN = "/api/run";
private static final String BATCH_API_RUN = "/api/batch/run";
private static final String HTTP_BATH = "http://%s:%s";
private static final String API_STOP = "/api/stop";
@ -50,6 +52,10 @@ public class TaskRunnerClient {
post(endpoint + API_RUN, taskRequest);
}
public static void batchRunApi(String endpoint, TaskBatchRequestDTO taskRequest) throws Exception {
post(endpoint + BATCH_API_RUN, taskRequest);
}
public static void stopApi(String endpoint, List<String> reportIds) throws Exception {
post(endpoint + API_STOP, reportIds);
}