feat(plugin): adapter plugin -- influxdb (#876)
Some checks are pending
Before checker with maven / before_checker_loading (push) Waiting to run
Before checker with maven / before_checker_ui (push) Blocked by required conditions
Before checker with maven / before_checker_style (push) Blocked by required conditions
Before checker with maven / before_checker_bugs (push) Blocked by required conditions
Before checker with maven / before_checker_test (push) Blocked by required conditions
Before checker with maven / before_checker_package (push) Blocked by required conditions
Analysis java code / Analyze (java) (push) Waiting to run
@ -1,5 +1,6 @@
|
||||
datacap-plugin-mysql=plugin/datacap-plugin-mysql/pom.xml
|
||||
datacap-plugin-clickhouse=plugin/datacap-plugin-clickhouse/pom.xml
|
||||
datacap-plugin-influxdb=plugin/datacap-plugin-influxdb/pom.xml
|
||||
datacap-convert-txt=convert/datacap-convert-txt/pom.xml
|
||||
datacap-convert-json=convert/datacap-convert-json/pom.xml
|
||||
datacap-convert-xml=convert/datacap-convert-xml/pom.xml
|
||||
|
@ -35,6 +35,21 @@
|
||||
],
|
||||
"url": "https://cdn.north.devlive.org/applications/datacap/plugins/2024.4.0-SNAPSHOT/plugin/datacap-plugin-clickhouse-bin.tar.gz"
|
||||
},
|
||||
{
|
||||
"key": "datacap-plugin-influxdb",
|
||||
"label": "InfluxDB",
|
||||
"description": "InfluxDB is a time series database that stores and retrieves data points. It is a distributed database with a focus on performance, scalability, and ease of use.",
|
||||
"i18nFormat": true,
|
||||
"type": "Connector",
|
||||
"version": "2024.4.0-SNAPSHOT",
|
||||
"author": "datacap-community",
|
||||
"logo": "https://cdn.north.devlive.org/applications/datacap/resources/logo/plugin/influxdb.svg",
|
||||
"released": "2024-11-21 23:11:15",
|
||||
"supportVersion": [
|
||||
"ALL"
|
||||
],
|
||||
"url": "https://cdn.north.devlive.org/applications/datacap/plugins/2024.4.0-SNAPSHOT/plugin/datacap-plugin-influxdb-bin.tar.gz"
|
||||
},
|
||||
{
|
||||
"key": "datacap-convert-csv",
|
||||
"label": "CsvConvert",
|
||||
|
@ -7,6 +7,7 @@ import io.edurt.datacap.plugin.utils.PluginPathUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
@ -81,7 +82,7 @@ public class PropertiesPluginLoader
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
catch (IOException e) {
|
||||
log.error("Failed to load plugins from properties file: {}", path, e);
|
||||
}
|
||||
return plugins;
|
||||
|
@ -93,12 +93,14 @@ public class TarPluginLoader
|
||||
public List<Plugin> load(Path path, Path targetDir, Set<String> parentClassLoaderPackages)
|
||||
{
|
||||
try {
|
||||
if (isValidTarPath(path)) {
|
||||
// 如果是 URL 路径,先下载到本地
|
||||
// If it's a URL path, download it first
|
||||
if (path.toString().startsWith("http") || path.toString().startsWith("https")) {
|
||||
path = downloadTarFile(path.toString().replace(":/", "://"));
|
||||
}
|
||||
|
||||
if (isValidTarFile(path)) {
|
||||
// 使用指定的目录或创建临时目录
|
||||
// Use specified directory or create temporary directory
|
||||
Path extractDir = targetDir != null ? targetDir : createTempDirectory();
|
||||
@ -119,6 +121,10 @@ public class TarPluginLoader
|
||||
|
||||
return plugins;
|
||||
}
|
||||
}
|
||||
|
||||
return List.of();
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("Failed to load plugins from tar file: {}", path, e);
|
||||
return List.of();
|
||||
@ -327,4 +333,39 @@ public class TarPluginLoader
|
||||
log.warn("Failed to cleanup temporary directory: {}", directory, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否为合法 Tar 文件路径
|
||||
* Check if it's a valid Tar file path
|
||||
*
|
||||
* @param path file path
|
||||
* @return true if it's a valid Tar file
|
||||
*/
|
||||
private boolean isValidTarPath(Path path)
|
||||
{
|
||||
String pathStr = path.toString();
|
||||
return (pathStr.startsWith("http") || pathStr.startsWith("https"))
|
||||
&& (pathStr.endsWith("tar.gz") || pathStr.endsWith("tar") || pathStr.endsWith("tgz"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否合法的 Tar 文件,通过解压文件头检查
|
||||
* Check if it's a valid Tar file, by checking the file header
|
||||
*
|
||||
* @param path file path
|
||||
* @return true if it's a valid Tar file
|
||||
*/
|
||||
private boolean isValidTarFile(Path path)
|
||||
{
|
||||
try (InputStream fileIn = Files.newInputStream(path);
|
||||
BufferedInputStream buffIn = new BufferedInputStream(fileIn);
|
||||
GZIPInputStream gzipIn = new GZIPInputStream(buffIn)) {
|
||||
log.debug("Validating tar file: {}", path);
|
||||
log.debug("Tar file header: {}", gzipIn.readNBytes(512));
|
||||
return true;
|
||||
}
|
||||
catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -421,12 +421,6 @@
|
||||
<!-- </dependency>-->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>io.edurt.datacap</groupId>-->
|
||||
<!-- <artifactId>datacap-plugin-influxdb</artifactId>-->
|
||||
<!-- <version>${project.version}</version>-->
|
||||
<!-- <scope>provided</scope>-->
|
||||
<!-- </dependency>-->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>io.edurt.datacap</groupId>-->
|
||||
<!-- <artifactId>datacap-captcha</artifactId>-->
|
||||
<!-- <version>${project.version}</version>-->
|
||||
<!-- </dependency>-->
|
||||
|
@ -6,14 +6,17 @@ import io.edurt.datacap.spi.adapter.HttpAdapter;
|
||||
import io.edurt.datacap.spi.adapter.JdbcAdapter;
|
||||
import io.edurt.datacap.spi.adapter.NativeAdapter;
|
||||
import io.edurt.datacap.spi.connection.Connection;
|
||||
import io.edurt.datacap.spi.connection.JdbcConfigure;
|
||||
import io.edurt.datacap.spi.connection.JdbcConnection;
|
||||
import io.edurt.datacap.spi.model.Configure;
|
||||
import io.edurt.datacap.spi.model.Response;
|
||||
import org.apache.commons.beanutils.BeanUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public interface PluginService
|
||||
extends Service
|
||||
{
|
||||
@ -54,23 +57,64 @@ public interface PluginService
|
||||
{
|
||||
Response response = new Response();
|
||||
try {
|
||||
JdbcConfigure jdbcConfigure = new JdbcConfigure();
|
||||
if (jdbcConfigure.getJdbcDriver() == null) {
|
||||
jdbcConfigure.setJdbcDriver(this.driver());
|
||||
}
|
||||
if (jdbcConfigure.getJdbcType() == null) {
|
||||
jdbcConfigure.setJdbcType(this.connectType());
|
||||
}
|
||||
BeanUtils.copyProperties(jdbcConfigure, configure);
|
||||
local.set(new JdbcConnection(jdbcConfigure, response));
|
||||
configure.setDriver(this.driver());
|
||||
configure.setType(this.connectType());
|
||||
configure.setUrl(Optional.of(url(configure)));
|
||||
local.set(new JdbcConnection(configure, response));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
response.setIsConnected(Boolean.FALSE);
|
||||
response.setMessage(ex.getMessage());
|
||||
log.error("Error connecting : {}", ex);
|
||||
log.error("Error connecting :", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建驱动路径
|
||||
* Build the driver path
|
||||
*
|
||||
* @param configure 配置信息 | Configuration information
|
||||
* @return 驱动路径 | Driver path
|
||||
*/
|
||||
default String url(Configure configure)
|
||||
{
|
||||
if (configure.getUrl().isPresent()) {
|
||||
return configure.getUrl().get();
|
||||
}
|
||||
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
buffer.append("jdbc:");
|
||||
buffer.append(configure.getType());
|
||||
buffer.append("://");
|
||||
buffer.append(configure.getHost());
|
||||
buffer.append(":");
|
||||
buffer.append(configure.getPort());
|
||||
if (configure.getDatabase().isPresent()) {
|
||||
buffer.append("/");
|
||||
buffer.append(configure.getDatabase().get());
|
||||
}
|
||||
if (configure.getSsl().isPresent()) {
|
||||
buffer.append(String.format("?ssl=%s", configure.getSsl().get()));
|
||||
}
|
||||
if (configure.getEnv().isPresent()) {
|
||||
Map<String, Object> env = configure.getEnv().get();
|
||||
List<String> flatEnv = env.entrySet()
|
||||
.stream()
|
||||
.map(value -> String.format("%s=%s", value.getKey(), value.getValue()))
|
||||
.collect(Collectors.toList());
|
||||
if (configure.getSsl().isEmpty()) {
|
||||
buffer.append("?");
|
||||
}
|
||||
else {
|
||||
if (configure.getIsAppendChar()) {
|
||||
buffer.append("&");
|
||||
}
|
||||
}
|
||||
buffer.append(String.join("&", flatEnv));
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
default Response execute(String content)
|
||||
{
|
||||
|
@ -3,8 +3,8 @@ package io.edurt.datacap.spi.adapter;
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import io.edurt.datacap.spi.column.Column;
|
||||
import io.edurt.datacap.spi.column.JdbcColumn;
|
||||
import io.edurt.datacap.spi.connection.JdbcConfigure;
|
||||
import io.edurt.datacap.spi.connection.JdbcConnection;
|
||||
import io.edurt.datacap.spi.model.Configure;
|
||||
import io.edurt.datacap.spi.model.Response;
|
||||
import io.edurt.datacap.spi.model.Time;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@ -39,7 +39,7 @@ public class JdbcAdapter
|
||||
processorTime.setStart(new Date().getTime());
|
||||
Response response = jdbcConnection.getResponse();
|
||||
Connection connection = (Connection) jdbcConnection.getConnection();
|
||||
JdbcConfigure configure = (JdbcConfigure) jdbcConnection.getConfigure();
|
||||
Configure configure = jdbcConnection.getConfigure();
|
||||
if (response.getIsConnected()) {
|
||||
try (Statement statement = connection.createStatement()) {
|
||||
List<String> headers = new ArrayList<>();
|
||||
|
@ -25,8 +25,6 @@ public abstract class Connection
|
||||
this.response.setConnection(connectionTime);
|
||||
}
|
||||
|
||||
protected abstract String formatJdbcUrl();
|
||||
|
||||
protected abstract java.sql.Connection openConnection();
|
||||
|
||||
public Object getConnection()
|
||||
|
@ -1,20 +0,0 @@
|
||||
package io.edurt.datacap.spi.connection;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import io.edurt.datacap.spi.model.Configure;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
@Data
|
||||
@ToString
|
||||
@EqualsAndHashCode
|
||||
@SuppressFBWarnings(value = {"EQ_OVERRIDING_EQUALS_NOT_SYMMETRIC"},
|
||||
justification = "I prefer to suppress these FindBugs warnings")
|
||||
public class JdbcConfigure
|
||||
extends Configure
|
||||
{
|
||||
private String jdbcDriver;
|
||||
private String jdbcType;
|
||||
private Boolean isAppendChar = Boolean.TRUE;
|
||||
}
|
@ -2,6 +2,7 @@ package io.edurt.datacap.spi.connection;
|
||||
|
||||
import io.edurt.datacap.plugin.PluginContextManager;
|
||||
import io.edurt.datacap.plugin.loader.PluginClassLoader;
|
||||
import io.edurt.datacap.spi.model.Configure;
|
||||
import io.edurt.datacap.spi.model.Response;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
@ -11,11 +12,8 @@ import java.sql.Driver;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.DriverPropertyInfo;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
public class JdbcConnection
|
||||
@ -23,88 +21,41 @@ public class JdbcConnection
|
||||
{
|
||||
private java.sql.Connection jdbcConnection;
|
||||
|
||||
public JdbcConnection(JdbcConfigure configure, Response response)
|
||||
public JdbcConnection(Configure configure, Response response)
|
||||
{
|
||||
super(configure, response);
|
||||
}
|
||||
|
||||
protected String formatJdbcUrl()
|
||||
{
|
||||
JdbcConfigure jdbcConfigure = getJdbcConfigure();
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
buffer.append("jdbc:");
|
||||
buffer.append(jdbcConfigure.getJdbcType());
|
||||
if (jdbcConfigure.getJdbcType().equals("influxdb")) {
|
||||
buffer.append(":");
|
||||
}
|
||||
else {
|
||||
buffer.append("://");
|
||||
}
|
||||
buffer.append(jdbcConfigure.getHost());
|
||||
buffer.append(":");
|
||||
buffer.append(jdbcConfigure.getPort());
|
||||
if (jdbcConfigure.getDatabase().isPresent()) {
|
||||
if (jdbcConfigure.getJdbcType().equals("solr")) {
|
||||
buffer.append("/?collection=");
|
||||
}
|
||||
else {
|
||||
buffer.append("/");
|
||||
}
|
||||
buffer.append(jdbcConfigure.getDatabase().get());
|
||||
}
|
||||
if (jdbcConfigure.getSsl().isPresent()) {
|
||||
buffer.append(String.format("?ssl=%s", jdbcConfigure.getSsl().get()));
|
||||
}
|
||||
if (jdbcConfigure.getEnv().isPresent()) {
|
||||
Map<String, Object> env = jdbcConfigure.getEnv().get();
|
||||
List<String> flatEnv = env.entrySet()
|
||||
.stream()
|
||||
.map(value -> String.format("%s=%s", value.getKey(), value.getValue()))
|
||||
.collect(Collectors.toList());
|
||||
if (jdbcConfigure.getSsl().isEmpty()) {
|
||||
buffer.append("?");
|
||||
}
|
||||
else {
|
||||
if (jdbcConfigure.getIsAppendChar()) {
|
||||
buffer.append("&");
|
||||
}
|
||||
}
|
||||
buffer.append(String.join("&", flatEnv));
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
protected JdbcConfigure getJdbcConfigure()
|
||||
{
|
||||
return (JdbcConfigure) this.configure;
|
||||
}
|
||||
|
||||
protected java.sql.Connection openConnection()
|
||||
{
|
||||
JdbcConfigure jdbcConfigure = getJdbcConfigure();
|
||||
|
||||
try {
|
||||
PluginClassLoader pluginClassLoader = jdbcConfigure.getPlugin().getPluginClassLoader();
|
||||
PluginClassLoader pluginClassLoader = configure.getPlugin().getPluginClassLoader();
|
||||
PluginContextManager.runWithClassLoader(pluginClassLoader, () -> {
|
||||
Class<?> driverClass = Class.forName(jdbcConfigure.getJdbcDriver(), true, pluginClassLoader);
|
||||
Class<?> driverClass = Class.forName(configure.getDriver(), true, pluginClassLoader);
|
||||
Driver driver = (Driver) driverClass.getDeclaredConstructor().newInstance();
|
||||
DriverManager.registerDriver(new DriverShim(driver));
|
||||
|
||||
String url = formatJdbcUrl();
|
||||
log.info("Connection driver {}", jdbcConfigure.getJdbcDriver());
|
||||
if (configure.getUrl().isEmpty()) {
|
||||
throw new RuntimeException("Connection url not present");
|
||||
}
|
||||
|
||||
String url = configure.getUrl().get();
|
||||
log.info("Connection driver {}", configure.getType());
|
||||
log.info("Connection url {}", url);
|
||||
if (jdbcConfigure.getUsername().isPresent() && jdbcConfigure.getPassword().isPresent()) {
|
||||
if (configure.getUsername().isPresent() && configure.getPassword().isPresent()) {
|
||||
log.info("Connection username with {} password with {}",
|
||||
jdbcConfigure.getUsername().get(), "***");
|
||||
this.jdbcConnection = DriverManager.getConnection(url,
|
||||
jdbcConfigure.getUsername().get(),
|
||||
jdbcConfigure.getPassword().get());
|
||||
configure.getUsername().get(), "***");
|
||||
this.jdbcConnection = DriverManager.getConnection(
|
||||
url,
|
||||
configure.getUsername().get(),
|
||||
configure.getPassword().get()
|
||||
);
|
||||
}
|
||||
else {
|
||||
log.info("Connection username and password not present");
|
||||
Properties properties = new Properties();
|
||||
if (jdbcConfigure.getUsername().isPresent()) {
|
||||
properties.put("user", jdbcConfigure.getUsername().get());
|
||||
if (configure.getUsername().isPresent()) {
|
||||
properties.put("user", configure.getUsername().get());
|
||||
}
|
||||
this.jdbcConnection = DriverManager.getConnection(url, properties);
|
||||
}
|
||||
|
@ -27,12 +27,26 @@ public class Configure
|
||||
private PluginManager pluginManager;
|
||||
private String host;
|
||||
private Integer port;
|
||||
|
||||
@Builder.Default
|
||||
private Optional<String> username = Optional.empty();
|
||||
|
||||
@Builder.Default
|
||||
private Optional<String> password = Optional.empty();
|
||||
|
||||
@Builder.Default
|
||||
private Optional<String> database = Optional.empty();
|
||||
|
||||
@Builder.Default
|
||||
private Optional<String> version = Optional.empty();
|
||||
|
||||
@Builder.Default
|
||||
private Optional<Map<String, Object>> env = Optional.empty();
|
||||
|
||||
@Builder.Default
|
||||
private Optional<Boolean> ssl = Optional.empty();
|
||||
|
||||
@Builder.Default
|
||||
private String format = "JsonConvert";
|
||||
// if `to`: skip
|
||||
private Optional<String> query = Optional.empty();
|
||||
@ -40,4 +54,15 @@ public class Configure
|
||||
private String home;
|
||||
private boolean usedConfig;
|
||||
private String id;
|
||||
|
||||
// 自定义 url
|
||||
// Custom url
|
||||
@Builder.Default
|
||||
private Optional<String> url = Optional.empty();
|
||||
|
||||
private String driver;
|
||||
private String type;
|
||||
|
||||
@Builder.Default
|
||||
private Boolean isAppendChar = Boolean.TRUE;
|
||||
}
|
||||
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 79 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
@ -15,7 +15,7 @@
|
||||
:key="plugin.name"
|
||||
:value="plugin.name">
|
||||
<ShadcnTooltip :content="plugin.name" class="p-1">
|
||||
<img class="h-16 w-16 object-contain" :src="'/static/images/plugin/' + plugin.name + '.png'" :alt="plugin.name">
|
||||
<img class="h-16 w-16 object-contain" :src="'/static/images/plugin/' + plugin.name.toLowerCase() + '.png'" :alt="plugin.name">
|
||||
</ShadcnTooltip>
|
||||
</ShadcnToggle>
|
||||
</ShadcnToggleGroup>
|
||||
|
@ -1,11 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200">
|
||||
<!-- 背景圆 -->
|
||||
<!-- Background circle -->
|
||||
<circle cx="100" cy="100" r="90" fill="#f0f8ff" />
|
||||
<circle cx="100" cy="100" r="100" fill="#f0f8ff" />
|
||||
|
||||
<!-- 外部圆环 -->
|
||||
<!-- Outer ring -->
|
||||
<circle cx="100" cy="100" r="80" fill="none" stroke="#1E90FF" stroke-width="8" />
|
||||
<circle cx="100" cy="100" r="96" fill="none" stroke="#1E90FF" stroke-width="8" />
|
||||
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg version="1.1"
|
||||
|
Before Width: | Height: | Size: 290 KiB After Width: | Height: | Size: 290 KiB |
4
logo/plugin/influxdb.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg enable-background="new -173 -143 900 900" height="900" viewBox="-173 -143 900 900" width="900" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m-173-143h900v900h-900z" fill="none" />
|
||||
<path d="m694.1 394.9-81-352.7c-4.6-19.3-22.1-38.6-41.4-44.2l-370.2-114.2c-4.6-1.8-10.1-1.8-15.7-1.8-15.7 0-32.2 6.4-43.3 15.7l-265.2 246.8c-14.7 12.9-22.1 38.7-17.5 57.1l86.6 377.6c4.6 19.3 22.1 38.7 41.4 44.2l346.2 106.8c4.6 1.8 10.1 1.8 15.7 1.8 15.7 0 32.2-6.4 43.3-15.7l283.6-263.3c14.8-13.8 22.1-38.7 17.5-58.1zm-453.9-427.3 254.1 78.3c10.1 2.8 10.1 7.4 0 10.1l-133.5 30.4c-10.1 2.8-23.9-1.8-31.3-9.2l-93-100.4c-8.3-8.2-6.5-11.9 3.7-9.2zm158.3 455.9c2.8 10.1-3.7 15.7-13.8 12.9l-274.4-84.7c-10.1-2.8-12-11.1-4.6-18.4l210-195.3c7.4-7.4 15.7-4.6 18.4 5.5zm-452.1-248.7 222.9-207.2c7.4-7.4 19.3-6.4 26.7.9l111.4 120.7c7.4 7.4 6.4 19.3-.9 26.7l-222.9 207.2c-7.4 7.4-19.3 6.4-26.7-.9l-111.4-120.6c-7.4-8.3-6.4-20.3.9-26.8zm54.4 328.8-58.9-258.8c-2.8-10.1 1.8-12 8.3-4.6l93 100.4c7.4 7.4 10.1 22.1 7.4 32.2l-40.6 130.8c-2.8 10.1-7.4 10.1-9.2 0zm325.9 151-291-89.3c-10.1-2.8-15.7-13.8-12.9-23.9l48.8-156.6c2.8-10.1 13.8-15.7 23.9-12.9l291 89.3c10.1 2.8 15.7 13.8 12.9 23.9l-48.8 156.6c-3.6 10.2-13.7 15.7-23.9 12.9zm257.8-211.8-194.2 180.5c-7.4 7.4-11 4.6-8.3-5.5l40.5-130.8c2.8-10.1 13.8-20.3 23.9-22.1l133.5-30.4c10.2-2.7 12 1.9 4.6 8.3zm21.2-38.6-160.2 36.8c-10.1 2.8-20.3-3.7-23-13.8l-68.1-296.5c-2.8-10.1 3.7-20.3 13.8-23l160.2-36.8c10.1-2.8 20.3 3.7 23 13.8l68.1 296.5c2.8 11-3.6 21.1-13.8 23z" fill="#22adf6" />
|
||||
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
@ -11,11 +11,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>datacap-plugin-influxdb</artifactId>
|
||||
<description>DataCap - InfluxDB</description>
|
||||
|
||||
<properties>
|
||||
<plugin.name>datacap-influxdb</plugin.name>
|
||||
</properties>
|
||||
<description>DataCap - Plugin - InfluxDB</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
@ -32,11 +28,6 @@
|
||||
<artifactId>kotlin-reflect</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>testcontainers</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.suteren.jdbc.influxdb</groupId>
|
||||
<artifactId>influxdb-jdbc</artifactId>
|
||||
@ -70,7 +61,24 @@
|
||||
<groupId>org.jetbrains.dokka</groupId>
|
||||
<artifactId>dokka-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-dependencies</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy-dependencies</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${project.build.directory}/dependencies</outputDirectory>
|
||||
<excludeScope>provided</excludeScope>
|
||||
<includeScope>runtime</includeScope>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
@ -1,21 +0,0 @@
|
||||
package io.edurt.datacap.plugin.influxdb
|
||||
|
||||
import com.google.inject.multibindings.Multibinder
|
||||
import io.edurt.datacap.spi.AbstractPluginModule
|
||||
import io.edurt.datacap.spi.PluginService
|
||||
import io.edurt.datacap.spi.PluginModule
|
||||
|
||||
class InfluxDBModule : AbstractPluginModule(), PluginModule
|
||||
{
|
||||
override fun get(): AbstractPluginModule
|
||||
{
|
||||
return this
|
||||
}
|
||||
|
||||
override fun configure()
|
||||
{
|
||||
Multibinder.newSetBinder(binder(), _root_ide_package_.io.edurt.datacap.spi.PluginService::class.java)
|
||||
.addBinding()
|
||||
.to(InfluxDBPlugin::class.java)
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
package io.edurt.datacap.plugin.influxdb
|
||||
|
||||
import io.edurt.datacap.spi.PluginService
|
||||
|
||||
class InfluxDBPlugin : _root_ide_package_.io.edurt.datacap.spi.PluginService
|
||||
{
|
||||
override fun validator(): String
|
||||
{
|
||||
return "SELECT 1"
|
||||
}
|
||||
|
||||
override fun driver(): String
|
||||
{
|
||||
return "net.suteren.jdbc.influxdb.InfluxDbDriver"
|
||||
}
|
||||
|
||||
override fun connectType(): String
|
||||
{
|
||||
return "influxdb"
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package io.edurt.datacap.plugin.influxdb
|
||||
|
||||
import io.edurt.datacap.plugin.Plugin
|
||||
|
||||
class InfluxdbPlugin : Plugin()
|
@ -0,0 +1,79 @@
|
||||
package io.edurt.datacap.plugin.influxdb
|
||||
|
||||
import io.edurt.datacap.spi.PluginService
|
||||
import io.edurt.datacap.spi.model.Configure
|
||||
|
||||
class InfluxdbService : PluginService
|
||||
{
|
||||
override fun validator(): String
|
||||
{
|
||||
return "SELECT 1"
|
||||
}
|
||||
|
||||
override fun driver(): String
|
||||
{
|
||||
return "net.suteren.jdbc.influxdb.InfluxDbDriver"
|
||||
}
|
||||
|
||||
override fun connectType(): String
|
||||
{
|
||||
return "influxdb"
|
||||
}
|
||||
|
||||
override fun url(configure: Configure): String
|
||||
{
|
||||
// 如果已经有自定义 URL,直接使用
|
||||
// If there is a custom URL, use it
|
||||
if (configure.url.isPresent)
|
||||
{
|
||||
return configure.url.get()
|
||||
}
|
||||
|
||||
val builder = StringBuilder("jdbc:${configure.type}:")
|
||||
|
||||
// 添加主机和端口
|
||||
// Add host and port
|
||||
builder.append(configure.host)
|
||||
if (configure.port != null)
|
||||
{
|
||||
builder.append(":").append(configure.port)
|
||||
}
|
||||
builder.append("/")
|
||||
|
||||
// 添加参数
|
||||
// Add parameters
|
||||
val params = mutableListOf<String>()
|
||||
|
||||
// 添加数据库
|
||||
// Add database
|
||||
configure.database.ifPresent { db ->
|
||||
params.add("db=$db")
|
||||
}
|
||||
|
||||
// 添加用户名和密码
|
||||
// Add username and password
|
||||
configure.username.ifPresent { username ->
|
||||
params.add("u=$username")
|
||||
}
|
||||
configure.password.ifPresent { password ->
|
||||
params.add("p=$password")
|
||||
}
|
||||
|
||||
// 添加其他环境参数
|
||||
// Add other environment parameters
|
||||
configure.env.ifPresent { envMap ->
|
||||
envMap.forEach { (key, value) ->
|
||||
params.add("$key=$value")
|
||||
}
|
||||
}
|
||||
|
||||
// 将所有参数添加到 URL
|
||||
// Add all parameters to the URL
|
||||
if (params.isNotEmpty())
|
||||
{
|
||||
builder.append("?").append(params.joinToString("&"))
|
||||
}
|
||||
|
||||
return builder.toString()
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
io.edurt.datacap.plugin.influxdb.InfluxdbPlugin
|
@ -0,0 +1 @@
|
||||
io.edurt.datacap.plugin.influxdb.InfluxdbService
|
@ -1 +0,0 @@
|
||||
io.edurt.datacap.plugin.influxdb.InfluxDBModule
|
@ -1,32 +0,0 @@
|
||||
package io.edurt.datacap.plugin.influxdb
|
||||
|
||||
import com.google.inject.Guice.createInjector
|
||||
import com.google.inject.Injector
|
||||
import com.google.inject.Key
|
||||
import com.google.inject.TypeLiteral
|
||||
import io.edurt.datacap.spi.Plugin
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
class InfluxDBModuleTest
|
||||
{
|
||||
private val name = "InfluxDB"
|
||||
private var injector: Injector? = null
|
||||
|
||||
@Before
|
||||
fun before()
|
||||
{
|
||||
injector = createInjector(InfluxDBModule())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test()
|
||||
{
|
||||
injector !!.getInstance(Key.get(object : TypeLiteral<Set<Plugin>>()
|
||||
{}))
|
||||
.stream()
|
||||
.findFirst()
|
||||
.ifPresent { assertEquals(name, it.name()) }
|
||||
}
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
package io.edurt.datacap.plugin.influxdb
|
||||
|
||||
import com.google.common.collect.Lists
|
||||
import com.google.inject.Guice.createInjector
|
||||
import com.google.inject.Injector
|
||||
import com.google.inject.Key
|
||||
import com.google.inject.TypeLiteral
|
||||
import io.edurt.datacap.file.FileManager
|
||||
import io.edurt.datacap.spi.Plugin
|
||||
import io.edurt.datacap.spi.model.Configure
|
||||
import io.edurt.datacap.spi.model.Response
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.slf4j.LoggerFactory.getLogger
|
||||
import org.testcontainers.containers.Network
|
||||
import org.testcontainers.lifecycle.Startables
|
||||
import org.testcontainers.shaded.org.awaitility.Awaitility.given
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class InfluxDBPluginTest
|
||||
{
|
||||
private val log = getLogger(this.javaClass)
|
||||
private val host = "TestInfluxDBContainer"
|
||||
private var container: InfluxDBContainer? = null
|
||||
private var injector: Injector? = null
|
||||
private var configure: Configure? = null
|
||||
|
||||
@Before
|
||||
fun before()
|
||||
{
|
||||
val network = Network.newNetwork()
|
||||
container = InfluxDBContainer()
|
||||
.withNetwork(network)
|
||||
.withNetworkAliases(host)
|
||||
container?.portBindings = Lists.newArrayList(String.format("%s:%s", InfluxDBContainer.PORT, InfluxDBContainer.DOCKER_PORT))
|
||||
Startables.deepStart(java.util.stream.Stream.of(container))
|
||||
.join()
|
||||
log.info("InfluxDB container started")
|
||||
given().ignoreExceptions()
|
||||
.await()
|
||||
.atMost(1, TimeUnit.MINUTES)
|
||||
|
||||
injector = createInjector(
|
||||
InfluxDBModule(),
|
||||
FileManager()
|
||||
)
|
||||
configure = Configure()
|
||||
configure !!.injector = injector
|
||||
configure !!.host = container?.host
|
||||
configure !!.port = InfluxDBContainer.PORT
|
||||
configure !!.username = Optional.of(InfluxDBContainer.USERNAME)
|
||||
configure !!.password = Optional.of(InfluxDBContainer.PASSWORD)
|
||||
configure !!.env = Optional.of(mapOf("useEncryption" to false, "useHTTP2" to false, "db" to "test"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test()
|
||||
{
|
||||
injector !!.getInstance(Key.get(object : TypeLiteral<Set<Plugin>>()
|
||||
{}))
|
||||
.stream()
|
||||
.findFirst()
|
||||
.ifPresent {
|
||||
it.connect(configure)
|
||||
val response: Response = it.execute(it.validator())
|
||||
log.info("================ Plugin executed information =================")
|
||||
if (! response.isSuccessful)
|
||||
{
|
||||
log.error("Message: {}", response.message)
|
||||
}
|
||||
else
|
||||
{
|
||||
response.columns.forEach { column -> log.info(column.toString()) }
|
||||
}
|
||||
assertTrue(response.isSuccessful)
|
||||
}
|
||||
}
|
||||
}
|
2
pom.xml
@ -75,7 +75,7 @@
|
||||
<!-- <module>plugin/datacap-plugin-paradedb</module>-->
|
||||
<!-- <module>plugin/datacap-plugin-timescale</module>-->
|
||||
<!-- <module>plugin/datacap-plugin-solr</module>-->
|
||||
<!-- <module>plugin/datacap-plugin-influxdb</module>-->
|
||||
<module>plugin/datacap-plugin-influxdb</module>
|
||||
<module>executor/datacap-executor-spi</module>
|
||||
<module>executor/datacap-executor-local</module>
|
||||
<module>executor/datacap-executor-seatunnel</module>
|
||||
|
@ -47,6 +47,10 @@
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.dokka</groupId>
|
||||
<artifactId>dokka-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-plugin</artifactId>
|
||||
|
@ -35,6 +35,10 @@
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.dokka</groupId>
|
||||
<artifactId>dokka-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-plugin</artifactId>
|
||||
|
@ -53,6 +53,10 @@
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.dokka</groupId>
|
||||
<artifactId>dokka-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-plugin</artifactId>
|
||||
|
@ -40,6 +40,10 @@
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.dokka</groupId>
|
||||
<artifactId>dokka-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-plugin</artifactId>
|
||||
|
@ -19,5 +19,52 @@
|
||||
<artifactId>datacap-plugin</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>testcontainers</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.edurt.datacap</groupId>
|
||||
<artifactId>datacap-spi</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.edurt.datacap</groupId>
|
||||
<artifactId>datacap-convert-spi</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.edurt.datacap</groupId>
|
||||
<artifactId>datacap-plugin-influxdb</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.dokka</groupId>
|
||||
<artifactId>dokka-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>test-compile</id>
|
||||
<goals>
|
||||
<goal>test-compile</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<sourceDirs>
|
||||
<sourceDir>${project.basedir}/src/test/kotlin</sourceDir>
|
||||
</sourceDirs>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
@ -4,7 +4,7 @@ import org.testcontainers.containers.GenericContainer
|
||||
import org.testcontainers.utility.DockerImageName
|
||||
import org.testcontainers.utility.DockerImageName.parse
|
||||
|
||||
class InfluxDBContainer : GenericContainer<InfluxDBContainer>
|
||||
class InfluxdbContainer : GenericContainer<InfluxdbContainer>
|
||||
{
|
||||
constructor() : super(parse(DEFAULT_IMAGE_NAME))
|
||||
{
|
@ -0,0 +1,29 @@
|
||||
package io.edurt.datacap.plugin.influxdb
|
||||
|
||||
import io.edurt.datacap.plugin.PluginConfigure
|
||||
import io.edurt.datacap.plugin.PluginManager
|
||||
import io.edurt.datacap.plugin.utils.PluginPathUtils
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Test
|
||||
|
||||
class InfluxdbPluginTest
|
||||
{
|
||||
private val pluginManager: PluginManager
|
||||
private val pluginName = "Influxdb"
|
||||
|
||||
init
|
||||
{
|
||||
val projectRoot = PluginPathUtils.findProjectRoot()
|
||||
val config = PluginConfigure.builder()
|
||||
.pluginsDir(projectRoot.resolve("plugin/datacap-plugin-influxdb"))
|
||||
.build()
|
||||
|
||||
pluginManager = PluginManager(config).apply { start() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test()
|
||||
{
|
||||
assertNotNull(pluginManager.getPlugin(pluginName).get())
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
package io.edurt.datacap.plugin.influxdb
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
|
||||
import io.edurt.datacap.plugin.PluginConfigure
|
||||
import io.edurt.datacap.plugin.PluginManager
|
||||
import io.edurt.datacap.plugin.utils.PluginPathUtils
|
||||
import io.edurt.datacap.spi.PluginService
|
||||
import io.edurt.datacap.spi.model.Configure
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Test
|
||||
import org.slf4j.LoggerFactory.getLogger
|
||||
import org.testcontainers.containers.Network
|
||||
import org.testcontainers.lifecycle.Startables
|
||||
import org.testcontainers.shaded.org.awaitility.Awaitility.given
|
||||
import java.nio.file.Path
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
@SuppressFBWarnings(value = ["RV_RETURN_VALUE_IGNORED_INFERRED", "SA_LOCAL_SELF_ASSIGNMENT"])
|
||||
class InfluxdbServiceTest
|
||||
{
|
||||
private val log = getLogger(this.javaClass)
|
||||
private val pluginManager: PluginManager
|
||||
private var container: InfluxdbContainer
|
||||
private val pluginName = "Influxdb"
|
||||
|
||||
init
|
||||
{
|
||||
val network = Network.newNetwork()
|
||||
|
||||
container = InfluxdbContainer()
|
||||
.withNetwork(network)
|
||||
.withNetworkAliases(this.javaClass.simpleName)
|
||||
|
||||
container.portBindings = listOf(String.format("%s:%s", InfluxdbContainer.PORT, InfluxdbContainer.DOCKER_PORT))
|
||||
|
||||
Startables.deepStart(java.util.stream.Stream.of(container))
|
||||
.join()
|
||||
|
||||
given().ignoreExceptions()
|
||||
.await()
|
||||
.atMost(1, TimeUnit.MINUTES)
|
||||
log.info("Influxdb container started")
|
||||
|
||||
val configFile = this::class
|
||||
.java
|
||||
.classLoader
|
||||
.getResource("influxdb-config.properties")
|
||||
assertNotNull(configFile)
|
||||
log.info("Specified config file: {}", configFile)
|
||||
|
||||
val projectRoot = PluginPathUtils.findProjectRoot()
|
||||
val config = PluginConfigure.builder()
|
||||
.pluginsDir(projectRoot.resolve(Path.of(configFile.path)))
|
||||
.build()
|
||||
|
||||
log.info("Initializing plugin manager")
|
||||
pluginManager = PluginManager(config).apply { start() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test()
|
||||
{
|
||||
val plugin = pluginManager.getPlugin(pluginName).get()
|
||||
assertNotNull(plugin)
|
||||
|
||||
val configure: Configure = Configure.builder()
|
||||
.host(container.host)
|
||||
.port(InfluxdbContainer.PORT)
|
||||
.username(Optional.of(InfluxdbContainer.USERNAME))
|
||||
.password(Optional.of(InfluxdbContainer.PASSWORD))
|
||||
.database(Optional.of(InfluxdbContainer.BUCKET))
|
||||
.plugin(plugin)
|
||||
.pluginManager(pluginManager)
|
||||
.env(
|
||||
Optional.of(
|
||||
mapOf(
|
||||
"useEncryption" to false,
|
||||
"useHTTP2" to false,
|
||||
"org" to "TestOrg"
|
||||
)
|
||||
)
|
||||
)
|
||||
.build()
|
||||
|
||||
val pluginService = plugin.getService(PluginService::class.java)
|
||||
val response = pluginService.execute(configure, pluginService.validator())
|
||||
assertNotNull(response)
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
datacap-plugin-influxdb=plugin/datacap-plugin-influxdb/pom.xml
|
||||
datacap-convert-json=convert/datacap-convert-json/pom.xml
|
@ -47,6 +47,10 @@
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.dokka</groupId>
|
||||
<artifactId>dokka-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-plugin</artifactId>
|
||||
|