Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/javadoc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ target/
### VS Code ###
.vscode/

.DS_Store
1 change: 1 addition & 0 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
java temurin-21.0.4+7.0.LTS
2 changes: 1 addition & 1 deletion juery-api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>fr.ght1pc9kc</groupId>
<artifactId>juery</artifactId>
<version>1.4.5-SNAPSHOT</version>
<version>1.5.0-SNAPSHOT</version>
</parent>

<artifactId>juery-api</artifactId>
Expand Down
2 changes: 1 addition & 1 deletion juery-basic/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>fr.ght1pc9kc</groupId>
<artifactId>juery</artifactId>
<version>1.4.5-SNAPSHOT</version>
<version>1.5.0-SNAPSHOT</version>
</parent>

<artifactId>juery-basic</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,19 @@ public interface QueryStringParser {
*/
PageRequest parse(String queryString);

/**
* Parse a Record form data object into {@link PageRequest}.
* <p>The record must contain only fields allow by the {@link ParserConfiguration}.
* Others fields are considered as property filters.</p>
* <p>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}</p>
* <p>All accessibles methods without argument are considerate except {@link Record#toString()} and {@link Record#hashCode()} ()}</p>
*
* @param form The form data as a record object
* @return The PageRequest
*/
<R extends Record> PageRequest parse(R form);

/**
* Parse querystring parameter into a {@link Criteria}.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<String> EXCLUDED_FILTER_PARAMETERS = Set.of("equals", "toString", "hashCode", "getClass");

private final ParserConfiguration config;

Expand All @@ -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);
Expand Down Expand Up @@ -106,6 +113,31 @@ public PageRequest parse(String queryString) {
return parse(queryStringToMap(queryString));
}

public <R extends Record> PageRequest parse(R form) {
Map<String, List<String>> 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<String> paramValue) {
if (paramValue == null || paramValue.isEmpty()) {
Expand All @@ -121,44 +153,43 @@ public Criteria parseCriterionParameter(String key, List<String> paramValue) {
return Criteria.property(key).in(filteredListValues);
}

String strValue = paramValue.get(0);
String strValue = paramValue.getFirst();

// Parse operation
BiFunction<CriterionProperty, Object, Criteria> 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);
}
Expand All @@ -168,12 +199,12 @@ public Criteria parseCriterionParameter(String key, List<String> paramValue) {

private Pagination parsePaginationByPage(Map<String, List<String>> 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());
Expand All @@ -187,18 +218,18 @@ private Pagination parsePaginationByPage(Map<String, List<String>> queryString)

private Pagination parsePaginationByOffset(Map<String, List<String>> 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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ void should_create_default_configuration() {
Assertions.assertThat(actual.excludeFilterParameters()).isEqualTo(Set.of(
"_p", "_pp", "_s", "_from", "_to"
));

}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -205,4 +207,42 @@ private static Stream<Arguments> should_format_page_request_to_query_string() {
public static Set<String> 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<String> _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"))
));
}
}
}
2 changes: 1 addition & 1 deletion juery-jooq/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>fr.ght1pc9kc</groupId>
<artifactId>juery</artifactId>
<version>1.4.5-SNAPSHOT</version>
<version>1.5.0-SNAPSHOT</version>
</parent>

<artifactId>juery-jooq</artifactId>
Expand Down
2 changes: 1 addition & 1 deletion juery-mongo/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>fr.ght1pc9kc</groupId>
<artifactId>juery</artifactId>
<version>1.4.5-SNAPSHOT</version>
<version>1.5.0-SNAPSHOT</version>
</parent>

<artifactId>juery-mongo</artifactId>
Expand Down
6 changes: 3 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<groupId>fr.ght1pc9kc</groupId>
<artifactId>juery</artifactId>
<version>1.4.5-SNAPSHOT</version>
<version>1.5.0-SNAPSHOT</version>
<packaging>pom</packaging>

<name>Juery</name>
Expand Down Expand Up @@ -35,8 +35,8 @@

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>

<lombok.version>1.18.38</lombok.version>
<jetbrains.version>26.0.2</jetbrains.version>
Expand Down