mirror of
https://gitee.com/devlive-community/datacap.git
synced 2024-11-30 11:07:41 +08:00
[Refactor] [Core] Refactoring plug-in configuration extraction mode (test connection part)
This commit is contained in:
parent
cc0a548c6d
commit
279fb2980a
12
server/src/main/etc/conf/plugins/README.md
Normal file
12
server/src/main/etc/conf/plugins/README.md
Normal file
@ -0,0 +1,12 @@
|
||||
## Plugin Configure
|
||||
|
||||
> Store some necessary configuration for each plugin, which applies to all related operations of the plugin
|
||||
|
||||
#### Support Types
|
||||
|
||||
- Number
|
||||
- String
|
||||
- List
|
||||
- Boolean
|
||||
- Array
|
||||
- Map
|
21
server/src/main/etc/conf/plugins/default.json
Normal file
21
server/src/main/etc/conf/plugins/default.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "Default",
|
||||
"supportTime": "2023-01-16",
|
||||
"configures": [
|
||||
{
|
||||
"field": "host",
|
||||
"type": "String",
|
||||
"required": true,
|
||||
"value": "",
|
||||
"message": "host is a required field, please be sure to enter"
|
||||
},
|
||||
{
|
||||
"field": "port",
|
||||
"type": "Number",
|
||||
"required": true,
|
||||
"min": 1,
|
||||
"max": 65535,
|
||||
"message": "host is a required field, please be sure to enter"
|
||||
}
|
||||
]
|
||||
}
|
62
server/src/main/etc/conf/plugins/jdbc/mysql.json
Normal file
62
server/src/main/etc/conf/plugins/jdbc/mysql.json
Normal file
@ -0,0 +1,62 @@
|
||||
{
|
||||
"name": "MySQL",
|
||||
"supportTime": "2022-09-19",
|
||||
"configures": [
|
||||
{
|
||||
"field": "name",
|
||||
"type": "String",
|
||||
"required": true,
|
||||
"message": "name is a required field, please be sure to enter"
|
||||
},
|
||||
{
|
||||
"field": "host",
|
||||
"type": "String",
|
||||
"required": true,
|
||||
"value": "127.0.0.1",
|
||||
"message": "host is a required field, please be sure to enter"
|
||||
},
|
||||
{
|
||||
"field": "port",
|
||||
"type": "Number",
|
||||
"required": true,
|
||||
"min": 1,
|
||||
"max": 65535,
|
||||
"value": 3306,
|
||||
"message": "port is a required field, please be sure to enter"
|
||||
},
|
||||
{
|
||||
"field": "username",
|
||||
"type": "String",
|
||||
"group": "authorization"
|
||||
},
|
||||
{
|
||||
"field": "password",
|
||||
"type": "String",
|
||||
"group": "authorization"
|
||||
},
|
||||
{
|
||||
"field": "ssl",
|
||||
"type": "Boolean",
|
||||
"group": "authorization"
|
||||
},
|
||||
{
|
||||
"field": "database",
|
||||
"type": "String",
|
||||
"required": true,
|
||||
"value": "default",
|
||||
"message": "database is a required field, please be sure to enter",
|
||||
"group": "advanced"
|
||||
},
|
||||
{
|
||||
"field": "configures",
|
||||
"type": "Array",
|
||||
"value": [
|
||||
{
|
||||
"field": "useOldAliasMetadataBehavior",
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"group": "custom"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package io.edurt.datacap.server.body;
|
||||
|
||||
import io.edurt.datacap.server.plugin.configure.IConfigure;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
@Data
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class SourceBody
|
||||
{
|
||||
private Long id;
|
||||
private String type;
|
||||
private String name;
|
||||
private IConfigure configure;
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
package io.edurt.datacap.server.common;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.edurt.datacap.server.plugin.configure.IConfigureField;
|
||||
import io.edurt.datacap.server.plugin.configure.IConfigureFieldName;
|
||||
import io.edurt.datacap.server.plugin.configure.IConfigureFieldType;
|
||||
import io.edurt.datacap.spi.FormatType;
|
||||
import io.edurt.datacap.spi.model.Configure;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class IConfigureCommon
|
||||
{
|
||||
private IConfigureCommon()
|
||||
{}
|
||||
|
||||
public static String preparedMessage(List<IConfigureField> configures)
|
||||
{
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
buffer.append(ServiceState.PLUGIN_CONFIGURE_REQUIRED.getValue());
|
||||
configures.forEach(v -> {
|
||||
buffer.append("<br/>");
|
||||
buffer.append("Field: " + v.getField());
|
||||
});
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
public static Configure preparedConfigure(List<IConfigureField> configures)
|
||||
{
|
||||
Configure configure = new Configure();
|
||||
configures.forEach(v -> {
|
||||
switch (v.getField()) {
|
||||
case host:
|
||||
configure.setHost(IConfigureCommon.getStringValue(configures, IConfigureFieldName.host));
|
||||
break;
|
||||
case port:
|
||||
configure.setPort(IConfigureCommon.getIntegerValue(configures, IConfigureFieldName.port));
|
||||
break;
|
||||
case username:
|
||||
configure.setUsername(Optional.ofNullable(IConfigureCommon.getStringValue(configures, IConfigureFieldName.username)));
|
||||
break;
|
||||
case password:
|
||||
configure.setPassword(Optional.ofNullable(IConfigureCommon.getStringValue(configures, IConfigureFieldName.password)));
|
||||
break;
|
||||
case database:
|
||||
String database = IConfigureCommon.getStringValue(configures, IConfigureFieldName.database);
|
||||
Optional<String> _database = StringUtils.isNotEmpty(database) ? Optional.ofNullable(database) : Optional.empty();
|
||||
configure.setDatabase(_database);
|
||||
break;
|
||||
case ssl:
|
||||
configure.setSsl(Optional.ofNullable(IConfigureCommon.getBooleanValue(configures, IConfigureFieldName.ssl)));
|
||||
break;
|
||||
case configures:
|
||||
configure.setEnv(Optional.ofNullable(IConfigureCommon.getMapValue(configures, IConfigureFieldName.configures)));
|
||||
break;
|
||||
}
|
||||
});
|
||||
configure.setFormat(FormatType.JSON);
|
||||
return configure;
|
||||
}
|
||||
|
||||
public static IConfigureField getConfigure(List<IConfigureField> configures, IConfigureFieldName key)
|
||||
{
|
||||
Optional<IConfigureField> configureFieldOptional = configures.stream().filter(v -> v.getField().equals(key)).findFirst();
|
||||
return configureFieldOptional.get();
|
||||
}
|
||||
|
||||
public static String getStringValue(List<IConfigureField> configures, IConfigureFieldName key)
|
||||
{
|
||||
return String.valueOf(getConfigure(configures, key).getValue());
|
||||
}
|
||||
|
||||
public static Integer getIntegerValue(List<IConfigureField> configures, IConfigureFieldName key)
|
||||
{
|
||||
return Integer.valueOf(getStringValue(configures, key));
|
||||
}
|
||||
|
||||
public static Long getLongValue(List<IConfigureField> configures, IConfigureFieldName key)
|
||||
{
|
||||
return Long.valueOf(getStringValue(configures, key));
|
||||
}
|
||||
|
||||
public static Boolean getBooleanValue(List<IConfigureField> configures, IConfigureFieldName key)
|
||||
{
|
||||
return Boolean.valueOf(getStringValue(configures, key));
|
||||
}
|
||||
|
||||
public static Map<String, Object> getMapValue(List<IConfigureField> configures, IConfigureFieldName key)
|
||||
{
|
||||
Map<String, Object> values = new ConcurrentHashMap<>();
|
||||
IConfigureField configureField = getConfigure(configures, key);
|
||||
if (configureField.getType().equals(IConfigureFieldType.Array)) {
|
||||
List<LinkedHashMap<String, Object>> list = (List<LinkedHashMap<String, Object>>) configureField.getValue();
|
||||
list.forEach(map -> values.put(String.valueOf(map.get(IConfigureFieldName.field.name())), map.get(IConfigureFieldName.value.name())));
|
||||
}
|
||||
else {
|
||||
Preconditions.checkArgument(false, "Not Support type");
|
||||
}
|
||||
return values;
|
||||
}
|
||||
}
|
@ -1,32 +1,58 @@
|
||||
package io.edurt.datacap.server.common;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import io.edurt.datacap.server.plugin.configure.IConfigure;
|
||||
import io.edurt.datacap.spi.Plugin;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
@Slf4j
|
||||
public class PluginCommon
|
||||
{
|
||||
private PluginCommon() {}
|
||||
|
||||
public static Optional<Plugin> getPluginByName(Injector injector, String pluginName)
|
||||
{
|
||||
Optional<Plugin> pluginOptional = injector.getInstance(Key.get(new TypeLiteral<Set<Plugin>>() {}))
|
||||
.stream()
|
||||
.filter(plugin -> plugin.name().equalsIgnoreCase(pluginName))
|
||||
.findFirst();
|
||||
Optional<Plugin> pluginOptional = injector.getInstance(Key.get(new TypeLiteral<Set<Plugin>>() {})).stream().filter(plugin -> plugin.name().equalsIgnoreCase(pluginName)).findFirst();
|
||||
return pluginOptional;
|
||||
}
|
||||
|
||||
public static Optional<Plugin> getPluginByNameAndType(Injector injector, String pluginName, String pluginType)
|
||||
{
|
||||
Optional<Plugin> pluginOptional = injector.getInstance(Key.get(new TypeLiteral<Set<Plugin>>() {}))
|
||||
.stream()
|
||||
.filter(plugin -> plugin.name().equalsIgnoreCase(pluginName) && plugin.type().name().equalsIgnoreCase(pluginType))
|
||||
.findFirst();
|
||||
Optional<Plugin> pluginOptional = injector.getInstance(Key.get(new TypeLiteral<Set<Plugin>>() {})).stream().filter(plugin -> plugin.name().equalsIgnoreCase(pluginName) && plugin.type().name().equalsIgnoreCase(pluginType)).findFirst();
|
||||
return pluginOptional;
|
||||
}
|
||||
|
||||
public static IConfigure loadConfigure(String type, String plugin, String resource, Environment environment)
|
||||
{
|
||||
String root = environment.getProperty("spring.config.location");
|
||||
if (!resource.endsWith(".json")) {
|
||||
resource = resource + ".json";
|
||||
}
|
||||
String path = root + String.format("plugins/%s/%s", type.toLowerCase(), resource.toLowerCase());
|
||||
log.info("Load plugin {} type {} resource {} configure file path {}", plugin, type, resource, path);
|
||||
String json = ResourceCommon.loadResourceToString(path, true);
|
||||
if (StringUtils.isEmpty(json)) {
|
||||
log.warn("Plugin {} type {} configuration file {} not found, load default configuration file", plugin, type, resource);
|
||||
json = ResourceCommon.loadResourceToString(root + "plugins/default.json", true);
|
||||
}
|
||||
Preconditions.checkArgument(StringUtils.isNotEmpty(json), "Invalid configuration file, it may be a bug, please submit issues to solve it.");
|
||||
IConfigure configure = null;
|
||||
try {
|
||||
configure = JSON.objectmapper.readValue(json, IConfigure.class);
|
||||
}
|
||||
catch (JsonProcessingException e) {
|
||||
log.error("Format configuration file, it may be a bug, please submit issues to solve it. plugin {} type {} resource {} configure file path {} message ", plugin, type, resource, path, e);
|
||||
Preconditions.checkArgument(StringUtils.isNotEmpty(json), "Format configuration file, it may be a bug, please submit issues to solve it.");
|
||||
}
|
||||
return configure;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,40 @@
|
||||
package io.edurt.datacap.server.common;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
|
||||
@Slf4j
|
||||
public class ResourceCommon
|
||||
{
|
||||
private ResourceCommon()
|
||||
{}
|
||||
|
||||
public static String loadResourceToString(String resource, boolean absolute)
|
||||
{
|
||||
try {
|
||||
if (absolute) {
|
||||
return IOUtils.toString(Files.newInputStream(new File(resource).toPath()));
|
||||
}
|
||||
else {
|
||||
ClassLoader loader = Thread.currentThread().getContextClassLoader();
|
||||
InputStream inputStream = loader.getResourceAsStream(resource);
|
||||
return IOUtils.toString(inputStream);
|
||||
}
|
||||
}
|
||||
catch (IOException | NullPointerException e) {
|
||||
log.error("Loading file error {}", resource);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean checkExists(String resource)
|
||||
{
|
||||
return ObjectUtils.isEmpty(loadResourceToString(resource, false)) ? false : true;
|
||||
}
|
||||
}
|
@ -7,6 +7,8 @@ public enum ServiceState
|
||||
PLUGIN_NOT_FOUND(2001, "Plugin dose not exists"),
|
||||
PLUGIN_EXECUTE_FAILED(2002, "Plugin execute failed"),
|
||||
PLUGIN_ONLY_ONE_TEMPLATE(2003, "Plug-ins support only templates with the same name"),
|
||||
PLUGIN_CONFIGURE_MISMATCH(2004, "The plug-in passed parameters do not match the system configuration"),
|
||||
PLUGIN_CONFIGURE_REQUIRED(2005, "Ensure that all required fields exist"),
|
||||
REQUEST_VALID_ARGUMENT(3001, "The related parameters cannot be verified"),
|
||||
REQUEST_VALID_ARGUMENT_FORMAT(3002, "Unable to format related parameters"),
|
||||
REQUEST_VALID_ARGUMENT_LAYOUT(3003, "Related parameters cannot be resolved"),
|
||||
|
@ -0,0 +1,30 @@
|
||||
package io.edurt.datacap.server.controller.user.v2;
|
||||
|
||||
import io.edurt.datacap.server.body.SourceBody;
|
||||
import io.edurt.datacap.server.common.Response;
|
||||
import io.edurt.datacap.server.service.SourceService;
|
||||
import io.edurt.datacap.server.validation.ValidationGroup;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController()
|
||||
@RequestMapping(value = "/api/v2/source")
|
||||
public class SourceV2Controller
|
||||
{
|
||||
private final SourceService sourceService;
|
||||
|
||||
public SourceV2Controller(SourceService sourceService)
|
||||
{
|
||||
this.sourceService = sourceService;
|
||||
}
|
||||
|
||||
@PostMapping(value = "test", produces = {MediaType.APPLICATION_JSON_VALUE})
|
||||
public Response<Object> testConnectionV2(@RequestBody @Validated(ValidationGroup.Crud.Create.class) SourceBody configure)
|
||||
{
|
||||
return this.sourceService.testConnectionV2(configure);
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package io.edurt.datacap.server.entity;
|
||||
|
||||
import io.edurt.datacap.server.plugin.configure.IConfigure;
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
|
||||
@ -10,4 +11,5 @@ public class PluginEntity
|
||||
private String name;
|
||||
private String description;
|
||||
private String type;
|
||||
private IConfigure configure;
|
||||
}
|
||||
|
@ -0,0 +1,23 @@
|
||||
package io.edurt.datacap.server.plugin.configure;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@SuppressFBWarnings(value = {"EI_EXPOSE_REP2", "EI_EXPOSE_REP2", "EI_EXPOSE_REP", "NP_NULL_PARAM_DEREF_NONVIRTUAL"},
|
||||
justification = "I prefer to suppress these FindBugs warnings")
|
||||
public class IConfigure
|
||||
{
|
||||
private String name;
|
||||
private Date supportTime;
|
||||
private List<IConfigureField> configures;
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package io.edurt.datacap.server.plugin.configure;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
@Data
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class IConfigureField
|
||||
{
|
||||
private IConfigureFieldName field; // The field name of the tag configuration
|
||||
private IConfigureFieldType type;
|
||||
private boolean required;
|
||||
private Long min;
|
||||
private Long max;
|
||||
private String message;
|
||||
private Object value;
|
||||
private IConfigureFieldGroup group = IConfigureFieldGroup.configure;
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package io.edurt.datacap.server.plugin.configure;
|
||||
|
||||
public enum IConfigureFieldGroup
|
||||
{
|
||||
configure,
|
||||
authorization,
|
||||
advanced,
|
||||
custom
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package io.edurt.datacap.server.plugin.configure;
|
||||
|
||||
public enum IConfigureFieldName
|
||||
{
|
||||
field,
|
||||
value,
|
||||
// ===== Support column name
|
||||
name,
|
||||
host,
|
||||
port,
|
||||
username,
|
||||
password,
|
||||
ssl,
|
||||
catalog,
|
||||
database,
|
||||
configures
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package io.edurt.datacap.server.plugin.configure;
|
||||
|
||||
public enum IConfigureFieldType
|
||||
{
|
||||
Number,
|
||||
String,
|
||||
List,
|
||||
Boolean,
|
||||
Array,
|
||||
Map
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package io.edurt.datacap.server.service;
|
||||
|
||||
import io.edurt.datacap.server.body.SharedSourceBody;
|
||||
import io.edurt.datacap.server.body.SourceBody;
|
||||
import io.edurt.datacap.server.common.Response;
|
||||
import io.edurt.datacap.server.entity.PageEntity;
|
||||
import io.edurt.datacap.server.entity.PluginEntity;
|
||||
@ -17,6 +18,7 @@ public interface SourceService
|
||||
|
||||
Response<Long> delete(Long id);
|
||||
|
||||
@Deprecated
|
||||
Response<Object> testConnection(SourceEntity configure);
|
||||
|
||||
Response<SourceEntity> getById(Long id);
|
||||
@ -26,4 +28,6 @@ public interface SourceService
|
||||
Response<Long> count();
|
||||
|
||||
Response<Object> shared(SharedSourceBody configure);
|
||||
|
||||
Response<Object> testConnectionV2(SourceBody configure);
|
||||
}
|
||||
|
@ -5,6 +5,8 @@ import com.google.inject.Key;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import io.edurt.datacap.server.adapter.PageRequestAdapter;
|
||||
import io.edurt.datacap.server.body.SharedSourceBody;
|
||||
import io.edurt.datacap.server.body.SourceBody;
|
||||
import io.edurt.datacap.server.common.IConfigureCommon;
|
||||
import io.edurt.datacap.server.common.JSON;
|
||||
import io.edurt.datacap.server.common.PluginCommon;
|
||||
import io.edurt.datacap.server.common.Response;
|
||||
@ -13,6 +15,8 @@ import io.edurt.datacap.server.entity.PageEntity;
|
||||
import io.edurt.datacap.server.entity.PluginEntity;
|
||||
import io.edurt.datacap.server.entity.SourceEntity;
|
||||
import io.edurt.datacap.server.entity.UserEntity;
|
||||
import io.edurt.datacap.server.plugin.configure.IConfigure;
|
||||
import io.edurt.datacap.server.plugin.configure.IConfigureField;
|
||||
import io.edurt.datacap.server.repository.SourceRepository;
|
||||
import io.edurt.datacap.server.repository.UserRepository;
|
||||
import io.edurt.datacap.server.security.UserDetailsService;
|
||||
@ -22,6 +26,7 @@ import io.edurt.datacap.spi.Plugin;
|
||||
import io.edurt.datacap.spi.model.Configure;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@ -31,6 +36,7 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class SourceServiceImpl
|
||||
@ -39,12 +45,14 @@ public class SourceServiceImpl
|
||||
private final SourceRepository sourceRepository;
|
||||
private final UserRepository userRepository;
|
||||
private final Injector injector;
|
||||
private final Environment environment;
|
||||
|
||||
public SourceServiceImpl(SourceRepository sourceRepository, UserRepository userRepository, Injector injector)
|
||||
public SourceServiceImpl(SourceRepository sourceRepository, UserRepository userRepository, Injector injector, Environment environment)
|
||||
{
|
||||
this.sourceRepository = sourceRepository;
|
||||
this.userRepository = userRepository;
|
||||
this.injector = injector;
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -70,6 +78,7 @@ public class SourceServiceImpl
|
||||
return Response.success(id);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public Response<Object> testConnection(SourceEntity configure)
|
||||
{
|
||||
@ -115,6 +124,7 @@ public class SourceServiceImpl
|
||||
entity.setName(plugin.name());
|
||||
entity.setDescription(plugin.description());
|
||||
entity.setType(plugin.type().name());
|
||||
entity.setConfigure(PluginCommon.loadConfigure(plugin.type().name(), plugin.name(), plugin.name(), environment));
|
||||
List<PluginEntity> plugins = pluginMap.get(plugin.type().name());
|
||||
if (ObjectUtils.isEmpty(plugins)) {
|
||||
plugins = new ArrayList<>();
|
||||
@ -149,4 +159,37 @@ public class SourceServiceImpl
|
||||
source.setPublish(configure.getPublish());
|
||||
return Response.success(this.sourceRepository.save(source));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response<Object> testConnectionV2(SourceBody configure)
|
||||
{
|
||||
Optional<Plugin> pluginOptional = PluginCommon.getPluginByNameAndType(this.injector, configure.getName(), configure.getType());
|
||||
if (!pluginOptional.isPresent()) {
|
||||
return Response.failure(ServiceState.PLUGIN_NOT_FOUND);
|
||||
}
|
||||
|
||||
// Check configure
|
||||
IConfigure iConfigure = PluginCommon.loadConfigure(configure.getType(), configure.getName(), configure.getName(), environment);
|
||||
if (ObjectUtils.isEmpty(iConfigure) || iConfigure.getConfigures().size() != configure.getConfigure().getConfigures().size()) {
|
||||
return Response.failure(ServiceState.PLUGIN_CONFIGURE_MISMATCH);
|
||||
}
|
||||
|
||||
// Filter required
|
||||
List<IConfigureField> requiredMismatchConfigures = configure.getConfigure().getConfigures().stream().filter(v -> v.isRequired())
|
||||
.filter(v -> ObjectUtils.isEmpty(v.getValue()))
|
||||
.collect(Collectors.toList());
|
||||
if (requiredMismatchConfigures.size() > 0) {
|
||||
return Response.failure(ServiceState.PLUGIN_CONFIGURE_REQUIRED, IConfigureCommon.preparedMessage(requiredMismatchConfigures));
|
||||
}
|
||||
|
||||
Plugin plugin = pluginOptional.get();
|
||||
Configure _configure = IConfigureCommon.preparedConfigure(configure.getConfigure().getConfigures());
|
||||
plugin.connect(_configure);
|
||||
io.edurt.datacap.spi.model.Response response = plugin.execute(plugin.validator());
|
||||
plugin.destroy();
|
||||
if (response.getIsSuccessful()) {
|
||||
return Response.success(response);
|
||||
}
|
||||
return Response.failure(ServiceState.PLUGIN_EXECUTE_FAILED, response.getMessage());
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,20 @@
|
||||
package io.edurt.datacap.server.common;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import io.edurt.datacap.server.plugin.configure.IConfigure;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
@SuppressFBWarnings(value = {"NP_NULL_PARAM_DEREF_NONVIRTUAL"},
|
||||
justification = "I prefer to suppress these FindBugs warnings")
|
||||
public class PluginCommonTest
|
||||
{
|
||||
private String resource = "default.json";
|
||||
|
||||
@Test
|
||||
public void loadConfigure()
|
||||
{
|
||||
IConfigure configure = PluginCommon.loadConfigure("JDBC", "MySQL", resource, null);
|
||||
Assert.assertNotNull(configure);
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package io.edurt.datacap.server.common;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ResourceCommonTest
|
||||
{
|
||||
private String resource = "default.json";
|
||||
private String resource2 = "empty.json";
|
||||
|
||||
@Test
|
||||
public void loadResourceToString()
|
||||
{
|
||||
Assert.assertNull(ResourceCommon.loadResourceToString(resource2, false));
|
||||
Assert.assertNotNull(ResourceCommon.loadResourceToString(resource, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkExists()
|
||||
{
|
||||
System.out.println(ResourceCommon.checkExists(resource));
|
||||
Assert.assertTrue(ResourceCommon.checkExists(resource));
|
||||
Assert.assertFalse(ResourceCommon.checkExists(resource2));
|
||||
}
|
||||
}
|
2
server/src/test/resources/default.json
Normal file
2
server/src/test/resources/default.json
Normal file
@ -0,0 +1,2 @@
|
||||
{
|
||||
}
|
21
server/src/test/resources/plugins/default.json
Normal file
21
server/src/test/resources/plugins/default.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "Default",
|
||||
"supportTime": "2023-01-16",
|
||||
"configures": [
|
||||
{
|
||||
"field": "host",
|
||||
"type": "String",
|
||||
"required": true,
|
||||
"value": "",
|
||||
"message": "host is a required field, please be sure to enter"
|
||||
},
|
||||
{
|
||||
"field": "port",
|
||||
"type": "Number",
|
||||
"required": true,
|
||||
"min": 0,
|
||||
"max": 65535,
|
||||
"message": "host is a required field, please be sure to enter"
|
||||
}
|
||||
]
|
||||
}
|
15
web/console-fe/package-lock.json
generated
15
web/console-fe/package-lock.json
generated
@ -22,6 +22,7 @@
|
||||
"monaco-editor-vue3": "^0.1.6",
|
||||
"monaco-editor-webpack-plugin": "^7.0.1",
|
||||
"nprogress": "^0.2.0",
|
||||
"squel": "^5.13.0",
|
||||
"view-ui-plus": "^1.3.1",
|
||||
"vue": "^3.2.13",
|
||||
"vue-clipboard3": "^2.0.0",
|
||||
@ -13042,6 +13043,15 @@
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="
|
||||
},
|
||||
"node_modules/squel": {
|
||||
"version": "5.13.0",
|
||||
"resolved": "https://registry.npmjs.org/squel/-/squel-5.13.0.tgz",
|
||||
"integrity": "sha512-Fzd8zqbuqNwzodO3yO6MkX8qiDoVBuwqAaa3eKNz4idhBf24IQHbatBhLUiHAGGl962eGvPVRxzRuFWZlSf49w==",
|
||||
"deprecated": "No longer maintained",
|
||||
"engines": {
|
||||
"node": ">= 0.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ssri": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz",
|
||||
@ -24659,6 +24669,11 @@
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="
|
||||
},
|
||||
"squel": {
|
||||
"version": "5.13.0",
|
||||
"resolved": "https://registry.npmjs.org/squel/-/squel-5.13.0.tgz",
|
||||
"integrity": "sha512-Fzd8zqbuqNwzodO3yO6MkX8qiDoVBuwqAaa3eKNz4idhBf24IQHbatBhLUiHAGGl962eGvPVRxzRuFWZlSf49w=="
|
||||
},
|
||||
"ssri": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz",
|
||||
|
@ -76,5 +76,6 @@ export default {
|
||||
client: 'Client',
|
||||
ip: 'IP',
|
||||
ua: 'User Agent',
|
||||
loginTime: 'Login Time'
|
||||
loginTime: 'Login Time',
|
||||
ssl: 'SSL'
|
||||
}
|
||||
|
@ -76,5 +76,6 @@ export default {
|
||||
client: '客户端',
|
||||
ip: 'IP',
|
||||
ua: '用户代理',
|
||||
loginTime: '登录时间'
|
||||
loginTime: '登录时间',
|
||||
ssl: 'SSL'
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import {SharedSource} from "@/model/SharedSource";
|
||||
import {isEmpty} from "lodash";
|
||||
|
||||
const baseUrl = "/api/v1/source";
|
||||
const baseUrlV2 = "/api/v2/source";
|
||||
|
||||
export class SourceService
|
||||
{
|
||||
@ -29,10 +30,9 @@ export class SourceService
|
||||
return new HttpCommon().delete(baseUrl + '/' + id);
|
||||
}
|
||||
|
||||
testConnection(configure: SourceModel): Promise<ResponseModel>
|
||||
testConnection(configure): Promise<ResponseModel>
|
||||
{
|
||||
configure.protocol = isEmpty(configure.protocol) ? 'HTTP' : configure.protocol;
|
||||
return new HttpCommon().post(baseUrl + '/test', configure);
|
||||
return new HttpCommon().post(baseUrlV2 + '/test', configure);
|
||||
}
|
||||
|
||||
getById(id: number): Promise<ResponseModel>
|
||||
|
@ -31,108 +31,72 @@
|
||||
<Col :span="3"/>
|
||||
</Row>
|
||||
<Form :model="formState" :label-width="80">
|
||||
<Tabs v-model="activeKey">
|
||||
<TabPane :label="$t('common.source')" name="type" icon="md-apps">
|
||||
<RadioGroup v-if="plugins" v-model="formState.type" type="button">
|
||||
<div v-for="key in Object.keys(plugins)" v-bind:key="key">
|
||||
<Divider orientation="left">{{ key }} ({{ plugins[key].length }})</Divider>
|
||||
<Space wrap :size="[8, 16]">
|
||||
<Tooltip v-for="plugin in plugins[key]" :content="plugin.description" transfer v-bind:key="plugin.name">
|
||||
<Radio :label="plugin.name + ' ' + plugin.type">
|
||||
<Avatar :src="'/static/images/plugin/' + plugin.name + '.png'" size="small" />
|
||||
<span style="margin-left: 2px;">{{ plugin.name }}</span>
|
||||
</Radio>
|
||||
</Tooltip>
|
||||
</Space>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</TabPane>
|
||||
<TabPane :disabled="!formState.type" name="configure" :label="$t('common.configure')" icon="md-cog">
|
||||
<div style="margin-top: 10px;">
|
||||
<Tabs v-model="activeKey" :animated="false" @update:modelValue="handlerFilterConfigure($event)">
|
||||
<TabPane :label="$t('common.' + type)" v-for="type in pluginTabs" :name="type" :disabled="!formState.type" icon="md-apps">
|
||||
<div v-if="type === 'source'">
|
||||
<RadioGroup v-if="plugins" v-model="formState.type" type="button" @on-change="handlerChangePlugin($event)">
|
||||
<div v-for="key in Object.keys(plugins)" v-bind:key="key">
|
||||
<Divider orientation="left">{{ key }} ({{ plugins[key].length }})</Divider>
|
||||
<Space wrap :size="[8, 16]">
|
||||
<Tooltip v-for="plugin in plugins[key]" :content="plugin.description" transfer v-bind:key="plugin.name">
|
||||
<Radio :label="plugin.name + ' ' + plugin.type">
|
||||
<Avatar :src="'/static/images/plugin/' + plugin.name + '.png'" size="small"/>
|
||||
<span style="margin-left: 2px;">{{ plugin.name }}</span>
|
||||
</Radio>
|
||||
</Tooltip>
|
||||
</Space>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</div>
|
||||
<div v-else style="margin-top: 10px;">
|
||||
<Row>
|
||||
<Col :span="5"/>
|
||||
<Col :span="14">
|
||||
<FormItem :label="$t('common.name')" prop="name">
|
||||
<Input type="text" v-model="formState.name"/>
|
||||
</FormItem>
|
||||
<FormItem :label="$t('common.host')" prop="host">
|
||||
<Input type="text" v-model="formState.host"/>
|
||||
</FormItem>
|
||||
<FormItem :label="$t('common.port')" prop="port">
|
||||
<InputNumber :max="65535" :min="1" v-model="formState.port"/>
|
||||
<FormItem v-for="configure in pluginTabConfigure" :required="configure.required" :prop="configure.field">
|
||||
<template #label>
|
||||
<span v-if="configure.field !== 'configures'">{{ $t('common.' + configure.field) }}</span>
|
||||
</template>
|
||||
<Input v-if="configure.type === 'String'" type="text" v-model="configure.value"/>
|
||||
<InputNumber v-else-if="configure.type === 'Number'" :max="configure.max" :min="configure.min" v-model="configure.value"/>
|
||||
<Switch v-else-if="configure.type === 'Boolean'" v-model="configure.value"/>
|
||||
<div v-else>
|
||||
<div style="margin-top: 10px;">
|
||||
<FormItem style="margin-bottom: 5px;">
|
||||
<Button size="small" type="primary" shape="circle" icon="md-add" @click="handlerPlusConfigure(configure.value)"/>
|
||||
</FormItem>
|
||||
<FormItem v-for="(element, index) in configure.value" :key="index" style="margin-bottom: 5px;">
|
||||
<Row :gutter="12">
|
||||
<Col :span="10">
|
||||
<FormItem>
|
||||
<Input v-model="element.field">
|
||||
<template #prepend>
|
||||
<span>{{ $t('common.field') }}</span>
|
||||
</template>
|
||||
</Input>
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col :span="10">
|
||||
<FormItem>
|
||||
<Input v-model="element.value">
|
||||
<template #prepend>
|
||||
<span>{{ $t('common.value') }}</span>
|
||||
</template>
|
||||
</Input>
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col :span="2">
|
||||
<Button size="small" type="error" shape="circle" icon="md-remove" @click="handlerMinusConfigure(element, configure.value)"/>
|
||||
</Col>
|
||||
</Row>
|
||||
</FormItem>
|
||||
</div>
|
||||
</div>
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col :span="5"/>
|
||||
</Row>
|
||||
</div>
|
||||
</TabPane>
|
||||
<TabPane :disabled="!formState.type" name="authorization" :label="$t('common.authorization')" icon="md-lock">
|
||||
<div style="margin-top: 10px;">
|
||||
<Row>
|
||||
<Col :span="5"/>
|
||||
<Col :span="14">
|
||||
<FormItem :label="$t('common.username')" prop="username">
|
||||
<Input type="text" v-model="formState.username"/>
|
||||
</FormItem>
|
||||
<FormItem :label="$t('common.password')" prop="password">
|
||||
<Input type="password" v-model="formState.password"/>
|
||||
</FormItem>
|
||||
<FormItem label="SSL" prop="ssl">
|
||||
<Switch v-model="formState.ssl"/>
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col :span="5"/>
|
||||
</Row>
|
||||
</div>
|
||||
</TabPane>
|
||||
<TabPane :disabled="!formState.type" name="advanced" :label="$t('common.advanced')" icon="md-archive">
|
||||
<div style="margin-top: 10px;">
|
||||
<Row>
|
||||
<Col :span="5"/>
|
||||
<Col :span="14">
|
||||
<FormItem :label="$t('common.catalog')" prop="catalog">
|
||||
<Input type="text" v-model="formState.catalog"/>
|
||||
</FormItem>
|
||||
<FormItem :label="$t('common.database')" prop="database">
|
||||
<Input type="text" v-model="formState.database"/>
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col :span="5"/>
|
||||
</Row>
|
||||
</div>
|
||||
</TabPane>
|
||||
<TabPane :disabled="!formState.type" name="custom" :label="$t('common.custom')" icon="md-hammer">
|
||||
<div style="margin-top: 10px;">
|
||||
<FormItem>
|
||||
<Button size="small" type="primary" shape="circle" icon="md-add" @click="handlerPlusConfigure()"/>
|
||||
</FormItem>
|
||||
<FormItem v-for="(element, index) in configure" :key="index" style="margin-bottom: 5px;">
|
||||
<Row :gutter="12">
|
||||
<Col :span="10">
|
||||
<FormItem>
|
||||
<Input v-model="element.field">
|
||||
<template #prepend>
|
||||
<span>{{ $t('common.field') }}</span>
|
||||
</template>
|
||||
</Input>
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col :span="10">
|
||||
<FormItem>
|
||||
<Input v-model="element.value">
|
||||
<template #prepend>
|
||||
<span>{{ $t('common.value') }}</span>
|
||||
</template>
|
||||
</Input>
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col :span="2">
|
||||
<Button size="small" type="error" shape="circle" icon="md-remove" @click="handlerMinusConfigure(element)"/>
|
||||
</Col>
|
||||
</Row>
|
||||
</FormItem>
|
||||
</div>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</Form>
|
||||
<template #footer>
|
||||
@ -182,16 +146,8 @@ export default defineComponent({
|
||||
labelCol: {span: 6},
|
||||
wrapperCol: {span: 12},
|
||||
};
|
||||
const validateMessages = {
|
||||
required: '${label} is required!',
|
||||
number: {
|
||||
range: '${label} must be between ${min} and ${max}',
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
activeKey: ref('type'),
|
||||
validateMessages,
|
||||
activeKey: ref('source'),
|
||||
layout
|
||||
};
|
||||
},
|
||||
@ -205,7 +161,10 @@ export default defineComponent({
|
||||
plugins: null,
|
||||
testInfo: {} as TestInfo,
|
||||
connectionLoading: false,
|
||||
configure: [] as Configure[]
|
||||
pluginTabs: ['source'],
|
||||
pluginConfigure: null,
|
||||
pluginTabConfigure: null,
|
||||
applyPlugin: null
|
||||
}
|
||||
},
|
||||
created()
|
||||
@ -270,14 +229,14 @@ export default defineComponent({
|
||||
},
|
||||
handlerTest()
|
||||
{
|
||||
this.connectionLoading = true;
|
||||
this.formState.configures = Arrays.arrayToObject(this.configure);
|
||||
const applyConfigure = clone(this.formState);
|
||||
const temp = clone(this.formState.type).split(' ');
|
||||
applyConfigure.type = temp[0];
|
||||
applyConfigure.protocol = temp[1];
|
||||
const configure = {
|
||||
type: temp[1],
|
||||
name: temp[0],
|
||||
configure: this.applyPlugin
|
||||
};
|
||||
new SourceService()
|
||||
.testConnection(applyConfigure)
|
||||
.testConnection(configure)
|
||||
.then((response) => {
|
||||
this.testInfo.percent = 100;
|
||||
if (response.status) {
|
||||
@ -293,17 +252,38 @@ export default defineComponent({
|
||||
this.connectionLoading = false;
|
||||
});
|
||||
},
|
||||
handlerPlusConfigure()
|
||||
handlerPlusConfigure(array: Array<Configure>)
|
||||
{
|
||||
const configure: Configure = {field: '', value: ''};
|
||||
this.configure.push(configure);
|
||||
},
|
||||
handlerMinusConfigure(configure: Configure)
|
||||
{
|
||||
const index = this.configure.indexOf(configure);
|
||||
if (index !== -1) {
|
||||
this.configure.splice(index, 1);
|
||||
if (array === null) {
|
||||
array = new Array<Configure>();
|
||||
}
|
||||
const configure: Configure = {field: '', value: ''};
|
||||
array.push(configure);
|
||||
},
|
||||
handlerMinusConfigure(configure: Configure, array: Array<Configure>)
|
||||
{
|
||||
const index = array.indexOf(configure);
|
||||
if (index !== -1) {
|
||||
array.splice(index, 1);
|
||||
}
|
||||
},
|
||||
handlerChangePlugin(value)
|
||||
{
|
||||
const pluginAndType = value.split(' ');
|
||||
const applyPlugins: [] = this.plugins[pluginAndType[1]];
|
||||
const applyPlugin = applyPlugins.filter(plugin => plugin['name'] === pluginAndType[0])[0];
|
||||
this.applyPlugin = applyPlugin['configure'];
|
||||
this.pluginConfigure = applyPlugin['configure']['configures'];
|
||||
// Clear
|
||||
this.pluginTabs = ['source'];
|
||||
this.pluginTabs = [...this.pluginTabs, ...Array.from(new Set(this.pluginConfigure.map(v => v.group)))];
|
||||
},
|
||||
handlerFilterConfigure(group: string)
|
||||
{
|
||||
if (group === 'source') {
|
||||
return;
|
||||
}
|
||||
this.pluginTabConfigure = this.pluginConfigure.filter(field => field.group === group);
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
Loading…
Reference in New Issue
Block a user