提交 f38e0b2e authored 作者: Matrix's avatar Matrix

feat(行为执行器): 增加了行为执行器的运行与行为变量解析

上级 acd1e56e
......@@ -3,6 +3,7 @@ package org.matrix.actuators.httpclient;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.matrix.actuators.usecase.BaseTestCaseResponseDetail;
......@@ -11,6 +12,7 @@ import org.springframework.http.HttpStatus;
/**
* @author huangxiahao
*/
@EqualsAndHashCode(callSuper = true)
@Data
@AllArgsConstructor
@NoArgsConstructor
......
package org.matrix.actuators.move;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* MoveRegularObject.
*
* @author Matrix <xhyrzldf@gmail.com>
* @since 2022/3/7 at 6:24 PM
* Suffering is the most powerful teacher of life.
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MoveRegularObject {
/**
* 在ThreadLocal里的key值
*/
private String key;
/**
* 要取的字段名
*/
private String colName;
}
package org.matrix.actuators.move;
/**
* MoveStrategy.
*
* @author Matrix <xhyrzldf@gmail.com>
* @since 2022/3/7 at 3:11 PM
* Suffering is the most powerful teacher of life.
*/
public enum MoveStrategy {
/**
* 前置动作
*/
PRE_MOVE,
/**
* 中间动作
*/
MID_MOVE,
/**
* 后置动作
*/
AFT_MOVE;
}
......@@ -45,7 +45,7 @@ import static org.springframework.util.CollectionUtils.isEmpty;
public class SqlExpActuator implements Actuator {
/**
* 用于正则找出形如${id}这样的SQL变量
* 用于正则找出形如${id} 或者 ${id}[1] 这样的SQL变量
*/
public static final String DYNAMIC_VAR_EXP = "\\$\\{(\\w*)}\\[?(\\d*)]?";
......@@ -62,13 +62,51 @@ public class SqlExpActuator implements Actuator {
private final IDynamicVariableService varService;
private final IConnectService connectService;
private final IDataSourceService dataSourceService;
private final ITestCaseService caseService;
private final ITestDataService dataService;
/**
* 解析并运行SQL语句,可以包含别的SQL变量,例如 'select * from user where user.id = ${user_id} '
* @param sqlDetail sql的poolId与sql语句的对象
* @param envId 环境id
* @param projectId 项目id
* @return 运行SQL后取完数值的结果, 是一张数据表, 以LIST<MAP>的形式呈现
*/
public List<Map<String, Object>> parseSql(SqlExpDetail sqlDetail,Long envId,Long projectId){
// 找到第一层的SQL变量,递归替换
String sqlExp = sqlDetail.getSqlExp();
Long connectId = sqlDetail.getPoolId();
List<SqlRegularObject> varList = findDynamicVarList(sqlExp);
for (SqlRegularObject sqlReg : varList) {
sqlExp = sqlExp.replaceAll(sqlReg.getVarName(), parseVarByName(sqlReg.getVarName(), envId, projectId));
}
// 运行SQL
DataSourceDTO dataSourceDTO = Optional.of(connectService.getById(connectId))
.orElseThrow(() -> new GlobalException(String.format("没有找到id = %d 的连接池connect对象", connectId)))
.toDataSourceDTO();
// 替换环境共享变量
sqlExp = envActuator.replaceEnvVar(sqlExp, envId);
// 校验dynamicVar里的detail是否是可以直接执行的SQL
boolean allVarParsed = findDynamicVarList(sqlExp).size() == 0;
if (allVarParsed) {
// 切换数据源,执行SQL,获取数值
Set<String> dataSources = dataSourceService.switchDataSource(dataSourceDTO);
log.info("当前存在的数据源 {}", dataSources);
List<Map<String, Object>> resultMap = jdbcTemplate.queryForList(sqlExp);
dataSourceService.switchMainDataSource();
return resultMap;
} else {
throw new GlobalException(String.format("SQL解析异常,SQL语句为%s,连接池id为%d", sqlDetail.getSqlExp(), sqlDetail.getPoolId()));
}
}
/**
* 解析给定的动态变量ByName
*
* @param varNameString 变量名,形如${id}这样的字符串
* @param varNameString 变量名,形如${id}或者是 ${id}[1]这样的字符串,默认下标为0
* @param projectId 该变量所在的项目ID
* @return 变量递归解析后的值
*/
......@@ -170,7 +208,8 @@ public class SqlExpActuator implements Actuator {
String sqlExp = dynamicVar.getSqlExpDetail().getSqlExp();
sqlExp = envActuator.replaceEnvVar(sqlExp, envId);
List<SqlRegularObject> dynamicVarList = findDynamicVarList(dynamicVar.getDetail());
String detail = dynamicVar.getDetail();
List<SqlRegularObject> dynamicVarList = findDynamicVarList(detail);
// 解析SQL表达式,判断是可以直接执行的SQL还是需要再递归解析动态变量
if (!isEmpty(dynamicVarList)) {
// 如果还存在动态变量,则继续递归解析
......
......@@ -7,6 +7,7 @@ import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.matrix.enums.ActionType;
/**
* <p>
......@@ -37,7 +38,7 @@ public class Action extends BaseEntity {
private String remark;
@ApiModelProperty("类型 1为SQL,2为HTTP,3为CASE,4为WAIT_TIME")
private Integer type;
private ActionType type;
@ApiModelProperty("详细参数")
private String detail;
......
......@@ -3,6 +3,8 @@ package org.matrix.database.service;
import org.matrix.database.entity.Action;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.Optional;
/**
* <p>
* 动作 服务类
......@@ -13,4 +15,6 @@ import com.baomidou.mybatisplus.extension.service.IService;
*/
public interface IActionService extends IService<Action> {
Optional<Action> findByMoveAndEnv(Long moveId, Long envId);
}
package org.matrix.database.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.matrix.database.entity.Action;
import org.matrix.database.mapper.ActionMapper;
import org.matrix.database.service.IActionService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import java.util.Optional;
/**
* <p>
* 动作 服务实现类
......@@ -17,4 +21,18 @@ import org.springframework.stereotype.Service;
@Service
public class ActionServiceImpl extends ServiceImpl<ActionMapper, Action> implements IActionService {
private final ActionMapper actionMapper;
public ActionServiceImpl(ActionMapper mapper) {
this.actionMapper = mapper;
}
@Override
public Optional<Action> findByMoveAndEnv(Long moveId, Long envId) {
LambdaQueryWrapper<Action> actionWrappers = Wrappers.lambdaQuery(Action.class);
Action action = actionMapper.selectOne(actionWrappers
.eq(Action::getMoveId, moveId)
.eq(Action::getEnvId, envId));
return Optional.ofNullable(action);
}
}
......@@ -35,7 +35,7 @@ public class DynamicVariableServiceImpl extends ServiceImpl<DynamicVariableMappe
*/
@Override
public Optional<DynamicVariable> getByName(String name, Long projectId) {
return Optional.ofNullable(mapper.selectOne(Wrappers.lambdaUpdate(DynamicVariable.class)
return Optional.ofNullable(mapper.selectOne(Wrappers.lambdaQuery(DynamicVariable.class)
.eq(DynamicVariable::getName, name)
.eq(DynamicVariable::getProjectId, projectId)));
}
......
package org.matrix.enums;
import com.baomidou.mybatisplus.annotation.EnumValue;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* ActionType.
* 动作的类型,该变量决定了运行时使用哪一种执行器
*
* @author Matrix <xhyrzldf@gmail.com>
* @since 2022/2/25 at 5:18 PM
* Suffering is the most powerful teacher of life.
*/
@Getter
@AllArgsConstructor
public enum ActionType {
/**
* SQL类型动作,该动作执行SQL语句
*/
SQL_ACTION(1, "SQL类型动作"),
/**
* HTTP类型动作,该动作执行HTTP接口
*/
HTTP_ACTION(2, "HTTP类型动作"),
/**
* 用例类型动作,该动作执行另外的测试用例
*/
CASE_ACTION(3, "用例类型动作"),
/**
* 等待类动作,线程休眠指定的秒数
*/
WAIT_ACTION(4, "等待类动作");
/**
* 数据库里记录的字段使用该字段来记录
*/
@EnumValue
private final int code;
private final String des;
}
package org.matrix.util;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;
/**
* BeanFlattener. javaBean 转对象
*
* @author Matrix <xhyrzldf@gmail.com>
* @since 2022/3/7 at 5:15 PM
* Suffering is the most powerful teacher of life.
*/
public final class BeanFlattener {
private BeanFlattener() {}
public static Map<String, Object> deepToMap(Object bean) {
Map<String, Object> map = new LinkedHashMap<>();
try {
putValues(bean, map, null);
} catch (IllegalAccessException x) {
throw new IllegalArgumentException(x);
}
return map;
}
private static void putValues(Object bean,
Map<String, Object> map,
String prefix)
throws IllegalAccessException {
Class<?> cls = bean.getClass();
for (Field field : cls.getDeclaredFields()) {
if (field.isSynthetic() || Modifier.isStatic(field.getModifiers())) {
continue;
}
field.setAccessible(true);
Object value = field.get(bean);
String key;
if (prefix == null) {
key = field.getName();
} else {
key = prefix + "." + field.getName();
}
if (isValue(value)) {
map.put(key, value);
} else {
putValues(value, map, key);
}
}
}
private static final Set<Class<?>> VALUE_CLASSES =
Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
Object.class, String.class, Boolean.class,
Character.class, Byte.class, Short.class,
Integer.class, Long.class, Float.class,
Double.class
// etc.
)));
private static boolean isValue(Object value) {
return value == null
|| value instanceof Enum<?>
|| VALUE_CLASSES.contains(value.getClass());
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论