mirror of https://github.com/halo-dev/halo
Fix the problem of being able to search private posts after making post private (#3859)
#### What type of PR is this? /kind bug /area core /milestone 2.5.x #### What this PR does / why we need it: This PR adds PostVisibleChangedEvent to synchronizing post indices when post visible is changed, whether from public to private or from private to public. #### Which issue(s) this PR fixes: Fixes https://github.com/halo-dev/halo/issues/3438 #### Special notes for your reviewer: 1. Install Search plugin 2. Create a post 3. Try to search the post 4. Make post private 5. Try to search the post 6. Make post public 7. Try to search the post #### Does this PR introduce a user-facing change? ```release-note 修复隐藏的文章已然能够被搜索到问题 ```pull/3838/head^2
parent
7b8613049a
commit
4cd6c2f67c
|
@ -10,7 +10,7 @@ import java.util.Set;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.jsoup.Jsoup;
|
import org.jsoup.Jsoup;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationEventPublisher;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import run.halo.app.content.PostService;
|
import run.halo.app.content.PostService;
|
||||||
|
@ -21,6 +21,7 @@ import run.halo.app.core.extension.content.Post;
|
||||||
import run.halo.app.core.extension.content.Snapshot;
|
import run.halo.app.core.extension.content.Snapshot;
|
||||||
import run.halo.app.event.post.PostPublishedEvent;
|
import run.halo.app.event.post.PostPublishedEvent;
|
||||||
import run.halo.app.event.post.PostUnpublishedEvent;
|
import run.halo.app.event.post.PostUnpublishedEvent;
|
||||||
|
import run.halo.app.event.post.PostVisibleChangedEvent;
|
||||||
import run.halo.app.extension.ExtensionClient;
|
import run.halo.app.extension.ExtensionClient;
|
||||||
import run.halo.app.extension.ExtensionOperator;
|
import run.halo.app.extension.ExtensionOperator;
|
||||||
import run.halo.app.extension.MetadataUtil;
|
import run.halo.app.extension.MetadataUtil;
|
||||||
|
@ -57,7 +58,7 @@ public class PostReconciler implements Reconciler<Reconciler.Request> {
|
||||||
private final PostPermalinkPolicy postPermalinkPolicy;
|
private final PostPermalinkPolicy postPermalinkPolicy;
|
||||||
private final CounterService counterService;
|
private final CounterService counterService;
|
||||||
|
|
||||||
private final ApplicationContext applicationContext;
|
private final ApplicationEventPublisher eventPublisher;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Result reconcile(Request request) {
|
public Result reconcile(Request request) {
|
||||||
|
@ -93,7 +94,7 @@ public class PostReconciler implements Reconciler<Reconciler.Request> {
|
||||||
&& Objects.equals(false, post.getSpec().getPublish())) {
|
&& Objects.equals(false, post.getSpec().getPublish())) {
|
||||||
boolean success = unPublishReconcile(name);
|
boolean success = unPublishReconcile(name);
|
||||||
if (success) {
|
if (success) {
|
||||||
applicationContext.publishEvent(new PostUnpublishedEvent(this, name));
|
eventPublisher.publishEvent(new PostUnpublishedEvent(this, name));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -163,7 +164,7 @@ public class PostReconciler implements Reconciler<Reconciler.Request> {
|
||||||
status.setLastModifyTime(releasedSnapshotOpt.get().getSpec().getLastModifyTime());
|
status.setLastModifyTime(releasedSnapshotOpt.get().getSpec().getLastModifyTime());
|
||||||
|
|
||||||
client.update(post);
|
client.update(post);
|
||||||
applicationContext.publishEvent(new PostPublishedEvent(this, name));
|
eventPublisher.publishEvent(new PostPublishedEvent(this, name));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,8 +232,11 @@ public class PostReconciler implements Reconciler<Reconciler.Request> {
|
||||||
} else {
|
} else {
|
||||||
labels.put(Post.DELETED_LABEL, Boolean.FALSE.toString());
|
labels.put(Post.DELETED_LABEL, Boolean.FALSE.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fireVisibleChangedEventIfChanged(post);
|
||||||
labels.put(Post.VISIBLE_LABEL,
|
labels.put(Post.VISIBLE_LABEL,
|
||||||
Objects.requireNonNullElse(spec.getVisible(), Post.VisibleEnum.PUBLIC).name());
|
Objects.requireNonNullElse(spec.getVisible(), Post.VisibleEnum.PUBLIC).name());
|
||||||
|
|
||||||
labels.put(Post.OWNER_LABEL, spec.getOwner());
|
labels.put(Post.OWNER_LABEL, spec.getOwner());
|
||||||
Instant publishTime = post.getSpec().getPublishTime();
|
Instant publishTime = post.getSpec().getPublishTime();
|
||||||
if (publishTime != null) {
|
if (publishTime != null) {
|
||||||
|
@ -254,6 +258,23 @@ public class PostReconciler implements Reconciler<Reconciler.Request> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void fireVisibleChangedEventIfChanged(Post post) {
|
||||||
|
var labels = post.getMetadata().getLabels();
|
||||||
|
if (labels == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var name = post.getMetadata().getName();
|
||||||
|
var oldVisibleStr = labels.get(Post.VISIBLE_LABEL);
|
||||||
|
if (oldVisibleStr != null) {
|
||||||
|
var oldVisible = Post.VisibleEnum.valueOf(oldVisibleStr);
|
||||||
|
var expectVisible = post.getSpec().getVisible();
|
||||||
|
if (!Objects.equals(oldVisible, expectVisible)) {
|
||||||
|
eventPublisher.publishEvent(
|
||||||
|
new PostVisibleChangedEvent(name, oldVisible, expectVisible));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void reconcileStatus(String name) {
|
private void reconcileStatus(String name) {
|
||||||
client.fetch(Post.class, name).ifPresent(post -> {
|
client.fetch(Post.class, name).ifPresent(post -> {
|
||||||
final Post oldPost = JsonUtils.deepCopy(post);
|
final Post oldPost = JsonUtils.deepCopy(post);
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
package run.halo.app.event.post;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import run.halo.app.core.extension.content.Post;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class PostVisibleChangedEvent implements PostEvent {
|
||||||
|
|
||||||
|
private final String postName;
|
||||||
|
|
||||||
|
private final Post.VisibleEnum oldVisible;
|
||||||
|
|
||||||
|
private final Post.VisibleEnum newVisible;
|
||||||
|
|
||||||
|
public PostVisibleChangedEvent(String postName, Post.VisibleEnum oldVisible,
|
||||||
|
Post.VisibleEnum newVisible) {
|
||||||
|
this.postName = postName;
|
||||||
|
this.oldVisible = oldVisible;
|
||||||
|
this.newVisible = newVisible;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return postName;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,10 +1,11 @@
|
||||||
package run.halo.app.search.post;
|
package run.halo.app.search.post;
|
||||||
|
|
||||||
|
import static run.halo.app.core.extension.content.Post.VisibleEnum.PUBLIC;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.context.SmartLifecycle;
|
import org.springframework.context.SmartLifecycle;
|
||||||
import org.springframework.context.event.EventListener;
|
import org.springframework.context.event.EventListener;
|
||||||
|
@ -14,6 +15,7 @@ import run.halo.app.event.post.PostEvent;
|
||||||
import run.halo.app.event.post.PostPublishedEvent;
|
import run.halo.app.event.post.PostPublishedEvent;
|
||||||
import run.halo.app.event.post.PostRecycledEvent;
|
import run.halo.app.event.post.PostRecycledEvent;
|
||||||
import run.halo.app.event.post.PostUnpublishedEvent;
|
import run.halo.app.event.post.PostUnpublishedEvent;
|
||||||
|
import run.halo.app.event.post.PostVisibleChangedEvent;
|
||||||
import run.halo.app.extension.controller.Controller;
|
import run.halo.app.extension.controller.Controller;
|
||||||
import run.halo.app.extension.controller.ControllerBuilder;
|
import run.halo.app.extension.controller.ControllerBuilder;
|
||||||
import run.halo.app.extension.controller.DefaultController;
|
import run.halo.app.extension.controller.DefaultController;
|
||||||
|
@ -51,21 +53,20 @@ public class PostEventReconciler implements Reconciler<PostEvent>, SmartLifecycl
|
||||||
@Override
|
@Override
|
||||||
public Result reconcile(PostEvent postEvent) {
|
public Result reconcile(PostEvent postEvent) {
|
||||||
if (postEvent instanceof PostPublishedEvent) {
|
if (postEvent instanceof PostPublishedEvent) {
|
||||||
try {
|
|
||||||
addPostDoc(postEvent.getName());
|
addPostDoc(postEvent.getName());
|
||||||
} catch (InterruptedException e) {
|
|
||||||
throw Exceptions.propagate(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (postEvent instanceof PostUnpublishedEvent
|
if (postEvent instanceof PostUnpublishedEvent
|
||||||
|| postEvent instanceof PostRecycledEvent) {
|
|| postEvent instanceof PostRecycledEvent) {
|
||||||
try {
|
|
||||||
deletePostDoc(postEvent.getName());
|
deletePostDoc(postEvent.getName());
|
||||||
} catch (InterruptedException e) {
|
}
|
||||||
throw Exceptions.propagate(e);
|
if (postEvent instanceof PostVisibleChangedEvent visibleChangedEvent) {
|
||||||
|
if (PUBLIC.equals(visibleChangedEvent.getOldVisible())) {
|
||||||
|
deletePostDoc(postEvent.getName());
|
||||||
|
} else if (PUBLIC.equals(visibleChangedEvent.getNewVisible())) {
|
||||||
|
addPostDoc(postEvent.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return Result.doNotRetry();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -95,9 +96,14 @@ public class PostEventReconciler implements Reconciler<PostEvent>, SmartLifecycl
|
||||||
postEventQueue.addImmediately(recycledEvent);
|
postEventQueue.addImmediately(recycledEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
void addPostDoc(String postName) throws InterruptedException {
|
@EventListener(PostVisibleChangedEvent.class)
|
||||||
var latch = new CountDownLatch(1);
|
public void handlePostVisibleChanged(PostVisibleChangedEvent event) {
|
||||||
var disposable = postFinder.getByName(postName)
|
postEventQueue.addImmediately(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addPostDoc(String postName) {
|
||||||
|
postFinder.getByName(postName)
|
||||||
|
.filter(postVo -> PUBLIC.equals(postVo.getSpec().getVisible()))
|
||||||
.map(PostDocUtils::from)
|
.map(PostDocUtils::from)
|
||||||
.flatMap(postDoc -> extensionGetter.getEnabledExtension(PostSearchService.class)
|
.flatMap(postDoc -> extensionGetter.getEnabledExtension(PostSearchService.class)
|
||||||
.doOnNext(searchService -> {
|
.doOnNext(searchService -> {
|
||||||
|
@ -108,21 +114,12 @@ public class PostEventReconciler implements Reconciler<PostEvent>, SmartLifecycl
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.doFinally(signalType -> latch.countDown())
|
.then()
|
||||||
.subscribe(service -> {
|
.block();
|
||||||
}, throwable -> {
|
|
||||||
throw Exceptions.propagate(throwable);
|
|
||||||
});
|
|
||||||
try {
|
|
||||||
latch.await();
|
|
||||||
} finally {
|
|
||||||
disposable.dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void deletePostDoc(String postName) throws InterruptedException {
|
void deletePostDoc(String postName) {
|
||||||
var latch = new CountDownLatch(1);
|
extensionGetter.getEnabledExtension(PostSearchService.class)
|
||||||
var disposable = extensionGetter.getEnabledExtension(PostSearchService.class)
|
|
||||||
.doOnNext(searchService -> {
|
.doOnNext(searchService -> {
|
||||||
try {
|
try {
|
||||||
searchService.removeDocuments(Set.of(postName));
|
searchService.removeDocuments(Set.of(postName));
|
||||||
|
@ -130,16 +127,8 @@ public class PostEventReconciler implements Reconciler<PostEvent>, SmartLifecycl
|
||||||
throw Exceptions.propagate(e);
|
throw Exceptions.propagate(e);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.doFinally(signalType -> latch.countDown())
|
.then()
|
||||||
.subscribe(service -> {
|
.block();
|
||||||
}, throwable -> {
|
|
||||||
throw Exceptions.propagate(throwable);
|
|
||||||
});
|
|
||||||
try {
|
|
||||||
latch.await();
|
|
||||||
} finally {
|
|
||||||
disposable.dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -19,7 +19,7 @@ import org.mockito.ArgumentCaptor;
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationEventPublisher;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
import run.halo.app.content.ContentWrapper;
|
import run.halo.app.content.ContentWrapper;
|
||||||
import run.halo.app.content.PostService;
|
import run.halo.app.content.PostService;
|
||||||
|
@ -50,7 +50,7 @@ class PostReconcilerTest {
|
||||||
private PostService postService;
|
private PostService postService;
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private ApplicationContext applicationContext;
|
private ApplicationEventPublisher eventPublisher;
|
||||||
|
|
||||||
@InjectMocks
|
@InjectMocks
|
||||||
private PostReconciler postReconciler;
|
private PostReconciler postReconciler;
|
||||||
|
@ -157,7 +157,7 @@ class PostReconcilerTest {
|
||||||
verify(client, times(4)).update(captor.capture());
|
verify(client, times(4)).update(captor.capture());
|
||||||
Post value = captor.getValue();
|
Post value = captor.getValue();
|
||||||
assertThat(value.getStatus().getLastModifyTime()).isEqualTo(lastModifyTime);
|
assertThat(value.getStatus().getLastModifyTime()).isEqualTo(lastModifyTime);
|
||||||
verify(applicationContext).publishEvent(any(PostPublishedEvent.class));
|
verify(eventPublisher).publishEvent(any(PostPublishedEvent.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
Loading…
Reference in New Issue