refactor: the way of router function register for reverse proxy (#2522)

#### What type of PR is this?
/kind improvement
/area core
/milestone 2.0

#### What this PR does / why we need it:
重构插件反向代理注册方式
#### Which issue(s) this PR fixes:

Fixes #2520

#### Special notes for your reviewer:
How to test:
1. clone 项目 https://github.com/halo-sigs/plugin-comment-widget 后 build 一个 jar
2. 将该 jar 作为一个插件安装到系统并启用它
3. 访问插件提供的反向代理资源
    ```
    curl --location --request GET 'http://localhost:8090/assets/PluginCommentWidget/static/comment-widget.iife.js'
    ```
    期望得到结果
4. 停用插件,重复 Step 3,期望得到 404
5. 重复数次 Step 3-4 均与期望相同即可 
#### Does this PR introduce a user-facing change?

```release-note
None
```
pull/2524/head
guqing 2022-10-09 11:42:30 +08:00 committed by GitHub
parent a4609f68d1
commit 04300308fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 289 additions and 161 deletions

View File

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

View File

@ -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<Reconciler.Request> {
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<String> finalizers = oldReverseProxy.getMetadata().getFinalizers();
if (finalizers != null && finalizers.contains(FINALIZER_NAME)) {
return;
}
client.fetch(ReverseProxy.class, oldReverseProxy.getMetadata().getName())
.ifPresent(reverseProxy -> {
Set<String> 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<String, String> 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);
}
}

View File

@ -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<ServerResponse> {
private final Map<String, RouterFunction<ServerResponse>> routerFunctionRegistry =
new ConcurrentHashMap<>();
private final ReverseProxyRouterFunctionFactory reverseProxyRouterFunctionFactory;
private final ReverseProxyRouterFunctionRegistry reverseProxyRouterFunctionFactory;
public PluginCompositeRouterFunction(
ReverseProxyRouterFunctionFactory reverseProxyRouterFunctionFactory) {
ReverseProxyRouterFunctionRegistry reverseProxyRouterFunctionFactory) {
this.reverseProxyRouterFunctionFactory = reverseProxyRouterFunctionFactory;
}
public RouterFunction<ServerResponse> getRouterFunction(String pluginId) {
return routerFunctionRegistry.get(pluginId);
}
@Override
@NonNull
public Mono<HandlerFunction<ServerResponse>> 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<Void> 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<RouterFunction<ServerResponse>> routerFunctions(
PluginApplicationContext applicationContext) {
List<RouterFunction<ServerResponse>> functions =
applicationContext.getBeanProvider(RouterFunction.class)
.orderedStream()
.map(router -> (RouterFunction<ServerResponse>) router)
.collect(Collectors.toList());
return (!CollectionUtils.isEmpty(functions) ? functions : Collections.emptyList());
private List<RouterFunction<ServerResponse>> routerFunctions() {
Stream<RouterFunction<ServerResponse>> routerFunctionStream =
ExtensionContextRegistry.getInstance().getPluginApplicationContexts()
.stream()
.flatMap(applicationContext -> applicationContext
.getBeanProvider(RouterFunction.class)
.orderedStream())
.map(router -> (RouterFunction<ServerResponse>) router);
return Stream.concat(routerFunctionStream,
reverseProxyRouterFunctionFactory.getRouterFunctions().stream())
.toList();
}
}

View File

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

View File

@ -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 {
* <p>Note that: returns {@code Null} if the plugin does not have a {@link ReverseProxy} custom
* resource.</p>
*
* @param pluginApplicationContext plugin application context
* @param applicationContext plugin application context or system application context
* @return A reverse proxy RouterFunction handle(nullable)
*/
@NonNull
public Mono<RouterFunction<ServerResponse>> create(
PluginApplicationContext pluginApplicationContext) {
return createReverseProxyRouterFunction(pluginApplicationContext);
public Mono<RouterFunction<ServerResponse>> create(ReverseProxy reverseProxy,
ApplicationContext applicationContext) {
return createReverseProxyRouterFunction(reverseProxy, applicationContext);
}
private Mono<RouterFunction<ServerResponse>> 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<ReverseProxyRule> 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<ReverseProxy> 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<ReverseProxyRule> getReverseProxyRules(String pluginId,
ReverseProxy reverseProxy) {
return Flux.fromIterable(reverseProxy.getRules())
.concatWith(Flux.fromIterable(getJsBundleRules(pluginId)));
}
private List<ReverseProxyRule> 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());
}

View File

@ -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<String, RouterFunction<ServerResponse>> proxyNameRouterFunctionRegistry =
new HashMap<>();
private final MultiValueMap<String, String> 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<Void> 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<Void> remove(String pluginId) {
Assert.notNull(pluginId, "The plugin id must not be null.");
long stamp = lock.writeLock();
try {
List<String> 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<Void> remove(String pluginId, String reverseProxyName) {
long stamp = lock.writeLock();
try {
List<String> 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<ServerResponse> getRouterFunction(String proxyName) {
long stamp = lock.readLock();
try {
return proxyNameRouterFunctionRegistry.get(proxyName);
} finally {
lock.unlockRead(stamp);
}
}
/**
* Gets all reverse proxy {@link RouterFunction}.
*/
public List<RouterFunction<ServerResponse>> getRouterFunctions() {
long stamp = lock.readLock();
try {
return List.copyOf(proxyNameRouterFunctionRegistry.values());
} finally {
lock.unlockRead(stamp);
}
}
}

View File

@ -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<ServerResponse> handlerFunction;
private RouterFunction<ServerResponse> 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<ServerResponse> 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));
}

View File

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