From c0986a4b4cd24c2f219259a806c6e5fa5f333728 Mon Sep 17 00:00:00 2001 From: guqing <38999863+guqing@users.noreply.github.com> Date: Tue, 1 Nov 2022 23:22:16 +0800 Subject: [PATCH] refactor: permalink automatically appends external url if it is configured (#2641) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #### What type of PR is this? /kind improvement /area core /milestone 2.0 #### What this PR does / why we need it: 如果配置了 halo.externalUrl 则 permalink 生成时会自动加上 external url #### Which issue(s) this PR fixes: Fixes # #### Special notes for your reviewer: how to test it? 1. 配置 `halo.externalUrl` 并启动 2. 创建几篇文章、标签、分类、自定义页面 3. console 端能看到访问路径自动带上了 external url 并且通过访问路径能正确访问到相应资源 /cc @halo-dev/sig-halo #### Does this PR introduce a user-facing change? ```release-note 文章/分类/标签的访问路径自动追加外部访问地址 ``` --- .../app/config/ExtensionConfiguration.java | 5 ++-- .../permalinks/CategoryPermalinkPolicy.java | 16 ++++++++-- .../permalinks/PostPermalinkPolicy.java | 17 +++++++++-- .../permalinks/TagPermalinkPolicy.java | 14 +++++++-- .../reconciler/SinglePageReconciler.java | 20 ++++++++----- .../CategoryPermalinkPolicyTest.java | 19 +++++++++++- .../permalinks/PostPermalinkPolicyTest.java | 30 ++++++++++++++++++- .../permalinks/TagPermalinkPolicyTest.java | 19 +++++++++++- .../reconciler/SinglePageReconcilerTest.java | 27 ++++++++++++++++- .../finders/impl/PostFinderImplTest.java | 1 - 10 files changed, 147 insertions(+), 21 deletions(-) diff --git a/src/main/java/run/halo/app/config/ExtensionConfiguration.java b/src/main/java/run/halo/app/config/ExtensionConfiguration.java index ee7a32d43..e0c3bdd49 100644 --- a/src/main/java/run/halo/app/config/ExtensionConfiguration.java +++ b/src/main/java/run/halo/app/config/ExtensionConfiguration.java @@ -197,10 +197,11 @@ public class ExtensionConfiguration { @Bean Controller singlePageController(ExtensionClient client, ContentService contentService, - ApplicationContext applicationContext, CounterService counterService) { + ApplicationContext applicationContext, CounterService counterService, + ExternalUrlSupplier externalUrlSupplier) { return new ControllerBuilder("single-page-controller", client) .reconciler(new SinglePageReconciler(client, contentService, - applicationContext, counterService) + applicationContext, counterService, externalUrlSupplier) ) .extension(new SinglePage()) .build(); diff --git a/src/main/java/run/halo/app/content/permalinks/CategoryPermalinkPolicy.java b/src/main/java/run/halo/app/content/permalinks/CategoryPermalinkPolicy.java index 619d6c89e..db3a2d463 100644 --- a/src/main/java/run/halo/app/content/permalinks/CategoryPermalinkPolicy.java +++ b/src/main/java/run/halo/app/content/permalinks/CategoryPermalinkPolicy.java @@ -1,9 +1,13 @@ package run.halo.app.content.permalinks; +import static org.springframework.web.util.UriUtils.encode; + +import java.nio.charset.StandardCharsets; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; import run.halo.app.core.extension.Category; import run.halo.app.extension.GroupVersionKind; +import run.halo.app.infra.ExternalUrlSupplier; import run.halo.app.infra.utils.PathUtils; import run.halo.app.theme.DefaultTemplateEnum; import run.halo.app.theme.router.PermalinkIndexAddCommand; @@ -23,15 +27,23 @@ public class CategoryPermalinkPolicy private final ApplicationContext applicationContext; private final PermalinkPatternProvider permalinkPatternProvider; + private final ExternalUrlSupplier externalUrlSupplier; + public CategoryPermalinkPolicy(ApplicationContext applicationContext, - PermalinkPatternProvider permalinkPatternProvider) { + PermalinkPatternProvider permalinkPatternProvider, + ExternalUrlSupplier externalUrlSupplier) { this.applicationContext = applicationContext; this.permalinkPatternProvider = permalinkPatternProvider; + this.externalUrlSupplier = externalUrlSupplier; } @Override public String permalink(Category category) { - return PathUtils.combinePath(pattern(), category.getSpec().getSlug()); + String slug = encode(category.getSpec().getSlug(), StandardCharsets.UTF_8); + String path = PathUtils.combinePath(pattern(), slug); + return externalUrlSupplier.get() + .resolve(path) + .normalize().toString(); } @Override diff --git a/src/main/java/run/halo/app/content/permalinks/PostPermalinkPolicy.java b/src/main/java/run/halo/app/content/permalinks/PostPermalinkPolicy.java index 0df955849..cc0d265e2 100644 --- a/src/main/java/run/halo/app/content/permalinks/PostPermalinkPolicy.java +++ b/src/main/java/run/halo/app/content/permalinks/PostPermalinkPolicy.java @@ -1,5 +1,8 @@ package run.halo.app.content.permalinks; +import static org.springframework.web.util.UriUtils.encode; + +import java.nio.charset.StandardCharsets; import java.text.DecimalFormat; import java.text.NumberFormat; import java.time.Instant; @@ -11,6 +14,7 @@ import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; import run.halo.app.core.extension.Post; import run.halo.app.extension.GroupVersionKind; +import run.halo.app.infra.ExternalUrlSupplier; import run.halo.app.infra.utils.PathUtils; import run.halo.app.theme.DefaultTemplateEnum; import run.halo.app.theme.router.PermalinkIndexAddCommand; @@ -30,11 +34,13 @@ public class PostPermalinkPolicy implements PermalinkPolicy, PermalinkWatc private final PermalinkPatternProvider permalinkPatternProvider; private final ApplicationContext applicationContext; + private final ExternalUrlSupplier externalUrlSupplier; public PostPermalinkPolicy(PermalinkPatternProvider permalinkPatternProvider, - ApplicationContext applicationContext) { + ApplicationContext applicationContext, ExternalUrlSupplier externalUrlSupplier) { this.permalinkPatternProvider = permalinkPatternProvider; this.applicationContext = applicationContext; + this.externalUrlSupplier = externalUrlSupplier; } @Override @@ -85,12 +91,17 @@ public class PostPermalinkPolicy implements PermalinkPolicy, PermalinkWatc ZonedDateTime zonedDateTime = archiveTime.atZone(ZoneId.systemDefault()); Properties properties = new Properties(); properties.put("name", post.getMetadata().getName()); - properties.put("slug", post.getSpec().getSlug()); + properties.put("slug", encode(post.getSpec().getSlug(), StandardCharsets.UTF_8)); properties.put("year", String.valueOf(zonedDateTime.getYear())); properties.put("month", NUMBER_FORMAT.format(zonedDateTime.getMonthValue())); properties.put("day", NUMBER_FORMAT.format(zonedDateTime.getDayOfMonth())); String simplifiedPattern = PathUtils.simplifyPathPattern(pattern); - return PROPERTY_PLACEHOLDER_HELPER.replacePlaceholders(simplifiedPattern, properties); + String permalink = + PROPERTY_PLACEHOLDER_HELPER.replacePlaceholders(simplifiedPattern, properties); + return externalUrlSupplier.get() + .resolve(permalink) + .normalize() + .toString(); } } diff --git a/src/main/java/run/halo/app/content/permalinks/TagPermalinkPolicy.java b/src/main/java/run/halo/app/content/permalinks/TagPermalinkPolicy.java index 9cf816550..a0946ffd4 100644 --- a/src/main/java/run/halo/app/content/permalinks/TagPermalinkPolicy.java +++ b/src/main/java/run/halo/app/content/permalinks/TagPermalinkPolicy.java @@ -1,9 +1,12 @@ package run.halo.app.content.permalinks; +import java.nio.charset.StandardCharsets; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; +import org.springframework.web.util.UriUtils; import run.halo.app.core.extension.Tag; import run.halo.app.extension.GroupVersionKind; +import run.halo.app.infra.ExternalUrlSupplier; import run.halo.app.infra.utils.PathUtils; import run.halo.app.theme.DefaultTemplateEnum; import run.halo.app.theme.router.PermalinkIndexAddCommand; @@ -22,15 +25,22 @@ public class TagPermalinkPolicy implements PermalinkPolicy, PermalinkWatch< private final PermalinkPatternProvider permalinkPatternProvider; private final ApplicationContext applicationContext; + private final ExternalUrlSupplier externalUrlSupplier; + public TagPermalinkPolicy(PermalinkPatternProvider permalinkPatternProvider, - ApplicationContext applicationContext) { + ApplicationContext applicationContext, ExternalUrlSupplier externalUrlSupplier) { this.permalinkPatternProvider = permalinkPatternProvider; this.applicationContext = applicationContext; + this.externalUrlSupplier = externalUrlSupplier; } @Override public String permalink(Tag tag) { - return PathUtils.combinePath(pattern(), tag.getSpec().getSlug()); + String slug = UriUtils.encode(tag.getSpec().getSlug(), StandardCharsets.UTF_8); + String path = PathUtils.combinePath(pattern(), slug); + return externalUrlSupplier.get() + .resolve(path) + .normalize().toString(); } @Override 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 744f76712..981c01145 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 @@ -27,8 +27,8 @@ import run.halo.app.extension.Ref; import run.halo.app.extension.controller.Reconciler; import run.halo.app.infra.Condition; import run.halo.app.infra.ConditionStatus; +import run.halo.app.infra.ExternalUrlSupplier; import run.halo.app.infra.utils.JsonUtils; -import run.halo.app.infra.utils.PathUtils; import run.halo.app.metrics.CounterService; import run.halo.app.metrics.MeterUtils; import run.halo.app.theme.router.PermalinkIndexAddCommand; @@ -54,12 +54,16 @@ public class SinglePageReconciler implements Reconciler { private final ApplicationContext applicationContext; private final CounterService counterService; + private final ExternalUrlSupplier externalUrlSupplier; + public SinglePageReconciler(ExtensionClient client, ContentService contentService, - ApplicationContext applicationContext, CounterService counterService) { + ApplicationContext applicationContext, CounterService counterService, + ExternalUrlSupplier externalUrlSupplier) { this.client = client; this.contentService = contentService; this.applicationContext = applicationContext; this.counterService = counterService; + this.externalUrlSupplier = externalUrlSupplier; } @Override @@ -153,13 +157,17 @@ public class SinglePageReconciler implements Reconciler { } private void permalinkOnDelete(SinglePage singlePage) { - singlePage.getStatusOrDefault() - .setPermalink(PathUtils.combinePath(singlePage.getSpec().getSlug())); ExtensionLocator locator = new ExtensionLocator(GVK, singlePage.getMetadata().getName(), singlePage.getSpec().getSlug()); applicationContext.publishEvent(new PermalinkIndexDeleteCommand(this, locator)); } + String createPermalink(SinglePage page) { + var permalink = encodePath(page.getSpec().getSlug(), UTF_8); + permalink = StringUtils.prependIfMissing(permalink, "/"); + return externalUrlSupplier.get().resolve(permalink).normalize().toString(); + } + private void permalinkOnAdd(SinglePage singlePage) { if (!singlePage.isPublished() || Objects.equals(true, singlePage.getSpec().getDeleted())) { return; @@ -175,10 +183,8 @@ public class SinglePageReconciler implements Reconciler { final SinglePage oldPage = JsonUtils.deepCopy(singlePage); permalinkOnDelete(oldPage); - var permalink = encodePath(singlePage.getSpec().getSlug(), UTF_8); - permalink = StringUtils.prependIfMissing(permalink, "/"); singlePage.getStatusOrDefault() - .setPermalink(permalink); + .setPermalink(createPermalink(singlePage)); permalinkOnAdd(singlePage); SinglePage.SinglePageSpec spec = singlePage.getSpec(); diff --git a/src/test/java/run/halo/app/content/permalinks/CategoryPermalinkPolicyTest.java b/src/test/java/run/halo/app/content/permalinks/CategoryPermalinkPolicyTest.java index f6f6f50ea..1e21b7df7 100644 --- a/src/test/java/run/halo/app/content/permalinks/CategoryPermalinkPolicyTest.java +++ b/src/test/java/run/halo/app/content/permalinks/CategoryPermalinkPolicyTest.java @@ -4,6 +4,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; +import java.net.URI; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -12,6 +13,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.context.ApplicationContext; import run.halo.app.core.extension.Category; import run.halo.app.extension.Metadata; +import run.halo.app.infra.ExternalUrlSupplier; import run.halo.app.theme.DefaultTemplateEnum; import run.halo.app.theme.router.PermalinkPatternProvider; @@ -30,12 +32,16 @@ class CategoryPermalinkPolicyTest { @Mock private ApplicationContext applicationContext; + @Mock + private ExternalUrlSupplier externalUrlSupplier; + private CategoryPermalinkPolicy categoryPermalinkPolicy; @BeforeEach void setUp() { categoryPermalinkPolicy = - new CategoryPermalinkPolicy(applicationContext, permalinkPatternProvider); + new CategoryPermalinkPolicy(applicationContext, permalinkPatternProvider, + externalUrlSupplier); } @Test @@ -50,8 +56,19 @@ class CategoryPermalinkPolicyTest { categorySpec.setSlug("slug-test"); category.setSpec(categorySpec); + when(externalUrlSupplier.get()).thenReturn(URI.create("")); String permalink = categoryPermalinkPolicy.permalink(category); assertThat(permalink).isEqualTo("/categories/slug-test"); + + when(externalUrlSupplier.get()).thenReturn(URI.create("http://exmaple.com")); + permalink = categoryPermalinkPolicy.permalink(category); + assertThat(permalink).isEqualTo("http://exmaple.com/categories/slug-test"); + String path = URI.create(permalink).getPath(); + assertThat(path).isEqualTo("/categories/slug-test"); + + category.getSpec().setSlug("中文 slug"); + permalink = categoryPermalinkPolicy.permalink(category); + assertThat(permalink).isEqualTo("http://exmaple.com/categories/%E4%B8%AD%E6%96%87%20slug"); } @Test diff --git a/src/test/java/run/halo/app/content/permalinks/PostPermalinkPolicyTest.java b/src/test/java/run/halo/app/content/permalinks/PostPermalinkPolicyTest.java index 96513eb3e..528547408 100644 --- a/src/test/java/run/halo/app/content/permalinks/PostPermalinkPolicyTest.java +++ b/src/test/java/run/halo/app/content/permalinks/PostPermalinkPolicyTest.java @@ -1,8 +1,10 @@ package run.halo.app.content.permalinks; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.when; +import java.net.URI; import java.text.DecimalFormat; import java.text.NumberFormat; import java.time.Instant; @@ -16,6 +18,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.context.ApplicationContext; import run.halo.app.content.TestPost; import run.halo.app.core.extension.Post; +import run.halo.app.infra.ExternalUrlSupplier; import run.halo.app.infra.utils.PathUtils; import run.halo.app.theme.DefaultTemplateEnum; import run.halo.app.theme.router.PermalinkPatternProvider; @@ -36,11 +39,16 @@ class PostPermalinkPolicyTest { @Mock private ApplicationContext applicationContext; + @Mock + private ExternalUrlSupplier externalUrlSupplier; + private PostPermalinkPolicy postPermalinkPolicy; @BeforeEach void setUp() { - postPermalinkPolicy = new PostPermalinkPolicy(permalinkPatternProvider, applicationContext); + lenient().when(externalUrlSupplier.get()).thenReturn(URI.create("")); + postPermalinkPolicy = new PostPermalinkPolicy(permalinkPatternProvider, applicationContext, + externalUrlSupplier); } @Test @@ -89,6 +97,26 @@ class PostPermalinkPolicyTest { assertThat(permalink).isEqualTo("/posts/test-post"); } + @Test + void permalinkWithExternalUrl() { + Post post = TestPost.postV1(); + post.getMetadata().setName("test-post"); + post.getSpec().setSlug("test-post-slug"); + Instant now = Instant.parse("2022-11-01T02:40:06.806310Z"); + post.getSpec().setPublishTime(now); + + when(externalUrlSupplier.get()).thenReturn(URI.create("http://example.com")); + + when(permalinkPatternProvider.getPattern(DefaultTemplateEnum.POST)) + .thenReturn("/{year}/{month}/{day}/{slug}"); + String permalink = postPermalinkPolicy.permalink(post); + assertThat(permalink).isEqualTo("http://example.com/2022/11/01/test-post-slug"); + + post.getSpec().setSlug("中文 slug"); + permalink = postPermalinkPolicy.permalink(post); + assertThat(permalink).isEqualTo("http://example.com/2022/11/01/%E4%B8%AD%E6%96%87%20slug"); + } + @Test void templateName() { String s = postPermalinkPolicy.templateName(); diff --git a/src/test/java/run/halo/app/content/permalinks/TagPermalinkPolicyTest.java b/src/test/java/run/halo/app/content/permalinks/TagPermalinkPolicyTest.java index cae6d58bf..d4cba2bdb 100644 --- a/src/test/java/run/halo/app/content/permalinks/TagPermalinkPolicyTest.java +++ b/src/test/java/run/halo/app/content/permalinks/TagPermalinkPolicyTest.java @@ -4,6 +4,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; +import java.net.URI; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -12,6 +13,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.context.ApplicationContext; import run.halo.app.core.extension.Tag; import run.halo.app.extension.Metadata; +import run.halo.app.infra.ExternalUrlSupplier; import run.halo.app.theme.DefaultTemplateEnum; import run.halo.app.theme.router.PermalinkPatternProvider; @@ -30,11 +32,15 @@ class TagPermalinkPolicyTest { @Mock private ApplicationContext applicationContext; + @Mock + private ExternalUrlSupplier externalUrlSupplier; + private TagPermalinkPolicy tagPermalinkPolicy; @BeforeEach void setUp() { - tagPermalinkPolicy = new TagPermalinkPolicy(permalinkPatternProvider, applicationContext); + tagPermalinkPolicy = new TagPermalinkPolicy(permalinkPatternProvider, applicationContext, + externalUrlSupplier); } @Test @@ -50,8 +56,19 @@ class TagPermalinkPolicyTest { tagSpec.setSlug("test-slug"); tag.setSpec(tagSpec); + when(externalUrlSupplier.get()).thenReturn(URI.create("")); + String permalink = tagPermalinkPolicy.permalink(tag); assertThat(permalink).isEqualTo("/tags/test-slug"); + + when(externalUrlSupplier.get()).thenReturn(URI.create("http://example.com")); + + permalink = tagPermalinkPolicy.permalink(tag); + assertThat(permalink).isEqualTo("http://example.com/tags/test-slug"); + + tag.getSpec().setSlug("中文slug"); + permalink = tagPermalinkPolicy.permalink(tag); + assertThat(permalink).isEqualTo("http://example.com/tags/%E4%B8%AD%E6%96%87slug"); } @Test 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 dee2d22e5..90154f3c8 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 @@ -9,6 +9,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static run.halo.app.content.TestPost.snapshotV1; +import java.net.URI; import java.util.List; import java.util.Optional; import java.util.Set; @@ -30,6 +31,7 @@ import run.halo.app.core.extension.Snapshot; import run.halo.app.extension.ExtensionClient; import run.halo.app.extension.Metadata; import run.halo.app.extension.controller.Reconciler; +import run.halo.app.infra.ExternalUrlSupplier; import run.halo.app.metrics.CounterService; import run.halo.app.theme.router.PermalinkIndexAddCommand; import run.halo.app.theme.router.PermalinkIndexDeleteCommand; @@ -54,12 +56,15 @@ class SinglePageReconcilerTest { @Mock private CounterService counterService; + @Mock + private ExternalUrlSupplier externalUrlSupplier; + private SinglePageReconciler singlePageReconciler; @BeforeEach void setUp() { singlePageReconciler = new SinglePageReconciler(client, contentService, applicationContext, - counterService); + counterService, externalUrlSupplier); } @Test @@ -80,6 +85,7 @@ class SinglePageReconcilerTest { snapshotV2.getSpec().setContributors(Set.of("guqing", "zhangsan")); when(contentService.listSnapshots(any())) .thenReturn(Flux.just(snapshotV1, snapshotV2)); + when(externalUrlSupplier.get()).thenReturn(URI.create("")); ArgumentCaptor captor = ArgumentCaptor.forClass(SinglePage.class); singlePageReconciler.reconcile(new Reconciler.Request(name)); @@ -95,6 +101,25 @@ class SinglePageReconcilerTest { verify(applicationContext, times(0)).publishEvent(isA(PermalinkIndexUpdateCommand.class)); } + @Test + void createPermalink() { + SinglePage page = pageV1(); + page.getSpec().setSlug("page-slug"); + + when(externalUrlSupplier.get()).thenReturn(URI.create("")); + + String permalink = singlePageReconciler.createPermalink(page); + assertThat(permalink).isEqualTo("/page-slug"); + + when(externalUrlSupplier.get()).thenReturn(URI.create("http://example.com")); + permalink = singlePageReconciler.createPermalink(page); + assertThat(permalink).isEqualTo("http://example.com/page-slug"); + + page.getSpec().setSlug("中文 slug"); + permalink = singlePageReconciler.createPermalink(page); + assertThat(permalink).isEqualTo("http://example.com/%E4%B8%AD%E6%96%87%20slug"); + } + public static SinglePage pageV1() { SinglePage page = new SinglePage(); page.setKind(Post.KIND); diff --git a/src/test/java/run/halo/app/theme/finders/impl/PostFinderImplTest.java b/src/test/java/run/halo/app/theme/finders/impl/PostFinderImplTest.java index 1d1c681fb..108a83ae5 100644 --- a/src/test/java/run/halo/app/theme/finders/impl/PostFinderImplTest.java +++ b/src/test/java/run/halo/app/theme/finders/impl/PostFinderImplTest.java @@ -104,7 +104,6 @@ class PostFinderImplTest { .thenReturn(Mono.just(listResult)); ListResult archives = postFinder.archives(1, 10); List items = archives.getItems(); - assertThat(items.size()).isEqualTo(2); assertThat(items.get(0).getYear()).isEqualTo("2022"); assertThat(items.get(0).getMonths().size()).isEqualTo(1);