mirror of https://github.com/halo-dev/halo
feat: plugin supports multiple ways of specifying licenses (#2152)
* feat: plugin supports multiple ways of specifying licenses * feat: add unit test case for deserialize licensepull/2158/head^2
parent
83d668b0a9
commit
fcbf0031a4
|
@ -1,10 +1,14 @@
|
||||||
package run.halo.app.plugin;
|
package run.halo.app.plugin;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
import run.halo.app.extension.AbstractExtension;
|
import run.halo.app.extension.AbstractExtension;
|
||||||
import run.halo.app.extension.GVK;
|
import run.halo.app.extension.GVK;
|
||||||
|
@ -42,7 +46,8 @@ public class Plugin extends AbstractExtension {
|
||||||
|
|
||||||
private String description;
|
private String description;
|
||||||
|
|
||||||
private String license;
|
@JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
|
||||||
|
private List<License> license;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SemVer format.
|
* SemVer format.
|
||||||
|
@ -51,4 +56,19 @@ public class Plugin extends AbstractExtension {
|
||||||
|
|
||||||
private String pluginClass = BasePlugin.class.getName();
|
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 = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,16 @@ package run.halo.app.plugin;
|
||||||
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.pf4j.DefaultPluginDescriptor;
|
import org.pf4j.DefaultPluginDescriptor;
|
||||||
import org.pf4j.PluginDependency;
|
import org.pf4j.PluginDependency;
|
||||||
import org.pf4j.PluginDescriptor;
|
import org.pf4j.PluginDescriptor;
|
||||||
import org.pf4j.PluginDescriptorFinder;
|
import org.pf4j.PluginDescriptorFinder;
|
||||||
import org.pf4j.util.FileUtils;
|
import org.pf4j.util.FileUtils;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a plugin descriptor for a plugin path.
|
* Find a plugin descriptor for a plugin path.
|
||||||
|
@ -41,6 +45,7 @@ public class YamlPluginDescriptorFinder implements PluginDescriptorFinder {
|
||||||
private DefaultPluginDescriptor convert(Plugin plugin) {
|
private DefaultPluginDescriptor convert(Plugin plugin) {
|
||||||
String pluginId = plugin.getMetadata().getName();
|
String pluginId = plugin.getMetadata().getName();
|
||||||
Plugin.PluginSpec spec = plugin.getSpec();
|
Plugin.PluginSpec spec = plugin.getSpec();
|
||||||
|
|
||||||
DefaultPluginDescriptor defaultPluginDescriptor =
|
DefaultPluginDescriptor defaultPluginDescriptor =
|
||||||
new DefaultPluginDescriptor(pluginId,
|
new DefaultPluginDescriptor(pluginId,
|
||||||
spec.getDescription(),
|
spec.getDescription(),
|
||||||
|
@ -48,7 +53,7 @@ public class YamlPluginDescriptorFinder implements PluginDescriptorFinder {
|
||||||
spec.getVersion(),
|
spec.getVersion(),
|
||||||
spec.getRequires(),
|
spec.getRequires(),
|
||||||
spec.getAuthor(),
|
spec.getAuthor(),
|
||||||
spec.getLicense());
|
joinLicense(spec.getLicense()));
|
||||||
// add dependencies
|
// add dependencies
|
||||||
spec.getPluginDependencies().forEach((pluginDepName, versionRequire) -> {
|
spec.getPluginDependencies().forEach((pluginDepName, versionRequire) -> {
|
||||||
PluginDependency dependency =
|
PluginDependency dependency =
|
||||||
|
@ -57,4 +62,13 @@ public class YamlPluginDescriptorFinder implements PluginDescriptorFinder {
|
||||||
});
|
});
|
||||||
return defaultPluginDescriptor;
|
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(","));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||||
import org.pf4j.PluginRuntimeException;
|
import org.pf4j.PluginRuntimeException;
|
||||||
import org.pf4j.util.FileUtils;
|
import org.pf4j.util.FileUtils;
|
||||||
import org.springframework.core.io.FileSystemResource;
|
import org.springframework.core.io.FileSystemResource;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
import run.halo.app.extension.Unstructured;
|
import run.halo.app.extension.Unstructured;
|
||||||
import run.halo.app.infra.utils.YamlUnstructuredLoader;
|
import run.halo.app.infra.utils.YamlUnstructuredLoader;
|
||||||
|
|
||||||
|
@ -70,8 +71,13 @@ public class YamlPluginFinder {
|
||||||
if (Files.notExists(propertiesPath)) {
|
if (Files.notExists(propertiesPath)) {
|
||||||
throw new PluginRuntimeException("Cannot find '{}' path", propertiesPath);
|
throw new PluginRuntimeException("Cannot find '{}' path", propertiesPath);
|
||||||
}
|
}
|
||||||
|
Resource propertyResource = new FileSystemResource(propertiesPath);
|
||||||
|
return unstructuredToPlugin(propertyResource);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Plugin unstructuredToPlugin(Resource propertyResource) {
|
||||||
YamlUnstructuredLoader yamlUnstructuredLoader =
|
YamlUnstructuredLoader yamlUnstructuredLoader =
|
||||||
new YamlUnstructuredLoader(new FileSystemResource(propertiesPath));
|
new YamlUnstructuredLoader(propertyResource);
|
||||||
List<Unstructured> unstructuredList = yamlUnstructuredLoader.load();
|
List<Unstructured> unstructuredList = yamlUnstructuredLoader.load();
|
||||||
if (unstructuredList.size() != 1) {
|
if (unstructuredList.size() != 1) {
|
||||||
throw new PluginRuntimeException("Unable to find plugin descriptor file '{}'",
|
throw new PluginRuntimeException("Unable to find plugin descriptor file '{}'",
|
||||||
|
|
|
@ -13,7 +13,10 @@ import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.pf4j.PluginRuntimeException;
|
import org.pf4j.PluginRuntimeException;
|
||||||
import org.skyscreamer.jsonassert.JSONAssert;
|
import org.skyscreamer.jsonassert.JSONAssert;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.security.util.InMemoryResource;
|
||||||
import org.springframework.util.ResourceUtils;
|
import org.springframework.util.ResourceUtils;
|
||||||
|
import run.halo.app.extension.Unstructured;
|
||||||
import run.halo.app.infra.utils.JsonUtils;
|
import run.halo.app.infra.utils.JsonUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -38,33 +41,36 @@ class YamlPluginFinderTest {
|
||||||
Plugin plugin = pluginFinder.find(testPath);
|
Plugin plugin = pluginFinder.find(testPath);
|
||||||
assertThat(plugin).isNotNull();
|
assertThat(plugin).isNotNull();
|
||||||
JSONAssert.assertEquals("""
|
JSONAssert.assertEquals("""
|
||||||
{
|
{
|
||||||
"spec": {
|
"spec": {
|
||||||
"displayName": "a name to show",
|
"displayName": "a name to show",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"author": "guqing",
|
"author": "guqing",
|
||||||
"logo": "https://guqing.xyz/avatar",
|
"logo": "https://guqing.xyz/avatar",
|
||||||
"pluginDependencies": {
|
"pluginDependencies": {
|
||||||
"banana": "0.0.1"
|
"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",
|
"apiVersion": "v1",
|
||||||
"description": "Tell me more about this plugin.",
|
"kind": "Plugin",
|
||||||
"license": "MIT",
|
"metadata": {
|
||||||
"requires": ">=2.0.0",
|
"name": "plugin-1",
|
||||||
"pluginClass": "run.halo.app.plugin.BasePlugin"
|
"labels": null,
|
||||||
},
|
"annotations": null,
|
||||||
"apiVersion": "v1",
|
"version": null,
|
||||||
"kind": "Plugin",
|
"creationTimestamp": null,
|
||||||
"metadata": {
|
"deletionTimestamp": null
|
||||||
"name": "plugin-1",
|
}
|
||||||
"labels": null,
|
|
||||||
"annotations": null,
|
|
||||||
"version": null,
|
|
||||||
"creationTimestamp": null,
|
|
||||||
"deletionTimestamp": null
|
|
||||||
}
|
}
|
||||||
}
|
""",
|
||||||
""",
|
|
||||||
JsonUtils.objectToJson(plugin),
|
JsonUtils.objectToJson(plugin),
|
||||||
false);
|
false);
|
||||||
}
|
}
|
||||||
|
@ -77,4 +83,94 @@ class YamlPluginFinderTest {
|
||||||
}).isInstanceOf(PluginRuntimeException.class)
|
}).isInstanceOf(PluginRuntimeException.class)
|
||||||
.hasMessage("Cannot find '/tmp/plugin.yaml' path");
|
.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue