Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add reset config API for theme and plugin #2964

Merged
merged 6 commits into from
Dec 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Predicate;
import lombok.extern.slf4j.Slf4j;
import org.springdoc.webflux.core.fn.SpringdocRouteBuilder;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.data.domain.Sort;
import org.springframework.http.MediaType;
import org.springframework.http.codec.multipart.FilePart;
Expand All @@ -49,7 +51,10 @@
import reactor.core.scheduler.Schedulers;
import reactor.util.retry.Retry;
import run.halo.app.core.extension.Plugin;
import run.halo.app.core.extension.Setting;
import run.halo.app.core.extension.theme.SettingUtils;
import run.halo.app.extension.Comparators;
import run.halo.app.extension.ConfigMap;
import run.halo.app.extension.ReactiveExtensionClient;
import run.halo.app.extension.router.IListRequest.QueryListRequest;
import run.halo.app.infra.utils.FileUtils;
Expand Down Expand Up @@ -95,6 +100,19 @@ public RouterFunction<ServerResponse> endpoint() {
.content(contentBuilder().mediaType(MediaType.MULTIPART_FORM_DATA_VALUE)
.schema(schemaBuilder().implementation(InstallRequest.class))))
)
.PUT("plugins/{name}/reset-config", this::resetSettingConfig,
builder -> builder.operationId("ResetPluginConfig")
.description("Reset the configMap of plugin setting.")
.tag(tag)
.parameter(parameterBuilder()
.name("name")
.in(ParameterIn.PATH)
.required(true)
.implementation(String.class)
)
.response(responseBuilder()
.implementation(ConfigMap.class))
)
.GET("plugins", this::list, builder -> {
builder.operationId("ListPlugins")
.tag(tag)
Expand All @@ -105,6 +123,32 @@ public RouterFunction<ServerResponse> endpoint() {
.build();
}

private Mono<ServerResponse> resetSettingConfig(ServerRequest request) {
String name = request.pathVariable("name");
return client.fetch(Plugin.class, name)
.filter(plugin -> StringUtils.hasText(plugin.getSpec().getSettingName()))
.flatMap(plugin -> {
String configMapName = plugin.getSpec().getConfigMapName();
String settingName = plugin.getSpec().getSettingName();
return client.fetch(Setting.class, settingName)
.map(SettingUtils::settingDefinedDefaultValueMap)
.flatMap(data -> updateConfigMapData(configMapName, data));
})
.flatMap(configMap -> ServerResponse.ok()
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(configMap));
}

private Mono<ConfigMap> updateConfigMapData(String configMapName, Map<String, String> data) {
return client.fetch(ConfigMap.class, configMapName)
.flatMap(configMap -> {
configMap.setData(data);
return client.update(configMap);
})
.retryWhen(Retry.fixedDelay(10, Duration.ofMillis(100))
.filter(t -> t instanceof OptimisticLockingFailureException));
}

private Mono<ServerResponse> upgrade(ServerRequest request) {
var pluginNameInPath = request.pathVariable("name");
var tempDirRef = new AtomicReference<Path>();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
package run.halo.app.core.extension.reconciler;

import com.fasterxml.jackson.databind.JsonNode;
import java.io.IOException;
import java.nio.file.Path;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.FileSystemUtils;
import run.halo.app.core.extension.Setting;
import run.halo.app.core.extension.Theme;
import run.halo.app.core.extension.theme.SettingUtils;
import run.halo.app.extension.ConfigMap;
import run.halo.app.extension.ExtensionClient;
import run.halo.app.extension.Metadata;
Expand Down Expand Up @@ -106,7 +102,7 @@ private void themeSettingDefaultConfig(Theme theme) {

client.fetch(Setting.class, theme.getSpec().getSettingName())
.ifPresent(setting -> {
Map<String, String> data = settingDefinedDefaultValueMap(setting);
var data = SettingUtils.settingDefinedDefaultValueMap(setting);
if (CollectionUtils.isEmpty(data)) {
return;
}
Expand All @@ -118,31 +114,6 @@ private void themeSettingDefaultConfig(Theme theme) {
});
}

Map<String, String> settingDefinedDefaultValueMap(Setting setting) {
final String defaultValueField = "value";
final String nameField = "name";
List<Setting.SettingForm> forms = setting.getSpec().getForms();
if (CollectionUtils.isEmpty(forms)) {
return null;
}
Map<String, String> data = new LinkedHashMap<>();
for (Setting.SettingForm form : forms) {
String group = form.getGroup();
Map<String, JsonNode> groupValue = form.getFormSchema().stream()
.map(o -> JsonUtils.DEFAULT_JSON_MAPPER.convertValue(o, JsonNode.class))
.filter(jsonNode -> jsonNode.isObject() && jsonNode.has(nameField)
&& jsonNode.has(defaultValueField))
.map(jsonNode -> {
String name = jsonNode.findValue(nameField).asText();
JsonNode value = jsonNode.findValue(defaultValueField);
return Map.entry(name, value);
})
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
data.put(group, JsonUtils.objectToJson(groupValue));
}
return data;
}

private void reconcileThemeDeletion(Theme theme) {
deleteThemeFiles(theme);
// delete theme setting form
Expand Down
48 changes: 48 additions & 0 deletions src/main/java/run/halo/app/core/extension/theme/SettingUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package run.halo.app.core.extension.theme;

import com.fasterxml.jackson.databind.JsonNode;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import lombok.experimental.UtilityClass;
import org.springframework.lang.NonNull;
import org.springframework.util.CollectionUtils;
import run.halo.app.core.extension.Setting;
import run.halo.app.infra.utils.JsonUtils;

@UtilityClass
public class SettingUtils {
private static final String VALUE_FIELD = "value";
private static final String NAME_FIELD = "name";

/**
* Read setting default value from {@link Setting} forms.
*
* @param setting {@link Setting} extension
* @return a map of setting default value
*/
@NonNull
public static Map<String, String> settingDefinedDefaultValueMap(Setting setting) {
List<Setting.SettingForm> forms = setting.getSpec().getForms();
if (CollectionUtils.isEmpty(forms)) {
return Map.of();
}
Map<String, String> data = new LinkedHashMap<>();
for (Setting.SettingForm form : forms) {
String group = form.getGroup();
Map<String, JsonNode> groupValue = form.getFormSchema().stream()
.map(o -> JsonUtils.DEFAULT_JSON_MAPPER.convertValue(o, JsonNode.class))
.filter(jsonNode -> jsonNode.isObject() && jsonNode.has(NAME_FIELD)
&& jsonNode.has(VALUE_FIELD))
.map(jsonNode -> {
String name = jsonNode.get(NAME_FIELD).asText();
JsonNode value = jsonNode.get(VALUE_FIELD);
return Map.entry(name, value);
})
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
data.put(group, JsonUtils.objectToJson(groupValue));
}
return data;
}
}
22 changes: 22 additions & 0 deletions src/main/java/run/halo/app/core/extension/theme/ThemeEndpoint.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import reactor.core.publisher.Mono;
import run.halo.app.core.extension.Theme;
import run.halo.app.core.extension.endpoint.CustomEndpoint;
import run.halo.app.extension.ConfigMap;
import run.halo.app.extension.ListResult;
import run.halo.app.extension.ReactiveExtensionClient;
import run.halo.app.extension.router.IListRequest;
Expand Down Expand Up @@ -101,6 +102,19 @@ public RouterFunction<ServerResponse> endpoint() {
.response(responseBuilder()
.implementation(Theme.class))
)
.PUT("themes/{name}/reset-config", this::resetSettingConfig,
builder -> builder.operationId("ResetThemeConfig")
.description("Reset the configMap of theme setting.")
.tag(tag)
.parameter(parameterBuilder()
.name("name")
.in(ParameterIn.PATH)
.required(true)
.implementation(String.class)
)
.response(responseBuilder()
.implementation(ConfigMap.class))
)
.GET("themes", this::listThemes,
builder -> {
builder.operationId("ListThemes")
Expand Down Expand Up @@ -215,6 +229,14 @@ Mono<ServerResponse> reloadTheme(ServerRequest request) {
.bodyValue(theme));
}

Mono<ServerResponse> resetSettingConfig(ServerRequest request) {
String name = request.pathVariable("name");
return themeService.resetSettingConfig(name)
.flatMap(theme -> ServerResponse.ok()
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(theme));
}

public record InstallRequest(
@Schema(required = true, description = "Theme zip file.") FilePart file) {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.io.InputStream;
import reactor.core.publisher.Mono;
import run.halo.app.core.extension.Theme;
import run.halo.app.extension.ConfigMap;

public interface ThemeService {

Expand All @@ -11,6 +12,8 @@ public interface ThemeService {
Mono<Theme> upgrade(String themeName, InputStream is);

Mono<Theme> reloadTheme(String name);

Mono<ConfigMap> resetSettingConfig(String name);
// TODO Migrate other useful methods in ThemeEndpoint in the future.

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@
import java.io.InputStream;
import java.nio.file.Path;
import java.time.Duration;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Predicate;
import java.util.zip.ZipInputStream;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.retry.RetryException;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
Expand Down Expand Up @@ -206,6 +208,29 @@ public Mono<Theme> reloadTheme(String name) {
});
}

@Override
public Mono<ConfigMap> resetSettingConfig(String name) {
return client.fetch(Theme.class, name)
.filter(theme -> StringUtils.isNotBlank(theme.getSpec().getSettingName()))
.flatMap(theme -> {
String configMapName = theme.getSpec().getConfigMapName();
String settingName = theme.getSpec().getSettingName();
return client.fetch(Setting.class, settingName)
.map(SettingUtils::settingDefinedDefaultValueMap)
.flatMap(data -> updateConfigMapData(configMapName, data));
});
}

private Mono<ConfigMap> updateConfigMapData(String configMapName, Map<String, String> data) {
return client.fetch(ConfigMap.class, configMapName)
.flatMap(configMap -> {
configMap.setData(data);
return client.update(configMap);
})
.retryWhen(Retry.fixedDelay(10, Duration.ofMillis(100))
.filter(t -> t instanceof OptimisticLockingFailureException));
}

private Mono<Void> waitForSettingDeleted(String settingName) {
return client.fetch(Setting.class, settingName)
.flatMap(setting -> client.delete(setting)
Expand Down
3 changes: 3 additions & 0 deletions src/main/resources/extensions/role-template-plugin.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ rules:
- apiGroups: [ "plugin.halo.run" ]
resources: [ "plugins" ]
verbs: [ "create", "patch", "update", "delete", "deletecollection" ]
- apiGroups: [ "api.console.halo.run" ]
resources: [ "plugins/upgrade", "plugins/resetconfig" ]
verbs: [ "*" ]
- nonResourceURLs: [ "/apis/api.console.halo.run/v1alpha1/plugins/*" ]
verbs: [ "create" ]
---
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/extensions/role-template-theme.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ rules:
resources: [ "themes" ]
verbs: [ "*" ]
- apiGroups: [ "api.console.halo.run" ]
resources: [ "themes", "themes/reload" ]
resources: [ "themes", "themes/reload", "themes/resetconfig" ]
verbs: [ "*" ]
- nonResourceURLs: [ "/apis/api.console.halo.run/themes/install" ]
verbs: [ "create" ]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,21 +173,6 @@ void themeSettingDefaultValue() throws IOException, JSONException {
true);
}

@Test
void settingDefinedDefaultValueMap() throws JSONException {
Setting setting = getFakeSetting();
when(haloProperties.getWorkDir()).thenReturn(tempDirectory);
Map<String, String> map = new ThemeReconciler(extensionClient, haloProperties)
.settingDefinedDefaultValueMap(setting);
JSONAssert.assertEquals("""
{
"sns": "{\\"email\\":\\"example@exmple.com\\"}"
}
""",
JsonUtils.objectToJson(map),
true);
}

private static Setting getFakeSetting() {
String settingJson = """
{
Expand Down
Loading