feat(接口测试): 接口调试执行示例

This commit is contained in:
AgAngle 2023-12-13 09:57:30 +08:00 committed by Craftsman
parent 4e4f17f195
commit d8e0fd0bc7
124 changed files with 3390 additions and 280 deletions

View File

@ -1,5 +1,6 @@
package io.metersphere; package io.metersphere;
import io.metersphere.api.config.JmeterProperties;
import io.metersphere.system.config.MinioProperties; import io.metersphere.system.config.MinioProperties;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
@ -21,7 +22,8 @@ import org.springframework.context.annotation.PropertySource;
}, encoding = "UTF-8", ignoreResourceNotFound = true) }, encoding = "UTF-8", ignoreResourceNotFound = true)
@ServletComponentScan @ServletComponentScan
@EnableConfigurationProperties({ @EnableConfigurationProperties({
MinioProperties.class MinioProperties.class,
JmeterProperties.class
}) })
public class Application { public class Application {
public static void main(String[] args) { public static void main(String[] args) {

View File

@ -2,9 +2,9 @@ package io.metersphere.listener;
import io.metersphere.api.event.ApiEventSource; import io.metersphere.api.event.ApiEventSource;
import io.metersphere.plan.listener.ExecEventListener; import io.metersphere.plan.listener.ExecEventListener;
import io.metersphere.system.service.PluginLoadService;
import io.metersphere.sdk.util.CommonBeanFactory; import io.metersphere.sdk.util.CommonBeanFactory;
import io.metersphere.sdk.util.LogUtils; import io.metersphere.sdk.util.LogUtils;
import io.metersphere.system.service.PluginLoadService;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner; import org.springframework.boot.ApplicationRunner;

View File

@ -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;
}

View File

@ -1,12 +1,12 @@
package io.metersphere.plugin.api.spi; package io.metersphere.plugin.api.spi;
import io.metersphere.plugin.api.dto.ParameterConfig;
import org.apache.jorphan.collections.HashTree; import org.apache.jorphan.collections.HashTree;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.HashMap; import java.util.function.Function;
import java.util.Map;
/** /**
* @author jianxing * @author jianxing
@ -15,44 +15,39 @@ import java.util.Map;
*/ */
public abstract class AbstractJmeterElementConverter<T extends MsTestElement> { 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() { public AbstractJmeterElementConverter() {
Type genericSuperclass = getClass().getGenericSuperclass(); Type genericSuperclass = getClass().getGenericSuperclass();
if (genericSuperclass instanceof ParameterizedType parameterizedType) { if (genericSuperclass instanceof ParameterizedType parameterizedType) {
// 获取泛型的具体类型 MsTestElement 的具体实现类 // 获取泛型的具体类型 MsTestElement 的具体实现类
MsTestElement baseClass = (MsTestElement) parameterizedType.getActualTypeArguments()[0]; testElementClass = ((Class) parameterizedType.getActualTypeArguments()[0]);
// 注册到解析器集合中
parserMap.put(baseClass, this);
} }
} }
/** /**
* MsTestElement 具体实现类转换为 HashTree * MsTestElement 具体实现类转换为 HashTree
*/ */
public abstract void toHashTree(HashTree tree, T element, MsParameter config); public abstract void toHashTree(HashTree tree, T element, ParameterConfig config);
/** /**
* 解析 MsTestElement 的子节点 * 解析 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) { if (element != null && element.getChildren() != null) {
element.getChildren().forEach(child -> { element.getChildren().forEach(child ->
getConverter(child).toHashTree(tree, element, config); getConverterFunc.apply(child.getClass())
}); .toHashTree(tree, element, config));
} }
} }
/**
* 获取对应组件的转换器
*/
public static AbstractJmeterElementConverter getConverter(MsTestElement element) {
return parserMap.get(element);
}
} }

View File

@ -1,8 +0,0 @@
package io.metersphere.plugin.api.spi;
/**
* @Author: jianxing
* @CreateTime: 2023-10-27 17:30
*/
public class MsParameter {
}

View File

@ -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
}

View File

@ -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
}

View File

@ -1,4 +1,4 @@
package io.metersphere.api.enums; package io.metersphere.sdk.constants;
/** /**
* @author: LAN * @author: LAN

View File

@ -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 {
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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<>();
}

View File

@ -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;
}

View File

@ -1,15 +1,10 @@
package io.metersphere.sdk.dto.api.task; 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 lombok.Data;
import org.apache.commons.collections4.MapUtils;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.security.NoSuchAlgorithmException; import java.util.List;
import java.util.Map;
/** /**
* 任务请求参数数据 * 任务请求参数数据
@ -24,27 +19,28 @@ public class TaskRequest implements Serializable {
private String kafkaConfig; private String kafkaConfig;
private String minioConfig; private String minioConfig;
private String poolId; private String poolId;
/**
* 是否需要实时接收单个步骤的结果
*/
private Boolean realTime;
/**
* 执行的资源ID
*/
private String testId;
/**
* 点击调试时尚未保存的文件ID列表
*/
private List<String> tempFileIds;
/**
* 执行模式
*/
private String runMode;
/**
* 资源类型
*/
private String resourceType;
// TODO 其它执行参数 // 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);
}
}
}
} }

View File

@ -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: 补充任务执行结果数据结构
}

View File

@ -6,11 +6,13 @@ import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Map; import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
@Component
public class CommonBeanFactory implements ApplicationContextAware { public class CommonBeanFactory implements ApplicationContextAware {
private static ApplicationContext context; private static ApplicationContext context;

View File

@ -9,14 +9,14 @@ public class EncryptUtils extends CodingUtils {
private static final String iv = "1234567890123456"; private static final String iv = "1234567890123456";
public static Object aesEncrypt(Object o) { public static String aesEncrypt(Object o) {
if (o == null) { if (o == null) {
return null; return null;
} }
return aesEncrypt(o.toString(), secretKey, iv); return aesEncrypt(o.toString(), secretKey, iv);
} }
public static Object aesDecrypt(Object o) { public static String aesDecrypt(Object o) {
if (o == null) { if (o == null) {
return null; return null;
} }
@ -33,7 +33,7 @@ public class EncryptUtils extends CodingUtils {
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
public static Object md5Encrypt(Object o) { public static String md5Encrypt(Object o) {
if (o == null) { if (o == null) {
return null; return null;
} }

View File

@ -31,6 +31,9 @@ public class FilterChainUtils {
//用户通过邮箱邀请自行注册的接口 //用户通过邮箱邀请自行注册的接口
filterChainDefinitionMap.put("/system/user/register-by-invite", "anon"); filterChainDefinitionMap.put("/system/user/register-by-invite", "anon");
// 下载测试资源
filterChainDefinitionMap.put("/api/execute/resource/**", "anon");
// for swagger // for swagger
filterChainDefinitionMap.put("/swagger-ui.html", "anon"); filterChainDefinitionMap.put("/swagger-ui.html", "anon");

View File

@ -296,3 +296,6 @@ api_debug_exist=接口已存在
follow=关注 follow=关注
unfollow=取消关注 unfollow=取消关注
api_definition_exist=接口已存在 api_definition_exist=接口已存在
execute_resource_pool_not_config_error=请在【项目管理-应用管理-接口测试】中选择资源池
resource_pool_execute_error=资源池调用失败

View File

@ -304,3 +304,5 @@ api_debug_exist=The API already exists
follow=Follow follow=Follow
unfollow=Unfollow unfollow=Unfollow
api_definition_exist=The API already exists 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

View File

@ -304,3 +304,5 @@ api_debug_exist=接口已存在
follow=关注 follow=关注
unfollow=取消关注 unfollow=取消关注
api_definition_exist=接口已存在 api_definition_exist=接口已存在
execute_resource_pool_not_config_error=请在【项目管理-应用管理-接口测试】中选择资源池
resource_pool_execute_error=资源池调用失败

View File

@ -304,3 +304,5 @@ api_debug_exist=接口已存在
follow=关注 follow=关注
unfollow=取消关注 unfollow=取消关注
api_definition_exist=接口已存在 api_definition_exist=接口已存在
execute_resource_pool_not_config_error=請在【項目管理-應用管理-接口測試】中選擇資源池
resource_pool_execute_error=資源池調用失敗

View File

@ -47,6 +47,11 @@
</exclusion> </exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
<dependency>
<groupId>org.apache.jmeter</groupId>
<artifactId>ApacheJMeter_http</artifactId>
<version>${jmeter.version}</version>
</dependency>
<!-- swagger 解析 --> <!-- swagger 解析 -->
<dependency> <dependency>
<groupId>io.swagger.parser.v3</groupId> <groupId>io.swagger.parser.v3</groupId>

View File

@ -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;
}

View File

@ -1,4 +1,4 @@
package io.metersphere.api.enums; package io.metersphere.api.constants;
/** /**
* @author: LAN * @author: LAN

View File

@ -1,4 +1,4 @@
package io.metersphere.api.enums; package io.metersphere.api.constants;
public enum ApiImportPlatform { public enum ApiImportPlatform {
MeterSphere, Postman, Swagger3, Plugin, Jmeter, Har MeterSphere, Postman, Swagger3, Plugin, Jmeter, Har

View File

@ -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);
}
}

View File

@ -1,10 +1,7 @@
package io.metersphere.api.controller.debug; package io.metersphere.api.controller.debug;
import io.metersphere.api.domain.ApiDebug; import io.metersphere.api.domain.ApiDebug;
import io.metersphere.api.dto.debug.ApiDebugAddRequest; import io.metersphere.api.dto.debug.*;
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.service.debug.ApiDebugLogService; import io.metersphere.api.service.debug.ApiDebugLogService;
import io.metersphere.api.service.debug.ApiDebugService; import io.metersphere.api.service.debug.ApiDebugService;
import io.metersphere.sdk.constants.PermissionConstants; import io.metersphere.sdk.constants.PermissionConstants;
@ -78,4 +75,11 @@ public class ApiDebugController {
public void delete(@PathVariable String id) { public void delete(@PathVariable String id) {
apiDebugService.delete(id, SessionUtils.getUserId()); 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);
}
} }

View File

@ -10,7 +10,9 @@ public enum ApiResultCode implements IResultCode {
API_DEBUG_EXIST(104001, "api_debug_exist"), API_DEBUG_EXIST(104001, "api_debug_exist"),
API_DEFINITION_EXIST(104002, "api_definition_exist"), API_DEFINITION_EXIST(104002, "api_definition_exist"),
API_DEFINITION_NOT_EXIST(104003, "resource_not_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; private final int code;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -1,7 +1,7 @@
package io.metersphere.api.dto.definition; package io.metersphere.api.dto.definition;
import io.metersphere.sdk.dto.api.request.http.Header; import io.metersphere.api.dto.request.http.Header;
import io.metersphere.sdk.dto.api.request.http.body.Body; import io.metersphere.api.dto.request.http.body.Body;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;

View File

@ -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 io.metersphere.plugin.api.spi.AbstractMsTestElement;
import lombok.Data; import lombok.Data;

View File

@ -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 {
}

View File

@ -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.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo;

View File

@ -1,4 +1,4 @@
package io.metersphere.sdk.dto.api.request.assertion; package io.metersphere.api.dto.request.assertion;
import lombok.Data; import lombok.Data;

View File

@ -1,4 +1,4 @@
package io.metersphere.sdk.dto.api.request.assertion; package io.metersphere.api.dto.request.assertion;
/** /**
* body断言中的断言类型 * body断言中的断言类型

View File

@ -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 com.fasterxml.jackson.annotation.JsonTypeName;
import io.metersphere.sdk.dto.api.request.assertion.body.DocumentAssertion; import io.metersphere.api.dto.request.assertion.body.DocumentAssertion;
import io.metersphere.sdk.dto.api.request.assertion.body.JSONPathAssertion; import io.metersphere.api.dto.request.assertion.body.JSONPathAssertion;
import io.metersphere.sdk.dto.api.request.assertion.body.RegexAssertion; import io.metersphere.api.dto.request.assertion.body.RegexAssertion;
import io.metersphere.sdk.dto.api.request.assertion.body.XPathAssertion; import io.metersphere.api.dto.request.assertion.body.XPathAssertion;
import lombok.Data; import lombok.Data;
/** /**

View File

@ -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 com.fasterxml.jackson.annotation.JsonTypeName;
import lombok.Data; import lombok.Data;

View File

@ -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 com.fasterxml.jackson.annotation.JsonTypeName;
import lombok.Data; import lombok.Data;

View File

@ -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 com.fasterxml.jackson.annotation.JsonTypeName;
import lombok.Data; import lombok.Data;

View File

@ -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 com.fasterxml.jackson.annotation.JsonTypeName;
import lombok.Data; import lombok.Data;

View File

@ -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 com.fasterxml.jackson.annotation.JsonTypeName;
import lombok.Data; import lombok.Data;

View File

@ -1,4 +1,4 @@
package io.metersphere.sdk.dto.api.request.assertion.body; package io.metersphere.api.dto.request.assertion.body;
import lombok.Data; import lombok.Data;

View File

@ -1,4 +1,4 @@
package io.metersphere.sdk.dto.api.request.assertion.body; package io.metersphere.api.dto.request.assertion.body;
import lombok.Data; import lombok.Data;

View File

@ -1,4 +1,4 @@
package io.metersphere.sdk.dto.api.request.assertion.body; package io.metersphere.api.dto.request.assertion.body;
import lombok.Data; import lombok.Data;

View File

@ -1,4 +1,4 @@
package io.metersphere.sdk.dto.api.request.assertion.body; package io.metersphere.api.dto.request.assertion.body;
import lombok.Data; import lombok.Data;

View File

@ -1,4 +1,4 @@
package io.metersphere.sdk.dto.api.request.assertion.body; package io.metersphere.api.dto.request.assertion.body;
import lombok.Data; import lombok.Data;

View File

@ -1,4 +1,4 @@
package io.metersphere.sdk.dto.api.request.assertion.body; package io.metersphere.api.dto.request.assertion.body;
import lombok.Data; import lombok.Data;

View File

@ -1,4 +1,4 @@
package io.metersphere.sdk.dto.api.request.assertion.body; package io.metersphere.api.dto.request.assertion.body;
import lombok.Data; import lombok.Data;

View File

@ -1,4 +1,4 @@
package io.metersphere.sdk.dto.api.request.assertion.body; package io.metersphere.api.dto.request.assertion.body;
import lombok.Data; import lombok.Data;

View File

@ -1,4 +1,4 @@
package io.metersphere.sdk.dto.api.request.assertion.body; package io.metersphere.api.dto.request.assertion.body;
import lombok.Data; import lombok.Data;

View File

@ -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.annotation.PluginSubType;
import io.metersphere.plugin.api.dto.TestElementDTO; import io.metersphere.plugin.api.dto.TestElementDTO;

View File

@ -1,4 +1,4 @@
package io.metersphere.sdk.dto.api.request.http; package io.metersphere.api.dto.request.http;
import lombok.Data; import lombok.Data;

View File

@ -1,4 +1,4 @@
package io.metersphere.sdk.dto.api.request.http; package io.metersphere.api.dto.request.http;
import lombok.Data; import lombok.Data;

View File

@ -1,4 +1,4 @@
package io.metersphere.sdk.dto.api.request.http; package io.metersphere.api.dto.request.http;
import lombok.Data; import lombok.Data;

View File

@ -1,4 +1,4 @@
package io.metersphere.sdk.dto.api.request.http; package io.metersphere.api.dto.request.http;
import lombok.Data; import lombok.Data;

View File

@ -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.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.sdk.dto.api.request.assertion.MsAssertionConfig; import io.metersphere.api.dto.request.assertion.MsAssertionConfig;
import io.metersphere.sdk.dto.api.request.http.auth.HTTPAuth; import io.metersphere.api.dto.request.http.auth.HTTPAuth;
import io.metersphere.sdk.dto.api.request.http.body.Body; import io.metersphere.api.dto.request.http.body.Body;
import io.metersphere.sdk.dto.api.request.processors.MsProcessorConfig; import io.metersphere.api.dto.request.processors.MsProcessorConfig;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;

View File

@ -1,4 +1,4 @@
package io.metersphere.sdk.dto.api.request.http; package io.metersphere.api.dto.request.http;
import lombok.Data; import lombok.Data;

View File

@ -1,4 +1,4 @@
package io.metersphere.sdk.dto.api.request.http; package io.metersphere.api.dto.request.http;
import lombok.Data; import lombok.Data;

View File

@ -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 com.fasterxml.jackson.annotation.JsonTypeName;
import lombok.Data; import lombok.Data;

View File

@ -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 com.fasterxml.jackson.annotation.JsonTypeName;
import lombok.Data; import lombok.Data;

View File

@ -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.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo;

View File

@ -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 com.fasterxml.jackson.annotation.JsonTypeName;
import lombok.Data; import lombok.Data;

View File

@ -1,4 +1,4 @@
package io.metersphere.sdk.dto.api.request.http.body; package io.metersphere.api.dto.request.http.body;
import lombok.Data; import lombok.Data;

View File

@ -1,4 +1,4 @@
package io.metersphere.sdk.dto.api.request.http.body; package io.metersphere.api.dto.request.http.body;
import lombok.Data; import lombok.Data;

View File

@ -1,4 +1,4 @@
package io.metersphere.sdk.dto.api.request.http.body; package io.metersphere.api.dto.request.http.body;
import lombok.Data; import lombok.Data;

View File

@ -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; import lombok.Data;
/** /**

View File

@ -1,4 +1,4 @@
package io.metersphere.sdk.dto.api.request.http.body; package io.metersphere.api.dto.request.http.body;
import lombok.Data; import lombok.Data;

View File

@ -1,4 +1,4 @@
package io.metersphere.sdk.dto.api.request.http.body; package io.metersphere.api.dto.request.http.body;
import lombok.Data; import lombok.Data;

View File

@ -1,4 +1,4 @@
package io.metersphere.sdk.dto.api.request.http.body; package io.metersphere.api.dto.request.http.body;
import lombok.Data; import lombok.Data;

View File

@ -1,4 +1,4 @@
package io.metersphere.sdk.dto.api.request.http.body; package io.metersphere.api.dto.request.http.body;
import lombok.Data; import lombok.Data;

View File

@ -1,4 +1,4 @@
package io.metersphere.sdk.dto.api.request.http.body; package io.metersphere.api.dto.request.http.body;
import lombok.Data; import lombok.Data;

View File

@ -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 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 lombok.Data;
import java.util.List; import java.util.List;

View File

@ -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 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 lombok.Data;
import java.util.List; import java.util.List;

View File

@ -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.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo;

View File

@ -1,4 +1,4 @@
package io.metersphere.sdk.dto.api.request.processors; package io.metersphere.api.dto.request.processors;
import lombok.Data; import lombok.Data;

View File

@ -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 com.fasterxml.jackson.annotation.JsonTypeName;
import io.metersphere.sdk.dto.api.request.http.KeyValueEnableParam; import io.metersphere.api.dto.request.http.KeyValueEnableParam;
import io.metersphere.sdk.dto.api.request.http.KeyValueParam; import io.metersphere.api.dto.request.http.KeyValueParam;
import lombok.Data; import lombok.Data;
import java.util.List; import java.util.List;

View File

@ -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 com.fasterxml.jackson.annotation.JsonTypeName;
import lombok.Data; import lombok.Data;

View File

@ -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 com.fasterxml.jackson.annotation.JsonTypeName;
import lombok.Data; import lombok.Data;

View File

@ -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 com.fasterxml.jackson.annotation.JsonTypeName;
import lombok.Data; import lombok.Data;

View File

@ -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.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo;

View File

@ -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 com.fasterxml.jackson.annotation.JsonTypeName;
import lombok.Data; import lombok.Data;

View File

@ -1,4 +1,4 @@
package io.metersphere.sdk.dto.api.request.processors.extract; package io.metersphere.api.dto.request.processors.extract;
import lombok.Data; import lombok.Data;

View File

@ -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 com.fasterxml.jackson.annotation.JsonTypeName;
import lombok.Data; import lombok.Data;

View File

@ -1,6 +1,6 @@
package io.metersphere.api.parser; 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.PostmanParser;
import io.metersphere.api.parser.api.Swagger3Parser; import io.metersphere.api.parser.api.Swagger3Parser;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;

View File

@ -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);
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -1,5 +1,6 @@
package io.metersphere.api.service; package io.metersphere.api.service;
import io.metersphere.api.dto.request.http.MsHTTPElement;
import io.metersphere.system.dto.ProtocolDTO; import io.metersphere.system.dto.ProtocolDTO;
import io.metersphere.system.service.ApiPluginService; import io.metersphere.system.service.ApiPluginService;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
@ -16,11 +17,18 @@ import java.util.List;
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public class ApiTestService { public class ApiTestService {
private static final String HTTP_PROTOCOL = "HTTP";
@Resource @Resource
private ApiPluginService apiPluginService; private ApiPluginService apiPluginService;
public List<ProtocolDTO> getProtocols(String orgId) { 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;
} }
} }

View File

@ -8,10 +8,12 @@ import io.metersphere.api.dto.debug.*;
import io.metersphere.api.mapper.ApiDebugBlobMapper; import io.metersphere.api.mapper.ApiDebugBlobMapper;
import io.metersphere.api.mapper.ApiDebugMapper; import io.metersphere.api.mapper.ApiDebugMapper;
import io.metersphere.api.mapper.ExtApiDebugMapper; import io.metersphere.api.mapper.ExtApiDebugMapper;
import io.metersphere.api.service.ApiExecuteService;
import io.metersphere.api.service.ApiFileResourceService; 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.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.project.service.ProjectService; import io.metersphere.project.service.ProjectService;
import io.metersphere.sdk.constants.ApiExecuteRunMode;
import io.metersphere.sdk.constants.DefaultRepositoryDir; import io.metersphere.sdk.constants.DefaultRepositoryDir;
import io.metersphere.sdk.exception.MSException; import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.BeanUtils; import io.metersphere.sdk.util.BeanUtils;
@ -45,6 +47,8 @@ public class ApiDebugService {
private ExtApiDebugMapper extApiDebugMapper; private ExtApiDebugMapper extApiDebugMapper;
@Resource @Resource
private ApiFileResourceService apiFileResourceService; private ApiFileResourceService apiFileResourceService;
@Resource
private ApiExecuteService apiExecuteService;
public List<ApiDebugSimpleDTO> list(String protocol, String userId) { public List<ApiDebugSimpleDTO> list(String protocol, String userId) {
return extApiDebugMapper.list(protocol, userId); return extApiDebugMapper.list(protocol, userId);
@ -162,4 +166,21 @@ public class ApiDebugService {
public String uploadTempFile(MultipartFile file) { public String uploadTempFile(MultipartFile file) {
return apiFileResourceService.uploadTempFile(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();
}
} }

View File

@ -6,9 +6,10 @@ import io.metersphere.api.domain.*;
import io.metersphere.api.dto.debug.ApiFileResourceUpdateRequest; import io.metersphere.api.dto.debug.ApiFileResourceUpdateRequest;
import io.metersphere.api.dto.definition.*; import io.metersphere.api.dto.definition.*;
import io.metersphere.api.enums.ApiDefinitionDocType; import io.metersphere.api.enums.ApiDefinitionDocType;
import io.metersphere.api.enums.ApiReportStatus;
import io.metersphere.api.mapper.*; import io.metersphere.api.mapper.*;
import io.metersphere.api.service.ApiFileResourceService; 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.sdk.util.*;
import io.metersphere.plugin.api.spi.AbstractMsTestElement; import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.project.mapper.ExtBaseProjectVersionMapper; import io.metersphere.project.mapper.ExtBaseProjectVersionMapper;

View File

@ -13,7 +13,7 @@ import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.project.domain.Project; import io.metersphere.project.domain.Project;
import io.metersphere.project.mapper.ProjectMapper; import io.metersphere.project.mapper.ProjectMapper;
import io.metersphere.sdk.constants.HttpMethodConstants; 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.BeanUtils;
import io.metersphere.sdk.util.JSON; import io.metersphere.sdk.util.JSON;
import io.metersphere.sdk.util.Translator; import io.metersphere.sdk.util.Translator;

View File

@ -6,6 +6,7 @@ import io.metersphere.api.dto.debug.ApiFileResourceUpdateRequest;
import io.metersphere.api.dto.definition.*; import io.metersphere.api.dto.definition.*;
import io.metersphere.api.mapper.*; import io.metersphere.api.mapper.*;
import io.metersphere.api.service.ApiFileResourceService; import io.metersphere.api.service.ApiFileResourceService;
import io.metersphere.api.utils.ApiDataUtils;
import io.metersphere.plugin.api.spi.AbstractMsTestElement; import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.project.domain.Project; import io.metersphere.project.domain.Project;
import io.metersphere.project.mapper.ProjectMapper; import io.metersphere.project.mapper.ProjectMapper;

View File

@ -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.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor; 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.json.JsonMapper;
import com.fasterxml.jackson.databind.jsontype.NamedType; import com.fasterxml.jackson.databind.jsontype.NamedType;
import com.fasterxml.jackson.databind.type.CollectionType; 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 io.metersphere.sdk.exception.MSException;
import java.io.IOException; import java.io.IOException;
@ -31,7 +31,7 @@ public class ApiDataUtils {
static { static {
// 默认内置的子组件 // 默认内置的子组件
namedTypes.add(new NamedType(MsDebugSampler.class, MsDebugSampler.class.getSimpleName())); namedTypes.add(new NamedType(MsHTTPElement.class, MsHTTPElement.class.getSimpleName()));
setObjectMapper(objectMapper); setObjectMapper(objectMapper);
namedTypes.forEach(objectMapper::registerSubtypes); namedTypes.forEach(objectMapper::registerSubtypes);

Some files were not shown because too many files have changed in this diff Show More