feat(系统设置): 插件管理支持上传JDBC驱动
This commit is contained in:
parent
2aba451d92
commit
3a77833613
|
@ -1,5 +1,5 @@
|
|||
package io.metersphere.sdk.constants;
|
||||
|
||||
public enum PluginScenarioType {
|
||||
API, PLATFORM
|
||||
API, PLATFORM, JDBC_DRIVER
|
||||
}
|
||||
|
|
|
@ -142,21 +142,25 @@ public class PluginManager {
|
|||
if (impClazz == superClazz) {
|
||||
return true;
|
||||
}
|
||||
Type[] interfaces = impClazz.getGenericInterfaces();
|
||||
try {
|
||||
Type[] interfaces = impClazz.getGenericInterfaces();
|
||||
|
||||
if (interfaces != null && interfaces.length > 0) {
|
||||
for (Type genericInterface : interfaces) {
|
||||
if (genericInterface instanceof Class && isImplClazz(superClazz, (Class) genericInterface)) {
|
||||
return true;
|
||||
if (interfaces != null && interfaces.length > 0) {
|
||||
for (Type genericInterface : interfaces) {
|
||||
if (genericInterface instanceof Class && isImplClazz(superClazz, (Class) genericInterface)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Type superclass = impClazz.getGenericSuperclass();
|
||||
if (superclass != null
|
||||
&& superclass instanceof Class
|
||||
&& isImplClazz(superClazz, (Class) superclass)) {
|
||||
return true;
|
||||
Type superclass = impClazz.getGenericSuperclass();
|
||||
if (superclass != null
|
||||
&& superclass instanceof Class
|
||||
&& isImplClazz(superClazz, (Class) superclass)) {
|
||||
return true;
|
||||
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
LogUtils.error(e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
package io.metersphere.sdk.service;
|
||||
|
||||
import io.metersphere.sdk.constants.PluginScenarioType;
|
||||
import io.metersphere.system.domain.Plugin;
|
||||
import io.metersphere.system.domain.PluginExample;
|
||||
import io.metersphere.system.mapper.PluginMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.sql.Driver;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public class JdbcDriverPluginService {
|
||||
|
||||
@Resource
|
||||
private PluginLoadService pluginLoadService;
|
||||
@Resource
|
||||
private PluginMapper pluginMapper;
|
||||
|
||||
public boolean isJdbcDriver(String pluginId) {
|
||||
return pluginLoadService.getImplClass(pluginId, Driver.class) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有的 JDBC 驱动的实现类
|
||||
* @return
|
||||
*/
|
||||
public List<String> getJdbcDriverClass() {
|
||||
PluginExample example = new PluginExample();
|
||||
example.createCriteria().andScenarioEqualTo(PluginScenarioType.JDBC_DRIVER.name());
|
||||
List<Plugin> plugins = pluginMapper.selectByExample(example);
|
||||
List<String> pluginDriverClassNames = plugins.stream().map(plugin ->
|
||||
pluginLoadService.getImplClass(plugin.getId(), Driver.class)
|
||||
).map(Class::getName).collect(Collectors.toList());
|
||||
// 已经内置了 mysql 依赖
|
||||
pluginDriverClassNames.add("com.mysql.jdbc.Driver");
|
||||
return pluginDriverClassNames;
|
||||
}
|
||||
}
|
|
@ -23,6 +23,7 @@ import org.springframework.web.multipart.MultipartFile;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.sql.Driver;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -210,9 +211,15 @@ public class PluginLoadService {
|
|||
|
||||
public boolean hasPluginKey(String currentPluginId, String pluginKey) {
|
||||
for (String pluginId : pluginManager.getClassLoaderMap().keySet()) {
|
||||
MsPlugin msPlugin = getMsPluginInstance(pluginId);
|
||||
if (!StringUtils.equals(currentPluginId, pluginId) && StringUtils.equals(msPlugin.getKey(), pluginKey)) {
|
||||
return true;
|
||||
MsPlugin msPlugin;
|
||||
try {
|
||||
msPlugin = getMsPluginInstance(pluginId);
|
||||
if (!StringUtils.equals(currentPluginId, pluginId) && StringUtils.equals(msPlugin.getKey(), pluginKey)) {
|
||||
return true;
|
||||
}
|
||||
} catch (MSException e) {
|
||||
// jdbc 驱动没有实现 MsPlugin 接口
|
||||
LogUtils.info(String.format("插件%s未实现 MsPlugin 接口", pluginId));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -230,4 +237,8 @@ public class PluginLoadService {
|
|||
public Object getPluginScriptContent(String pluginId, String scriptId) {
|
||||
return getPluginScriptConfig(pluginId, scriptId).get("script");
|
||||
}
|
||||
|
||||
public Class getImplClass(String pluginId, Class<Driver> driverClass) {
|
||||
return pluginManager.getImplClass(pluginId, driverClass);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,9 +4,11 @@ package io.metersphere.system.service;
|
|||
import io.metersphere.plugin.sdk.api.MsPlugin;
|
||||
import io.metersphere.sdk.constants.KafkaPluginTopicType;
|
||||
import io.metersphere.sdk.constants.KafkaTopicConstants;
|
||||
import io.metersphere.sdk.constants.PluginScenarioType;
|
||||
import io.metersphere.sdk.controller.handler.result.CommonResultCode;
|
||||
import io.metersphere.sdk.dto.OptionDTO;
|
||||
import io.metersphere.sdk.exception.MSException;
|
||||
import io.metersphere.sdk.service.JdbcDriverPluginService;
|
||||
import io.metersphere.sdk.service.PluginLoadService;
|
||||
import io.metersphere.sdk.util.BeanUtils;
|
||||
import io.metersphere.system.domain.Plugin;
|
||||
|
@ -28,6 +30,7 @@ import org.springframework.web.multipart.MultipartFile;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.sql.Driver;
|
||||
import java.util.*;
|
||||
|
||||
import static io.metersphere.system.controller.result.SystemResultCode.PLUGIN_EXIST;
|
||||
|
@ -52,6 +55,8 @@ public class PluginService {
|
|||
@Resource
|
||||
private PluginLoadService pluginLoadService;
|
||||
@Resource
|
||||
JdbcDriverPluginService jdbcDriverPluginService;
|
||||
@Resource
|
||||
private KafkaTemplate<String, String> kafkaTemplate;
|
||||
|
||||
public List<PluginDTO> list() {
|
||||
|
@ -60,8 +65,10 @@ public class PluginService {
|
|||
Map<String, List<OptionDTO>> scripteMap = pluginScriptService.getScripteMap(pluginIds);
|
||||
Map<String, List<OptionDTO>> orgMap = pluginOrganizationService.getOrgMap(pluginIds);
|
||||
plugins.forEach(plugin -> {
|
||||
plugin.setPluginForms(scripteMap.get(plugin.getId()));
|
||||
plugin.setOrganizations(orgMap.get(plugin.getId()));
|
||||
List<OptionDTO> pluginForms = scripteMap.get(plugin.getId());
|
||||
List<OptionDTO> organizations = orgMap.get(plugin.getId());
|
||||
plugin.setPluginForms(pluginForms == null ? new ArrayList<>(0) : pluginForms);
|
||||
plugin.setOrganizations(organizations == null ? new ArrayList<>(0) : organizations);
|
||||
});
|
||||
return plugins;
|
||||
}
|
||||
|
@ -90,19 +97,32 @@ public class PluginService {
|
|||
pluginLoadService.loadPlugin(id, file);
|
||||
// 上传插件
|
||||
pluginLoadService.uploadPlugin(id, file);
|
||||
// 获取插件前端配置脚本
|
||||
List<String> frontendScript = pluginLoadService.getFrontendScripts(id);
|
||||
|
||||
MsPlugin msPlugin = pluginLoadService.getMsPluginInstance(id);
|
||||
plugin.setScenario(msPlugin.getType());
|
||||
plugin.setXpack(msPlugin.isXpack());
|
||||
plugin.setPluginId(msPlugin.getPluginId());
|
||||
if (jdbcDriverPluginService.isJdbcDriver(id)) {
|
||||
Class implClass = pluginLoadService.getImplClass(id, Driver.class);
|
||||
// mysql 已经内置了依赖,不允许上传
|
||||
if (implClass.getName().startsWith("com.mysql")) {
|
||||
throw new MSException(PLUGIN_TYPE_EXIST);
|
||||
}
|
||||
plugin.setScenario(PluginScenarioType.JDBC_DRIVER.name());
|
||||
plugin.setXpack(false);
|
||||
plugin.setPluginId(file.getOriginalFilename());
|
||||
} else {
|
||||
// 非数据库驱动插件,解析脚本和插件信息
|
||||
// 获取插件前端配置脚本
|
||||
List<String> frontendScript = pluginLoadService.getFrontendScripts(id);
|
||||
|
||||
// 校验插件类型是否重复
|
||||
checkPluginKeyExist(id, msPlugin.getKey());
|
||||
MsPlugin msPlugin = pluginLoadService.getMsPluginInstance(id);
|
||||
plugin.setScenario(msPlugin.getType());
|
||||
plugin.setXpack(msPlugin.isXpack());
|
||||
plugin.setPluginId(msPlugin.getPluginId());
|
||||
|
||||
// 保存插件脚本
|
||||
pluginScriptService.add(id, frontendScript);
|
||||
// 校验插件类型是否重复
|
||||
checkPluginKeyExist(id, msPlugin.getKey());
|
||||
|
||||
// 保存插件脚本
|
||||
pluginScriptService.add(id, frontendScript);
|
||||
}
|
||||
|
||||
// 保存插件和组织的关联关系
|
||||
if (!request.getGlobal()) {
|
||||
|
|
|
@ -5,6 +5,7 @@ import io.metersphere.sdk.constants.PermissionConstants;
|
|||
import io.metersphere.sdk.constants.PluginScenarioType;
|
||||
import io.metersphere.sdk.dto.OptionDTO;
|
||||
import io.metersphere.sdk.log.constants.OperationLogType;
|
||||
import io.metersphere.sdk.service.JdbcDriverPluginService;
|
||||
import io.metersphere.sdk.util.JSON;
|
||||
import io.metersphere.system.controller.param.PluginUpdateRequestDefinition;
|
||||
import io.metersphere.system.domain.*;
|
||||
|
@ -52,6 +53,8 @@ public class PluginControllerTests extends BaseTest {
|
|||
private PluginScriptMapper pluginScriptMapper;
|
||||
@Resource
|
||||
private OrganizationService organizationService;
|
||||
@Resource
|
||||
private JdbcDriverPluginService jdbcDriverPluginService;
|
||||
private static Plugin addPlugin;
|
||||
private static Plugin anotherAddPlugin;
|
||||
|
||||
|
@ -77,10 +80,6 @@ public class PluginControllerTests extends BaseTest {
|
|||
this.getClass().getClassLoader().getResource("file/metersphere-mqtt-plugin-3.x.jar")
|
||||
.getPath()
|
||||
);
|
||||
File anotherJarFile = new File(
|
||||
this.getClass().getClassLoader().getResource("file/metersphere-jira-plugin-3.x.jar")
|
||||
.getPath()
|
||||
);
|
||||
|
||||
request.setName("test");
|
||||
request.setDescription("test desc");
|
||||
|
@ -112,6 +111,10 @@ public class PluginControllerTests extends BaseTest {
|
|||
request.setEnable(true);
|
||||
request.setName("test2");
|
||||
request.setOrganizationIds(Arrays.asList(org.getId()));
|
||||
File anotherJarFile = new File(
|
||||
this.getClass().getClassLoader().getResource("file/metersphere-jira-plugin-3.x.jar")
|
||||
.getPath()
|
||||
);
|
||||
MvcResult antoherMvcResult = this.requestMultipartWithOkAndReturn(DEFAULT_ADD,
|
||||
getDefaultMultiPartParam(request, anotherJarFile));
|
||||
Plugin antoherPlugin = pluginMapper.selectByPrimaryKey(getResultData(antoherMvcResult, Plugin.class).getId());
|
||||
|
@ -121,6 +124,17 @@ public class PluginControllerTests extends BaseTest {
|
|||
anotherAddPlugin = antoherPlugin;
|
||||
addPlugin = plugin;
|
||||
|
||||
// 校验数据库驱动上传成功
|
||||
request.setName("my-driver");
|
||||
request.setOrganizationIds(Arrays.asList(org.getId()));
|
||||
File myDriver = new File(
|
||||
this.getClass().getClassLoader().getResource("file/my-driver-1.0.jar")
|
||||
.getPath()
|
||||
);
|
||||
this.requestMultipartWithOkAndReturn(DEFAULT_ADD,
|
||||
getDefaultMultiPartParam(request, myDriver));
|
||||
Assertions.assertEquals(jdbcDriverPluginService.getJdbcDriverClass(), Arrays.asList("io.jianxing.MyDriver", "com.mysql.jdbc.Driver"));
|
||||
|
||||
// @@重名校验异常
|
||||
// 校验插件名称重名
|
||||
assertErrorCode(this.requestMultipart(DEFAULT_ADD,
|
||||
|
@ -139,6 +153,14 @@ public class PluginControllerTests extends BaseTest {
|
|||
assertErrorCode(this.requestMultipart(DEFAULT_ADD,
|
||||
getDefaultMultiPartParam(request, typeRepeatFile)), PLUGIN_TYPE_EXIST);
|
||||
|
||||
// @@校验禁止上传mysql驱动
|
||||
File mysqlDriver = new File(
|
||||
this.getClass().getClassLoader().getResource("file/test-mysql-driver-1.0.jar")
|
||||
.getPath()
|
||||
);
|
||||
assertErrorCode(this.requestMultipart(DEFAULT_ADD,
|
||||
getDefaultMultiPartParam(request, mysqlDriver)), PLUGIN_TYPE_EXIST);
|
||||
|
||||
// @@校验插件脚本解析失败
|
||||
File scriptParseFile = new File(
|
||||
this.getClass().getClassLoader().getResource("file/metersphere-plugin-script-parse-error.jar")
|
||||
|
@ -153,7 +175,7 @@ public class PluginControllerTests extends BaseTest {
|
|||
.getPath()
|
||||
);
|
||||
assertErrorCode(this.requestMultipart(DEFAULT_ADD,
|
||||
getDefaultMultiPartParam(request, scriptIdRepeatFile)), PLUGIN_SCRIPT_EXIST);
|
||||
getDefaultMultiPartParam(request, scriptIdRepeatFile)), PLUGIN_SCRIPT_EXIST); // @@校验插件脚本ID重复
|
||||
|
||||
// @@校验日志
|
||||
checkLog(this.addPlugin.getId(), OperationLogType.ADD);
|
||||
|
@ -237,14 +259,9 @@ public class PluginControllerTests extends BaseTest {
|
|||
MvcResult mvcResult = this.requestGetWithOkAndReturn(DEFAULT_LIST);
|
||||
// 校验数据是否正确
|
||||
List<PluginDTO> pluginList = getResultDataArray(mvcResult, PluginDTO.class);
|
||||
Assertions.assertEquals(2, pluginList.size());
|
||||
Assertions.assertEquals(3, pluginList.size());
|
||||
for (PluginDTO pluginDTO : pluginList) {
|
||||
Plugin comparePlugin = null;
|
||||
if (StringUtils.equals(pluginDTO.getId(), addPlugin.getId())) {
|
||||
comparePlugin = pluginMapper.selectByPrimaryKey(addPlugin.getId());
|
||||
} else if (StringUtils.equals(pluginDTO.getId(), anotherAddPlugin.getId())) {
|
||||
comparePlugin = pluginMapper.selectByPrimaryKey(anotherAddPlugin.getId());
|
||||
}
|
||||
Plugin comparePlugin = pluginMapper.selectByPrimaryKey(pluginDTO.getId());
|
||||
Plugin plugin = JSON.parseObject(JSON.toJSONString(pluginDTO), Plugin.class);
|
||||
List<String> scriptIds = pluginDTO.getPluginForms().stream().map(OptionDTO::getId).toList();
|
||||
Assertions.assertEquals(plugin, comparePlugin);
|
||||
|
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue