Skip to content

Commit

Permalink
Merge branch 'main' into feature/system-info
Browse files Browse the repository at this point in the history
  • Loading branch information
ruibaby authored Dec 4, 2024
2 parents 57c19dd + d06b40c commit 2a1ffc5
Show file tree
Hide file tree
Showing 24 changed files with 278 additions and 443 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public RouterFunction<ServerResponse> endpoint() {
private Mono<ServerResponse> updateConfigByGroup(ServerRequest request) {
final var group = request.pathVariable("group");
return request.bodyToMono(ObjectNode.class)
.flatMap(objectNode -> configurableEnvironmentFetcher.getConfigMap()
.flatMap(objectNode -> configurableEnvironmentFetcher.loadConfigMap()
.flatMap(configMap -> {
var data = configMap.getData();
data.put(group, JsonUtils.objectToJson(objectNode));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,6 @@ public boolean isSystemSetting(String name) {
}

private Optional<ConfigMap> getConfigMap(String name) {
return environmentFetcher.getConfigMapBlocking();
return environmentFetcher.loadConfigMapBlocking();
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,28 @@
package run.halo.app.infra;

import static run.halo.app.extension.index.query.QueryFactory.equal;

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.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
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.ExtensionMatcher;
import run.halo.app.extension.ListOptions;
import run.halo.app.extension.ReactiveExtensionClient;
import run.halo.app.extension.controller.Controller;
import run.halo.app.extension.controller.ControllerBuilder;
import run.halo.app.extension.controller.Reconciler;
import run.halo.app.infra.utils.JsonParseException;
import run.halo.app.infra.utils.JsonUtils;

Expand All @@ -27,9 +36,10 @@
* @since 2.0.0
*/
@Component
public class SystemConfigurableEnvironmentFetcher {
public class SystemConfigurableEnvironmentFetcher implements Reconciler<Reconciler.Request> {
private final ReactiveExtensionClient extensionClient;
private final ConversionService conversionService;
private final AtomicReference<ConfigMap> configMapCache = new AtomicReference<>();

public SystemConfigurableEnvironmentFetcher(ReactiveExtensionClient extensionClient,
ConversionService conversionService) {
Expand Down Expand Up @@ -71,31 +81,27 @@ private Mono<Map<String, String>> getValuesInternal() {
.defaultIfEmpty(Map.of());
}

public Mono<ConfigMap> getConfigMap() {
return Mono.fromSupplier(configMapCache::get)
.switchIfEmpty(Mono.defer(this::loadConfigMapInternal));
}

/**
* Gets config map.
* Load the system config map from the extension client.
*
* @return a new {@link ConfigMap} named <code>system</code> by json merge patch.
* @return latest configMap from {@link ReactiveExtensionClient} without any cache.
*/
public Mono<ConfigMap> getConfigMap() {
Mono<ConfigMap> mapMono =
extensionClient.fetch(ConfigMap.class, SystemSetting.SYSTEM_CONFIG_DEFAULT);
if (mapMono == null) {
return Mono.empty();
}
return mapMono.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 Mono<ConfigMap> loadConfigMap() {
return loadConfigMapInternal();
}

public Optional<ConfigMap> getConfigMapBlocking() {
return getConfigMap().blockOptional();
/**
* Gets the system config map without any cache.
*
* @return load configMap from {@link ReactiveExtensionClient}
*/
public Optional<ConfigMap> loadConfigMapBlocking() {
return loadConfigMapInternal().blockOptional();
}

private Map<String, String> mergeData(Map<String, String> defaultData,
Expand Down Expand Up @@ -132,7 +138,7 @@ private Map<String, String> mergeData(Map<String, String> defaultData,
return copiedDefault;
}

String mergeRemappingFunction(String dataV, String defaultV) {
private String mergeRemappingFunction(String dataV, String defaultV) {
JsonNode dataJsonValue = nullSafeToJsonNode(dataV);
// original
JsonNode defaultJsonValue = nullSafeToJsonNode(defaultV);
Expand All @@ -147,8 +153,57 @@ String mergeRemappingFunction(String dataV, String defaultV) {
}
}

JsonNode nullSafeToJsonNode(String json) {
private JsonNode nullSafeToJsonNode(String json) {
return StringUtils.isBlank(json) ? JsonNodeFactory.instance.nullNode()
: JsonUtils.jsonToObject(json, JsonNode.class);
}

@Override
public Result reconcile(Request request) {
loadConfigMapInternal()
// should never happen
.switchIfEmpty(Mono.error(new IllegalStateException("System configMap not found.")))
.doOnNext(configMapCache::set)
.block();
return Result.doNotRetry();
}

@Override
public Controller setupWith(ControllerBuilder builder) {
ExtensionMatcher matcher = extension -> Objects.equals(extension.getMetadata().getName(),
SystemSetting.SYSTEM_CONFIG);
return builder
.extension(new ConfigMap())
.syncAllOnStart(true)
.syncAllListOptions(ListOptions.builder()
.fieldQuery(equal("metadata.name", SystemSetting.SYSTEM_CONFIG))
.build())
.onAddMatcher(matcher)
.onUpdateMatcher(matcher)
.onDeleteMatcher(matcher)
.build();
}

/**
* Gets config map.
*
* @return a new {@link ConfigMap} named <code>system</code> by json merge patch.
*/
private Mono<ConfigMap> loadConfigMapInternal() {
Mono<ConfigMap> mapMono =
extensionClient.fetch(ConfigMap.class, SystemSetting.SYSTEM_CONFIG_DEFAULT);
if (mapMono == null) {
return Mono.empty();
}
return mapMono.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)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package run.halo.app.plugin.extensionpoint;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.DisposableBean;
import run.halo.app.extension.Extension;
import run.halo.app.extension.ExtensionClient;
import run.halo.app.extension.controller.Controller;
import run.halo.app.extension.controller.ControllerBuilder;
import run.halo.app.extension.controller.Reconciler;

@RequiredArgsConstructor
abstract class AbstractDefinitionGetter<E extends Extension>
implements Reconciler<Reconciler.Request>, DisposableBean {

protected final ConcurrentMap<String, E> cache = new ConcurrentHashMap<>();

private final ExtensionClient client;

private final E watchType;

abstract void putCache(E definition);

@Override
@SuppressWarnings("unchecked")
public Result reconcile(Request request) {
client.fetch((Class<E>) watchType.getClass(), request.name())
.ifPresent(this::putCache);
return Result.doNotRetry();
}

@Override
public Controller setupWith(ControllerBuilder builder) {
return builder.extension(watchType)
.syncAllOnStart(true)
.build();
}

@Override
public void destroy() throws Exception {
cache.clear();
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package run.halo.app.plugin.extensionpoint;

import static run.halo.app.extension.index.query.QueryFactory.equal;

import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
Expand All @@ -11,15 +9,9 @@
import org.pf4j.PluginManager;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import run.halo.app.extension.ListOptions;
import run.halo.app.extension.ListResult;
import run.halo.app.extension.PageRequestImpl;
import run.halo.app.extension.ReactiveExtensionClient;
import run.halo.app.extension.router.selector.FieldSelector;
import run.halo.app.infra.SystemConfigurableEnvironmentFetcher;
import run.halo.app.infra.SystemSetting.ExtensionPointEnabled;

Expand All @@ -33,7 +25,9 @@ public class DefaultExtensionGetter implements ExtensionGetter {

private final BeanFactory beanFactory;

private final ReactiveExtensionClient client;
private final ExtensionDefinitionGetter extensionDefinitionGetter;

private final ExtensionPointDefinitionGetter extensionPointDefinitionGetter;

@Override
public <T extends ExtensionPoint> Flux<T> getExtensions(Class<T> extensionPoint) {
Expand Down Expand Up @@ -86,9 +80,7 @@ private <T extends ExtensionPoint> Flux<T> getEnabledExtensions(String epdName,
}
var extensions = getExtensions(extensionPoint).cache();
return Flux.fromIterable(extensionDefNames)
.flatMapSequential(extensionDefName ->
client.fetch(ExtensionDefinition.class, extensionDefName)
)
.flatMapSequential(extensionDefinitionGetter::get)
.flatMapSequential(extensionDef -> {
var className = extensionDef.getSpec().getClassName();
return extensions.filter(
Expand All @@ -101,15 +93,7 @@ private <T extends ExtensionPoint> Flux<T> getEnabledExtensions(String epdName,

private Mono<ExtensionPointDefinition> fetchExtensionPointDefinition(
Class<? extends ExtensionPoint> extensionPoint) {
var listOptions = new ListOptions();
listOptions.setFieldSelector(FieldSelector.of(
equal("spec.className", extensionPoint.getName())
));
var sort = Sort.by("metadata.creationTimestamp", "metadata.name").ascending();
return client.listBy(ExtensionPointDefinition.class, listOptions,
PageRequestImpl.ofSize(1).withSort(sort)
)
.flatMap(list -> Mono.justOrEmpty(ListResult.first(list)));
return extensionPointDefinitionGetter.getByClassName(extensionPoint.getName());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package run.halo.app.plugin.extensionpoint;

import reactor.core.publisher.Mono;

public interface ExtensionDefinitionGetter {

/**
* Gets extension definition by extension definition name.
*
* @param name extension definition name
*/
Mono<ExtensionDefinition> get(String name);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package run.halo.app.plugin.extensionpoint;

import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
import run.halo.app.extension.ExtensionClient;

@Component
public class ExtensionDefinitionGetterImpl
extends AbstractDefinitionGetter<ExtensionDefinition>
implements ExtensionDefinitionGetter {

public ExtensionDefinitionGetterImpl(ExtensionClient client) {
super(client, new ExtensionDefinition());
}

@Override
public Mono<ExtensionDefinition> get(String name) {
return Mono.fromSupplier(() -> cache.get(name));
}

@Override
void putCache(ExtensionDefinition definition) {
cache.put(definition.getMetadata().getName(), definition);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package run.halo.app.plugin.extensionpoint;

import reactor.core.publisher.Mono;

public interface ExtensionPointDefinitionGetter {

/**
* Gets extension point definition by extension point class.
* <p>Retrieve by filedSelector: <code>spec.className</code></p>
*
* @param className extension point class name
*/
Mono<ExtensionPointDefinition> getByClassName(String className);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package run.halo.app.plugin.extensionpoint;

import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
import run.halo.app.extension.ExtensionClient;

@Component
public class ExtensionPointDefinitionGetterImpl
extends AbstractDefinitionGetter<ExtensionPointDefinition>
implements ExtensionPointDefinitionGetter {

public ExtensionPointDefinitionGetterImpl(ExtensionClient client) {
super(client, new ExtensionPointDefinition());
}

@Override
public Mono<ExtensionPointDefinition> getByClassName(String className) {
return Mono.fromSupplier(() -> cache.get(className));
}

@Override
void putCache(ExtensionPointDefinition definition) {
var className = definition.getSpec().getClassName();
cache.put(className, definition);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public RequestInfo newRequestInfo(ServerHttpRequest request) {
RequestInfo requestInfo =
new RequestInfo(false, path.value(), request.getMethod().name().toLowerCase());

String[] currentParts = splitPath(request.getPath().value());
String[] currentParts = splitPath(path.value());

if (currentParts.length < 3) {
// return a non-resource request
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ private Mono<Void> doInitialization(SetupRequest body) {
)
.subscribeOn(Schedulers.boundedElastic());

var basicConfigMono = Mono.defer(() -> systemConfigFetcher.getConfigMap()
var basicConfigMono = Mono.defer(() -> systemConfigFetcher.loadConfigMap()
.flatMap(configMap -> {
mergeToBasicConfig(body, configMap);
return client.update(configMap);
Expand Down
Loading

0 comments on commit 2a1ffc5

Please sign in to comment.