mirror of https://github.com/halo-dev/halo
refactor: reload theme.yaml when theme reload (#2486)
#### 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 ```pull/2493/head
parent
2d87a95193
commit
fe3860a8b2
|
@ -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<Unstructured> unstructureds =
|
||||
new YamlUnstructuredLoader(new FileSystemResource(themeManifestPath))
|
||||
.load();
|
||||
|
|
|
@ -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<Setting> captor = ArgumentCaptor.forClass(Setting.class);
|
||||
ArgumentCaptor<AbstractExtension> 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<AbstractExtension> 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);
|
||||
|
|
Loading…
Reference in New Issue