refactor: exception prompts during plugin installation (#3993)

#### What type of PR is this?
/kind improvement
/area core
/milestone 2.6.x

#### What this PR does / why we need it:
优化插件安装失败的提示信息

插件安装和升级时由于包格式不正确改为如下提示(Localization)
<img width="449" alt="image" src="https://github.com/halo-dev/halo/assets/38999863/37da0d42-88fa-40c5-a2b9-b8e2698a5930">

how to test it?
使用下面的插件安装和升级会提示 plugin.yaml 缺失
[failed-plugins.zip](https://github.com/halo-dev/halo/files/11560921/failed-plugins.zip)

see #3843 for more details

#### Which issue(s) this PR fixes:

Fixes #3843

#### Does this PR introduce a user-facing change?

```release-note
优化插件安装失败的提示信息
```
pull/4005/head
guqing 2023-05-26 22:56:12 +08:00 committed by GitHub
parent 710261b035
commit c8cc9f2710
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 51 additions and 38 deletions

View File

@ -28,6 +28,7 @@ import run.halo.app.extension.MetadataUtil;
import run.halo.app.extension.ReactiveExtensionClient;
import run.halo.app.infra.SystemVersionSupplier;
import run.halo.app.infra.exception.PluginAlreadyExistsException;
import run.halo.app.infra.exception.PluginInstallationException;
import run.halo.app.infra.exception.UnsatisfiedAttributeValueException;
import run.halo.app.infra.utils.FileUtils;
import run.halo.app.infra.utils.VersionUtils;
@ -70,49 +71,47 @@ public class PluginServiceImpl implements PluginService {
@Override
public Mono<Plugin> install(Path path) {
return Mono.defer(() -> {
final var pluginFinder = new YamlPluginFinder();
final var pluginInPath = pluginFinder.find(path);
// validate the plugin version
satisfiesRequiresVersion(pluginInPath);
return findPluginManifest(path)
.flatMap(pluginInPath -> {
// validate the plugin version
satisfiesRequiresVersion(pluginInPath);
return client.fetch(Plugin.class, pluginInPath.getMetadata().getName())
.flatMap(oldPlugin -> Mono.<Plugin>error(
new PluginAlreadyExistsException(oldPlugin.getMetadata().getName())))
.switchIfEmpty(Mono.defer(
() -> copyToPluginHome(pluginInPath)
.map(pluginFinder::find)
.doOnNext(p -> {
// Disable auto enable after installation
p.getSpec().setEnabled(false);
})
.flatMap(client::create)));
});
return client.fetch(Plugin.class, pluginInPath.getMetadata().getName())
.flatMap(oldPlugin -> Mono.<Plugin>error(
new PluginAlreadyExistsException(oldPlugin.getMetadata().getName())))
.switchIfEmpty(Mono.defer(
() -> copyToPluginHome(pluginInPath)
.flatMap(this::findPluginManifest)
.doOnNext(p -> {
// Disable auto enable after installation
p.getSpec().setEnabled(false);
})
.flatMap(client::create))
);
});
}
@Override
public Mono<Plugin> upgrade(String name, Path path) {
return Mono.defer(() -> {
// pre-check the plugin in the path
final var pluginFinder = new YamlPluginFinder();
final var pluginInPath = pluginFinder.find(path);
Validate.notNull(pluginInPath.statusNonNull().getLoadLocation());
satisfiesRequiresVersion(pluginInPath);
if (!Objects.equals(name, pluginInPath.getMetadata().getName())) {
return Mono.error(new ServerWebInputException(
"The provided plugin " + pluginInPath.getMetadata().getName()
+ " didn't match the given plugin " + name));
}
return findPluginManifest(path)
.flatMap(pluginInPath -> {
// pre-check the plugin in the path
Validate.notNull(pluginInPath.statusNonNull().getLoadLocation());
satisfiesRequiresVersion(pluginInPath);
if (!Objects.equals(name, pluginInPath.getMetadata().getName())) {
return Mono.error(new ServerWebInputException(
"The provided plugin " + pluginInPath.getMetadata().getName()
+ " didn't match the given plugin " + name));
}
// check if the plugin exists
return client.fetch(Plugin.class, name)
.switchIfEmpty(Mono.error(() -> new ServerWebInputException(
"The given plugin with name " + name + " was not found.")))
// copy plugin into plugin home
.flatMap(prevPlugin -> copyToPluginHome(pluginInPath))
.flatMap(pluginPath -> updateReloadAnno(name, pluginPath));
});
// check if the plugin exists
return client.fetch(Plugin.class, name)
.switchIfEmpty(Mono.error(() -> new ServerWebInputException(
"The given plugin with name " + name + " was not found.")))
// copy plugin into plugin home
.flatMap(prevPlugin -> copyToPluginHome(pluginInPath))
.flatMap(pluginPath -> updateReloadAnno(name, pluginPath));
});
}
@Override
@ -125,6 +124,17 @@ public class PluginServiceImpl implements PluginService {
return updateReloadAnno(name, pluginWrapper.getPluginPath());
}
Mono<Plugin> findPluginManifest(Path path) {
return Mono.fromSupplier(
() -> {
final var pluginFinder = new YamlPluginFinder();
return pluginFinder.find(path);
})
.onErrorMap(e -> new PluginInstallationException("Failed to parse the plugin manifest",
"problemDetail.plugin.missingManifest", null)
);
}
private Mono<Plugin> updateReloadAnno(String name, Path pluginPath) {
return client.get(Plugin.class, name)
.flatMap(plugin -> {

View File

@ -75,8 +75,9 @@ public class YamlPluginFinder {
}
protected Plugin readPluginDescriptor(Path pluginPath) {
Path propertiesPath = getManifestPath(pluginPath, propertiesFileName);
Path propertiesPath = null;
try {
propertiesPath = getManifestPath(pluginPath, propertiesFileName);
if (propertiesPath == null) {
throw new PluginRuntimeException("Cannot find the plugin manifest path");
}

View File

@ -45,3 +45,4 @@ problemDetail.theme.version.unsatisfied.requires=The theme requires a minimum sy
problemDetail.directoryTraversal=Directory traversal detected. Base path is {0}, but real path is {1}.
problemDetail.plugin.version.unsatisfied.requires=Plugin requires a minimum system version of {0}, but the current version is {1}.
problemDetail.plugin.missingManifest=Missing plugin manifest file "plugin.yaml" or manifest file does not conform to the specification.

View File

@ -14,6 +14,7 @@ problemDetail.user.signUpFailed.disallowed=系统不允许注册新用户。
problemDetail.user.duplicateName=用户名 {0} 已存在,请更换用户名后重试。
problemDetail.plugin.version.unsatisfied.requires=插件要求一个最小的系统版本为 {0}, 但当前版本为 {1}。
problemDetail.plugin.missingManifest=缺少 plugin.yaml 配置文件或配置文件不符合规范。
problemDetail.theme.version.unsatisfied.requires=主题要求一个最小的系统版本为 {0}, 但当前版本为 {1}。
problemDetail.theme.install.missingManifest=缺少 theme.yaml 配置文件或配置文件不符合规范。