Skip to content

Commit

Permalink
refactor: optimize theme and plugin config retrieval and saving
Browse files Browse the repository at this point in the history
  • Loading branch information
guqing committed Sep 14, 2024
1 parent 46793af commit 8762122
Show file tree
Hide file tree
Showing 9 changed files with 177 additions and 169 deletions.
32 changes: 10 additions & 22 deletions api-docs/openapi/v3_0/aggregated.json
Original file line number Diff line number Diff line change
Expand Up @@ -2888,7 +2888,7 @@
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ConfigMap"
"type": "object"
}
}
},
Expand Down Expand Up @@ -2916,22 +2916,16 @@
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ConfigMap"
"type": "object"
}
}
},
"required": true
},
"responses": {
"default": {
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ConfigMap"
}
}
},
"description": "default response"
"204": {
"content": {},
"description": "No Content"
}
},
"tags": [
Expand Down Expand Up @@ -4562,7 +4556,7 @@
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ConfigMap"
"type": "object"
}
}
},
Expand Down Expand Up @@ -4590,22 +4584,16 @@
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ConfigMap"
"type": "object"
}
}
},
"required": true
},
"responses": {
"default": {
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ConfigMap"
}
}
},
"description": "default response"
"204": {
"content": {},
"description": "No Content"
}
},
"tags": [
Expand Down
32 changes: 10 additions & 22 deletions api-docs/openapi/v3_0/apis_console.api_v1alpha1.json
Original file line number Diff line number Diff line change
Expand Up @@ -755,7 +755,7 @@
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ConfigMap"
"type": "object"
}
}
},
Expand Down Expand Up @@ -783,22 +783,16 @@
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ConfigMap"
"type": "object"
}
}
},
"required": true
},
"responses": {
"default": {
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ConfigMap"
}
}
},
"description": "default response"
"204": {
"content": {},
"description": "No Content"
}
},
"tags": [
Expand Down Expand Up @@ -2429,7 +2423,7 @@
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ConfigMap"
"type": "object"
}
}
},
Expand Down Expand Up @@ -2457,22 +2451,16 @@
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ConfigMap"
"type": "object"
}
}
},
"required": true
},
"responses": {
"default": {
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ConfigMap"
}
}
},
"description": "default response"
"204": {
"content": {},
"description": "No Content"
}
},
"tags": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import static run.halo.app.extension.router.QueryParamBuildUtil.sortParameter;
import static run.halo.app.infra.utils.FileUtils.deleteFileSilently;

import com.fasterxml.jackson.databind.node.ObjectNode;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.Schema;
import java.io.FileNotFoundException;
Expand All @@ -41,6 +42,7 @@
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.data.domain.Sort;
import org.springframework.http.CacheControl;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.multipart.FilePart;
import org.springframework.http.codec.multipart.FormFieldPart;
Expand All @@ -61,6 +63,7 @@
import run.halo.app.core.extension.Plugin;
import run.halo.app.core.extension.Setting;
import run.halo.app.core.extension.service.PluginService;
import run.halo.app.core.extension.service.SettingConfigService;
import run.halo.app.core.extension.theme.SettingUtils;
import run.halo.app.extension.ConfigMap;
import run.halo.app.extension.ListOptions;
Expand All @@ -80,6 +83,8 @@ public class PluginEndpoint implements CustomEndpoint, InitializingBean {

private final ReactiveUrlDataBufferFetcher reactiveUrlDataBufferFetcher;

private final SettingConfigService settingConfigService;

private final WebProperties webProperties;

private final Scheduler scheduler = Schedulers.boundedElastic();
Expand All @@ -91,10 +96,12 @@ public class PluginEndpoint implements CustomEndpoint, InitializingBean {
public PluginEndpoint(ReactiveExtensionClient client,
PluginService pluginService,
ReactiveUrlDataBufferFetcher reactiveUrlDataBufferFetcher,
SettingConfigService settingConfigService,
WebProperties webProperties) {
this.client = client;
this.pluginService = pluginService;
this.reactiveUrlDataBufferFetcher = reactiveUrlDataBufferFetcher;
this.settingConfigService = settingConfigService;
this.webProperties = webProperties;
}

Expand Down Expand Up @@ -170,9 +177,10 @@ public RouterFunction<ServerResponse> endpoint() {
.requestBody(requestBodyBuilder()
.required(true)
.content(contentBuilder().mediaType(MediaType.APPLICATION_JSON_VALUE)
.schema(schemaBuilder().implementation(ConfigMap.class))))
.schema(schemaBuilder().implementation(ObjectNode.class))))
.response(responseBuilder()
.implementation(ConfigMap.class))
.responseCode(String.valueOf(HttpStatus.NO_CONTENT.value()))
.implementation(Void.class))
)
.PUT("plugins/{name}/reset-config", this::resetSettingConfig,
builder -> builder.operationId("ResetPluginConfig")
Expand Down Expand Up @@ -252,7 +260,7 @@ public RouterFunction<ServerResponse> endpoint() {
.implementation(String.class)
)
.response(responseBuilder()
.implementation(ConfigMap.class))
.implementation(ObjectNode.class))
)
.GET("plugin-presets", this::listPresets,
builder -> builder.operationId("ListPluginPresets")
Expand Down Expand Up @@ -407,8 +415,8 @@ private Mono<ServerResponse> fetchPluginConfig(ServerRequest request) {
final var name = request.pathVariable("name");
return client.fetch(Plugin.class, name)
.mapNotNull(plugin -> plugin.getSpec().getConfigMapName())
.flatMap(configMapName -> client.fetch(ConfigMap.class, configMapName))
.flatMap(configMap -> ServerResponse.ok().bodyValue(configMap));
.flatMap(settingConfigService::fetchConfig)
.flatMap(json -> ServerResponse.ok().bodyValue(json));
}

private Mono<ServerResponse> fetchPluginSetting(ServerRequest request) {
Expand All @@ -431,29 +439,11 @@ private Mono<ServerResponse> updatePluginConfig(ServerRequest request) {
})
.flatMap(plugin -> {
final String configMapName = plugin.getSpec().getConfigMapName();
return request.bodyToMono(ConfigMap.class)
.doOnNext(configMapToUpdate -> {
var configMapNameToUpdate = configMapToUpdate.getMetadata().getName();
if (!configMapName.equals(configMapNameToUpdate)) {
throw new ServerWebInputException(
"The name from the request body does not match the plugin "
+ "configMapName name.");
}
})
.flatMap(configMapToUpdate -> client.fetch(ConfigMap.class, configMapName)
.map(persisted -> {
configMapToUpdate.getMetadata()
.setVersion(persisted.getMetadata().getVersion());
return configMapToUpdate;
})
.switchIfEmpty(client.create(configMapToUpdate))
)
.flatMap(client::update)
.retryWhen(Retry.backoff(5, Duration.ofMillis(300))
.filter(OptimisticLockingFailureException.class::isInstance)
);
return request.bodyToMono(ObjectNode.class)
.flatMap(configJsonData ->
settingConfigService.upsertConfig(configMapName, configJsonData));
})
.flatMap(configMap -> ServerResponse.ok().bodyValue(configMap));
.then(ServerResponse.noContent().build());
}

private Mono<ServerResponse> resetSettingConfig(ServerRequest request) {
Expand All @@ -473,16 +463,16 @@ private Mono<ServerResponse> resetSettingConfig(ServerRequest request) {
}

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))
return Mono.defer(() -> client.fetch(ConfigMap.class, configMapName)
.flatMap(configMap -> {
configMap.setData(data);
return client.update(configMap);
})
)
.retryWhen(Retry.backoff(5, Duration.ofMillis(100))
.filter(t -> t instanceof OptimisticLockingFailureException));
}


private Mono<ServerResponse> install(ServerRequest request) {
return request.multipartData()
.map(InstallRequest::new)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package run.halo.app.core.extension.service;

import com.fasterxml.jackson.databind.node.ObjectNode;
import reactor.core.publisher.Mono;
import run.halo.app.core.extension.Setting;
import run.halo.app.extension.ConfigMap;

/**
* {@link Setting} related {@link ConfigMap} service.
*
* @author guqing
* @since 2.20.0
*/
public interface SettingConfigService {

Mono<Void> upsertConfig(String configMapName, ObjectNode configJsonData);

Mono<ObjectNode> fetchConfig(String configMapName);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package run.halo.app.core.extension.service.impl;

import com.fasterxml.jackson.databind.node.ObjectNode;
import java.time.Duration;
import lombok.RequiredArgsConstructor;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import reactor.core.publisher.Mono;
import reactor.util.retry.Retry;
import run.halo.app.core.extension.Setting;
import run.halo.app.core.extension.service.SettingConfigService;
import run.halo.app.core.extension.theme.SettingUtils;
import run.halo.app.extension.ConfigMap;
import run.halo.app.extension.Metadata;
import run.halo.app.extension.ReactiveExtensionClient;

/**
* {@link Setting} related {@link ConfigMap} service implementation.
*
* @author guqing
* @since 2.20.0
*/
@Component
@RequiredArgsConstructor
public class SettingConfigServiceImpl implements SettingConfigService {
private final ReactiveExtensionClient client;

@Override
public Mono<Void> upsertConfig(String configMapName, ObjectNode configJsonData) {
Assert.notNull(configMapName, "Config map name must not be null");
Assert.notNull(configJsonData, "Config json data must not be null");
var data = SettingUtils.settingConfigJsonToMap(configJsonData);
return Mono.defer(() -> client.fetch(ConfigMap.class, configMapName)
.flatMap(persisted -> {
persisted.setData(data);
return client.update(persisted);
}))
.retryWhen(Retry.backoff(5, Duration.ofMillis(300))
.filter(OptimisticLockingFailureException.class::isInstance)
)
.switchIfEmpty(Mono.defer(() -> {
var configMap = new ConfigMap();
configMap.setMetadata(new Metadata());
configMap.getMetadata().setName(configMapName);
configMap.setData(data);
return client.create(configMap);
}))
.then();
}

@Override
public Mono<ObjectNode> fetchConfig(String configMapName) {
return client.fetch(ConfigMap.class, configMapName)
.map(SettingUtils::settingConfigToJson);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,30 @@ public static Map<String, String> mergePatch(Map<String, String> modified,
}
}

JsonNode mapToJsonNode(Map<String, String> map) {
/**
* Convert {@link Setting} related configMap data to JsonNode.
*
* @param configMap {@link ConfigMap} instance
* @return JsonNode
*/
public static ObjectNode settingConfigToJson(ConfigMap configMap) {
if (configMap.getData() == null) {
return JsonNodeFactory.instance.objectNode();
}
return mapToJsonNode(configMap.getData());
}

/**
* Convert the result of {@link #settingConfigToJson(ConfigMap)} in reverse to Map.
*
* @param node JsonNode object
* @return {@link ConfigMap#getData()}
*/
public static Map<String, String> settingConfigJsonToMap(ObjectNode node) {
return jsonNodeToStringMap(node);
}

ObjectNode mapToJsonNode(Map<String, String> map) {
ObjectNode objectNode = JsonNodeFactory.instance.objectNode();
map.forEach((k, v) -> {
if (isJson(v)) {
Expand Down
Loading

0 comments on commit 8762122

Please sign in to comment.