feat(系统设置): 初始化插件sdk

--task=1012390 --user=陈建星 系统设置-系统-插件管理-后台 https://www.tapd.cn/55049933/s/1399012
This commit is contained in:
jianxing 2023-07-28 17:28:59 +08:00 committed by jianxing
parent c97d176ad0
commit a2157ac189
41 changed files with 392 additions and 133 deletions

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.metersphere</groupId>
<artifactId>plugin</artifactId>
<version>${revision}</version>
</parent>
<artifactId>metersphere-api-plugin-sdk</artifactId>
<version>${revision}</version>
<dependencies>
<dependency>
<groupId>io.metersphere</groupId>
<artifactId>metersphere-plugin-sdk</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>org.apache.jmeter</groupId>
<artifactId>ApacheJMeter_core</artifactId>
<version>${jmeter.version}</version>
</dependency>
<dependency>
<groupId>org.apache.jmeter</groupId>
<artifactId>jorphan</artifactId>
<version>${jmeter.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -1,4 +1,4 @@
package io.metersphere.plugin.annotation;
package io.metersphere.plugin.api.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;

View File

@ -0,0 +1,15 @@
package io.metersphere.plugin.api.api;
import io.metersphere.plugin.sdk.api.AbstractMsPlugin;
public abstract class AbstractApiPlugin extends AbstractMsPlugin {
private static final String API_PLUGIN_TYPE = "API";
public AbstractApiPlugin(ClassLoader pluginClassLoader) {
super(pluginClassLoader);
}
@Override
public String getType() {
return API_PLUGIN_TYPE;
}
}

View File

@ -2,7 +2,7 @@ package io.metersphere.plugin.api.dto;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import io.metersphere.plugin.util.PluginLogUtils;
import io.metersphere.plugin.sdk.util.PluginLogUtils;
import lombok.Data;
import org.apache.jmeter.save.SaveService;
import org.apache.jorphan.collections.ListedHashTree;

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.metersphere</groupId>
<artifactId>plugin</artifactId>
<version>${revision}</version>
</parent>
<artifactId>metersphere-platform-plugin-sdk</artifactId>
<version>${revision}</version>
<dependencies>
<dependency>
<groupId>io.metersphere</groupId>
<artifactId>metersphere-plugin-sdk</artifactId>
<version>${revision}</version>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>

View File

@ -0,0 +1,15 @@
package io.metersphere.plugin.platform.api;
import io.metersphere.plugin.sdk.api.AbstractMsPlugin;
public abstract class AbstractPlatformPlugin extends AbstractMsPlugin {
private static final String PLATFORM_PLUGIN_TYPE = "PLATFORM";
public AbstractPlatformPlugin(ClassLoader pluginClassLoader) {
super(pluginClassLoader);
}
@Override
public String getType() {
return PLATFORM_PLUGIN_TYPE;
}
}

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.metersphere</groupId>
<artifactId>plugin</artifactId>
<version>${revision}</version>
</parent>
<groupId>io.metersphere</groupId>
<artifactId>metersphere-plugin-sdk</artifactId>
<version>${revision}</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,84 @@
package io.metersphere.plugin.sdk.api;
import io.metersphere.plugin.sdk.util.PluginLogUtils;
import org.apache.commons.io.IOUtils;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
public abstract class AbstractMsPlugin implements MsPlugin {
private static final String SCRIPT_DIR = "script";
private ClassLoader pluginClassLoader;
public AbstractMsPlugin(ClassLoader pluginClassLoader) {
this.pluginClassLoader = pluginClassLoader;
}
protected InputStream readResource(String name) {
return pluginClassLoader.getResourceAsStream(name);
}
/**
* @return 返回该加载前端配置文件的目录默认是 script
* 可以重写定制
*/
protected String getScriptDir() {
return SCRIPT_DIR;
}
/**
* @return 返回 resources下的 script 下的 json 文件
*/
@Override
public List<String> getFrontendScript() {
List<String> scriptList = new ArrayList<>();
String scriptDirName = getScriptDir();
URL scriptDir = pluginClassLoader.getResource(scriptDirName);
if (scriptDir != null) {
File resourceDir = new File(scriptDir.getFile());
List<String> filePaths = getFilePaths(resourceDir);
for (String filePath : filePaths) {
InputStream in = readResource(scriptDirName + "/" + filePath);
try {
if (in != null) {
scriptList.add(IOUtils.toString(in));
}
} catch (IOException e) {
PluginLogUtils.error(e);
}
}
}
return scriptList;
}
private static List<String> getFilePaths(File directory) {
List<String> filePaths = new ArrayList<>();
File[] files = directory.listFiles();
if (files != null) {
for (File file : files) {
if (file.isDirectory()) {
filePaths.addAll(getFilePaths(file));
} else {
filePaths.add(file.getAbsolutePath());
}
}
}
return filePaths;
}
@Override
public String getName() {
return getKey();
}
@Override
public String getPluginId() {
return getName().toLowerCase() + "-" + getVersion();
}
}

View File

@ -0,0 +1,50 @@
package io.metersphere.plugin.sdk.api;
import java.util.List;
/**
* 插件的基本信息
*
* @author jianxing.chen
*/
public interface MsPlugin {
/**
* @return 返回该插件是否是开源的默认是
*/
boolean isXpack();
/**
* @return 返回插件的类型
* 目前支持接口插件和平台(APIPLATFORM)
*/
String getType();
/**
* @return 返回插件的关键字例如 Jira
*/
String getKey();
/**
* @return 返回插件的名称
* 默认返回 key
*/
String getName();
/**
* @return 返回插件的ID
* 默认是 key + 版本号
*/
String getPluginId();
/**
* @return 返回前端渲染需要的数据
* 默认会返回 resources下的 script 下的 json 文件
*/
List<String> getFrontendScript();
/**
* @return 返回插件的版本
*/
String getVersion();
}

View File

@ -0,0 +1,99 @@
package io.metersphere.plugin.sdk.util;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.type.CollectionType;
import com.fasterxml.jackson.databind.type.TypeFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
public class JSON {
private static final ObjectMapper objectMapper = new ObjectMapper();
private static final TypeFactory typeFactory = objectMapper.getTypeFactory();
static {
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// 自动检测所有类的全部属性
objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
// 如果一个对象中没有任何的属性那么在序列化的时候就会报错
objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
}
public static String toJSONString(Object value) {
try {
return objectMapper.writeValueAsString(value);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static Object parseObject(String content) {
return parseObject(content, Object.class);
}
public static <T> T parseObject(String content, Class<T> valueType) {
try {
return objectMapper.readValue(content, valueType);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static <T> T parseObject(String content, TypeReference<T> valueType) {
try {
return objectMapper.readValue(content, valueType);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static <T> T parseObject(InputStream src, Class<T> valueType) {
try {
return objectMapper.readValue(src, valueType);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static List parseArray(String content) {
return parseArray(content, Object.class);
}
public static <T> List<T> parseArray(String content, Class<T> valueType) {
CollectionType javaType = typeFactory.constructCollectionType(List.class, valueType);
try {
return objectMapper.readValue(content, javaType);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static <T> List<T> parseArray(String content, TypeReference<T> valueType) {
try {
JavaType subType = typeFactory.constructType(valueType);
CollectionType javaType = typeFactory.constructCollectionType(List.class, subType);
return objectMapper.readValue(content, javaType);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static Map parseMap(String jsonObject) {
try {
return objectMapper.readValue(jsonObject, new TypeReference<>() {
});
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -1,4 +1,4 @@
package io.metersphere.plugin.util;
package io.metersphere.plugin.sdk.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@ -8,18 +8,23 @@
<groupId>io.metersphere</groupId>
<artifactId>framework</artifactId>
<version>${revision}</version>
</parent>
<groupId>io.metersphere</groupId>
<artifactId>metersphere-plugin-sdk</artifactId>
<artifactId>plugin</artifactId>
<version>${revision}</version>
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<name>metersphere-plugin-sdk</name>
<description>MeterSphere plugin</description>
<modules>
<module>metersphere-plugin-sdk</module>
<module>metersphere-api-plugin-sdk</module>
<module>metersphere-platform-plugin-sdk</module>
</modules>
<dependencies>
<dependency>

View File

@ -1,18 +0,0 @@
package io.metersphere.plugin.api.api;
import io.metersphere.plugin.api.dto.ApiPluginDTO;
public abstract class ApiPluginService {
/**
* 初始化页面脚本
* 上传JAR到MeterSphere平台后会自动调用
*/
public abstract ApiPluginDTO init();
/**
* 自定义方法用于在自定义插件中初始化数据
* @return 返回要处理的JSON数据
*/
public abstract String custom(String... args);
}

View File

@ -1,46 +0,0 @@
package io.metersphere.plugin.platform.api;
/**
* 插件的基本信息
* @author jianxing.chen
*/
public interface PluginMetaInfo {
/**
* 该插件是否是开源的
* 默认是
* @return
*/
boolean isXpack();
/**
* 返回插件的关键字
* @return
*/
String getKey();
/**
* 返回插件的名称
* @return
*/
String getLabel();
/**
* 返回前端渲染需要的数据
* 默认会返回 resources下的 json/frontend.json
* @return
*/
String getFrontendMetaData();
/**
* 返回插件的版本
* @return
*/
String getVersion();
/**
* 插件是否支持第三方模板
* @return
*/
boolean isThirdPartTemplateSupport();
}

View File

@ -27,19 +27,20 @@
<!-- 基础包 -->
<dependency>
<groupId>io.metersphere</groupId>
<artifactId>metersphere-plugin-sdk</artifactId>
<artifactId>metersphere-api-plugin-sdk</artifactId>
<version>${revision}</version>
<exclusions>
<exclusion>
<artifactId>log4j-slf4j-impl</artifactId>
<groupId>org.apache.logging.log4j</groupId>
</exclusion>
<exclusion>
<artifactId>ApacheJMeter_core</artifactId>
<groupId>org.apache.jmeter</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.metersphere</groupId>
<artifactId>metersphere-platform-plugin-sdk</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.core5</groupId>
<artifactId>httpcore5</artifactId>

View File

@ -1,38 +1,14 @@
package io.metersphere.sdk.plugin.loader;
import io.metersphere.plugin.platform.api.Platform;
import io.metersphere.plugin.platform.api.PluginMetaInfo;
import io.metersphere.plugin.platform.dto.PlatformRequest;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.List;
/**
* @author jianxing.chen
*/
public class PlatformPluginManager extends PluginManager {
/**
* 获取当前加载的插件元数据列表
* @return
*/
public List<PluginMetaInfo> getPluginMetaInfoList() {
List<PluginMetaInfo> platFormOptions = new ArrayList<>();
for (String pluginId : getClassLoaderMap().keySet()) {
platFormOptions.add(getImplInstance(pluginId, PluginMetaInfo.class));
}
return platFormOptions;
}
/**
* 获取当前插件的元数据
* @param pluginId 插件ID
* @return
*/
public PluginMetaInfo getPluginMetaInfo(String pluginId) {
return getImplInstance(pluginId, PluginMetaInfo.class);
}
/**
* 获取对应插件的 Platform 对象
@ -44,36 +20,4 @@ public class PlatformPluginManager extends PluginManager {
return getImplInstance(pluginId, Platform.class, request);
}
/**
* 获取对应插件的 Platform 对象
* @param key 插件 key
* @param request
* @return
*/
public Platform getPlatformByKey(String key, PlatformRequest request) {
for (String pluginId : getClassLoaderMap().keySet()) {
// 查找对应 key 的插件
PluginMetaInfo pluginMetaInfo = getPluginMetaInfo(pluginId);
if (StringUtils.equals(pluginMetaInfo.getKey(), key)) {
return getPlatform(pluginId, request);
}
}
return null;
}
/**
* 获取当前插件的元数据
* @param key 插件key
* @return
*/
public PluginMetaInfo getPluginMetaInfoByKey(String key) {
for (String pluginId : getClassLoaderMap().keySet()) {
// 查找对应 key 的插件
PluginMetaInfo pluginMetaInfo = getPluginMetaInfo(pluginId);
if (StringUtils.equals(pluginMetaInfo.getKey(), key)) {
return pluginMetaInfo;
}
}
return null;
}
}

View File

@ -1,6 +1,6 @@
package io.metersphere.api.dto.jmeter.processors;
import io.metersphere.plugin.annotation.PluginSubType;
import io.metersphere.plugin.api.annotation.PluginSubType;
import io.metersphere.plugin.api.dto.TestElementDTO;
import lombok.Data;
import lombok.EqualsAndHashCode;

View File

@ -1,6 +1,6 @@
package io.metersphere.api.dto.jmeter.sampler;
import io.metersphere.plugin.annotation.PluginSubType;
import io.metersphere.plugin.api.annotation.PluginSubType;
import io.metersphere.plugin.api.dto.TestElementDTO;
import lombok.Data;
import lombok.EqualsAndHashCode;