mirror of https://github.com/halo-dev/halo
fix: permalink update when slug changed (#2382)
<!-- Thanks for sending a pull request! Here are some tips for you: 1. 如果这是你的第一次,请阅读我们的贡献指南:<https://github.com/halo-dev/halo/blob/master/CONTRIBUTING.md>。 1. If this is your first time, please read our contributor guidelines: <https://github.com/halo-dev/halo/blob/master/CONTRIBUTING.md>. 2. 请根据你解决问题的类型为 Pull Request 添加合适的标签。 2. Please label this pull request according to what type of issue you are addressing, especially if this is a release targeted pull request. 3. 请确保你已经添加并运行了适当的测试。 3. Ensure you have added or ran the appropriate tests for your PR. --> #### What type of PR is this? /kind bug /area core /milestone 2.0 <!-- 添加其中一个类别: Add one of the following kinds: /kind bug /kind cleanup /kind documentation /kind feature /kind improvement 适当添加其中一个或多个类别(可选): Optionally add one or more of the following kinds if applicable: /kind api-change /kind deprecation /kind failing-test /kind flake /kind regression --> #### What this PR does / why we need it: 修复文章 分类 标签的 slug 改变时,没有重新生成 permalink 的问题 #### Which issue(s) this PR fixes: <!-- PR 合并时自动关闭 issue。 Automatically closes linked issue when PR is merged. 用法:`Fixes #<issue 号>`,或者 `Fixes (粘贴 issue 完整链接)` Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`. --> Fixes # #### Special notes for your reviewer: /cc @halo-dev/sig-halo #### Does this PR introduce a user-facing change? <!-- 如果当前 Pull Request 的修改不会造成用户侧的任何变更,在 `release-note` 代码块儿中填写 `NONE`。 否则请填写用户侧能够理解的 Release Note。如果当前 Pull Request 包含破坏性更新(Break Change), Release Note 需要以 `action required` 开头。 If no, just write "NONE" in the release-note block below. If yes, a release note is required: Enter your extended release note in the block below. If the PR requires additional action from users switching to the new release, include the string "action required". --> ```release-note None ```pull/2392/head
parent
969fcde641
commit
e25a3d2232
|
@ -1,6 +1,30 @@
|
|||
package run.halo.app.content.permalinks;
|
||||
|
||||
import java.util.Objects;
|
||||
import run.halo.app.extension.GroupVersionKind;
|
||||
|
||||
/**
|
||||
* Slug can be modified, so it is not included in {@link #equals(Object)} and {@link #hashCode()}.
|
||||
*
|
||||
* @param gvk group version kind
|
||||
* @param name extension name
|
||||
* @param slug extension slug
|
||||
*/
|
||||
public record ExtensionLocator(GroupVersionKind gvk, String name, String slug) {
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ExtensionLocator locator = (ExtensionLocator) o;
|
||||
return gvk.equals(locator.gvk) && name.equals(locator.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(gvk, name);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package run.halo.app.core.extension.reconciler;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import run.halo.app.content.permalinks.CategoryPermalinkPolicy;
|
||||
import run.halo.app.core.extension.Category;
|
||||
import run.halo.app.extension.ExtensionClient;
|
||||
|
@ -13,7 +15,7 @@ import run.halo.app.infra.utils.JsonUtils;
|
|||
* @since 2.0.0
|
||||
*/
|
||||
public class CategoryReconciler implements Reconciler<Reconciler.Request> {
|
||||
|
||||
private static final String FINALIZER_NAME = "category-protection";
|
||||
private final ExtensionClient client;
|
||||
private final CategoryPermalinkPolicy categoryPermalinkPolicy;
|
||||
|
||||
|
@ -26,25 +28,67 @@ public class CategoryReconciler implements Reconciler<Reconciler.Request> {
|
|||
@Override
|
||||
public Result reconcile(Request request) {
|
||||
client.fetch(Category.class, request.name())
|
||||
.ifPresent(category -> {
|
||||
if (isDeleted(category)) {
|
||||
cleanUpResourcesAndRemoveFinalizer(request.name());
|
||||
return;
|
||||
}
|
||||
addFinalizerIfNecessary(category);
|
||||
|
||||
reconcileStatus(request.name());
|
||||
});
|
||||
return new Result(false, null);
|
||||
}
|
||||
|
||||
private void addFinalizerIfNecessary(Category oldCategory) {
|
||||
Set<String> finalizers = oldCategory.getMetadata().getFinalizers();
|
||||
if (finalizers != null && finalizers.contains(FINALIZER_NAME)) {
|
||||
return;
|
||||
}
|
||||
client.fetch(Category.class, oldCategory.getMetadata().getName())
|
||||
.ifPresent(category -> {
|
||||
Set<String> newFinalizers = category.getMetadata().getFinalizers();
|
||||
if (newFinalizers == null) {
|
||||
newFinalizers = new HashSet<>();
|
||||
category.getMetadata().setFinalizers(newFinalizers);
|
||||
}
|
||||
newFinalizers.add(FINALIZER_NAME);
|
||||
client.update(category);
|
||||
});
|
||||
}
|
||||
|
||||
private void cleanUpResources(Category category) {
|
||||
// remove permalink from permalink indexer
|
||||
categoryPermalinkPolicy.onPermalinkDelete(category);
|
||||
}
|
||||
|
||||
private void cleanUpResourcesAndRemoveFinalizer(String categoryName) {
|
||||
client.fetch(Category.class, categoryName).ifPresent(category -> {
|
||||
cleanUpResources(category);
|
||||
if (category.getMetadata().getFinalizers() != null) {
|
||||
category.getMetadata().getFinalizers().remove(FINALIZER_NAME);
|
||||
}
|
||||
client.update(category);
|
||||
});
|
||||
}
|
||||
|
||||
private void reconcileStatus(String name) {
|
||||
client.fetch(Category.class, name)
|
||||
.ifPresent(category -> {
|
||||
Category oldCategory = JsonUtils.deepCopy(category);
|
||||
reconcilePermalink(category);
|
||||
categoryPermalinkPolicy.onPermalinkDelete(oldCategory);
|
||||
|
||||
category.getStatusOrDefault()
|
||||
.setPermalink(categoryPermalinkPolicy.permalink(category));
|
||||
categoryPermalinkPolicy.onPermalinkAdd(category);
|
||||
|
||||
if (!oldCategory.equals(category)) {
|
||||
client.update(category);
|
||||
}
|
||||
});
|
||||
return new Result(false, null);
|
||||
}
|
||||
|
||||
private void reconcilePermalink(Category category) {
|
||||
category.getStatusOrDefault()
|
||||
.setPermalink(categoryPermalinkPolicy.permalink(category));
|
||||
|
||||
if (category.getMetadata().getDeletionTimestamp() != null) {
|
||||
categoryPermalinkPolicy.onPermalinkDelete(category);
|
||||
return;
|
||||
}
|
||||
categoryPermalinkPolicy.onPermalinkAdd(category);
|
||||
private boolean isDeleted(Category category) {
|
||||
return category.getMetadata().getDeletionTimestamp() != null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ import run.halo.app.infra.utils.JsonUtils;
|
|||
* @since 2.0.0
|
||||
*/
|
||||
public class PostReconciler implements Reconciler<Reconciler.Request> {
|
||||
private static final String FINALIZER_NAME = "post-protection";
|
||||
private final ExtensionClient client;
|
||||
private final ContentService contentService;
|
||||
private final PostPermalinkPolicy postPermalinkPolicy;
|
||||
|
@ -48,110 +49,152 @@ public class PostReconciler implements Reconciler<Reconciler.Request> {
|
|||
public Result reconcile(Request request) {
|
||||
client.fetch(Post.class, request.name())
|
||||
.ifPresent(post -> {
|
||||
Post oldPost = JsonUtils.deepCopy(post);
|
||||
|
||||
doReconcile(post);
|
||||
permalinkReconcile(post);
|
||||
|
||||
if (!oldPost.equals(post)) {
|
||||
client.update(post);
|
||||
if (isDeleted(post)) {
|
||||
cleanUpResourcesAndRemoveFinalizer(request.name());
|
||||
return;
|
||||
}
|
||||
addFinalizerIfNecessary(post);
|
||||
|
||||
reconcileMetadata(request.name());
|
||||
reconcileStatus(request.name());
|
||||
});
|
||||
return new Result(false, null);
|
||||
}
|
||||
|
||||
private void permalinkReconcile(Post post) {
|
||||
post.getStatusOrDefault()
|
||||
.setPermalink(postPermalinkPolicy.permalink(post));
|
||||
private void reconcileMetadata(String name) {
|
||||
client.fetch(Post.class, name).ifPresent(post -> {
|
||||
final Post oldPost = JsonUtils.deepCopy(post);
|
||||
Post.PostSpec spec = post.getSpec();
|
||||
|
||||
if (Objects.equals(true, post.getSpec().getDeleted())
|
||||
|| post.getMetadata().getDeletionTimestamp() != null
|
||||
|| Objects.equals(false, post.getSpec().getPublished())) {
|
||||
postPermalinkPolicy.onPermalinkDelete(post);
|
||||
return;
|
||||
}
|
||||
postPermalinkPolicy.onPermalinkAdd(post);
|
||||
// handle logic delete
|
||||
Map<String, String> labels = getLabelsOrDefault(post);
|
||||
if (Objects.equals(spec.getDeleted(), true)) {
|
||||
labels.put(Post.DELETED_LABEL, Boolean.TRUE.toString());
|
||||
} else {
|
||||
labels.put(Post.DELETED_LABEL, Boolean.FALSE.toString());
|
||||
}
|
||||
// synchronize some fields to labels to query
|
||||
labels.put(Post.PHASE_LABEL, post.getStatusOrDefault().getPhase());
|
||||
labels.put(Post.VISIBLE_LABEL,
|
||||
Objects.requireNonNullElse(spec.getVisible(), Post.VisibleEnum.PUBLIC).name());
|
||||
labels.put(Post.OWNER_LABEL, spec.getOwner());
|
||||
|
||||
if (!oldPost.equals(post)) {
|
||||
client.update(post);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void doReconcile(Post post) {
|
||||
String name = post.getMetadata().getName();
|
||||
Post.PostSpec spec = post.getSpec();
|
||||
Post.PostStatus status = post.getStatusOrDefault();
|
||||
if (status.getPhase() == null) {
|
||||
status.setPhase(Post.PostPhase.DRAFT.name());
|
||||
}
|
||||
private void reconcileStatus(String name) {
|
||||
client.fetch(Post.class, name).ifPresent(post -> {
|
||||
final Post oldPost = JsonUtils.deepCopy(post);
|
||||
postPermalinkPolicy.onPermalinkDelete(oldPost);
|
||||
|
||||
// handle excerpt
|
||||
Post.Excerpt excerpt = spec.getExcerpt();
|
||||
if (excerpt == null) {
|
||||
excerpt = new Post.Excerpt();
|
||||
excerpt.setAutoGenerate(true);
|
||||
spec.setExcerpt(excerpt);
|
||||
}
|
||||
post.getStatusOrDefault()
|
||||
.setPermalink(postPermalinkPolicy.permalink(post));
|
||||
if (isPublished(post)) {
|
||||
postPermalinkPolicy.onPermalinkAdd(post);
|
||||
}
|
||||
|
||||
if (excerpt.getAutoGenerate()) {
|
||||
contentService.getContent(spec.getHeadSnapshot())
|
||||
.subscribe(content -> {
|
||||
String contentRevised = content.content();
|
||||
status.setExcerpt(getExcerpt(contentRevised));
|
||||
});
|
||||
} else {
|
||||
status.setExcerpt(excerpt.getRaw());
|
||||
}
|
||||
|
||||
// handle contributors
|
||||
String headSnapshot = post.getSpec().getHeadSnapshot();
|
||||
contentService.listSnapshots(Snapshot.SubjectRef.of(Post.KIND, name))
|
||||
.collectList()
|
||||
.subscribe(snapshots -> {
|
||||
List<String> contributors = snapshots.stream()
|
||||
.map(snapshot -> {
|
||||
Set<String> usernames = snapshot.getSpec().getContributors();
|
||||
return Objects.requireNonNullElseGet(usernames,
|
||||
() -> new HashSet<String>());
|
||||
})
|
||||
.flatMap(Set::stream)
|
||||
.distinct()
|
||||
.sorted()
|
||||
.toList();
|
||||
status.setContributors(contributors);
|
||||
|
||||
// update in progress status
|
||||
snapshots.stream()
|
||||
.filter(snapshot -> snapshot.getMetadata().getName().equals(headSnapshot))
|
||||
.findAny()
|
||||
.ifPresent(snapshot -> {
|
||||
status.setInProgress(!isPublished(snapshot));
|
||||
Post.PostStatus status = post.getStatusOrDefault();
|
||||
if (status.getPhase() == null) {
|
||||
status.setPhase(Post.PostPhase.DRAFT.name());
|
||||
}
|
||||
Post.PostSpec spec = post.getSpec();
|
||||
// handle excerpt
|
||||
Post.Excerpt excerpt = spec.getExcerpt();
|
||||
if (excerpt == null) {
|
||||
excerpt = new Post.Excerpt();
|
||||
excerpt.setAutoGenerate(true);
|
||||
spec.setExcerpt(excerpt);
|
||||
}
|
||||
if (excerpt.getAutoGenerate()) {
|
||||
contentService.getContent(spec.getHeadSnapshot())
|
||||
.subscribe(content -> {
|
||||
String contentRevised = content.content();
|
||||
status.setExcerpt(getExcerpt(contentRevised));
|
||||
});
|
||||
} else {
|
||||
status.setExcerpt(excerpt.getRaw());
|
||||
}
|
||||
|
||||
// handle contributors
|
||||
String headSnapshot = post.getSpec().getHeadSnapshot();
|
||||
contentService.listSnapshots(Snapshot.SubjectRef.of(Post.KIND, name))
|
||||
.collectList()
|
||||
.subscribe(snapshots -> {
|
||||
List<String> contributors = snapshots.stream()
|
||||
.map(snapshot -> {
|
||||
Set<String> usernames = snapshot.getSpec().getContributors();
|
||||
return Objects.requireNonNullElseGet(usernames,
|
||||
() -> new HashSet<String>());
|
||||
})
|
||||
.flatMap(Set::stream)
|
||||
.distinct()
|
||||
.sorted()
|
||||
.toList();
|
||||
status.setContributors(contributors);
|
||||
|
||||
// update in progress status
|
||||
snapshots.stream()
|
||||
.filter(
|
||||
snapshot -> snapshot.getMetadata().getName().equals(headSnapshot))
|
||||
.findAny()
|
||||
.ifPresent(snapshot -> {
|
||||
status.setInProgress(!isPublished(snapshot));
|
||||
});
|
||||
});
|
||||
|
||||
// handle cancel publish,has released version and published is false and not handled
|
||||
if (StringUtils.isNotBlank(spec.getReleaseSnapshot())
|
||||
&& Objects.equals(false, spec.getPublished())
|
||||
&& !StringUtils.equals(status.getPhase(), Post.PostPhase.DRAFT.name())) {
|
||||
Condition condition = new Condition();
|
||||
condition.setType("CancelledPublish");
|
||||
condition.setStatus(ConditionStatus.TRUE);
|
||||
condition.setReason(condition.getType());
|
||||
condition.setMessage(StringUtils.EMPTY);
|
||||
condition.setLastTransitionTime(Instant.now());
|
||||
status.getConditionsOrDefault().add(condition);
|
||||
status.setPhase(Post.PostPhase.DRAFT.name());
|
||||
}
|
||||
|
||||
if (!oldPost.equals(post)) {
|
||||
client.update(post);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void addFinalizerIfNecessary(Post oldPost) {
|
||||
Set<String> finalizers = oldPost.getMetadata().getFinalizers();
|
||||
if (finalizers != null && finalizers.contains(FINALIZER_NAME)) {
|
||||
return;
|
||||
}
|
||||
client.fetch(Post.class, oldPost.getMetadata().getName())
|
||||
.ifPresent(post -> {
|
||||
Set<String> newFinalizers = post.getMetadata().getFinalizers();
|
||||
if (newFinalizers == null) {
|
||||
newFinalizers = new HashSet<>();
|
||||
post.getMetadata().setFinalizers(newFinalizers);
|
||||
}
|
||||
newFinalizers.add(FINALIZER_NAME);
|
||||
client.update(post);
|
||||
});
|
||||
}
|
||||
|
||||
// handle cancel publish,has released version and published is false and not handled
|
||||
if (StringUtils.isNotBlank(spec.getReleaseSnapshot())
|
||||
&& Objects.equals(false, spec.getPublished())
|
||||
&& !StringUtils.equals(status.getPhase(), Post.PostPhase.DRAFT.name())) {
|
||||
Condition condition = new Condition();
|
||||
condition.setType("CancelledPublish");
|
||||
condition.setStatus(ConditionStatus.TRUE);
|
||||
condition.setReason(condition.getType());
|
||||
condition.setMessage(StringUtils.EMPTY);
|
||||
condition.setLastTransitionTime(Instant.now());
|
||||
status.getConditionsOrDefault().add(condition);
|
||||
status.setPhase(Post.PostPhase.DRAFT.name());
|
||||
}
|
||||
private void cleanUpResourcesAndRemoveFinalizer(String postName) {
|
||||
client.fetch(Post.class, postName).ifPresent(post -> {
|
||||
cleanUpResources(post);
|
||||
if (post.getMetadata().getFinalizers() != null) {
|
||||
post.getMetadata().getFinalizers().remove(FINALIZER_NAME);
|
||||
}
|
||||
client.update(post);
|
||||
});
|
||||
}
|
||||
|
||||
// handle logic delete
|
||||
Map<String, String> labels = getLabelsOrDefault(post);
|
||||
if (isDeleted(post)) {
|
||||
labels.put(Post.DELETED_LABEL, Boolean.TRUE.toString());
|
||||
// TODO do more about logic delete such as remove router
|
||||
} else {
|
||||
labels.put(Post.DELETED_LABEL, Boolean.FALSE.toString());
|
||||
}
|
||||
// synchronize some fields to labels to query
|
||||
labels.put(Post.PHASE_LABEL, status.getPhase());
|
||||
labels.put(Post.VISIBLE_LABEL,
|
||||
Objects.requireNonNullElse(spec.getVisible(), Post.VisibleEnum.PUBLIC).name());
|
||||
labels.put(Post.OWNER_LABEL, spec.getOwner());
|
||||
private void cleanUpResources(Post post) {
|
||||
// remove permalink from permalink indexer
|
||||
postPermalinkPolicy.onPermalinkDelete(post);
|
||||
}
|
||||
|
||||
private Map<String, String> getLabelsOrDefault(Post post) {
|
||||
|
@ -175,6 +218,10 @@ public class PostReconciler implements Reconciler<Reconciler.Request> {
|
|||
return snapshot.getSpec().getPublishTime() != null;
|
||||
}
|
||||
|
||||
private boolean isPublished(Post post) {
|
||||
return Objects.equals(true, post.getSpec().getPublished());
|
||||
}
|
||||
|
||||
private boolean isDeleted(Post post) {
|
||||
return Objects.equals(true, post.getSpec().getDeleted())
|
||||
|| post.getMetadata().getDeletionTimestamp() != null;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package run.halo.app.core.extension.reconciler;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import run.halo.app.content.permalinks.TagPermalinkPolicy;
|
||||
import run.halo.app.core.extension.Tag;
|
||||
import run.halo.app.extension.ExtensionClient;
|
||||
|
@ -13,6 +15,7 @@ import run.halo.app.infra.utils.JsonUtils;
|
|||
* @since 2.0.0
|
||||
*/
|
||||
public class TagReconciler implements Reconciler<Reconciler.Request> {
|
||||
private static final String FINALIZER_NAME = "tag-protection";
|
||||
private final ExtensionClient client;
|
||||
private final TagPermalinkPolicy tagPermalinkPolicy;
|
||||
|
||||
|
@ -25,25 +28,66 @@ public class TagReconciler implements Reconciler<Reconciler.Request> {
|
|||
public Result reconcile(Request request) {
|
||||
client.fetch(Tag.class, request.name())
|
||||
.ifPresent(tag -> {
|
||||
Tag oldTag = JsonUtils.deepCopy(tag);
|
||||
|
||||
this.reconcilePermalink(tag);
|
||||
|
||||
if (!tag.equals(oldTag)) {
|
||||
client.update(tag);
|
||||
if (isDeleted(tag)) {
|
||||
cleanUpResourcesAndRemoveFinalizer(request.name());
|
||||
return;
|
||||
}
|
||||
addFinalizerIfNecessary(tag);
|
||||
|
||||
this.reconcileStatus(request.name());
|
||||
});
|
||||
return new Result(false, null);
|
||||
}
|
||||
|
||||
private void reconcilePermalink(Tag tag) {
|
||||
tag.getStatusOrDefault()
|
||||
.setPermalink(tagPermalinkPolicy.permalink(tag));
|
||||
private void cleanUpResources(Tag tag) {
|
||||
// remove permalink from permalink indexer
|
||||
tagPermalinkPolicy.onPermalinkDelete(tag);
|
||||
}
|
||||
|
||||
if (tag.getMetadata().getDeletionTimestamp() != null) {
|
||||
tagPermalinkPolicy.onPermalinkDelete(tag);
|
||||
private void addFinalizerIfNecessary(Tag oldTag) {
|
||||
Set<String> finalizers = oldTag.getMetadata().getFinalizers();
|
||||
if (finalizers != null && finalizers.contains(FINALIZER_NAME)) {
|
||||
return;
|
||||
}
|
||||
tagPermalinkPolicy.onPermalinkAdd(tag);
|
||||
client.fetch(Tag.class, oldTag.getMetadata().getName())
|
||||
.ifPresent(tag -> {
|
||||
Set<String> newFinalizers = tag.getMetadata().getFinalizers();
|
||||
if (newFinalizers == null) {
|
||||
newFinalizers = new HashSet<>();
|
||||
tag.getMetadata().setFinalizers(newFinalizers);
|
||||
}
|
||||
newFinalizers.add(FINALIZER_NAME);
|
||||
client.update(tag);
|
||||
});
|
||||
}
|
||||
|
||||
private void cleanUpResourcesAndRemoveFinalizer(String tagName) {
|
||||
client.fetch(Tag.class, tagName).ifPresent(tag -> {
|
||||
cleanUpResources(tag);
|
||||
if (tag.getMetadata().getFinalizers() != null) {
|
||||
tag.getMetadata().getFinalizers().remove(FINALIZER_NAME);
|
||||
}
|
||||
client.update(tag);
|
||||
});
|
||||
}
|
||||
|
||||
private void reconcileStatus(String tagName) {
|
||||
client.fetch(Tag.class, tagName)
|
||||
.ifPresent(tag -> {
|
||||
Tag oldTag = JsonUtils.deepCopy(tag);
|
||||
tagPermalinkPolicy.onPermalinkDelete(oldTag);
|
||||
|
||||
tag.getStatusOrDefault()
|
||||
.setPermalink(tagPermalinkPolicy.permalink(tag));
|
||||
tagPermalinkPolicy.onPermalinkAdd(tag);
|
||||
|
||||
if (!oldTag.equals(tag)) {
|
||||
client.update(tag);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isDeleted(Tag tag) {
|
||||
return tag.getMetadata().getDeletionTimestamp() != null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package run.halo.app.theme.router;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
@ -52,6 +53,9 @@ public class PermalinkIndexer {
|
|||
List<String> permalinks = permalinkLookup.get(locator.gvk());
|
||||
if (permalinks != null) {
|
||||
permalinks.remove(permalink);
|
||||
if (permalinks.isEmpty()) {
|
||||
permalinkLookup.remove(locator.gvk());
|
||||
}
|
||||
}
|
||||
permalinkLocatorMap.remove(permalink);
|
||||
} finally {
|
||||
|
@ -83,7 +87,7 @@ public class PermalinkIndexer {
|
|||
public List<String> getPermalinks(GroupVersionKind gvk) {
|
||||
readWriteLock.readLock().lock();
|
||||
try {
|
||||
return permalinkLookup.get(gvk);
|
||||
return Objects.requireNonNullElse(permalinkLookup.get(gvk), List.of());
|
||||
} finally {
|
||||
readWriteLock.readLock().unlock();
|
||||
}
|
||||
|
@ -196,7 +200,7 @@ public class PermalinkIndexer {
|
|||
|
||||
@EventListener(PermalinkIndexDeleteCommand.class)
|
||||
public void onPermalinkDelete(PermalinkIndexDeleteCommand deleteCommand) {
|
||||
register(deleteCommand.getLocator(), deleteCommand.getPermalink());
|
||||
remove(deleteCommand.getLocator(), deleteCommand.getPermalink());
|
||||
}
|
||||
|
||||
@EventListener(PermalinkIndexUpdateCommand.class)
|
||||
|
|
|
@ -10,10 +10,10 @@ import static org.mockito.Mockito.when;
|
|||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
@ -43,17 +43,14 @@ class PostReconcilerTest {
|
|||
@Mock
|
||||
private PostPermalinkPolicy postPermalinkPolicy;
|
||||
|
||||
@InjectMocks
|
||||
private PostReconciler postReconciler;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
postReconciler = new PostReconciler(client, contentService, postPermalinkPolicy);
|
||||
}
|
||||
|
||||
@Test
|
||||
void reconcile() {
|
||||
String name = "post-A";
|
||||
Post post = TestPost.postV1();
|
||||
post.getSpec().setPublished(false);
|
||||
post.getSpec().setHeadSnapshot("post-A-head-snapshot");
|
||||
when(client.fetch(eq(Post.class), eq(name)))
|
||||
.thenReturn(Optional.of(post));
|
||||
|
@ -72,7 +69,12 @@ class PostReconcilerTest {
|
|||
ArgumentCaptor<Post> captor = ArgumentCaptor.forClass(Post.class);
|
||||
postReconciler.reconcile(new Reconciler.Request(name));
|
||||
|
||||
verify(client, times(1)).update(captor.capture());
|
||||
verify(client, times(3)).update(captor.capture());
|
||||
|
||||
verify(postPermalinkPolicy, times(1)).permalink(any());
|
||||
verify(postPermalinkPolicy, times(0)).onPermalinkAdd(any());
|
||||
verify(postPermalinkPolicy, times(1)).onPermalinkDelete(any());
|
||||
verify(postPermalinkPolicy, times(0)).onPermalinkUpdate(any());
|
||||
|
||||
Post value = captor.getValue();
|
||||
assertThat(value.getStatus().getExcerpt()).isEqualTo("hello world");
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
package run.halo.app.core.extension.reconciler;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import run.halo.app.content.permalinks.TagPermalinkPolicy;
|
||||
import run.halo.app.core.extension.Tag;
|
||||
import run.halo.app.extension.ExtensionClient;
|
||||
import run.halo.app.extension.Metadata;
|
||||
|
||||
/**
|
||||
* Tests for {@link TagReconciler}.
|
||||
*
|
||||
* @author guqing
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class TagReconcilerTest {
|
||||
@Mock
|
||||
private ExtensionClient client;
|
||||
|
||||
@Mock
|
||||
private TagPermalinkPolicy tagPermalinkPolicy;
|
||||
|
||||
@InjectMocks
|
||||
private TagReconciler tagReconciler;
|
||||
|
||||
@Test
|
||||
void reconcile() {
|
||||
Tag tag = tag();
|
||||
when(client.fetch(eq(Tag.class), eq("fake-tag")))
|
||||
.thenReturn(Optional.of(tag));
|
||||
when(tagPermalinkPolicy.permalink(any()))
|
||||
.thenAnswer(arg -> "/tags/" + tag.getSpec().getSlug());
|
||||
ArgumentCaptor<Tag> captor = ArgumentCaptor.forClass(Tag.class);
|
||||
|
||||
tagReconciler.reconcile(new TagReconciler.Request("fake-tag"));
|
||||
|
||||
verify(client, times(2)).update(captor.capture());
|
||||
verify(tagPermalinkPolicy, times(1)).onPermalinkAdd(any());
|
||||
verify(tagPermalinkPolicy, times(1)).onPermalinkDelete(any());
|
||||
Tag capture = captor.getValue();
|
||||
assertThat(capture.getStatus().getPermalink()).isEqualTo("/tags/fake-slug");
|
||||
|
||||
// change slug
|
||||
tag.getSpec().setSlug("new-slug");
|
||||
tagReconciler.reconcile(new TagReconciler.Request("fake-tag"));
|
||||
verify(client, times(3)).update(captor.capture());
|
||||
verify(tagPermalinkPolicy, times(2)).onPermalinkAdd(any());
|
||||
verify(tagPermalinkPolicy, times(2)).onPermalinkDelete(any());
|
||||
assertThat(capture.getStatus().getPermalink()).isEqualTo("/tags/new-slug");
|
||||
}
|
||||
|
||||
@Test
|
||||
void reconcileDelete() {
|
||||
Tag tag = tag();
|
||||
tag.getMetadata().setDeletionTimestamp(Instant.now());
|
||||
when(client.fetch(eq(Tag.class), eq("fake-tag")))
|
||||
.thenReturn(Optional.of(tag));
|
||||
ArgumentCaptor<Tag> captor = ArgumentCaptor.forClass(Tag.class);
|
||||
|
||||
tagReconciler.reconcile(new TagReconciler.Request("fake-tag"));
|
||||
verify(client, times(1)).update(captor.capture());
|
||||
verify(tagPermalinkPolicy, times(0)).onPermalinkAdd(any());
|
||||
verify(tagPermalinkPolicy, times(1)).onPermalinkDelete(any());
|
||||
verify(tagPermalinkPolicy, times(0)).permalink(any());
|
||||
}
|
||||
|
||||
Tag tag() {
|
||||
Tag tag = new Tag();
|
||||
tag.setMetadata(new Metadata());
|
||||
tag.getMetadata().setName("fake-tag");
|
||||
|
||||
tag.setSpec(new Tag.TagSpec());
|
||||
tag.getSpec().setSlug("fake-slug");
|
||||
|
||||
tag.setStatus(new Tag.TagStatus());
|
||||
return tag;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue