Commit 5c7245ac by inrgihc

接口出参格式可配置

parent 543350cb
# SQLREST
![SQLREST](https://gitee.com/inrgihc/sqlrest/raw/master/sqlrest-manager-ui/src/assets/LOGO.png#pic_center)
> 将数据库的SQL生成RESTful风格的http接口的工具
## 一、工具介绍
......@@ -33,15 +35,18 @@
- Greenplum(需使用PostgreSQL类型)
- IBM的DB2
- Sybase数据库
- 国产达梦数据库DMDB
- 国产达梦数据库DM
- 国产人大金仓数据库Kingbase8
- 国产翰高数据库HighGo
- 国产神通数据库Oscar
- 国产南大通用数据库GBase8a
- Apache Hive
- SQLite3
- OpenGuass
- OpenGauss
- ClickHouse
- Apache Doris
- StarRocks
- OceanBase
### 3、模块结构功能
......
docs/images/003.PNG

83.2 KB | W: | H:

docs/images/003.PNG

63.7 KB | W: | H:

docs/images/003.PNG
docs/images/003.PNG
docs/images/003.PNG
docs/images/003.PNG
  • 2-up
  • Swipe
  • Onion skin
......@@ -153,6 +153,12 @@
<version>3.0.8</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
</dependencyManagement>
......
......@@ -38,6 +38,12 @@
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast-eureka-one</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
</dependencies>
</project>
\ No newline at end of file
package com.gitee.sqlrest.cache;
import java.util.Map;
public interface CacheFactory {
<T> Map<String, T> getCacheMap(String key);
}
package com.gitee.sqlrest.cache;
import com.gitee.sqlrest.common.consts.Constants;
import com.hazelcast.config.Config;
import com.hazelcast.eureka.one.EurekaOneDiscoveryStrategyFactory;
import com.netflix.discovery.EurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class HazelcastCacheConfig {
@Bean
public Config hazelcastCacheConfigFromEureka(EurekaClient eurekaClient) {
EurekaOneDiscoveryStrategyFactory.setEurekaClient(eurekaClient);
Config config = new Config();
config.getNetworkConfig().getJoin().getMulticastConfig().setEnabled(false);
config.getNetworkConfig().getJoin().getEurekaConfig()
.setEnabled(true)
.setProperty("self-registration", "true")
.setProperty("namespace", "hazelcast")
.setProperty("use-metadata-for-host-and-port", "true")
.setProperty("skip-eureka-registration-verification", "true");
config.getMapConfig(Constants.CACHE_KEY_TOKEN_CLIENT)
.setTimeToLiveSeconds(Constants.CLIENT_TOKEN_DURATION_SECONDS.intValue());
return config;
}
}
package com.gitee.sqlrest.cache;
import com.gitee.sqlrest.cache.hazelcast.HazelcastCacheFactory;
import com.gitee.sqlrest.cache.redis.JedisClient;
import com.gitee.sqlrest.cache.redis.RedisCacheFactory;
import com.gitee.sqlrest.cache.redis.RedisProperties;
import com.gitee.sqlrest.common.consts.Constants;
import com.hazelcast.config.Config;
import com.hazelcast.eureka.one.EurekaOneDiscoveryStrategyFactory;
import com.netflix.discovery.EurekaClient;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
@Configuration
public class SqlrestCacheConfiguration {
@Configuration
@ConditionalOnProperty(value = "sqlrest.cache.hazelcast.enabled", havingValue = "true")
static class HazelcastCacheConfig {
@Bean
public Config hazelcastCacheConfigFromEureka(EurekaClient eurekaClient) {
EurekaOneDiscoveryStrategyFactory.setEurekaClient(eurekaClient);
Config config = new Config();
config.getNetworkConfig().getJoin().getMulticastConfig().setEnabled(false);
config.getNetworkConfig().getJoin().getEurekaConfig()
.setEnabled(true)
.setProperty("self-registration", "true")
.setProperty("namespace", "hazelcast")
.setProperty("use-metadata-for-host-and-port", "true")
.setProperty("skip-eureka-registration-verification", "true");
config.getMapConfig(Constants.CACHE_KEY_TOKEN_CLIENT)
.setTimeToLiveSeconds(Constants.CLIENT_TOKEN_DURATION_SECONDS.intValue());
return config;
}
@Bean
public CacheFactory hazelcastCacheFactory() {
return new HazelcastCacheFactory();
}
}
@Configuration
@EnableConfigurationProperties(RedisProperties.class)
@ConditionalOnProperty(value = "sqlrest.cache.redis.enabled", havingValue = "true")
static class RedisCacheConfig {
@Bean
public JedisPool jedisPool(RedisProperties properties) {
RedisProperties.Pool pool = properties.getPool();
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(pool.getMaxActive());
poolConfig.setMaxIdle(pool.getMaxIdle());
poolConfig.setMinIdle(pool.getMinIdle());
if (pool.getTimeBetweenEvictionRuns() != null) {
poolConfig.setTimeBetweenEvictionRunsMillis(pool.getTimeBetweenEvictionRuns().toMillis());
}
if (pool.getMaxWait() != null) {
poolConfig.setMaxWaitMillis(pool.getMaxWait().toMillis());
}
return new JedisPool(poolConfig, properties.getHost(),
properties.getPort(), (int) properties.getTimeout().toMillis(),
properties.getUsername(), properties.getPassword(),
properties.getDatabase(), properties.isSsl());
}
@Bean
public JedisClient jedisClient(JedisPool jedisPool) {
return new JedisClient(jedisPool);
}
@Bean
public CacheFactory redisCacheFactory() {
return new RedisCacheFactory();
}
}
}
package com.gitee.sqlrest.cache;
package com.gitee.sqlrest.cache.hazelcast;
import com.gitee.sqlrest.cache.CacheFactory;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.map.IMap;
import java.util.Map;
import javax.annotation.Resource;
import org.springframework.stereotype.Component;
@Component
public class HazelcastCacheFactory {
public class HazelcastCacheFactory implements CacheFactory {
@Resource
private HazelcastInstance hazelcastInstance;
public <T> IMap<String, T> getCacheMap(String key) {
@Override
public <T> Map<String, T> getCacheMap(String key) {
return hazelcastInstance.getMap(key);
}
......
package com.gitee.sqlrest.cache.redis;
import java.util.function.Consumer;
import java.util.function.Function;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
public class JedisClient {
private JedisPool jedisPool;
public JedisClient(JedisPool jedisPool) {
this.jedisPool = jedisPool;
}
private Jedis getJedis() {
Jedis jedis = this.jedisPool.getResource();
jedis.ping();
return jedis;
}
public <T> T doAction(Function<Jedis, T> function) {
Jedis jedis = getJedis();
try {
return function.apply(jedis);
} finally {
jedis.close();
}
}
public void doConsume(Consumer<Jedis> action) {
Jedis jedis = getJedis();
try {
action.accept(jedis);
} finally {
jedis.close();
}
}
}
package com.gitee.sqlrest.cache.redis;
import com.gitee.sqlrest.cache.CacheFactory;
import java.util.Map;
import javax.annotation.Resource;
public class RedisCacheFactory implements CacheFactory {
@Resource
private JedisClient jedisClient;
@Override
public <T> Map<String, T> getCacheMap(String key) {
return new RedisCacheMap<>(key, jedisClient);
}
}
package com.gitee.sqlrest.cache.redis;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.json.JSONUtil;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class RedisCacheMap<V> implements Map<String, V> {
private final String hashTableKey;
final private JedisClient jedisClient;
public RedisCacheMap(String hashTableKey, JedisClient jedisClient) {
this.hashTableKey = hashTableKey;
this.jedisClient = jedisClient;
}
@Override
public int size() {
return jedisClient.doAction(
jedis -> jedis.hgetAll(hashTableKey).size()
);
}
@Override
public boolean isEmpty() {
return 0 == size();
}
@Override
public boolean containsKey(Object o) {
return jedisClient.doAction(
jedis -> jedis.hexists(hashTableKey, o.toString())
);
}
@Override
public boolean containsValue(Object o) {
return false;
}
@Override
public V get(Object o) {
return jedisClient.doAction(
jedis -> {
String value = jedis.hget(hashTableKey, o.toString());
return JSONUtil.toBean(value, new TypeReference<V>() {
}, true);
}
);
}
@Override
public V put(String k, V v) {
return jedisClient.doAction(
jedis -> {
String value = jedis.hget(hashTableKey, k);
jedis.hset(hashTableKey, k, JSONUtil.toJsonStr(v));
return JSONUtil.toBean(value, new TypeReference<V>() {
}, true);
}
);
}
@Override
public V remove(Object o) {
return jedisClient.doAction(
jedis -> {
String value = jedis.hget(hashTableKey, o.toString());
jedis.hdel(hashTableKey, o.toString());
return JSONUtil.toBean(value, new TypeReference<V>() {
}, true);
}
);
}
@Override
public void putAll(Map<? extends String, ? extends V> map) {
Map<String, String> values = new HashMap<>();
map.forEach((k, v) -> values.put(k, JSONUtil.toJsonStr(v)));
jedisClient.doConsume(
jedis -> jedis.hmset(hashTableKey, values)
);
}
@Override
public void clear() {
jedisClient.doConsume(
jedis -> jedis.hmset(hashTableKey, Collections.emptyMap())
);
}
@Override
public Set<String> keySet() {
return jedisClient.doAction(
jedis -> jedis.hkeys(hashTableKey)
);
}
@Override
public Collection<V> values() {
return Collections.emptyList();
}
@Override
public Set<Entry<String, V>> entrySet() {
return Collections.emptySet();
}
}
package com.gitee.sqlrest.cache.redis;
import java.time.Duration;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
@Data
@ConfigurationProperties(prefix = "sqlrest.cache.redis")
public class RedisProperties {
@Data
public static class Pool {
private int maxIdle = 8;
private int minIdle = 0;
private int maxActive = 8;
private Duration maxWait = Duration.ofMillis(-1L);
private Duration timeBetweenEvictionRuns;
}
private boolean enabled = false;
private int database = 0;
private String host = "127.0.0.1";
private String username;
private String password;
private int port = 6379;
private boolean ssl = false;
private Duration timeout = Duration.ofSeconds(1);
private Duration connectTimeout = Duration.ofSeconds(1);
private String clientName = "sqlrest";
private Pool pool;
}
package com.gitee.sqlrest.common.dto;
import com.gitee.sqlrest.common.enums.ParamLocationEnum;
import com.gitee.sqlrest.common.enums.ParamTypeEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
......@@ -16,6 +17,9 @@ public class ItemParam {
@ApiModelProperty("参数名")
private String name;
@ApiModelProperty("参数位置")
private ParamLocationEnum location;
@ApiModelProperty("参数类型")
private ParamTypeEnum type;
......
......@@ -39,4 +39,11 @@ public enum DataTypeFormatEnum {
public int getNumberScale() {
return numberScale;
}
public String getDefault() {
if (numberScale > 0) {
return String.valueOf(numberScale);
}
return defaultPattern;
}
}
package com.gitee.sqlrest.common.enums;
import cn.hutool.core.util.StrUtil;
import java.util.function.Function;
public enum NamingStrategyEnum {
LOWER_CAMEL_CASE("属性名转换为小驼峰命名"),
UPPER_CAMEL_CASE("属性名转换为大驼峰命名"),
SNAKE_CASE("属性名转换为蛇形命名"),
LOWER_CASE("属性名转换为小写字母"),
NONE(Function.identity(), "无转换"),
CAMEL_CASE(StrUtil::toCamelCase, "属性名转换为驼峰命名"),
SNAKE_CASE(StrUtil::toUnderlineCase, "属性名转换为蛇形命名"),
LOWER_CASE(String::toLowerCase, "属性名转换为小写字母"),
UPPER_CASE(String::toUpperCase, "属性名转换为大写字母"),
;
private Function<String, String> function;
private String description;
NamingStrategyEnum(String description) {
NamingStrategyEnum(Function<String, String> function, String description) {
this.function = function;
this.description = description;
}
public Function<String, String> getFunction() {
return function;
}
public String getDescription() {
return description;
}
......
package com.gitee.sqlrest.common.enums;
public enum ParamLocationEnum {
REQUEST_HEADER("请求头"),
REQUEST_BODY("请求体"),
FORM_DATA("表单数据"),
;
private String name;
ParamLocationEnum(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
......@@ -123,7 +123,8 @@ public enum ProductTypeEnum {
"jdbc:db2://172.17.2.10:50000/testdb:driverType=4;fullyMaterializeLobData=true;fullyMaterializeInputStreams=true;progressiveStreaming=2;progresssiveLocators=2;")
.sqlSchemaList("SELECT SCHEMANAME FROM SYSCAT.SCHEMATA ")
.adapter(database -> Pair.of(null, database))
.pageSql("SELECT * FROM (SELECT TMP_PAGE.*,ROWNUMBER() OVER() AS ROW_ID FROM ( %s ) AS TMP_PAGE) TMP_PAGE WHERE ROW_ID BETWEEN ? AND ?")
.pageSql(
"SELECT * FROM (SELECT TMP_PAGE.*,ROWNUMBER() OVER() AS ROW_ID FROM ( %s ) AS TMP_PAGE) TMP_PAGE WHERE ROW_ID BETWEEN ? AND ?")
.build()),
/**
......@@ -297,7 +298,68 @@ public enum ProductTypeEnum {
.sqlSchemaList("SELECT schema_name FROM information_schema.schemata ")
.adapter(database -> Pair.of(null, database))
.pageSql("select * from (%s) alias limit ?, ? ")
.build());
.build()),
/**
* Doris数据库类型
*/
DORIS(
ProductContext.builder()
.id(16)
.quote("`")
.name("doris")
.driver("com.mysql.jdbc.Driver")
.defaultPort(3306)
.testSql("/* ping */ SELECT 1")
.urlPrefix("jdbc:mysql://")
.tplUrls(new String[]{"jdbc:mysql://{host}[:{port}]/[{database}][\\?{params}]"})
.urlSample(
"jdbc:mysql://172.17.2.10:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai&tinyInt1isBit=false&rewriteBatchedStatements=true&useCompression=true")
.sqlSchemaList("SELECT `SCHEMA_NAME` FROM `information_schema`.`SCHEMATA`")
.adapter(database -> Pair.of(database, null))
.pageSql("select * from (%s) alias limit ?, ? ")
.build()),
/**
* StarRocks数据库类型
*/
STARROCKS(
ProductContext.builder()
.id(17)
.quote("`")
.name("starrocks")
.driver("com.mysql.jdbc.Driver")
.defaultPort(3306)
.testSql("/* ping */ SELECT 1")
.urlPrefix("jdbc:mysql://")
.tplUrls(new String[]{"jdbc:mysql://{host}[:{port}]/[{database}][\\?{params}]"})
.urlSample(
"jdbc:mysql://172.17.2.10:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai&tinyInt1isBit=false&rewriteBatchedStatements=true&useCompression=true")
.sqlSchemaList("SELECT `SCHEMA_NAME` FROM `information_schema`.`SCHEMATA`")
.adapter(database -> Pair.of(database, null))
.pageSql("select * from (%s) alias limit ?, ? ")
.build()),
/**
* OceanBase数据库类型
*/
OCEANBASE(
ProductContext.builder()
.id(18)
.quote("`")
.name("oceanbase")
.driver("com.oceanbase.jdbc.Driver")
.defaultPort(2881)
.testSql("/* ping */ SELECT 1")
.urlPrefix("jdbc:oceanbase://")
.tplUrls(new String[]{"jdbc:oceanbase://{host}[:{port}]/[{database}][\\?{params}]"})
.urlSample(
"jdbc:oceanbase://127.0.0.1:2881/test?pool=false&useUnicode=true&characterEncoding=utf-8&useSSL=false")
.sqlSchemaList("SELECT `SCHEMA_NAME` FROM `information_schema`.`SCHEMATA`")
.adapter(database -> Pair.of(database, null))
.pageSql("select * from (%s) alias limit ?, ? ")
.build()),
;
private ProductContext context;
......@@ -325,6 +387,10 @@ public enum ProductTypeEnum {
return this.context.getTplUrls();
}
public String getTestSql() {
return this.context.getTestSql();
}
public String getSample() {
return this.context.getUrlSample();
}
......
......@@ -47,6 +47,12 @@
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
\ No newline at end of file
......@@ -2,6 +2,7 @@ package com.gitee.sqlrest.core.dto;
import com.baomidou.mybatisplus.annotation.TableField;
import com.gitee.sqlrest.common.dto.ItemParam;
import com.gitee.sqlrest.common.enums.NamingStrategyEnum;
import com.gitee.sqlrest.persistence.entity.ApiContextEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
......@@ -35,6 +36,12 @@ public class ApiAssignmentDetailResponse extends ApiAssignmentBaseResponse {
@ApiModelProperty("SQL列表")
private List<ApiContextEntity> sqlList;
@ApiModelProperty("接口出参数据类型转换格式")
private List<DataTypeFormatMapValue> formatMap;
@ApiModelProperty("接口出参属性命名策略")
private NamingStrategyEnum namingStrategy;
@ApiModelProperty("是否开启流量控制")
private Boolean flowStatus;
......
package com.gitee.sqlrest.core.dto;
import com.gitee.sqlrest.common.dto.ItemParam;
import com.gitee.sqlrest.common.enums.DataTypeFormatEnum;
import com.gitee.sqlrest.common.enums.ExecuteEngineEnum;
import com.gitee.sqlrest.common.enums.HttpMethodEnum;
import com.gitee.sqlrest.common.enums.NamingStrategyEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.util.List;
import java.util.Map;
import lombok.Data;
import lombok.NoArgsConstructor;
......@@ -50,9 +53,15 @@ public class ApiAssignmentSaveRequest {
@ApiModelProperty("SQL列表")
private List<String> contextList;
@ApiModelProperty("接口入参")
@ApiModelProperty("接口入参列表")
private List<ItemParam> params;
@ApiModelProperty("接口出参数据类型转换格式")
private List<DataTypeFormatMapValue> formatMap;
@ApiModelProperty("接口出参属性命名策略")
private NamingStrategyEnum namingStrategy;
@ApiModelProperty("是否开启流量控制")
private Boolean flowStatus;
......
package com.gitee.sqlrest.core.dto;
import com.gitee.sqlrest.common.dto.ParamValue;
import com.gitee.sqlrest.common.enums.DataTypeFormatEnum;
import com.gitee.sqlrest.common.enums.ExecuteEngineEnum;
import com.gitee.sqlrest.common.enums.NamingStrategyEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.util.List;
import java.util.Map;
import lombok.Data;
import lombok.NoArgsConstructor;
......@@ -23,7 +21,7 @@ public class ApiDebugExecuteRequest {
private ExecuteEngineEnum engine;
@ApiModelProperty("数据类型转换格式")
private Map<DataTypeFormatEnum, String> formatMap;
private List<DataTypeFormatMapValue> formatMap;
@ApiModelProperty("属性命名策略")
private NamingStrategyEnum namingStrategy;
......
package com.gitee.sqlrest.core.dto;
import com.gitee.sqlrest.common.enums.DataTypeFormatEnum;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DataTypeFormatMapValue {
private DataTypeFormatEnum key;
private String value;
private String remark;
}
package com.gitee.sqlrest.core.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
@Data
@SuperBuilder(toBuilder = true)
@NoArgsConstructor
@AllArgsConstructor
@ApiModel("枚举键值")
public class NameValueBaseResponse {
@ApiModelProperty("枚举至")
private Enum key;
@ApiModelProperty("说明")
private String value;
}
package com.gitee.sqlrest.core.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
@Data
@SuperBuilder(toBuilder = true)
@NoArgsConstructor
@AllArgsConstructor
@ApiModel("带说明的枚举键值")
public class NameValueRemarkResponse extends NameValueBaseResponse {
@ApiModelProperty("注释")
private String remark;
}
package com.gitee.sqlrest.core.exec;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.io.IoUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gitee.sqlrest.common.dto.ItemParam;
import com.gitee.sqlrest.common.dto.ResultEntity;
import com.gitee.sqlrest.common.enums.ParamLocationEnum;
import com.gitee.sqlrest.common.enums.ParamTypeEnum;
import com.gitee.sqlrest.common.exception.ResponseErrorCode;
import com.gitee.sqlrest.core.driver.DriverLoadService;
......@@ -10,19 +15,24 @@ import com.gitee.sqlrest.core.util.DataSourceUtils;
import com.gitee.sqlrest.persistence.dao.DataSourceDao;
import com.gitee.sqlrest.persistence.entity.ApiAssignmentEntity;
import com.gitee.sqlrest.persistence.entity.DataSourceEntity;
import com.google.common.base.Charsets;
import com.zaxxer.hikari.HikariDataSource;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
@Slf4j
......@@ -34,37 +44,46 @@ public class ApiExecuteService {
@Resource
private DriverLoadService driverLoadService;
public ResultEntity<Object> execute(ApiAssignmentEntity config, HttpServletRequest request,
HttpServletResponse response) {
public ResultEntity<Object> execute(ApiAssignmentEntity config, HttpServletRequest request) {
try {
DataSourceEntity dsEntity = dataSourceDao.getById(config.getDatasourceId());
if (null == dsEntity) {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
String message = "datasource[id=" + config.getDatasourceId() + " not exist!";
log.warn("Error for handle api[id={}],information:{}", config.getId(), message);
return ResultEntity.failed(ResponseErrorCode.ERROR_INTERNAL_ERROR, message);
return ResultEntity.failed(ResponseErrorCode.ERROR_RESOURCE_NOT_EXISTS, message);
}
Map<String, Object> paramValues = obtainParameterValues(request, config.getParams());
File driverPath = driverLoadService.getVersionDriverFile(dsEntity.getType(), dsEntity.getVersion());
HikariDataSource dataSource = DataSourceUtils.getHikariDataSource(dsEntity, driverPath.getAbsolutePath());
Object result = ApiExecutorEngineFactory
.getExecutor(config.getEngine(), dataSource, dsEntity.getType())
.execute(config.getContextList(), paramValues);
.execute(config.getContextList(), paramValues, config.getNamingStrategy());
if (result instanceof Collection) {
Collection r = (Collection) result;
return ResultEntity.success(config.getContextList().size() == 1 ? r.stream().findFirst().get() : r);
}
return ResultEntity.success(result);
} catch (Throwable t) {
return ResultEntity.failed(ResponseErrorCode.ERROR_INTERNAL_ERROR, ExceptionUtil.getMessage(t));
}
}
private Map<String, Object> obtainParameterValues(HttpServletRequest request, List<ItemParam> params) {
Map<String, Object> map = new HashMap<>();
if (null != params && params.size() > 0) {
Map<String, Object> bodyMap = getRequestBodyMap(request);
for (ItemParam param : params) {
String name = param.getName();
ParamTypeEnum type = param.getType();
ParamLocationEnum location = param.getLocation();
String defaultValue = param.getDefaultValue();
if (location == ParamLocationEnum.REQUEST_HEADER) {
map.put(name, request.getHeader(name));
} else if (location == ParamLocationEnum.REQUEST_BODY) {
map.put(name, bodyMap.get(name));
} else {
boolean isArray = Optional.ofNullable(param.getIsArray()).orElse(false);
Boolean required = Optional.ofNullable(param.getRequired()).orElse(false);
String defaultValue = param.getDefaultValue();
if (isArray) {
String[] values = request.getParameterValues(name);
if (null != values && values.length > 0) {
......@@ -84,7 +103,33 @@ public class ApiExecuteService {
}
}
}
}
return map;
}
public Map<String, Object> getRequestBodyMap(HttpServletRequest request) {
ObjectMapper mapper = new ObjectMapper();
try {
String jsonString = IoUtil.read(request.getInputStream(), Charsets.UTF_8);
if (StringUtils.isBlank(jsonString)) {
return Collections.emptyMap();
}
Map<String, Object> resultMap = new HashMap<>();
JsonNode rootNode = mapper.readTree(jsonString);
if (!rootNode.isContainerNode()) {
return Collections.emptyMap();
}
Iterator<Entry<String, JsonNode>> fields = rootNode.fields();
while (fields.hasNext()) {
Map.Entry<String, JsonNode> entry = fields.next();
JsonNode jsonNode = entry.getValue();
Object value = mapper.convertValue(jsonNode, Object.class);
resultMap.put(entry.getKey(), value);
}
return resultMap;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
package com.gitee.sqlrest.core.exec.engine;
import com.gitee.sqlrest.common.enums.NamingStrategyEnum;
import com.gitee.sqlrest.persistence.entity.ApiContextEntity;
import java.util.List;
import java.util.Map;
public interface ApiExecutorEngine {
Object execute(List<ApiContextEntity> scripts, Map<String, Object> params);
Object execute(List<ApiContextEntity> scripts, Map<String, Object> params, NamingStrategyEnum strategy);
}
package com.gitee.sqlrest.core.exec.engine.impl;
import cn.hutool.extra.spring.SpringUtil;
import com.gitee.sqlrest.common.enums.NamingStrategyEnum;
import com.gitee.sqlrest.common.enums.ProductTypeEnum;
import com.gitee.sqlrest.core.dto.ScriptEditorCompletion;
import com.gitee.sqlrest.core.exec.annotation.Module;
......@@ -86,9 +87,9 @@ public class ScriptExecutorService extends AbstractExecutorEngine {
}
@Override
public Object execute(List<ApiContextEntity> scripts, Map<String, Object> params) {
public Object execute(List<ApiContextEntity> scripts, Map<String, Object> params, NamingStrategyEnum strategy) {
EnvVarModule envModule = SpringUtil.getBean(EnvVarModule.class);
DbVarModule dbModule = new DbVarModule(dataSource, productType, params);
DbVarModule dbModule = new DbVarModule(dataSource, productType, params, strategy);
List<Object> results = new ArrayList<>();
for (ApiContextEntity entity : scripts) {
......
......@@ -2,6 +2,7 @@ package com.gitee.sqlrest.core.exec.engine.impl;
import cn.hutool.core.util.NumberUtil;
import com.gitee.sqlrest.common.consts.Constants;
import com.gitee.sqlrest.common.enums.NamingStrategyEnum;
import com.gitee.sqlrest.common.enums.ProductTypeEnum;
import com.gitee.sqlrest.core.exec.engine.AbstractExecutorEngine;
import com.gitee.sqlrest.core.util.SqlJdbcUtils;
......@@ -25,7 +26,7 @@ public class SqlExecutorService extends AbstractExecutorEngine {
}
@Override
public Object execute(List<ApiContextEntity> scripts, Map<String, Object> params) {
public Object execute(List<ApiContextEntity> scripts, Map<String, Object> params, NamingStrategyEnum strategy) {
List<Object> dataList = new ArrayList<>();
Configuration cfg = new Configuration();
try (Connection connection = this.dataSource.getConnection()) {
......@@ -40,7 +41,7 @@ public class SqlExecutorService extends AbstractExecutorEngine {
int size = (null == params.get(Constants.PARAM_PAGE_SIZE))
? 10
: NumberUtil.parseInt(params.get(Constants.PARAM_PAGE_SIZE).toString());
dataList.add(SqlJdbcUtils.execute(connection, sqlMeta, page, size));
dataList.add(SqlJdbcUtils.execute(connection, sqlMeta, strategy, page, size));
}
connection.commit();
return dataList;
......
......@@ -2,9 +2,11 @@ package com.gitee.sqlrest.core.exec.module;
import cn.hutool.core.util.NumberUtil;
import com.gitee.sqlrest.common.consts.Constants;
import com.gitee.sqlrest.common.enums.NamingStrategyEnum;
import com.gitee.sqlrest.common.enums.ProductTypeEnum;
import com.gitee.sqlrest.core.exec.annotation.Comment;
import com.gitee.sqlrest.core.exec.annotation.Module;
import com.gitee.sqlrest.core.util.ConvertUtils;
import com.gitee.sqlrest.template.Configuration;
import com.gitee.sqlrest.template.SqlMeta;
import com.gitee.sqlrest.template.SqlTemplate;
......@@ -15,7 +17,9 @@ import java.sql.Statement;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javax.sql.DataSource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataAccessException;
......@@ -37,11 +41,26 @@ public class DbVarModule {
private JdbcTemplate jdbcTemplate;
private ProductTypeEnum productType;
private Map<String, Object> params;
private Function<String, String> converter;
public DbVarModule(DataSource dataSource, ProductTypeEnum productType, Map<String, Object> params) {
public DbVarModule(DataSource dataSource, ProductTypeEnum productType, Map<String, Object> params,
NamingStrategyEnum strategy) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.productType = productType;
this.params = params;
if (null == strategy) {
strategy = NamingStrategyEnum.NONE;
}
this.converter = strategy.getFunction();
}
private Map<String, Object> build(Map<String, Object> row) {
return ConvertUtils.to(row, converter);
}
private List<Map<String, Object>> build(List<Map<String, Object>> rows) {
return rows.stream().map(this::build).collect(Collectors.toList());
}
@Comment("查询所有的数据列表")
......@@ -49,7 +68,7 @@ public class DbVarModule {
log.info("Enter selectAll() function, SQL:{},params:{}", sqlOrXml, params);
SqlTemplate template = cfg.getTemplate(sqlOrXml);
SqlMeta sqlMeta = template.process(params);
return jdbcTemplate.queryForList(sqlMeta.getSql(), sqlMeta.getParameter().toArray());
return build(jdbcTemplate.queryForList(sqlMeta.getSql(), sqlMeta.getParameter().toArray()));
}
@Comment("count所有数据的总数")
......@@ -67,7 +86,7 @@ public class DbVarModule {
log.info("Enter selectOne() function, SQL:{},params:{}", sqlOrXml, params);
SqlTemplate template = cfg.getTemplate(sqlOrXml);
SqlMeta sqlMeta = template.process(params);
return jdbcTemplate
return build(jdbcTemplate
.query(sqlMeta.getSql(), new ResultSetExtractor<Map<String, Object>>() {
private ColumnMapRowMapper mapper = new ColumnMapRowMapper();
......@@ -79,7 +98,7 @@ public class DbVarModule {
return null;
}
},
sqlMeta.getParameter().toArray());
sqlMeta.getParameter().toArray()));
}
@Comment("分页查询数据列表")
......@@ -98,7 +117,7 @@ public class DbVarModule {
: NumberUtil.parseInt(params.get(Constants.PARAM_PAGE_SIZE).toString());
parameters.add(((page - 1) * size) < 0 ? 0 : (page - 1) * size);
parameters.add(size);
return jdbcTemplate.queryForList(pageSql, parameters.toArray());
return build(jdbcTemplate.queryForList(pageSql, parameters.toArray()));
}
@Comment("执行insert操作,返回插入主键")
......@@ -115,7 +134,7 @@ public class DbVarModule {
return ps;
},
keyHolder);
return keyHolder.getKeys();
return build(keyHolder.getKeys());
}
@Comment("执行update操作,返回受影响行数")
......
......@@ -5,6 +5,8 @@ import cn.hutool.core.exceptions.ExceptionUtil;
import com.gitee.sqlrest.common.dto.PageResult;
import com.gitee.sqlrest.common.dto.ParamValue;
import com.gitee.sqlrest.common.dto.ResultEntity;
import com.gitee.sqlrest.common.enums.DataTypeFormatEnum;
import com.gitee.sqlrest.common.enums.NamingStrategyEnum;
import com.gitee.sqlrest.common.enums.OnOffEnum;
import com.gitee.sqlrest.common.enums.ParamTypeEnum;
import com.gitee.sqlrest.common.exception.CommonException;
......@@ -15,6 +17,8 @@ import com.gitee.sqlrest.core.dto.ApiAssignmentDetailResponse;
import com.gitee.sqlrest.core.dto.ApiAssignmentSaveRequest;
import com.gitee.sqlrest.core.dto.ApiDebugExecuteRequest;
import com.gitee.sqlrest.core.dto.AssignmentSearchRequest;
import com.gitee.sqlrest.core.dto.DataTypeFormatMapValue;
import com.gitee.sqlrest.core.dto.NameValueRemarkResponse;
import com.gitee.sqlrest.core.dto.ScriptEditorCompletion;
import com.gitee.sqlrest.core.dto.SqlParamParseResponse;
import com.gitee.sqlrest.core.exec.ApiExecuteService;
......@@ -35,8 +39,10 @@ import com.gitee.sqlrest.template.SqlTemplate;
import com.google.common.base.Charsets;
import com.zaxxer.hikari.HikariDataSource;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
......@@ -50,6 +56,7 @@ import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
......@@ -144,6 +151,10 @@ public class ApiAssignmentService {
}
}
if (null == request.getNamingStrategy()) {
request.setNamingStrategy(NamingStrategyEnum.CAMEL_CASE);
}
File driverPath = driverLoadService.getVersionDriverFile(dataSourceEntity.getType(), dataSourceEntity.getVersion());
ResultEntity entity;
......@@ -151,22 +162,23 @@ public class ApiAssignmentService {
HikariDataSource dataSource = DataSourceUtils.getHikariDataSource(dataSourceEntity, driverPath.getAbsolutePath());
Object result = ApiExecutorEngineFactory
.getExecutor(request.getEngine(), dataSource, dataSourceEntity.getType())
.execute(scripts, params);
.execute(scripts, params, request.getNamingStrategy());
if (result instanceof Collection) {
Collection r = (Collection) result;
result = scripts.size() == 1 ? r.stream().findFirst().get() : r;
}
entity = ResultEntity.success(result);
response.setStatus(HttpServletResponse.SC_OK);
} catch (Exception e) {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
entity = ResultEntity.failed(ResponseErrorCode.ERROR_INTERNAL_ERROR, ExceptionUtil.getMessage(e));
}
String json = JacksonUtils.toJsonStr(entity, request.getFormatMap(), request.getNamingStrategy());
try {
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setCharacterEncoding(Charsets.UTF_8.name());
response.getWriter().append(json);
response.getWriter().append(JacksonUtils.toJsonStr(entity, request.getFormatMap().stream()
.collect(Collectors.toMap(DataTypeFormatMapValue::getKey, DataTypeFormatMapValue::getValue, (a, b) -> a))));
} catch (Exception e) {
throw new RuntimeException(e);
}
......@@ -183,6 +195,10 @@ public class ApiAssignmentService {
if (CollectionUtils.isEmpty(request.getContextList())) {
throw new CommonException(ResponseErrorCode.ERROR_INVALID_ARGUMENT, "contextList");
}
if (null == request.getNamingStrategy()) {
request.setNamingStrategy(NamingStrategyEnum.CAMEL_CASE);
}
List<ApiContextEntity> contextList = getContextListEntity(request.getContextList());
ApiAssignmentEntity assignmentEntity = new ApiAssignmentEntity();
......@@ -199,6 +215,9 @@ public class ApiAssignmentService {
assignmentEntity.setEngine(request.getEngine());
assignmentEntity.setStatus(false);
assignmentEntity.setContextList(contextList);
assignmentEntity.setResponseFormat(request.getFormatMap().stream()
.collect(Collectors.toMap(DataTypeFormatMapValue::getKey, DataTypeFormatMapValue::getValue, (a, b) -> a)));
assignmentEntity.setNamingStrategy(request.getNamingStrategy());
assignmentEntity.setFlowStatus(Optional.ofNullable(request.getFlowStatus()).orElse(false));
assignmentEntity.setFlowGrade(request.getFlowGrade());
assignmentEntity.setFlowCount(request.getFlowCount());
......@@ -222,6 +241,10 @@ public class ApiAssignmentService {
throw new CommonException(ResponseErrorCode.ERROR_INVALID_ARGUMENT, "sqlTextList");
}
if (null == request.getNamingStrategy()) {
request.setNamingStrategy(NamingStrategyEnum.CAMEL_CASE);
}
List<ApiContextEntity> contextList = getContextListEntity(request.getContextList());
ApiAssignmentEntity assignmentEntity = new ApiAssignmentEntity();
......@@ -239,6 +262,9 @@ public class ApiAssignmentService {
assignmentEntity.setStatus(false);
assignmentEntity.setEngine(request.getEngine());
assignmentEntity.setContextList(contextList);
assignmentEntity.setResponseFormat(request.getFormatMap().stream()
.collect(Collectors.toMap(DataTypeFormatMapValue::getKey, DataTypeFormatMapValue::getValue, (a, b) -> a)));
assignmentEntity.setNamingStrategy(request.getNamingStrategy());
assignmentEntity.setFlowStatus(Optional.ofNullable(request.getFlowStatus()).orElse(false));
assignmentEntity.setFlowGrade(request.getFlowGrade());
assignmentEntity.setFlowCount(request.getFlowCount());
......@@ -255,15 +281,43 @@ public class ApiAssignmentService {
ApiAssignmentDetailResponse response = new ApiAssignmentDetailResponse();
BeanUtil.copyProperties(assignmentEntity, response);
response.setSqlList(assignmentEntity.getContextList());
List<DataTypeFormatMapValue> formatMap = new ArrayList<>();
for (Map.Entry<DataTypeFormatEnum, String> entry : assignmentEntity.getResponseFormat().entrySet()) {
formatMap.add(
DataTypeFormatMapValue.builder()
.key(entry.getKey())
.value(entry.getValue())
.remark(entry.getKey().getClassName())
.build());
}
response.setFormatMap(formatMap);
return response;
}
public ResultEntity testAssignment(Long id, HttpServletRequest request, HttpServletResponse response) {
public void testAssignment(Long id, HttpServletRequest request, HttpServletResponse response) {
ApiAssignmentEntity assignmentEntity = apiAssignmentDao.getById(id, true);
if (null == assignmentEntity) {
throw new CommonException(ResponseErrorCode.ERROR_RESOURCE_NOT_EXISTS, "id=" + id);
}
return apiExecuteService.execute(assignmentEntity, request, response);
String json;
ResultEntity result = apiExecuteService.execute(assignmentEntity, request);
if (0 != result.getCode()) {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
json = JacksonUtils.toJsonStr(ResultEntity.failed(ResponseErrorCode.ERROR_INTERNAL_ERROR));
} else {
response.setStatus(HttpServletResponse.SC_OK);
json = JacksonUtils.toJsonStr(result, assignmentEntity.getResponseFormat());
}
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setCharacterEncoding(Charsets.UTF_8.name());
try {
response.getWriter().append(json);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void deleteAssignment(Long id) {
......
......@@ -3,7 +3,6 @@ package com.gitee.sqlrest.core.servlet;
import com.gitee.sqlrest.common.consts.Constants;
import com.gitee.sqlrest.common.dto.ResultEntity;
import com.gitee.sqlrest.common.enums.HttpMethodEnum;
import com.gitee.sqlrest.common.enums.NamingStrategyEnum;
import com.gitee.sqlrest.common.exception.ResponseErrorCode;
import com.gitee.sqlrest.core.exec.ApiExecuteService;
import com.gitee.sqlrest.core.util.JacksonUtils;
......@@ -11,7 +10,6 @@ import com.gitee.sqlrest.persistence.dao.ApiAssignmentDao;
import com.gitee.sqlrest.persistence.entity.ApiAssignmentEntity;
import com.google.common.base.Charsets;
import java.io.IOException;
import java.util.Collections;
import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
......@@ -34,21 +32,19 @@ public class ApiServletService {
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setCharacterEncoding(Charsets.UTF_8.name());
String path = request.getRequestURI().substring(Constants.API_PATH_PREFIX.length() + 2);
String json = null;
ResultEntity result = ResultEntity.success();
ApiAssignmentEntity apiConfigEntity = apiAssignmentDao.getByUk(method, path);
if (null == apiConfigEntity || !apiConfigEntity.getStatus()) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
String message = String.format("/%s/%s[%s]", Constants.API_PATH_PREFIX, path, method.name());
ResultEntity result = ResultEntity.failed(ResponseErrorCode.ERROR_PATH_NOT_EXISTS, message);
json = JacksonUtils.toJsonStr(result, Collections.emptyMap(), null);
result = ResultEntity.failed(ResponseErrorCode.ERROR_PATH_NOT_EXISTS, message);
} else {
ResultEntity result = apiExecuteService.execute(apiConfigEntity, request, response);
result = apiExecuteService.execute(apiConfigEntity, request);
}
if (0 != result.getCode()) {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
json = JacksonUtils.toJsonStr(result, Collections.emptyMap(), NamingStrategyEnum.UPPER_CAMEL_CASE);
}
String json = JacksonUtils.toJsonStr(result, apiConfigEntity.getResponseFormat());
response.getWriter().append(json);
}
......
package com.gitee.sqlrest.core.servlet;
import com.gitee.sqlrest.cache.HazelcastCacheFactory;
import com.gitee.sqlrest.cache.CacheFactory;
import com.gitee.sqlrest.common.consts.Constants;
import com.gitee.sqlrest.common.dto.AccessToken;
import com.gitee.sqlrest.common.enums.DurationTimeEnum;
......@@ -14,7 +14,6 @@ import java.util.Map;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
......@@ -26,7 +25,7 @@ public class ClientTokenService {
@Resource
private AppClientDao appClientDao;
@Resource
private HazelcastCacheFactory hazelcastCacheFactory;
private CacheFactory cacheFactory;
@EventListener(ApplicationReadyEvent.class)
public void init() {
......@@ -52,7 +51,7 @@ public class ClientTokenService {
}
}
Map tokenClientMap = hazelcastCacheFactory.getCacheMap(Constants.CACHE_KEY_TOKEN_CLIENT);
Map tokenClientMap = cacheFactory.getCacheMap(Constants.CACHE_KEY_TOKEN_CLIENT);
tokenClientMap.put(appClient.getAccessToken(), clientToken);
}
}
......@@ -73,7 +72,7 @@ public class ClientTokenService {
.realName(appClient.getName())
.appKey(clientId)
.accessToken(token)
.createTimestamp(System.currentTimeMillis()/1000)
.createTimestamp(System.currentTimeMillis() / 1000)
.expireSeconds(Constants.CLIENT_TOKEN_DURATION_SECONDS)
.build();
if (DurationTimeEnum.TIME_VALUE.equals(appClient.getExpireDuration())) {
......@@ -93,7 +92,7 @@ public class ClientTokenService {
appClientDao.updateTokenByAppKey(clientId, token);
}
Map tokenClientMap = hazelcastCacheFactory.getCacheMap(Constants.CACHE_KEY_TOKEN_CLIENT);
Map tokenClientMap = cacheFactory.getCacheMap(Constants.CACHE_KEY_TOKEN_CLIENT);
tokenClientMap.put(token, clientToken);
return clientToken;
......@@ -103,7 +102,7 @@ public class ClientTokenService {
if (StringUtils.isBlank(tokenStr)) {
return null;
}
Map<String, AccessToken> tokenClientMap = hazelcastCacheFactory
Map<String, AccessToken> tokenClientMap = cacheFactory
.getCacheMap(Constants.CACHE_KEY_TOKEN_CLIENT);
AccessToken clientToken = tokenClientMap.get(tokenStr);
if (null == clientToken) {
......
package com.gitee.sqlrest.core.util;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Function;
public class ConvertUtils {
public static Map<String, Object> to(Map<String, Object> row) {
return row;
}
public static Map<String, Object> to(Map<String, Object> row, Function<String, String> converter) {
if (null == converter) {
return row;
}
Map<String, Object> ret = new LinkedHashMap<>();
row.forEach((key, val) -> ret.put(converter.apply(key), val));
return ret;
}
}
package com.gitee.sqlrest.core.util;
import cn.hutool.core.util.ClassLoaderUtil;
import com.gitee.sqlrest.common.enums.ProductTypeEnum;
import com.gitee.sqlrest.common.model.JarFileClassLoader;
import com.gitee.sqlrest.common.model.SimpleDataSource;
import com.gitee.sqlrest.persistence.entity.DataSourceEntity;
......@@ -60,18 +61,14 @@ public final class DataSourceUtils {
HikariDataSource ds = new HikariDataSource();
ds.setPoolName("The_JDBC_Connection");
ds.setJdbcUrl(properties.getUrl());
if (properties.getDriver().contains("oracle")) {
ds.setConnectionTestQuery("SELECT 'Hello' from DUAL");
if (ProductTypeEnum.ORACLE == properties.getType()) {
ds.setConnectionTestQuery(properties.getType().getTestSql());
// https://blog.csdn.net/qq_20960159/article/details/78593936
System.getProperties().setProperty("oracle.jdbc.J2EE13Compliant", "true");
// Oracle在通过jdbc连接的时候需要添加一个参数来设置是否获取注释
parameters.put("remarksReporting", "true");
} else if (properties.getDriver().contains("db2")) {
ds.setConnectionTestQuery("SELECT 1 FROM SYSIBM.SYSDUMMY1");
} else if (properties.getDriver().contains("mongodb")) {
ds.setConnectionTestQuery("use admin;");
} else if (!ds.getJdbcUrl().contains("jdbc:jest://")) {
ds.setConnectionTestQuery("SELECT 1");
} else if (StringUtils.isNotBlank(properties.getType().getTestSql())) {
ds.setConnectionTestQuery(properties.getType().getTestSql());
}
ds.setMaximumPoolSize(MAX_THREAD_COUNT);
ds.setMinimumIdle(MAX_THREAD_COUNT);
......
......@@ -3,12 +3,9 @@ package com.gitee.sqlrest.core.util;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import com.gitee.sqlrest.common.enums.DataTypeFormatEnum;
import com.gitee.sqlrest.common.enums.NamingStrategyEnum;
import com.gitee.sqlrest.core.serdes.DateTimeSerDesFactory;
import java.util.Collections;
import java.util.Map;
......@@ -16,20 +13,13 @@ import java.util.Map;
public final class JacksonUtils {
public static String toJsonStr(Object obj) {
return toJsonStr(obj, Collections.emptyMap(), null);
return toJsonStr(obj, Collections.emptyMap());
}
public static String toJsonStr(Object obj, Map<DataTypeFormatEnum, String> formatMap, NamingStrategyEnum namingStrategy) {
public static String toJsonStr(Object obj, Map<DataTypeFormatEnum, String> formatMap) {
// https://www.jianshu.com/p/1368547350c6
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(createSerializeModule(formatMap));
if (null != namingStrategy) {
PropertyNamingStrategy strategy = createPropertyNamingStrategy(namingStrategy);
if (null != strategy) {
objectMapper.setPropertyNamingStrategy(strategy);
}
}
try {
return objectMapper.writeValueAsString(obj);
} catch (JsonProcessingException e) {
......@@ -37,20 +27,6 @@ public final class JacksonUtils {
}
}
private static PropertyNamingStrategy createPropertyNamingStrategy(NamingStrategyEnum namingStrategy) {
if (namingStrategy == NamingStrategyEnum.LOWER_CAMEL_CASE) {
return PropertyNamingStrategies.LOWER_CAMEL_CASE;
} else if (namingStrategy == NamingStrategyEnum.UPPER_CAMEL_CASE) {
return PropertyNamingStrategies.UPPER_CAMEL_CASE;
} else if (namingStrategy == NamingStrategyEnum.SNAKE_CASE) {
return PropertyNamingStrategies.SNAKE_CASE;
} else if (namingStrategy == NamingStrategyEnum.LOWER_CASE) {
return PropertyNamingStrategies.LOWER_CASE;
} else {
return null;
}
}
private static Module createSerializeModule(Map<DataTypeFormatEnum, String> formatMap) {
Map<DataTypeFormatEnum, String> finalFormatMap = (null == formatMap) ? Collections.emptyMap() : formatMap;
SimpleModule module = new SimpleModule();
......
package com.gitee.sqlrest.core.util;
import com.gitee.sqlrest.common.enums.NamingStrategyEnum;
import com.gitee.sqlrest.template.SqlMeta;
import java.sql.Connection;
import java.sql.PreparedStatement;
......@@ -9,6 +10,7 @@ import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
......@@ -16,7 +18,8 @@ import lombok.extern.slf4j.Slf4j;
@UtilityClass
public class SqlJdbcUtils {
public static Object execute(Connection connection, SqlMeta sqlMeta, int page, int size) throws SQLException {
public static Object execute(Connection connection, SqlMeta sqlMeta, NamingStrategyEnum strategy, int page, int size)
throws SQLException {
List<Object> paramValues = sqlMeta.getParameter();
PreparedStatement statement = connection.prepareStatement(sqlMeta.getSql());
statement.setQueryTimeout(300);
......@@ -25,6 +28,7 @@ public class SqlJdbcUtils {
statement.setObject(i, paramValues.get(i - 1));
}
log.info("ExecuteSQL:{}\n{}", sqlMeta.getSql(), paramValues);
Function<String, String> converter = (null == strategy) ? Function.identity() : strategy.getFunction();
if (statement.execute()) {
int skipNumber = size * (page - 1);
try (ResultSet rs = statement.getResultSet()) {
......@@ -45,7 +49,7 @@ public class SqlJdbcUtils {
}
}
if (skipNumber <= 0) {
list.add(row);
list.add(ConvertUtils.to(row, converter));
if (list.size() >= size) {
break;
}
......
package com.gitee.sqlrest.core;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gitee.sqlrest.core.util.JacksonUtils;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import org.junit.Test;
public class JacksonUtilsTest {
@Test
public void testToJsonStr001() {
Map<String, Object> result = new HashMap<>();
result.put("user_name", "test");
result.put("user_age", 23);
result.put("user_sex", 1);
String json = JacksonUtils.toJsonStr(result, Collections.emptyMap());
System.out.println(json);
}
@Test
public void testJsonStrToMap() throws JsonProcessingException {
String jsonString = "{\"user_sex\":1,\"user_name\":\"test\",\"user_age\":23}";
Map<String, Object> resultMap = new HashMap<>();
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(jsonString);
Iterator<Entry<String, JsonNode>> fields = rootNode.fields();
while (fields.hasNext()) {
Map.Entry<String, JsonNode> entry = fields.next();
JsonNode jsonNode = entry.getValue();
Object value = mapper.convertValue(jsonNode, Object.class);
resultMap.put(entry.getKey(), value);
}
System.out.println(resultMap);
}
}
......@@ -13,6 +13,17 @@ spring:
validation-query: SELECT 1
test-on-borrow: true
sqlrest:
cache:
hazelcast:
enabled: true
redis:
enabled: false
host: 127.0.0.1
port: 6379
password: 123456
database: 0
mybatis:
configuration:
lazy-loading-enabled: true
......
......@@ -46,6 +46,17 @@ spring:
allowed-methods: '*'
allowed-headers: "*"
sqlrest:
cache:
hazelcast:
enabled: true
redis:
enabled: false
host: 127.0.0.1
port: 6379
password: 123456
database: 0
mybatis:
configuration:
lazy-loading-enabled: true
......
......@@ -24,12 +24,6 @@
</dependency>
<dependency>
<groupId>com.sun.jersey.contribs</groupId>
<artifactId>jersey-apache-client4</artifactId>
<version>1.19.4</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
......
......@@ -3,13 +3,19 @@ package com.gitee.sqlrest.manager.controller;
import com.gitee.sqlrest.common.consts.Constants;
import com.gitee.sqlrest.common.dto.PageResult;
import com.gitee.sqlrest.common.dto.ResultEntity;
import com.gitee.sqlrest.common.enums.DataTypeFormatEnum;
import com.gitee.sqlrest.common.enums.NamingStrategyEnum;
import com.gitee.sqlrest.core.dto.ApiAssignmentBaseResponse;
import com.gitee.sqlrest.core.dto.ApiAssignmentSaveRequest;
import com.gitee.sqlrest.core.dto.ApiDebugExecuteRequest;
import com.gitee.sqlrest.core.dto.AssignmentSearchRequest;
import com.gitee.sqlrest.core.dto.NameValueBaseResponse;
import com.gitee.sqlrest.core.dto.NameValueRemarkResponse;
import com.gitee.sqlrest.core.service.ApiAssignmentService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import java.util.Arrays;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
......@@ -38,6 +44,37 @@ public class ApiAssignmentController {
return ResultEntity.success(apiAssignmentService.completions());
}
@ApiOperation(value = "响应属性命名策略")
@GetMapping(value = "/response-naming-strategy", produces = MediaType.APPLICATION_JSON_VALUE)
public ResultEntity responseNamingStrategy() {
return ResultEntity.success(
Arrays.stream(NamingStrategyEnum.values())
.map(
e ->
NameValueBaseResponse.builder()
.key(e)
.value(e.getDescription())
.build()
).collect(Collectors.toList())
);
}
@ApiOperation(value = "响应数据类型格式")
@GetMapping(value = "/response-type-format", produces = MediaType.APPLICATION_JSON_VALUE)
public ResultEntity responseTypeFormat() {
return ResultEntity.success(
Arrays.stream(DataTypeFormatEnum.values())
.map(
e ->
NameValueRemarkResponse.builder()
.key(e)
.value(e.getDefault())
.remark(e.getClassName())
.build()
).collect(Collectors.toList())
);
}
@ApiOperation(value = "获取SQL中的入参列表")
@PostMapping(value = "/parse", produces = MediaType.APPLICATION_JSON_VALUE)
public ResultEntity parse(@RequestParam("sql") String sql) {
......@@ -72,8 +109,8 @@ public class ApiAssignmentController {
@ApiOperation(value = "测试API执行")
@PostMapping(value = "/test/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
public ResultEntity test(@PathVariable("id") Long id, HttpServletRequest request, HttpServletResponse response) {
return apiAssignmentService.testAssignment(id, request, response);
public void test(@PathVariable("id") Long id, HttpServletRequest request, HttpServletResponse response) {
apiAssignmentService.testAssignment(id, request, response);
}
@ApiOperation(value = "删除API配置")
......
......@@ -19,6 +19,17 @@ spring:
table: SQLREST_SCHEMA_HISTORY
enabled: true
sqlrest:
cache:
hazelcast:
enabled: true
redis:
enabled: false
host: 127.0.0.1
port: 6379
password: 123456
database: 0
mybatis:
configuration:
lazy-loading-enabled: true
......
ALTER TABLE `SQLREST_API_ASSIGNMENT`
ADD COLUMN `response_format` tinytext NULL comment '响应格式配置' AFTER `engine`,
ADD COLUMN `naming_strategy` varchar(16) NOT NULL DEFAULT 'NONE' comment '命名策略' AFTER `response_format` ;
UPDATE SQLREST_API_ASSIGNMENT set response_format = '{"LOCAL_DATE":"yyyy-MM-dd","DATE":"yyyy-MM-dd","TIMESTAMP":"yyyy-MM-dd HH:mm","LOCAL_DATE_TIME":"yyyy-MM-dd HH:mm","TIME":"HH:mm:ss","BIG_DECIMAL":"6"}' where response_format is null;
<!DOCTYPE html><html><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>SQLREST工具</title><link href=/static/css/app.b00ebac3d2add7487e4a6432cef8d975.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=/static/js/manifest.255d24ba50ec0408e7ae.js></script><script type=text/javascript src=/static/js/vendor.b8089f9fd73f8896df25.js></script><script type=text/javascript src=/static/js/app.907b2e3813e3bdcda00d.js></script></body></html>
\ No newline at end of file
<!DOCTYPE html><html><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>SQLREST工具</title><link href=/static/css/app.3cc3f3ced0962b332bb5c7f66f5a4311.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=/static/js/manifest.038d1629b6d567190efe.js></script><script type=text/javascript src=/static/js/vendor.b8089f9fd73f8896df25.js></script><script type=text/javascript src=/static/js/app.907b2e3813e3bdcda00d.js></script></body></html>
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
!function(e){var n=window.webpackJsonp;window.webpackJsonp=function(r,c,a){for(var f,d,i,u=0,b=[];u<r.length;u++)d=r[u],t[d]&&b.push(t[d][0]),t[d]=0;for(f in c)Object.prototype.hasOwnProperty.call(c,f)&&(e[f]=c[f]);for(n&&n(r,c,a);b.length;)b.shift()();if(a)for(u=0;u<a.length;u++)i=o(o.s=a[u]);return i};var r={},t={22:0};function o(n){if(r[n])return r[n].exports;var t=r[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,o),t.l=!0,t.exports}o.e=function(e){var n=t[e];if(0===n)return new Promise(function(e){e()});if(n)return n[2];var r=new Promise(function(r,o){n=t[e]=[r,o]});n[2]=r;var c=document.getElementsByTagName("head")[0],a=document.createElement("script");a.type="text/javascript",a.charset="utf-8",a.async=!0,a.timeout=12e4,o.nc&&a.setAttribute("nonce",o.nc),a.src=o.p+"static/js/"+e+"."+{0:"f553862d2c4a87a9b53f",1:"347dcbee299ce37dade6",2:"140338f6a5528feea1a3",3:"712d5b861100b5e0b704",4:"f8494b8dd039413f79c8",5:"f69840e8bd74f4d4e92b",6:"8f85de06573e2a5f9562",7:"7ea6008d16a44e79a428",8:"7483ee6d3a25506eb489",9:"1f165c58c9933d0da8a7",10:"cdd03027e5c73f31170c",11:"cdde61370dec5108c322",12:"57d1188c7336fe654844",13:"2865b5654b1d3bdf6e13",14:"42cdbd66a7803b30c641",15:"3b3f0c03ff4fed9903cc",16:"4de955682c1f7710c7ea",17:"819547b2361d544d3b8b",18:"5e7f065a8d031847e833",19:"3936346cb7e30aa279e2"}[e]+".js";var f=setTimeout(d,12e4);function d(){a.onerror=a.onload=null,clearTimeout(f);var n=t[e];0!==n&&(n&&n[1](new Error("Loading chunk "+e+" failed.")),t[e]=void 0)}return a.onerror=a.onload=d,c.appendChild(a),r},o.m=e,o.c=r,o.d=function(e,n,r){o.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},o.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(n,"a",n),n},o.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},o.p="/",o.oe=function(e){throw console.error(e),e}}([]);
//# sourceMappingURL=manifest.255d24ba50ec0408e7ae.js.map
\ No newline at end of file
!function(e){var n=window.webpackJsonp;window.webpackJsonp=function(r,c,a){for(var d,f,i,u=0,b=[];u<r.length;u++)f=r[u],t[f]&&b.push(t[f][0]),t[f]=0;for(d in c)Object.prototype.hasOwnProperty.call(c,d)&&(e[d]=c[d]);for(n&&n(r,c,a);b.length;)b.shift()();if(a)for(u=0;u<a.length;u++)i=o(o.s=a[u]);return i};var r={},t={22:0};function o(n){if(r[n])return r[n].exports;var t=r[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,o),t.l=!0,t.exports}o.e=function(e){var n=t[e];if(0===n)return new Promise(function(e){e()});if(n)return n[2];var r=new Promise(function(r,o){n=t[e]=[r,o]});n[2]=r;var c=document.getElementsByTagName("head")[0],a=document.createElement("script");a.type="text/javascript",a.charset="utf-8",a.async=!0,a.timeout=12e4,o.nc&&a.setAttribute("nonce",o.nc),a.src=o.p+"static/js/"+e+"."+{0:"04668914d5dbd2646823",1:"347dcbee299ce37dade6",2:"140338f6a5528feea1a3",3:"712d5b861100b5e0b704",4:"f8494b8dd039413f79c8",5:"f69840e8bd74f4d4e92b",6:"8f85de06573e2a5f9562",7:"7ea6008d16a44e79a428",8:"7483ee6d3a25506eb489",9:"1f165c58c9933d0da8a7",10:"cdd03027e5c73f31170c",11:"cdde61370dec5108c322",12:"57d1188c7336fe654844",13:"2865b5654b1d3bdf6e13",14:"42cdbd66a7803b30c641",15:"3b3f0c03ff4fed9903cc",16:"4de955682c1f7710c7ea",17:"819547b2361d544d3b8b",18:"5e7f065a8d031847e833",19:"3936346cb7e30aa279e2"}[e]+".js";var d=setTimeout(f,12e4);function f(){a.onerror=a.onload=null,clearTimeout(d);var n=t[e];0!==n&&(n&&n[1](new Error("Loading chunk "+e+" failed.")),t[e]=void 0)}return a.onerror=a.onload=f,c.appendChild(a),r},o.m=e,o.c=r,o.d=function(e,n,r){o.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},o.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(n,"a",n),n},o.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},o.p="/",o.oe=function(e){throw console.error(e),e}}([]);
//# sourceMappingURL=manifest.038d1629b6d567190efe.js.map
\ No newline at end of file
......@@ -6,12 +6,15 @@ import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.gitee.sqlrest.common.dto.ItemParam;
import com.gitee.sqlrest.common.enums.DataTypeFormatEnum;
import com.gitee.sqlrest.common.enums.ExecuteEngineEnum;
import com.gitee.sqlrest.common.enums.HttpMethodEnum;
import com.gitee.sqlrest.common.enums.OnOffEnum;
import com.gitee.sqlrest.common.enums.NamingStrategyEnum;
import com.gitee.sqlrest.persistence.handler.FormatMapHandler;
import com.gitee.sqlrest.persistence.handler.ListParamHandler;
import java.sql.Timestamp;
import java.util.List;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
......@@ -64,6 +67,12 @@ public class ApiAssignmentEntity {
@TableField(value = "engine", typeHandler = EnumTypeHandler.class)
private ExecuteEngineEnum engine;
@TableField(value = "response_format", typeHandler = FormatMapHandler.class)
private Map<DataTypeFormatEnum, String> responseFormat;
@TableField(value = "naming_strategy", typeHandler = EnumTypeHandler.class)
private NamingStrategyEnum namingStrategy;
@TableField(value = "flow_status")
private Boolean flowStatus;
......
package com.gitee.sqlrest.persistence.handler;
import com.fasterxml.jackson.core.type.TypeReference;
import com.gitee.sqlrest.common.enums.DataTypeFormatEnum;
import com.gitee.sqlrest.persistence.util.JsonUtils;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
public class FormatMapHandler extends BaseTypeHandler<Map<DataTypeFormatEnum, String>> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Map<DataTypeFormatEnum, String> value,
JdbcType jdbcType) throws SQLException {
ps.setString(i, map2string(value));
}
@Override
public Map<DataTypeFormatEnum, String> getNullableResult(ResultSet rs, String columnName) throws SQLException {
return string2map(rs.getString(columnName));
}
@Override
public Map<DataTypeFormatEnum, String> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return string2map(rs.getString(columnIndex));
}
@Override
public Map<DataTypeFormatEnum, String> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return string2map(cs.getString(columnIndex));
}
private String map2string(Map<DataTypeFormatEnum, String> map) {
if (map == null || map.isEmpty()) {
return null;
}
return JsonUtils.toJsonString(map);
}
private Map<DataTypeFormatEnum, String> string2map(String str) {
if (str == null || str.isEmpty()) {
return new HashMap<>(2);
}
return JsonUtils.toBeanType(str, new TypeReference<Map<DataTypeFormatEnum, String>>() {
});
}
}
package com.gitee.sqlrest.persistence.util;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Collections;
......@@ -40,6 +41,19 @@ public class JsonUtils {
return null;
}
public static <T> T toBeanType(String jsonString, TypeReference<T> clazz) {
if (null != jsonString) {
try {
return jacksonMapper.readValue(jsonString, clazz);
} catch (JsonProcessingException e) {
String className = clazz.getType().getTypeName();
log.error(" parse json [{}] to class [{}] error:{}", jsonString, className, e);
throw new RuntimeException("parse json string to object error:" + e.getMessage());
}
}
return null;
}
public static <T> List<T> toBeanList(String jsonString, Class<T> clazz) {
if (null != jsonString) {
try {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment