refactor: the value structure of ConfigMap for Setting custom extension (#2243)

<!--  Thanks for sending a pull request!  Here are some tips for you:
1. 如果这是你的第一次,请阅读我们的贡献指南:<https://github.com/halo-dev/halo/blob/master/CONTRIBUTING.md>。
1. If this is your first time, please read our contributor guidelines: <https://github.com/halo-dev/halo/blob/master/CONTRIBUTING.md>.
2. 请根据你解决问题的类型为 Pull Request 添加合适的标签。
2. Please label this pull request according to what type of issue you are addressing, especially if this is a release targeted pull request.
3. 请确保你已经添加并运行了适当的测试。
3. Ensure you have added or ran the appropriate tests for your PR.
-->

#### What type of PR is this?
/kind improvement
/area core
/milestone 2.0
<!--
添加其中一个类别:
Add one of the following kinds:

/kind bug
/kind cleanup
/kind documentation
/kind feature
/kind improvement

适当添加其中一个或多个类别(可选):
Optionally add one or more of the following kinds if applicable:

/kind api-change
/kind deprecation
/kind failing-test
/kind flake
/kind regression
-->

#### What this PR does / why we need it:
由于 Setting 自定义模型关联表单值的存储结构改变,对应修改 SettingFetcher 的取值方式
https://github.com/halo-dev/rfcs/pull/18
#### Which issue(s) this PR fixes:

<!--
PR 合并时自动关闭 issue。
Automatically closes linked issue when PR is merged.

用法:`Fixes #<issue 号>`,或者 `Fixes (粘贴 issue 完整链接)`
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
-->
Fixes #

#### Special notes for your reviewer:
/cc @halo-dev/sig-halo 
#### Does this PR introduce a user-facing change?

<!--
如果当前 Pull Request 的修改不会造成用户侧的任何变更,在 `release-note` 代码块儿中填写 `NONE`。
否则请填写用户侧能够理解的 Release Note。如果当前 Pull Request 包含破坏性更新(Break Change),
Release Note 需要以 `action required` 开头。
If no, just write "NONE" in the release-note block below.
If yes, a release note is required:
Enter your extended release note in the block below. If the PR requires additional action from users switching to the new release, include the string "action required".
-->

```release-note
None
```
pull/2247/head
guqing 2022-07-14 16:19:09 +08:00 committed by GitHub
parent 55040d6918
commit a8db2e5e4b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 31 additions and 20 deletions

View File

@ -135,7 +135,7 @@ public class UserEndpoint implements CustomEndpoint {
.parameter(parameterBuilder().in(ParameterIn.PATH).name("name") .parameter(parameterBuilder().in(ParameterIn.PATH).name("name")
.description("User name") .description("User name")
.required(true)) .required(true))
.response(responseBuilder().implementation(Set.class))) .response(responseBuilder().implementation(UserPermission.class)))
.build(); .build();
} }

View File

@ -3,8 +3,11 @@ package run.halo.app.plugin;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import java.util.Collection;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.lang.NonNull; import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
@ -25,9 +28,7 @@ import run.halo.app.infra.utils.JsonUtils;
*/ */
public class SettingFetcher { public class SettingFetcher {
private static final String PLUGIN_SETTING_VALUE = "setting"; private final AtomicReference<Map<String, JsonNode>> valueRef = new AtomicReference<>(null);
private final AtomicReference<JsonNode> valueRef = new AtomicReference<>(null);
private final ExtensionClient extensionClient; private final ExtensionClient extensionClient;
@ -49,9 +50,16 @@ public class SettingFetcher {
return getInternal(group); return getInternal(group);
} }
/**
* Get values from {@link ConfigMap}.
*
* @return a unmodifiable map of values(non-null).
*/
@NonNull @NonNull
public JsonNode getValues() { public Map<String, JsonNode> getValues() {
return valueRef.updateAndGet(m -> m != null ? m : getValuesInternal()); Map<String, JsonNode> values =
valueRef.updateAndGet(m -> m != null ? m : getValuesInternal());
return Map.copyOf(values);
} }
private JsonNode getInternal(String group) { private JsonNode getInternal(String group) {
@ -59,13 +67,14 @@ public class SettingFetcher {
.orElse(JsonNodeFactory.instance.missingNode()); .orElse(JsonNodeFactory.instance.missingNode());
} }
private JsonNode getValuesInternal() { private Map<String, JsonNode> getValuesInternal() {
return configMap(pluginName) return configMap(pluginName)
.filter(configMap -> configMap.getData() != null .filter(configMap -> configMap.getData() != null)
&& configMap.getData().containsKey(PLUGIN_SETTING_VALUE)) .map(ConfigMap::getData)
.map(configMap -> configMap.getData().get(PLUGIN_SETTING_VALUE)) .map(Map::entrySet)
.map(this::readTree) .stream()
.orElse(JsonNodeFactory.instance.missingNode()); .flatMap(Collection::stream)
.collect(Collectors.toMap(Map.Entry::getKey, entry -> readTree(entry.getValue())));
} }
private Optional<ConfigMap> configMap(String pluginName) { private Optional<ConfigMap> configMap(String pluginName) {
@ -80,6 +89,9 @@ public class SettingFetcher {
} }
private JsonNode readTree(String json) { private JsonNode readTree(String json) {
if (StringUtils.isBlank(json)) {
return JsonNodeFactory.instance.missingNode();
}
try { try {
return JsonUtils.DEFAULT_JSON_MAPPER.readTree(json); return JsonUtils.DEFAULT_JSON_MAPPER.readTree(json);
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {

View File

@ -54,7 +54,7 @@ class SettingFetcherTest {
@Test @Test
void getValues() throws JSONException { void getValues() throws JSONException {
JsonNode values = settingFetcher.getValues(); Map<String, JsonNode> values = settingFetcher.getValues();
verify(extensionClient, times(1)).fetch(eq(ConfigMap.class), any()); verify(extensionClient, times(1)).fetch(eq(ConfigMap.class), any());
@ -62,7 +62,7 @@ class SettingFetcherTest {
JSONAssert.assertEquals(getSns(), JsonUtils.objectToJson(values.get("sns")), true); JSONAssert.assertEquals(getSns(), JsonUtils.objectToJson(values.get("sns")), true);
// The extensionClient will only be called once // The extensionClient will only be called once
JsonNode callAgain = settingFetcher.getValues(); Map<String, JsonNode> callAgain = settingFetcher.getValues();
assertThat(callAgain).isNotNull(); assertThat(callAgain).isNotNull();
verify(extensionClient, times(1)).fetch(eq(ConfigMap.class), any()); verify(extensionClient, times(1)).fetch(eq(ConfigMap.class), any());
} }
@ -98,15 +98,14 @@ class SettingFetcherTest {
configMap.setMetadata(metadata); configMap.setMetadata(metadata);
configMap.setKind("ConfigMap"); configMap.setKind("ConfigMap");
configMap.setApiVersion("v1alpha1"); configMap.setApiVersion("v1alpha1");
configMap.setData(Map.of("setting", String.format(""" configMap.setData(Map.of("sns", getSns(),
{ "basic", """
"sns": %s, {
"basic": {
"color": "red", "color": "red",
"width": "100" "width": "100"
} }
} """)
""", getSns()))); );
return configMap; return configMap;
} }