Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: add creationTime field for comment extension to resolve migration issue #3341

Merged
merged 6 commits into from
Feb 23, 2023
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ public Mono<Comment> create(Comment comment) {
if (comment.getSpec().getOwner() != null) {
return Mono.just(comment);
}
if (comment.getSpec().getCreationTime() == null) {
comment.getSpec().setCreationTime(Instant.now());
}
// populate owner from current user
return fetchCurrentUser()
.map(this::toCommentOwner)
Expand Down
12 changes: 6 additions & 6 deletions src/main/java/run/halo/app/content/comment/CommentSorter.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ static Comparator<Comment> from(CommentSorter sorter, Boolean ascending) {

static Comparator<Comment> from(CommentSorter sorter) {
if (sorter == null) {
return defaultCommentComparator();
return lastReplyTimeComparator();
}
if (CREATE_TIME.equals(sorter)) {
Function<Comment, Instant> comparatorFunc =
comment -> comment.getMetadata().getCreationTimestamp();
return Comparator.comparing(comparatorFunc)
comment -> comment.getSpec().getCreationTime();
return Comparator.comparing(comparatorFunc, Comparators.nullsLow())
.thenComparing(name);
}

Expand Down Expand Up @@ -65,12 +65,12 @@ static CommentSorter convertFrom(String sort) {
return null;
}

static Comparator<Comment> defaultCommentComparator() {
static Comparator<Comment> lastReplyTimeComparator() {
Function<Comment, Instant> comparatorFunc =
comment -> {
Instant lastReplyTime = comment.getStatusOrDefault().getLastReplyTime();
return Optional.ofNullable(
lastReplyTime).orElse(comment.getMetadata().getCreationTimestamp());
return Optional.ofNullable(lastReplyTime)
.orElse(comment.getSpec().getCreationTime());
};
return Comparator.comparing(comparatorFunc, Comparators.nullsLow())
.thenComparing(name);
Expand Down
20 changes: 20 additions & 0 deletions src/main/java/run/halo/app/content/comment/ReplyService.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package run.halo.app.content.comment;

import java.time.Instant;
import java.util.Comparator;
import java.util.function.Function;
import org.springframework.util.comparator.Comparators;
import reactor.core.publisher.Mono;
import run.halo.app.core.extension.content.Reply;
import run.halo.app.extension.ListResult;
Expand All @@ -15,4 +19,20 @@ public interface ReplyService {
Mono<Reply> create(String commentName, Reply reply);

Mono<ListResult<ListedReply>> list(ReplyQuery query);

/**
* Ascending order by creation time.
*
* @return reply comparator
*/
static Comparator<Reply> creationTimeAscComparator() {
Function<Reply, Instant> creationTime = reply -> reply.getSpec().getCreationTime();
Function<Reply, Instant> metadataCreationTime =
reply -> reply.getMetadata().getCreationTimestamp();
// ascending order by creation time
// asc nulls high will be placed at the end
return Comparator.comparing(creationTime, Comparators.nullsHigh())
.thenComparing(metadataCreationTime)
.thenComparing(reply -> reply.getMetadata().getName());
}
}
13 changes: 5 additions & 8 deletions src/main/java/run/halo/app/content/comment/ReplyServiceImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import static run.halo.app.extension.router.selector.SelectorUtil.labelAndFieldSelectorToPredicate;

import java.time.Instant;
import java.util.Comparator;
import java.util.function.Function;
import java.util.function.Predicate;
import org.apache.commons.lang3.BooleanUtils;
Expand Down Expand Up @@ -51,6 +50,9 @@ public Mono<Reply> create(String commentName, Reply reply) {
if (reply.getSpec().getPriority() == null) {
reply.getSpec().setPriority(0);
}
if (reply.getSpec().getCreationTime() == null) {
reply.getSpec().setCreationTime(Instant.now());
}
return environmentFetcher.fetchComment()
.map(commentSetting -> {
if (Boolean.FALSE.equals(commentSetting.getEnable())) {
Expand Down Expand Up @@ -104,7 +106,8 @@ private boolean checkReplyOwner(Reply reply, Boolean onlySystemUser) {

@Override
public Mono<ListResult<ListedReply>> list(ReplyQuery query) {
return client.list(Reply.class, getReplyPredicate(query), defaultComparator(),
return client.list(Reply.class, getReplyPredicate(query),
ReplyService.creationTimeAscComparator(),
query.getPage(), query.getSize())
.flatMap(list -> Flux.fromStream(list.get()
.map(this::toListedReply))
Expand Down Expand Up @@ -140,12 +143,6 @@ private Mono<OwnerInfo> getOwnerInfo(Reply reply) {
"Unsupported owner kind: " + owner.getKind());
}

Comparator<Reply> defaultComparator() {
Function<Reply, Instant> createTime = reply -> reply.getMetadata().getCreationTimestamp();
return Comparator.comparing(createTime)
.thenComparing(reply -> reply.getMetadata().getName());
}

Predicate<Reply> getReplyPredicate(ReplyQuery query) {
Predicate<Reply> predicate = reply -> true;
if (query.getCommentName() != null) {
Expand Down
21 changes: 19 additions & 2 deletions src/main/java/run/halo/app/core/extension/content/Comment.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package run.halo.app.core.extension.content;

import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;

import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import java.time.Instant;
Expand Down Expand Up @@ -70,6 +72,11 @@ public static class BaseCommentSpec {

private Instant approvedTime;

/**
* The user-defined creation time default is <code>metadata.creationTimestamp</code>.
*/
private Instant creationTime;

@Schema(required = true, defaultValue = "0")
private Integer priority;

Expand All @@ -84,6 +91,15 @@ public static class BaseCommentSpec {

@Schema(required = true, defaultValue = "false")
private Boolean hidden;

/**
* If the creation time is null, use approvedTime.
*
* @return if creationTime is null returned approved time, otherwise creationTime.
*/
public Instant getCreationTime() {
return defaultIfNull(creationTime, approvedTime);
}
}

@Data
Expand Down Expand Up @@ -130,8 +146,9 @@ public static int getUnreadReplyCount(List<Reply> replies, Instant lastReadTime)
if (lastReadTime == null) {
return true;
}
return existingReply.getMetadata().getCreationTimestamp()
.isAfter(lastReadTime);
Instant creationTime = defaultIfNull(existingReply.getSpec().getCreationTime(),
existingReply.getMetadata().getCreationTimestamp());
return creationTime.isAfter(lastReadTime);
})
.count();
return (int) unreadReplyCount;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
package run.halo.app.core.extension.reconciler;

import java.time.Instant;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import run.halo.app.content.comment.ReplyService;
import run.halo.app.core.extension.Counter;
import run.halo.app.core.extension.content.Comment;
import run.halo.app.core.extension.content.Constant;
Expand Down Expand Up @@ -110,7 +109,7 @@ private void updateUnReplyCountIfNecessary(Comment comment) {
List<Reply> replies = client.list(Reply.class,
reply -> commentName.equals(reply.getSpec().getCommentName())
&& reply.getMetadata().getDeletionTimestamp() == null,
defaultReplyComparator());
ReplyService.creationTimeAscComparator());

// calculate unread reply count
comment.getStatusOrDefault()
Expand Down Expand Up @@ -181,11 +180,4 @@ private GroupVersionKind groupVersionKind(Ref ref) {
}
return new GroupVersionKind(ref.getGroup(), ref.getVersion(), ref.getKind());
}

Comparator<Reply> defaultReplyComparator() {
Function<Reply, Instant> createTime = reply -> reply.getMetadata().getCreationTimestamp();
return Comparator.comparing(createTime)
.thenComparing(reply -> reply.getMetadata().getName())
.reversed();
}
}
26 changes: 12 additions & 14 deletions src/main/java/run/halo/app/metrics/ReplyEventReconciler.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package run.halo.app.metrics;

import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;

import java.time.Duration;
import java.time.Instant;
import java.util.Comparator;
import java.util.List;
import java.util.function.Function;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.SmartLifecycle;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import run.halo.app.content.comment.ReplyService;
import run.halo.app.core.extension.content.Comment;
import run.halo.app.core.extension.content.Reply;
import run.halo.app.event.post.ReplyEvent;
Expand Down Expand Up @@ -51,20 +52,24 @@ public Result reconcile(ReplyEvent request) {
.filter(comment -> comment.getMetadata().getDeletionTimestamp() == null)
.ifPresent(comment -> {

// order by reply creation time desc to get first as last reply time
List<Reply> replies = client.list(Reply.class,
record -> commentName.equals(record.getSpec().getCommentName())
&& record.getMetadata().getDeletionTimestamp() == null,
defaultReplyComparator());
ReplyService.creationTimeAscComparator().reversed());

Comment.CommentStatus status = comment.getStatusOrDefault();
// total reply count
status.setReplyCount(replies.size());

// calculate last reply time
if (!replies.isEmpty()) {
Instant lastReplyTime = replies.get(0).getMetadata().getCreationTimestamp();
status.setLastReplyTime(lastReplyTime);
}
Instant lastReplyTime = replies.stream()
.findFirst()
.map(reply -> defaultIfNull(reply.getSpec().getCreationTime(),
reply.getMetadata().getCreationTimestamp())
)
.orElse(null);
status.setLastReplyTime(lastReplyTime);

Instant lastReadTime = comment.getSpec().getLastReadTime();
status.setUnreadReplyCount(Comment.getUnreadReplyCount(replies, lastReadTime));
Expand All @@ -74,13 +79,6 @@ record -> commentName.equals(record.getSpec().getCommentName())
return new Result(false, null);
}

Comparator<Reply> defaultReplyComparator() {
Function<Reply, Instant> createTime = reply -> reply.getMetadata().getCreationTimestamp();
return Comparator.comparing(createTime)
.thenComparing(reply -> reply.getMetadata().getName())
.reversed();
}

@Override
public Controller setupWith(ControllerBuilder builder) {
return new DefaultController<>(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package run.halo.app.theme.finders.impl;

import java.security.Principal;
import java.time.Instant;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Predicate;
import org.apache.commons.lang3.BooleanUtils;
Expand All @@ -13,9 +11,11 @@
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.util.Assert;
import org.springframework.util.comparator.Comparators;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import run.halo.app.content.comment.OwnerInfo;
import run.halo.app.content.comment.ReplyService;
import run.halo.app.core.extension.User;
import run.halo.app.core.extension.content.Comment;
import run.halo.app.core.extension.content.Reply;
Expand Down Expand Up @@ -70,12 +70,11 @@ public Mono<ListResult<CommentVo>> list(Ref ref, Integer page, Integer size) {

@Override
public Mono<ListResult<ReplyVo>> listReply(String commentName, Integer page, Integer size) {
Comparator<Reply> comparator =
Comparator.comparing(reply -> reply.getMetadata().getCreationTimestamp());
return fixedReplyPredicate(commentName)
.flatMap(fixedPredicate ->
client.list(Reply.class, fixedPredicate,
comparator.reversed(), pageNullSafe(page), sizeNullSafe(size))
ReplyService.creationTimeAscComparator(), pageNullSafe(page),
sizeNullSafe(size))
.flatMap(list -> Flux.fromStream(list.get().map(this::toReplyVo))
.concatMap(Function.identity())
.collectList()
Expand Down Expand Up @@ -166,19 +165,36 @@ Mono<String> getCurrentUserWithoutAnonymous() {
}

static Comparator<Comment> defaultComparator() {
Function<Comment, Boolean> top =
comment -> Objects.requireNonNullElse(comment.getSpec().getTop(), false);
Function<Comment, Integer> priority =
comment -> Objects.requireNonNullElse(comment.getSpec().getPriority(), 0);
Function<Comment, Instant> creationTimestamp =
comment -> comment.getMetadata().getCreationTimestamp();
Function<Comment, String> name =
comment -> comment.getMetadata().getName();
return Comparator.comparing(top)
.thenComparing(priority)
.thenComparing(creationTimestamp)
.thenComparing(name)
.reversed();
return new CommentComparator();
}

static class CommentComparator implements Comparator<Comment> {
@Override
public int compare(Comment c1, Comment c2) {
boolean c1Top = BooleanUtils.isTrue(c1.getSpec().getTop());
boolean c2Top = BooleanUtils.isTrue(c2.getSpec().getTop());
if (c1Top == c2Top) {
int c1Priority = ObjectUtils.defaultIfNull(c1.getSpec().getPriority(), 0);
int c2Priority = ObjectUtils.defaultIfNull(c2.getSpec().getPriority(), 0);
if (c1Top) {
// 都置顶
return Integer.compare(c1Priority, c2Priority);
}

// 两个评论不置顶根据 creationTime 降序排列
return Comparator.comparing(
(Comment comment) -> comment.getSpec().getCreationTime(),
Comparators.nullsLow())
.thenComparing((Comment comment) -> comment.getMetadata().getName())
.compare(c2, c1);
} else if (c1Top) {
// 只有 c1 置顶,c1 排前面
return -1;
} else {
// 只有c2置顶, c2排在前面
return 1;
}
}
}

int pageNullSafe(Integer page) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ void create() throws JSONException {

verify(client, times(1)).create(captor.capture());
Comment comment = captor.getValue();
comment.getSpec().setCreationTime(null);
JSONAssert.assertEquals("""
{
"spec": {
Expand Down
Loading