feat: plugin supports multiple ways of specifying licenses (#2152)

* feat: plugin supports multiple ways of specifying licenses

* feat: add unit test case for deserialize license
pull/2158/head^2
guqing 2022-06-15 23:36:12 +08:00 committed by GitHub
parent 83d668b0a9
commit fcbf0031a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 165 additions and 29 deletions

View File

@ -1,10 +1,14 @@
package run.halo.app.plugin;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import run.halo.app.extension.AbstractExtension;
import run.halo.app.extension.GVK;
@ -42,7 +46,8 @@ public class Plugin extends AbstractExtension {
private String description;
private String license;
@JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
private List<License> license;
/**
* SemVer format.
@ -51,4 +56,19 @@ public class Plugin extends AbstractExtension {
private String pluginClass = BasePlugin.class.getName();
}
@Getter
@Setter
public static class License {
private String name;
private String url;
public License() {
}
public License(String name) {
this.name = name;
this.url = "";
}
}
}

View File

@ -2,12 +2,16 @@ package run.halo.app.plugin;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.pf4j.DefaultPluginDescriptor;
import org.pf4j.PluginDependency;
import org.pf4j.PluginDescriptor;
import org.pf4j.PluginDescriptorFinder;
import org.pf4j.util.FileUtils;
import org.springframework.util.CollectionUtils;
/**
* Find a plugin descriptor for a plugin path.
@ -41,6 +45,7 @@ public class YamlPluginDescriptorFinder implements PluginDescriptorFinder {
private DefaultPluginDescriptor convert(Plugin plugin) {
String pluginId = plugin.getMetadata().getName();
Plugin.PluginSpec spec = plugin.getSpec();
DefaultPluginDescriptor defaultPluginDescriptor =
new DefaultPluginDescriptor(pluginId,
spec.getDescription(),
@ -48,7 +53,7 @@ public class YamlPluginDescriptorFinder implements PluginDescriptorFinder {
spec.getVersion(),
spec.getRequires(),
spec.getAuthor(),
spec.getLicense());
joinLicense(spec.getLicense()));
// add dependencies
spec.getPluginDependencies().forEach((pluginDepName, versionRequire) -> {
PluginDependency dependency =
@ -57,4 +62,13 @@ public class YamlPluginDescriptorFinder implements PluginDescriptorFinder {
});
return defaultPluginDescriptor;
}
private String joinLicense(List<Plugin.License> licenses) {
if (CollectionUtils.isEmpty(licenses)) {
return StringUtils.EMPTY;
}
return licenses.stream()
.map(Plugin.License::getName)
.collect(Collectors.joining(","));
}
}

View File

@ -9,6 +9,7 @@ import lombok.extern.slf4j.Slf4j;
import org.pf4j.PluginRuntimeException;
import org.pf4j.util.FileUtils;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import run.halo.app.extension.Unstructured;
import run.halo.app.infra.utils.YamlUnstructuredLoader;
@ -70,8 +71,13 @@ public class YamlPluginFinder {
if (Files.notExists(propertiesPath)) {
throw new PluginRuntimeException("Cannot find '{}' path", propertiesPath);
}
Resource propertyResource = new FileSystemResource(propertiesPath);
return unstructuredToPlugin(propertyResource);
}
protected Plugin unstructuredToPlugin(Resource propertyResource) {
YamlUnstructuredLoader yamlUnstructuredLoader =
new YamlUnstructuredLoader(new FileSystemResource(propertiesPath));
new YamlUnstructuredLoader(propertyResource);
List<Unstructured> unstructuredList = yamlUnstructuredLoader.load();
if (unstructuredList.size() != 1) {
throw new PluginRuntimeException("Unable to find plugin descriptor file '{}'",

View File

@ -13,7 +13,10 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.pf4j.PluginRuntimeException;
import org.skyscreamer.jsonassert.JSONAssert;
import org.springframework.core.io.Resource;
import org.springframework.security.util.InMemoryResource;
import org.springframework.util.ResourceUtils;
import run.halo.app.extension.Unstructured;
import run.halo.app.infra.utils.JsonUtils;
/**
@ -38,33 +41,36 @@ class YamlPluginFinderTest {
Plugin plugin = pluginFinder.find(testPath);
assertThat(plugin).isNotNull();
JSONAssert.assertEquals("""
{
"spec": {
"displayName": "a name to show",
"version": "0.0.1",
"author": "guqing",
"logo": "https://guqing.xyz/avatar",
"pluginDependencies": {
"banana": "0.0.1"
{
"spec": {
"displayName": "a name to show",
"version": "0.0.1",
"author": "guqing",
"logo": "https://guqing.xyz/avatar",
"pluginDependencies": {
"banana": "0.0.1"
},
"homepage": "https://github.com/guqing/halo-plugin-1",
"description": "Tell me more about this plugin.",
"license": [{
"name": "MIT",
"url": ""
}],
"requires": ">=2.0.0",
"pluginClass": "run.halo.app.plugin.BasePlugin"
},
"homepage": "https://github.com/guqing/halo-plugin-1",
"description": "Tell me more about this plugin.",
"license": "MIT",
"requires": ">=2.0.0",
"pluginClass": "run.halo.app.plugin.BasePlugin"
},
"apiVersion": "v1",
"kind": "Plugin",
"metadata": {
"name": "plugin-1",
"labels": null,
"annotations": null,
"version": null,
"creationTimestamp": null,
"deletionTimestamp": null
"apiVersion": "v1",
"kind": "Plugin",
"metadata": {
"name": "plugin-1",
"labels": null,
"annotations": null,
"version": null,
"creationTimestamp": null,
"deletionTimestamp": null
}
}
}
""",
""",
JsonUtils.objectToJson(plugin),
false);
}
@ -77,4 +83,94 @@ class YamlPluginFinderTest {
}).isInstanceOf(PluginRuntimeException.class)
.hasMessage("Cannot find '/tmp/plugin.yaml' path");
}
}
@Test
void acceptArrayLicense() throws JSONException, JsonProcessingException {
Resource pluginResource = new InMemoryResource("""
apiVersion: v1
kind: Plugin
metadata:
name: plugin-1
spec:
license: "MIT"
""");
Plugin plugin = pluginFinder.unstructuredToPlugin(pluginResource);
assertThat(plugin.getSpec()).isNotNull();
JSONAssert.assertEquals("""
[{
"name": "MIT",
"url": ""
}]
""", JsonUtils.objectToJson(plugin.getSpec().getLicense()), false);
}
@Test
void acceptMultipleItemArrayLicense() throws JsonProcessingException, JSONException {
Resource pluginResource = new InMemoryResource("""
apiVersion: v1
kind: Plugin
metadata:
name: plugin-1
spec:
license: ["MIT", "Apache-2.0"]
""");
Plugin plugin = pluginFinder.unstructuredToPlugin(pluginResource);
assertThat(plugin.getSpec()).isNotNull();
JSONAssert.assertEquals("""
[{
"name": "MIT",
"url": ""
},
{
"name": "Apache-2.0",
"url": ""
}]
""", JsonUtils.objectToJson(plugin.getSpec().getLicense()), false);
}
@Test
void acceptArrayObjectLicense() throws JSONException, JsonProcessingException {
Resource pluginResource = new InMemoryResource("""
apiVersion: v1
kind: Plugin
metadata:
name: plugin-1
spec:
license:
- name: MIT
url: https://exmple.com
""");
Plugin plugin = pluginFinder.unstructuredToPlugin(pluginResource);
assertThat(plugin.getSpec()).isNotNull();
JSONAssert.assertEquals("""
[{
"name": "MIT",
"url": "https://exmple.com"
}]
""", JsonUtils.objectToJson(plugin.getSpec().getLicense()), false);
}
@Test
void deserializeLicense() throws JSONException, JsonProcessingException {
String pluginJson = """
{
"apiVersion": "v1",
"kind": "Plugin",
"metadata": {
"name": "plugin-1"
},
"spec": {
"license": [
{
"name": "MIT",
"url": "https://exmple.com"
}
]
}
}
""";
Plugin plugin = Unstructured.OBJECT_MAPPER.readValue(pluginJson, Plugin.class);
assertThat(plugin.getSpec()).isNotNull();
JSONAssert.assertEquals(pluginJson, JsonUtils.objectToJson(plugin), false);
}
}