diff --git a/src/main/java/run/halo/app/config/ExtensionConfiguration.java b/src/main/java/run/halo/app/config/ExtensionConfiguration.java index 52c608608..31c75de5b 100644 --- a/src/main/java/run/halo/app/config/ExtensionConfiguration.java +++ b/src/main/java/run/halo/app/config/ExtensionConfiguration.java @@ -17,6 +17,7 @@ import run.halo.app.core.extension.Menu; import run.halo.app.core.extension.MenuItem; import run.halo.app.core.extension.Plugin; import run.halo.app.core.extension.Post; +import run.halo.app.core.extension.ReverseProxy; import run.halo.app.core.extension.Role; import run.halo.app.core.extension.RoleBinding; import run.halo.app.core.extension.SinglePage; @@ -30,6 +31,7 @@ import run.halo.app.core.extension.reconciler.MenuItemReconciler; import run.halo.app.core.extension.reconciler.MenuReconciler; import run.halo.app.core.extension.reconciler.PluginReconciler; import run.halo.app.core.extension.reconciler.PostReconciler; +import run.halo.app.core.extension.reconciler.ReverseProxyReconciler; import run.halo.app.core.extension.reconciler.RoleBindingReconciler; import run.halo.app.core.extension.reconciler.RoleReconciler; import run.halo.app.core.extension.reconciler.SinglePageReconciler; @@ -56,6 +58,7 @@ import run.halo.app.infra.properties.HaloProperties; import run.halo.app.plugin.ExtensionComponentsFinder; import run.halo.app.plugin.HaloPluginManager; import run.halo.app.plugin.resources.JsBundleRuleProvider; +import run.halo.app.plugin.resources.ReverseProxyRouterFunctionRegistry; import run.halo.app.theme.router.TemplateRouteManager; @Configuration(proxyBeanMethods = false) @@ -212,6 +215,15 @@ public class ExtensionConfiguration { .extension(new Comment()) .build(); } + + @Bean + Controller reverseProxyController(ExtensionClient client, + ReverseProxyRouterFunctionRegistry reverseProxyRouterFunctionRegistry) { + return new ControllerBuilder("reverse-proxy-controller", client) + .reconciler(new ReverseProxyReconciler(client, reverseProxyRouterFunctionRegistry)) + .extension(new ReverseProxy()) + .build(); + } } } diff --git a/src/main/java/run/halo/app/core/extension/reconciler/ReverseProxyReconciler.java b/src/main/java/run/halo/app/core/extension/reconciler/ReverseProxyReconciler.java new file mode 100644 index 000000000..ef397a559 --- /dev/null +++ b/src/main/java/run/halo/app/core/extension/reconciler/ReverseProxyReconciler.java @@ -0,0 +1,94 @@ +package run.halo.app.core.extension.reconciler; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import org.apache.commons.lang3.StringUtils; +import run.halo.app.core.extension.ReverseProxy; +import run.halo.app.extension.ExtensionClient; +import run.halo.app.extension.controller.Reconciler; +import run.halo.app.plugin.PluginConst; +import run.halo.app.plugin.resources.ReverseProxyRouterFunctionRegistry; + +/** + * Reconciler for {@link ReverseProxy}. + * + * @author guqing + * @since 2.0.0 + */ +public class ReverseProxyReconciler implements Reconciler { + private static final String FINALIZER_NAME = "reverse-proxy-protection"; + private final ExtensionClient client; + private final ReverseProxyRouterFunctionRegistry routerFunctionRegistry; + + public ReverseProxyReconciler(ExtensionClient client, + ReverseProxyRouterFunctionRegistry routerFunctionRegistry) { + this.client = client; + this.routerFunctionRegistry = routerFunctionRegistry; + } + + @Override + public Result reconcile(Request request) { + return client.fetch(ReverseProxy.class, request.name()) + .map(reverseProxy -> { + if (isDeleted(reverseProxy)) { + cleanUpResourcesAndRemoveFinalizer(request.name()); + return new Result(false, null); + } + addFinalizerIfNecessary(reverseProxy); + registerReverseProxy(reverseProxy); + return new Result(false, null); + }) + .orElse(new Result(false, null)); + } + + private void registerReverseProxy(ReverseProxy reverseProxy) { + String pluginId = getPluginId(reverseProxy); + routerFunctionRegistry.register(pluginId, reverseProxy).block(); + } + + private void cleanUpResources(ReverseProxy reverseProxy) { + String pluginId = getPluginId(reverseProxy); + routerFunctionRegistry.remove(pluginId).block(); + } + + private void addFinalizerIfNecessary(ReverseProxy oldReverseProxy) { + Set finalizers = oldReverseProxy.getMetadata().getFinalizers(); + if (finalizers != null && finalizers.contains(FINALIZER_NAME)) { + return; + } + client.fetch(ReverseProxy.class, oldReverseProxy.getMetadata().getName()) + .ifPresent(reverseProxy -> { + Set newFinalizers = reverseProxy.getMetadata().getFinalizers(); + if (newFinalizers == null) { + newFinalizers = new HashSet<>(); + reverseProxy.getMetadata().setFinalizers(newFinalizers); + } + newFinalizers.add(FINALIZER_NAME); + client.update(reverseProxy); + }); + } + + private void cleanUpResourcesAndRemoveFinalizer(String name) { + client.fetch(ReverseProxy.class, name).ifPresent(reverseProxy -> { + cleanUpResources(reverseProxy); + if (reverseProxy.getMetadata().getFinalizers() != null) { + reverseProxy.getMetadata().getFinalizers().remove(FINALIZER_NAME); + } + client.update(reverseProxy); + }); + } + + private boolean isDeleted(ReverseProxy reverseProxy) { + return reverseProxy.getMetadata().getDeletionTimestamp() != null; + } + + private String getPluginId(ReverseProxy reverseProxy) { + Map labels = reverseProxy.getMetadata().getLabels(); + if (labels == null) { + return PluginConst.SYSTEM_PLUGIN_NAME; + } + return StringUtils.defaultString(labels.get(PluginConst.PLUGIN_NAME_LABEL_NAME), + PluginConst.SYSTEM_PLUGIN_NAME); + } +} diff --git a/src/main/java/run/halo/app/plugin/PluginCompositeRouterFunction.java b/src/main/java/run/halo/app/plugin/PluginCompositeRouterFunction.java index 608f397b9..02563370c 100644 --- a/src/main/java/run/halo/app/plugin/PluginCompositeRouterFunction.java +++ b/src/main/java/run/halo/app/plugin/PluginCompositeRouterFunction.java @@ -1,15 +1,9 @@ package run.halo.app.plugin; -import java.util.Collections; import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; -import org.pf4j.PluginWrapper; -import org.springframework.context.event.EventListener; +import java.util.stream.Stream; import org.springframework.lang.NonNull; import org.springframework.stereotype.Component; -import org.springframework.util.CollectionUtils; import org.springframework.web.reactive.function.server.HandlerFunction; import org.springframework.web.reactive.function.server.RouterFunction; import org.springframework.web.reactive.function.server.RouterFunctions; @@ -17,9 +11,7 @@ import org.springframework.web.reactive.function.server.ServerRequest; import org.springframework.web.reactive.function.server.ServerResponse; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import run.halo.app.plugin.event.HaloPluginStartedEvent; -import run.halo.app.plugin.event.HaloPluginStoppedEvent; -import run.halo.app.plugin.resources.ReverseProxyRouterFunctionFactory; +import run.halo.app.plugin.resources.ReverseProxyRouterFunctionRegistry; /** * A composite {@link RouterFunction} implementation for plugin. @@ -30,69 +22,37 @@ import run.halo.app.plugin.resources.ReverseProxyRouterFunctionFactory; @Component public class PluginCompositeRouterFunction implements RouterFunction { - private final Map> routerFunctionRegistry = - new ConcurrentHashMap<>(); - - private final ReverseProxyRouterFunctionFactory reverseProxyRouterFunctionFactory; + private final ReverseProxyRouterFunctionRegistry reverseProxyRouterFunctionFactory; public PluginCompositeRouterFunction( - ReverseProxyRouterFunctionFactory reverseProxyRouterFunctionFactory) { + ReverseProxyRouterFunctionRegistry reverseProxyRouterFunctionFactory) { this.reverseProxyRouterFunctionFactory = reverseProxyRouterFunctionFactory; } - public RouterFunction getRouterFunction(String pluginId) { - return routerFunctionRegistry.get(pluginId); - } - @Override @NonNull public Mono> route(@NonNull ServerRequest request) { - return Flux.fromIterable(routerFunctionRegistry.values()) + return Flux.fromIterable(routerFunctions()) .concatMap(routerFunction -> routerFunction.route(request)) .next(); } @Override public void accept(@NonNull RouterFunctions.Visitor visitor) { - routerFunctionRegistry.values().forEach(routerFunction -> routerFunction.accept(visitor)); - } - - /** - * Obtains the user-defined {@link RouterFunction} from the plugin - * {@link PluginApplicationContext} and create {@link RouterFunction} according to the - * reverse proxy configuration file then register them to {@link #routerFunctionRegistry}. - * - * @param haloPluginStartedEvent event for plugin started - */ - @EventListener(HaloPluginStartedEvent.class) - public Mono onPluginStarted(HaloPluginStartedEvent haloPluginStartedEvent) { - PluginWrapper plugin = haloPluginStartedEvent.getPlugin(); - // Obtain plugin application context - PluginApplicationContext pluginApplicationContext = - ExtensionContextRegistry.getInstance().getByPluginId(plugin.getPluginId()); - - return Flux.fromIterable(routerFunctions(pluginApplicationContext)) - .concatWith(reverseProxyRouterFunctionFactory.create(pluginApplicationContext)) - .reduce(RouterFunction::and) - .doOnNext(routerFunction -> - routerFunctionRegistry.put(plugin.getPluginId(), routerFunction)) - .then(); - } - - @EventListener(HaloPluginStoppedEvent.class) - public void onPluginStopped(HaloPluginStoppedEvent haloPluginStoppedEvent) { - PluginWrapper plugin = haloPluginStoppedEvent.getPlugin(); - routerFunctionRegistry.remove(plugin.getPluginId()); + routerFunctions().forEach(routerFunction -> routerFunction.accept(visitor)); } @SuppressWarnings("unchecked") - private List> routerFunctions( - PluginApplicationContext applicationContext) { - List> functions = - applicationContext.getBeanProvider(RouterFunction.class) - .orderedStream() - .map(router -> (RouterFunction) router) - .collect(Collectors.toList()); - return (!CollectionUtils.isEmpty(functions) ? functions : Collections.emptyList()); + private List> routerFunctions() { + Stream> routerFunctionStream = + ExtensionContextRegistry.getInstance().getPluginApplicationContexts() + .stream() + .flatMap(applicationContext -> applicationContext + .getBeanProvider(RouterFunction.class) + .orderedStream()) + .map(router -> (RouterFunction) router); + return Stream.concat(routerFunctionStream, + reverseProxyRouterFunctionFactory.getRouterFunctions().stream()) + .toList(); } } diff --git a/src/main/java/run/halo/app/plugin/PluginConst.java b/src/main/java/run/halo/app/plugin/PluginConst.java index 1e0964e5a..a8bb8cf6e 100644 --- a/src/main/java/run/halo/app/plugin/PluginConst.java +++ b/src/main/java/run/halo/app/plugin/PluginConst.java @@ -11,4 +11,6 @@ public interface PluginConst { * Plugin metadata labels key. */ String PLUGIN_NAME_LABEL_NAME = "plugin.halo.run/plugin-name"; + + String SYSTEM_PLUGIN_NAME = "system"; } diff --git a/src/main/java/run/halo/app/plugin/resources/ReverseProxyRouterFunctionFactory.java b/src/main/java/run/halo/app/plugin/resources/ReverseProxyRouterFunctionFactory.java index 4ea9b799d..85c77e36d 100644 --- a/src/main/java/run/halo/app/plugin/resources/ReverseProxyRouterFunctionFactory.java +++ b/src/main/java/run/halo/app/plugin/resources/ReverseProxyRouterFunctionFactory.java @@ -6,9 +6,9 @@ import static org.springframework.web.reactive.function.server.RequestPredicates import java.util.ArrayList; import java.util.List; -import java.util.function.Predicate; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.springframework.context.ApplicationContext; import org.springframework.core.io.Resource; import org.springframework.lang.NonNull; import org.springframework.stereotype.Component; @@ -23,7 +23,6 @@ import reactor.core.publisher.Mono; import run.halo.app.core.extension.ReverseProxy; import run.halo.app.core.extension.ReverseProxy.FileReverseProxyProvider; import run.halo.app.core.extension.ReverseProxy.ReverseProxyRule; -import run.halo.app.extension.ReactiveExtensionClient; import run.halo.app.infra.utils.PathUtils; import run.halo.app.plugin.PluginApplicationContext; import run.halo.app.plugin.PluginConst; @@ -41,13 +40,9 @@ import run.halo.app.plugin.PluginConst; public class ReverseProxyRouterFunctionFactory { private static final String REVERSE_PROXY_API_PREFIX = "/assets"; - private final ReactiveExtensionClient extensionClient; - private final JsBundleRuleProvider jsBundleRuleProvider; - public ReverseProxyRouterFunctionFactory(ReactiveExtensionClient extensionClient, - JsBundleRuleProvider jsBundleRuleProvider) { - this.extensionClient = extensionClient; + public ReverseProxyRouterFunctionFactory(JsBundleRuleProvider jsBundleRuleProvider) { this.jsBundleRuleProvider = jsBundleRuleProvider; } @@ -57,21 +52,22 @@ public class ReverseProxyRouterFunctionFactory { *

Note that: returns {@code Null} if the plugin does not have a {@link ReverseProxy} custom * resource.

* - * @param pluginApplicationContext plugin application context + * @param applicationContext plugin application context or system application context * @return A reverse proxy RouterFunction handle(nullable) */ @NonNull - public Mono> create( - PluginApplicationContext pluginApplicationContext) { - return createReverseProxyRouterFunction(pluginApplicationContext); + public Mono> create(ReverseProxy reverseProxy, + ApplicationContext applicationContext) { + return createReverseProxyRouterFunction(reverseProxy, applicationContext); } private Mono> createReverseProxyRouterFunction( - PluginApplicationContext pluginApplicationContext) { - Assert.notNull(pluginApplicationContext, "The pluginApplicationContext must not be null."); - - var pluginId = pluginApplicationContext.getPluginId(); - var rules = getReverseProxyRules(pluginId); + ReverseProxy reverseProxy, + ApplicationContext applicationContext) { + Assert.notNull(reverseProxy, "The reverseProxy must not be null."); + Assert.notNull(applicationContext, "The applicationContext must not be null."); + final var pluginId = getPluginId(applicationContext); + var rules = getReverseProxyRules(pluginId, reverseProxy); return rules.map(rule -> { String routePath = buildRoutePath(pluginId, rule); @@ -80,7 +76,7 @@ public class ReverseProxyRouterFunctionFactory { return RouterFunctions.route(GET(routePath).and(accept(ALL)), request -> { Resource resource = - loadResourceByFileRule(pluginApplicationContext, rule, request); + loadResourceByFileRule(pluginId, applicationContext, rule, request); if (!resource.exists()) { return ServerResponse.notFound().build(); } @@ -90,22 +86,17 @@ public class ReverseProxyRouterFunctionFactory { }).reduce(RouterFunction::and); } - private Flux getReverseProxyRules(String pluginId) { - return extensionClient.list(ReverseProxy.class, hasPluginId(pluginId), null) - .map(ReverseProxy::getRules) - .flatMapIterable(rules -> rules) - .concatWith(Flux.fromIterable(getJsBundleRules(pluginId))); + private String getPluginId(ApplicationContext applicationContext) { + if (applicationContext instanceof PluginApplicationContext pluginApplicationContext) { + return pluginApplicationContext.getPluginId(); + } + return PluginConst.SYSTEM_PLUGIN_NAME; } - private Predicate hasPluginId(String pluginId) { - return proxy -> { - var labels = proxy.getMetadata().getLabels(); - if (labels == null) { - return false; - } - var pluginName = labels.get(PluginConst.PLUGIN_NAME_LABEL_NAME); - return pluginId.equals(pluginName); - }; + private Flux getReverseProxyRules(String pluginId, + ReverseProxy reverseProxy) { + return Flux.fromIterable(reverseProxy.getRules()) + .concatWith(Flux.fromIterable(getJsBundleRules(pluginId))); } private List getJsBundleRules(String pluginId) { @@ -136,7 +127,8 @@ public class ReverseProxyRouterFunctionFactory { * @return a Resource handle for the specified resource location by the plugin(never null); */ @NonNull - private Resource loadResourceByFileRule(PluginApplicationContext pluginApplicationContext, + private Resource loadResourceByFileRule(String pluginId, + ApplicationContext pluginApplicationContext, ReverseProxyRule rule, ServerRequest request) { Assert.notNull(rule.file(), "File rule must not be null."); FileReverseProxyProvider file = rule.file(); @@ -149,7 +141,7 @@ public class ReverseProxyRouterFunctionFactory { filename = configuredFilename; } else { AntPathMatcher antPathMatcher = new AntPathMatcher(); - String routePath = buildRoutePath(pluginApplicationContext.getPluginId(), rule); + String routePath = buildRoutePath(pluginId, rule); filename = antPathMatcher.extractPathWithinPattern(routePath, request.path()); } diff --git a/src/main/java/run/halo/app/plugin/resources/ReverseProxyRouterFunctionRegistry.java b/src/main/java/run/halo/app/plugin/resources/ReverseProxyRouterFunctionRegistry.java new file mode 100644 index 000000000..113448044 --- /dev/null +++ b/src/main/java/run/halo/app/plugin/resources/ReverseProxyRouterFunctionRegistry.java @@ -0,0 +1,128 @@ +package run.halo.app.plugin.resources; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.locks.StampedLock; +import org.springframework.stereotype.Component; +import org.springframework.util.Assert; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.ServerResponse; +import reactor.core.publisher.Mono; +import run.halo.app.core.extension.ReverseProxy; +import run.halo.app.plugin.ExtensionContextRegistry; +import run.halo.app.plugin.PluginApplicationContext; + +/** + * A registry for {@link RouterFunction} of plugin. + * + * @author guqing + * @since 2.0.0 + */ +@Component +public class ReverseProxyRouterFunctionRegistry { + private final ReverseProxyRouterFunctionFactory reverseProxyRouterFunctionFactory; + private final StampedLock lock = new StampedLock(); + private final Map> proxyNameRouterFunctionRegistry = + new HashMap<>(); + private final MultiValueMap pluginIdReverseProxyMap = + new LinkedMultiValueMap<>(); + + public ReverseProxyRouterFunctionRegistry( + ReverseProxyRouterFunctionFactory reverseProxyRouterFunctionFactory) { + this.reverseProxyRouterFunctionFactory = reverseProxyRouterFunctionFactory; + } + + /** + * Register reverse proxy router function. + * + * @param pluginId plugin id + * @param reverseProxy reverse proxy + * @return a mono + */ + public Mono register(String pluginId, ReverseProxy reverseProxy) { + Assert.notNull(pluginId, "The plugin id must not be null."); + final String proxyName = reverseProxy.getMetadata().getName(); + long stamp = lock.writeLock(); + try { + pluginIdReverseProxyMap.add(pluginId, proxyName); + // Obtain plugin application context + PluginApplicationContext pluginApplicationContext = + ExtensionContextRegistry.getInstance().getByPluginId(pluginId); + return reverseProxyRouterFunctionFactory.create(reverseProxy, pluginApplicationContext) + .map(routerFunction -> { + proxyNameRouterFunctionRegistry.put(proxyName, routerFunction); + return routerFunction; + }) + .then(); + } finally { + lock.unlockWrite(stamp); + } + } + + /** + * Remove reverse proxy router function by plugin id. + * + * @param pluginId plugin id + */ + public Mono remove(String pluginId) { + Assert.notNull(pluginId, "The plugin id must not be null."); + long stamp = lock.writeLock(); + try { + List proxyNames = pluginIdReverseProxyMap.remove(pluginId); + if (proxyNames == null) { + return Mono.empty(); + } + for (String proxyName : proxyNames) { + proxyNameRouterFunctionRegistry.remove(proxyName); + } + return Mono.empty(); + } finally { + lock.unlockWrite(stamp); + } + } + + /** + * Remove reverse proxy router function by pluginId and reverse proxy name. + */ + public Mono remove(String pluginId, String reverseProxyName) { + long stamp = lock.writeLock(); + try { + List proxyNames = pluginIdReverseProxyMap.get(pluginId); + if (proxyNames == null) { + return Mono.empty(); + } + proxyNames.remove(reverseProxyName); + proxyNameRouterFunctionRegistry.remove(reverseProxyName); + return Mono.empty(); + } finally { + lock.unlockWrite(stamp); + } + } + + /** + * Gets reverse proxy {@link RouterFunction} by reverse proxy name. + */ + public RouterFunction getRouterFunction(String proxyName) { + long stamp = lock.readLock(); + try { + return proxyNameRouterFunctionRegistry.get(proxyName); + } finally { + lock.unlockRead(stamp); + } + } + + /** + * Gets all reverse proxy {@link RouterFunction}. + */ + public List> getRouterFunctions() { + long stamp = lock.readLock(); + try { + return List.copyOf(proxyNameRouterFunctionRegistry.values()); + } finally { + lock.unlockRead(stamp); + } + } +} diff --git a/src/test/java/run/halo/app/plugin/PluginCompositeRouterFunctionTest.java b/src/test/java/run/halo/app/plugin/PluginCompositeRouterFunctionTest.java index 5e7054d80..7906da3aa 100644 --- a/src/test/java/run/halo/app/plugin/PluginCompositeRouterFunctionTest.java +++ b/src/test/java/run/halo/app/plugin/PluginCompositeRouterFunctionTest.java @@ -1,18 +1,13 @@ package run.halo.app.plugin; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import java.util.stream.Stream; +import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import org.pf4j.PluginWrapper; -import org.springframework.beans.factory.ObjectProvider; import org.springframework.http.codec.ServerCodecConfigurer; import org.springframework.mock.http.server.reactive.MockServerHttpRequest; import org.springframework.mock.web.server.MockServerWebExchange; @@ -23,9 +18,7 @@ import org.springframework.web.reactive.function.server.support.RouterFunctionMa import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; -import run.halo.app.plugin.event.HaloPluginStartedEvent; -import run.halo.app.plugin.event.HaloPluginStoppedEvent; -import run.halo.app.plugin.resources.ReverseProxyRouterFunctionFactory; +import run.halo.app.plugin.resources.ReverseProxyRouterFunctionRegistry; /** * Tests for {@link PluginCompositeRouterFunction}. @@ -38,46 +31,27 @@ class PluginCompositeRouterFunctionTest { private final ServerCodecConfigurer codecConfigurer = ServerCodecConfigurer.create(); @Mock - private ReverseProxyRouterFunctionFactory reverseProxyRouterFunctionFactory; - - @Mock - private PluginApplicationContext pluginApplicationContext; - - @Mock - private PluginWrapper pluginWrapper; + private ReverseProxyRouterFunctionRegistry reverseProxyRouterFunctionRegistry; private PluginCompositeRouterFunction compositeRouterFunction; private HandlerFunction handlerFunction; - private RouterFunction routerFunction; @BeforeEach @SuppressWarnings("unchecked") void setUp() { compositeRouterFunction = - new PluginCompositeRouterFunction(reverseProxyRouterFunctionFactory); - - ExtensionContextRegistry.getInstance().register("fakeA", pluginApplicationContext); - when(pluginWrapper.getPluginId()).thenReturn("fakeA"); + new PluginCompositeRouterFunction(reverseProxyRouterFunctionRegistry); handlerFunction = request -> ServerResponse.ok().build(); - routerFunction = request -> Mono.just(handlerFunction); + RouterFunction routerFunction = request -> Mono.just(handlerFunction); - var objectProvider = mock(ObjectProvider.class); - when(objectProvider.orderedStream()).thenReturn(Stream.of(routerFunction)); - - when(pluginApplicationContext.getBeanProvider(RouterFunction.class)) - .thenReturn(objectProvider); - when(reverseProxyRouterFunctionFactory.create(any())).thenReturn(Mono.empty()); + when(reverseProxyRouterFunctionRegistry.getRouterFunctions()) + .thenReturn(List.of(routerFunction)); } @Test void route() { - // trigger haloPluginStartedEvent - StepVerifier.create(compositeRouterFunction.onPluginStarted( - new HaloPluginStartedEvent(this, pluginWrapper))) - .verifyComplete(); - RouterFunctionMapping mapping = new RouterFunctionMapping(compositeRouterFunction); mapping.setMessageReaders(this.codecConfigurer.getReaders()); @@ -89,30 +63,6 @@ class PluginCompositeRouterFunctionTest { .verify(); } - @Test - void onPluginStarted() { - assertThat(compositeRouterFunction.getRouterFunction("fakeA")).isNull(); - - // trigger haloPluginStartedEvent - StepVerifier.create(compositeRouterFunction.onPluginStarted( - new HaloPluginStartedEvent(this, pluginWrapper))) - .verifyComplete(); - assertThat(compositeRouterFunction.getRouterFunction("fakeA")).isEqualTo(routerFunction); - } - - @Test - void onPluginStopped() { - // trigger haloPluginStartedEvent - StepVerifier.create(compositeRouterFunction.onPluginStarted( - new HaloPluginStartedEvent(this, pluginWrapper))) - .verifyComplete(); - assertThat(compositeRouterFunction.getRouterFunction("fakeA")).isEqualTo(routerFunction); - - // trigger HaloPluginStoppedEvent - compositeRouterFunction.onPluginStopped(new HaloPluginStoppedEvent(this, pluginWrapper)); - assertThat(compositeRouterFunction.getRouterFunction("fakeA")).isNull(); - } - private ServerWebExchange createExchange(String urlTemplate) { return MockServerWebExchange.from(MockServerHttpRequest.get(urlTemplate)); } diff --git a/src/test/java/run/halo/app/plugin/resources/ReverseProxyRouterFunctionFactoryTest.java b/src/test/java/run/halo/app/plugin/resources/ReverseProxyRouterFunctionFactoryTest.java index 8d6680a09..e74a20a29 100644 --- a/src/test/java/run/halo/app/plugin/resources/ReverseProxyRouterFunctionFactoryTest.java +++ b/src/test/java/run/halo/app/plugin/resources/ReverseProxyRouterFunctionFactoryTest.java @@ -1,7 +1,5 @@ package run.halo.app.plugin.resources; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; import java.util.List; @@ -11,11 +9,9 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import reactor.core.publisher.Flux; import reactor.test.StepVerifier; import run.halo.app.core.extension.ReverseProxy; import run.halo.app.extension.Metadata; -import run.halo.app.extension.ReactiveExtensionClient; import run.halo.app.plugin.HaloPluginManager; import run.halo.app.plugin.PluginApplicationContext; import run.halo.app.plugin.PluginConst; @@ -29,9 +25,6 @@ import run.halo.app.plugin.PluginConst; @ExtendWith(MockitoExtension.class) class ReverseProxyRouterFunctionFactoryTest { - @Mock - private ReactiveExtensionClient extensionClient; - @Mock private PluginApplicationContext pluginApplicationContext; @Mock @@ -42,19 +35,16 @@ class ReverseProxyRouterFunctionFactoryTest { @BeforeEach void setUp() { JsBundleRuleProvider jsBundleRuleProvider = new JsBundleRuleProvider(haloPluginManager); - reverseProxyRouterFunctionFactory = new ReverseProxyRouterFunctionFactory(extensionClient, - jsBundleRuleProvider); - - ReverseProxy reverseProxy = mockReverseProxy(); + reverseProxyRouterFunctionFactory = + new ReverseProxyRouterFunctionFactory(jsBundleRuleProvider); when(pluginApplicationContext.getPluginId()).thenReturn("fakeA"); - when(extensionClient.list(eq(ReverseProxy.class), any(), any())).thenReturn( - Flux.just(reverseProxy)); } @Test void create() { - var routerFunction = reverseProxyRouterFunctionFactory.create(pluginApplicationContext); + var routerFunction = + reverseProxyRouterFunctionFactory.create(mockReverseProxy(), pluginApplicationContext); StepVerifier.create(routerFunction) .expectNextCount(1) .verifyComplete();