feat(接口测试): 接口调试执行示例
This commit is contained in:
parent
4e4f17f195
commit
d8e0fd0bc7
|
@ -1,5 +1,6 @@
|
|||
package io.metersphere;
|
||||
|
||||
import io.metersphere.api.config.JmeterProperties;
|
||||
import io.metersphere.system.config.MinioProperties;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
@ -21,7 +22,8 @@ import org.springframework.context.annotation.PropertySource;
|
|||
}, encoding = "UTF-8", ignoreResourceNotFound = true)
|
||||
@ServletComponentScan
|
||||
@EnableConfigurationProperties({
|
||||
MinioProperties.class
|
||||
MinioProperties.class,
|
||||
JmeterProperties.class
|
||||
})
|
||||
public class Application {
|
||||
public static void main(String[] args) {
|
||||
|
|
|
@ -2,9 +2,9 @@ package io.metersphere.listener;
|
|||
|
||||
import io.metersphere.api.event.ApiEventSource;
|
||||
import io.metersphere.plan.listener.ExecEventListener;
|
||||
import io.metersphere.system.service.PluginLoadService;
|
||||
import io.metersphere.sdk.util.CommonBeanFactory;
|
||||
import io.metersphere.sdk.util.LogUtils;
|
||||
import io.metersphere.system.service.PluginLoadService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package io.metersphere.plugin.api.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @Author: jianxing
|
||||
* @CreateTime: 2023-10-27 17:30
|
||||
*/
|
||||
@Data
|
||||
public class ParameterConfig {
|
||||
private String reportId;
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
package io.metersphere.plugin.api.spi;
|
||||
|
||||
|
||||
import io.metersphere.plugin.api.dto.ParameterConfig;
|
||||
import org.apache.jorphan.collections.HashTree;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* @author jianxing
|
||||
|
@ -15,44 +15,39 @@ import java.util.Map;
|
|||
*/
|
||||
public abstract class AbstractJmeterElementConverter<T extends MsTestElement> {
|
||||
|
||||
public Class<? extends MsTestElement> testElementClass;
|
||||
|
||||
/**
|
||||
* 解析器集合
|
||||
* key 为 MsTestElement 实现类的 Class
|
||||
* value 为对应的转换器
|
||||
* 获取转换器的函数
|
||||
* 主应用在实例化转换器的时候会设置
|
||||
*/
|
||||
private static final Map<MsTestElement, AbstractJmeterElementConverter<?>> parserMap = new HashMap<>();
|
||||
private Function<Class<? extends MsTestElement>, AbstractJmeterElementConverter> getConverterFunc;
|
||||
|
||||
public void setGetConverterFunc(Function<Class<? extends MsTestElement>, AbstractJmeterElementConverter> getConverterFunc) {
|
||||
this.getConverterFunc = getConverterFunc;
|
||||
}
|
||||
|
||||
public AbstractJmeterElementConverter() {
|
||||
Type genericSuperclass = getClass().getGenericSuperclass();
|
||||
if (genericSuperclass instanceof ParameterizedType parameterizedType) {
|
||||
// 获取泛型的具体类型,即 MsTestElement 的具体实现类
|
||||
MsTestElement baseClass = (MsTestElement) parameterizedType.getActualTypeArguments()[0];
|
||||
// 注册到解析器集合中
|
||||
parserMap.put(baseClass, this);
|
||||
testElementClass = ((Class) parameterizedType.getActualTypeArguments()[0]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将 MsTestElement 具体实现类转换为 HashTree
|
||||
*/
|
||||
public abstract void toHashTree(HashTree tree, T element, MsParameter config);
|
||||
public abstract void toHashTree(HashTree tree, T element, ParameterConfig config);
|
||||
|
||||
/**
|
||||
* 解析 MsTestElement 的子节点
|
||||
*/
|
||||
public void parseChild(HashTree tree, AbstractMsTestElement element, MsParameter config) {
|
||||
public void parseChild(HashTree tree, AbstractMsTestElement element, ParameterConfig config) {
|
||||
if (element != null && element.getChildren() != null) {
|
||||
element.getChildren().forEach(child -> {
|
||||
getConverter(child).toHashTree(tree, element, config);
|
||||
});
|
||||
element.getChildren().forEach(child ->
|
||||
getConverterFunc.apply(child.getClass())
|
||||
.toHashTree(tree, element, config));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对应组件的转换器
|
||||
*/
|
||||
public static AbstractJmeterElementConverter getConverter(MsTestElement element) {
|
||||
return parserMap.get(element);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
package io.metersphere.plugin.api.spi;
|
||||
|
||||
/**
|
||||
* @Author: jianxing
|
||||
* @CreateTime: 2023-10-27 17:30
|
||||
*/
|
||||
public class MsParameter {
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package io.metersphere.sdk.constants;
|
||||
|
||||
/**
|
||||
* 接口执行时的资源类型
|
||||
* @Author: jianxing
|
||||
* @CreateTime: 2023-12-08 10:53
|
||||
*/
|
||||
public enum ApiExecuteResourceType {
|
||||
API_DEBUG, API, API_CASE, API_SCENARIO
|
||||
// todo plan api
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package io.metersphere.sdk.constants;
|
||||
|
||||
/**
|
||||
* 接口执行时的资源类型
|
||||
* @Author: jianxing
|
||||
* @CreateTime: 2023-12-08 10:53
|
||||
*/
|
||||
public enum ApiExecuteRunMode {
|
||||
/**
|
||||
* 手动运行
|
||||
*/
|
||||
RUN,
|
||||
/**
|
||||
* 调试
|
||||
*/
|
||||
DEBUG,
|
||||
/**
|
||||
* jenkins 触发
|
||||
*/
|
||||
JENKINS,
|
||||
/**
|
||||
* 定时任务
|
||||
*/
|
||||
SCENARIO
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.api.enums;
|
||||
package io.metersphere.sdk.constants;
|
||||
|
||||
/**
|
||||
* @author: LAN
|
|
@ -1,10 +0,0 @@
|
|||
package io.metersphere.sdk.dto.api.request;
|
||||
|
||||
import io.metersphere.plugin.api.spi.MsParameter;
|
||||
|
||||
/**
|
||||
* @Author: jianxing
|
||||
* @CreateTime: 2023-10-27 17:30
|
||||
*/
|
||||
public class ParameterConfig extends MsParameter {
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
package io.metersphere.sdk.dto.api.request.sampler;
|
||||
|
||||
import io.metersphere.plugin.api.annotation.PluginSubType;
|
||||
import io.metersphere.plugin.api.dto.TestElementDTO;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.jmeter.sampler.DebugSampler;
|
||||
import org.apache.jmeter.save.SaveService;
|
||||
import org.apache.jmeter.testelement.TestElement;
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@PluginSubType("MsDebugSampler")
|
||||
public class MsDebugSampler extends TestElementDTO {
|
||||
private boolean displayJMeterProperties = false;
|
||||
private boolean displayJMeterVariables = true;
|
||||
private boolean displaySystemProperties = false;
|
||||
|
||||
|
||||
private DebugSampler debugSampler() {
|
||||
DebugSampler debugSampler = new DebugSampler();
|
||||
debugSampler.setEnabled(this.isEnable());
|
||||
if (StringUtils.isEmpty(this.getName())) {
|
||||
this.setName(MsDebugSampler.class.getSimpleName());
|
||||
}
|
||||
debugSampler.setName(this.getName());
|
||||
debugSampler.setProperty(TestElement.TEST_CLASS, DebugSampler.class.getName());
|
||||
debugSampler.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TestBeanGUI"));
|
||||
|
||||
debugSampler.setDisplaySystemProperties(this.displaySystemProperties);
|
||||
debugSampler.setDisplayJMeterVariables(this.displayJMeterVariables);
|
||||
debugSampler.setDisplayJMeterProperties(this.displayJMeterProperties);
|
||||
|
||||
//上面三行直接Set属性会导致DebugSampler构建时取不到值,可能是JMeter的Bug,需要SetProperty
|
||||
debugSampler.setProperty("displayJMeterProperties", this.displayJMeterProperties);
|
||||
debugSampler.setProperty("displayJMeterVariables", this.displayJMeterVariables);
|
||||
debugSampler.setProperty("displaySystemProperties", this.displaySystemProperties);
|
||||
return debugSampler;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package io.metersphere.sdk.dto.api.result;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class MsRegexDTO {
|
||||
private String subject;
|
||||
private String condition;
|
||||
private String value;
|
||||
private String errorCode;
|
||||
private boolean pass;
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package io.metersphere.sdk.dto.api.result;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 实际请求详情
|
||||
*/
|
||||
@Data
|
||||
public class RequestResult {
|
||||
// 请求ID
|
||||
private String id;
|
||||
|
||||
// 步骤请求唯一ID
|
||||
private String resourceId;
|
||||
|
||||
private String threadName;
|
||||
|
||||
private String name;
|
||||
|
||||
private String url;
|
||||
|
||||
private long requestSize;
|
||||
|
||||
private long startTime;
|
||||
|
||||
private long endTime;
|
||||
|
||||
private int error;
|
||||
|
||||
private boolean success;
|
||||
|
||||
private String headers;
|
||||
|
||||
private String cookies;
|
||||
|
||||
private String body;
|
||||
|
||||
private String status;
|
||||
|
||||
private int totalAssertionCount = 0;
|
||||
|
||||
private int passAssertionsCount= 0;
|
||||
|
||||
private List<RequestResult> subRequestResults = new ArrayList<>();
|
||||
|
||||
private ResponseResult responseResult = new ResponseResult();
|
||||
|
||||
public void increasePassAssertionCount() {
|
||||
this.passAssertionsCount++;
|
||||
}
|
||||
|
||||
private String fakeErrorMessage;
|
||||
// 误报编码名称
|
||||
private String fakeErrorCode;
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package io.metersphere.sdk.dto.api.result;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 断言结果
|
||||
*/
|
||||
@Data
|
||||
public class ResponseAssertionResult {
|
||||
|
||||
private String name;
|
||||
|
||||
private String content;
|
||||
|
||||
private String script;
|
||||
|
||||
private String message;
|
||||
|
||||
private boolean pass;
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package io.metersphere.sdk.dto.api.result;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 响应结果
|
||||
*/
|
||||
@Data
|
||||
public class ResponseResult {
|
||||
|
||||
private String responseCode;
|
||||
|
||||
private String responseMessage;
|
||||
|
||||
private long responseTime;
|
||||
|
||||
private long latency;
|
||||
|
||||
private long responseSize;
|
||||
|
||||
private String headers;
|
||||
|
||||
private String body;
|
||||
|
||||
private String vars;
|
||||
|
||||
private String console;
|
||||
|
||||
private String contentType;
|
||||
|
||||
private byte[] imageUrl;
|
||||
|
||||
private final List<ResponseAssertionResult> assertions = new ArrayList<>();
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package io.metersphere.sdk.dto.api.result;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
public class TaskResult implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
// TODO: 补充任务执行结果数据结构
|
||||
|
||||
private List<RequestResult> requestResults;
|
||||
private String runMode;
|
||||
private String queueId;
|
||||
private String reportId;
|
||||
private String reportType;
|
||||
private String testPlanReportId;
|
||||
private String testId;
|
||||
private String runType;
|
||||
private String console;
|
||||
private String runningDebugSampler;
|
||||
private Boolean hasEnded;
|
||||
private boolean retryEnable;
|
||||
private Map<String, Object> arbitraryData;
|
||||
Map<String, List<MsRegexDTO>> fakeErrorMap;
|
||||
}
|
|
@ -1,15 +1,10 @@
|
|||
package io.metersphere.sdk.dto.api.task;
|
||||
|
||||
import io.metersphere.sdk.exception.MSException;
|
||||
import io.metersphere.sdk.util.JSON;
|
||||
import io.metersphere.sdk.util.RsaUtils;
|
||||
import lombok.Data;
|
||||
import org.apache.commons.collections4.MapUtils;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 任务请求参数数据
|
||||
|
@ -24,27 +19,28 @@ public class TaskRequest implements Serializable {
|
|||
private String kafkaConfig;
|
||||
private String minioConfig;
|
||||
private String poolId;
|
||||
/**
|
||||
* 是否需要实时接收单个步骤的结果
|
||||
*/
|
||||
private Boolean realTime;
|
||||
|
||||
/**
|
||||
* 执行的资源ID
|
||||
*/
|
||||
private String testId;
|
||||
/**
|
||||
* 点击调试时,尚未保存的文件ID列表
|
||||
*/
|
||||
private List<String> tempFileIds;
|
||||
/**
|
||||
* 执行模式
|
||||
*/
|
||||
private String runMode;
|
||||
/**
|
||||
* 资源类型
|
||||
*/
|
||||
private String resourceType;
|
||||
|
||||
|
||||
// TODO 其它执行参数
|
||||
|
||||
// 默认声明对象时获取配置参数
|
||||
public void setKafkaConfig(Map<String, String> kafkaConfig) {
|
||||
if (MapUtils.isNotEmpty(kafkaConfig)) {
|
||||
try {
|
||||
this.kafkaConfig = RsaUtils.publicEncrypt(JSON.toJSONString(kafkaConfig), this.getReportId());
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new MSException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setMinioConfig(Map<String, String> minioConfig) {
|
||||
if (MapUtils.isNotEmpty(minioConfig)) {
|
||||
try {
|
||||
this.minioConfig = RsaUtils.publicEncrypt(JSON.toJSONString(minioConfig), this.getReportId());
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new MSException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
package io.metersphere.sdk.dto.api.task;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
public class TaskResult implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
// TODO: 补充任务执行结果数据结构
|
||||
}
|
|
@ -6,11 +6,13 @@ import org.jetbrains.annotations.NotNull;
|
|||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
@Component
|
||||
public class CommonBeanFactory implements ApplicationContextAware {
|
||||
private static ApplicationContext context;
|
||||
|
||||
|
|
|
@ -9,14 +9,14 @@ public class EncryptUtils extends CodingUtils {
|
|||
private static final String iv = "1234567890123456";
|
||||
|
||||
|
||||
public static Object aesEncrypt(Object o) {
|
||||
public static String aesEncrypt(Object o) {
|
||||
if (o == null) {
|
||||
return null;
|
||||
}
|
||||
return aesEncrypt(o.toString(), secretKey, iv);
|
||||
}
|
||||
|
||||
public static Object aesDecrypt(Object o) {
|
||||
public static String aesDecrypt(Object o) {
|
||||
if (o == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ public class EncryptUtils extends CodingUtils {
|
|||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static Object md5Encrypt(Object o) {
|
||||
public static String md5Encrypt(Object o) {
|
||||
if (o == null) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -31,6 +31,9 @@ public class FilterChainUtils {
|
|||
//用户通过邮箱邀请自行注册的接口
|
||||
filterChainDefinitionMap.put("/system/user/register-by-invite", "anon");
|
||||
|
||||
// 下载测试资源
|
||||
filterChainDefinitionMap.put("/api/execute/resource/**", "anon");
|
||||
|
||||
|
||||
// for swagger
|
||||
filterChainDefinitionMap.put("/swagger-ui.html", "anon");
|
||||
|
|
|
@ -296,3 +296,6 @@ api_debug_exist=接口已存在
|
|||
follow=关注
|
||||
unfollow=取消关注
|
||||
api_definition_exist=接口已存在
|
||||
|
||||
execute_resource_pool_not_config_error=请在【项目管理-应用管理-接口测试】中选择资源池
|
||||
resource_pool_execute_error=资源池调用失败
|
||||
|
|
|
@ -304,3 +304,5 @@ api_debug_exist=The API already exists
|
|||
follow=Follow
|
||||
unfollow=Unfollow
|
||||
api_definition_exist=The API already exists
|
||||
execute_resource_pool_not_config_error=Select a resource pool in 【Project Management - Application Management - Interface Testing】
|
||||
resource_pool_execute_error=The resource pool call failed
|
||||
|
|
|
@ -304,3 +304,5 @@ api_debug_exist=接口已存在
|
|||
follow=关注
|
||||
unfollow=取消关注
|
||||
api_definition_exist=接口已存在
|
||||
execute_resource_pool_not_config_error=请在【项目管理-应用管理-接口测试】中选择资源池
|
||||
resource_pool_execute_error=资源池调用失败
|
||||
|
|
|
@ -304,3 +304,5 @@ api_debug_exist=接口已存在
|
|||
follow=关注
|
||||
unfollow=取消关注
|
||||
api_definition_exist=接口已存在
|
||||
execute_resource_pool_not_config_error=請在【項目管理-應用管理-接口測試】中選擇資源池
|
||||
resource_pool_execute_error=資源池調用失敗
|
||||
|
|
|
@ -47,6 +47,11 @@
|
|||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.jmeter</groupId>
|
||||
<artifactId>ApacheJMeter_http</artifactId>
|
||||
<version>${jmeter.version}</version>
|
||||
</dependency>
|
||||
<!-- swagger 解析 -->
|
||||
<dependency>
|
||||
<groupId>io.swagger.parser.v3</groupId>
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
package io.metersphere.api.config;
|
||||
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
@ConfigurationProperties(prefix = JmeterProperties.JMETER_PREFIX)
|
||||
@Setter
|
||||
@Getter
|
||||
public class JmeterProperties {
|
||||
|
||||
public static final String JMETER_PREFIX = "jmeter";
|
||||
private String home;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.api.enums;
|
||||
package io.metersphere.api.constants;
|
||||
|
||||
/**
|
||||
* @author: LAN
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.api.enums;
|
||||
package io.metersphere.api.constants;
|
||||
|
||||
public enum ApiImportPlatform {
|
||||
MeterSphere, Postman, Swagger3, Plugin, Jmeter, Har
|
|
@ -0,0 +1,42 @@
|
|||
package io.metersphere.api.controller;
|
||||
|
||||
import io.metersphere.api.service.ApiExecuteService;
|
||||
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.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* @Author: jianxing
|
||||
* @CreateTime: 2023-12-05 17:52
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/execute/resource")
|
||||
public class ApiExecuteResourceController {
|
||||
|
||||
@Resource
|
||||
private StringRedisTemplate stringRedisTemplate;
|
||||
@Resource
|
||||
private ApiExecuteService apiExecuteService;
|
||||
|
||||
/**
|
||||
* 获取执行脚本
|
||||
* @param reportId
|
||||
* @param testId
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("script")
|
||||
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);
|
||||
return Optional.ofNullable(script).orElse(StringUtils.EMPTY);
|
||||
}
|
||||
}
|
|
@ -1,10 +1,7 @@
|
|||
package io.metersphere.api.controller.debug;
|
||||
|
||||
import io.metersphere.api.domain.ApiDebug;
|
||||
import io.metersphere.api.dto.debug.ApiDebugAddRequest;
|
||||
import io.metersphere.api.dto.debug.ApiDebugDTO;
|
||||
import io.metersphere.api.dto.debug.ApiDebugSimpleDTO;
|
||||
import io.metersphere.api.dto.debug.ApiDebugUpdateRequest;
|
||||
import io.metersphere.api.dto.debug.*;
|
||||
import io.metersphere.api.service.debug.ApiDebugLogService;
|
||||
import io.metersphere.api.service.debug.ApiDebugService;
|
||||
import io.metersphere.sdk.constants.PermissionConstants;
|
||||
|
@ -78,4 +75,11 @@ public class ApiDebugController {
|
|||
public void delete(@PathVariable String id) {
|
||||
apiDebugService.delete(id, SessionUtils.getUserId());
|
||||
}
|
||||
|
||||
@PostMapping("/debug")
|
||||
@Operation(summary = "运行接口调试")
|
||||
@RequiresPermissions(PermissionConstants.PROJECT_API_DEBUG_EXECUTE)
|
||||
public String debug(@Validated @RequestBody ApiDebugRunRequest request) {
|
||||
return apiDebugService.debug(request);
|
||||
}
|
||||
}
|
|
@ -10,7 +10,9 @@ public enum ApiResultCode implements IResultCode {
|
|||
API_DEBUG_EXIST(104001, "api_debug_exist"),
|
||||
API_DEFINITION_EXIST(104002, "api_definition_exist"),
|
||||
API_DEFINITION_NOT_EXIST(104003, "resource_not_exist"),
|
||||
API_DEFINITION_MODULE_NOT_EXIST(10404, "resource_not_exist");
|
||||
API_DEFINITION_MODULE_NOT_EXIST(10404, "resource_not_exist"),
|
||||
RESOURCE_POOL_EXECUTE_ERROR(104005, "resource_pool_execute_error"),
|
||||
EXECUTE_RESOURCE_POOL_NOT_CONFIG(104006, "execute_resource_pool_not_config_error");
|
||||
|
||||
|
||||
private final int code;
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package io.metersphere.api.dto.debug;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class ApiDebugRunRequest {
|
||||
private String id;
|
||||
private String environmentId;
|
||||
|
||||
@Schema(description = "点击调试时尚未保存的文件ID列表")
|
||||
private List<String> tempFileIds;
|
||||
|
||||
private String request;
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package io.metersphere.api.dto.debug;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class ApiResourceRunRequest {
|
||||
private String id;
|
||||
private String projectId;
|
||||
private String testId;
|
||||
private String reportId;
|
||||
private String environmentId;
|
||||
/**
|
||||
* 执行模式
|
||||
*/
|
||||
private String runMode;
|
||||
/**
|
||||
* 资源类型
|
||||
*/
|
||||
private String resourceType;
|
||||
@Schema(description = "点击调试时尚未保存的文件ID列表")
|
||||
private List<String> tempFileIds;
|
||||
private String request;
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
package io.metersphere.api.dto.definition;
|
||||
|
||||
import io.metersphere.sdk.dto.api.request.http.Header;
|
||||
import io.metersphere.sdk.dto.api.request.http.body.Body;
|
||||
import io.metersphere.api.dto.request.http.Header;
|
||||
import io.metersphere.api.dto.request.http.body.Body;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request;
|
||||
package io.metersphere.api.dto.request;
|
||||
|
||||
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
|
||||
import lombok.Data;
|
|
@ -1,10 +0,0 @@
|
|||
package io.metersphere.api.dto.request;
|
||||
|
||||
import io.metersphere.plugin.api.spi.MsParameter;
|
||||
|
||||
/**
|
||||
* @Author: jianxing
|
||||
* @CreateTime: 2023-10-27 17:30
|
||||
*/
|
||||
public class ParameterConfig extends MsParameter {
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.assertion;
|
||||
package io.metersphere.api.dto.request.assertion;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonSubTypes;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.assertion;
|
||||
package io.metersphere.api.dto.request.assertion;
|
||||
|
||||
import lombok.Data;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.assertion;
|
||||
package io.metersphere.api.dto.request.assertion;
|
||||
|
||||
/**
|
||||
* body断言中的断言类型
|
|
@ -1,10 +1,10 @@
|
|||
package io.metersphere.sdk.dto.api.request.assertion;
|
||||
package io.metersphere.api.dto.request.assertion;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import io.metersphere.sdk.dto.api.request.assertion.body.DocumentAssertion;
|
||||
import io.metersphere.sdk.dto.api.request.assertion.body.JSONPathAssertion;
|
||||
import io.metersphere.sdk.dto.api.request.assertion.body.RegexAssertion;
|
||||
import io.metersphere.sdk.dto.api.request.assertion.body.XPathAssertion;
|
||||
import io.metersphere.api.dto.request.assertion.body.DocumentAssertion;
|
||||
import io.metersphere.api.dto.request.assertion.body.JSONPathAssertion;
|
||||
import io.metersphere.api.dto.request.assertion.body.RegexAssertion;
|
||||
import io.metersphere.api.dto.request.assertion.body.XPathAssertion;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.assertion;
|
||||
package io.metersphere.api.dto.request.assertion;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import lombok.Data;
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.assertion;
|
||||
package io.metersphere.api.dto.request.assertion;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import lombok.Data;
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.assertion;
|
||||
package io.metersphere.api.dto.request.assertion;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import lombok.Data;
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.assertion;
|
||||
package io.metersphere.api.dto.request.assertion;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import lombok.Data;
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.assertion;
|
||||
package io.metersphere.api.dto.request.assertion;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import lombok.Data;
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.assertion.body;
|
||||
package io.metersphere.api.dto.request.assertion.body;
|
||||
|
||||
import lombok.Data;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.assertion.body;
|
||||
package io.metersphere.api.dto.request.assertion.body;
|
||||
|
||||
import lombok.Data;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.assertion.body;
|
||||
package io.metersphere.api.dto.request.assertion.body;
|
||||
|
||||
import lombok.Data;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.assertion.body;
|
||||
package io.metersphere.api.dto.request.assertion.body;
|
||||
|
||||
import lombok.Data;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.assertion.body;
|
||||
package io.metersphere.api.dto.request.assertion.body;
|
||||
|
||||
import lombok.Data;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.assertion.body;
|
||||
package io.metersphere.api.dto.request.assertion.body;
|
||||
|
||||
import lombok.Data;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.assertion.body;
|
||||
package io.metersphere.api.dto.request.assertion.body;
|
||||
|
||||
import lombok.Data;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.assertion.body;
|
||||
package io.metersphere.api.dto.request.assertion.body;
|
||||
|
||||
|
||||
import lombok.Data;
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.assertion.body;
|
||||
package io.metersphere.api.dto.request.assertion.body;
|
||||
|
||||
|
||||
import lombok.Data;
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.logic.controller;
|
||||
package io.metersphere.api.dto.request.controller;
|
||||
|
||||
import io.metersphere.plugin.api.annotation.PluginSubType;
|
||||
import io.metersphere.plugin.api.dto.TestElementDTO;
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.http;
|
||||
package io.metersphere.api.dto.request.http;
|
||||
|
||||
import lombok.Data;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.http;
|
||||
package io.metersphere.api.dto.request.http;
|
||||
|
||||
import lombok.Data;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.http;
|
||||
package io.metersphere.api.dto.request.http;
|
||||
|
||||
import lombok.Data;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.http;
|
||||
package io.metersphere.api.dto.request.http;
|
||||
|
||||
import lombok.Data;
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
package io.metersphere.sdk.dto.api.request.http;
|
||||
package io.metersphere.api.dto.request.http;
|
||||
|
||||
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
|
||||
import io.metersphere.sdk.dto.api.request.assertion.MsAssertionConfig;
|
||||
import io.metersphere.sdk.dto.api.request.http.auth.HTTPAuth;
|
||||
import io.metersphere.sdk.dto.api.request.http.body.Body;
|
||||
import io.metersphere.sdk.dto.api.request.processors.MsProcessorConfig;
|
||||
import io.metersphere.api.dto.request.assertion.MsAssertionConfig;
|
||||
import io.metersphere.api.dto.request.http.auth.HTTPAuth;
|
||||
import io.metersphere.api.dto.request.http.body.Body;
|
||||
import io.metersphere.api.dto.request.processors.MsProcessorConfig;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.http;
|
||||
package io.metersphere.api.dto.request.http;
|
||||
|
||||
import lombok.Data;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.http;
|
||||
package io.metersphere.api.dto.request.http;
|
||||
|
||||
import lombok.Data;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.http.auth;
|
||||
package io.metersphere.api.dto.request.http.auth;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import lombok.Data;
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.http.auth;
|
||||
package io.metersphere.api.dto.request.http.auth;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import lombok.Data;
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.http.auth;
|
||||
package io.metersphere.api.dto.request.http.auth;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonSubTypes;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.http.auth;
|
||||
package io.metersphere.api.dto.request.http.auth;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import lombok.Data;
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.http.body;
|
||||
package io.metersphere.api.dto.request.http.body;
|
||||
|
||||
import lombok.Data;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.http.body;
|
||||
package io.metersphere.api.dto.request.http.body;
|
||||
|
||||
import lombok.Data;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.http.body;
|
||||
package io.metersphere.api.dto.request.http.body;
|
||||
|
||||
import lombok.Data;
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
package io.metersphere.sdk.dto.api.request.http.body;
|
||||
package io.metersphere.api.dto.request.http.body;
|
||||
|
||||
import io.metersphere.sdk.dto.api.request.http.KeyValueEnableParam;
|
||||
import io.metersphere.api.dto.request.http.KeyValueEnableParam;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.http.body;
|
||||
package io.metersphere.api.dto.request.http.body;
|
||||
|
||||
import lombok.Data;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.http.body;
|
||||
package io.metersphere.api.dto.request.http.body;
|
||||
|
||||
import lombok.Data;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.http.body;
|
||||
package io.metersphere.api.dto.request.http.body;
|
||||
|
||||
import lombok.Data;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.http.body;
|
||||
package io.metersphere.api.dto.request.http.body;
|
||||
|
||||
import lombok.Data;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.http.body;
|
||||
package io.metersphere.api.dto.request.http.body;
|
||||
|
||||
import lombok.Data;
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package io.metersphere.sdk.dto.api.request.processors;
|
||||
package io.metersphere.api.dto.request.processors;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import io.metersphere.sdk.dto.api.request.http.KeyValueParam;
|
||||
import io.metersphere.api.dto.request.http.KeyValueParam;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
|
@ -1,7 +1,7 @@
|
|||
package io.metersphere.sdk.dto.api.request.processors;
|
||||
package io.metersphere.api.dto.request.processors;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import io.metersphere.sdk.dto.api.request.processors.extract.MsExtract;
|
||||
import io.metersphere.api.dto.request.processors.extract.MsExtract;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.processors;
|
||||
package io.metersphere.api.dto.request.processors;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonSubTypes;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.processors;
|
||||
package io.metersphere.api.dto.request.processors;
|
||||
|
||||
import lombok.Data;
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
package io.metersphere.sdk.dto.api.request.processors;
|
||||
package io.metersphere.api.dto.request.processors;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import io.metersphere.sdk.dto.api.request.http.KeyValueEnableParam;
|
||||
import io.metersphere.sdk.dto.api.request.http.KeyValueParam;
|
||||
import io.metersphere.api.dto.request.http.KeyValueEnableParam;
|
||||
import io.metersphere.api.dto.request.http.KeyValueParam;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.processors;
|
||||
package io.metersphere.api.dto.request.processors;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import lombok.Data;
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.processors;
|
||||
package io.metersphere.api.dto.request.processors;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import lombok.Data;
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.processors.extract;
|
||||
package io.metersphere.api.dto.request.processors.extract;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import lombok.Data;
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.processors.extract;
|
||||
package io.metersphere.api.dto.request.processors.extract;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonSubTypes;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.processors.extract;
|
||||
package io.metersphere.api.dto.request.processors.extract;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import lombok.Data;
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.processors.extract;
|
||||
package io.metersphere.api.dto.request.processors.extract;
|
||||
|
||||
import lombok.Data;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.dto.api.request.processors.extract;
|
||||
package io.metersphere.api.dto.request.processors.extract;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import lombok.Data;
|
|
@ -1,6 +1,6 @@
|
|||
package io.metersphere.api.parser;
|
||||
|
||||
import io.metersphere.api.enums.ApiImportPlatform;
|
||||
import io.metersphere.api.constants.ApiImportPlatform;
|
||||
import io.metersphere.api.parser.api.PostmanParser;
|
||||
import io.metersphere.api.parser.api.Swagger3Parser;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
package io.metersphere.api.parser;
|
||||
|
||||
|
||||
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
|
||||
import io.metersphere.plugin.api.dto.ParameterConfig;
|
||||
|
||||
/**
|
||||
* @Author: jianxing
|
||||
* @CreateTime: 2023-10-27 10:07
|
||||
*
|
||||
* 执行对象解析器
|
||||
*/
|
||||
public interface TestElementParser {
|
||||
|
||||
/**
|
||||
* 将 MsTestElement 转换为对应执行引擎的执行对象
|
||||
* @param msTestElement
|
||||
* @param config
|
||||
* @return
|
||||
*/
|
||||
String parse(AbstractMsTestElement msTestElement, ParameterConfig config);
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package io.metersphere.api.parser;
|
||||
|
||||
import io.metersphere.api.parser.jmeter.JmeterTestElementParser;
|
||||
|
||||
/**
|
||||
* @Author: jianxing
|
||||
* @CreateTime: 2023-10-30 10:59
|
||||
* 解析器工厂
|
||||
*
|
||||
*/
|
||||
public class TestElementParserFactory {
|
||||
|
||||
/**
|
||||
* 获取默认解析器
|
||||
* @return
|
||||
*/
|
||||
public static TestElementParser getDefaultParser() {
|
||||
return new JmeterTestElementParser();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
package io.metersphere.api.parser.jmeter;
|
||||
|
||||
import io.metersphere.api.parser.TestElementParser;
|
||||
import io.metersphere.api.utils.JmeterElementConverterRegister;
|
||||
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
|
||||
import io.metersphere.plugin.api.dto.ParameterConfig;
|
||||
import io.metersphere.sdk.exception.MSException;
|
||||
import io.metersphere.sdk.util.LogUtils;
|
||||
import org.apache.jmeter.config.Arguments;
|
||||
import org.apache.jmeter.control.LoopController;
|
||||
import org.apache.jmeter.sampler.DebugSampler;
|
||||
import org.apache.jmeter.save.SaveService;
|
||||
import org.apache.jmeter.testelement.TestElement;
|
||||
import org.apache.jmeter.testelement.TestPlan;
|
||||
import org.apache.jmeter.threads.ThreadGroup;
|
||||
import org.apache.jorphan.collections.HashTree;
|
||||
import org.apache.jorphan.collections.ListedHashTree;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
||||
/**
|
||||
* @Author: jianxing
|
||||
* @CreateTime: 2023-10-27 10:07
|
||||
* <p>
|
||||
* 将 Ms 的组件转换为 jmx 脚本
|
||||
*/
|
||||
public class JmeterTestElementParser implements TestElementParser {
|
||||
|
||||
private Boolean onSampleError;
|
||||
private String name;
|
||||
private Boolean enable = true;
|
||||
private ParameterConfig config;
|
||||
private boolean displayJMeterProperties = false;
|
||||
|
||||
private boolean displayJMeterVariables = true;
|
||||
|
||||
private boolean displaySystemProperties = false;
|
||||
|
||||
@Override
|
||||
public String parse(AbstractMsTestElement msTestElement, ParameterConfig config) {
|
||||
this.config = config;
|
||||
HashTree hashTree = new ListedHashTree();
|
||||
TestPlan testPlan = getPlan();
|
||||
name = msTestElement.getName();
|
||||
enable = msTestElement.getEnable();
|
||||
final HashTree testPlanTree = hashTree.add(testPlan);
|
||||
final HashTree groupTree = testPlanTree.add(getThreadGroup());
|
||||
// 添加 debugSampler
|
||||
groupTree.add(getDebugSampler());
|
||||
|
||||
// 解析 msTestElement
|
||||
JmeterElementConverterRegister.getConverter(msTestElement.getClass())
|
||||
.toHashTree(groupTree, msTestElement, config);
|
||||
|
||||
return getJmx(hashTree);
|
||||
}
|
||||
|
||||
public String getJmx(HashTree hashTree) {
|
||||
try (ByteArrayOutputStream bas = new ByteArrayOutputStream()) {
|
||||
SaveService.saveTree(hashTree, bas);
|
||||
return bas.toString();
|
||||
} catch (Exception e) {
|
||||
LogUtils.error(e);
|
||||
throw new MSException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public TestPlan getPlan() {
|
||||
TestPlan testPlan = new TestPlan(name);
|
||||
testPlan.setProperty(TestElement.TEST_CLASS, TestPlan.class.getName());
|
||||
testPlan.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TestPlanGui"));
|
||||
testPlan.setEnabled(true);
|
||||
testPlan.setFunctionalMode(false);
|
||||
testPlan.setSerialized(false);
|
||||
testPlan.setTearDownOnShutdown(true);
|
||||
testPlan.setUserDefinedVariables(new Arguments());
|
||||
return testPlan;
|
||||
}
|
||||
|
||||
public ThreadGroup getThreadGroup() {
|
||||
LoopController loopController = new LoopController();
|
||||
loopController.setName("LoopController");
|
||||
loopController.setProperty(TestElement.TEST_CLASS, LoopController.class.getName());
|
||||
loopController.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("LoopControlPanel"));
|
||||
loopController.setEnabled(this.enable);
|
||||
loopController.setLoops(1);
|
||||
|
||||
ThreadGroup threadGroup = new ThreadGroup();
|
||||
threadGroup.setEnabled(this.enable);
|
||||
threadGroup.setName(config.getReportId());
|
||||
threadGroup.setProperty(TestElement.TEST_CLASS, ThreadGroup.class.getName());
|
||||
threadGroup.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("ThreadGroupGui"));
|
||||
threadGroup.setNumThreads(1);
|
||||
threadGroup.setRampUp(1);
|
||||
threadGroup.setDelay(0);
|
||||
threadGroup.setDuration(0);
|
||||
threadGroup.setProperty(ThreadGroup.ON_SAMPLE_ERROR, ThreadGroup.ON_SAMPLE_ERROR_CONTINUE);
|
||||
threadGroup.setScheduler(false);
|
||||
if (onSampleError != null && !onSampleError) {
|
||||
threadGroup.setProperty("ThreadGroup.on_sample_error", "stopthread");
|
||||
}
|
||||
threadGroup.setSamplerController(loopController);
|
||||
return threadGroup;
|
||||
}
|
||||
|
||||
private DebugSampler getDebugSampler() {
|
||||
DebugSampler debugSampler = new DebugSampler();
|
||||
debugSampler.setEnabled(true);
|
||||
debugSampler.setName("DebugSampler");
|
||||
debugSampler.setProperty(TestElement.TEST_CLASS, DebugSampler.class.getName());
|
||||
debugSampler.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TestBeanGUI"));
|
||||
|
||||
debugSampler.setDisplaySystemProperties(this.displaySystemProperties);
|
||||
debugSampler.setDisplayJMeterVariables(this.displayJMeterVariables);
|
||||
debugSampler.setDisplayJMeterProperties(this.displayJMeterProperties);
|
||||
|
||||
// 上面三行直接Set属性会导致DebugSampler构建时取不到值,可能是JMeter的Bug,需要SetProperty
|
||||
debugSampler.setProperty("displayJMeterProperties", this.displayJMeterProperties);
|
||||
debugSampler.setProperty("displayJMeterVariables", this.displayJMeterVariables);
|
||||
debugSampler.setProperty("displaySystemProperties", this.displaySystemProperties);
|
||||
return debugSampler;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package io.metersphere.api.parser.jmeter;
|
||||
|
||||
|
||||
import io.metersphere.api.dto.request.http.MsHTTPElement;
|
||||
import io.metersphere.plugin.api.spi.AbstractJmeterElementConverter;
|
||||
import io.metersphere.plugin.api.dto.ParameterConfig;
|
||||
import org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy;
|
||||
import org.apache.jmeter.save.SaveService;
|
||||
import org.apache.jmeter.testelement.TestElement;
|
||||
import org.apache.jorphan.collections.HashTree;
|
||||
|
||||
/**
|
||||
* @Author: jianxing
|
||||
* @CreateTime: 2023-10-27 10:07
|
||||
*
|
||||
* 脚本解析器
|
||||
*/
|
||||
public class MsHTTPElementConverter extends AbstractJmeterElementConverter<MsHTTPElement> {
|
||||
|
||||
@Override
|
||||
public void toHashTree(HashTree tree, MsHTTPElement msHTTPElement, ParameterConfig msParameter) {
|
||||
ParameterConfig config = msParameter;
|
||||
|
||||
HTTPSamplerProxy sampler = new HTTPSamplerProxy();
|
||||
sampler.setName(msHTTPElement.getName());
|
||||
sampler.setProperty(TestElement.TEST_CLASS, HTTPSamplerProxy.class.getName());
|
||||
sampler.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("HttpTestSampleGui"));
|
||||
|
||||
HashTree httpTree = tree.add(sampler);
|
||||
parseChild(httpTree, msHTTPElement, config);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package io.metersphere.api.parser.jmeter;
|
||||
|
||||
|
||||
import io.metersphere.api.dto.request.MsScenario;
|
||||
import io.metersphere.plugin.api.spi.AbstractJmeterElementConverter;
|
||||
import io.metersphere.plugin.api.dto.ParameterConfig;
|
||||
import org.apache.jorphan.collections.HashTree;
|
||||
|
||||
/**
|
||||
* @Author: jianxing
|
||||
* @CreateTime: 2023-10-27 10:07
|
||||
*
|
||||
* 脚本解析器
|
||||
*/
|
||||
public class MsScenarioConverter extends AbstractJmeterElementConverter<MsScenario> {
|
||||
|
||||
@Override
|
||||
public void toHashTree(HashTree tree, MsScenario msScenario, ParameterConfig msParameter) {
|
||||
ParameterConfig config = msParameter;
|
||||
|
||||
parseChild(tree, msScenario, config);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,179 @@
|
|||
package io.metersphere.api.service;
|
||||
|
||||
import io.metersphere.api.config.JmeterProperties;
|
||||
import io.metersphere.api.config.KafkaConfig;
|
||||
import io.metersphere.api.controller.result.ApiResultCode;
|
||||
import io.metersphere.api.dto.debug.ApiResourceRunRequest;
|
||||
import io.metersphere.api.parser.TestElementParser;
|
||||
import io.metersphere.api.parser.TestElementParserFactory;
|
||||
import io.metersphere.api.utils.ApiDataUtils;
|
||||
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
|
||||
import io.metersphere.plugin.api.dto.ParameterConfig;
|
||||
import io.metersphere.project.domain.ProjectApplication;
|
||||
import io.metersphere.project.service.ProjectApplicationService;
|
||||
import io.metersphere.sdk.constants.ProjectApplicationType;
|
||||
import io.metersphere.sdk.dto.api.task.TaskRequest;
|
||||
import io.metersphere.sdk.exception.MSException;
|
||||
import io.metersphere.sdk.util.*;
|
||||
import io.metersphere.system.config.MinioProperties;
|
||||
import io.metersphere.system.domain.TestResourcePool;
|
||||
import io.metersphere.system.dto.pool.TestResourceDTO;
|
||||
import io.metersphere.system.dto.pool.TestResourceNodeDTO;
|
||||
import io.metersphere.system.service.CommonProjectService;
|
||||
import io.metersphere.system.service.SystemParameterService;
|
||||
import io.metersphere.system.service.TestResourcePoolService;
|
||||
import io.metersphere.system.utils.TaskRunnerClient;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.jmeter.util.JMeterUtils;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.io.File;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static io.metersphere.api.controller.result.ApiResultCode.RESOURCE_POOL_EXECUTE_ERROR;
|
||||
|
||||
/**
|
||||
* @author jianxing
|
||||
* @date : 2023-11-6
|
||||
*/
|
||||
@Service
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public class ApiExecuteService {
|
||||
@Resource
|
||||
private ProjectApplicationService projectApplicationService;
|
||||
@Resource
|
||||
private TestResourcePoolService testResourcePoolService;
|
||||
@Resource
|
||||
private CommonProjectService commonProjectService;
|
||||
@Resource
|
||||
private SystemParameterService systemParameterService;
|
||||
@Resource
|
||||
private StringRedisTemplate stringRedisTemplate;
|
||||
@Resource
|
||||
private JmeterProperties jmeterProperties;
|
||||
|
||||
@PostConstruct
|
||||
private void init() {
|
||||
String jmeterHome = getJmeterHome();
|
||||
JMeterUtils.loadJMeterProperties(jmeterHome + "/bin/jmeter.properties");
|
||||
JMeterUtils.setJMeterHome(jmeterHome);
|
||||
JMeterUtils.setLocale(LocaleContextHolder.getLocale());
|
||||
}
|
||||
|
||||
public String getJmeterHome() {
|
||||
String home = getClass().getResource("/").getPath() + "jmeter";
|
||||
try {
|
||||
File file = new File(home);
|
||||
if (file.exists()) {
|
||||
return home;
|
||||
} else {
|
||||
return jmeterProperties.getHome();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return jmeterProperties.getHome();
|
||||
}
|
||||
}
|
||||
|
||||
public String getScriptRedisKey(String reportId, String testId) {
|
||||
return reportId + "_" + testId;
|
||||
}
|
||||
|
||||
public void debug(ApiResourceRunRequest request) {
|
||||
TestResourceDTO resourcePoolDTO = getAvailableResourcePoolDTO(request.getProjectId());
|
||||
String reportId = request.getReportId();
|
||||
String testId = request.getTestId();
|
||||
|
||||
TaskRequest taskRequest = new TaskRequest();
|
||||
BeanUtils.copyBean(taskRequest, request);
|
||||
|
||||
String msUrl = systemParameterService.getBaseInfo().getUrl();
|
||||
taskRequest.setKafkaConfig(EncryptUtils.aesEncrypt(JSON.toJSONString(KafkaConfig.getKafkaConfig())));
|
||||
taskRequest.setMinioConfig(EncryptUtils.aesEncrypt(JSON.toJSONString(getMinio())));
|
||||
taskRequest.setMsUrl(msUrl);
|
||||
taskRequest.setReportId(reportId);
|
||||
taskRequest.setRealTime(true);
|
||||
|
||||
// todo 环境配置
|
||||
// EnvironmentRequest environmentRequest = environmentService.get(request.getEnvironmentId());
|
||||
// todo 误报
|
||||
// todo 获取接口插件和jar包
|
||||
// todo 处理公共脚本
|
||||
|
||||
ParameterConfig parameterConfig = new ParameterConfig();
|
||||
parameterConfig.setReportId(reportId);
|
||||
String executeScript = parseExecuteScript(request.getRequest(), parameterConfig);
|
||||
|
||||
// 将测试脚本缓存到 redis
|
||||
String scriptRedisKey = getScriptRedisKey(reportId, testId);
|
||||
stringRedisTemplate.opsForValue().set(scriptRedisKey, executeScript);
|
||||
|
||||
List<TestResourceNodeDTO> nodesList = resourcePoolDTO.getNodesList();
|
||||
int index = new SecureRandom().nextInt(nodesList.size());
|
||||
TestResourceNodeDTO testResourceNodeDTO = nodesList.get(index);
|
||||
String endpoint = TaskRunnerClient.getEndpoint(testResourceNodeDTO.getIp(), testResourceNodeDTO.getPort());
|
||||
try {
|
||||
LogUtils.info(String.format("开始发送请求【 %s 】到 %s 节点执行", testId, endpoint), reportId);
|
||||
TaskRunnerClient.debugApi(endpoint, taskRequest);
|
||||
} catch (Exception e) {
|
||||
LogUtils.error(e);
|
||||
// 调用失败清理脚本
|
||||
stringRedisTemplate.delete(scriptRedisKey);
|
||||
throw new MSException(RESOURCE_POOL_EXECUTE_ERROR, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成执行脚本
|
||||
* @param testElementStr
|
||||
* @param msParameter
|
||||
* @return
|
||||
*/
|
||||
private static String parseExecuteScript(String testElementStr, ParameterConfig msParameter) {
|
||||
// 解析生成脚本
|
||||
TestElementParser defaultParser = TestElementParserFactory.getDefaultParser();
|
||||
AbstractMsTestElement msTestElement = ApiDataUtils.parseObject(testElementStr, AbstractMsTestElement.class);
|
||||
return defaultParser.parse(msTestElement, msParameter);
|
||||
}
|
||||
|
||||
|
||||
public static Map<String, String> getMinio() {
|
||||
MinioProperties minioProperties = CommonBeanFactory.getBean(MinioProperties.class);
|
||||
Map<String, String> minioPros = new HashMap<>();
|
||||
minioPros.put("endpoint", minioProperties.getEndpoint());
|
||||
minioPros.put("accessKey", minioProperties.getAccessKey());
|
||||
minioPros.put("secretKey", minioProperties.getSecretKey());
|
||||
return minioPros;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前项目配置的接口默认资源池
|
||||
* @param projectId
|
||||
* @param
|
||||
*/
|
||||
public TestResourceDTO getAvailableResourcePoolDTO(String projectId) {
|
||||
// 查询接口默认资源池
|
||||
ProjectApplication resourcePoolConfig = projectApplicationService.getByType(projectId, ProjectApplicationType.API.API_RESOURCE_POOL_ID.name());
|
||||
// 没有配置接口默认资源池
|
||||
if (resourcePoolConfig == null || StringUtils.isBlank(resourcePoolConfig.getTypeValue())) {
|
||||
throw new MSException(ApiResultCode.EXECUTE_RESOURCE_POOL_NOT_CONFIG);
|
||||
}
|
||||
String resourcePoolId = StringUtils.isBlank(resourcePoolConfig.getTypeValue()) ? null : resourcePoolConfig.getTypeValue();
|
||||
TestResourcePool testResourcePool = testResourcePoolService.getTestResourcePool(resourcePoolId);
|
||||
if (testResourcePool == null ||
|
||||
// 资源池禁用
|
||||
!testResourcePool.getEnable() ||
|
||||
// 项目没有资源池权限
|
||||
!commonProjectService.validateProjectResourcePool(testResourcePool, projectId)) {
|
||||
throw new MSException(ApiResultCode.EXECUTE_RESOURCE_POOL_NOT_CONFIG);
|
||||
}
|
||||
return testResourcePoolService.getTestResourceDTO(resourcePoolId);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package io.metersphere.api.service;
|
||||
|
||||
import io.metersphere.api.dto.request.http.MsHTTPElement;
|
||||
import io.metersphere.system.dto.ProtocolDTO;
|
||||
import io.metersphere.system.service.ApiPluginService;
|
||||
import jakarta.annotation.Resource;
|
||||
|
@ -16,11 +17,18 @@ import java.util.List;
|
|||
@Transactional(rollbackFor = Exception.class)
|
||||
public class ApiTestService {
|
||||
|
||||
private static final String HTTP_PROTOCOL = "HTTP";
|
||||
@Resource
|
||||
private ApiPluginService apiPluginService;
|
||||
|
||||
public List<ProtocolDTO> getProtocols(String orgId) {
|
||||
return apiPluginService.getProtocols(orgId);
|
||||
List<ProtocolDTO> protocols = apiPluginService.getProtocols(orgId);
|
||||
// 将 http 协议放最前面
|
||||
ProtocolDTO protocolDTO = new ProtocolDTO();
|
||||
protocolDTO.setProtocol(HTTP_PROTOCOL);
|
||||
protocolDTO.setPolymorphicName(MsHTTPElement.class.getSimpleName());
|
||||
protocols.addFirst(protocolDTO);
|
||||
return protocols;
|
||||
}
|
||||
|
||||
}
|
|
@ -8,10 +8,12 @@ import io.metersphere.api.dto.debug.*;
|
|||
import io.metersphere.api.mapper.ApiDebugBlobMapper;
|
||||
import io.metersphere.api.mapper.ApiDebugMapper;
|
||||
import io.metersphere.api.mapper.ExtApiDebugMapper;
|
||||
import io.metersphere.api.service.ApiExecuteService;
|
||||
import io.metersphere.api.service.ApiFileResourceService;
|
||||
import io.metersphere.sdk.util.ApiDataUtils;
|
||||
import io.metersphere.api.utils.ApiDataUtils;
|
||||
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
|
||||
import io.metersphere.project.service.ProjectService;
|
||||
import io.metersphere.sdk.constants.ApiExecuteRunMode;
|
||||
import io.metersphere.sdk.constants.DefaultRepositoryDir;
|
||||
import io.metersphere.sdk.exception.MSException;
|
||||
import io.metersphere.sdk.util.BeanUtils;
|
||||
|
@ -45,6 +47,8 @@ public class ApiDebugService {
|
|||
private ExtApiDebugMapper extApiDebugMapper;
|
||||
@Resource
|
||||
private ApiFileResourceService apiFileResourceService;
|
||||
@Resource
|
||||
private ApiExecuteService apiExecuteService;
|
||||
|
||||
public List<ApiDebugSimpleDTO> list(String protocol, String userId) {
|
||||
return extApiDebugMapper.list(protocol, userId);
|
||||
|
@ -162,4 +166,21 @@ public class ApiDebugService {
|
|||
public String uploadTempFile(MultipartFile file) {
|
||||
return apiFileResourceService.uploadTempFile(file);
|
||||
}
|
||||
|
||||
public String debug(ApiDebugRunRequest request) {
|
||||
String id = request.getId();
|
||||
ApiDebug apiDebug = checkResourceExist(id);
|
||||
|
||||
ApiResourceRunRequest runRequest = BeanUtils.copyBean(new ApiResourceRunRequest(), request);
|
||||
runRequest.setProjectId(apiDebug.getProjectId());
|
||||
runRequest.setTestId(id);
|
||||
runRequest.setReportId(id);
|
||||
runRequest.setResourceType(ApiResourceType.API_DEBUG.name());
|
||||
runRequest.setRunMode(ApiExecuteRunMode.DEBUG.name());
|
||||
|
||||
apiExecuteService.debug(runRequest);
|
||||
|
||||
return runRequest.getReportId();
|
||||
}
|
||||
|
||||
}
|
|
@ -6,9 +6,10 @@ import io.metersphere.api.domain.*;
|
|||
import io.metersphere.api.dto.debug.ApiFileResourceUpdateRequest;
|
||||
import io.metersphere.api.dto.definition.*;
|
||||
import io.metersphere.api.enums.ApiDefinitionDocType;
|
||||
import io.metersphere.api.enums.ApiReportStatus;
|
||||
import io.metersphere.api.mapper.*;
|
||||
import io.metersphere.api.service.ApiFileResourceService;
|
||||
import io.metersphere.api.utils.ApiDataUtils;
|
||||
import io.metersphere.sdk.constants.ApiReportStatus;
|
||||
import io.metersphere.sdk.util.*;
|
||||
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
|
||||
import io.metersphere.project.mapper.ExtBaseProjectVersionMapper;
|
||||
|
|
|
@ -13,7 +13,7 @@ import io.metersphere.plugin.api.spi.AbstractMsTestElement;
|
|||
import io.metersphere.project.domain.Project;
|
||||
import io.metersphere.project.mapper.ProjectMapper;
|
||||
import io.metersphere.sdk.constants.HttpMethodConstants;
|
||||
import io.metersphere.sdk.util.ApiDataUtils;
|
||||
import io.metersphere.api.utils.ApiDataUtils;
|
||||
import io.metersphere.sdk.util.BeanUtils;
|
||||
import io.metersphere.sdk.util.JSON;
|
||||
import io.metersphere.sdk.util.Translator;
|
||||
|
|
|
@ -6,6 +6,7 @@ import io.metersphere.api.dto.debug.ApiFileResourceUpdateRequest;
|
|||
import io.metersphere.api.dto.definition.*;
|
||||
import io.metersphere.api.mapper.*;
|
||||
import io.metersphere.api.service.ApiFileResourceService;
|
||||
import io.metersphere.api.utils.ApiDataUtils;
|
||||
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
|
||||
import io.metersphere.project.domain.Project;
|
||||
import io.metersphere.project.mapper.ProjectMapper;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.sdk.util;
|
||||
package io.metersphere.api.utils;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||
|
@ -10,7 +10,7 @@ import com.fasterxml.jackson.databind.SerializationFeature;
|
|||
import com.fasterxml.jackson.databind.json.JsonMapper;
|
||||
import com.fasterxml.jackson.databind.jsontype.NamedType;
|
||||
import com.fasterxml.jackson.databind.type.CollectionType;
|
||||
import io.metersphere.sdk.dto.api.request.sampler.MsDebugSampler;
|
||||
import io.metersphere.api.dto.request.http.MsHTTPElement;
|
||||
import io.metersphere.sdk.exception.MSException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -31,7 +31,7 @@ public class ApiDataUtils {
|
|||
|
||||
static {
|
||||
// 默认内置的子组件
|
||||
namedTypes.add(new NamedType(MsDebugSampler.class, MsDebugSampler.class.getSimpleName()));
|
||||
namedTypes.add(new NamedType(MsHTTPElement.class, MsHTTPElement.class.getSimpleName()));
|
||||
|
||||
setObjectMapper(objectMapper);
|
||||
namedTypes.forEach(objectMapper::registerSubtypes);
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue