Skip to content

Commit

Permalink
Merge branch 'main' into feature/2349
Browse files Browse the repository at this point in the history
  • Loading branch information
JohnNiang committed May 25, 2023
2 parents 12381ff + 533f0cf commit ec09a3d
Show file tree
Hide file tree
Showing 38 changed files with 1,082 additions and 982 deletions.
2 changes: 1 addition & 1 deletion application/build.gradle
@@ -1,5 +1,5 @@
plugins {
id 'org.springframework.boot' version '3.1.0-RC2'
id 'org.springframework.boot' version '3.1.0'
id 'io.spring.dependency-management' version '1.1.0'
id "com.gorylenko.gradle-git-properties" version "2.3.2"
id "checkstyle"
Expand Down
141 changes: 126 additions & 15 deletions application/src/main/java/run/halo/app/content/PostQuery.java
@@ -1,12 +1,24 @@
package run.halo.app.content;

import static java.util.Comparator.comparing;
import static run.halo.app.extension.router.selector.SelectorUtil.labelAndFieldSelectorToPredicate;

import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Sort;
import org.springframework.lang.Nullable;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import run.halo.app.core.extension.content.Post;
import run.halo.app.core.extension.endpoint.SortResolver;
import run.halo.app.extension.Comparators;
import run.halo.app.extension.router.IListRequest;

/**
Expand All @@ -17,8 +29,11 @@
*/
public class PostQuery extends IListRequest.QueryListRequest {

public PostQuery(MultiValueMap<String, String> queryParams) {
super(queryParams);
private final ServerWebExchange exchange;

public PostQuery(ServerRequest request) {
super(request.queryParams());
this.exchange = request.exchange();
}

@Nullable
Expand Down Expand Up @@ -57,24 +72,120 @@ public String getKeyword() {
return StringUtils.defaultIfBlank(queryParams.getFirst("keyword"), null);
}

@Schema(description = "Post collation.")
public PostSorter getSort() {
String sort = queryParams.getFirst("sort");
return PostSorter.convertFrom(sort);
}

@Schema(description = "ascending order If it is true; otherwise, it is in descending order.")
public Boolean getSortOrder() {
String sortOrder = queryParams.getFirst("sortOrder");
return convertBooleanOrNull(sortOrder);
@ArraySchema(uniqueItems = true,
arraySchema = @Schema(name = "sort",
description = "Sort property and direction of the list result. Supported fields: "
+ "creationTimestamp,publishTime"),
schema = @Schema(description = "like field,asc or field,desc",
implementation = String.class,
example = "creationTimestamp,desc"))
public Sort getSort() {
return SortResolver.defaultInstance.resolve(exchange);
}

@Nullable
private Set<String> listToSet(List<String> param) {
return param == null ? null : Set.copyOf(param);
}

private Boolean convertBooleanOrNull(String value) {
return StringUtils.isBlank(value) ? null : Boolean.parseBoolean(value);
/**
* Build a comparator from the query object.
*
* @return a comparator
*/
public Comparator<Post> toComparator() {
var sort = getSort();
var creationTimestampOrder = sort.getOrderFor("creationTimestamp");
List<Comparator<Post>> comparators = new ArrayList<>();
if (creationTimestampOrder != null) {
Comparator<Post> comparator =
comparing(post -> post.getMetadata().getCreationTimestamp());
if (creationTimestampOrder.isDescending()) {
comparator = comparator.reversed();
}
comparators.add(comparator);
}

var publishTimeOrder = sort.getOrderFor("publishTime");
if (publishTimeOrder != null) {
Comparator<Object> nullsComparator = publishTimeOrder.isAscending()
? org.springframework.util.comparator.Comparators.nullsLow()
: org.springframework.util.comparator.Comparators.nullsHigh();
Comparator<Post> comparator =
comparing(post -> post.getSpec().getPublishTime(), nullsComparator);
if (publishTimeOrder.isDescending()) {
comparator = comparator.reversed();
}
comparators.add(comparator);
}
comparators.add(Comparators.compareCreationTimestamp(false));
comparators.add(Comparators.compareName(true));
return comparators.stream()
.reduce(Comparator::thenComparing)
.orElse(null);
}

/**
* Build a predicate from the query object.
*
* @return a predicate
*/
public Predicate<Post> toPredicate() {
Predicate<Post> paramPredicate = post ->
contains(getCategories(), post.getSpec().getCategories())
&& contains(getTags(), post.getSpec().getTags())
&& contains(getContributors(), post.getStatusOrDefault().getContributors());

String keyword = getKeyword();
if (keyword != null) {
paramPredicate = paramPredicate.and(post -> {
String excerpt = post.getStatusOrDefault().getExcerpt();
return StringUtils.containsIgnoreCase(excerpt, keyword)
|| StringUtils.containsIgnoreCase(post.getSpec().getSlug(), keyword)
|| StringUtils.containsIgnoreCase(post.getSpec().getTitle(), keyword);
});
}

Post.PostPhase publishPhase = getPublishPhase();
if (publishPhase != null) {
paramPredicate = paramPredicate.and(post -> {
if (Post.PostPhase.PENDING_APPROVAL.equals(publishPhase)) {
return !post.isPublished()
&& Post.PostPhase.PENDING_APPROVAL.name()
.equalsIgnoreCase(post.getStatusOrDefault().getPhase());
}
// published
if (Post.PostPhase.PUBLISHED.equals(publishPhase)) {
return post.isPublished();
}
// draft
return !post.isPublished();
});
}

Post.VisibleEnum visible = getVisible();
if (visible != null) {
paramPredicate =
paramPredicate.and(post -> visible.equals(post.getSpec().getVisible()));
}

Predicate<Post> predicate = labelAndFieldSelectorToPredicate(getLabelSelector(),
getFieldSelector());
return predicate.and(paramPredicate);
}

boolean contains(Collection<String> left, List<String> right) {
// parameter is null, it means that ignore this condition
if (left == null) {
return true;
}
// else, it means that right is empty
if (left.isEmpty()) {
return right.isEmpty();
}
if (right == null) {
return false;
}
return right.stream().anyMatch(left::contains);
}
}
135 changes: 122 additions & 13 deletions application/src/main/java/run/halo/app/content/SinglePageQuery.java
@@ -1,13 +1,25 @@
package run.halo.app.content;

import static java.util.Comparator.comparing;
import static run.halo.app.extension.router.selector.SelectorUtil.labelAndFieldSelectorToPredicate;

import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Sort;
import org.springframework.lang.Nullable;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import run.halo.app.core.extension.content.Post;
import run.halo.app.core.extension.content.SinglePage;
import run.halo.app.core.extension.endpoint.SortResolver;
import run.halo.app.extension.Comparators;
import run.halo.app.extension.router.IListRequest;

/**
Expand All @@ -18,8 +30,11 @@
*/
public class SinglePageQuery extends IListRequest.QueryListRequest {

public SinglePageQuery(MultiValueMap<String, String> queryParams) {
super(queryParams);
private final ServerWebExchange exchange;

public SinglePageQuery(ServerRequest request) {
super(request.queryParams());
this.exchange = request.exchange();
}

@Nullable
Expand Down Expand Up @@ -47,19 +62,113 @@ public String getKeyword() {
return StringUtils.defaultIfBlank(queryParams.getFirst("keyword"), null);
}

@Schema(description = "SinglePage collation.")
public SinglePageSorter getSort() {
String sort = queryParams.getFirst("sort");
return SinglePageSorter.convertFrom(sort);
@ArraySchema(uniqueItems = true,
arraySchema = @Schema(name = "sort",
description = "Sort property and direction of the list result. Supported fields: "
+ "creationTimestamp,publishTime"),
schema = @Schema(description = "like field,asc or field,desc",
implementation = String.class,
example = "creationTimestamp,desc"))
public Sort getSort() {
return SortResolver.defaultInstance.resolve(exchange);
}

/**
* Build a comparator for {@link SinglePageQuery}.
*
* @return comparator
*/
public Comparator<SinglePage> toComparator() {
var sort = getSort();
var creationTimestampOrder = sort.getOrderFor("creationTimestamp");
List<Comparator<SinglePage>> comparators = new ArrayList<>();
if (creationTimestampOrder != null) {
Comparator<SinglePage> comparator =
comparing(page -> page.getMetadata().getCreationTimestamp());
if (creationTimestampOrder.isDescending()) {
comparator = comparator.reversed();
}
comparators.add(comparator);
}

var publishTimeOrder = sort.getOrderFor("publishTime");
if (publishTimeOrder != null) {
Comparator<Object> nullsComparator = publishTimeOrder.isAscending()
? org.springframework.util.comparator.Comparators.nullsLow()
: org.springframework.util.comparator.Comparators.nullsHigh();
Comparator<SinglePage> comparator =
comparing(page -> page.getSpec().getPublishTime(), nullsComparator);
if (publishTimeOrder.isDescending()) {
comparator = comparator.reversed();
}
comparators.add(comparator);
}
comparators.add(Comparators.compareCreationTimestamp(false));
comparators.add(Comparators.compareName(true));
return comparators.stream()
.reduce(Comparator::thenComparing)
.orElse(null);
}

@Schema(description = "ascending order If it is true; otherwise, it is in descending order.")
public Boolean getSortOrder() {
String sortOrder = queryParams.getFirst("sortOrder");
return convertBooleanOrNull(sortOrder);
/**
* Build a predicate for {@link SinglePageQuery}.
*
* @return predicate
*/
public Predicate<SinglePage> toPredicate() {
Predicate<SinglePage> paramPredicate = singlePage -> contains(getContributors(),
singlePage.getStatusOrDefault().getContributors());

String keyword = getKeyword();
if (keyword != null) {
paramPredicate = paramPredicate.and(page -> {
String excerpt = page.getStatusOrDefault().getExcerpt();
return StringUtils.containsIgnoreCase(excerpt, keyword)
|| StringUtils.containsIgnoreCase(page.getSpec().getSlug(), keyword)
|| StringUtils.containsIgnoreCase(page.getSpec().getTitle(), keyword);
});
}

Post.PostPhase publishPhase = getPublishPhase();
if (publishPhase != null) {
paramPredicate = paramPredicate.and(page -> {
if (Post.PostPhase.PENDING_APPROVAL.equals(publishPhase)) {
return !page.isPublished()
&& Post.PostPhase.PENDING_APPROVAL.name()
.equalsIgnoreCase(page.getStatusOrDefault().getPhase());
}
// published
if (Post.PostPhase.PUBLISHED.equals(publishPhase)) {
return page.isPublished();
}
// draft
return !page.isPublished();
});
}

Post.VisibleEnum visible = getVisible();
if (visible != null) {
paramPredicate =
paramPredicate.and(post -> visible.equals(post.getSpec().getVisible()));
}

Predicate<SinglePage> predicate = labelAndFieldSelectorToPredicate(getLabelSelector(),
getFieldSelector());
return predicate.and(paramPredicate);
}

private Boolean convertBooleanOrNull(String value) {
return StringUtils.isBlank(value) ? null : Boolean.parseBoolean(value);
boolean contains(Collection<String> left, List<String> right) {
// parameter is null, it means that ignore this condition
if (left == null) {
return true;
}
// else, it means that right is empty
if (left.isEmpty()) {
return right.isEmpty();
}
if (right == null) {
return false;
}
return right.stream().anyMatch(left::contains);
}
}

0 comments on commit ec09a3d

Please sign in to comment.