提交 1c7070a2 authored 作者: Matrix's avatar Matrix

fix(base): 提交了演示部分

上级 3dd24695
...@@ -29,7 +29,7 @@ public class EnvironmentActuator implements Actuator { ...@@ -29,7 +29,7 @@ public class EnvironmentActuator implements Actuator {
/** /**
* 用于正则找出形如#{id}这样的环境 * 用于正则找出形如#{id}这样的环境
*/ */
public static final String ENV_VAR_EXP = "(#\\{([\\w|[\\u4e00-\\u9fa5]]*)})"; public static final String ENV_VAR_EXP = "(#\\{([\\w|\\u4e00-\\u9fa5]*)})";
/** /**
* 替换掉SQL表达式中的形如#{id}这样的环境共享变量 * 替换掉SQL表达式中的形如#{id}这样的环境共享变量
...@@ -49,10 +49,7 @@ public class EnvironmentActuator implements Actuator { ...@@ -49,10 +49,7 @@ public class EnvironmentActuator implements Actuator {
.orElseThrow(() -> new GlobalException("没有找到对应ID的example(env)对象,id = " + envId)); .orElseThrow(() -> new GlobalException("没有找到对应ID的example(env)对象,id = " + envId));
Map<String, String> envMap = env.getVariable(); Map<String, String> envMap = env.getVariable();
List<String> envList = ReUtil.findAll(ENV_VAR_EXP, sqlExp, 2, new ArrayList<>()); List<String> envList = ReUtil.findAll(ENV_VAR_EXP, sqlExp, 2, new ArrayList<>());
// 对传入的表达式进行一次验证
if (envList.size() == 0) {
throw new GlobalException(String.format("没有在表达式中 %s 寻找到环境变量,您需要提供例如 `#{var_example}`这样的语法", sqlExp));
}
// 到对应env的变量池里找到值替换掉 // 到对应env的变量池里找到值替换掉
for (String key : envList) { for (String key : envList) {
if (envMap.containsKey(key)) { if (envMap.containsKey(key)) {
......
...@@ -113,7 +113,7 @@ public class SqlExpActuator implements Actuator { ...@@ -113,7 +113,7 @@ public class SqlExpActuator implements Actuator {
* @return 变量递归解析后的值 * @return 变量递归解析后的值
*/ */
public String parseVarByName(String varNameString, Long envId, Long projectId) { public String parseVarByName(String varNameString, Long envId, Long projectId) {
LogQueueRuntime.addNewLog(this.getClass(), SQL_ACTUATOR, String.format("初始化即系变量,变量名=%s,环境id=%d,项目id=%d", varNameString, envId, projectId)); LogQueueRuntime.addNewLog(this.getClass(), SQL_ACTUATOR, String.format("初始化变量,变量名=%s,环境id=%d,项目id=%d", varNameString, envId, projectId));
List<SqlRegularObject> varList = findDynamicVarList(varNameString); List<SqlRegularObject> varList = findDynamicVarList(varNameString);
if (varList.size() == 1) { if (varList.size() == 1) {
SqlRegularObject sqlReg = varList.get(0); SqlRegularObject sqlReg = varList.get(0);
...@@ -122,7 +122,7 @@ public class SqlExpActuator implements Actuator { ...@@ -122,7 +122,7 @@ public class SqlExpActuator implements Actuator {
.orElseThrow(() -> new GlobalException(String.format("没有找到project id = %d 且 name = %s 的动态变量", projectId, sqlReg.getVarName()))); .orElseThrow(() -> new GlobalException(String.format("没有找到project id = %d 且 name = %s 的动态变量", projectId, sqlReg.getVarName())));
return parseVar(envId, sqlReg.getIndex(), projectId, variable); return parseVar(envId, sqlReg.getIndex(), projectId, variable);
} else { } else {
throw new GlobalException("varNameString 参数请一次只输入一个动态变量! 你的提供的参数为: " + varNameString); throw new GlobalException(String.format("varNameString 参数请一次只输入一个动态变量(形如 ${id})! 你的提供的参数为: %s , 解析后的变量列表是 %s", varNameString, varList));
} }
} }
...@@ -208,10 +208,14 @@ public class SqlExpActuator implements Actuator { ...@@ -208,10 +208,14 @@ public class SqlExpActuator implements Actuator {
* @param dynamicVar {@link DynamicVariable} * @param dynamicVar {@link DynamicVariable}
* @return 解析后的字符串 * @return 解析后的字符串
*/ */
@SuppressWarnings("DuplicatedCode")
private String sqlVarHandler(Long envId, Integer takenIndex, Long projectId, DynamicVariable dynamicVar) { private String sqlVarHandler(Long envId, Integer takenIndex, Long projectId, DynamicVariable dynamicVar) {
// 首先替换掉#{id}这类的共享变量(如果有的话) // 首先替换掉#{id}这类的共享变量(如果有的话)
String sqlExp = dynamicVar.getSqlExpDetail().getSqlExp(); String sqlExp = dynamicVar.getSqlExpDetail().getSqlExp();
sqlExp = envActuator.replaceEnvVar(sqlExp, envId); if (envId!=null && envId > 0){
sqlExp = envActuator.replaceEnvVar(sqlExp, envId);
}
String detail = dynamicVar.getDetail(); String detail = dynamicVar.getDetail();
List<SqlRegularObject> dynamicVarList = findDynamicVarList(detail); List<SqlRegularObject> dynamicVarList = findDynamicVarList(detail);
...@@ -230,7 +234,7 @@ public class SqlExpActuator implements Actuator { ...@@ -230,7 +234,7 @@ public class SqlExpActuator implements Actuator {
} }
} }
// 执行SQL // 执行SQL
List<Map<String, Object>> resultList = runSql(dynamicVar, envId); List<Map<String, Object>> resultList = runSqlDirectly(dynamicVar, envId);
//正则 //正则
return takenResultString(resultList, dynamicVar.getTakenField(), Math.toIntExact(takenIndex)); return takenResultString(resultList, dynamicVar.getTakenField(), Math.toIntExact(takenIndex));
} }
...@@ -252,15 +256,56 @@ public class SqlExpActuator implements Actuator { ...@@ -252,15 +256,56 @@ public class SqlExpActuator implements Actuator {
JSON.toJSON(resultMap), takenField))).toString(); JSON.toJSON(resultMap), takenField))).toString();
} }
/**
* 递归的运行一段SQL语句,可以解析出其中的环境变量(#{id}),动态变量(${id})
*
* @param projectId 用于确定寻找哪些动态变量的id
* @param envId 用于确定使用的环境变量池的id,如果envId = null,则代表无需替换环境变量
* @param connectId 用于确定使用哪个数据源
* @return 运行SQL后取完数值的结果, 是一张数据表, 以LIST<MAP>的形式呈现
*/
@SuppressWarnings("DuplicatedCode")
public List<Map<String, Object>> runSqlRec(String sqlExp, Long projectId, Long envId, Long connectId) {
// 替换环境表达式
if (envId!=null && envId > 0){
sqlExp = envActuator.replaceEnvVar(sqlExp, envId);
}
List<SqlRegularObject> dynamicVarList = findDynamicVarList(sqlExp);
// 解析SQL表达式,判断是可以直接执行的SQL还是需要再递归解析动态变量
if (!isEmpty(dynamicVarList)) {
// 如果还存在动态变量,则继续递归解析
for (SqlRegularObject sqlRegular : dynamicVarList) {
DynamicVariable recVar = varService.getByName(sqlRegular.getVarName(), projectId)
.map(DynamicVariable::parseSqlDetail)
.orElseThrow(() -> new GlobalException(
String.format("没有找到项目id = %d 下,name = %s的变量", projectId, sqlRegular.getVarName())));
String calculatedValue = parseVar(envId, sqlRegular.getIndex(), projectId, recVar);
LogQueueRuntime.addNewLog(this.getClass(), SQL_ACTUATOR, String.format("正在进行SQL变量计算 变量名: %s 计算后的替换值: %s", sqlRegular.getVarName(), calculatedValue));
sqlExp = sqlExp.replaceAll(String.format(REPLACE_VAR_EXP, sqlRegular.getVarName()), calculatedValue);
}
}
// 切换数据源,执行SQL
DataSourceDTO dataSourceDTO = Optional.of(connectService.getById(connectId))
.orElseThrow(() -> new GlobalException(String.format("没有找到id = %d 的连接池connect对象", connectId)))
.toDataSourceDTO();
// 切换数据源,执行SQL,获取数值
LogQueueRuntime.addNewLog(getClass(), SQL_ACTUATOR, String.format("当前执行的SQL语句: %s, 使用的数据源: %s", sqlExp, dataSourceDTO));
Set<String> dataSources = dataSourceService.switchDataSource(dataSourceDTO);
log.info("当前使用的的数据源 {}", dataSourceService.peek());
List<Map<String, Object>> resultMap = jdbcTemplate.queryForList(sqlExp);
dataSourceService.switchMainDataSource();
return resultMap;
}
/** /**
* 执行一段SQL,该SQL表达式必须要是可以直接执行的语句 * 直接执行一段SQL,该SQL表达式必须要是可以直接执行的语句
* *
* @param dynamicVar 动态变量对象(必须是可以直接执行SQL 例`select * from user where id = 5`) * @param dynamicVar 动态变量对象(必须是可以直接执行SQL 例`select * from user where id = 5`)
* @param envId 环境ID * @param envId 环境ID
* @return 运行SQL后取完数值的结果, 是一张数据表, 以LIST<MAP>的形式呈现 * @return 运行SQL后取完数值的结果, 是一张数据表, 以LIST<MAP>的形式呈现
*/ */
private List<Map<String, Object>> runSql(DynamicVariable dynamicVar, Long envId) { private List<Map<String, Object>> runSqlDirectly(DynamicVariable dynamicVar, Long envId) {
String sqlExp = dynamicVar.getSqlExpDetail().getSqlExp(); String sqlExp = dynamicVar.getSqlExpDetail().getSqlExp();
Long connectId = dynamicVar.getSqlExpDetail().getPoolId(); Long connectId = dynamicVar.getSqlExpDetail().getPoolId();
DataSourceDTO dataSourceDTO = Optional.of(connectService.getById(connectId)) DataSourceDTO dataSourceDTO = Optional.of(connectService.getById(connectId))
......
...@@ -55,15 +55,18 @@ public class ConnectController { ...@@ -55,15 +55,18 @@ public class ConnectController {
@ApiOperation("测试数据库连接") @ApiOperation("测试数据库连接")
@PostMapping("/test") @PostMapping("/test")
public ResponseEntity<String> testConnect(@RequestBody Connect connect) { public ResponseEntity<String> testConnect(@RequestBody Connect connect) {
long startTime = System.currentTimeMillis();
long endTime;
try { try {
dataSourceService.testConnection(connect.toDataSourceDTO()); dataSourceService.testConnection(connect.toDataSourceDTO());
} catch (SQLException e) { } catch (SQLException e) {
endTime = System.currentTimeMillis();
e.printStackTrace(); e.printStackTrace();
log.warn("[数据库] 连接目标数据源数据库失败,目标数据源数据 = {}", connect); log.warn("[数据库] 连接目标数据源数据库失败,目标数据源数据 = {}", connect);
return ResponseEntity.status(503).body(String.format("连接失败,SQL状态 = %s 异常信息 = %s ", e.getSQLState(), e.getMessage())); return ResponseEntity.status(503).body(String.format("连接失败,SQL状态 = %s 异常信息 = %s ,响应时间 %d ms", e.getSQLState(), e.getMessage(), endTime - startTime));
} }
return ResponseEntity.ok("连接成功!"); endTime = System.currentTimeMillis();
return ResponseEntity.ok(String.format("连接成功!响应时间 %d ms", endTime - startTime));
} }
/** /**
......
package org.matrix.autotest.controller; package org.matrix.autotest.controller;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j;
import org.matrix.actuators.datasource.DataSourceDTO;
import org.matrix.actuators.datasource.IDataSourceService; import org.matrix.actuators.datasource.IDataSourceService;
import org.matrix.actuators.sql.SqlExpActuator;
import org.matrix.autotest.entity.GrammarTable;
import org.matrix.autotest.entity.runSqlQuery;
import org.matrix.database.service.IConnectService;
import org.matrix.exception.GlobalException;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.Set;
/** /**
* SqlController. * SqlController.
...@@ -21,19 +25,41 @@ import java.util.Map; ...@@ -21,19 +25,41 @@ import java.util.Map;
* @since 2022/1/24 at 8:57 PM * @since 2022/1/24 at 8:57 PM
* Suffering is the most powerful teacher of life. * Suffering is the most powerful teacher of life.
*/ */
@RequestMapping("/db") @SuppressWarnings("ALL")
@Slf4j
@RestController @RestController
@AllArgsConstructor @RequestMapping("/db")
public class SqlController { public class SqlController {
private final JdbcTemplate jdbcTemplate; private final JdbcTemplate jdbcTemplate;
private final IDataSourceService dataSourceService; private final IDataSourceService dataSourceService;
private final SqlExpActuator sqlActuator;
private final IConnectService connectService;
@SuppressWarnings("FieldCanBeLocal") @SuppressWarnings("FieldCanBeLocal")
private final String GET_ALL_TABLES = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='key_stone';"; private final String GET_ALL_TABLES = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='key_stone';";
public SqlController(JdbcTemplate jdbcTemplate, IDataSourceService dataSourceService, SqlExpActuator sqlActuator, IConnectService connectService) {
this.jdbcTemplate = jdbcTemplate;
this.dataSourceService = dataSourceService;
this.sqlActuator = sqlActuator;
this.connectService = connectService;
}
/** /**
* 运行一段SQL * 递归地运行一段SQL(可以递归解析环境变量,动态变量)
*
* @param sqlQuery 要查询的SQL
* @return SQL结果 以MAP的JSON形式传,但是字段数量不固定
*/
@PostMapping("/runSql")
@ApiOperation("递归地运行一段SQL")
public ResponseEntity<List<Map<String, Object>>> runSql(@RequestBody runSqlQuery sqlQuery) {
List<Map<String, Object>> results = sqlActuator.runSqlRec(sqlQuery.getSql(), sqlQuery.getProjectId(), sqlQuery.getEnvId(), sqlQuery.getConnectId());
return ResponseEntity.ok(results);
}
/**
* 运行一段SQL,要确定可以连接的数据源(用于连接的数据源)环境(用于寻找环境变量)项目id(用于确定需要递归解析的动态变量)
* *
* @param sql 要查询的SQL语句 * @param sql 要查询的SQL语句
* @return SQL结果 以MAP的JSON形式传,但是字段数量不固定 * @return SQL结果 以MAP的JSON形式传,但是字段数量不固定
...@@ -56,5 +82,23 @@ public class SqlController { ...@@ -56,5 +82,23 @@ public class SqlController {
public ResponseEntity<List<Map<String, Object>>> getTableNames() { public ResponseEntity<List<Map<String, Object>>> getTableNames() {
return ResponseEntity.ok(jdbcTemplate.queryForList(GET_ALL_TABLES)); return ResponseEntity.ok(jdbcTemplate.queryForList(GET_ALL_TABLES));
} }
@GetMapping("/hints")
@ApiOperation("获得数据库的语法提示")
public ResponseEntity<List<GrammarTable>> getDbHints(@RequestParam Long connectId) {
String tableSql = "SELECT a.table_name tableName,a.table_comment tableDes,b.COLUMN_NAME fieldName,b.column_comment fieldDes,b.column_type fieldType FROM information_schema. TABLES a LEFT JOIN information_schema. COLUMNS b ON a.table_name = b.TABLE_NAME WHERE a.table_schema = '%s' ORDER BY a.table_name";
// 切换数据源,执行SQL
DataSourceDTO dataSourceDTO = Optional.ofNullable(connectService.getById(connectId))
.orElseThrow(() -> new GlobalException(String.format("没有找到id = %d 的连接池connect对象", connectId)))
.toDataSourceDTO();
Set<String> dataSources = dataSourceService.switchDataSource(dataSourceDTO);
log.info("当前使用的的数据源 {}", dataSourceService.peek());
String databaseName = jdbcTemplate.queryForList("select database();").get(0).get("database()").toString();
tableSql = String.format(tableSql, databaseName);
List<GrammarTable> grammarTables = new GrammarTable().trans2Table(jdbcTemplate.queryForList(tableSql));
dataSourceService.switchMainDataSource();
return ResponseEntity.ok(grammarTables);
}
} }
...@@ -26,6 +26,7 @@ import org.springframework.web.bind.annotation.*; ...@@ -26,6 +26,7 @@ import org.springframework.web.bind.annotation.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.ThreadPoolExecutor;
import static java.util.Optional.ofNullable; import static java.util.Optional.ofNullable;
......
package org.matrix.autotest.entity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* GrammarField. 用于语法提示的表字段
*
* @author Matrix <xhyrzldf@gmail.com>
* @since 2022/3/25 at 11:07 AM
* Suffering is the most powerful teacher of life.
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel("用于语法提示的表字段")
public class GrammarField {
@ApiModelProperty("字段名")
private String fieldName;
@ApiModelProperty("字段描述")
private String fieldDes;
@ApiModelProperty("字段类型")
private String fieldType;
}
package org.matrix.autotest.entity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toList;
/**
* GrammarTable. 语法提示的数据库表结构
*
* @author Matrix <xhyrzldf@gmail.com>
* @since 2022/3/25 at 11:06 AM
* Suffering is the most powerful teacher of life.
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel("语法提示的数据库表结构")
public class GrammarTable {
@ApiModelProperty("表名")
private String tableName;
@ApiModelProperty("表描述")
private String tableDes;
@ApiModelProperty("字段列表")
private List<GrammarField> fields;
public GrammarTable(String tableName, String tableDes) {
this.tableName = tableName;
this.tableDes = tableDes;
this.fields = new ArrayList<>();
}
public List<GrammarTableField> tran2TableField(List<Map<String, Object>> originalMap) {
return originalMap.stream()
.map(m -> new GrammarTableField(m.get("tableName").toString(), m.get("tableDes").toString(), m.get("fieldName").toString(), m.get("fieldDes").toString(), m.get("fieldType").toString()))
.collect(toList());
}
public List<GrammarTable> trans2Table(List<Map<String, Object>> originalMap) {
List<GrammarTableField> tableFields = originalMap.stream()
.map(m -> new GrammarTableField(m.get("tableName").toString(), m.get("tableDes").toString(), m.get("fieldName").toString(), m.get("fieldDes").toString(), m.get("fieldType").toString()))
.collect(toList());
List<GrammarTable> grammarTables = new ArrayList<>();
tableFields.stream()
.collect(groupingBy(GrammarTableField::getTableName))
.forEach((k, v) -> {
GrammarTable table = new GrammarTable(v.get(0).getTableName(), v.get(0).getTableDes());
for (GrammarTableField field : v) {
table.getFields().add(field.extractField());
}
grammarTables.add(table);
});
return grammarTables;
}
}
package org.matrix.autotest.entity;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* GrammarTableField.
* {@link GrammarTable} 与 {@link GrammarField} 两个对象的平展混合字段
*
* @author Matrix <xhyrzldf@gmail.com>
* @since 2022/3/25 at 11:24 AM
* Suffering is the most powerful teacher of life.
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class GrammarTableField {
@ApiModelProperty("表名")
private String tableName;
@ApiModelProperty("表描述")
private String tableDes;
@ApiModelProperty("字段名")
private String fieldName;
@ApiModelProperty("字段描述")
private String fieldDes;
@ApiModelProperty("字段类型")
private String fieldType;
public GrammarField extractField(){
return new GrammarField(fieldName, fieldDes, fieldType);
}
}
package org.matrix.autotest.entity;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* runSqlQuery.
* 运行一段SQL所需要的参数
*
* @author Matrix <xhyrzldf@gmail.com>
* @since 2022/3/24 at 8:45 PM
* Suffering is the most powerful teacher of life.
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class runSqlQuery {
@ApiModelProperty("项目主键id,用于确定动态变量池解析用哪个项目的动态变量池")
private Long projectId;
@ApiModelProperty("环境id,用于确定需要解析的环境变量池所需的id")
private Long envId;
@ApiModelProperty("用于确定执行这段SQL所需要的连接池环境")
private Long connectId;
@ApiModelProperty("要执行的SQL语句,这里只支持select查询语句")
private String sql;
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论