diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index c4c3258..8f741b4 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -15,10 +15,10 @@ jobs:
- uses: actions/checkout@v4.2.2
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- - name: Set up JDK 17
+ - name: Set up JDK 21
uses: actions/setup-java@v4.7.1
with:
- java-version: 17
+ java-version: 21
distribution: 'temurin'
- name: Cache Maven packages
uses: actions/cache@v4.2.3
diff --git a/.github/workflows/javadoc.yml b/.github/workflows/javadoc.yml
index 0cfcab1..f8a0f08 100644
--- a/.github/workflows/javadoc.yml
+++ b/.github/workflows/javadoc.yml
@@ -24,10 +24,10 @@ jobs:
- name: Checkout the repo
uses: actions/checkout@v4.2.2
- - name: Set up JDK 17
+ - name: Set up JDK 21
uses: actions/setup-java@v4.7.1
with:
- java-version: 17
+ java-version: 21
distribution: 'temurin'
- name: Cache Maven packages
diff --git a/.gitignore b/.gitignore
index 4a8e25e..1dadc5e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,3 +22,4 @@ target/
### VS Code ###
.vscode/
+.DS_Store
diff --git a/.tool-versions b/.tool-versions
new file mode 100644
index 0000000..0308c10
--- /dev/null
+++ b/.tool-versions
@@ -0,0 +1 @@
+java temurin-21.0.4+7.0.LTS
diff --git a/juery-api/pom.xml b/juery-api/pom.xml
index 8c8f1d8..69ba3ab 100644
--- a/juery-api/pom.xml
+++ b/juery-api/pom.xml
@@ -4,7 +4,7 @@
fr.ght1pc9kc
juery
- 1.4.5-SNAPSHOT
+ 1.5.0-SNAPSHOT
juery-api
diff --git a/juery-basic/pom.xml b/juery-basic/pom.xml
index 52e8b70..4609ffb 100644
--- a/juery-basic/pom.xml
+++ b/juery-basic/pom.xml
@@ -4,7 +4,7 @@
fr.ght1pc9kc
juery
- 1.4.5-SNAPSHOT
+ 1.5.0-SNAPSHOT
juery-basic
diff --git a/juery-basic/src/main/java/fr/ght1pc9kc/juery/basic/QueryStringParser.java b/juery-basic/src/main/java/fr/ght1pc9kc/juery/basic/QueryStringParser.java
index f823ecd..30c02f4 100644
--- a/juery-basic/src/main/java/fr/ght1pc9kc/juery/basic/QueryStringParser.java
+++ b/juery-basic/src/main/java/fr/ght1pc9kc/juery/basic/QueryStringParser.java
@@ -69,6 +69,19 @@ public interface QueryStringParser {
*/
PageRequest parse(String queryString);
+ /**
+ * Parse a Record form data object into {@link PageRequest}.
+ *
The record must contain only fields allow by the {@link ParserConfiguration}.
+ * Others fields are considered as property filters.
+ * The type of the fields must be correct according to the {@link ParserConfiguration} ({@link Integer} or {@link String}.
+ * The type for property filters are {@link String} or {@link java.util.Collection} of {@link String}
+ * All accessibles methods without argument are considerate except {@link Record#toString()} and {@link Record#hashCode()} ()}
+ *
+ * @param form The form data as a record object
+ * @return The PageRequest
+ */
+ PageRequest parse(R form);
+
/**
* Parse querystring parameter into a {@link Criteria}.
*
diff --git a/juery-basic/src/main/java/fr/ght1pc9kc/juery/basic/parser/QueryStringParserImpl.java b/juery-basic/src/main/java/fr/ght1pc9kc/juery/basic/parser/QueryStringParserImpl.java
index 6fe5d22..090054c 100644
--- a/juery-basic/src/main/java/fr/ght1pc9kc/juery/basic/parser/QueryStringParserImpl.java
+++ b/juery-basic/src/main/java/fr/ght1pc9kc/juery/basic/parser/QueryStringParserImpl.java
@@ -16,13 +16,19 @@
import fr.ght1pc9kc.juery.basic.utils.TemporalUtils;
import lombok.RequiredArgsConstructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.Set;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -40,6 +46,7 @@
public final class QueryStringParserImpl implements QueryStringParser {
private static final int PAGE_START_INDEX = 0;
private static final QueryStringFilterVisitor CRITERIA_FORMATTER = new QueryStringFilterVisitor();
+ private static final Set EXCLUDED_FILTER_PARAMETERS = Set.of("equals", "toString", "hashCode", "getClass");
private final ParserConfiguration config;
@@ -58,7 +65,7 @@ public String format(PageRequest pr) {
if (!pr.filter().isEmpty()) {
qs.append(pr.filter().accept(CRITERIA_FORMATTER));
}
- if (qs.length() == 0) {
+ if (qs.isEmpty()) {
return "";
}
var c = qs.charAt(qs.length() - 1);
@@ -106,6 +113,31 @@ public PageRequest parse(String queryString) {
return parse(queryStringToMap(queryString));
}
+ public PageRequest parse(R form) {
+ Map> map = new HashMap<>();
+ Method[] declaredMethods = form.getClass().getDeclaredMethods();
+
+ Stream.of(declaredMethods)
+ .filter(not(m -> EXCLUDED_FILTER_PARAMETERS.contains(m.getName())))
+ .filter(m -> m.getParameterCount() == 0 && !Modifier.isStatic(m.getModifiers())
+ && m.canAccess(form))
+ .forEach(m -> {
+ try {
+ Optional.ofNullable(m.invoke(form))
+ .ifPresent(v -> {
+ if (v instanceof Collection> collValue) {
+ map.put(m.getName(), collValue.stream().map(Object::toString).toList());
+ } else {
+ map.put(m.getName(), List.of(v.toString()));
+ }
+ });
+ } catch (InvocationTargetException | IllegalAccessException e) {
+ // do nothing
+ }
+ });
+ return parse(map);
+ }
+
@Override
public Criteria parseCriterionParameter(String key, List paramValue) {
if (paramValue == null || paramValue.isEmpty()) {
@@ -121,44 +153,43 @@ public Criteria parseCriterionParameter(String key, List paramValue) {
return Criteria.property(key).in(filteredListValues);
}
- String strValue = paramValue.get(0);
+ String strValue = paramValue.getFirst();
// Parse operation
BiFunction operation = CriterionProperty::eq;
- Object typedValue = null;
+ Object typedValue;
if (!StringUtils.isBlank(strValue)) {
- switch (strValue.charAt(0)) {
- case QS_START_CHAR:
+ typedValue = switch (strValue.charAt(0)) {
+ case QS_START_CHAR -> {
operation = CriterionProperty::startWith;
- typedValue = parseValueType(strValue.substring(1));
- break;
- case QS_END_CHAR:
+ yield parseValueType(strValue.substring(1));
+ }
+ case QS_END_CHAR -> {
operation = CriterionProperty::endWith;
- typedValue = parseValueType(strValue.substring(1));
- break;
- case QS_CONTAINS_CHAR:
+ yield parseValueType(strValue.substring(1));
+ }
+ case QS_CONTAINS_CHAR -> {
operation = CriterionProperty::contains;
- typedValue = strValue.substring(1);
- break;
- case QS_LT_CHAR:
+ yield strValue.substring(1);
+ }
+ case QS_LT_CHAR -> {
operation = CriterionProperty::lt;
- typedValue = parseValueType(strValue.substring(1));
- break;
- case QS_GT_CHAR:
+ yield parseValueType(strValue.substring(1));
+ }
+ case QS_GT_CHAR -> {
operation = CriterionProperty::gt;
- typedValue = parseValueType(strValue.substring(1));
- break;
- case QS_LTE_CHAR:
+ yield parseValueType(strValue.substring(1));
+ }
+ case QS_LTE_CHAR -> {
operation = CriterionProperty::lte;
- typedValue = parseValueType(strValue.substring(1));
- break;
- case QS_GTE_CHAR:
+ yield parseValueType(strValue.substring(1));
+ }
+ case QS_GTE_CHAR -> {
operation = CriterionProperty::gte;
- typedValue = parseValueType(strValue.substring(1));
- break;
- default:
- typedValue = parseValueType(strValue);
- }
+ yield parseValueType(strValue.substring(1));
+ }
+ default -> parseValueType(strValue);
+ };
} else {
typedValue = parseValueType(strValue);
}
@@ -168,12 +199,12 @@ public Criteria parseCriterionParameter(String key, List paramValue) {
private Pagination parsePaginationByPage(Map> queryString) {
int page = Optional.ofNullable(queryString.get(config.pageParameter()))
- .flatMap(l -> Optional.ofNullable(l.get(0)))
+ .flatMap(l -> Optional.ofNullable(l.getFirst()))
.map(Integer::parseInt)
.orElse(0);
int size = Optional.ofNullable(queryString.get(config.sizeParameter()))
- .flatMap(l -> Optional.ofNullable(l.get(0)))
+ .flatMap(l -> Optional.ofNullable(l.getFirst()))
.map(Integer::parseInt)
.map(i -> Math.min(i, config.maxPageSize()))
.orElse(config.maxPageSize());
@@ -187,18 +218,18 @@ private Pagination parsePaginationByPage(Map> queryString)
private Pagination parsePaginationByOffset(Map> queryString) {
int offset = Optional.ofNullable(queryString.get(config.fromParameter()))
- .flatMap(l -> Optional.ofNullable(l.get(0)))
+ .flatMap(l -> Optional.ofNullable(l.getFirst()))
.map(Integer::parseInt)
.orElse(PAGE_START_INDEX);
int maxTo = offset + config.maxPageSize() - 1;
int size = Optional.ofNullable(queryString.get(config.sizeParameter()))
- .flatMap(l -> Optional.ofNullable(l.get(0)))
+ .flatMap(l -> Optional.ofNullable(l.getFirst()))
.map(Integer::parseInt)
.map(i -> Math.min(i, config.maxPageSize()))
.orElseGet(() -> Optional.ofNullable(queryString.get(config.toParameter()))
- .flatMap(l -> Optional.ofNullable(l.get(0)))
+ .flatMap(l -> Optional.ofNullable(l.getFirst()))
.map(Integer::parseInt)
.map(i -> Math.min(i, maxTo))
.filter(i -> i > offset)
diff --git a/juery-basic/src/test/java/fr/ght1pc9kc/juery/basic/ParserConfigurationTest.java b/juery-basic/src/test/java/fr/ght1pc9kc/juery/basic/ParserConfigurationTest.java
index 763a2f9..6c1cb5c 100644
--- a/juery-basic/src/test/java/fr/ght1pc9kc/juery/basic/ParserConfigurationTest.java
+++ b/juery-basic/src/test/java/fr/ght1pc9kc/juery/basic/ParserConfigurationTest.java
@@ -18,6 +18,7 @@ void should_create_default_configuration() {
Assertions.assertThat(actual.excludeFilterParameters()).isEqualTo(Set.of(
"_p", "_pp", "_s", "_from", "_to"
));
+
}
@Test
diff --git a/juery-basic/src/test/java/fr/ght1pc9kc/juery/basic/parser/QueryStringParserImplDefaultTest.java b/juery-basic/src/test/java/fr/ght1pc9kc/juery/basic/parser/QueryStringParserImplDefaultTest.java
index b3542d0..5167c65 100644
--- a/juery-basic/src/test/java/fr/ght1pc9kc/juery/basic/parser/QueryStringParserImplDefaultTest.java
+++ b/juery-basic/src/test/java/fr/ght1pc9kc/juery/basic/parser/QueryStringParserImplDefaultTest.java
@@ -8,11 +8,13 @@
import fr.ght1pc9kc.juery.api.pagination.Sort;
import fr.ght1pc9kc.juery.basic.QueryStringParser;
import org.assertj.core.api.Assertions;
+import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.time.LocalDateTime;
+import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -205,4 +207,42 @@ private static Stream should_format_page_request_to_query_string() {
public static Set assertableQueryString(String queryString) {
return Set.of(queryString.split("&"));
}
+
+ @Test
+ void should_parse_record_as_page_request() {
+ record MyRecord(
+ Integer _p,
+ Integer _pp,
+ Integer _from,
+ Integer _to,
+ String _s,
+ Collection _id,
+ String name
+ ) {
+ }
+ {
+ MyRecord sample = new MyRecord(1, 10, null, null, "name,-email", List.of("42", "24"), "^Obiwan");
+
+ Assertions.assertThat(tested.parse(sample))
+ .isEqualTo(PageRequest.of(
+ Pagination.of(10, 10, Sort.of(
+ new Order(Direction.ASC, "name"),
+ new Order(Direction.DESC, "email"))),
+ Criteria.property("_id").in(42, 24)
+ .and(Criteria.property("name").startWith("Obiwan"))
+ ));
+ }
+ {
+ MyRecord sample = new MyRecord(null, null, 101, 151, "name,-email", List.of("42", "24"), "∋Obiwan");
+
+ Assertions.assertThat(tested.parse(sample))
+ .isEqualTo(PageRequest.of(
+ Pagination.of(101, 50, Sort.of(
+ new Order(Direction.ASC, "name"),
+ new Order(Direction.DESC, "email"))),
+ Criteria.property("_id").in(42, 24)
+ .and(Criteria.property("name").contains("Obiwan"))
+ ));
+ }
+ }
}
\ No newline at end of file
diff --git a/juery-jooq/pom.xml b/juery-jooq/pom.xml
index 921eccb..911b7f0 100644
--- a/juery-jooq/pom.xml
+++ b/juery-jooq/pom.xml
@@ -4,7 +4,7 @@
fr.ght1pc9kc
juery
- 1.4.5-SNAPSHOT
+ 1.5.0-SNAPSHOT
juery-jooq
diff --git a/juery-mongo/pom.xml b/juery-mongo/pom.xml
index 873cad8..bbebc8f 100644
--- a/juery-mongo/pom.xml
+++ b/juery-mongo/pom.xml
@@ -4,7 +4,7 @@
fr.ght1pc9kc
juery
- 1.4.5-SNAPSHOT
+ 1.5.0-SNAPSHOT
juery-mongo
diff --git a/pom.xml b/pom.xml
index 693e464..7287021 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
fr.ght1pc9kc
juery
- 1.4.5-SNAPSHOT
+ 1.5.0-SNAPSHOT
pom
Juery
@@ -35,8 +35,8 @@
UTF-8
- 17
- 17
+ 21
+ 21
1.18.38
26.0.2