mirror of https://github.com/halo-dev/halo
refactor: plugin js bundle file loading and routing rules (#2556)
#### What type of PR is this? /kind improvement /area core /milestone 2.0 /kind api-change #### What this PR does / why we need it: 重构插件 JsBundle 文件加载方式及路由规则 - 将插件静态资源的访问路由规则从 /assets/{plugin-name}/** 改为 /plugins/{plugin-name}/assets/** 与主题静态资源规则结构一致 - 默认在 Halo 中提供 /plugins/{plugin-name}/assets/console/** 路由以确保插件都能加载到最基础的 JsBundle 文件 #### Which issue(s) this PR fixes: Fixes #2555 #### Special notes for your reviewer: how to test it? 1. 安装并启用一个插件能访问到 `/plugins/{plugin-name}/assets/console/main.js` 和 `/plugins/{plugin-name}/assets/console/style.css` 即为功能正确 2. 在插件的 extensions 目录创建一个 reverse proxy 的自定义模型 yaml 资源,并使用此插件,插件反向代理规则能正确访问到文件即为功能正确 /cc @halo-dev/sig-halo #### Does this PR introduce a user-facing change? ```release-note 重构插件 JsBundle 文件加载方式及路由规则 ```pull/2582/head
parent
08fe1858cf
commit
6cce2202a1
|
@ -57,7 +57,6 @@ import run.halo.app.infra.SystemConfigurableEnvironmentFetcher;
|
||||||
import run.halo.app.infra.properties.HaloProperties;
|
import run.halo.app.infra.properties.HaloProperties;
|
||||||
import run.halo.app.plugin.ExtensionComponentsFinder;
|
import run.halo.app.plugin.ExtensionComponentsFinder;
|
||||||
import run.halo.app.plugin.HaloPluginManager;
|
import run.halo.app.plugin.HaloPluginManager;
|
||||||
import run.halo.app.plugin.resources.JsBundleRuleProvider;
|
|
||||||
import run.halo.app.plugin.resources.ReverseProxyRouterFunctionRegistry;
|
import run.halo.app.plugin.resources.ReverseProxyRouterFunctionRegistry;
|
||||||
import run.halo.app.theme.router.TemplateRouteManager;
|
import run.halo.app.theme.router.TemplateRouteManager;
|
||||||
|
|
||||||
|
@ -116,10 +115,9 @@ public class ExtensionConfiguration {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
Controller pluginController(ExtensionClient client, HaloPluginManager haloPluginManager,
|
Controller pluginController(ExtensionClient client, HaloPluginManager haloPluginManager) {
|
||||||
JsBundleRuleProvider jsBundleRule) {
|
|
||||||
return new ControllerBuilder("plugin-controller", client)
|
return new ControllerBuilder("plugin-controller", client)
|
||||||
.reconciler(new PluginReconciler(client, haloPluginManager, jsBundleRule))
|
.reconciler(new PluginReconciler(client, haloPluginManager))
|
||||||
.extension(new Plugin())
|
.extension(new Plugin())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,7 @@ import run.halo.app.extension.controller.Reconciler.Request;
|
||||||
import run.halo.app.infra.utils.JsonUtils;
|
import run.halo.app.infra.utils.JsonUtils;
|
||||||
import run.halo.app.plugin.HaloPluginManager;
|
import run.halo.app.plugin.HaloPluginManager;
|
||||||
import run.halo.app.plugin.PluginStartingError;
|
import run.halo.app.plugin.PluginStartingError;
|
||||||
import run.halo.app.plugin.resources.JsBundleRuleProvider;
|
import run.halo.app.plugin.resources.BundleResourceUtils;
|
||||||
import run.halo.app.plugin.resources.ReverseProxyRouterFunctionFactory;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plugin reconciler.
|
* Plugin reconciler.
|
||||||
|
@ -32,14 +31,10 @@ public class PluginReconciler implements Reconciler<Request> {
|
||||||
private final ExtensionClient client;
|
private final ExtensionClient client;
|
||||||
private final HaloPluginManager haloPluginManager;
|
private final HaloPluginManager haloPluginManager;
|
||||||
|
|
||||||
private final JsBundleRuleProvider jsBundleRule;
|
|
||||||
|
|
||||||
public PluginReconciler(ExtensionClient client,
|
public PluginReconciler(ExtensionClient client,
|
||||||
HaloPluginManager haloPluginManager,
|
HaloPluginManager haloPluginManager) {
|
||||||
JsBundleRuleProvider jsBundleRule) {
|
|
||||||
this.client = client;
|
this.client = client;
|
||||||
this.haloPluginManager = haloPluginManager;
|
this.haloPluginManager = haloPluginManager;
|
||||||
this.jsBundleRule = jsBundleRule;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -129,15 +124,12 @@ public class PluginReconciler implements Reconciler<Request> {
|
||||||
PluginState currentState = haloPluginManager.startPlugin(pluginName);
|
PluginState currentState = haloPluginManager.startPlugin(pluginName);
|
||||||
handleStatus(plugin, currentState, PluginState.STARTED);
|
handleStatus(plugin, currentState, PluginState.STARTED);
|
||||||
Plugin.PluginStatus status = plugin.statusNonNull();
|
Plugin.PluginStatus status = plugin.statusNonNull();
|
||||||
// TODO Check whether the JS bundle rule exists. If it does not exist, do not populate
|
|
||||||
// populate stylesheet path
|
|
||||||
jsBundleRule.jsRule(pluginName)
|
|
||||||
.map(jsRule -> ReverseProxyRouterFunctionFactory.buildRoutePath(pluginName, jsRule))
|
|
||||||
.ifPresent(status::setEntry);
|
|
||||||
|
|
||||||
jsBundleRule.cssRule(pluginName)
|
String jsBundlePath = BundleResourceUtils.getJsBundlePath(haloPluginManager, pluginName);
|
||||||
.map(cssRule -> ReverseProxyRouterFunctionFactory.buildRoutePath(pluginName, cssRule))
|
status.setEntry(jsBundlePath);
|
||||||
.ifPresent(status::setStylesheet);
|
|
||||||
|
String cssBundlePath = BundleResourceUtils.getCssBundlePath(haloPluginManager, pluginName);
|
||||||
|
status.setStylesheet(cssBundlePath);
|
||||||
|
|
||||||
status.setLastStartTime(Instant.now());
|
status.setLastStartTime(Instant.now());
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,13 @@ import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.web.reactive.accept.RequestedContentTypeResolver;
|
import org.springframework.web.reactive.accept.RequestedContentTypeResolver;
|
||||||
|
import org.springframework.web.reactive.function.server.RouterFunction;
|
||||||
|
import org.springframework.web.reactive.function.server.RouterFunctions;
|
||||||
|
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||||
|
import run.halo.app.plugin.resources.BundleResourceUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plugin autoconfiguration for Spring Boot.
|
* Plugin autoconfiguration for Spring Boot.
|
||||||
|
@ -150,4 +155,22 @@ public class PluginAutoConfiguration {
|
||||||
pluginManager.setSystemVersion(pluginProperties.getSystemVersion());
|
pluginManager.setSystemVersion(pluginProperties.getSystemVersion());
|
||||||
return pluginManager;
|
return pluginManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public RouterFunction<ServerResponse> pluginJsBundleRoute(HaloPluginManager haloPluginManager) {
|
||||||
|
return RouterFunctions.route()
|
||||||
|
.GET("/plugins/{name}/assets/console/{*resource}", request -> {
|
||||||
|
String pluginName = request.pathVariable("name");
|
||||||
|
String fileName = request.pathVariable("resource");
|
||||||
|
|
||||||
|
Resource jsBundleResource =
|
||||||
|
BundleResourceUtils.getJsBundleResource(haloPluginManager, pluginName,
|
||||||
|
fileName);
|
||||||
|
if (jsBundleResource == null) {
|
||||||
|
return ServerResponse.notFound().build();
|
||||||
|
}
|
||||||
|
return ServerResponse.ok().bodyValue(jsBundleResource);
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,4 +13,8 @@ public interface PluginConst {
|
||||||
String PLUGIN_NAME_LABEL_NAME = "plugin.halo.run/plugin-name";
|
String PLUGIN_NAME_LABEL_NAME = "plugin.halo.run/plugin-name";
|
||||||
|
|
||||||
String SYSTEM_PLUGIN_NAME = "system";
|
String SYSTEM_PLUGIN_NAME = "system";
|
||||||
|
|
||||||
|
static String assertsRoutePrefix(String pluginName) {
|
||||||
|
return "/plugins/" + pluginName + "/assets/";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
package run.halo.app.plugin.resources;
|
||||||
|
|
||||||
|
import org.pf4j.PluginWrapper;
|
||||||
|
import org.springframework.core.io.DefaultResourceLoader;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import run.halo.app.infra.utils.PathUtils;
|
||||||
|
import run.halo.app.plugin.HaloPluginManager;
|
||||||
|
import run.halo.app.plugin.PluginConst;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugin bundle resources utils.
|
||||||
|
*
|
||||||
|
* @author guqing
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
public abstract class BundleResourceUtils {
|
||||||
|
private static final String CONSOLE_BUNDLE_LOCATION = "console";
|
||||||
|
private static final String JS_BUNDLE = "main.js";
|
||||||
|
private static final String CSS_BUNDLE = "style.css";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets plugin css bundle resource path relative to the plugin classpath if exists.
|
||||||
|
*
|
||||||
|
* @return css bundle resource path if exists, otherwise return null.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static String getCssBundlePath(HaloPluginManager haloPluginManager,
|
||||||
|
String pluginName) {
|
||||||
|
Resource jsBundleResource = getJsBundleResource(haloPluginManager, pluginName, CSS_BUNDLE);
|
||||||
|
if (jsBundleResource != null) {
|
||||||
|
return consoleResourcePath(pluginName, CSS_BUNDLE);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String consoleResourcePath(String pluginName, String name) {
|
||||||
|
return PathUtils.combinePath(PluginConst.assertsRoutePrefix(pluginName),
|
||||||
|
CONSOLE_BUNDLE_LOCATION, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets plugin js bundle resource path relative to the plugin classpath if exists.
|
||||||
|
*
|
||||||
|
* @return js bundle resource path if exists, otherwise return null.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static String getJsBundlePath(HaloPluginManager haloPluginManager,
|
||||||
|
String pluginName) {
|
||||||
|
Resource jsBundleResource = getJsBundleResource(haloPluginManager, pluginName, JS_BUNDLE);
|
||||||
|
if (jsBundleResource != null) {
|
||||||
|
return consoleResourcePath(pluginName, JS_BUNDLE);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets js bundle resource by plugin name in console location.
|
||||||
|
*
|
||||||
|
* @return js bundle resource if exists, otherwise null
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static Resource getJsBundleResource(HaloPluginManager pluginManager, String pluginName,
|
||||||
|
String bundleName) {
|
||||||
|
Assert.hasText(pluginName, "The pluginName must not be blank");
|
||||||
|
Assert.hasText(bundleName, "Bundle name must not be blank");
|
||||||
|
|
||||||
|
DefaultResourceLoader resourceLoader = getResourceLoader(pluginManager, pluginName);
|
||||||
|
if (resourceLoader == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String path = PathUtils.combinePath(CONSOLE_BUNDLE_LOCATION, bundleName);
|
||||||
|
Resource resource = resourceLoader.getResource(path);
|
||||||
|
return resource.exists() ? resource : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static DefaultResourceLoader getResourceLoader(HaloPluginManager pluginManager,
|
||||||
|
String pluginName) {
|
||||||
|
Assert.notNull(pluginManager, "Plugin manager must not be null");
|
||||||
|
PluginWrapper plugin = pluginManager.getPlugin(pluginName);
|
||||||
|
if (plugin == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new DefaultResourceLoader(plugin.getPluginClassLoader());
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,71 +0,0 @@
|
||||||
package run.halo.app.plugin.resources;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
import org.pf4j.PluginWrapper;
|
|
||||||
import org.springframework.core.io.DefaultResourceLoader;
|
|
||||||
import org.springframework.lang.NonNull;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import run.halo.app.core.extension.ReverseProxy.FileReverseProxyProvider;
|
|
||||||
import run.halo.app.core.extension.ReverseProxy.ReverseProxyRule;
|
|
||||||
import run.halo.app.plugin.HaloPluginManager;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO Optimize code to support user customize js bundle rules.
|
|
||||||
*
|
|
||||||
* @author guqing
|
|
||||||
* @since 2.0.0
|
|
||||||
*/
|
|
||||||
@Component
|
|
||||||
public class JsBundleRuleProvider {
|
|
||||||
private static final String JS_LOCATION = "/console/main.js";
|
|
||||||
private static final String CSS_LOCATION = "/console/style.css";
|
|
||||||
|
|
||||||
private static final FileReverseProxyProvider JS_FILE_PROXY =
|
|
||||||
new FileReverseProxyProvider("console", "main.js");
|
|
||||||
|
|
||||||
private static final FileReverseProxyProvider CSS_FILE_PROXY =
|
|
||||||
new FileReverseProxyProvider("console", "style.css");
|
|
||||||
|
|
||||||
private final HaloPluginManager haloPluginManager;
|
|
||||||
|
|
||||||
public JsBundleRuleProvider(HaloPluginManager haloPluginManager) {
|
|
||||||
this.haloPluginManager = haloPluginManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets plugin js bundle rule.
|
|
||||||
*
|
|
||||||
* @param pluginName plugin name
|
|
||||||
* @return a js bundle rule
|
|
||||||
*/
|
|
||||||
public Optional<ReverseProxyRule> jsRule(String pluginName) {
|
|
||||||
return Optional.of(JS_LOCATION)
|
|
||||||
.filter(path -> createResourceLoader(pluginName)
|
|
||||||
.getResource(path).exists())
|
|
||||||
.map(path -> new ReverseProxyRule(path, JS_FILE_PROXY));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets plugin stylesheet rule.
|
|
||||||
*
|
|
||||||
* @param pluginName plugin name
|
|
||||||
* @return a stylesheet bundle rule
|
|
||||||
*/
|
|
||||||
public Optional<ReverseProxyRule> cssRule(String pluginName) {
|
|
||||||
return Optional.of(CSS_LOCATION)
|
|
||||||
.filter(path -> createResourceLoader(pluginName)
|
|
||||||
.getResource(path)
|
|
||||||
.exists())
|
|
||||||
.map(path -> new ReverseProxyRule(path, CSS_FILE_PROXY));
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private DefaultResourceLoader createResourceLoader(String pluginName) {
|
|
||||||
PluginWrapper plugin = haloPluginManager.getPlugin(pluginName);
|
|
||||||
if (plugin == null) {
|
|
||||||
return new DefaultResourceLoader();
|
|
||||||
}
|
|
||||||
return new DefaultResourceLoader(plugin.getPluginClassLoader());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -4,20 +4,19 @@ import static org.springframework.http.MediaType.ALL;
|
||||||
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
|
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
|
||||||
import static org.springframework.web.reactive.function.server.RequestPredicates.accept;
|
import static org.springframework.web.reactive.function.server.RequestPredicates.accept;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.http.server.PathContainer;
|
||||||
import org.springframework.lang.NonNull;
|
import org.springframework.lang.NonNull;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.util.AntPathMatcher;
|
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.web.reactive.function.server.RouterFunction;
|
import org.springframework.web.reactive.function.server.RouterFunction;
|
||||||
import org.springframework.web.reactive.function.server.RouterFunctions;
|
import org.springframework.web.reactive.function.server.RouterFunctions;
|
||||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||||
|
import org.springframework.web.util.pattern.PathPatternParser;
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
import run.halo.app.core.extension.ReverseProxy;
|
import run.halo.app.core.extension.ReverseProxy;
|
||||||
|
@ -38,13 +37,6 @@ import run.halo.app.plugin.PluginConst;
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
public class ReverseProxyRouterFunctionFactory {
|
public class ReverseProxyRouterFunctionFactory {
|
||||||
private static final String REVERSE_PROXY_API_PREFIX = "/assets";
|
|
||||||
|
|
||||||
private final JsBundleRuleProvider jsBundleRuleProvider;
|
|
||||||
|
|
||||||
public ReverseProxyRouterFunctionFactory(JsBundleRuleProvider jsBundleRuleProvider) {
|
|
||||||
this.jsBundleRuleProvider = jsBundleRuleProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Create {@link RouterFunction} according to the {@link ReverseProxy} custom resource
|
* <p>Create {@link RouterFunction} according to the {@link ReverseProxy} custom resource
|
||||||
|
@ -67,7 +59,7 @@ public class ReverseProxyRouterFunctionFactory {
|
||||||
Assert.notNull(reverseProxy, "The reverseProxy must not be null.");
|
Assert.notNull(reverseProxy, "The reverseProxy must not be null.");
|
||||||
Assert.notNull(applicationContext, "The applicationContext must not be null.");
|
Assert.notNull(applicationContext, "The applicationContext must not be null.");
|
||||||
final var pluginId = getPluginId(applicationContext);
|
final var pluginId = getPluginId(applicationContext);
|
||||||
var rules = getReverseProxyRules(pluginId, reverseProxy);
|
var rules = getReverseProxyRules(reverseProxy);
|
||||||
|
|
||||||
return rules.map(rule -> {
|
return rules.map(rule -> {
|
||||||
String routePath = buildRoutePath(pluginId, rule);
|
String routePath = buildRoutePath(pluginId, rule);
|
||||||
|
@ -93,21 +85,13 @@ public class ReverseProxyRouterFunctionFactory {
|
||||||
return PluginConst.SYSTEM_PLUGIN_NAME;
|
return PluginConst.SYSTEM_PLUGIN_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Flux<ReverseProxyRule> getReverseProxyRules(String pluginId,
|
private Flux<ReverseProxyRule> getReverseProxyRules(ReverseProxy reverseProxy) {
|
||||||
ReverseProxy reverseProxy) {
|
return Flux.fromIterable(reverseProxy.getRules());
|
||||||
return Flux.fromIterable(reverseProxy.getRules())
|
|
||||||
.concatWith(Flux.fromIterable(getJsBundleRules(pluginId)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<ReverseProxyRule> getJsBundleRules(String pluginId) {
|
|
||||||
List<ReverseProxyRule> rules = new ArrayList<>(2);
|
|
||||||
jsBundleRuleProvider.jsRule(pluginId).ifPresent(rules::add);
|
|
||||||
jsBundleRuleProvider.cssRule(pluginId).ifPresent(rules::add);
|
|
||||||
return rules;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String buildRoutePath(String pluginId, ReverseProxyRule reverseProxyRule) {
|
public static String buildRoutePath(String pluginId, ReverseProxyRule reverseProxyRule) {
|
||||||
return PathUtils.combinePath(REVERSE_PROXY_API_PREFIX, pluginId, reverseProxyRule.path());
|
return PathUtils.combinePath(PluginConst.assertsRoutePrefix(pluginId),
|
||||||
|
reverseProxyRule.path());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -140,13 +124,13 @@ public class ReverseProxyRouterFunctionFactory {
|
||||||
if (StringUtils.isNotBlank(configuredFilename)) {
|
if (StringUtils.isNotBlank(configuredFilename)) {
|
||||||
filename = configuredFilename;
|
filename = configuredFilename;
|
||||||
} else {
|
} else {
|
||||||
AntPathMatcher antPathMatcher = new AntPathMatcher();
|
|
||||||
String routePath = buildRoutePath(pluginId, rule);
|
String routePath = buildRoutePath(pluginId, rule);
|
||||||
filename =
|
PathContainer pathContainer = PathPatternParser.defaultInstance.parse(routePath)
|
||||||
antPathMatcher.extractPathWithinPattern(routePath, request.path());
|
.extractPathWithinPattern(PathContainer.parsePath(request.path()));
|
||||||
|
filename = pathContainer.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
String filePath = PathUtils.appendPathSeparatorIfMissing(directory) + filename;
|
String filePath = PathUtils.combinePath(directory, filename);
|
||||||
return pluginApplicationContext.getResource(filePath);
|
return pluginApplicationContext.getResource(filePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,6 @@ import run.halo.app.extension.controller.Reconciler;
|
||||||
import run.halo.app.infra.utils.JsonUtils;
|
import run.halo.app.infra.utils.JsonUtils;
|
||||||
import run.halo.app.plugin.HaloPluginManager;
|
import run.halo.app.plugin.HaloPluginManager;
|
||||||
import run.halo.app.plugin.PluginStartingError;
|
import run.halo.app.plugin.PluginStartingError;
|
||||||
import run.halo.app.plugin.resources.JsBundleRuleProvider;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link PluginReconciler}.
|
* Tests for {@link PluginReconciler}.
|
||||||
|
@ -49,8 +48,7 @@ class PluginReconcilerTest {
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
JsBundleRuleProvider jsBundleRule = new JsBundleRuleProvider(haloPluginManager);
|
pluginReconciler = new PluginReconciler(extensionClient, haloPluginManager);
|
||||||
pluginReconciler = new PluginReconciler(extensionClient, haloPluginManager, jsBundleRule);
|
|
||||||
|
|
||||||
when(haloPluginManager.getPlugin(any())).thenReturn(pluginWrapper);
|
when(haloPluginManager.getPlugin(any())).thenReturn(pluginWrapper);
|
||||||
when(haloPluginManager.getUnresolvedPlugins()).thenReturn(List.of());
|
when(haloPluginManager.getUnresolvedPlugins()).thenReturn(List.of());
|
||||||
|
|
|
@ -21,6 +21,9 @@ class PathUtilsTest {
|
||||||
String s = PathUtils.combinePath(segments.split(","));
|
String s = PathUtils.combinePath(segments.split(","));
|
||||||
assertThat(s).isEqualTo(expected);
|
assertThat(s).isEqualTo(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
String s = PathUtils.combinePath("a", "", "c");
|
||||||
|
assertThat(s).isEqualTo("/a/c");
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, String> getCombinePathCases() {
|
private Map<String, String> getCombinePathCases() {
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
package run.halo.app.plugin.resources;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.lenient;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
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.Mockito;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import org.pf4j.PluginClassLoader;
|
||||||
|
import org.pf4j.PluginWrapper;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
import run.halo.app.plugin.HaloPluginManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link BundleResourceUtils}.
|
||||||
|
*
|
||||||
|
* @author guqing
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class BundleResourceUtilsTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private HaloPluginManager pluginManager;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() throws MalformedURLException {
|
||||||
|
PluginWrapper pluginWrapper = Mockito.mock(PluginWrapper.class);
|
||||||
|
PluginClassLoader pluginClassLoader = Mockito.mock(PluginClassLoader.class);
|
||||||
|
when(pluginWrapper.getPluginClassLoader()).thenReturn(pluginClassLoader);
|
||||||
|
lenient().when(pluginManager.getPlugin(eq("fake-plugin"))).thenReturn(pluginWrapper);
|
||||||
|
|
||||||
|
lenient().when(pluginClassLoader.getResource(eq("console/main.js"))).thenReturn(
|
||||||
|
new URL("file://console/main.js"));
|
||||||
|
lenient().when(pluginClassLoader.getResource(eq("console/style.css"))).thenReturn(
|
||||||
|
new URL("file://console/style.css"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getCssBundlePath() {
|
||||||
|
String cssBundlePath =
|
||||||
|
BundleResourceUtils.getCssBundlePath(pluginManager, "nothing-plugin");
|
||||||
|
assertThat(cssBundlePath).isNull();
|
||||||
|
|
||||||
|
cssBundlePath = BundleResourceUtils.getCssBundlePath(pluginManager, "fake-plugin");
|
||||||
|
assertThat(cssBundlePath).isEqualTo("/plugins/fake-plugin/assets/console/style.css");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getJsBundlePath() {
|
||||||
|
String jsBundlePath =
|
||||||
|
BundleResourceUtils.getJsBundlePath(pluginManager, "nothing-plugin");
|
||||||
|
assertThat(jsBundlePath).isNull();
|
||||||
|
|
||||||
|
jsBundlePath = BundleResourceUtils.getJsBundlePath(pluginManager, "fake-plugin");
|
||||||
|
assertThat(jsBundlePath).isEqualTo("/plugins/fake-plugin/assets/console/main.js");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getJsBundleResource() {
|
||||||
|
Resource jsBundleResource =
|
||||||
|
BundleResourceUtils.getJsBundleResource(pluginManager, "fake-plugin", "main.js");
|
||||||
|
assertThat(jsBundleResource).isNotNull();
|
||||||
|
assertThat(jsBundleResource.exists()).isTrue();
|
||||||
|
|
||||||
|
jsBundleResource =
|
||||||
|
BundleResourceUtils.getJsBundleResource(pluginManager, "fake-plugin", "test.js");
|
||||||
|
assertThat(jsBundleResource).isNull();
|
||||||
|
|
||||||
|
jsBundleResource =
|
||||||
|
BundleResourceUtils.getJsBundleResource(pluginManager, "nothing-plugin", "main.js");
|
||||||
|
assertThat(jsBundleResource).isNull();
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,7 +12,6 @@ import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
import reactor.test.StepVerifier;
|
import reactor.test.StepVerifier;
|
||||||
import run.halo.app.core.extension.ReverseProxy;
|
import run.halo.app.core.extension.ReverseProxy;
|
||||||
import run.halo.app.extension.Metadata;
|
import run.halo.app.extension.Metadata;
|
||||||
import run.halo.app.plugin.HaloPluginManager;
|
|
||||||
import run.halo.app.plugin.PluginApplicationContext;
|
import run.halo.app.plugin.PluginApplicationContext;
|
||||||
import run.halo.app.plugin.PluginConst;
|
import run.halo.app.plugin.PluginConst;
|
||||||
|
|
||||||
|
@ -27,16 +26,13 @@ class ReverseProxyRouterFunctionFactoryTest {
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private PluginApplicationContext pluginApplicationContext;
|
private PluginApplicationContext pluginApplicationContext;
|
||||||
@Mock
|
|
||||||
private HaloPluginManager haloPluginManager;
|
|
||||||
|
|
||||||
private ReverseProxyRouterFunctionFactory reverseProxyRouterFunctionFactory;
|
private ReverseProxyRouterFunctionFactory reverseProxyRouterFunctionFactory;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
JsBundleRuleProvider jsBundleRuleProvider = new JsBundleRuleProvider(haloPluginManager);
|
|
||||||
reverseProxyRouterFunctionFactory =
|
reverseProxyRouterFunctionFactory =
|
||||||
new ReverseProxyRouterFunctionFactory(jsBundleRuleProvider);
|
new ReverseProxyRouterFunctionFactory();
|
||||||
|
|
||||||
when(pluginApplicationContext.getPluginId()).thenReturn("fakeA");
|
when(pluginApplicationContext.getPluginId()).thenReturn("fakeA");
|
||||||
}
|
}
|
||||||
|
@ -50,7 +46,6 @@ class ReverseProxyRouterFunctionFactoryTest {
|
||||||
.verifyComplete();
|
.verifyComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private ReverseProxy mockReverseProxy() {
|
private ReverseProxy mockReverseProxy() {
|
||||||
ReverseProxy.ReverseProxyRule reverseProxyRule =
|
ReverseProxy.ReverseProxyRule reverseProxyRule =
|
||||||
new ReverseProxy.ReverseProxyRule("/static/**",
|
new ReverseProxy.ReverseProxyRule("/static/**",
|
||||||
|
|
Loading…
Reference in New Issue