refactor: scanning of the jsBundleRule for plugins (#2249)

<!--  Thanks for sending a pull request!  Here are some tips for you:
1. 如果这是你的第一次,请阅读我们的贡献指南:<https://github.com/halo-dev/halo/blob/master/CONTRIBUTING.md>。
1. If this is your first time, please read our contributor guidelines: <https://github.com/halo-dev/halo/blob/master/CONTRIBUTING.md>.
2. 请根据你解决问题的类型为 Pull Request 添加合适的标签。
2. Please label this pull request according to what type of issue you are addressing, especially if this is a release targeted pull request.
3. 请确保你已经添加并运行了适当的测试。
3. Ensure you have added or ran the appropriate tests for your PR.
-->

#### What type of PR is this?
/kind improvement
/area core
/milestone 2.0
<!--
添加其中一个类别:
Add one of the following kinds:

/kind bug
/kind cleanup
/kind documentation
/kind feature
/kind improvement

适当添加其中一个或多个类别(可选):
Optionally add one or more of the following kinds if applicable:

/kind api-change
/kind deprecation
/kind failing-test
/kind flake
/kind regression
-->

#### What this PR does / why we need it:
重构 JsBundle 获取方式,增加对 js entry 的文件校验如果不存在,则启用插件时不生成反向代理 APIs
#### Which issue(s) this PR fixes:

<!--
PR 合并时自动关闭 issue。
Automatically closes linked issue when PR is merged.

用法:`Fixes #<issue 号>`,或者 `Fixes (粘贴 issue 完整链接)`
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
-->
Fixes #

#### Special notes for your reviewer:
/cc @halo-dev/sig-halo 
#### Does this PR introduce a user-facing change?

<!--
如果当前 Pull Request 的修改不会造成用户侧的任何变更,在 `release-note` 代码块儿中填写 `NONE`。
否则请填写用户侧能够理解的 Release Note。如果当前 Pull Request 包含破坏性更新(Break Change),
Release Note 需要以 `action required` 开头。
If no, just write "NONE" in the release-note block below.
If yes, a release note is required:
Enter your extended release note in the block below. If the PR requires additional action from users switching to the new release, include the string "action required".
-->

```release-note
None
```
pull/2264/head
guqing 2022-07-18 22:42:04 +08:00 committed by GitHub
parent 1571b9bcf8
commit 275df33d75
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 60 additions and 48 deletions

View File

@ -44,7 +44,7 @@ public class Plugin extends AbstractExtension {
@JsonIgnore @JsonIgnore
public PluginStatus statusNonNull() { public PluginStatus statusNonNull() {
if (this.status == null) { if (this.status == null) {
return new PluginStatus(); this.status = new PluginStatus();
} }
return status; return status;
} }
@ -87,7 +87,8 @@ public class Plugin extends AbstractExtension {
@NonNull @NonNull
@JsonIgnore @JsonIgnore
public List<String> extensionLocationsNonNull() { public List<String> extensionLocationsNonNull() {
return Objects.requireNonNullElseGet(extensionLocations, List::of); this.extensionLocations = Objects.requireNonNullElseGet(extensionLocations, List::of);
return this.extensionLocations;
} }
} }

View File

@ -10,14 +10,12 @@ import lombok.extern.slf4j.Slf4j;
import org.pf4j.PluginRuntimeException; import org.pf4j.PluginRuntimeException;
import org.pf4j.PluginState; import org.pf4j.PluginState;
import org.pf4j.PluginWrapper; import org.pf4j.PluginWrapper;
import org.pf4j.RuntimeMode;
import run.halo.app.core.extension.Plugin; import run.halo.app.core.extension.Plugin;
import run.halo.app.extension.ExtensionClient; import run.halo.app.extension.ExtensionClient;
import run.halo.app.extension.controller.Reconciler; 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.YamlPluginFinder;
import run.halo.app.plugin.resources.JsBundleRuleProvider; import run.halo.app.plugin.resources.JsBundleRuleProvider;
import run.halo.app.plugin.resources.ReverseProxyRouterFunctionFactory; import run.halo.app.plugin.resources.ReverseProxyRouterFunctionFactory;
@ -83,8 +81,6 @@ public class PluginReconciler implements Reconciler {
return; return;
} }
ensureSpecUpToDateWhenDevelopmentMode(pluginWrapper, plugin);
if (!Objects.equals(pluginStatus.getPhase(), pluginWrapper.getPluginState())) { if (!Objects.equals(pluginStatus.getPhase(), pluginWrapper.getPluginState())) {
// Set to the correct state // Set to the correct state
pluginStatus.setPhase(pluginWrapper.getPluginState()); pluginStatus.setPhase(pluginWrapper.getPluginState());
@ -134,12 +130,14 @@ public class PluginReconciler implements Reconciler {
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 // TODO Check whether the JS bundle rule exists. If it does not exist, do not populate
// populate stylesheet path // populate stylesheet path
String jsBundleRoute = ReverseProxyRouterFunctionFactory.buildRoutePath(pluginName, jsBundleRule.jsRule(pluginName)
jsBundleRule.jsRule(pluginName)); .map(jsRule -> ReverseProxyRouterFunctionFactory.buildRoutePath(pluginName, jsRule))
String cssBundleRoute = ReverseProxyRouterFunctionFactory.buildRoutePath(pluginName, .ifPresent(status::setEntry);
jsBundleRule.cssRule(pluginName));
status.setEntry(jsBundleRoute); jsBundleRule.cssRule(pluginName)
status.setStylesheet(cssBundleRoute); .map(cssRule -> ReverseProxyRouterFunctionFactory.buildRoutePath(pluginName, cssRule))
.ifPresent(status::setStylesheet);
status.setLastStartTime(Instant.now()); status.setLastStartTime(Instant.now());
} }
@ -170,18 +168,4 @@ public class PluginReconciler implements Reconciler {
throw new PluginRuntimeException(startingError.getMessage()); throw new PluginRuntimeException(startingError.getMessage());
} }
} }
private void ensureSpecUpToDateWhenDevelopmentMode(PluginWrapper pluginWrapper,
Plugin oldPlugin) {
if (RuntimeMode.DEPLOYMENT.equals(pluginWrapper.getRuntimeMode())) {
return;
}
YamlPluginFinder yamlPluginFinder = new YamlPluginFinder();
Plugin pluginFromPath = yamlPluginFinder.find(pluginWrapper.getPluginPath());
// ensure plugin spec is up to date
Plugin.PluginSpec pluginSpec = JsonUtils.deepCopy(pluginFromPath.getSpec());
if (!Objects.equals(oldPlugin.getSpec(), pluginSpec)) {
oldPlugin.setSpec(pluginSpec);
}
}
} }

View File

@ -1,7 +1,13 @@
package run.halo.app.plugin.resources; 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 org.springframework.stereotype.Component;
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.plugin.HaloPluginManager;
/** /**
* TODO Optimize code to support user customize js bundle rules. * TODO Optimize code to support user customize js bundle rules.
@ -11,6 +17,20 @@ import run.halo.app.core.extension.ReverseProxy;
*/ */
@Component @Component
public class JsBundleRuleProvider { public class JsBundleRuleProvider {
private static final String JS_LOCATION = "/admin/main.js";
private static final String CSS_LOCATION = "/admin/style.css";
private static final FileReverseProxyProvider JS_FILE_PROXY =
new FileReverseProxyProvider("admin", "main.js");
private static final FileReverseProxyProvider CSS_FILE_PROXY =
new FileReverseProxyProvider("admin", "style.css");
private final HaloPluginManager haloPluginManager;
public JsBundleRuleProvider(HaloPluginManager haloPluginManager) {
this.haloPluginManager = haloPluginManager;
}
/** /**
* Gets plugin js bundle rule. * Gets plugin js bundle rule.
@ -18,10 +38,11 @@ public class JsBundleRuleProvider {
* @param pluginName plugin name * @param pluginName plugin name
* @return a js bundle rule * @return a js bundle rule
*/ */
public ReverseProxy.ReverseProxyRule jsRule(String pluginName) { public Optional<ReverseProxyRule> jsRule(String pluginName) {
ReverseProxy.FileReverseProxyProvider return Optional.of(JS_LOCATION)
file = new ReverseProxy.FileReverseProxyProvider("admin", "main.js"); .filter(path -> createResourceLoader(pluginName)
return new ReverseProxy.ReverseProxyRule("/admin/main.js", file); .getResource(path).exists())
.map(path -> new ReverseProxyRule(path, JS_FILE_PROXY));
} }
/** /**
@ -30,9 +51,21 @@ public class JsBundleRuleProvider {
* @param pluginName plugin name * @param pluginName plugin name
* @return a stylesheet bundle rule * @return a stylesheet bundle rule
*/ */
public ReverseProxy.ReverseProxyRule cssRule(String pluginName) { public Optional<ReverseProxyRule> cssRule(String pluginName) {
ReverseProxy.FileReverseProxyProvider return Optional.of(CSS_LOCATION)
file = new ReverseProxy.FileReverseProxyProvider("admin", "style.css"); .filter(path -> createResourceLoader(pluginName)
return new ReverseProxy.ReverseProxyRule("/admin/style.css", file); .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());
}
} }

View File

@ -114,15 +114,8 @@ public class ReverseProxyRouterFunctionFactory {
private List<ReverseProxyRule> getJsBundleRules(String pluginId) { private List<ReverseProxyRule> getJsBundleRules(String pluginId) {
List<ReverseProxyRule> rules = new ArrayList<>(2); List<ReverseProxyRule> rules = new ArrayList<>(2);
ReverseProxyRule jsRule = jsBundleRuleProvider.jsRule(pluginId); jsBundleRuleProvider.jsRule(pluginId).ifPresent(rules::add);
if (jsRule != null) { jsBundleRuleProvider.cssRule(pluginId).ifPresent(rules::add);
rules.add(jsRule);
}
ReverseProxyRule cssRule = jsBundleRuleProvider.cssRule(pluginId);
if (cssRule != null) {
rules.add(cssRule);
}
return rules; return rules;
} }

View File

@ -19,7 +19,6 @@ import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import org.pf4j.PluginState; import org.pf4j.PluginState;
import org.pf4j.PluginWrapper; import org.pf4j.PluginWrapper;
import org.pf4j.RuntimeMode;
import run.halo.app.core.extension.Plugin; import run.halo.app.core.extension.Plugin;
import run.halo.app.extension.ExtensionClient; import run.halo.app.extension.ExtensionClient;
import run.halo.app.extension.controller.Reconciler; import run.halo.app.extension.controller.Reconciler;
@ -50,11 +49,10 @@ class PluginReconcilerTest {
@BeforeEach @BeforeEach
void setUp() { void setUp() {
JsBundleRuleProvider jsBundleRule = new JsBundleRuleProvider(); JsBundleRuleProvider jsBundleRule = new JsBundleRuleProvider(haloPluginManager);
pluginReconciler = new PluginReconciler(extensionClient, haloPluginManager, jsBundleRule); pluginReconciler = new PluginReconciler(extensionClient, haloPluginManager, jsBundleRule);
when(haloPluginManager.getPlugin(any())).thenReturn(pluginWrapper); when(haloPluginManager.getPlugin(any())).thenReturn(pluginWrapper);
when(pluginWrapper.getRuntimeMode()).thenReturn(RuntimeMode.DEPLOYMENT);
when(haloPluginManager.getUnresolvedPlugins()).thenReturn(List.of()); when(haloPluginManager.getUnresolvedPlugins()).thenReturn(List.of());
} }

View File

@ -17,6 +17,7 @@ import org.springframework.web.reactive.function.server.ServerResponse;
import run.halo.app.core.extension.ReverseProxy; import run.halo.app.core.extension.ReverseProxy;
import run.halo.app.extension.ExtensionClient; import run.halo.app.extension.ExtensionClient;
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;
@ -34,12 +35,14 @@ 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(); JsBundleRuleProvider jsBundleRuleProvider = new JsBundleRuleProvider(haloPluginManager);
reverseProxyRouterFunctionFactory = new ReverseProxyRouterFunctionFactory(extensionClient, reverseProxyRouterFunctionFactory = new ReverseProxyRouterFunctionFactory(extensionClient,
jsBundleRuleProvider); jsBundleRuleProvider);