diff --git a/src/main/java/run/halo/app/core/extension/content/Post.java b/src/main/java/run/halo/app/core/extension/content/Post.java index 09fc97235..a057c506c 100644 --- a/src/main/java/run/halo/app/core/extension/content/Post.java +++ b/src/main/java/run/halo/app/core/extension/content/Post.java @@ -150,6 +150,8 @@ public class Post extends AbstractExtension { private List contributors; + private Instant lastModifyTime; + @JsonIgnore public ConditionList getConditionsOrDefault() { if (this.conditions == null) { diff --git a/src/main/java/run/halo/app/core/extension/reconciler/PostReconciler.java b/src/main/java/run/halo/app/core/extension/reconciler/PostReconciler.java index 91f7edf92..403039aaf 100644 --- a/src/main/java/run/halo/app/core/extension/reconciler/PostReconciler.java +++ b/src/main/java/run/halo/app/core/extension/reconciler/PostReconciler.java @@ -5,6 +5,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; import lombok.AllArgsConstructor; import org.apache.commons.lang3.StringUtils; @@ -13,7 +14,6 @@ import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; import org.springframework.util.Assert; import run.halo.app.content.ContentService; -import run.halo.app.content.PostService; import run.halo.app.content.permalinks.PostPermalinkPolicy; import run.halo.app.core.extension.content.Comment; import run.halo.app.core.extension.content.Post; @@ -53,7 +53,6 @@ public class PostReconciler implements Reconciler { private static final String FINALIZER_NAME = "post-protection"; private final ExtensionClient client; private final ContentService contentService; - private final PostService postService; private final PostPermalinkPolicy postPermalinkPolicy; private final CounterService counterService; @@ -126,9 +125,9 @@ public class PostReconciler implements Reconciler { Post.PostStatus status = post.getStatusOrDefault(); // validate release snapshot - boolean present = client.fetch(Snapshot.class, releaseSnapshot) - .isPresent(); - if (!present) { + Optional releasedSnapshotOpt = + client.fetch(Snapshot.class, releaseSnapshot); + if (releasedSnapshotOpt.isEmpty()) { Condition condition = Condition.builder() .type(Post.PostPhase.FAILED.name()) .reason("SnapshotNotFound") @@ -159,6 +158,9 @@ public class PostReconciler implements Reconciler { post.getSpec().setPublishTime(Instant.now()); } + // populate lastModifyTime + status.setLastModifyTime(releasedSnapshotOpt.get().getSpec().getLastModifyTime()); + client.update(post); applicationContext.publishEvent(new PostPublishedEvent(this, name)); }); @@ -301,6 +303,12 @@ public class PostReconciler implements Reconciler { !StringUtils.equals(headSnapshot, post.getSpec().getReleaseSnapshot())); }); + if (post.isPublished() && status.getLastModifyTime() == null) { + client.fetch(Snapshot.class, post.getSpec().getReleaseSnapshot()) + .ifPresent(releasedSnapshot -> + status.setLastModifyTime(releasedSnapshot.getSpec().getLastModifyTime())); + } + if (!oldPost.equals(post)) { client.update(post); } diff --git a/src/main/java/run/halo/app/core/extension/reconciler/SinglePageReconciler.java b/src/main/java/run/halo/app/core/extension/reconciler/SinglePageReconciler.java index f8c4ab566..26ede9ead 100644 --- a/src/main/java/run/halo/app/core/extension/reconciler/SinglePageReconciler.java +++ b/src/main/java/run/halo/app/core/extension/reconciler/SinglePageReconciler.java @@ -8,6 +8,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -127,9 +128,9 @@ public class SinglePageReconciler implements Reconciler { SinglePage.SinglePageStatus status = page.getStatusOrDefault(); // validate release snapshot - boolean present = client.fetch(Snapshot.class, releaseSnapshot) - .isPresent(); - if (!present) { + Optional releasedSnapshotOpt = + client.fetch(Snapshot.class, releaseSnapshot); + if (releasedSnapshotOpt.isEmpty()) { Condition condition = Condition.builder() .type(Post.PostPhase.FAILED.name()) .reason("SnapshotNotFound") @@ -161,6 +162,9 @@ public class SinglePageReconciler implements Reconciler { page.getSpec().setPublishTime(Instant.now()); } + // populate lastModifyTime + status.setLastModifyTime(releasedSnapshotOpt.get().getSpec().getLastModifyTime()); + client.update(page); }); } @@ -365,6 +369,12 @@ public class SinglePageReconciler implements Reconciler { status.setInProgress(!StringUtils.equals(releaseSnapshot, headSnapshot)); }); + if (singlePage.isPublished() && status.getLastModifyTime() == null) { + client.fetch(Snapshot.class, singlePage.getSpec().getReleaseSnapshot()) + .ifPresent(releasedSnapshot -> + status.setLastModifyTime(releasedSnapshot.getSpec().getLastModifyTime())); + } + if (!oldPage.equals(singlePage)) { client.update(singlePage); } diff --git a/src/test/java/run/halo/app/core/extension/reconciler/PostReconcilerTest.java b/src/test/java/run/halo/app/core/extension/reconciler/PostReconcilerTest.java index a85de7685..1aa342bc0 100644 --- a/src/test/java/run/halo/app/core/extension/reconciler/PostReconcilerTest.java +++ b/src/test/java/run/halo/app/core/extension/reconciler/PostReconcilerTest.java @@ -7,16 +7,19 @@ 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.HashMap; import java.util.List; import java.util.Optional; import java.util.Set; +import org.junit.jupiter.api.Nested; 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 org.springframework.context.ApplicationContext; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import run.halo.app.content.ContentService; @@ -26,6 +29,7 @@ import run.halo.app.content.TestPost; import run.halo.app.content.permalinks.PostPermalinkPolicy; import run.halo.app.core.extension.content.Post; import run.halo.app.core.extension.content.Snapshot; +import run.halo.app.event.post.PostPublishedEvent; import run.halo.app.extension.ExtensionClient; import run.halo.app.extension.controller.Reconciler; @@ -48,6 +52,9 @@ class PostReconcilerTest { @Mock private PostService postService; + @Mock + private ApplicationContext applicationContext; + @InjectMocks private PostReconciler postReconciler; @@ -119,4 +126,68 @@ class PostReconcilerTest { Post value = captor.getValue(); assertThat(value.getStatus().getExcerpt()).isEqualTo("hello world"); } + + @Nested + class LastModifyTimeTest { + @Test + void reconcileLastModifyTimeWhenPostIsPublished() { + String name = "post-A"; + Post post = TestPost.postV1(); + post.getSpec().setPublish(true); + post.getSpec().setHeadSnapshot("post-A-head-snapshot"); + post.getSpec().setReleaseSnapshot("post-fake-released-snapshot"); + when(client.fetch(eq(Post.class), eq(name))) + .thenReturn(Optional.of(post)); + when(contentService.getContent(eq(post.getSpec().getReleaseSnapshot()))) + .thenReturn(Mono.just(ContentWrapper.builder() + .snapshotName(post.getSpec().getHeadSnapshot()) + .raw("hello world") + .content("

hello world

") + .rawType("markdown") + .build())); + Instant lastModifyTime = Instant.now(); + Snapshot snapshotV2 = TestPost.snapshotV2(); + snapshotV2.getSpec().setLastModifyTime(lastModifyTime); + when(client.fetch(eq(Snapshot.class), eq(post.getSpec().getReleaseSnapshot()))) + .thenReturn(Optional.of(snapshotV2)); + + when(contentService.listSnapshots(any())) + .thenReturn(Flux.empty()); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Post.class); + postReconciler.reconcile(new Reconciler.Request(name)); + + verify(client, times(4)).update(captor.capture()); + Post value = captor.getValue(); + assertThat(value.getStatus().getLastModifyTime()).isEqualTo(lastModifyTime); + verify(applicationContext).publishEvent(any(PostPublishedEvent.class)); + } + + @Test + void reconcileLastModifyTimeWhenPostIsNotPublished() { + String name = "post-A"; + Post post = TestPost.postV1(); + post.getSpec().setPublish(false); + post.getSpec().setHeadSnapshot("post-A-head-snapshot"); + when(client.fetch(eq(Post.class), eq(name))) + .thenReturn(Optional.of(post)); + when(contentService.getContent(eq(post.getSpec().getReleaseSnapshot()))) + .thenReturn(Mono.just(ContentWrapper.builder() + .snapshotName(post.getSpec().getHeadSnapshot()) + .raw("hello world") + .content("

hello world

") + .rawType("markdown") + .build())); + + when(contentService.listSnapshots(any())) + .thenReturn(Flux.empty()); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Post.class); + postReconciler.reconcile(new Reconciler.Request(name)); + + verify(client, times(3)).update(captor.capture()); + Post value = captor.getValue(); + assertThat(value.getStatus().getLastModifyTime()).isNull(); + } + } } \ No newline at end of file diff --git a/src/test/java/run/halo/app/core/extension/reconciler/SinglePageReconcilerTest.java b/src/test/java/run/halo/app/core/extension/reconciler/SinglePageReconcilerTest.java index 876350af1..27cb2bb99 100644 --- a/src/test/java/run/halo/app/core/extension/reconciler/SinglePageReconcilerTest.java +++ b/src/test/java/run/halo/app/core/extension/reconciler/SinglePageReconcilerTest.java @@ -10,9 +10,11 @@ import static org.mockito.Mockito.when; import static run.halo.app.content.TestPost.snapshotV1; import java.net.URI; +import java.time.Instant; import java.util.List; import java.util.Optional; import java.util.Set; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; @@ -123,6 +125,74 @@ class SinglePageReconcilerTest { assertThat(permalink).isEqualTo("http://example.com/%E4%B8%AD%E6%96%87%20slug"); } + @Nested + class LastModifyTimeTest { + @Test + void reconcileLastModifyTimeWhenPageIsPublished() { + String name = "page-A"; + when(externalUrlSupplier.get()).thenReturn(URI.create("")); + + SinglePage page = pageV1(); + page.getSpec().setPublish(true); + page.getSpec().setHeadSnapshot("page-A-head-snapshot"); + page.getSpec().setReleaseSnapshot("page-fake-released-snapshot"); + when(client.fetch(eq(SinglePage.class), eq(name))) + .thenReturn(Optional.of(page)); + when(contentService.getContent(eq(page.getSpec().getHeadSnapshot()))) + .thenReturn(Mono.just(ContentWrapper.builder() + .snapshotName(page.getSpec().getHeadSnapshot()) + .raw("hello world") + .content("

hello world

") + .rawType("markdown") + .build()) + ); + Instant lastModifyTime = Instant.now(); + Snapshot snapshotV2 = TestPost.snapshotV2(); + snapshotV2.getSpec().setLastModifyTime(lastModifyTime); + when(client.fetch(eq(Snapshot.class), eq(page.getSpec().getReleaseSnapshot()))) + .thenReturn(Optional.of(snapshotV2)); + + when(contentService.listSnapshots(any())) + .thenReturn(Flux.empty()); + + ArgumentCaptor captor = ArgumentCaptor.forClass(SinglePage.class); + singlePageReconciler.reconcile(new Reconciler.Request(name)); + + verify(client, times(4)).update(captor.capture()); + SinglePage value = captor.getValue(); + assertThat(value.getStatus().getLastModifyTime()).isEqualTo(lastModifyTime); + } + + @Test + void reconcileLastModifyTimeWhenPageIsNotPublished() { + String name = "page-A"; + when(externalUrlSupplier.get()).thenReturn(URI.create("")); + + SinglePage page = pageV1(); + page.getSpec().setPublish(false); + when(client.fetch(eq(SinglePage.class), eq(name))) + .thenReturn(Optional.of(page)); + when(contentService.getContent(eq(page.getSpec().getHeadSnapshot()))) + .thenReturn(Mono.just(ContentWrapper.builder() + .snapshotName(page.getSpec().getHeadSnapshot()) + .raw("hello world") + .content("

hello world

") + .rawType("markdown") + .build()) + ); + + when(contentService.listSnapshots(any())) + .thenReturn(Flux.empty()); + + ArgumentCaptor captor = ArgumentCaptor.forClass(SinglePage.class); + singlePageReconciler.reconcile(new Reconciler.Request(name)); + + verify(client, times(3)).update(captor.capture()); + SinglePage value = captor.getValue(); + assertThat(value.getStatus().getLastModifyTime()).isNull(); + } + } + public static SinglePage pageV1() { SinglePage page = new SinglePage(); page.setKind(Post.KIND);