diff --git a/dao/src/main/java/se/fortnox/reactivewizard/db/query/parts/CollectionOptionsQueryPart.java b/dao/src/main/java/se/fortnox/reactivewizard/db/query/parts/CollectionOptionsQueryPart.java index 45afef38..7eed2b50 100644 --- a/dao/src/main/java/se/fortnox/reactivewizard/db/query/parts/CollectionOptionsQueryPart.java +++ b/dao/src/main/java/se/fortnox/reactivewizard/db/query/parts/CollectionOptionsQueryPart.java @@ -8,6 +8,10 @@ import java.lang.reflect.Method; import java.sql.SQLException; +import java.util.Arrays; +import java.util.Collections; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import static com.google.common.collect.Iterables.indexOf; import static java.util.Arrays.asList; @@ -17,6 +21,7 @@ public class CollectionOptionsQueryPart implements QueryPart { private final int collectionOptionsArgIndex; private final Query queryAnnotation; private final boolean isMono; + private static final Pattern SORT_BY_PART = Pattern.compile("(?[a-zåäö0-9_]+)(\\s+(?asc|desc))?"); public CollectionOptionsQueryPart(Method method) { collectionOptionsArgIndex = indexOf(asList(method.getParameterTypes()), CollectionOptions.class::isAssignableFrom); @@ -36,11 +41,33 @@ public void visit(StringBuilder sql, Object[] args) { if (collectionOptions != null) { if (collectionOptions.getSortBy() != null) { String sortBy = CamelSnakeConverter.camelToSnake(collectionOptions.getSortBy()); - for (String allowed : queryAnnotation.allowedSortColumns()) { - if (allowed.equals(sortBy)) { - collectionOptionsHasOrderBy = true; - addOrderBy(sql, buildOrderBy(collectionOptions.getOrder(), allowed)); - break; + var sortByParts = sortBy.split(","); + if (sortByParts.length == 1) { + for (String allowed : queryAnnotation.allowedSortColumns()) { + if (allowed.equals(sortBy)) { + collectionOptionsHasOrderBy = true; + addOrderBy(sql, buildOrderBy(collectionOptions.getOrder(), allowed)); + break; + } + } + } else { + Collections.reverse(Arrays.asList(sortByParts)); + for (var sortByPart: sortByParts) { + Matcher matcher = SORT_BY_PART.matcher(sortByPart); + if (matcher.find()) { + var sortByColumn = matcher.group("sortby"); + if (sortByColumn != null) { + var order = matcher.group("order"); + for (String allowed : queryAnnotation.allowedSortColumns()) { + if (allowed.equals(sortByColumn)) { + collectionOptionsHasOrderBy = true; + var sortOrder = order != null ? CollectionOptions.SortOrder.valueOf(order.toUpperCase()) : collectionOptions.getOrder(); + addOrderBy(sql, buildOrderBy(sortOrder, allowed)); + break; + } + } + } + } } } } diff --git a/dao/src/test/java/se/fortnox/reactivewizard/db/CollectionOptionsTest.java b/dao/src/test/java/se/fortnox/reactivewizard/db/CollectionOptionsTest.java index 789f1744..13387516 100644 --- a/dao/src/test/java/se/fortnox/reactivewizard/db/CollectionOptionsTest.java +++ b/dao/src/test/java/se/fortnox/reactivewizard/db/CollectionOptionsTest.java @@ -170,6 +170,20 @@ void shouldAddDefaultSortBeforeQueryOrderBy() throws SQLException { mockDb.verifySelect("select * from table order by text desc, id LIMIT 101"); } + @Test + void shouldAddMultipleSort() throws SQLException { + CollectionOptions collectionOptions = new CollectionOptions("name asc, id desc", CollectionOptions.SortOrder.ASC); + collectionOptionsDao.selectWithDefaultSortingAllowMultiple(collectionOptions).blockFirst(); + mockDb.verifySelect("select * from table order by name ASC, id DESC, id LIMIT 101"); + } + + @Test + void shouldFallbackToCollectionOptionSortOrderMultipleSort() throws SQLException { + CollectionOptions collectionOptions = new CollectionOptions("name, id", CollectionOptions.SortOrder.DESC); + collectionOptionsDao.selectWithDefaultSortingAllowMultiple(collectionOptions).blockFirst(); + mockDb.verifySelect("select * from table order by name DESC, id DESC, id LIMIT 101"); + } + @Test void shouldAddOrderByBeforeQueryOrderByWithoutDefaultSort() throws Exception { CollectionOptions collectionOptions = new CollectionOptions("name", CollectionOptions.SortOrder.ASC); @@ -201,6 +215,9 @@ interface CollectionOptionsDao { @Query(value = "select * from table order by id", allowedSortColumns = {"name"}) Flux selectWithDefaultSorting(CollectionOptions collectionOptions); + @Query(value = "select * from table order by id", allowedSortColumns = {"name","id"}) + Flux selectWithDefaultSortingAllowMultiple(CollectionOptions collectionOptions); + @Query(value = "select * from table order by id", allowedSortColumns = {"name"}, defaultSort = "text desc") Flux selectWithDefaultSortingInQueryAndOptions(CollectionOptions collectionOptions);