Build safe chart aggregation query models
This commit is contained in:
@@ -0,0 +1,37 @@
|
|||||||
|
package com.ruoyi.generator.domain.front.dto.block;
|
||||||
|
|
||||||
|
public class ChartQueryParameter
|
||||||
|
{
|
||||||
|
private String name;
|
||||||
|
private Object value;
|
||||||
|
|
||||||
|
public ChartQueryParameter()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChartQueryParameter(String name, Object value)
|
||||||
|
{
|
||||||
|
this.name = name;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName()
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name)
|
||||||
|
{
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getValue()
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(Object value)
|
||||||
|
{
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package com.ruoyi.generator.domain.front.dto.block;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ChartQueryRenderModel
|
||||||
|
{
|
||||||
|
private String tableName;
|
||||||
|
private String dimensionSql;
|
||||||
|
private String metricSelectSql;
|
||||||
|
private String whereSql;
|
||||||
|
private String groupBySql;
|
||||||
|
private String orderBySql;
|
||||||
|
private Integer limit;
|
||||||
|
private List<ChartQueryParameter> parameters;
|
||||||
|
private List<ChartMetricConfig> metrics;
|
||||||
|
private ChartDateRangeConfig dateRange;
|
||||||
|
private boolean metricOnly;
|
||||||
|
|
||||||
|
public String getTableName() { return tableName; }
|
||||||
|
public void setTableName(String tableName) { this.tableName = tableName; }
|
||||||
|
public String getDimensionSql() { return dimensionSql; }
|
||||||
|
public void setDimensionSql(String dimensionSql) { this.dimensionSql = dimensionSql; }
|
||||||
|
public String getMetricSelectSql() { return metricSelectSql; }
|
||||||
|
public void setMetricSelectSql(String metricSelectSql) { this.metricSelectSql = metricSelectSql; }
|
||||||
|
public String getWhereSql() { return whereSql; }
|
||||||
|
public void setWhereSql(String whereSql) { this.whereSql = whereSql; }
|
||||||
|
public String getGroupBySql() { return groupBySql; }
|
||||||
|
public void setGroupBySql(String groupBySql) { this.groupBySql = groupBySql; }
|
||||||
|
public String getOrderBySql() { return orderBySql; }
|
||||||
|
public void setOrderBySql(String orderBySql) { this.orderBySql = orderBySql; }
|
||||||
|
public Integer getLimit() { return limit; }
|
||||||
|
public void setLimit(Integer limit) { this.limit = limit; }
|
||||||
|
public List<ChartQueryParameter> getParameters() { return parameters; }
|
||||||
|
public void setParameters(List<ChartQueryParameter> parameters) { this.parameters = parameters; }
|
||||||
|
public List<ChartMetricConfig> getMetrics() { return metrics; }
|
||||||
|
public void setMetrics(List<ChartMetricConfig> metrics) { this.metrics = metrics; }
|
||||||
|
public ChartDateRangeConfig getDateRange() { return dateRange; }
|
||||||
|
public void setDateRange(ChartDateRangeConfig dateRange) { this.dateRange = dateRange; }
|
||||||
|
public boolean isMetricOnly() { return metricOnly; }
|
||||||
|
public void setMetricOnly(boolean metricOnly) { this.metricOnly = metricOnly; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,243 @@
|
|||||||
|
package com.ruoyi.generator.util;
|
||||||
|
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import com.ruoyi.common.exception.ServiceException;
|
||||||
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
import com.ruoyi.generator.domain.GenTable;
|
||||||
|
import com.ruoyi.generator.domain.GenTableColumn;
|
||||||
|
import com.ruoyi.generator.domain.front.dto.block.BusinessBlockDefinition;
|
||||||
|
import com.ruoyi.generator.domain.front.dto.block.BusinessBlockInstance;
|
||||||
|
import com.ruoyi.generator.domain.front.dto.block.ChartDatasetConfig;
|
||||||
|
import com.ruoyi.generator.domain.front.dto.block.ChartDateRangeConfig;
|
||||||
|
import com.ruoyi.generator.domain.front.dto.block.ChartDimensionConfig;
|
||||||
|
import com.ruoyi.generator.domain.front.dto.block.ChartFilterConfig;
|
||||||
|
import com.ruoyi.generator.domain.front.dto.block.ChartMetricConfig;
|
||||||
|
import com.ruoyi.generator.domain.front.dto.block.ChartQueryParameter;
|
||||||
|
import com.ruoyi.generator.domain.front.dto.block.ChartQueryRenderModel;
|
||||||
|
import com.ruoyi.generator.domain.front.dto.block.ChartSortConfig;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class ChartBlockRenderSupport
|
||||||
|
{
|
||||||
|
public ChartQueryRenderModel build(GenTable table, BusinessBlockDefinition definition,
|
||||||
|
BusinessBlockInstance instance)
|
||||||
|
{
|
||||||
|
if (table == null || definition == null || instance == null || instance.getDataset() == null)
|
||||||
|
{
|
||||||
|
throw new ServiceException("图表查询配置不完整");
|
||||||
|
}
|
||||||
|
|
||||||
|
ChartDatasetConfig dataset = instance.getDataset();
|
||||||
|
boolean metricOnly = "metric".equalsIgnoreCase(StringUtils.defaultString(definition.getChartType()));
|
||||||
|
List<ChartMetricConfig> metrics = dataset.getMetrics() == null
|
||||||
|
? Collections.<ChartMetricConfig>emptyList() : dataset.getMetrics();
|
||||||
|
List<ChartQueryParameter> parameters = new ArrayList<ChartQueryParameter>();
|
||||||
|
|
||||||
|
String dimensionSql = metricOnly ? "" : dimensionSql(table, dataset.getDimension());
|
||||||
|
ChartQueryRenderModel model = new ChartQueryRenderModel();
|
||||||
|
model.setTableName(table.getTableName());
|
||||||
|
model.setDimensionSql(dimensionSql);
|
||||||
|
model.setMetricSelectSql(metricSelectSql(table, metrics));
|
||||||
|
model.setWhereSql(whereSql(table, dataset, parameters));
|
||||||
|
model.setGroupBySql(metricOnly ? "" : dimensionSql);
|
||||||
|
model.setOrderBySql(orderBySql(dataset.getSort(), metricOnly));
|
||||||
|
model.setLimit(dataset.getLimit());
|
||||||
|
model.setParameters(parameters);
|
||||||
|
model.setMetrics(metrics);
|
||||||
|
model.setDateRange(dataset.getDateRange());
|
||||||
|
model.setMetricOnly(metricOnly);
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String dimensionSql(GenTable table, ChartDimensionConfig dimension)
|
||||||
|
{
|
||||||
|
if (dimension == null)
|
||||||
|
{
|
||||||
|
throw new ServiceException("图表维度配置不存在");
|
||||||
|
}
|
||||||
|
String column = requireColumn(table, dimension.getField()).getColumnName();
|
||||||
|
String timeUnit = StringUtils.defaultString(dimension.getTimeUnit()).toLowerCase();
|
||||||
|
if ("day".equals(timeUnit))
|
||||||
|
{
|
||||||
|
return "DATE_FORMAT(t." + column + ", '%Y-%m-%d')";
|
||||||
|
}
|
||||||
|
if ("week".equals(timeUnit))
|
||||||
|
{
|
||||||
|
return "DATE_FORMAT(t." + column + ", '%x-W%v')";
|
||||||
|
}
|
||||||
|
if ("month".equals(timeUnit))
|
||||||
|
{
|
||||||
|
return "DATE_FORMAT(t." + column + ", '%Y-%m')";
|
||||||
|
}
|
||||||
|
return "t." + column;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String metricSelectSql(GenTable table, List<ChartMetricConfig> metrics)
|
||||||
|
{
|
||||||
|
List<String> selections = new ArrayList<String>();
|
||||||
|
for (ChartMetricConfig metric : metrics)
|
||||||
|
{
|
||||||
|
String aggregate = StringUtils.defaultString(metric.getAggregate()).toLowerCase();
|
||||||
|
String column = StringUtils.isEmpty(metric.getField())
|
||||||
|
? "" : requireColumn(table, metric.getField()).getColumnName();
|
||||||
|
selections.add(metricSql(column, aggregate, metric.getAlias()));
|
||||||
|
}
|
||||||
|
return join(selections, ", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
private String metricSql(String column, String aggregate, String alias)
|
||||||
|
{
|
||||||
|
if ("count".equals(aggregate))
|
||||||
|
{
|
||||||
|
return StringUtils.isEmpty(column)
|
||||||
|
? "COUNT(*) AS " + alias
|
||||||
|
: "COUNT(t." + column + ") AS " + alias;
|
||||||
|
}
|
||||||
|
if ("avg".equals(aggregate))
|
||||||
|
{
|
||||||
|
return "AVG(t." + column + ") AS " + alias;
|
||||||
|
}
|
||||||
|
return "SUM(t." + column + ") AS " + alias;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String whereSql(GenTable table, ChartDatasetConfig dataset, List<ChartQueryParameter> parameters)
|
||||||
|
{
|
||||||
|
List<String> conditions = new ArrayList<String>();
|
||||||
|
List<ChartFilterConfig> filters = dataset.getFilters() == null
|
||||||
|
? Collections.<ChartFilterConfig>emptyList() : dataset.getFilters();
|
||||||
|
for (ChartFilterConfig filter : filters)
|
||||||
|
{
|
||||||
|
String column = requireColumn(table, filter.getField()).getColumnName();
|
||||||
|
String operator = StringUtils.defaultString(filter.getOperator()).toLowerCase();
|
||||||
|
addFilterCondition(conditions, parameters, column, operator, filter.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
ChartDateRangeConfig dateRange = dataset.getDateRange();
|
||||||
|
if (dateRange != null)
|
||||||
|
{
|
||||||
|
String column = requireColumn(table, dateRange.getField()).getColumnName();
|
||||||
|
conditions.add("t." + column + " >= #{dateStart}");
|
||||||
|
conditions.add("t." + column + " < #{dateEnd}");
|
||||||
|
}
|
||||||
|
return join(conditions, " AND ");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addFilterCondition(List<String> conditions, List<ChartQueryParameter> parameters,
|
||||||
|
String column, String operator, Object value)
|
||||||
|
{
|
||||||
|
String columnSql = "t." + column;
|
||||||
|
if ("is_null".equals(operator))
|
||||||
|
{
|
||||||
|
conditions.add(columnSql + " IS NULL");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ("is_not_null".equals(operator))
|
||||||
|
{
|
||||||
|
conditions.add(columnSql + " IS NOT NULL");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ("between".equals(operator))
|
||||||
|
{
|
||||||
|
List<Object> values = values(value);
|
||||||
|
String first = addParameter(parameters, values.get(0));
|
||||||
|
String second = addParameter(parameters, values.get(1));
|
||||||
|
conditions.add(columnSql + " BETWEEN " + first + " AND " + second);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ("in".equals(operator))
|
||||||
|
{
|
||||||
|
List<String> placeholders = new ArrayList<String>();
|
||||||
|
for (Object item : values(value))
|
||||||
|
{
|
||||||
|
placeholders.add(addParameter(parameters, item));
|
||||||
|
}
|
||||||
|
conditions.add(columnSql + " IN (" + join(placeholders, ", ") + ")");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String sqlOperator = operatorSql(operator);
|
||||||
|
conditions.add(columnSql + " " + sqlOperator + " " + addParameter(parameters, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String operatorSql(String operator)
|
||||||
|
{
|
||||||
|
if ("ne".equals(operator)) return "<>";
|
||||||
|
if ("gt".equals(operator)) return ">";
|
||||||
|
if ("gte".equals(operator)) return ">=";
|
||||||
|
if ("lt".equals(operator)) return "<";
|
||||||
|
if ("lte".equals(operator)) return "<=";
|
||||||
|
return "=";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String addParameter(List<ChartQueryParameter> parameters, Object value)
|
||||||
|
{
|
||||||
|
String name = "filter" + parameters.size();
|
||||||
|
parameters.add(new ChartQueryParameter(name, value));
|
||||||
|
return "#{" + name + "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Object> values(Object value)
|
||||||
|
{
|
||||||
|
List<Object> values = new ArrayList<Object>();
|
||||||
|
if (value instanceof Collection)
|
||||||
|
{
|
||||||
|
values.addAll((Collection<?>) value);
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
if (value != null && value.getClass().isArray())
|
||||||
|
{
|
||||||
|
for (int index = 0; index < Array.getLength(value); index++)
|
||||||
|
{
|
||||||
|
values.add(Array.get(value, index));
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
values.add(value);
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String orderBySql(ChartSortConfig sort, boolean metricOnly)
|
||||||
|
{
|
||||||
|
if (sort == null)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
String by = "dimension".equals(sort.getBy()) && !metricOnly ? "dimensionKey" : sort.getBy();
|
||||||
|
return by + " " + StringUtils.defaultString(sort.getOrder()).toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
private GenTableColumn requireColumn(GenTable table, String field)
|
||||||
|
{
|
||||||
|
List<GenTableColumn> columns = table.getColumns() == null
|
||||||
|
? Collections.<GenTableColumn>emptyList() : table.getColumns();
|
||||||
|
for (GenTableColumn column : columns)
|
||||||
|
{
|
||||||
|
if (column != null && (StringUtils.equals(field, column.getColumnName())
|
||||||
|
|| StringUtils.equals(field, column.getJavaField())))
|
||||||
|
{
|
||||||
|
return column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new ServiceException("图表字段不存在:" + table.getTableName() + "."
|
||||||
|
+ StringUtils.defaultString(field));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String join(List<String> values, String delimiter)
|
||||||
|
{
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
for (String value : values)
|
||||||
|
{
|
||||||
|
if (result.length() > 0)
|
||||||
|
{
|
||||||
|
result.append(delimiter);
|
||||||
|
}
|
||||||
|
result.append(value);
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,226 @@
|
|||||||
|
package com.ruoyi.generator.util;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertSame;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import org.junit.Test;
|
||||||
|
import com.ruoyi.generator.domain.GenTable;
|
||||||
|
import com.ruoyi.generator.domain.GenTableColumn;
|
||||||
|
import com.ruoyi.generator.domain.front.dto.block.BusinessBlockDefinition;
|
||||||
|
import com.ruoyi.generator.domain.front.dto.block.BusinessBlockInstance;
|
||||||
|
import com.ruoyi.generator.domain.front.dto.block.ChartDatasetConfig;
|
||||||
|
import com.ruoyi.generator.domain.front.dto.block.ChartDateRangeConfig;
|
||||||
|
import com.ruoyi.generator.domain.front.dto.block.ChartDimensionConfig;
|
||||||
|
import com.ruoyi.generator.domain.front.dto.block.ChartFilterConfig;
|
||||||
|
import com.ruoyi.generator.domain.front.dto.block.ChartMetricConfig;
|
||||||
|
import com.ruoyi.generator.domain.front.dto.block.ChartQueryRenderModel;
|
||||||
|
import com.ruoyi.generator.domain.front.dto.block.ChartSortConfig;
|
||||||
|
|
||||||
|
public class ChartBlockRenderSupportTest
|
||||||
|
{
|
||||||
|
private final ChartBlockRenderSupport support = new ChartBlockRenderSupport();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void rendersBoundSingleTableAggregation()
|
||||||
|
{
|
||||||
|
ChartQueryRenderModel model = support.build(orderTable(), definition("line"), lineInstance());
|
||||||
|
|
||||||
|
assertEquals("shop_order", model.getTableName());
|
||||||
|
assertEquals("DATE_FORMAT(t.create_time, '%Y-%m-%d')", model.getDimensionSql());
|
||||||
|
assertEquals("DATE_FORMAT(t.create_time, '%Y-%m-%d')", model.getGroupBySql());
|
||||||
|
assertTrue(model.getMetricSelectSql().contains("SUM(t.amount) AS salesAmount"));
|
||||||
|
assertTrue(model.getWhereSql().contains("t.status = #{filter0}"));
|
||||||
|
assertEquals("filter0", model.getParameters().get(0).getName());
|
||||||
|
assertEquals("PAID", model.getParameters().get(0).getValue());
|
||||||
|
assertEquals("dimensionKey ASC", model.getOrderBySql());
|
||||||
|
assertEquals(Integer.valueOf(100), model.getLimit());
|
||||||
|
assertFalse(model.isMetricOnly());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void neverCopiesFilterValuesIntoSql()
|
||||||
|
{
|
||||||
|
BusinessBlockInstance instance = lineInstance();
|
||||||
|
instance.getDataset().getFilters().get(0).setValue("PAID' OR 1=1 --");
|
||||||
|
|
||||||
|
ChartQueryRenderModel model = support.build(orderTable(), definition("line"), instance);
|
||||||
|
|
||||||
|
assertTrue(model.getWhereSql().contains("#{filter0}"));
|
||||||
|
assertFalse(model.getWhereSql().contains("OR 1=1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void formatsDayWeekAndMonthDimensionsFromResolvedColumns()
|
||||||
|
{
|
||||||
|
assertDimension("day", "DATE_FORMAT(t.create_time, '%Y-%m-%d')");
|
||||||
|
assertDimension("week", "DATE_FORMAT(t.create_time, '%x-W%v')");
|
||||||
|
assertDimension("month", "DATE_FORMAT(t.create_time, '%Y-%m')");
|
||||||
|
assertDimension(null, "t.create_time");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void rendersCountSumAndAverageMetrics()
|
||||||
|
{
|
||||||
|
BusinessBlockInstance instance = lineInstance();
|
||||||
|
instance.getDataset().setMetrics(Arrays.asList(
|
||||||
|
metric(null, "count", "orderCount"),
|
||||||
|
metric("amount", "sum", "salesAmount"),
|
||||||
|
metric("amount", "avg", "averageAmount")));
|
||||||
|
|
||||||
|
ChartQueryRenderModel model = support.build(orderTable(), definition("line"), instance);
|
||||||
|
|
||||||
|
assertEquals("COUNT(*) AS orderCount, SUM(t.amount) AS salesAmount, AVG(t.amount) AS averageAmount",
|
||||||
|
model.getMetricSelectSql());
|
||||||
|
assertEquals(3, model.getMetrics().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void rendersBetweenInNullAndDateRangeConditionsAsBoundParameters()
|
||||||
|
{
|
||||||
|
BusinessBlockInstance instance = lineInstance();
|
||||||
|
ChartDateRangeConfig dateRange = new ChartDateRangeConfig();
|
||||||
|
dateRange.setField("createTime");
|
||||||
|
dateRange.setPreset("last_30_days");
|
||||||
|
instance.getDataset().setDateRange(dateRange);
|
||||||
|
instance.getDataset().setFilters(Arrays.asList(
|
||||||
|
filter("amount", "between", Arrays.asList(10, 20)),
|
||||||
|
filter("status", "in", new String[] { "PAID", "SHIPPED" }),
|
||||||
|
filter("createTime", "is_not_null", null),
|
||||||
|
filter("id", "is_null", null)));
|
||||||
|
|
||||||
|
ChartQueryRenderModel model = support.build(orderTable(), definition("line"), instance);
|
||||||
|
|
||||||
|
assertTrue(model.getWhereSql().contains("t.amount BETWEEN #{filter0} AND #{filter1}"));
|
||||||
|
assertTrue(model.getWhereSql().contains("t.status IN (#{filter2}, #{filter3})"));
|
||||||
|
assertTrue(model.getWhereSql().contains("t.create_time IS NOT NULL"));
|
||||||
|
assertTrue(model.getWhereSql().contains("t.id IS NULL"));
|
||||||
|
assertTrue(model.getWhereSql().contains("t.create_time >= #{dateStart}"));
|
||||||
|
assertTrue(model.getWhereSql().contains("t.create_time < #{dateEnd}"));
|
||||||
|
assertEquals(Arrays.asList(10, 20, "PAID", "SHIPPED"), Arrays.asList(
|
||||||
|
model.getParameters().get(0).getValue(),
|
||||||
|
model.getParameters().get(1).getValue(),
|
||||||
|
model.getParameters().get(2).getValue(),
|
||||||
|
model.getParameters().get(3).getValue()));
|
||||||
|
assertSame(dateRange, model.getDateRange());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void rendersMetricOnlyQueryWithoutDimensionOrGrouping()
|
||||||
|
{
|
||||||
|
BusinessBlockInstance instance = lineInstance();
|
||||||
|
instance.getDataset().setDimension(null);
|
||||||
|
instance.getDataset().setMetrics(Collections.singletonList(metric("id", "count", "orderCount")));
|
||||||
|
instance.getDataset().setSort(null);
|
||||||
|
instance.getDataset().setLimit(Integer.valueOf(1));
|
||||||
|
|
||||||
|
ChartQueryRenderModel model = support.build(orderTable(), definition("metric"), instance);
|
||||||
|
|
||||||
|
assertTrue(model.isMetricOnly());
|
||||||
|
assertEquals("", model.getDimensionSql());
|
||||||
|
assertEquals("", model.getGroupBySql());
|
||||||
|
assertEquals("", model.getOrderBySql());
|
||||||
|
assertEquals("COUNT(t.id) AS orderCount", model.getMetricSelectSql());
|
||||||
|
assertEquals(Integer.valueOf(1), model.getLimit());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void rendersMetricAliasSortAndConfiguredChartLimits()
|
||||||
|
{
|
||||||
|
BusinessBlockInstance instance = lineInstance();
|
||||||
|
instance.getDataset().getSort().setBy("salesAmount");
|
||||||
|
instance.getDataset().getSort().setOrder("desc");
|
||||||
|
instance.getDataset().setLimit(Integer.valueOf(20));
|
||||||
|
|
||||||
|
ChartQueryRenderModel model = support.build(orderTable(), definition("bar"), instance);
|
||||||
|
|
||||||
|
assertEquals("salesAmount DESC", model.getOrderBySql());
|
||||||
|
assertEquals(Integer.valueOf(20), model.getLimit());
|
||||||
|
|
||||||
|
instance.getDataset().setLimit(Integer.valueOf(10));
|
||||||
|
assertEquals(Integer.valueOf(10),
|
||||||
|
support.build(orderTable(), definition("pie"), instance).getLimit());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertDimension(String timeUnit, String expected)
|
||||||
|
{
|
||||||
|
BusinessBlockInstance instance = lineInstance();
|
||||||
|
instance.getDataset().getDimension().setField("createTime");
|
||||||
|
instance.getDataset().getDimension().setTimeUnit(timeUnit);
|
||||||
|
assertEquals(expected, support.build(orderTable(), definition("line"), instance).getDimensionSql());
|
||||||
|
}
|
||||||
|
|
||||||
|
private BusinessBlockDefinition definition(String chartType)
|
||||||
|
{
|
||||||
|
BusinessBlockDefinition definition = new BusinessBlockDefinition();
|
||||||
|
definition.setKind("chart");
|
||||||
|
definition.setChartType(chartType);
|
||||||
|
return definition;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BusinessBlockInstance lineInstance()
|
||||||
|
{
|
||||||
|
ChartDimensionConfig dimension = new ChartDimensionConfig();
|
||||||
|
dimension.setField("create_time");
|
||||||
|
dimension.setTimeUnit("day");
|
||||||
|
|
||||||
|
ChartSortConfig sort = new ChartSortConfig();
|
||||||
|
sort.setBy("dimension");
|
||||||
|
sort.setOrder("asc");
|
||||||
|
|
||||||
|
ChartDatasetConfig dataset = new ChartDatasetConfig();
|
||||||
|
dataset.setTable("shop_order");
|
||||||
|
dataset.setDimension(dimension);
|
||||||
|
dataset.setMetrics(Collections.singletonList(metric("amount", "sum", "salesAmount")));
|
||||||
|
dataset.setFilters(Collections.singletonList(filter("status", "eq", "PAID")));
|
||||||
|
dataset.setSort(sort);
|
||||||
|
dataset.setLimit(Integer.valueOf(100));
|
||||||
|
|
||||||
|
BusinessBlockInstance instance = new BusinessBlockInstance();
|
||||||
|
instance.setId("admin_line_chart_001");
|
||||||
|
instance.setDataset(dataset);
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ChartMetricConfig metric(String field, String aggregate, String alias)
|
||||||
|
{
|
||||||
|
ChartMetricConfig metric = new ChartMetricConfig();
|
||||||
|
metric.setField(field);
|
||||||
|
metric.setAggregate(aggregate);
|
||||||
|
metric.setAlias(alias);
|
||||||
|
metric.setLabel(alias);
|
||||||
|
return metric;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ChartFilterConfig filter(String field, String operator, Object value)
|
||||||
|
{
|
||||||
|
ChartFilterConfig filter = new ChartFilterConfig();
|
||||||
|
filter.setField(field);
|
||||||
|
filter.setOperator(operator);
|
||||||
|
filter.setValue(value);
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
private GenTable orderTable()
|
||||||
|
{
|
||||||
|
GenTable table = new GenTable();
|
||||||
|
table.setTableName("shop_order");
|
||||||
|
table.setColumns(Arrays.asList(
|
||||||
|
column("id", "id"),
|
||||||
|
column("amount", "amount"),
|
||||||
|
column("status", "status"),
|
||||||
|
column("create_time", "createTime")));
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
private GenTableColumn column(String columnName, String javaField)
|
||||||
|
{
|
||||||
|
GenTableColumn column = new GenTableColumn();
|
||||||
|
column.setColumnName(columnName);
|
||||||
|
column.setJavaField(javaField);
|
||||||
|
return column;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user