(improvement)(headless) Modify the headless module structure to api, core and server (#588)

Co-authored-by: jolunoluo
This commit is contained in:
LXW
2024-01-02 16:43:28 +08:00
committed by GitHub
parent af1c560cc4
commit e7f13572d7
352 changed files with 2296 additions and 2675 deletions

View File

@@ -1,28 +1,69 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.tencent.supersonic</groupId>
<artifactId>headless</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>headless-api</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- <dependency>-->
<!-- <groupId>ru.yandex.clickhouse</groupId>-->
<!-- <artifactId>clickhouse-jdbc</artifactId>-->
<!-- <version>${clickhouse.jdbc.version}</version>-->
<!-- </dependency>-->
<dependency>
<groupId>com.clickhouse</groupId>
<artifactId>clickhouse-jdbc</artifactId>
<version>${clickhouse.jdbc.version}</version>
<!-- use uber jar with all dependencies included, change classifier to http for smaller jar -->
<classifier>all</classifier>
</dependency>
<dependency>
<groupId>com.tencent.supersonic</groupId>
<artifactId>headless-query</artifactId>
<artifactId>common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>com.tencent.supersonic</groupId>
<artifactId>auth-api</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>
</project>

View File

@@ -1,12 +0,0 @@
package com.tencent.supersonic.headless.api.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiHeaderCheck {
}

View File

@@ -1,74 +0,0 @@
package com.tencent.supersonic.headless.api.aspect;
import com.tencent.supersonic.common.pojo.Pair;
import com.tencent.supersonic.common.pojo.exception.InvalidArgumentException;
import com.tencent.supersonic.common.util.SignatureUtils;
import com.tencent.supersonic.headless.api.service.AppService;
import com.tencent.supersonic.headless.common.model.enums.AppStatusEnum;
import com.tencent.supersonic.headless.common.model.response.AppDetailResp;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
@Component
@Aspect
@Order(1)
@Slf4j
public class ApiHeaderCheckAspect {
public static final String APPID = "appId";
private static final String TIMESTAMP = "timestamp";
private static final String SIGNATURE = "signature";
@Autowired
private AppService appService;
@Pointcut("@annotation(com.tencent.supersonic.headless.api.annotation.ApiHeaderCheck)")
private void apiPermissionCheck() {
}
@Around("apiPermissionCheck()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
Object[] objects = joinPoint.getArgs();
HttpServletRequest request = (HttpServletRequest) objects[1];
checkHeader(request);
return joinPoint.proceed();
}
private void checkHeader(HttpServletRequest request) {
String timestampStr = request.getHeader(TIMESTAMP);
String signature = request.getHeader(SIGNATURE);
String appId = request.getHeader(APPID);
if (StringUtils.isBlank(timestampStr)) {
throw new InvalidArgumentException("header中timestamp不可为空");
}
if (StringUtils.isBlank(signature)) {
throw new InvalidArgumentException("header中signature不可为空");
}
if (StringUtils.isBlank(appId)) {
throw new InvalidArgumentException("header中appId不可为空");
}
AppDetailResp appDetailResp = appService.getApp(Integer.parseInt(appId));
if (appDetailResp == null) {
throw new InvalidArgumentException("该appId对应的应用不存在");
}
if (!AppStatusEnum.ONLINE.equals(appDetailResp.getAppStatus())) {
throw new InvalidArgumentException("该应用暂时为非在线状态");
}
Pair<Boolean, String> checkResult = SignatureUtils.isValidSignature(appId, appDetailResp.getAppSecret(),
Long.parseLong(timestampStr), signature);
if (!checkResult.first) {
throw new InvalidArgumentException(checkResult.second);
}
}
}

View File

@@ -1,41 +0,0 @@
package com.tencent.supersonic.headless.api.persistence.dataobject;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.util.Date;
@Data
@TableName("s2_app")
public class AppDO {
@TableId(type = IdType.AUTO)
private Integer id;
private String name;
private String description;
private String config;
private Date endDate;
private Integer qps;
private String owner;
private Integer status;
private String appSecret;
private String createdBy;
private String updatedBy;
private Date createdAt;
private Date updatedAt;
}

View File

@@ -1,10 +0,0 @@
package com.tencent.supersonic.headless.api.persistence.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.tencent.supersonic.headless.api.persistence.dataobject.AppDO;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface AppMapper extends BaseMapper<AppDO> {
}

View File

@@ -1,43 +0,0 @@
package com.tencent.supersonic.headless.api.rest;
import com.tencent.supersonic.headless.api.service.ApiQueryService;
import com.tencent.supersonic.headless.common.model.response.QueryResultWithSchemaResp;
import com.tencent.supersonic.headless.common.query.request.MetaQueryApiReq;
import com.tencent.supersonic.headless.common.query.request.QueryApiReq;
import com.tencent.supersonic.headless.common.query.request.QueryStructReq;
import com.tencent.supersonic.headless.common.query.response.ApiQueryResultResp;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
@RestController
@RequestMapping("/api/semantic/apiQuery")
@Slf4j
public class ApiQueryController {
@Autowired
private ApiQueryService apiQueryService;
@PostMapping("/metricDataQueryById")
public ApiQueryResultResp metricDataQueryById(@RequestBody QueryApiReq queryApiReq,
HttpServletRequest request) throws Exception {
return apiQueryService.metricDataQueryById(queryApiReq, request);
}
@PostMapping("/metaQuery")
public Object metaQuery(@RequestBody MetaQueryApiReq metaQueryApiReq, HttpServletRequest request) {
return apiQueryService.metaQuery(metaQueryApiReq, request);
}
@PostMapping("/dataQueryByStruct")
public QueryResultWithSchemaResp dataQueryByStruct(QueryStructReq queryStructReq,
HttpServletRequest request) throws Exception {
return apiQueryService.dataQueryByStruct(queryStructReq, request);
}
}

View File

@@ -1,84 +0,0 @@
package com.tencent.supersonic.headless.api.rest;
import com.github.pagehelper.PageInfo;
import com.tencent.supersonic.auth.api.authentication.pojo.User;
import com.tencent.supersonic.auth.api.authentication.utils.UserHolder;
import com.tencent.supersonic.headless.api.service.AppService;
import com.tencent.supersonic.headless.common.model.request.AppQueryReq;
import com.tencent.supersonic.headless.common.model.request.AppReq;
import com.tencent.supersonic.headless.common.model.response.AppDetailResp;
import com.tencent.supersonic.headless.common.model.response.AppResp;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@RestController
@RequestMapping("/api/semantic/app")
public class AppController {
@Autowired
private AppService appService;
@PostMapping
public boolean save(@RequestBody AppReq app,
HttpServletRequest request, HttpServletResponse response) {
User user = UserHolder.findUser(request, response);
appService.save(app, user);
return true;
}
@PutMapping
public boolean update(@RequestBody AppReq app,
HttpServletRequest request, HttpServletResponse response) {
User user = UserHolder.findUser(request, response);
appService.update(app, user);
return true;
}
@PutMapping("/online/{id}")
public boolean online(@PathVariable("id") Integer id,
HttpServletRequest request, HttpServletResponse response) {
User user = UserHolder.findUser(request, response);
appService.online(id, user);
return true;
}
@PutMapping("/offline/{id}")
public boolean offline(@PathVariable("id") Integer id,
HttpServletRequest request, HttpServletResponse response) {
User user = UserHolder.findUser(request, response);
appService.offline(id, user);
return true;
}
@DeleteMapping("/{id}")
public boolean delete(@PathVariable("id") Integer id,
HttpServletRequest request, HttpServletResponse response) {
User user = UserHolder.findUser(request, response);
appService.delete(id, user);
return true;
}
@GetMapping("/{id}")
public AppDetailResp getApp(@PathVariable("id") Integer id,
HttpServletRequest request, HttpServletResponse response) {
User user = UserHolder.findUser(request, response);
return appService.getApp(id, user);
}
@PostMapping("/page")
public PageInfo<AppResp> pageApp(@RequestBody AppQueryReq appQueryReq,
HttpServletRequest request, HttpServletResponse response) {
User user = UserHolder.findUser(request, response);
return appService.pageApp(appQueryReq, user);
}
}

View File

@@ -1,47 +0,0 @@
package com.tencent.supersonic.headless.api.service;
import com.tencent.supersonic.headless.common.model.response.QueryResultWithSchemaResp;
import com.tencent.supersonic.headless.common.query.request.MetaQueryApiReq;
import com.tencent.supersonic.headless.common.query.request.QueryApiReq;
import com.tencent.supersonic.headless.common.query.request.QueryStructReq;
import com.tencent.supersonic.headless.common.query.response.ApiQueryResultResp;
import com.tencent.supersonic.headless.api.annotation.ApiHeaderCheck;
import javax.servlet.http.HttpServletRequest;
/**
* Api service for other apps to query meta info and data
*/
public interface ApiQueryService {
/**
* Query the metric data based on the metric id.
* The data will be drilled down based on the information configured when applying for the APP.
* @param queryApiReq
* @param request
* @return
* @throws Exception
*/
@ApiHeaderCheck
ApiQueryResultResp metricDataQueryById(QueryApiReq queryApiReq, HttpServletRequest request) throws Exception;
/**
* Query data based on structure
* @param queryStructReq
* @param request
* @return
* @throws Exception
*/
@ApiHeaderCheck
QueryResultWithSchemaResp dataQueryByStruct(QueryStructReq queryStructReq,
HttpServletRequest request) throws Exception;
/**
* Query the meta information of the metric, dimension and tag
* @param metaQueryApiReq
* @param request
* @return
*/
@ApiHeaderCheck
Object metaQuery(MetaQueryApiReq metaQueryApiReq, HttpServletRequest request);
}

View File

@@ -1,28 +0,0 @@
package com.tencent.supersonic.headless.api.service;
import com.github.pagehelper.PageInfo;
import com.tencent.supersonic.auth.api.authentication.pojo.User;
import com.tencent.supersonic.headless.common.model.request.AppQueryReq;
import com.tencent.supersonic.headless.common.model.request.AppReq;
import com.tencent.supersonic.headless.common.model.response.AppDetailResp;
import com.tencent.supersonic.headless.common.model.response.AppResp;
public interface AppService {
AppDetailResp save(AppReq app, User user);
AppDetailResp update(AppReq app, User user);
void online(Integer id, User user);
void offline(Integer id, User user);
void delete(Integer id, User user);
PageInfo<AppResp> pageApp(AppQueryReq appQueryReq, User user);
AppDetailResp getApp(Integer id, User user);
AppDetailResp getApp(Integer id);
}

View File

@@ -1,177 +0,0 @@
package com.tencent.supersonic.headless.api.service.impl;
import com.google.common.collect.Lists;
import com.tencent.supersonic.auth.api.authentication.pojo.User;
import com.tencent.supersonic.common.pojo.Aggregator;
import com.tencent.supersonic.common.pojo.Constants;
import com.tencent.supersonic.common.pojo.DateConf;
import com.tencent.supersonic.common.pojo.enums.ApiItemType;
import com.tencent.supersonic.common.pojo.enums.TimeDimensionEnum;
import com.tencent.supersonic.common.pojo.exception.InvalidArgumentException;
import com.tencent.supersonic.headless.api.aspect.ApiHeaderCheckAspect;
import com.tencent.supersonic.headless.api.service.ApiQueryService;
import com.tencent.supersonic.headless.api.service.AppService;
import com.tencent.supersonic.headless.common.model.pojo.Item;
import com.tencent.supersonic.headless.common.model.response.AppDetailResp;
import com.tencent.supersonic.headless.common.model.response.DimensionResp;
import com.tencent.supersonic.headless.common.model.response.MetricResp;
import com.tencent.supersonic.headless.common.model.response.QueryResultWithSchemaResp;
import com.tencent.supersonic.headless.common.query.pojo.ApiQuerySingleResult;
import com.tencent.supersonic.headless.common.query.request.MetaQueryApiReq;
import com.tencent.supersonic.headless.common.query.request.QueryApiReq;
import com.tencent.supersonic.headless.common.query.request.QueryStructReq;
import com.tencent.supersonic.headless.common.query.response.ApiQueryResultResp;
import com.tencent.supersonic.headless.model.domain.DimensionService;
import com.tencent.supersonic.headless.model.domain.MetricService;
import com.tencent.supersonic.headless.model.domain.pojo.DimensionFilter;
import com.tencent.supersonic.headless.model.domain.pojo.MetaFilter;
import com.tencent.supersonic.headless.api.annotation.ApiHeaderCheck;
import com.tencent.supersonic.headless.query.service.QueryService;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@Service
public class ApiQueryServiceImpl implements ApiQueryService {
private static final long result_size = 10000;
private AppService appService;
private MetricService metricService;
private DimensionService dimensionService;
private QueryService queryService;
public ApiQueryServiceImpl(AppService appService,
MetricService metricService,
DimensionService dimensionService,
QueryService queryService) {
this.appService = appService;
this.metricService = metricService;
this.dimensionService = dimensionService;
this.queryService = queryService;
}
@Override
@ApiHeaderCheck
public ApiQueryResultResp metricDataQueryById(QueryApiReq queryApiReq,
HttpServletRequest request) throws Exception {
AppDetailResp appDetailResp = getAppDetailResp(request);
authCheck(appDetailResp, queryApiReq.getIds(), ApiItemType.METRIC);
List<ApiQuerySingleResult> results = Lists.newArrayList();
Map<Long, Item> map = appDetailResp.getConfig().getItems().stream()
.collect(Collectors.toMap(Item::getId, i -> i));
for (Long id : queryApiReq.getIds()) {
Item item = map.get(id);
ApiQuerySingleResult apiQuerySingleResult = dataQuery(appDetailResp.getId(),
item, queryApiReq.getDateConf());
results.add(apiQuerySingleResult);
}
return ApiQueryResultResp.builder().results(results).build();
}
@Override
@ApiHeaderCheck
public QueryResultWithSchemaResp dataQueryByStruct(QueryStructReq queryStructReq,
HttpServletRequest request) throws Exception {
AppDetailResp appDetailResp = getAppDetailResp(request);
structAuthCheck(appDetailResp, queryStructReq);
return queryService.queryByStruct(queryStructReq, User.getAppUser(appDetailResp.getId()));
}
@Override
@ApiHeaderCheck
public Object metaQuery(MetaQueryApiReq metaQueryApiReq, HttpServletRequest request) {
AppDetailResp appDetailResp = getAppDetailResp(request);
MetaFilter metaFilter = new MetaFilter();
metaFilter.setIds(metaQueryApiReq.getIds());
if (ApiItemType.METRIC.equals(metaQueryApiReq.getType())) {
authCheck(appDetailResp, metaQueryApiReq.getIds(), ApiItemType.METRIC);
return metricService.getMetrics(metaFilter);
} else if (ApiItemType.DIMENSION.equals(metaQueryApiReq.getType())) {
authCheck(appDetailResp, metaQueryApiReq.getIds(), ApiItemType.DIMENSION);
return dimensionService.getDimensions(metaFilter);
} else if (ApiItemType.TAG.equals(metaQueryApiReq.getType())) {
throw new InvalidArgumentException("标签元数据类型正在支持中");
}
throw new InvalidArgumentException("不支持的元数据类型:" + metaQueryApiReq.getType());
}
private ApiQuerySingleResult dataQuery(Integer appId, Item item, DateConf dateConf) throws Exception {
MetricResp metricResp = metricService.getMetric(item.getId());
List<Item> items = item.getRelateItems();
List<DimensionResp> dimensionResps = Lists.newArrayList();
if (!CollectionUtils.isEmpty(items)) {
List<Long> ids = items.stream().map(Item::getId).collect(Collectors.toList());
DimensionFilter dimensionFilter = new DimensionFilter();
dimensionFilter.setIds(ids);
dimensionResps = dimensionService.getDimensions(dimensionFilter);
}
QueryStructReq queryStructReq = buildQueryStructReq(dimensionResps, metricResp, dateConf);
QueryResultWithSchemaResp queryResultWithSchemaResp =
queryService.queryByStruct(queryStructReq, User.getAppUser(appId));
ApiQuerySingleResult apiQuerySingleResult = new ApiQuerySingleResult();
apiQuerySingleResult.setItem(item);
apiQuerySingleResult.setResult(queryResultWithSchemaResp);
return apiQuerySingleResult;
}
private AppDetailResp getAppDetailResp(HttpServletRequest request) {
int appId = Integer.parseInt(request.getHeader(ApiHeaderCheckAspect.APPID));
return appService.getApp(appId);
}
private void authCheck(AppDetailResp appDetailResp, List<Long> ids, ApiItemType type) {
Set<Long> idsInApp = appDetailResp.getConfig().getAllItems().stream()
.filter(item -> type.equals(item.getType())).map(Item::getId).collect(Collectors.toSet());
if (!idsInApp.containsAll(ids)) {
throw new InvalidArgumentException("查询范围超过应用申请范围, 请检查");
}
}
private void structAuthCheck(AppDetailResp appDetailResp, QueryStructReq queryStructReq) {
List<Long> metricIdsToQuery = metricService.getMetrics(new MetaFilter(queryStructReq.getModelIds()))
.stream().filter(metricResp -> queryStructReq.getMetrics().contains(metricResp.getBizName()))
.map(MetricResp::getId).collect(Collectors.toList());
List<Long> dimensionIdsToQuery = dimensionService.getDimensions(new MetaFilter(queryStructReq.getModelIds()))
.stream().filter(dimensionResp -> queryStructReq.getGroups().contains(dimensionResp.getBizName()))
.map(DimensionResp::getId).collect(Collectors.toList());
authCheck(appDetailResp, metricIdsToQuery, ApiItemType.METRIC);
authCheck(appDetailResp, dimensionIdsToQuery, ApiItemType.DIMENSION);
}
private QueryStructReq buildQueryStructReq(List<DimensionResp> dimensionResps,
MetricResp metricResp, DateConf dateConf) {
Set<Long> modelIds = dimensionResps.stream().map(DimensionResp::getModelId).collect(Collectors.toSet());
modelIds.add(metricResp.getModelId());
QueryStructReq queryStructReq = new QueryStructReq();
queryStructReq.setGroups(dimensionResps.stream()
.map(DimensionResp::getBizName).collect(Collectors.toList()));
queryStructReq.getGroups().add(0, getTimeDimension(dateConf));
Aggregator aggregator = new Aggregator();
aggregator.setColumn(metricResp.getBizName());
queryStructReq.setAggregators(Lists.newArrayList(aggregator));
queryStructReq.setDateInfo(dateConf);
queryStructReq.setModelIds(modelIds);
queryStructReq.setLimit(result_size);
return queryStructReq;
}
private String getTimeDimension(DateConf dateConf) {
if (Constants.MONTH.equals(dateConf.getPeriod())) {
return TimeDimensionEnum.MONTH.getName();
} else if (Constants.WEEK.equals(dateConf.getPeriod())) {
return TimeDimensionEnum.WEEK.getName();
} else {
return TimeDimensionEnum.DAY.getName();
}
}
}

View File

@@ -1,225 +0,0 @@
package com.tencent.supersonic.headless.api.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.tencent.supersonic.auth.api.authentication.pojo.User;
import com.tencent.supersonic.common.pojo.exception.InvalidArgumentException;
import com.tencent.supersonic.common.pojo.exception.InvalidPermissionException;
import com.tencent.supersonic.common.util.BeanMapper;
import com.tencent.supersonic.common.util.PageUtils;
import com.tencent.supersonic.headless.api.persistence.dataobject.AppDO;
import com.tencent.supersonic.headless.api.persistence.mapper.AppMapper;
import com.tencent.supersonic.headless.api.service.AppService;
import com.tencent.supersonic.headless.common.model.enums.AppStatusEnum;
import com.tencent.supersonic.headless.common.model.pojo.AppConfig;
import com.tencent.supersonic.headless.common.model.request.AppQueryReq;
import com.tencent.supersonic.headless.common.model.request.AppReq;
import com.tencent.supersonic.headless.common.model.response.AppDetailResp;
import com.tencent.supersonic.headless.common.model.response.AppResp;
import com.tencent.supersonic.headless.common.model.response.DimensionResp;
import com.tencent.supersonic.headless.common.model.response.MetricResp;
import com.tencent.supersonic.headless.model.domain.DimensionService;
import com.tencent.supersonic.headless.model.domain.MetricService;
import com.tencent.supersonic.headless.model.domain.pojo.MetaFilter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
@Service
public class AppServiceImpl extends ServiceImpl<AppMapper, AppDO> implements AppService {
private AppMapper appMapper;
private MetricService metricService;
private DimensionService dimensionService;
public AppServiceImpl(AppMapper appMapper, MetricService metricService,
DimensionService dimensionService) {
this.appMapper = appMapper;
this.metricService = metricService;
this.dimensionService = dimensionService;
}
@Override
public AppDetailResp save(AppReq app, User user) {
app.createdBy(user.getName());
AppDO appDO = new AppDO();
BeanMapper.mapper(app, appDO);
appDO.setStatus(AppStatusEnum.INIT.getCode());
appDO.setConfig(JSONObject.toJSONString(app.getConfig()));
appDO.setAppSecret(getUniqueId());
appMapper.insert(appDO);
return convertDetail(appDO);
}
@Override
public AppDetailResp update(AppReq app, User user) {
app.updatedBy(user.getName());
AppDO appDO = getById(app.getId());
checkAuth(appDO, user);
BeanMapper.mapper(app, appDO);
appDO.setConfig(JSONObject.toJSONString(app.getConfig()));
appMapper.updateById(appDO);
return convertDetail(appDO);
}
@Override
public void online(Integer id, User user) {
AppDO appDO = getAppDO(id);
checkAuth(appDO, user);
appDO.setStatus(AppStatusEnum.ONLINE.getCode());
appDO.setUpdatedAt(new Date());
appDO.setUpdatedBy(user.getName());
updateById(appDO);
}
@Override
public void offline(Integer id, User user) {
AppDO appDO = getAppDO(id);
checkAuth(appDO, user);
appDO.setStatus(AppStatusEnum.OFFLINE.getCode());
appDO.setUpdatedAt(new Date());
appDO.setUpdatedBy(user.getName());
updateById(appDO);
}
@Override
public void delete(Integer id, User user) {
AppDO appDO = getAppDO(id);
checkAuth(appDO, user);
appDO.setStatus(AppStatusEnum.DELETED.getCode());
appDO.setUpdatedAt(new Date());
appDO.setUpdatedBy(user.getName());
updateById(appDO);
}
@Override
public PageInfo<AppResp> pageApp(AppQueryReq appQueryReq, User user) {
PageInfo<AppDO> appDOPageInfo = PageHelper.startPage(appQueryReq.getCurrent(),
appQueryReq.getPageSize())
.doSelectPageInfo(() -> queryApp(appQueryReq));
PageInfo<AppResp> appPageInfo = PageUtils.pageInfo2PageInfoVo(appDOPageInfo);
Map<Long, MetricResp> metricResps = metricService.getMetrics(new MetaFilter())
.stream().collect(Collectors.toMap(MetricResp::getId, m -> m));
Map<Long, DimensionResp> dimensionResps = dimensionService.getDimensions(new MetaFilter())
.stream().collect(Collectors.toMap(DimensionResp::getId, m -> m));
appPageInfo.setList(appDOPageInfo.getList().stream().map(appDO
-> convert(appDO, dimensionResps, metricResps, user))
.collect(Collectors.toList()));
return appPageInfo;
}
public List<AppDO> queryApp(AppQueryReq appQueryReq) {
QueryWrapper<AppDO> appDOQueryWrapper = new QueryWrapper<>();
appDOQueryWrapper.lambda().ne(AppDO::getStatus, AppStatusEnum.DELETED.getCode());
if (StringUtils.isNotBlank(appQueryReq.getName())) {
appDOQueryWrapper.lambda().like(AppDO::getName, appQueryReq.getName());
}
if (!CollectionUtils.isEmpty(appQueryReq.getStatus())) {
appDOQueryWrapper.lambda().in(AppDO::getStatus, appQueryReq.getStatus());
}
if (StringUtils.isNotBlank(appQueryReq.getCreatedBy())) {
appDOQueryWrapper.lambda().eq(AppDO::getCreatedBy, appQueryReq.getCreatedBy());
}
return list(appDOQueryWrapper);
}
@Override
public AppDetailResp getApp(Integer id, User user) {
AppDO appDO = getAppDO(id);
Map<Long, MetricResp> metricResps = metricService.getMetrics(new MetaFilter())
.stream().collect(Collectors.toMap(MetricResp::getId, m -> m));
Map<Long, DimensionResp> dimensionResps = dimensionService.getDimensions(new MetaFilter())
.stream().collect(Collectors.toMap(DimensionResp::getId, m -> m));
checkAuth(appDO, user);
return convertDetail(appDO, dimensionResps, metricResps);
}
@Override
public AppDetailResp getApp(Integer id) {
AppDO appDO = getAppDO(id);
return convertDetail(appDO, new HashMap<>(), new HashMap<>());
}
private AppDO getAppDO(Integer id) {
AppDO appDO = getById(id);
if (appDO == null) {
throw new InvalidArgumentException("该应用不存在");
}
return appDO;
}
private void checkAuth(AppDO appDO, User user) {
if (!hasAuth(appDO, user)) {
throw new InvalidPermissionException("您不是该应用的负责人, 暂无权查看或者修改");
}
}
private boolean hasAuth(AppDO appDO, User user) {
if (appDO.getCreatedBy().equalsIgnoreCase(user.getName())) {
return true;
}
return StringUtils.isNotBlank(appDO.getOwner())
&& appDO.getOwner().contains(user.getName());
}
private AppResp convert(AppDO appDO, Map<Long, DimensionResp> dimensionMap,
Map<Long, MetricResp> metricMap, User user) {
AppResp app = new AppResp();
BeanMapper.mapper(appDO, app);
AppConfig appConfig = JSONObject.parseObject(appDO.getConfig(), AppConfig.class);
fillItemName(appConfig, dimensionMap, metricMap);
app.setConfig(appConfig);
app.setAppStatus(AppStatusEnum.fromCode(appDO.getStatus()));
app.setHasAdminRes(hasAuth(appDO, user));
return app;
}
private AppDetailResp convertDetail(AppDO appDO) {
return convertDetail(appDO, new HashMap<>(), new HashMap<>());
}
private AppDetailResp convertDetail(AppDO appDO, Map<Long, DimensionResp> dimensionMap,
Map<Long, MetricResp> metricMap) {
AppDetailResp app = new AppDetailResp();
BeanMapper.mapper(appDO, app);
AppConfig appConfig = JSONObject.parseObject(appDO.getConfig(), AppConfig.class);
fillItemName(appConfig, dimensionMap, metricMap);
app.setConfig(appConfig);
app.setAppStatus(AppStatusEnum.fromCode(appDO.getStatus()));
return app;
}
private void fillItemName(AppConfig appConfig, Map<Long, DimensionResp> dimensionMap,
Map<Long, MetricResp> metricMap) {
appConfig.getItems().forEach(metricItem -> {
metricItem.setName(metricMap.getOrDefault(metricItem.getId(), new MetricResp()).getName());
metricItem.setBizName(metricMap.getOrDefault(metricItem.getId(), new MetricResp()).getBizName());
metricItem.setCreatedBy(metricMap.getOrDefault(metricItem.getId(), new MetricResp()).getCreatedBy());
metricItem.getRelateItems().forEach(dimensionItem -> {
dimensionItem.setName(dimensionMap.getOrDefault(dimensionItem.getId(), new DimensionResp()).getName());
dimensionItem.setBizName(dimensionMap.getOrDefault(dimensionItem.getId(),
new DimensionResp()).getBizName());
dimensionItem.setCreatedBy(dimensionMap.getOrDefault(dimensionItem.getId(),
new DimensionResp()).getCreatedBy());
});
});
}
private String getUniqueId() {
return UUID.randomUUID().toString().replaceAll("_", "");
}
}

View File

@@ -0,0 +1,15 @@
package com.tencent.supersonic.headless.common.core.enums;
public enum AggOption {
NATIVE,
AGGREGATION,
DEFAULT;
public static AggOption getAggregation(boolean isNativeQuery) {
return isNativeQuery ? NATIVE : AGGREGATION;
}
public static boolean isAgg(AggOption aggOption) {
return NATIVE.equals(aggOption) ? false : true;
}
}

View File

@@ -0,0 +1,8 @@
package com.tencent.supersonic.headless.common.core.enums;
public enum QueryOptMode {
NONE,
MATERIALIZATION
}

View File

@@ -0,0 +1,34 @@
package com.tencent.supersonic.headless.common.core.enums;
public enum QueryTypeBack {
NORMAL("NORMAL", 0),
PRE_FLUSH("PRE_FLUSH", 1);
private String value;
private Integer state;
QueryTypeBack(String value, Integer state) {
this.value = value;
this.state = state;
}
public static QueryTypeBack of(String src) {
for (QueryTypeBack operatorEnum : QueryTypeBack.values()) {
if (src.toUpperCase().contains(operatorEnum.value)) {
return operatorEnum;
}
}
return null;
}
public String getValue() {
return value;
}
public Integer getState() {
return state;
}
}

View File

@@ -0,0 +1,18 @@
package com.tencent.supersonic.headless.common.core.pojo;
import lombok.Data;
@Data
public class Cache {
private Boolean cache = true;
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");
sb.append("\"cache\":")
.append(cache);
sb.append('}');
return sb.toString();
}
}

View File

@@ -0,0 +1,17 @@
package com.tencent.supersonic.headless.common.core.pojo;
import com.tencent.supersonic.headless.common.core.enums.AggOption;
import lombok.Data;
import java.util.List;
@Data
public class MetricTable {
private String alias;
private List<String> metrics;
private List<String> dimensions;
private String where;
private AggOption aggOption = AggOption.DEFAULT;
}

View File

@@ -0,0 +1,34 @@
package com.tencent.supersonic.headless.common.core.pojo;
import lombok.Data;
import javax.validation.constraints.NotBlank;
@Data
public class Param {
@NotBlank(message = "Invald parameter name")
private String name;
@NotBlank(message = "Invalid parameter value")
private String value;
public Param() {
}
public Param(String name, String value) {
this.name = name;
this.value = value;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");
sb.append("\"name\":\"")
.append(name).append('\"');
sb.append(",\"value\":\"")
.append(value).append('\"');
sb.append('}');
return sb.toString();
}
}

View File

@@ -0,0 +1,15 @@
package com.tencent.supersonic.headless.common.core.pojo;
import com.tencent.supersonic.headless.common.server.pojo.Item;
import com.tencent.supersonic.headless.common.server.response.QueryResultWithSchemaResp;
import lombok.Data;
@Data
public class SingleItemQueryResult {
private Item item;
private QueryResultWithSchemaResp result;
}

View File

@@ -0,0 +1,16 @@
package com.tencent.supersonic.headless.common.core.request;
import com.tencent.supersonic.common.pojo.DateConf;
import lombok.Data;
import java.util.List;
@Data
public class BatchDownloadReq {
private List<Long> metricIds;
private DateConf dateInfo;
private boolean isTransform = true;
}

View File

@@ -0,0 +1,14 @@
package com.tencent.supersonic.headless.common.core.request;
import lombok.Data;
@Data
public class DownloadStructReq extends QueryStructReq {
private boolean isTransform;
public void setIsTransform(boolean isTransform) {
this.isTransform = isTransform;
}
}

View File

@@ -0,0 +1,20 @@
package com.tencent.supersonic.headless.common.core.request;
import com.tencent.supersonic.headless.common.server.enums.QueryType;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Data
@ToString
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ExplainSqlReq<T> {
private QueryType queryTypeEnum;
private T queryReq;
}

View File

@@ -0,0 +1,27 @@
package com.tencent.supersonic.headless.common.core.request;
import java.util.List;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Data
@ToString
@NoArgsConstructor
public class ItemUseReq {
private String startTime;
private Long modelId;
private List<Long> modelIds;
private Boolean cacheEnable = true;
private String metric;
public ItemUseReq(String startTime, Long modelId) {
this.startTime = startTime;
this.modelId = modelId;
}
public ItemUseReq(String startTime, List<Long> modelIds) {
this.startTime = startTime;
this.modelIds = modelIds;
}
}

View File

@@ -0,0 +1,17 @@
package com.tencent.supersonic.headless.common.core.request;
import com.tencent.supersonic.common.pojo.enums.ApiItemType;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import java.util.List;
@Data
public class MetaQueryApiReq {
@NotEmpty(message = "ids不可为空")
private List<Long> ids;
private ApiItemType type = ApiItemType.METRIC;
}

View File

@@ -0,0 +1,20 @@
package com.tencent.supersonic.headless.common.core.request;
import com.tencent.supersonic.common.pojo.ColumnOrder;
import java.util.List;
import java.util.Map;
import lombok.Data;
@Data
public class MetricReq {
private List<String> metrics;
private List<String> dimensions;
private String rootPath = "";
private Map<String, String> variables;
private String where;
private Long limit;
private List<ColumnOrder> order;
private boolean nativeQuery = false;
}

View File

@@ -0,0 +1,25 @@
package com.tencent.supersonic.headless.common.core.request;
import com.tencent.supersonic.headless.common.core.pojo.MetricTable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.Data;
@Data
public class ParseSqlReq {
private String rootPath = "";
private Map<String, String> variables;
private String sql = "";
private List<MetricTable> tables;
private boolean supportWith = true;
private boolean withAlias = true;
public Map<String, String> getVariables() {
if (variables == null) {
variables = new HashMap<>();
}
return variables;
}
}

View File

@@ -0,0 +1,16 @@
package com.tencent.supersonic.headless.common.core.request;
import com.tencent.supersonic.common.pojo.DateConf;
import lombok.Data;
import lombok.ToString;
@Data
@ToString
public class QueryDimValueReq {
private Long modelId;
private String dimensionBizName;
private String value;
private DateConf dateInfo;
}

View File

@@ -0,0 +1,20 @@
package com.tencent.supersonic.headless.common.core.request;
import com.tencent.supersonic.common.pojo.DateConf;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import java.util.List;
@Data
public class QueryItemReq {
@NotEmpty(message = "ids不可为空")
private List<Long> ids;
private DateConf dateConf = new DateConf();
//result size of single id
private Long limit;
}

View File

@@ -0,0 +1,24 @@
package com.tencent.supersonic.headless.common.core.request;
import com.alibaba.fastjson.JSONObject;
import java.util.List;
import lombok.Data;
import lombok.ToString;
import org.apache.commons.codec.digest.DigestUtils;
@Data
@ToString
public class QueryMultiStructReq {
List<QueryStructReq> queryStructReqs;
public String toCustomizedString() {
return JSONObject.toJSONString(queryStructReqs);
}
public String generateCommandMd5() {
return DigestUtils.md5Hex(this.toCustomizedString());
}
}

View File

@@ -0,0 +1,30 @@
package com.tencent.supersonic.headless.common.core.request;
import com.google.common.collect.Lists;
import lombok.Data;
import lombok.ToString;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@Data
@ToString
public class QueryS2SQLReq {
private Set<Long> modelIds;
private String sql;
private Map<String, String> variables;
public void setModelId(Long modelId) {
modelIds = new HashSet<>();
modelIds.add(modelId);
}
public List<Long> getModelIds() {
return Lists.newArrayList(modelIds);
}
}

View File

@@ -0,0 +1,298 @@
package com.tencent.supersonic.headless.common.core.request;
import com.google.common.collect.Lists;
import com.tencent.supersonic.common.pojo.Aggregator;
import com.tencent.supersonic.common.pojo.Constants;
import com.tencent.supersonic.common.pojo.DateConf;
import com.tencent.supersonic.common.pojo.Filter;
import com.tencent.supersonic.common.pojo.Order;
import com.tencent.supersonic.common.pojo.enums.QueryType;
import com.tencent.supersonic.common.pojo.enums.AggOperatorEnum;
import com.tencent.supersonic.common.util.ContextUtils;
import com.tencent.supersonic.common.util.DateModeUtils;
import com.tencent.supersonic.common.util.SqlFilterUtils;
import com.tencent.supersonic.common.util.jsqlparser.SqlParserAddHelper;
import com.tencent.supersonic.headless.common.core.pojo.Cache;
import com.tencent.supersonic.headless.common.core.pojo.Param;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Function;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.select.GroupByElement;
import net.sf.jsqlparser.statement.select.Limit;
import net.sf.jsqlparser.statement.select.OrderByElement;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SelectExpressionItem;
import net.sf.jsqlparser.statement.select.SelectItem;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.util.Strings;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
@Data
@Slf4j
public class QueryStructReq {
private Set<Long> modelIds;
private String modelName;
private List<String> groups = new ArrayList<>();
private List<Aggregator> aggregators = new ArrayList<>();
private List<Order> orders = new ArrayList<>();
private List<Filter> dimensionFilters = new ArrayList<>();
private List<Filter> metricFilters = new ArrayList<>();
private List<Param> params = new ArrayList<>();
private DateConf dateInfo;
private Long limit = 2000L;
private QueryType queryType = QueryType.ID;
private Cache cacheInfo;
/**
* Later deleted for compatibility only
*/
private String s2SQL;
/**
* Later deleted for compatibility only
*/
private String correctS2SQL;
public void setModelId(Long modelId) {
modelIds = new HashSet<>();
modelIds.add(modelId);
}
public List<Long> getModelIds() {
return Lists.newArrayList(modelIds);
}
public Set<Long> getModelIdSet() {
return modelIds;
}
public List<String> getGroups() {
if (!CollectionUtils.isEmpty(this.groups)) {
this.groups = groups.stream().filter(group -> !Strings.isEmpty(group)).collect(Collectors.toList());
}
if (CollectionUtils.isEmpty(this.groups)) {
this.groups = Lists.newArrayList();
}
return this.groups;
}
public List<String> getMetrics() {
List<String> metrics = Lists.newArrayList();
if (!CollectionUtils.isEmpty(this.aggregators)) {
metrics = aggregators.stream().map(Aggregator::getColumn).collect(Collectors.toList());
}
return metrics;
}
public List<Order> getOrders() {
if (orders == null) {
return Lists.newArrayList();
}
return orders;
}
public List<Param> getParams() {
if (params == null) {
return Lists.newArrayList();
}
return params;
}
public String toCustomizedString() {
StringBuilder stringBuilder = new StringBuilder("{");
stringBuilder.append("\"modelId\":")
.append(modelIds);
stringBuilder.append(",\"groups\":")
.append(groups);
stringBuilder.append(",\"aggregators\":")
.append(aggregators);
stringBuilder.append(",\"orders\":")
.append(orders);
stringBuilder.append(",\"filters\":")
.append(dimensionFilters);
stringBuilder.append(",\"dateInfo\":")
.append(dateInfo);
stringBuilder.append(",\"params\":")
.append(params);
stringBuilder.append(",\"limit\":")
.append(limit);
stringBuilder.append('}');
return stringBuilder.toString();
}
public String generateCommandMd5() {
return DigestUtils.md5Hex(this.toCustomizedString());
}
public List<Filter> getOriginalFilter() {
return dimensionFilters;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");
sb.append("\"modelId\":")
.append(modelIds);
sb.append(",\"groups\":")
.append(groups);
sb.append(",\"aggregators\":")
.append(aggregators);
sb.append(",\"orders\":")
.append(orders);
sb.append(",\"dimensionFilters\":")
.append(dimensionFilters);
sb.append(",\"metricFilters\":")
.append(metricFilters);
sb.append(",\"params\":")
.append(params);
sb.append(",\"dateInfo\":")
.append(dateInfo);
sb.append(",\"limit\":")
.append(limit);
sb.append(",\"cacheInfo\":")
.append(cacheInfo);
sb.append('}');
return sb.toString();
}
/**
* convert queryStructReq to QueryS2QLReq
*
* @param queryStructReq
* @return
*/
public QueryS2SQLReq convert(QueryStructReq queryStructReq) {
String sql = null;
try {
sql = buildSql(queryStructReq);
} catch (Exception e) {
log.error("buildSql error", e);
}
QueryS2SQLReq result = new QueryS2SQLReq();
result.setSql(sql);
result.setModelIds(queryStructReq.getModelIdSet());
result.setVariables(new HashMap<>());
return result;
}
private String buildSql(QueryStructReq queryStructReq) throws JSQLParserException {
Select select = new Select();
//1.Set the select items (columns)
PlainSelect plainSelect = new PlainSelect();
List<SelectItem> selectItems = new ArrayList<>();
List<String> groups = queryStructReq.getGroups();
if (!CollectionUtils.isEmpty(groups)) {
for (String group : groups) {
selectItems.add(new SelectExpressionItem(new Column(group)));
}
}
List<Aggregator> aggregators = queryStructReq.getAggregators();
if (!CollectionUtils.isEmpty(aggregators)) {
for (Aggregator aggregator : aggregators) {
String columnName = aggregator.getColumn();
if (queryStructReq.getQueryType().isNativeAggQuery()) {
selectItems.add(new SelectExpressionItem(new Column(columnName)));
} else {
Function sumFunction = new Function();
AggOperatorEnum func = aggregator.getFunc();
if (AggOperatorEnum.UNKNOWN.equals(func)) {
func = AggOperatorEnum.SUM;
}
sumFunction.setName(func.getOperator());
if (AggOperatorEnum.COUNT_DISTINCT.equals(func)) {
sumFunction.setName("count");
sumFunction.setDistinct(true);
}
sumFunction.setParameters(new ExpressionList(new Column(columnName)));
SelectExpressionItem selectExpressionItem = new SelectExpressionItem(sumFunction);
selectExpressionItem.setAlias(new Alias(columnName));
selectItems.add(selectExpressionItem);
}
}
}
plainSelect.setSelectItems(selectItems);
//2.Set the table name
Table table = new Table(queryStructReq.getModelName());
plainSelect.setFromItem(table);
//3.Set the order by clause
List<Order> orders = queryStructReq.getOrders();
if (!CollectionUtils.isEmpty(orders)) {
List<OrderByElement> orderByElements = new ArrayList<>();
for (Order order : orders) {
if (StringUtils.isBlank(order.getColumn())) {
continue;
}
OrderByElement orderByElement = new OrderByElement();
orderByElement.setExpression(new Column(order.getColumn()));
orderByElement.setAsc(false);
if (Constants.ASC_UPPER.equalsIgnoreCase(order.getDirection())) {
orderByElement.setAsc(true);
}
orderByElements.add(orderByElement);
}
plainSelect.setOrderByElements(orderByElements);
}
//4.Set the group by clause
if (!CollectionUtils.isEmpty(groups) && !queryStructReq.getQueryType().isNativeAggQuery()) {
GroupByElement groupByElement = new GroupByElement();
for (String group : groups) {
groupByElement.addGroupByExpression(new Column(group));
}
plainSelect.setGroupByElement(groupByElement);
}
//5.Set the limit clause
if (Objects.nonNull(queryStructReq.getLimit())) {
Limit limit = new Limit();
limit.setRowCount(new LongValue(queryStructReq.getLimit()));
plainSelect.setLimit(limit);
}
select.setSelectBody(plainSelect);
//6.Set where
List<Filter> dimensionFilters = queryStructReq.getDimensionFilters();
SqlFilterUtils sqlFilterUtils = ContextUtils.getBean(SqlFilterUtils.class);
String whereClause = sqlFilterUtils.getWhereClause(dimensionFilters, false);
String sql = select.toString();
if (StringUtils.isNotBlank(whereClause)) {
Expression expression = CCJSqlParserUtil.parseCondExpression(whereClause);
sql = SqlParserAddHelper.addWhere(sql, expression);
}
//7.Set DateInfo
DateModeUtils dateModeUtils = ContextUtils.getBean(DateModeUtils.class);
String dateWhereStr = dateModeUtils.getDateWhereStr(queryStructReq.getDateInfo());
if (StringUtils.isNotBlank(dateWhereStr)) {
Expression expression = CCJSqlParserUtil.parseCondExpression(dateWhereStr);
sql = SqlParserAddHelper.addWhere(sql, expression);
}
return sql;
}
}

View File

@@ -0,0 +1,15 @@
package com.tencent.supersonic.headless.common.core.response;
import com.tencent.supersonic.headless.common.core.pojo.SingleItemQueryResult;
import lombok.Builder;
import lombok.Data;
import java.util.List;
@Data
@Builder
public class ItemQueryResultResp {
private List<SingleItemQueryResult> results;
}

View File

@@ -0,0 +1,24 @@
package com.tencent.supersonic.headless.common.core.response;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Data
@NoArgsConstructor
@ToString
public class ItemUseResp {
private Long domainId;
private String type;
private Long itemId;
private String bizName;
private Long useCnt;
public ItemUseResp(Long domainId, String type, String bizName, Long useCnt) {
this.domainId = domainId;
this.type = type;
this.bizName = bizName;
this.useCnt = useCnt;
}
}

View File

@@ -0,0 +1,28 @@
package com.tencent.supersonic.headless.common.core.response;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class SqlParserResp {
private String sql = "";
private String sourceId = "";
private String errMsg = "";
private Boolean ok;
public boolean isOk() {
this.ok = "".equals(errMsg) && !"".equals(sql);
return ok;
}
public SqlParserResp error(String msg) {
this.setErrMsg(msg);
return this;
}
}

View File

@@ -0,0 +1,29 @@
package com.tencent.supersonic.headless.common.server.enums;
public enum AppStatus {
INIT(0),
ONLINE(1),
OFFLINE(2),
DELETED(3),
UNKNOWN(4);
private Integer code;
AppStatus(Integer code) {
this.code = code;
}
public Integer getCode() {
return code;
}
public static AppStatus fromCode(Integer code) {
for (AppStatus appStatusEnum : AppStatus.values()) {
if (appStatusEnum.getCode().equals(code)) {
return appStatusEnum;
}
}
return AppStatus.UNKNOWN;
}
}

View File

@@ -0,0 +1,109 @@
package com.tencent.supersonic.headless.common.server.enums;
import com.tencent.supersonic.common.pojo.Constants;
import java.util.HashSet;
import java.util.Set;
public enum DataType {
MYSQL("mysql", "mysql", "com.mysql.cj.jdbc.Driver", "`", "`", "'", "'"),
HIVE2("hive2", "hive", "org.apache.hive.jdbc.HiveDriver", "`", "`", "`", "`"),
ORACLE("oracle", "oracle", "oracle.jdbc.driver.OracleDriver", "\"", "\"", "\"", "\""),
SQLSERVER("sqlserver", "sqlserver", "com.microsoft.sqlserver.jdbc.SQLServerDriver", "\"", "\"", "\"", "\""),
H2("h2", "h2", "org.h2.Driver", "`", "`", "\"", "\""),
PHOENIX("phoenix", "hbase phoenix", "org.apache.phoenix.jdbc.PhoenixDriver", "", "", "\"", "\""),
MONGODB("mongo", "mongodb", "mongodb.jdbc.MongoDriver", "`", "`", "\"", "\""),
ELASTICSEARCH("elasticsearch", "elasticsearch", "com.amazon.opendistroforelasticsearch.jdbc.Driver", "", "", "'",
"'"),
PRESTO("presto", "presto", "com.facebook.presto.jdbc.PrestoDriver", "\"", "\"", "\"", "\""),
MOONBOX("moonbox", "moonbox", "moonbox.jdbc.MbDriver", "`", "`", "`", "`"),
CASSANDRA("cassandra", "cassandra", "com.github.adejanovski.cassandra.jdbc.CassandraDriver", "", "", "'", "'"),
CLICKHOUSE("clickhouse", "clickhouse", "ru.yandex.clickhouse.ClickHouseDriver", "", "", "\"", "\""),
KYLIN("kylin", "kylin", "org.apache.kylin.jdbc.Driver", "\"", "\"", "\"", "\""),
VERTICA("vertica", "vertica", "com.vertica.jdbc.Driver", "", "", "'", "'"),
HANA("sap", "sap hana", "com.sap.db.jdbc.Driver", "", "", "'", "'"),
IMPALA("impala", "impala", "com.cloudera.impala.jdbc41.Driver", "", "", "'", "'"),
TDENGINE("TAOS", "TAOS", "com.taosdata.jdbc.TSDBDriver", "'", "'", "\"", "\"");
private String feature;
private String desc;
private String driver;
private String keywordPrefix;
private String keywordSuffix;
private String aliasPrefix;
private String aliasSuffix;
DataType(String feature, String desc, String driver, String keywordPrefix, String keywordSuffix,
String aliasPrefix, String aliasSuffix) {
this.feature = feature;
this.desc = desc;
this.driver = driver;
this.keywordPrefix = keywordPrefix;
this.keywordSuffix = keywordSuffix;
this.aliasPrefix = aliasPrefix;
this.aliasSuffix = aliasSuffix;
}
public static DataType urlOf(String jdbcUrl) throws RuntimeException {
String url = jdbcUrl.toLowerCase().trim();
for (DataType dataTypeEnum : values()) {
if (url.startsWith(String.format(Constants.JDBC_PREFIX_FORMATTER, dataTypeEnum.feature))) {
return dataTypeEnum;
}
}
return null;
}
public static Set<String> getAllSupportedDatasourceNameSet() {
Set<String> datasourceSet = new HashSet<>();
for (DataType datasource : values()) {
datasourceSet.add(datasource.getFeature());
}
return datasourceSet;
}
public String getFeature() {
return feature;
}
public String getDesc() {
return desc;
}
public String getDriver() {
return driver;
}
public String getKeywordPrefix() {
return keywordPrefix;
}
public String getKeywordSuffix() {
return keywordSuffix;
}
public String getAliasPrefix() {
return aliasPrefix;
}
public String getAliasSuffix() {
return aliasSuffix;
}
}

View File

@@ -0,0 +1,9 @@
package com.tencent.supersonic.headless.common.server.enums;
public enum DimensionType {
categorical,
time
}

View File

@@ -0,0 +1,8 @@
package com.tencent.supersonic.headless.common.server.enums;
public enum IdentifyType {
primary,
foreign,
}

View File

@@ -0,0 +1,9 @@
package com.tencent.supersonic.headless.common.server.enums;
public enum MetricType {
ATOMIC,
DERIVED
}

View File

@@ -0,0 +1,27 @@
package com.tencent.supersonic.headless.common.server.enums;
import java.util.Objects;
public enum ModelSourceType {
FULL,
PARTITION,
ZIPPER;
public static ModelSourceType of(String src) {
for (ModelSourceType modelSourceTypeEnum : ModelSourceType.values()) {
if (Objects.nonNull(src) && src.equalsIgnoreCase(modelSourceTypeEnum.name())) {
return modelSourceTypeEnum;
}
}
return null;
}
public static boolean isZipper(ModelSourceType modelSourceTypeEnum) {
return Objects.nonNull(modelSourceTypeEnum) && ZIPPER.equals(modelSourceTypeEnum);
}
public static boolean isZipper(String str) {
ModelSourceType modelSourceTypeEnum = of(str);
return Objects.nonNull(modelSourceTypeEnum) && isZipper(modelSourceTypeEnum);
}
}

View File

@@ -0,0 +1,28 @@
package com.tencent.supersonic.headless.common.server.enums;
public enum QueryType {
SQL("SQL"),
STRUCT("STRUCT");
private String value;
QueryType(String value) {
this.value = value;
}
public static QueryType of(String src) {
for (QueryType operatorEnum : QueryType.values()) {
if (src.toUpperCase().contains(operatorEnum.value)) {
return operatorEnum;
}
}
return null;
}
public String getValue() {
return value;
}
}

View File

@@ -0,0 +1,10 @@
package com.tencent.supersonic.headless.common.server.enums;
public enum SemanticType {
CATEGORY,
ID,
DATE,
NUMBER
}

View File

@@ -0,0 +1,24 @@
package com.tencent.supersonic.headless.common.server.pojo;
import com.google.common.collect.Lists;
import lombok.Data;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@Data
public class AppConfig {
private List<Item> items = Lists.newArrayList();
public Set<Item> getAllItems() {
Set<Item> itemSet = new HashSet<>();
for (Item item : items) {
itemSet.add(item);
itemSet.addAll(item.getRelateItems());
}
return itemSet;
}
}

View File

@@ -0,0 +1,70 @@
package com.tencent.supersonic.headless.common.server.pojo;
import com.tencent.supersonic.common.pojo.Constants;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Dim {
private String name;
private String type;
private String expr;
private String dateFormat = Constants.DAY_FORMAT;
private DimensionTimeTypeParams typeParams;
private Integer isCreateDimension = 0;
private String bizName;
private String description;
private int isTag;
public Dim(String name, String bizName, String type, Integer isCreateDimension) {
this.name = name;
this.type = type;
this.isCreateDimension = isCreateDimension;
this.bizName = bizName;
}
public Dim(String name, String bizName, String type, Integer isCreateDimension, int isTag) {
this.name = name;
this.type = type;
this.isCreateDimension = isCreateDimension;
this.bizName = bizName;
this.isTag = isTag;
}
public Dim(String name, String type, String expr, String dateFormat, DimensionTimeTypeParams typeParams,
Integer isCreateDimension, String bizName) {
this.name = name;
this.type = type;
this.expr = expr;
this.dateFormat = dateFormat;
this.typeParams = typeParams;
this.isCreateDimension = isCreateDimension;
this.bizName = bizName;
}
public static Dim getDefault() {
return new Dim("日期", "time", "2023-05-28",
Constants.DAY_FORMAT,
new DimensionTimeTypeParams("true", "day"),
0, "imp_date"
);
}
public String getFieldName() {
return bizName;
}
}

View File

@@ -0,0 +1,25 @@
package com.tencent.supersonic.headless.common.server.pojo;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class DimValueMap {
/**
* dimension value in db
*/
private String techName;
/**
* dimension value for result show
*/
private String bizName;
/**
* dimension value for user query
*/
private List<String> alias = new ArrayList<>();
}

View File

@@ -0,0 +1,17 @@
package com.tencent.supersonic.headless.common.server.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class DimensionTimeTypeParams {
private String isPrimary = "true";
private String timeGranularity = "day";
}

View File

@@ -0,0 +1,19 @@
package com.tencent.supersonic.headless.common.server.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class DrillDownDimension {
private Long dimensionId;
private boolean necessary;
public DrillDownDimension(Long dimensionId) {
this.dimensionId = dimensionId;
}
}

View File

@@ -0,0 +1,26 @@
package com.tencent.supersonic.headless.common.server.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.util.List;
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class Entity {
/**
* uniquely identifies an entity
*/
private Long entityId;
/**
* entity name list
*/
private List<String> names;
}

View File

@@ -0,0 +1,35 @@
package com.tencent.supersonic.headless.common.server.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Identify {
private String name;
/**
* primary, foreign
*/
private String type;
private String bizName;
private List<String> entityNames;
public Identify(String name, String type, String bizName) {
this.name = name;
this.type = type;
this.bizName = bizName;
}
public String getFieldName() {
return bizName;
}
}

View File

@@ -0,0 +1,27 @@
package com.tencent.supersonic.headless.common.server.pojo;
import com.google.common.collect.Lists;
import com.tencent.supersonic.common.pojo.enums.ApiItemType;
import lombok.Data;
import java.util.List;
@Data
public class Item {
private Long id;
private String name;
private String bizName;
private String createdBy;
private ApiItemType type;
private List<Item> relateItems = Lists.newArrayList();
public String getValue() {
return name;
}
}

View File

@@ -0,0 +1,17 @@
package com.tencent.supersonic.headless.common.server.pojo;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NonNull;
import lombok.ToString;
@Data
@AllArgsConstructor
@ToString
public class ItemDateFilter {
private List<Long> itemIds;
@NonNull
private String type;
}

View File

@@ -0,0 +1,35 @@
package com.tencent.supersonic.headless.common.server.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Measure {
private String name;
private String agg;
private String expr;
private String createMetric;
private String bizName;
private Integer isCreateMetric = 0;
public Measure(String name, String bizName, String agg, Integer isCreateMetric) {
this.name = name;
this.agg = agg;
this.isCreateMetric = isCreateMetric;
this.bizName = bizName;
}
public String getFieldName() {
return expr;
}
}

View File

@@ -0,0 +1,18 @@
package com.tencent.supersonic.headless.common.server.pojo;
import com.tencent.supersonic.common.pojo.RecordInfo;
import lombok.Data;
@Data
public class MetricQueryDefaultConfig extends RecordInfo {
private Long id;
private Long metricId;
private String userName;
//string of queryStruct
private String defaultConfig;
}

View File

@@ -0,0 +1,14 @@
package com.tencent.supersonic.headless.common.server.pojo;
import java.util.List;
import com.google.common.collect.Lists;
import lombok.Data;
@Data
public class MetricTypeParams {
private List<Measure> measures = Lists.newArrayList();
private String expr;
}

View File

@@ -0,0 +1,44 @@
package com.tencent.supersonic.headless.common.server.pojo;
import com.google.common.collect.Lists;
import com.tencent.supersonic.headless.common.server.enums.DimensionType;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.stream.Collectors;
@Data
public class ModelDetail {
private String queryType;
private String sqlQuery;
private String tableQuery;
private List<Identify> identifiers;
private List<Dim> dimensions;
private List<Measure> measures;
public String getSqlQuery() {
if (StringUtils.isNotBlank(sqlQuery) && sqlQuery.endsWith(";")) {
sqlQuery = sqlQuery.substring(0, sqlQuery.length() - 1);
}
return sqlQuery;
}
public List<Dim> getTimeDims() {
if (CollectionUtils.isEmpty(dimensions)) {
return Lists.newArrayList();
}
return dimensions.stream()
.filter(dim -> DimensionType.time.name().equalsIgnoreCase(dim.getType()))
.collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,18 @@
package com.tencent.supersonic.headless.common.server.pojo;
import lombok.Data;
import lombok.ToString;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@Data
@ToString
public class QueryResult<T> implements Serializable {
private int pageNo = -1;
private int pageSize = -1;
private long totalCount = -1;
private List<T> resultList = new ArrayList<T>();
}

View File

@@ -0,0 +1,225 @@
package com.tencent.supersonic.headless.common.server.pojo;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import java.util.List;
import java.util.Objects;
import lombok.Data;
import lombok.ToString;
@Data
@ToString
public class QueryStat {
private Long id;
private String traceId;
private Long modelId;
private String user;
private String createdAt;
/**
* corresponding type, such as sql, struct, etc
*/
private String queryType;
/**
* NORMAL, PRE_FLUSH
*/
private Integer queryTypeBack;
private String querySqlCmd;
private String querySqlCmdMd5;
private String queryStructCmd;
private String queryStructCmdMd5;
private String sql;
private String sqlMd5;
private String queryEngine;
private Long startTime;
private Long elapsedMs;
private String queryState;
private Boolean nativeQuery;
private String startDate;
private String endDate;
private String dimensions;
private String metrics;
private String selectCols;
private String aggCols;
private String filterCols;
private String groupByCols;
private String orderByCols;
private Boolean useResultCache;
private Boolean useSqlCache;
private String sqlCacheKey;
private String resultCacheKey;
private String queryOptMode;
public QueryStat setQueryOptMode(String queryOptMode) {
this.queryOptMode = queryOptMode;
return this;
}
public QueryStat setQuerySqlCmdMd5(String querySqlCmdMd5) {
this.querySqlCmdMd5 = querySqlCmdMd5;
return this;
}
public QueryStat setQueryStructCmdMd5(String queryStructCmdMd5) {
this.queryStructCmdMd5 = queryStructCmdMd5;
return this;
}
public QueryStat setStartTime(Long startTime) {
this.startTime = startTime;
return this;
}
public QueryStat setQueryTypeBack(Integer queryTypeBack) {
this.queryTypeBack = queryTypeBack;
return this;
}
public QueryStat setNativeQuery(Boolean nativeQuery) {
this.nativeQuery = nativeQuery;
return this;
}
public QueryStat setTraceId(String traceId) {
this.traceId = traceId;
return this;
}
public QueryStat setModelId(Long modelId) {
this.modelId = modelId;
return this;
}
public QueryStat setUser(String user) {
this.user = user;
return this;
}
public QueryStat setQueryType(String queryType) {
this.queryType = queryType;
return this;
}
public QueryStat setQuerySqlCmd(String querySqlCmd) {
this.querySqlCmd = querySqlCmd;
return this;
}
public QueryStat setQueryStructCmd(String queryStructCmd) {
this.queryStructCmd = queryStructCmd;
return this;
}
public QueryStat setSql(String sql) {
this.sql = sql;
return this;
}
public QueryStat setSqlMd5(String sqlMd5) {
this.sqlMd5 = sqlMd5;
return this;
}
public QueryStat setQueryEngine(String queryEngine) {
this.queryEngine = queryEngine;
return this;
}
public QueryStat setElapsedMs(Long elapsedMs) {
this.elapsedMs = elapsedMs;
return this;
}
public QueryStat setQueryState(String queryState) {
this.queryState = queryState;
return this;
}
public QueryStat setStartDate(String startDate) {
this.startDate = startDate;
return this;
}
public QueryStat setEndDate(String endDate) {
this.endDate = endDate;
return this;
}
public QueryStat setDimensions(String dimensions) {
this.dimensions = dimensions;
return this;
}
public QueryStat setMetrics(String metrics) {
this.metrics = metrics;
return this;
}
public QueryStat setSelectCols(String selectCols) {
this.selectCols = selectCols;
return this;
}
public QueryStat setAggCols(String aggCols) {
this.aggCols = aggCols;
return this;
}
public QueryStat setFilterCols(String filterCols) {
this.filterCols = filterCols;
return this;
}
public QueryStat setGroupByCols(String groupByCols) {
this.groupByCols = groupByCols;
return this;
}
public QueryStat setOrderByCols(String orderByCols) {
this.orderByCols = orderByCols;
return this;
}
public QueryStat setUseResultCache(Boolean useResultCache) {
this.useResultCache = useResultCache;
return this;
}
public QueryStat setUseSqlCache(Boolean useSqlCache) {
this.useSqlCache = useSqlCache;
return this;
}
public QueryStat setSqlCacheKey(String sqlCacheKey) {
this.sqlCacheKey = sqlCacheKey;
return this;
}
public QueryStat setResultCacheKey(String resultCacheKey) {
this.resultCacheKey = resultCacheKey;
return this;
}
public QueryStat setId(Long id) {
this.id = id;
return this;
}
public QueryStat setCreatedAt(String createdAt) {
this.createdAt = createdAt;
return this;
}
public List<String> getMetricListBizName() {
if (Objects.isNull(metrics)) {
return Lists.newArrayList();
}
return JSONObject.parseArray(metrics, String.class);
}
public List<String> getDimensionListBizName() {
return JSONObject.parseArray(dimensions, String.class);
}
}

View File

@@ -0,0 +1,18 @@
package com.tencent.supersonic.headless.common.server.pojo;
import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class RelateDimension {
List<DrillDownDimension> drillDownDimensions = Lists.newArrayList();
}

View File

@@ -0,0 +1,62 @@
package com.tencent.supersonic.headless.common.server.pojo;
import com.google.common.base.Objects;
import com.tencent.supersonic.common.pojo.RecordInfo;
import com.tencent.supersonic.common.pojo.enums.SensitiveLevelEnum;
import com.tencent.supersonic.common.pojo.enums.TypeEnums;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import lombok.Data;
import lombok.ToString;
import org.apache.commons.lang3.StringUtils;
@Data
@ToString(callSuper = true)
public class SchemaItem extends RecordInfo {
private static String aliasSplit = ",";
private Long id;
private String name;
private String bizName;
private String description;
private Integer status;
private TypeEnums typeEnum;
private Integer sensitiveLevel = SensitiveLevelEnum.LOW.getCode();
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (!super.equals(o)) {
return false;
}
SchemaItem that = (SchemaItem) o;
return Objects.equal(id, that.id) && Objects.equal(name, that.name)
&& Objects.equal(bizName, that.bizName) && Objects.equal(
description, that.description) && Objects.equal(status, that.status)
&& typeEnum == that.typeEnum && Objects.equal(sensitiveLevel, that.sensitiveLevel);
}
@Override
public int hashCode() {
return Objects.hashCode(super.hashCode(), id, name, bizName, description, status, typeEnum, sensitiveLevel);
}
public static List<String> getAliasList(String alias) {
if (StringUtils.isEmpty(alias)) {
return new ArrayList<>();
}
return Arrays.asList(alias.split(aliasSplit));
}
}

View File

@@ -0,0 +1,29 @@
package com.tencent.supersonic.headless.common.server.request;
import com.google.common.collect.Lists;
import com.tencent.supersonic.common.pojo.PageBaseReq;
import com.tencent.supersonic.headless.common.server.enums.AppStatus;
import lombok.Data;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.stream.Collectors;
@Data
public class AppQueryReq extends PageBaseReq {
private String name;
private List<AppStatus> appStatus;
private String createdBy;
public List<Integer> getStatus() {
if (CollectionUtils.isEmpty(appStatus)) {
return Lists.newArrayList();
}
return appStatus.stream().map(AppStatus::getCode).collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,35 @@
package com.tencent.supersonic.headless.common.server.request;
import com.tencent.supersonic.common.pojo.RecordInfo;
import com.tencent.supersonic.headless.common.server.pojo.AppConfig;
import lombok.Data;
import org.springframework.util.CollectionUtils;
import java.util.Date;
import java.util.List;
@Data
public class AppReq extends RecordInfo {
private Long id;
private String name;
private String description;
private AppConfig config;
private Date endDate;
private Integer qps;
private List<String> owners;
public String getOwner() {
if (CollectionUtils.isEmpty(owners)) {
return "";
}
return String.join(",", owners);
}
}

View File

@@ -0,0 +1,55 @@
package com.tencent.supersonic.headless.common.server.request;
import com.google.common.collect.Lists;
import com.tencent.supersonic.headless.common.server.enums.DataType;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import java.util.List;
@Data
public class DatabaseReq {
private Long id;
private String name;
private String type;
private String host;
private String port;
private String username;
private String password;
private String database;
private String version;
private String description;
private String url;
private List<String> admins = Lists.newArrayList();
private List<String> viewers = Lists.newArrayList();
public String getConnectUrl() {
if (StringUtils.isNotBlank(url)) {
return url;
}
String databaseUrl = database;
if (StringUtils.isBlank(databaseUrl)) {
databaseUrl = "";
} else {
databaseUrl = "/" + database;
}
if (type.equalsIgnoreCase(DataType.MYSQL.getFeature())) {
return String.format("jdbc:%s://%s:%s%s?sessionVariables=sql_mode='IGNORE_SPACE'&allowMultiQueries=true",
type, host, port, databaseUrl);
}
return String.format("jdbc:%s://%s:%s%s", type, host, port, databaseUrl);
}
}

View File

@@ -0,0 +1,42 @@
package com.tencent.supersonic.headless.common.server.request;
import java.util.ArrayList;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class DateInfoReq {
private String type;
private Long itemId;
private String dateFormat;
private String startDate;
private String endDate;
private String datePeriod;
private List<String> unavailableDateList = new ArrayList<>();
public DateInfoReq(String type, Long itemId, String dateFormat, String startDate, String endDate) {
this.type = type;
this.itemId = itemId;
this.dateFormat = dateFormat;
this.startDate = startDate;
this.endDate = endDate;
}
public DateInfoReq(String type, Long itemId, String dateFormat, String startDate, String endDate,
List<String> unavailableDateList) {
this.type = type;
this.itemId = itemId;
this.dateFormat = dateFormat;
this.startDate = startDate;
this.endDate = endDate;
this.unavailableDateList = unavailableDateList;
}
}

View File

@@ -0,0 +1,33 @@
package com.tencent.supersonic.headless.common.server.request;
import com.tencent.supersonic.common.pojo.enums.DataTypeEnums;
import com.tencent.supersonic.headless.common.server.pojo.DimValueMap;
import com.tencent.supersonic.headless.common.server.pojo.SchemaItem;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.util.List;
@Data
public class DimensionReq extends SchemaItem {
private Long modelId;
private String type;
@NotNull(message = "expr can not be null")
private String expr;
//DATE ID CATEGORY
private String semanticType = "CATEGORY";
private String alias;
private List<String> defaultValues;
private List<DimValueMap> dimValueMaps;
private DataTypeEnums dataType;
private int isTag;
}

View File

@@ -0,0 +1,42 @@
package com.tencent.supersonic.headless.common.server.request;
import com.tencent.supersonic.headless.common.server.pojo.SchemaItem;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class DomainReq extends SchemaItem {
private Long parentId = 0L;
private Integer isOpen = 0;
private List<String> viewers = new ArrayList<>();
private List<String> viewOrgs = new ArrayList<>();
private List<String> admins = new ArrayList<>();
private List<String> adminOrgs = new ArrayList<>();
public String getViewer() {
return String.join(",", viewers);
}
public String getViewOrg() {
return String.join(",", viewOrgs);
}
public String getAdmin() {
return String.join(",", admins);
}
public String getAdminOrg() {
return String.join(",", adminOrgs);
}
}

View File

@@ -0,0 +1,12 @@
package com.tencent.supersonic.headless.common.server.request;
import lombok.Data;
@Data
public class DomainUpdateReq extends DomainReq {
private Long id;
}

View File

@@ -0,0 +1,13 @@
package com.tencent.supersonic.headless.common.server.request;
import lombok.Data;
import java.util.List;
@Data
public class MetaBatchReq {
private List<Long> ids;
private Integer status;
}

View File

@@ -0,0 +1,39 @@
package com.tencent.supersonic.headless.common.server.request;
import com.tencent.supersonic.common.pojo.DataFormat;
import com.tencent.supersonic.headless.common.server.pojo.RelateDimension;
import com.tencent.supersonic.headless.common.server.pojo.SchemaItem;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Data
public class MetricBaseReq extends SchemaItem {
private Long modelId;
private String alias;
private String dataFormatType;
private DataFormat dataFormat;
private List<String> tags;
private RelateDimension relateDimension;
private Map<String, Object> ext = new HashMap<>();
public String getTag() {
if (CollectionUtils.isEmpty(tags)) {
return "";
}
return StringUtils.join(tags, ",");
}
}

View File

@@ -0,0 +1,30 @@
package com.tencent.supersonic.headless.common.server.request;
import com.tencent.supersonic.headless.common.server.enums.MetricType;
import com.tencent.supersonic.headless.common.server.pojo.Measure;
import com.tencent.supersonic.headless.common.server.pojo.MetricTypeParams;
import lombok.Data;
import java.util.List;
@Data
public class MetricReq extends MetricBaseReq {
private MetricType metricType;
private MetricTypeParams typeParams;
public MetricType getMetricType() {
if (metricType != null) {
return metricType;
}
List<Measure> measureList = typeParams.getMeasures();
if (measureList.size() == 1 && typeParams.getExpr().trim().equalsIgnoreCase(measureList.get(0).getBizName())) {
return MetricType.ATOMIC;
} else if (measureList.size() >= 1) {
return MetricType.DERIVED;
}
throw new RuntimeException("measure can not be none");
}
}

View File

@@ -0,0 +1,76 @@
package com.tencent.supersonic.headless.common.server.request;
import com.google.common.collect.Lists;
import com.tencent.supersonic.headless.common.server.pojo.Dim;
import com.tencent.supersonic.headless.common.server.pojo.DrillDownDimension;
import com.tencent.supersonic.headless.common.server.pojo.ModelDetail;
import com.tencent.supersonic.headless.common.server.pojo.SchemaItem;
import lombok.Data;
import java.util.List;
@Data
public class ModelReq extends SchemaItem {
private Long databaseId;
private Long domainId;
private String filterSql;
private Integer isOpen;
private List<DrillDownDimension> drillDownDimensions;
private String alias;
private String sourceType;
private ModelDetail modelDetail;
private List<String> viewers;
private List<String> viewOrgs;
private List<String> admins;
private List<String> adminOrgs;
public List<Dim> getTimeDimension() {
if (modelDetail == null) {
return Lists.newArrayList();
}
return modelDetail.getTimeDims();
}
public String getViewer() {
if (viewers == null) {
return null;
}
return String.join(",", viewers);
}
public String getViewOrg() {
if (viewOrgs == null) {
return null;
}
return String.join(",", viewOrgs);
}
public String getAdmin() {
if (admins == null) {
return null;
}
return String.join(",", admins);
}
public String getAdminOrg() {
if (adminOrgs == null) {
return null;
}
return String.join(",", adminOrgs);
}
}

View File

@@ -0,0 +1,14 @@
package com.tencent.supersonic.headless.common.server.request;
import lombok.Data;
import java.util.List;
@Data
public class ModelSchemaFilterReq {
/**
* if domainIds is empty, get all domain info
*/
private List<Long> modelIds;
}

View File

@@ -0,0 +1,12 @@
package com.tencent.supersonic.headless.common.server.request;
import lombok.Data;
@Data
public class PageDimensionReq extends PageSchemaItemReq {
private Integer isTag;
}

View File

@@ -0,0 +1,12 @@
package com.tencent.supersonic.headless.common.server.request;
import lombok.Data;
@Data
public class PageMetricReq extends PageSchemaItemReq {
private String type;
}

View File

@@ -0,0 +1,21 @@
package com.tencent.supersonic.headless.common.server.request;
import com.google.common.collect.Lists;
import com.tencent.supersonic.common.pojo.PageBaseReq;
import lombok.Data;
import java.util.List;
@Data
public class PageSchemaItemReq extends PageBaseReq {
private String id;
private String name;
private String bizName;
private String createdBy;
private List<Long> domainIds = Lists.newArrayList();
private List<Long> modelIds = Lists.newArrayList();
private Integer sensitiveLevel;
private Integer status;
private String key;
private List<Long> ids;
}

View File

@@ -0,0 +1,26 @@
package com.tencent.supersonic.headless.common.server.request;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
@Data
public class SqlExecuteReq {
public static final String LIMIT_WRAPPER = " select * from ( %s ) a limit 1000 ";
@NotNull(message = "modelId can not be null")
private Long id;
@NotBlank(message = "sql can not be blank")
private String sql;
public String getSql() {
if (StringUtils.isNotBlank(sql) && sql.endsWith(";")) {
sql = sql.substring(0, sql.length() - 1);
}
return String.format(LIMIT_WRAPPER, sql);
}
}

View File

@@ -0,0 +1,25 @@
package com.tencent.supersonic.headless.common.server.request;
import java.util.Date;
import lombok.Data;
@Data
public class ViewInfoReq {
private Long id;
private Long domainId;
private String type;
private Date createdAt;
private String createdBy;
private Date updatedAt;
private String updatedBy;
private String config;
}

View File

@@ -0,0 +1,10 @@
package com.tencent.supersonic.headless.common.server.response;
import lombok.Data;
@Data
public class AppDetailResp extends AppResp {
private String appSecret;
}

View File

@@ -0,0 +1,44 @@
package com.tencent.supersonic.headless.common.server.response;
import com.google.common.collect.Lists;
import com.tencent.supersonic.common.pojo.RecordInfo;
import com.tencent.supersonic.headless.common.server.enums.AppStatus;
import com.tencent.supersonic.headless.common.server.pojo.AppConfig;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
@Data
public class AppResp extends RecordInfo {
private Integer id;
private String name;
private String description;
private AppStatus appStatus;
private AppConfig config;
private Date endDate;
private Integer qps;
private List<String> owners;
private boolean hasAdminRes;
public void setOwner(String owner) {
if (StringUtils.isBlank(owner)) {
owners = Lists.newArrayList();
return;
}
owners = Arrays.asList(owner.split(","));
}
}

View File

@@ -0,0 +1,67 @@
package com.tencent.supersonic.headless.common.server.response;
import com.google.common.collect.Lists;
import com.tencent.supersonic.common.pojo.RecordInfo;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class DatabaseResp extends RecordInfo {
private Long id;
private String name;
private String description;
private List<String> admins = Lists.newArrayList();
private List<String> viewers = Lists.newArrayList();
private String type;
private String url;
private String username;
private String password;
private String database;
private String version;
private boolean hasPermission = false;
private boolean hasUsePermission = false;
private boolean hasEditPermission = false;
public String getHost() {
Pattern p = Pattern.compile("jdbc:(?<db>\\w+):.*((//)|@)(?<host>.+):(?<port>\\d+).*");
Matcher m = p.matcher(url);
if (m.find()) {
return m.group("host");
}
return "";
}
public String getPort() {
Pattern p = Pattern.compile("jdbc:(?<db>\\w+):.*((//)|@)(?<host>.+):(?<port>\\d+).*");
Matcher m = p.matcher(url);
if (m.find()) {
return m.group("port");
}
return "";
}
}

View File

@@ -0,0 +1,16 @@
package com.tencent.supersonic.headless.common.server.response;
import lombok.Data;
import lombok.ToString;
import java.util.List;
@Data
@ToString(callSuper = true)
public class DimSchemaResp extends DimensionResp {
private Long useCnt = 0L;
private List<String> entityAlias;
}

View File

@@ -0,0 +1,43 @@
package com.tencent.supersonic.headless.common.server.response;
import com.tencent.supersonic.common.pojo.enums.DataTypeEnums;
import com.tencent.supersonic.headless.common.server.pojo.DimValueMap;
import com.tencent.supersonic.headless.common.server.pojo.SchemaItem;
import lombok.Data;
import lombok.ToString;
import java.util.List;
@Data
@ToString(callSuper = true)
public class DimensionResp extends SchemaItem {
private Long modelId;
private String type;
private String expr;
private String fullPath;
private String modelName;
private String modelBizName;
private String modelFilterSql;
//DATE ID CATEGORY
private String semanticType;
private String alias;
private List<String> defaultValues;
private List<DimValueMap> dimValueMaps;
private DataTypeEnums dataType;
private int isTag;
}

View File

@@ -0,0 +1,56 @@
package com.tencent.supersonic.headless.common.server.response;
import com.tencent.supersonic.headless.common.server.pojo.Entity;
import com.tencent.supersonic.headless.common.server.pojo.SchemaItem;
import lombok.Data;
import lombok.ToString;
import java.util.List;
import java.util.Objects;
@Data
@ToString
public class DomainResp extends SchemaItem {
private Long parentId;
private String fullPath;
private List<String> viewers;
private List<String> viewOrgs;
private List<String> admins;
private List<String> adminOrgs;
private Integer isOpen = 0;
private Integer dimensionCnt;
private Integer metricCnt;
private Entity entity;
private boolean hasEditPermission = false;
@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) {
return false;
}
DomainResp that = (DomainResp) o;
if (getId() == null || that.getId() == null) {
return false;
}
return Objects.equals(getId().intValue(), that.getId().intValue());
}
@Override
public int hashCode() {
if (getId() == null) {
return 0;
}
return getId().hashCode();
}
}

View File

@@ -0,0 +1,20 @@
package com.tencent.supersonic.headless.common.server.response;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.io.Serializable;
@Data
@ToString
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ExplainResp implements Serializable {
private String sql;
}

View File

@@ -0,0 +1,30 @@
package com.tencent.supersonic.headless.common.server.response;
import lombok.Data;
@Data
public class MeasureResp {
private String name;
//sum max min avg count distinct
private String agg;
private String expr;
private String constraint;
private String alias;
private Long datasourceId;
private String datasourceName;
private String datasourceBizName;
private String bizName;
private Long modelId;
}

View File

@@ -0,0 +1,91 @@
package com.tencent.supersonic.headless.common.server.response;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.tencent.supersonic.common.pojo.DataFormat;
import com.tencent.supersonic.headless.common.server.pojo.DrillDownDimension;
import com.tencent.supersonic.headless.common.server.pojo.Measure;
import com.tencent.supersonic.headless.common.server.pojo.MetricTypeParams;
import com.tencent.supersonic.headless.common.server.pojo.RelateDimension;
import com.tencent.supersonic.headless.common.server.pojo.SchemaItem;
import lombok.Data;
import lombok.ToString;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@Data
@ToString(callSuper = true)
public class MetricResp extends SchemaItem {
private Long modelId;
private Long domainId;
private String modelName;
//ATOMIC DERIVED
private String type;
private MetricTypeParams typeParams;
private String dataFormatType;
private DataFormat dataFormat;
private String alias;
private List<String> tags;
private RelateDimension relateDimension;
private boolean hasAdminRes = false;
private Boolean isCollect;
private Map<String, Object> ext = new HashMap<>();
public void setTag(String tag) {
if (StringUtils.isBlank(tag)) {
tags = Lists.newArrayList();
} else {
tags = Arrays.asList(tag.split(","));
}
}
public Set<Long> getNecessaryDimensionIds() {
if (relateDimension == null || CollectionUtils.isEmpty(relateDimension.getDrillDownDimensions())) {
return Sets.newHashSet();
}
return relateDimension.getDrillDownDimensions().stream().filter(DrillDownDimension::isNecessary)
.map(DrillDownDimension::getDimensionId).collect(Collectors.toSet());
}
public String getRelaDimensionIdKey() {
if (relateDimension == null || CollectionUtils.isEmpty(relateDimension.getDrillDownDimensions())) {
return "";
}
return relateDimension.getDrillDownDimensions().stream()
.map(DrillDownDimension::getDimensionId)
.map(String::valueOf)
.collect(Collectors.joining(","));
}
public List<Measure> getMeasures() {
if (typeParams == null) {
return Lists.newArrayList();
}
return typeParams.getMeasures();
}
public String getDefaultAgg() {
return typeParams.getMeasures().get(0).getAgg();
}
}

View File

@@ -0,0 +1,12 @@
package com.tencent.supersonic.headless.common.server.response;
import lombok.Data;
import lombok.ToString;
@Data
@ToString(callSuper = true)
public class MetricSchemaResp extends MetricResp {
private Long useCnt = 0L;
}

View File

@@ -0,0 +1,72 @@
package com.tencent.supersonic.headless.common.server.response;
import com.google.common.collect.Lists;
import com.tencent.supersonic.headless.common.server.pojo.Dim;
import com.tencent.supersonic.headless.common.server.pojo.DrillDownDimension;
import com.tencent.supersonic.headless.common.server.pojo.Identify;
import com.tencent.supersonic.headless.common.server.pojo.ModelDetail;
import com.tencent.supersonic.headless.common.server.pojo.SchemaItem;
import lombok.Data;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
@Data
public class ModelResp extends SchemaItem {
private Long domainId;
private Long databaseId;
private ModelDetail modelDetail;
private String depends;
private String sourceType;
private String filterSql;
private List<String> viewers = new ArrayList<>();
private List<String> viewOrgs = new ArrayList<>();
private List<String> admins = new ArrayList<>();
private List<String> adminOrgs = new ArrayList<>();
private Integer isOpen;
private List<DrillDownDimension> drillDownDimensions;
private String alias;
private String fullPath;
public boolean openToAll() {
return isOpen != null && isOpen == 1;
}
public Identify getPrimaryIdentify() {
if (modelDetail == null) {
return null;
}
if (CollectionUtils.isEmpty(modelDetail.getIdentifiers())) {
return null;
}
for (Identify identify : modelDetail.getIdentifiers()) {
if (!CollectionUtils.isEmpty(identify.getEntityNames())) {
return identify;
}
}
return null;
}
public List<Dim> getTimeDimension() {
if (modelDetail == null) {
return Lists.newArrayList();
}
return modelDetail.getTimeDims();
}
}

View File

@@ -0,0 +1,19 @@
package com.tencent.supersonic.headless.common.server.response;
import lombok.Data;
import java.util.List;
@Data
public class ModelSchemaRelaResp {
private Long domainId;
private ModelResp model;
private List<MetricResp> metrics;
private List<DimensionResp> dimensions;
}

View File

@@ -0,0 +1,34 @@
package com.tencent.supersonic.headless.common.server.response;
import com.tencent.supersonic.common.pojo.ModelRela;
import com.tencent.supersonic.headless.common.server.pojo.Identify;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ModelSchemaResp extends ModelResp {
private List<MetricSchemaResp> metrics;
private List<DimSchemaResp> dimensions;
private List<ModelRela> modelRelas;
public DimSchemaResp getPrimaryKey() {
Identify identify = getPrimaryIdentify();
if (identify == null) {
return null;
}
for (DimSchemaResp dimension : dimensions) {
if (identify.getBizName().equals(dimension.getBizName())) {
dimension.setEntityAlias(identify.getEntityNames());
return dimension;
}
}
return null;
}
}

View File

@@ -0,0 +1,34 @@
package com.tencent.supersonic.headless.common.server.response;
import com.google.common.collect.Lists;
import com.tencent.supersonic.common.pojo.QueryAuthorization;
import com.tencent.supersonic.common.pojo.QueryColumn;
import com.tencent.supersonic.headless.common.server.enums.SemanticType;
import com.tencent.supersonic.headless.common.server.pojo.QueryResult;
import lombok.Data;
import lombok.ToString;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Data
@ToString
public class QueryResultWithSchemaResp extends QueryResult<Map<String, Object>> {
List<QueryColumn> columns = Lists.newArrayList();
String sql;
QueryAuthorization queryAuthorization;
public List<QueryColumn> getMetricColumns() {
return columns.stream()
.filter(queryColumn -> SemanticType.NUMBER.name().equals(queryColumn.getShowType()))
.collect(Collectors.toList());
}
public List<QueryColumn> getDimensionColumns() {
return columns.stream()
.filter(queryColumn -> !SemanticType.NUMBER.name().equals(queryColumn.getShowType()))
.collect(Collectors.toList());
}
}