[Refactor] [Core] Refactoring plug-in configuration extraction mode (test connection part)

This commit is contained in:
qianmoQ 2023-01-18 20:59:46 +08:00
parent cc0a548c6d
commit 279fb2980a
26 changed files with 646 additions and 132 deletions

View 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

View 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"
}
]
}

View 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"
}
]
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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"),

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -0,0 +1,9 @@
package io.edurt.datacap.server.plugin.configure;
public enum IConfigureFieldGroup
{
configure,
authorization,
advanced,
custom
}

View File

@ -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
}

View File

@ -0,0 +1,11 @@
package io.edurt.datacap.server.plugin.configure;
public enum IConfigureFieldType
{
Number,
String,
List,
Boolean,
Array,
Map
}

View File

@ -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);
}

View File

@ -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());
}
}

View File

@ -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);
}
}

View File

@ -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));
}
}

View File

@ -0,0 +1,2 @@
{
}

View 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"
}
]
}

View File

@ -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",

View File

@ -76,5 +76,6 @@ export default {
client: 'Client',
ip: 'IP',
ua: 'User Agent',
loginTime: 'Login Time'
loginTime: 'Login Time',
ssl: 'SSL'
}

View File

@ -76,5 +76,6 @@ export default {
client: '客户端',
ip: 'IP',
ua: '用户代理',
loginTime: '登录时间'
loginTime: '登录时间',
ssl: 'SSL'
}

View File

@ -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>

View File

@ -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: {