Skip to content

Commit 496847d

Browse files
committed
Merge remote-tracking branch 'upstream/main'
2 parents cac0163 + 5e073bf commit 496847d

File tree

71 files changed

+1497
-882
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+1497
-882
lines changed

api/build.gradle

+6-1
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,17 @@ plugins {
22
id 'java-library'
33
id 'halo.publish'
44
id 'jacoco'
5-
id "io.freefair.lombok" version "8.4"
5+
id "io.freefair.lombok"
66
}
77

88
group = 'run.halo.app'
99
description = 'API of halo project, connecting by other projects.'
1010

11+
java {
12+
sourceCompatibility = JavaVersion.VERSION_17
13+
targetCompatibility = JavaVersion.VERSION_17
14+
}
15+
1116
compileJava.options.encoding = "UTF-8"
1217
compileTestJava.options.encoding = "UTF-8"
1318
javadoc.options.encoding = "UTF-8"

api/src/main/java/run/halo/app/core/extension/content/Post.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ public class Post extends AbstractExtension {
4141
public static final String CATEGORIES_ANNO = "content.halo.run/categories";
4242
public static final String LAST_RELEASED_SNAPSHOT_ANNO =
4343
"content.halo.run/last-released-snapshot";
44-
public static final String TAGS_ANNO = "content.halo.run/tags";
44+
public static final String LAST_ASSOCIATED_TAGS_ANNO = "content.halo.run/last-associated-tags";
45+
4546
public static final String DELETED_LABEL = "content.halo.run/deleted";
4647
public static final String PUBLISHED_LABEL = "content.halo.run/published";
4748
public static final String OWNER_LABEL = "content.halo.run/owner";

api/src/main/java/run/halo/app/core/extension/content/Tag.java

+4
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ public class Tag extends AbstractExtension {
2727

2828
public static final GroupVersionKind GVK = GroupVersionKind.fromExtension(Tag.class);
2929

30+
public static final String REQUIRE_SYNC_ON_STARTUP_INDEX_NAME = "requireSyncOnStartup";
31+
3032
@Schema(requiredMode = REQUIRED)
3133
private TagSpec spec;
3234

@@ -77,5 +79,7 @@ public static class TagStatus {
7779
public Integer visiblePostCount;
7880

7981
public Integer postCount;
82+
83+
private Long observedVersion;
8084
}
8185
}

api/src/main/java/run/halo/app/extension/index/query/QueryFactory.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ public static Query and(Collection<Query> queries) {
120120
return new And(queries);
121121
}
122122

123-
public static Query and(Query query1, Query query2) {
123+
public static And and(Query query1, Query query2) {
124124
Collection<Query> queries = Arrays.asList(query1, query2);
125125
return new And(queries);
126126
}

application/build.gradle

+8-9
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,24 @@ import de.undercouch.gradle.tasks.download.Download
22
import org.gradle.crypto.checksum.Checksum
33

44
plugins {
5-
id 'org.springframework.boot' version '3.2.3'
6-
id 'io.spring.dependency-management' version '1.1.0'
7-
id "com.gorylenko.gradle-git-properties" version "2.3.2"
5+
id 'org.springframework.boot'
6+
id 'io.spring.dependency-management'
7+
id "com.gorylenko.gradle-git-properties"
88
id "checkstyle"
99
id 'java'
1010
id 'jacoco'
11-
id "de.undercouch.download" version "5.3.1"
12-
id "io.freefair.lombok" version "8.4"
13-
id 'org.gradle.crypto.checksum' version '1.4.0'
11+
id "de.undercouch.download"
12+
id "io.freefair.lombok"
13+
id 'org.gradle.crypto.checksum'
1414
}
1515

1616
group = 'run.halo.app'
1717
compileJava.options.encoding = 'UTF-8'
1818
compileTestJava.options.encoding = 'UTF-8'
1919

2020
java {
21-
toolchain {
22-
languageVersion = JavaLanguageVersion.of(17)
23-
}
21+
sourceCompatibility = JavaVersion.VERSION_17
22+
targetCompatibility = JavaVersion.VERSION_17
2423
}
2524

2625
checkstyle {

application/src/main/java/run/halo/app/content/ListedPost.java

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import io.swagger.v3.oas.annotations.media.Schema;
66
import java.util.List;
77
import lombok.Data;
8+
import lombok.experimental.Accessors;
89
import run.halo.app.core.extension.content.Category;
910
import run.halo.app.core.extension.content.Post;
1011
import run.halo.app.core.extension.content.Tag;
@@ -17,6 +18,7 @@
1718
* @since 2.0.0
1819
*/
1920
@Data
21+
@Accessors(chain = true)
2022
public class ListedPost {
2123

2224
@Schema(requiredMode = REQUIRED)

application/src/main/java/run/halo/app/content/ListedSinglePage.java

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import io.swagger.v3.oas.annotations.media.Schema;
66
import java.util.List;
77
import lombok.Data;
8+
import lombok.experimental.Accessors;
89
import run.halo.app.core.extension.content.SinglePage;
910

1011

@@ -15,6 +16,7 @@
1516
* @since 2.0.0
1617
*/
1718
@Data
19+
@Accessors(chain = true)
1820
public class ListedSinglePage {
1921

2022
@Schema(requiredMode = REQUIRED)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
package run.halo.app.content;
2+
3+
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
4+
import static run.halo.app.extension.index.query.QueryFactory.and;
5+
import static run.halo.app.extension.index.query.QueryFactory.equal;
6+
import static run.halo.app.extension.index.query.QueryFactory.isNull;
7+
8+
import com.google.common.collect.Sets;
9+
import java.time.Duration;
10+
import java.time.Instant;
11+
import java.util.List;
12+
import java.util.Optional;
13+
import java.util.Set;
14+
import org.apache.commons.lang3.StringUtils;
15+
import org.springframework.context.SmartLifecycle;
16+
import org.springframework.context.event.EventListener;
17+
import org.springframework.stereotype.Component;
18+
import run.halo.app.core.extension.content.Post;
19+
import run.halo.app.core.extension.content.Tag;
20+
import run.halo.app.core.extension.content.Tag.TagStatus;
21+
import run.halo.app.event.post.PostDeletedEvent;
22+
import run.halo.app.event.post.PostEvent;
23+
import run.halo.app.event.post.PostUpdatedEvent;
24+
import run.halo.app.extension.ExtensionClient;
25+
import run.halo.app.extension.ListOptions;
26+
import run.halo.app.extension.MetadataUtil;
27+
import run.halo.app.extension.PageRequestImpl;
28+
import run.halo.app.extension.controller.Controller;
29+
import run.halo.app.extension.controller.ControllerBuilder;
30+
import run.halo.app.extension.controller.DefaultController;
31+
import run.halo.app.extension.controller.DefaultQueue;
32+
import run.halo.app.extension.controller.Reconciler;
33+
import run.halo.app.extension.controller.RequestQueue;
34+
import run.halo.app.extension.router.selector.FieldSelector;
35+
import run.halo.app.extension.router.selector.LabelSelector;
36+
import run.halo.app.infra.utils.JsonUtils;
37+
38+
/**
39+
* Update {@link TagStatus#postCount} when post related to tag is updated.
40+
*
41+
* @author guqing
42+
* @since 2.13.0
43+
*/
44+
@Component
45+
public class TagPostCountUpdater
46+
implements Reconciler<TagPostCountUpdater.PostRelatedTags>, SmartLifecycle {
47+
48+
private final RequestQueue<PostRelatedTags> tagQueue;
49+
50+
private final Controller postEventController;
51+
52+
private final ExtensionClient client;
53+
54+
private volatile boolean running = false;
55+
56+
/**
57+
* Construct a {@link TagPostCountUpdater} with the given {@link ExtensionClient}.
58+
*/
59+
public TagPostCountUpdater(ExtensionClient client) {
60+
this.client = client;
61+
62+
this.tagQueue = new DefaultQueue<>(Instant::now);
63+
this.postEventController = this.setupWith(null);
64+
}
65+
66+
@Override
67+
public Result reconcile(PostRelatedTags postRelatedTags) {
68+
for (var tag : postRelatedTags.tags()) {
69+
updateTagRelatedPostCount(tag);
70+
}
71+
72+
// Update last associated tags when handled
73+
client.fetch(Post.class, postRelatedTags.postName()).ifPresent(post -> {
74+
var tags = defaultIfNull(post.getSpec().getTags(), List.<String>of());
75+
var annotations = MetadataUtil.nullSafeAnnotations(post);
76+
var tagAnno = JsonUtils.objectToJson(tags);
77+
var oldTagAnno = annotations.get(Post.LAST_ASSOCIATED_TAGS_ANNO);
78+
79+
if (!tagAnno.equals(oldTagAnno)) {
80+
annotations.put(Post.LAST_ASSOCIATED_TAGS_ANNO, tagAnno);
81+
client.update(post);
82+
}
83+
});
84+
return Result.doNotRetry();
85+
}
86+
87+
88+
@Override
89+
public Controller setupWith(ControllerBuilder builder) {
90+
return new DefaultController<>(
91+
this.getClass().getName(),
92+
this,
93+
tagQueue,
94+
null,
95+
Duration.ofMillis(100),
96+
Duration.ofMinutes(10)
97+
);
98+
}
99+
100+
@Override
101+
public void start() {
102+
postEventController.start();
103+
running = true;
104+
}
105+
106+
@Override
107+
public void stop() {
108+
running = false;
109+
postEventController.dispose();
110+
}
111+
112+
@Override
113+
public boolean isRunning() {
114+
return running;
115+
}
116+
117+
/**
118+
* Listen to post event to calculate post related to tag for updating.
119+
*/
120+
@EventListener(PostEvent.class)
121+
public void onPostUpdated(PostEvent postEvent) {
122+
var postName = postEvent.getName();
123+
if (postEvent instanceof PostUpdatedEvent) {
124+
var tagsToUpdate = calcTagsToUpdate(postEvent.getName());
125+
tagQueue.addImmediately(new PostRelatedTags(postName, tagsToUpdate));
126+
return;
127+
}
128+
129+
if (postEvent instanceof PostDeletedEvent deletedEvent) {
130+
var tags = defaultIfNull(deletedEvent.getPost().getSpec().getTags(),
131+
List.<String>of());
132+
tagQueue.addImmediately(new PostRelatedTags(postName, Sets.newHashSet(tags)));
133+
}
134+
}
135+
136+
private Set<String> calcTagsToUpdate(String postName) {
137+
var post = client.fetch(Post.class, postName).orElseThrow();
138+
var annotations = MetadataUtil.nullSafeAnnotations(post);
139+
var oldTags = Optional.ofNullable(annotations.get(Post.LAST_ASSOCIATED_TAGS_ANNO))
140+
.filter(StringUtils::isNotBlank)
141+
.map(tagsJson -> JsonUtils.jsonToObject(tagsJson, String[].class))
142+
.orElse(new String[0]);
143+
144+
var tagsToUpdate = Sets.newHashSet(oldTags);
145+
var newTags = post.getSpec().getTags();
146+
if (newTags != null) {
147+
tagsToUpdate.addAll(newTags);
148+
}
149+
return tagsToUpdate;
150+
}
151+
152+
public record PostRelatedTags(String postName, Set<String> tags) {
153+
}
154+
155+
private void updateTagRelatedPostCount(String tagName) {
156+
client.fetch(Tag.class, tagName).ifPresent(tag -> {
157+
var commonFieldQuery = and(
158+
equal("spec.tags", tag.getMetadata().getName()),
159+
isNull("metadata.deletionTimestamp")
160+
);
161+
// Update post count
162+
var allPostOptions = new ListOptions();
163+
allPostOptions.setFieldSelector(FieldSelector.of(commonFieldQuery));
164+
var result = client.listBy(Post.class, allPostOptions, PageRequestImpl.ofSize(1));
165+
tag.getStatusOrDefault().setPostCount((int) result.getTotal());
166+
167+
// Update visible post count
168+
var publicPostOptions = new ListOptions();
169+
publicPostOptions.setLabelSelector(LabelSelector.builder()
170+
.eq(Post.PUBLISHED_LABEL, "true")
171+
.build());
172+
publicPostOptions.setFieldSelector(FieldSelector.of(
173+
and(
174+
commonFieldQuery,
175+
equal("spec.deleted", "false"),
176+
equal("spec.visible", Post.VisibleEnum.PUBLIC.name())
177+
)
178+
));
179+
var publicPosts =
180+
client.listBy(Post.class, publicPostOptions, PageRequestImpl.ofSize(1));
181+
tag.getStatusOrDefault().setVisiblePostCount((int) publicPosts.getTotal());
182+
183+
client.update(tag);
184+
});
185+
}
186+
}

application/src/main/java/run/halo/app/content/comment/CommentQuery.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,16 @@ public String getOwnerName() {
5656
@ArraySchema(uniqueItems = true,
5757
arraySchema = @Schema(name = "sort",
5858
description = "Sort property and direction of the list result. Supported fields: "
59-
+ "creationTimestamp,replyCount,lastReplyTime"),
59+
+ "metadata.creationTimestamp,status.replyCount,status.lastReplyTime"),
6060
schema = @Schema(description = "like field,asc or field,desc",
6161
implementation = String.class,
6262
example = "creationTimestamp,desc"))
6363
public Sort getSort() {
6464
var sort = SortResolver.defaultInstance.resolve(exchange);
65-
return sort.and(Sort.by("spec.creationTime", "metadata.name").descending());
65+
return sort.and(Sort.by("status.lastReplyTime",
66+
"spec.creationTime",
67+
"metadata.name"
68+
).descending());
6669
}
6770

6871
public PageRequest toPageRequest() {

application/src/main/java/run/halo/app/content/comment/CommentService.java

+4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package run.halo.app.content.comment;
22

3+
import org.springframework.lang.NonNull;
34
import reactor.core.publisher.Mono;
45
import run.halo.app.core.extension.content.Comment;
56
import run.halo.app.extension.ListResult;
7+
import run.halo.app.extension.Ref;
68

79
/**
810
* An application service for {@link Comment}.
@@ -15,4 +17,6 @@ public interface CommentService {
1517
Mono<ListResult<ListedComment>> listComment(CommentQuery query);
1618

1719
Mono<Comment> create(Comment comment);
20+
21+
Mono<Void> removeBySubject(@NonNull Ref subjectRef);
1822
}

0 commit comments

Comments
 (0)