From fe3860a8b27390b34aaae224a9596c795301ffb5 Mon Sep 17 00:00:00 2001 From: guqing <38999863+guqing@users.noreply.github.com> Date: Wed, 28 Sep 2022 23:28:18 +0800 Subject: [PATCH] refactor: reload theme.yaml when theme reload (#2486) 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: 主题 reload endpoint 增加对 theme.yaml 的 reload 操作 #### Which issue(s) this PR fixes: Fixes # #### Special notes for your reviewer: /cc @halo-dev/sig-halo #### Does this PR introduce a user-facing change? ```release-note None ``` --- .../extension/endpoint/ThemeEndpoint.java | 29 +++++++++--- .../extension/endpoint/ThemeEndpointTest.java | 45 ++++++++++++++++--- 2 files changed, 61 insertions(+), 13 deletions(-) diff --git a/src/main/java/run/halo/app/core/extension/endpoint/ThemeEndpoint.java b/src/main/java/run/halo/app/core/extension/endpoint/ThemeEndpoint.java index ac70a8a2b..dfd7f48f8 100644 --- a/src/main/java/run/halo/app/core/extension/endpoint/ThemeEndpoint.java +++ b/src/main/java/run/halo/app/core/extension/endpoint/ThemeEndpoint.java @@ -53,6 +53,7 @@ import run.halo.app.infra.properties.HaloProperties; import run.halo.app.infra.utils.DataBufferUtils; import run.halo.app.infra.utils.FileUtils; import run.halo.app.infra.utils.YamlUnstructuredLoader; +import run.halo.app.theme.ThemePathPolicy; /** * Endpoint for managing themes. @@ -66,10 +67,12 @@ public class ThemeEndpoint implements CustomEndpoint { private final ReactiveExtensionClient client; private final HaloProperties haloProperties; + private final ThemePathPolicy themePathPolicy; public ThemeEndpoint(ReactiveExtensionClient client, HaloProperties haloProperties) { this.client = client; this.haloProperties = haloProperties; + this.themePathPolicy = new ThemePathPolicy(haloProperties.getWorkDir()); } @Override @@ -101,7 +104,7 @@ public class ThemeEndpoint implements CustomEndpoint { .implementation(String.class) ) .response(responseBuilder() - .implementation(Setting.class)) + .implementation(Theme.class)) ) .build(); } @@ -124,12 +127,26 @@ public class ThemeEndpoint implements CustomEndpoint { persistent.setSpec(setting.getSpec()); return client.update(persistent); }) - .switchIfEmpty(Mono.defer(() -> client.create(setting)))) - .orElse(Mono.empty()); + .switchIfEmpty(Mono.defer(() -> client.create(setting))) + .thenReturn(theme) + ) + .orElse(Mono.just(theme)); }) - .flatMap(setting -> ServerResponse.ok() + .flatMap(themeToUse -> { + Path themePath = themePathPolicy.generate(themeToUse); + Path themeManifestPath = ThemeUtils.resolveThemeManifest(themePath); + if (themeManifestPath == null) { + return Mono.error(new IllegalArgumentException( + "The manifest file [theme.yaml] is required.")); + } + Unstructured unstructured = ThemeUtils.loadThemeManifest(themeManifestPath); + Theme newTheme = Unstructured.OBJECT_MAPPER.convertValue(unstructured, Theme.class); + themeToUse.setSpec(newTheme.getSpec()); + return client.update(themeToUse); + }) + .flatMap(theme -> ServerResponse.ok() .contentType(MediaType.APPLICATION_JSON) - .bodyValue(setting)); + .bodyValue(theme)); } public record InstallRequest( @@ -299,7 +316,7 @@ public class ThemeEndpoint implements CustomEndpoint { } } - private static Unstructured loadThemeManifest(Path themeManifestPath) { + static Unstructured loadThemeManifest(Path themeManifestPath) { List unstructureds = new YamlUnstructuredLoader(new FileSystemResource(themeManifestPath)) .load(); diff --git a/src/test/java/run/halo/app/core/extension/endpoint/ThemeEndpointTest.java b/src/test/java/run/halo/app/core/extension/endpoint/ThemeEndpointTest.java index 83e15428b..6b6547dbb 100644 --- a/src/test/java/run/halo/app/core/extension/endpoint/ThemeEndpointTest.java +++ b/src/test/java/run/halo/app/core/extension/endpoint/ThemeEndpointTest.java @@ -30,6 +30,7 @@ import org.springframework.web.reactive.function.BodyInserters; import reactor.core.publisher.Mono; import run.halo.app.core.extension.Setting; import run.halo.app.core.extension.Theme; +import run.halo.app.extension.AbstractExtension; import run.halo.app.extension.Metadata; import run.halo.app.extension.ReactiveExtensionClient; import run.halo.app.extension.Unstructured; @@ -61,11 +62,10 @@ class ThemeEndpointTest { @BeforeEach void setUp() throws IOException { tmpHaloWorkDir = Files.createTempDirectory("halo-unit-test"); + when(haloProperties.getWorkDir()).thenReturn(tmpHaloWorkDir); ThemeEndpoint themeEndpoint = new ThemeEndpoint(extensionClient, haloProperties); - when(haloProperties.getWorkDir()).thenReturn(tmpHaloWorkDir); - defaultTheme = ResourceUtils.getFile("classpath:themes/test-theme.zip"); webTestClient = WebTestClient @@ -122,6 +122,7 @@ class ThemeEndpointTest { theme.setMetadata(new Metadata()); theme.getMetadata().setName("fake-theme"); theme.setSpec(new Theme.ThemeSpec()); + theme.getSpec().setDisplayName("Hello"); theme.getSpec().setSettingName("fake-setting"); when(extensionClient.fetch(Theme.class, "fake-theme")) .thenReturn(Mono.just(theme)); @@ -152,9 +153,20 @@ class ThemeEndpointTest { children: Register """); + Files.writeString(themeWorkDir.resolve("theme.yaml"), """ + apiVersion: v1alpha1 + kind: Theme + metadata: + name: fake-theme + spec: + displayName: Fake Theme + """); + when(extensionClient.update(any(Theme.class))) + .thenReturn(Mono.just(theme)); + when(extensionClient.update(any(Setting.class))) .thenReturn(Mono.just(setting)); - ArgumentCaptor captor = ArgumentCaptor.forClass(Setting.class); + ArgumentCaptor captor = ArgumentCaptor.forClass(Setting.class); webTestClient.put() .uri("/themes/fake-theme/reload-setting") .exchange() @@ -162,10 +174,12 @@ class ThemeEndpointTest { .isOk() .expectBody(Setting.class) .value(settingRes -> { - verify(extensionClient, times(1)).update(captor.capture()); + verify(extensionClient, times(2)).update(captor.capture()); verify(extensionClient, times(0)).create(any(Setting.class)); - Setting value = captor.getValue(); - System.out.println(JsonUtils.objectToJson(value)); + List allValues = captor.getAllValues(); + assertThat(allValues.get(0)).isInstanceOfAny(Setting.class); + Setting newSetting = (Setting) allValues.get(0); + Theme newTheme = (Theme) allValues.get(1); try { JSONAssert.assertEquals(""" { @@ -188,7 +202,24 @@ class ThemeEndpointTest { "metadata": {} } """, - JsonUtils.objectToJson(value), + JsonUtils.objectToJson(newSetting), + true); + + JSONAssert.assertEquals(""" + { + "spec": { + "displayName": "Fake Theme", + "version": "*", + "require": "*" + }, + "apiVersion": "theme.halo.run/v1alpha1", + "kind": "Theme", + "metadata": { + "name": "fake-theme" + } + } + """, + JsonUtils.objectToJson(newTheme), true); } catch (JSONException e) { throw new RuntimeException(e);