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
guqing 2022-09-21 12:06:10 +08:00 committed by GitHub
parent 1714f8edb2
commit ac8dd74211
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 349 additions and 42 deletions

View File

@ -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'

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
apiVersion: v1alpha1
kind: "ConfigMap"
metadata:
name: system
name: system-default
data:
theme: |
{

View File

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

View File

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

View File

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

View File

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