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
guqing 2022-09-28 23:28:18 +08:00 committed by GitHub
parent 2d87a95193
commit fe3860a8b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 61 additions and 13 deletions

View File

@ -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();

View File

@ -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);