mirror of https://github.com/halo-dev/halo
feat: the ConfigMap named system to store user-defined configurations (#2415)
#### What type of PR is this? /kind improvement /area core /milestone 2.0 #### What this PR does / why we need it: 将原来系统默认的系统配置 system 改名为 system-default, 并使用名为 system 的 ConfigMap 来存储用户自定义的系统配置。系统最终配置为用户自定义系统配置 Merge Patch 系统默认配置的结果。 see also #2304 #### Which issue(s) this PR fixes: Fixes #2304 #### Special notes for your reviewer: /cc @halo-dev/sig-halo #### Does this PR introduce a user-facing change? ```release-note None ```pull/2443/head
parent
1714f8edb2
commit
ac8dd74211
|
@ -46,6 +46,7 @@ ext {
|
|||
javaDiffUtils = "4.12"
|
||||
guava = "31.1-jre"
|
||||
jsoup = "1.15.2"
|
||||
jsonPatch = "1.13"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
@ -75,6 +76,7 @@ dependencies {
|
|||
implementation "org.jsoup:jsoup:$jsoup"
|
||||
implementation "io.github.java-diff-utils:java-diff-utils:$javaDiffUtils"
|
||||
implementation "org.springframework.integration:spring-integration-core"
|
||||
implementation "com.github.java-json-tools:json-patch:$jsonPatch"
|
||||
|
||||
compileOnly 'org.projectlombok:lombok'
|
||||
testCompileOnly 'org.projectlombok:lombok'
|
||||
|
|
|
@ -51,6 +51,7 @@ import run.halo.app.extension.controller.ControllerBuilder;
|
|||
import run.halo.app.extension.controller.ControllerManager;
|
||||
import run.halo.app.extension.router.ExtensionCompositeRouterFunction;
|
||||
import run.halo.app.infra.ExternalUrlSupplier;
|
||||
import run.halo.app.infra.SystemConfigurableEnvironmentFetcher;
|
||||
import run.halo.app.infra.properties.HaloProperties;
|
||||
import run.halo.app.plugin.HaloPluginManager;
|
||||
import run.halo.app.plugin.resources.JsBundleRuleProvider;
|
||||
|
@ -171,9 +172,11 @@ public class ExtensionConfiguration {
|
|||
|
||||
@Bean
|
||||
Controller systemSettingController(ExtensionClient client,
|
||||
SystemConfigurableEnvironmentFetcher environmentFetcher,
|
||||
ApplicationContext applicationContext) {
|
||||
return new ControllerBuilder("system-setting-controller", client)
|
||||
.reconciler(new SystemSettingReconciler(client, applicationContext))
|
||||
.reconciler(new SystemSettingReconciler(client, environmentFetcher,
|
||||
applicationContext))
|
||||
.extension(new ConfigMap())
|
||||
.build();
|
||||
}
|
||||
|
|
|
@ -3,13 +3,16 @@ package run.halo.app.core.extension.reconciler;
|
|||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import run.halo.app.extension.ConfigMap;
|
||||
import run.halo.app.extension.ExtensionClient;
|
||||
import run.halo.app.extension.Metadata;
|
||||
import run.halo.app.extension.controller.Reconciler;
|
||||
import run.halo.app.infra.SystemConfigurableEnvironmentFetcher;
|
||||
import run.halo.app.infra.SystemSetting;
|
||||
import run.halo.app.infra.utils.JsonUtils;
|
||||
import run.halo.app.infra.utils.PathUtils;
|
||||
|
@ -28,12 +31,16 @@ public class SystemSettingReconciler implements Reconciler<Reconciler.Request> {
|
|||
public static final String FINALIZER_NAME = "system-setting-protection";
|
||||
|
||||
private final ExtensionClient client;
|
||||
private final SystemConfigurableEnvironmentFetcher environmentFetcher;
|
||||
private final ApplicationContext applicationContext;
|
||||
|
||||
private final RouteRuleReconciler routeRuleReconciler = new RouteRuleReconciler();
|
||||
|
||||
public SystemSettingReconciler(ExtensionClient client, ApplicationContext applicationContext) {
|
||||
public SystemSettingReconciler(ExtensionClient client,
|
||||
SystemConfigurableEnvironmentFetcher environmentFetcher,
|
||||
ApplicationContext applicationContext) {
|
||||
this.client = client;
|
||||
this.environmentFetcher = environmentFetcher;
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
|
@ -47,11 +54,44 @@ public class SystemSettingReconciler implements Reconciler<Reconciler.Request> {
|
|||
.ifPresent(configMap -> {
|
||||
addFinalizerIfNecessary(configMap);
|
||||
routeRuleReconciler.reconcile(name);
|
||||
customizeSystem(name);
|
||||
});
|
||||
return new Result(false, null);
|
||||
}
|
||||
|
||||
private void customizeSystem(String name) {
|
||||
if (!SystemSetting.SYSTEM_CONFIG_DEFAULT.equals(name)) {
|
||||
return;
|
||||
}
|
||||
// configMap named system not found then create it by system-default
|
||||
Optional<ConfigMap> systemOpt = client.fetch(ConfigMap.class, SystemSetting.SYSTEM_CONFIG);
|
||||
if (systemOpt.isPresent()) {
|
||||
return;
|
||||
}
|
||||
ConfigMap system = client.fetch(ConfigMap.class, SystemSetting.SYSTEM_CONFIG_DEFAULT)
|
||||
.map(configMap -> {
|
||||
// create a new configMap named system by system-default
|
||||
ConfigMap systemConfigMap = new ConfigMap();
|
||||
systemConfigMap.setMetadata(new Metadata());
|
||||
systemConfigMap.getMetadata().setName(SystemSetting.SYSTEM_CONFIG);
|
||||
systemConfigMap.setData(configMap.getData());
|
||||
return systemConfigMap;
|
||||
})
|
||||
.orElseGet(() -> {
|
||||
// empty configMap named system
|
||||
ConfigMap configMap = new ConfigMap();
|
||||
configMap.setMetadata(new Metadata());
|
||||
configMap.getMetadata().setName(SystemSetting.SYSTEM_CONFIG);
|
||||
configMap.setData(new HashMap<>());
|
||||
return configMap;
|
||||
});
|
||||
client.create(system);
|
||||
}
|
||||
|
||||
private void addFinalizerIfNecessary(ConfigMap oldConfigMap) {
|
||||
if (SystemSetting.SYSTEM_CONFIG.equals(oldConfigMap.getMetadata().getName())) {
|
||||
return;
|
||||
}
|
||||
Set<String> finalizers = oldConfigMap.getMetadata().getFinalizers();
|
||||
if (finalizers != null && finalizers.contains(FINALIZER_NAME)) {
|
||||
return;
|
||||
|
@ -70,7 +110,7 @@ public class SystemSettingReconciler implements Reconciler<Reconciler.Request> {
|
|||
|
||||
class RouteRuleReconciler {
|
||||
|
||||
public void reconcile(String name) {
|
||||
public void reconcile(String name) {
|
||||
reconcileArchivesRule(name);
|
||||
reconcileTagsRule(name);
|
||||
reconcileCategoriesRule(name);
|
||||
|
@ -78,7 +118,7 @@ public class SystemSettingReconciler implements Reconciler<Reconciler.Request> {
|
|||
}
|
||||
|
||||
private void reconcileArchivesRule(String name) {
|
||||
client.fetch(ConfigMap.class, name).ifPresent(configMap -> {
|
||||
getConfigMap(name).ifPresent(configMap -> {
|
||||
SystemSetting.ThemeRouteRules oldRules = getOldRouteRulesFromAnno(configMap);
|
||||
SystemSetting.ThemeRouteRules newRules = getRouteRules(configMap);
|
||||
|
||||
|
@ -128,7 +168,7 @@ public class SystemSettingReconciler implements Reconciler<Reconciler.Request> {
|
|||
}
|
||||
|
||||
private void reconcileTagsRule(String name) {
|
||||
client.fetch(ConfigMap.class, name).ifPresent(configMap -> {
|
||||
getConfigMap(name).ifPresent(configMap -> {
|
||||
SystemSetting.ThemeRouteRules oldRules = getOldRouteRulesFromAnno(configMap);
|
||||
SystemSetting.ThemeRouteRules newRules = getRouteRules(configMap);
|
||||
final String oldTagsPrefix = oldRules.getTags();
|
||||
|
@ -147,7 +187,7 @@ public class SystemSettingReconciler implements Reconciler<Reconciler.Request> {
|
|||
}
|
||||
|
||||
private void reconcileCategoriesRule(String name) {
|
||||
client.fetch(ConfigMap.class, name).ifPresent(configMap -> {
|
||||
getConfigMap(name).ifPresent(configMap -> {
|
||||
SystemSetting.ThemeRouteRules oldRules = getOldRouteRulesFromAnno(configMap);
|
||||
SystemSetting.ThemeRouteRules newRules = getRouteRules(configMap);
|
||||
final String oldCategoriesPrefix = oldRules.getCategories();
|
||||
|
@ -166,7 +206,7 @@ public class SystemSettingReconciler implements Reconciler<Reconciler.Request> {
|
|||
}
|
||||
|
||||
private void reconcilePostRule(String name) {
|
||||
client.fetch(ConfigMap.class, name).ifPresent(configMap -> {
|
||||
getConfigMap(name).ifPresent(configMap -> {
|
||||
SystemSetting.ThemeRouteRules oldRules = getOldRouteRulesFromAnno(configMap);
|
||||
SystemSetting.ThemeRouteRules newRules = getRouteRules(configMap);
|
||||
|
||||
|
@ -239,6 +279,11 @@ public class SystemSettingReconciler implements Reconciler<Reconciler.Request> {
|
|||
}
|
||||
|
||||
public boolean isSystemSetting(String name) {
|
||||
return SystemSetting.SYSTEM_CONFIG.equals(name);
|
||||
return SystemSetting.SYSTEM_CONFIG.equals(name)
|
||||
|| SystemSetting.SYSTEM_CONFIG_DEFAULT.equals(name);
|
||||
}
|
||||
|
||||
private Optional<ConfigMap> getConfigMap(String name) {
|
||||
return environmentFetcher.getConfigMapBlocking();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,28 @@
|
|||
package run.halo.app.infra;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
|
||||
import com.github.fge.jsonpatch.JsonPatchException;
|
||||
import com.github.fge.jsonpatch.mergepatch.JsonMergePatch;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.stereotype.Component;
|
||||
import reactor.core.publisher.Mono;
|
||||
import run.halo.app.extension.ConfigMap;
|
||||
import run.halo.app.extension.ReactiveExtensionClient;
|
||||
import run.halo.app.infra.utils.JsonParseException;
|
||||
import run.halo.app.infra.utils.JsonUtils;
|
||||
|
||||
/**
|
||||
* A fetcher that fetches the system configuration from the extension client.
|
||||
* If there are {@link ConfigMap}s named <code>system-default</code> and <code>system</code> at
|
||||
* the same time, the {@link ConfigMap} named system will be json merge patch to
|
||||
* {@link ConfigMap} named <code>system-default</code>
|
||||
*
|
||||
* @author guqing
|
||||
* @since 2.0.0
|
||||
*/
|
||||
|
@ -49,7 +62,80 @@ public class SystemConfigurableEnvironmentFetcher {
|
|||
.defaultIfEmpty(Map.of());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets config map.
|
||||
*
|
||||
* @return a new {@link ConfigMap} named <code>system</code> by json merge patch.
|
||||
*/
|
||||
public Mono<ConfigMap> getConfigMap() {
|
||||
return extensionClient.fetch(ConfigMap.class, SystemSetting.SYSTEM_CONFIG);
|
||||
return extensionClient.fetch(ConfigMap.class, SystemSetting.SYSTEM_CONFIG_DEFAULT)
|
||||
.flatMap(systemDefault ->
|
||||
extensionClient.fetch(ConfigMap.class, SystemSetting.SYSTEM_CONFIG)
|
||||
.map(system -> {
|
||||
Map<String, String> defaultData = systemDefault.getData();
|
||||
Map<String, String> data = system.getData();
|
||||
Map<String, String> mergedData = mergeData(defaultData, data);
|
||||
system.setData(mergedData);
|
||||
return system;
|
||||
})
|
||||
.switchIfEmpty(Mono.just(systemDefault)));
|
||||
}
|
||||
|
||||
public Optional<ConfigMap> getConfigMapBlocking() {
|
||||
return getConfigMap().blockOptional();
|
||||
}
|
||||
|
||||
private Map<String, String> mergeData(Map<String, String> defaultData,
|
||||
Map<String, String> data) {
|
||||
if (defaultData == null) {
|
||||
return data;
|
||||
}
|
||||
if (data == null) {
|
||||
return defaultData;
|
||||
}
|
||||
|
||||
Map<String, String> copiedDefault = new LinkedHashMap<>(defaultData);
|
||||
// // merge the data map entries into the default map
|
||||
data.forEach((group, dataValue) -> {
|
||||
// https://www.rfc-editor.org/rfc/rfc7386
|
||||
String defaultV = copiedDefault.get(group);
|
||||
String newValue;
|
||||
if (dataValue == null) {
|
||||
if (copiedDefault.containsKey(group)) {
|
||||
newValue = null;
|
||||
} else {
|
||||
newValue = defaultV;
|
||||
}
|
||||
} else {
|
||||
newValue = mergeRemappingFunction(dataValue, defaultV);
|
||||
}
|
||||
|
||||
if (newValue == null) {
|
||||
copiedDefault.remove(group);
|
||||
} else {
|
||||
copiedDefault.put(group, newValue);
|
||||
}
|
||||
});
|
||||
return copiedDefault;
|
||||
}
|
||||
|
||||
String mergeRemappingFunction(String dataV, String defaultV) {
|
||||
JsonNode dataJsonValue = nullSafeToJsonNode(dataV);
|
||||
// original
|
||||
JsonNode defaultJsonValue = nullSafeToJsonNode(defaultV);
|
||||
try {
|
||||
// patch
|
||||
JsonMergePatch jsonMergePatch = JsonMergePatch.fromJson(dataJsonValue);
|
||||
// apply patch to original
|
||||
JsonNode patchedNode = jsonMergePatch.apply(defaultJsonValue);
|
||||
return JsonUtils.objectToJson(patchedNode);
|
||||
} catch (JsonPatchException e) {
|
||||
throw new JsonParseException(e);
|
||||
}
|
||||
}
|
||||
|
||||
JsonNode nullSafeToJsonNode(String json) {
|
||||
return StringUtils.isBlank(json) ? JsonNodeFactory.instance.nullNode()
|
||||
: JsonUtils.jsonToObject(json, JsonNode.class);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import lombok.Data;
|
|||
* @since 2.0.0
|
||||
*/
|
||||
public class SystemSetting {
|
||||
public static final String SYSTEM_CONFIG_DEFAULT = "system-default";
|
||||
public static final String SYSTEM_CONFIG = "system";
|
||||
|
||||
@Data
|
||||
|
|
|
@ -16,7 +16,7 @@ import run.halo.app.infra.utils.JsonParseException;
|
|||
import run.halo.app.infra.utils.JsonUtils;
|
||||
|
||||
/**
|
||||
* <p>A value fetcher for pPlugin form configuration.</p>
|
||||
* <p>A value fetcher for plugin form configuration.</p>
|
||||
*
|
||||
* @author guqing
|
||||
* @since 2.0.0
|
||||
|
|
|
@ -2,8 +2,7 @@ package run.halo.app.theme.router;
|
|||
|
||||
import java.util.Map;
|
||||
import org.springframework.stereotype.Component;
|
||||
import run.halo.app.extension.ConfigMap;
|
||||
import run.halo.app.extension.ExtensionClient;
|
||||
import run.halo.app.infra.SystemConfigurableEnvironmentFetcher;
|
||||
import run.halo.app.infra.SystemSetting;
|
||||
import run.halo.app.infra.utils.JsonUtils;
|
||||
import run.halo.app.theme.DefaultTemplateEnum;
|
||||
|
@ -18,14 +17,14 @@ import run.halo.app.theme.DefaultTemplateEnum;
|
|||
@Component
|
||||
public class PermalinkPatternProvider {
|
||||
|
||||
private final ExtensionClient client;
|
||||
private final SystemConfigurableEnvironmentFetcher environmentFetcher;
|
||||
|
||||
public PermalinkPatternProvider(ExtensionClient client) {
|
||||
this.client = client;
|
||||
public PermalinkPatternProvider(SystemConfigurableEnvironmentFetcher environmentFetcher) {
|
||||
this.environmentFetcher = environmentFetcher;
|
||||
}
|
||||
|
||||
private SystemSetting.ThemeRouteRules getPermalinkRules() {
|
||||
return client.fetch(ConfigMap.class, SystemSetting.SYSTEM_CONFIG)
|
||||
return environmentFetcher.getConfigMapBlocking()
|
||||
.map(configMap -> {
|
||||
Map<String, String> data = configMap.getData();
|
||||
return data.get(SystemSetting.ThemeRouteRules.GROUP);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
apiVersion: v1alpha1
|
||||
kind: "ConfigMap"
|
||||
metadata:
|
||||
name: system
|
||||
name: system-default
|
||||
data:
|
||||
theme: |
|
||||
{
|
||||
|
|
|
@ -36,6 +36,7 @@ import run.halo.app.core.extension.service.UserService;
|
|||
import run.halo.app.extension.Metadata;
|
||||
import run.halo.app.extension.ReactiveExtensionClient;
|
||||
import run.halo.app.extension.exception.ExtensionNotFoundException;
|
||||
import run.halo.app.infra.SystemConfigurableEnvironmentFetcher;
|
||||
import run.halo.app.infra.utils.JsonUtils;
|
||||
|
||||
@SpringBootTest
|
||||
|
@ -55,6 +56,9 @@ class UserEndpointTest {
|
|||
@MockBean
|
||||
UserService userService;
|
||||
|
||||
@MockBean
|
||||
SystemConfigurableEnvironmentFetcher environmentFetcher;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
// disable authorization
|
||||
|
|
|
@ -8,7 +8,6 @@ import static org.mockito.Mockito.verify;
|
|||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
@ -23,6 +22,7 @@ import run.halo.app.extension.ConfigMap;
|
|||
import run.halo.app.extension.ExtensionClient;
|
||||
import run.halo.app.extension.Metadata;
|
||||
import run.halo.app.extension.controller.Reconciler;
|
||||
import run.halo.app.infra.SystemConfigurableEnvironmentFetcher;
|
||||
import run.halo.app.infra.SystemSetting;
|
||||
import run.halo.app.infra.utils.JsonUtils;
|
||||
|
||||
|
@ -41,11 +41,15 @@ class SystemSettingReconcilerTest {
|
|||
@Mock
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Mock
|
||||
private SystemConfigurableEnvironmentFetcher environmentFetcher;
|
||||
|
||||
private SystemSettingReconciler systemSettingReconciler;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
systemSettingReconciler = new SystemSettingReconciler(client, applicationContext);
|
||||
systemSettingReconciler = new SystemSettingReconciler(client, environmentFetcher,
|
||||
applicationContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -54,14 +58,14 @@ class SystemSettingReconcilerTest {
|
|||
rules.setArchives("archives-new");
|
||||
return rules;
|
||||
});
|
||||
when(environmentFetcher.getConfigMapBlocking()).thenReturn(Optional.of(configMap));
|
||||
when(client.fetch(eq(ConfigMap.class), eq(SystemSetting.SYSTEM_CONFIG)))
|
||||
.thenReturn(Optional.of(configMap));
|
||||
systemSettingReconciler.reconcile(new Reconciler.Request(SystemSetting.SYSTEM_CONFIG));
|
||||
ArgumentCaptor<ConfigMap> captor = ArgumentCaptor.forClass(ConfigMap.class);
|
||||
verify(client, times(2)).update(captor.capture());
|
||||
verify(client, times(1)).update(captor.capture());
|
||||
|
||||
List<ConfigMap> allValues = captor.getAllValues();
|
||||
ConfigMap updatedConfigMap = allValues.get(1);
|
||||
ConfigMap updatedConfigMap = captor.getValue();
|
||||
assertThat(rulesFrom(updatedConfigMap).getArchives()).isEqualTo("archives-new");
|
||||
assertThat(rulesFrom(updatedConfigMap).getPost()).isEqualTo("/archives-new/{slug}");
|
||||
|
||||
|
@ -78,14 +82,14 @@ class SystemSettingReconcilerTest {
|
|||
rules.setTags("tags-new");
|
||||
return rules;
|
||||
});
|
||||
when(environmentFetcher.getConfigMapBlocking()).thenReturn(Optional.of(configMap));
|
||||
when(client.fetch(eq(ConfigMap.class), eq(SystemSetting.SYSTEM_CONFIG)))
|
||||
.thenReturn(Optional.of(configMap));
|
||||
systemSettingReconciler.reconcile(new Reconciler.Request(SystemSetting.SYSTEM_CONFIG));
|
||||
ArgumentCaptor<ConfigMap> captor = ArgumentCaptor.forClass(ConfigMap.class);
|
||||
verify(client, times(2)).update(captor.capture());
|
||||
verify(client, times(1)).update(captor.capture());
|
||||
|
||||
List<ConfigMap> allValues = captor.getAllValues();
|
||||
ConfigMap updatedConfigMap = allValues.get(1);
|
||||
ConfigMap updatedConfigMap = captor.getValue();
|
||||
assertThat(rulesFrom(updatedConfigMap).getTags()).isEqualTo("tags-new");
|
||||
|
||||
assertThat(oldRulesFromAnno(updatedConfigMap).getTags()).isEqualTo("tags-new");
|
||||
|
@ -99,14 +103,14 @@ class SystemSettingReconcilerTest {
|
|||
rules.setCategories("categories-new");
|
||||
return rules;
|
||||
});
|
||||
when(environmentFetcher.getConfigMapBlocking()).thenReturn(Optional.of(configMap));
|
||||
when(client.fetch(eq(ConfigMap.class), eq(SystemSetting.SYSTEM_CONFIG)))
|
||||
.thenReturn(Optional.of(configMap));
|
||||
systemSettingReconciler.reconcile(new Reconciler.Request(SystemSetting.SYSTEM_CONFIG));
|
||||
ArgumentCaptor<ConfigMap> captor = ArgumentCaptor.forClass(ConfigMap.class);
|
||||
verify(client, times(2)).update(captor.capture());
|
||||
verify(client, times(1)).update(captor.capture());
|
||||
|
||||
List<ConfigMap> allValues = captor.getAllValues();
|
||||
ConfigMap updatedConfigMap = allValues.get(1);
|
||||
ConfigMap updatedConfigMap = captor.getValue();
|
||||
assertThat(rulesFrom(updatedConfigMap).getCategories()).isEqualTo("categories-new");
|
||||
|
||||
assertThat(oldRulesFromAnno(updatedConfigMap).getCategories()).isEqualTo("categories-new");
|
||||
|
@ -120,14 +124,14 @@ class SystemSettingReconcilerTest {
|
|||
rules.setPost("/post-new/{slug}");
|
||||
return rules;
|
||||
});
|
||||
when(environmentFetcher.getConfigMapBlocking()).thenReturn(Optional.of(configMap));
|
||||
when(client.fetch(eq(ConfigMap.class), eq(SystemSetting.SYSTEM_CONFIG)))
|
||||
.thenReturn(Optional.of(configMap));
|
||||
systemSettingReconciler.reconcile(new Reconciler.Request(SystemSetting.SYSTEM_CONFIG));
|
||||
ArgumentCaptor<ConfigMap> captor = ArgumentCaptor.forClass(ConfigMap.class);
|
||||
verify(client, times(2)).update(captor.capture());
|
||||
verify(client, times(1)).update(captor.capture());
|
||||
|
||||
List<ConfigMap> allValues = captor.getAllValues();
|
||||
ConfigMap updatedConfigMap = allValues.get(1);
|
||||
ConfigMap updatedConfigMap = captor.getValue();
|
||||
assertThat(rulesFrom(updatedConfigMap).getPost()).isEqualTo("/post-new/{slug}");
|
||||
|
||||
assertThat(oldRulesFromAnno(updatedConfigMap).getPost()).isEqualTo("/post-new/{slug}");
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
package run.halo.app.infra;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import org.json.JSONException;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
import run.halo.app.extension.ConfigMap;
|
||||
import run.halo.app.extension.Metadata;
|
||||
import run.halo.app.extension.ReactiveExtensionClient;
|
||||
import run.halo.app.infra.utils.JsonUtils;
|
||||
|
||||
/**
|
||||
* Tests for {@link SystemConfigurableEnvironmentFetcher}.
|
||||
*
|
||||
* @author guqing
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class SystemConfigurableEnvironmentFetcherTest {
|
||||
|
||||
@Mock
|
||||
private ReactiveExtensionClient client;
|
||||
|
||||
@InjectMocks
|
||||
private SystemConfigurableEnvironmentFetcher environmentFetcher;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
lenient().when(client.fetch(eq(ConfigMap.class), eq("system-default")))
|
||||
.thenReturn(Mono.just(systemDefault()));
|
||||
lenient().when(client.fetch(eq(ConfigMap.class), eq("system")))
|
||||
.thenReturn(Mono.just(system()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getConfigMap() {
|
||||
environmentFetcher.getConfigMap()
|
||||
.as(StepVerifier::create)
|
||||
.consumeNextWith(configMap -> {
|
||||
assertThat(configMap.getMetadata().getName())
|
||||
.isEqualTo(SystemSetting.SYSTEM_CONFIG);
|
||||
try {
|
||||
JSONAssert.assertEquals(expectedJson(),
|
||||
JsonUtils.objectToJson(configMap),
|
||||
true);
|
||||
} catch (JSONException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
String expectedJson() {
|
||||
String routeRules =
|
||||
"{\\\"categories\\\":\\\"topics\\\",\\\"archives\\\":\\\"archives-new\\\","
|
||||
+ "\\\"post\\\":\\\"/archives-new/{slug}\\\"}";
|
||||
String fakeArray = "{\\\"select\\\":[{\\\"label\\\":\\\"Hello\\\","
|
||||
+ "\\\"value\\\":\\\"hello\\\"},{\\\"label\\\":\\\"Awesome\\\","
|
||||
+ "\\\"value\\\":\\\"awesome\\\"}]}";
|
||||
return """
|
||||
{
|
||||
"data": {
|
||||
"routeRules": "%s",
|
||||
"seo": "{\\"blockSpiders\\":\\"true\\",\\"keywords\\":\\"Hello,Test,Fake\\"}",
|
||||
"fakeArray": "%s"
|
||||
},
|
||||
"apiVersion": "v1alpha1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": {
|
||||
"name": "system"
|
||||
}
|
||||
}
|
||||
""".formatted(routeRules, fakeArray);
|
||||
}
|
||||
|
||||
ConfigMap systemDefault() {
|
||||
ConfigMap configMap = new ConfigMap();
|
||||
configMap.setMetadata(new Metadata());
|
||||
configMap.getMetadata().setName("system-default");
|
||||
configMap.setData(new LinkedHashMap<>());
|
||||
configMap.getData().put("routeRules", """
|
||||
{
|
||||
"categories": "categories",
|
||||
"archives": "archives",
|
||||
"post": "/archives/{slug}",
|
||||
"tags": "tags"
|
||||
}
|
||||
"""
|
||||
);
|
||||
configMap.getData().put("seo", """
|
||||
{
|
||||
"blockSpiders": "false",
|
||||
"keywords": "Hello,Test,Fake"
|
||||
}
|
||||
"""
|
||||
);
|
||||
configMap.getData().put("post", """
|
||||
{
|
||||
"pageSize": "10"
|
||||
}
|
||||
"""
|
||||
);
|
||||
configMap.getData().put("fakeArray", """
|
||||
{
|
||||
"select": [{
|
||||
"label": "Hello",
|
||||
"value": "hello"
|
||||
}, {
|
||||
"label": "Test",
|
||||
"value": "test"
|
||||
}]
|
||||
}
|
||||
"""
|
||||
);
|
||||
return configMap;
|
||||
}
|
||||
|
||||
ConfigMap system() {
|
||||
ConfigMap configMap = new ConfigMap();
|
||||
configMap.setMetadata(new Metadata());
|
||||
configMap.getMetadata().setName("system");
|
||||
configMap.setData(new LinkedHashMap<>());
|
||||
// will delete the tags key and replace some values
|
||||
configMap.getData().put("routeRules", """
|
||||
{
|
||||
"categories": "topics",
|
||||
"archives": "archives-new",
|
||||
"post": "/archives-new/{slug}",
|
||||
"tags": null
|
||||
}
|
||||
"""
|
||||
);
|
||||
configMap.getData().put("seo", """
|
||||
{
|
||||
"blockSpiders": "true"
|
||||
}
|
||||
"""
|
||||
);
|
||||
|
||||
// deleted post group here
|
||||
configMap.getData().put("post", null);
|
||||
|
||||
configMap.getData().put("fakeArray", """
|
||||
{
|
||||
"select": [{
|
||||
"label": "Hello",
|
||||
"value": "hello"
|
||||
}, {
|
||||
"label": "Awesome",
|
||||
"value": "awesome"
|
||||
}]
|
||||
}
|
||||
"""
|
||||
);
|
||||
return configMap;
|
||||
}
|
||||
}
|
|
@ -1,19 +1,18 @@
|
|||
package run.halo.app.theme.router;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import run.halo.app.extension.ConfigMap;
|
||||
import run.halo.app.extension.ExtensionClient;
|
||||
import run.halo.app.extension.Metadata;
|
||||
import run.halo.app.infra.SystemConfigurableEnvironmentFetcher;
|
||||
import run.halo.app.infra.SystemSetting;
|
||||
import run.halo.app.infra.utils.JsonUtils;
|
||||
import run.halo.app.theme.DefaultTemplateEnum;
|
||||
|
@ -28,18 +27,14 @@ import run.halo.app.theme.DefaultTemplateEnum;
|
|||
class PermalinkPatternProviderTest {
|
||||
|
||||
@Mock
|
||||
private ExtensionClient client;
|
||||
private SystemConfigurableEnvironmentFetcher environmentFetcher;
|
||||
|
||||
@InjectMocks
|
||||
private PermalinkPatternProvider permalinkPatternProvider;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
permalinkPatternProvider = new PermalinkPatternProvider(client);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getPatternThenDefault() {
|
||||
when(client.fetch(eq(ConfigMap.class), eq(SystemSetting.SYSTEM_CONFIG)))
|
||||
when(environmentFetcher.getConfigMapBlocking())
|
||||
.thenReturn(Optional.empty());
|
||||
|
||||
String pattern = permalinkPatternProvider.getPattern(DefaultTemplateEnum.POST);
|
||||
|
@ -79,7 +74,7 @@ class PermalinkPatternProviderTest {
|
|||
|
||||
configMap.setData(Map.of("routeRules", JsonUtils.objectToJson(themeRouteRules)));
|
||||
|
||||
when(client.fetch(eq(ConfigMap.class), eq(SystemSetting.SYSTEM_CONFIG)))
|
||||
when(environmentFetcher.getConfigMapBlocking())
|
||||
.thenReturn(Optional.of(configMap));
|
||||
|
||||
String pattern = permalinkPatternProvider.getPattern(DefaultTemplateEnum.POST);
|
||||
|
|
Loading…
Reference in New Issue