feat(plugin): support install plugin for tar

This commit is contained in:
qianmoQ 2024-11-23 19:01:07 +08:00
parent c7cf964089
commit 85c34e7acf
30 changed files with 354 additions and 246 deletions

View File

@ -44,6 +44,8 @@ jobs:
before_checker_bugs:
runs-on: ubuntu-latest
needs:
- before_checker_ui
steps:
- name: Checkout
uses: actions/checkout@v3
@ -61,11 +63,26 @@ jobs:
- name: Run SpotBugs for server
run: ./mvnw clean install spotbugs:spotbugs -Dcheckstyle.skip -Dgpg.skip -Dskip.pnpm -DskipTests=true -f core/datacap-server/pom.xml
before_checker_package:
before_checker_test:
runs-on: ubuntu-latest
needs:
- before_checker_style
- before_checker_bugs
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Maven Checker Style
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
- run: chmod 755 ./mvnw
- run: ./mvnw clean install package test -Dspotbugs.skip -Dgpg.skip -Dcheckstyle.skip -Dskip.pnpm
before_checker_package:
runs-on: ubuntu-latest
needs:
- before_checker_test
steps:
- name: Checkout
uses: actions/checkout@v3

View File

@ -2,6 +2,7 @@ package io.edurt.datacap.plugin;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.nio.file.Path;
import java.nio.file.Paths;
@ -10,6 +11,7 @@ import java.nio.file.Paths;
@Builder
public class PluginConfigure
{
@Setter
private Path pluginsDir;
private boolean autoReload;
private long scanInterval;

View File

@ -7,6 +7,7 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.edurt.datacap.common.utils.DateUtils;
import io.edurt.datacap.plugin.loader.PluginClassLoader;
import io.edurt.datacap.plugin.loader.PluginLoaderFactory;
import io.edurt.datacap.plugin.loader.TarPluginLoader;
import io.edurt.datacap.plugin.utils.PluginClassLoaderUtils;
import io.edurt.datacap.plugin.utils.VersionUtils;
import lombok.Getter;
@ -33,7 +34,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Stream;
@Slf4j
@SuppressFBWarnings(value = {"NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE"})
@SuppressFBWarnings(value = {"NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", "EI_EXPOSE_REP2"})
public class PluginManager
{
// 插件配置
@ -140,11 +141,13 @@ public class PluginManager
throw new IllegalArgumentException("Source path and target directory cannot be null or empty");
}
// 验证源路径是否存在
// Verify source path exists
if (!Files.exists(sourcePath)) {
log.error("Source plugin path does not exist: {}", sourcePath);
return false;
// 对于本地文件才验证存在性
// Only verify existence for local files
if (!sourcePath.toString().startsWith("http") && !sourcePath.toString().startsWith("https")) {
if (!Files.exists(sourcePath)) {
log.error("Source plugin path does not exist: {}", sourcePath);
return false;
}
}
// 验证目标目录名称合法性
@ -217,6 +220,15 @@ public class PluginManager
// 检测并处理插件类型
// Detect and handle plugin type
try {
// 检测并处理插件类型
// Detect and handle plugin type
if ("tar".equals(extension)
|| "tar.gz".equals(extension)
|| "tgz".equals(extension)
|| "gz".equals(extension)
) {
installed.set(installTarPlugin(sourcePath, tempDir));
}
// Properties 插件
// Properties plugin
if ("properties".equals(extension)) {
@ -266,7 +278,7 @@ public class PluginManager
throws IOException
{
if (Files.exists(pluginPath)) {
String timestamp = DateUtils.formatYMDHMSWithInterval();
String timestamp = DateUtils.formatYMDHMS();
Path backupPath = pluginPath.getParent().resolve(pluginPath.getFileName() + ".backup." + timestamp);
try {
@ -441,6 +453,50 @@ public class PluginManager
return true;
}
// 安装 Tar 类型插件
// Install Tar type plugin
private boolean installTarPlugin(Path sourcePath, Path tempDir)
throws IOException
{
// 直接使用 TarPluginLoader 处理
// Directly use TarPluginLoader to handle
TarPluginLoader tarPluginLoader = new TarPluginLoader();
// 先加载插件到临时目录
// Load plugins to temporary directory
List<Plugin> plugins = tarPluginLoader.load(sourcePath, tempDir);
if (!plugins.isEmpty()) {
// 查找解压后的子目录
// Find extracted subdirectory
try (Stream<Path> paths = Files.list(tempDir)) {
Optional<Path> subDir = paths
.filter(Files::isDirectory)
.findFirst();
if (subDir.isPresent()) {
// 如果存在子目录将其内容移动到临时目录根目录
// If subdirectory exists, move its contents to temp directory root
Path source = subDir.get();
try (Stream<Path> files = Files.list(source)) {
files.forEach(file -> {
try {
Path target = tempDir.resolve(file.getFileName());
Files.move(file, target);
}
catch (IOException e) {
log.error("Failed to move file: {} to {}", file, tempDir, e);
}
});
}
// 删除空的子目录
// Delete empty subdirectory
Files.delete(source);
}
}
return true;
}
return false;
}
// 复制目录
// Copy directory
private void copyDirectory(Path source, Path target)

View File

@ -74,6 +74,22 @@ public class TarPluginLoader
*/
@Override
public List<Plugin> load(Path path)
{
return load(path, null);
}
/**
* 加载插件到指定目录
* Load plugins to specified directory
*
* @param path 插件路径或 URL
* Plugin path or URL
* @param targetDir 目标解压目录如果为null则使用临时目录
* Target extraction directory, use temporary directory if null
* @return 加载的插件列表
* List of loaded plugins
*/
public List<Plugin> load(Path path, Path targetDir)
{
try {
// 如果是 URL 路径先下载到本地
@ -82,9 +98,9 @@ public class TarPluginLoader
path = downloadTarFile(path.toString().replace(":/", "://"));
}
// 创建临时解压目录
// Create temporary directory for extraction
Path extractDir = createTempDirectory();
// 使用指定的目录或创建临时目录
// Use specified directory or create temporary directory
Path extractDir = targetDir != null ? targetDir : createTempDirectory();
// 解压 tar 文件
// Extract tar file
@ -94,9 +110,11 @@ public class TarPluginLoader
// Load plugins from extracted directory
List<Plugin> plugins = loadPluginsFromDirectory(extractDir);
// 清理临时目录
// Cleanup temporary directory
cleanupTempDirectory(extractDir);
// 如果使用的是临时目录则清理
// Clean up if using temporary directory
if (targetDir == null) {
cleanupTempDirectory(extractDir);
}
return plugins;
}

View File

@ -5,7 +5,6 @@ import com.google.inject.Injector;
import io.edurt.datacap.common.utils.EnvironmentUtils;
import io.edurt.datacap.plugin.PluginManager;
import io.edurt.datacap.plugin.utils.PluginPathUtils;
import io.edurt.datacap.spi.PluginLoader;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -37,10 +36,10 @@ public class PluginConfiguration
// 开发模式下生效
// In development mode, it is effective
if (EnvironmentUtils.isIdeEnvironment()) {
log.info("Development mode is development mode");
config.setPluginsDir(projectRoot.resolve(Path.of(String.join("/", root, "plugins.properties"))));
}
// if (EnvironmentUtils.isIdeEnvironment()) {
// log.info("Development mode is development mode");
// config.setPluginsDir(projectRoot.resolve(Path.of(String.join("/", root, "plugins.properties"))));
// }
log.info("Plugins directory: {}", config.getPluginsDir());
PluginManager pluginManager = new PluginManager(config);
@ -52,6 +51,6 @@ public class PluginConfiguration
@Bean
public Injector injector()
{
return Guice.createInjector(new PluginLoader());
return Guice.createInjector();
}
}

View File

@ -40,23 +40,23 @@ public class ScheduleRunnerConfigure
@Override
public void run(String... args)
{
// this.scheduledRepository.findAllByActiveIsTrueAndIsSystemIsTrue()
// .forEach(task -> {
// log.info("Add new task [ {} ] to scheduler", task.getName());
// switch (task.getType()) {
// case SOURCE_SYNCHRONIZE:
// SyncMetadataScheduledRunnable syncMetadataScheduledRunnable = new SyncMetadataScheduledRunnable(task.getName(), injector, sourceRepository, sourceService);
// this.scheduledCronRegistrar.addCronTask(syncMetadataScheduledRunnable, task.getExpression());
// executorService.submit(syncMetadataScheduledRunnable);
// break;
// case SOURCE_CHECK:
// CheckScheduledRunnable checkScheduledRunnable = new CheckScheduledRunnable(task.getName(), this.injector, this.sourceRepository);
// this.scheduledCronRegistrar.addCronTask(checkScheduledRunnable, task.getExpression());
// executorService.submit(checkScheduledRunnable);
// break;
// default:
// log.warn("Unsupported task type [ {} ]", task.getType());
// }
// });
this.scheduledRepository.findAllByActiveIsTrueAndIsSystemIsTrue()
.forEach(task -> {
log.info("Add new task [ {} ] to scheduler", task.getName());
switch (task.getType()) {
case SOURCE_SYNCHRONIZE:
SyncMetadataScheduledRunnable syncMetadataScheduledRunnable = new SyncMetadataScheduledRunnable(task.getName(), injector, sourceRepository, sourceService);
this.scheduledCronRegistrar.addCronTask(syncMetadataScheduledRunnable, task.getExpression());
executorService.submit(syncMetadataScheduledRunnable);
break;
case SOURCE_CHECK:
CheckScheduledRunnable checkScheduledRunnable = new CheckScheduledRunnable(task.getName(), this.injector, this.sourceRepository);
this.scheduledCronRegistrar.addCronTask(checkScheduledRunnable, task.getExpression());
executorService.submit(checkScheduledRunnable);
break;
default:
log.warn("Unsupported task type [ {} ]", task.getType());
}
});
}
}

View File

@ -1,61 +1,47 @@
package io.edurt.datacap.server.controller;
import com.google.common.collect.Maps;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
import io.edurt.datacap.common.response.CommonResponse;
import io.edurt.datacap.executor.ExecutorService;
import io.edurt.datacap.plugin.PluginManager;
import io.edurt.datacap.plugin.PluginMetadata;
import io.edurt.datacap.scheduler.SchedulerService;
import lombok.Data;
import org.springframework.web.bind.annotation.GetMapping;
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@RestController
@RequestMapping(value = "/api/v1/plugin")
public class PluginController
{
private final PluginManager pluginManager;
private final Injector injector;
public PluginController(PluginManager pluginManager, Injector injector)
public PluginController(PluginManager pluginManager)
{
this.pluginManager = pluginManager;
this.injector = injector;
}
@GetMapping
public CommonResponse<Map<String, Set<String>>> getPlugins()
{
Map<String, Set<String>> plugins = Maps.newHashMap();
Set<String> executors = injector.getInstance(Key.get(new TypeLiteral<Set<ExecutorService>>() {}))
.stream()
.map(ExecutorService::name)
.collect(Collectors.toSet());
plugins.put("executor", executors);
Set<String> schedulers = injector.getInstance(Key.get(new TypeLiteral<Set<SchedulerService>>() {}))
.stream()
.map(SchedulerService::name)
.collect(Collectors.toSet());
plugins.put("scheduler", schedulers);
return CommonResponse.success(plugins);
}
@GetMapping(value = {"filter"})
public CommonResponse<List<PluginMetadata>> getPluginByType(@RequestParam String type)
public CommonResponse<List<PluginMetadata>> getPlugins()
{
return CommonResponse.success(pluginManager.getPluginInfos());
}
// @PostMapping(value = "install")
@PostMapping(value = "install")
public CommonResponse<Boolean> installPlugin(@RequestBody PluginInstallRequest request)
{
return CommonResponse.success(pluginManager.installPlugin(Path.of(request.url), request.name));
}
@Data
public static class PluginInstallRequest
{
private String url;
// 插件名称默认作为插件的安装目录
// Plugin name, default as the installation directory
private String name;
}
}

View File

@ -1,5 +1,6 @@
package io.edurt.datacap.service.audit;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.edurt.datacap.common.enums.State;
import io.edurt.datacap.common.response.CommonResponse;
import io.edurt.datacap.common.utils.CodeUtils;
@ -42,6 +43,7 @@ import static java.util.Objects.requireNonNull;
@Aspect
@Component
@Slf4j
@SuppressFBWarnings(value = {"EI_EXPOSE_REP2"})
public class AuditPluginHandler
{
private final ThreadLocal<PluginAuditEntity> threadLocalPluginAudit = new ThreadLocal<>();

View File

@ -85,7 +85,7 @@ import static java.util.Objects.requireNonNull;
@Service
@Slf4j
@SuppressFBWarnings(value = {"REC_CATCH_EXCEPTION", "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", "EI_EXPOSE_REP2"})
@SuppressFBWarnings(value = {"REC_CATCH_EXCEPTION", "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", "EI_EXPOSE_REP2", "DLS_DEAD_LOCAL_STORE"})
public class DataSetServiceImpl
implements DataSetService
{

View File

@ -1,5 +1,6 @@
package io.edurt.datacap.service.service.impl;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.edurt.datacap.common.enums.ServiceState;
import io.edurt.datacap.common.response.CommonResponse;
import io.edurt.datacap.common.sql.SqlBuilder;
@ -24,6 +25,7 @@ import java.io.File;
@Slf4j
@Service
@SuppressFBWarnings(value = {"EI_EXPOSE_REP2"})
public class ExecuteServiceImpl
implements ExecuteService
{

View File

@ -37,7 +37,7 @@ import java.util.stream.Collectors;
@Slf4j
@Service
@SuppressFBWarnings(value = {"DM_BOXED_PRIMITIVE_FOR_PARSING", "DM_DEFAULT_ENCODING"})
@SuppressFBWarnings(value = {"DM_BOXED_PRIMITIVE_FOR_PARSING", "DM_DEFAULT_ENCODING", "EI_EXPOSE_REP2"})
public class PluginAuditServiceImpl
implements PluginAuditService
{

View File

@ -3,7 +3,6 @@ package io.edurt.datacap.service.service.impl;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.Maps;
import com.google.common.io.Files;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.edurt.datacap.common.enums.NodeType;
import io.edurt.datacap.common.enums.ServiceState;
@ -20,7 +19,6 @@ import io.edurt.datacap.service.body.SourceBody;
import io.edurt.datacap.service.common.ConfigureUtils;
import io.edurt.datacap.service.common.PluginUtils;
import io.edurt.datacap.service.configure.IConfigure;
import io.edurt.datacap.service.configure.IConfigureField;
import io.edurt.datacap.service.entity.ColumnEntity;
import io.edurt.datacap.service.entity.DatabaseEntity;
import io.edurt.datacap.service.entity.PageEntity;
@ -70,7 +68,7 @@ import java.util.stream.Collectors;
@Slf4j
@Service
@SuppressFBWarnings(value = {"RV_RETURN_VALUE_IGNORED_BAD_PRACTICE", "REC_CATCH_EXCEPTION", "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE"})
@SuppressFBWarnings(value = {"RV_RETURN_VALUE_IGNORED_BAD_PRACTICE", "REC_CATCH_EXCEPTION", "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", "EI_EXPOSE_REP2"})
public class SourceServiceImpl
implements SourceService
{

View File

@ -1,6 +1,7 @@
package io.edurt.datacap.service.service.impl;
import com.google.common.collect.Lists;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.edurt.datacap.common.enums.ServiceState;
import io.edurt.datacap.common.response.CommonResponse;
import io.edurt.datacap.common.sql.SqlBuilder;
@ -65,6 +66,7 @@ import java.util.stream.Collectors;
@Slf4j
@Service
@SuppressFBWarnings(value = {"EI_EXPOSE_REP2"})
public class TableServiceImpl
implements TableService
{

View File

@ -24,7 +24,7 @@ import java.io.IOException;
@Slf4j
@Service
@SuppressFBWarnings(value = {"DLS_DEAD_LOCAL_STORE"})
@SuppressFBWarnings(value = {"DLS_DEAD_LOCAL_STORE", "EI_EXPOSE_REP2"})
public class UploadServiceImpl
implements UploadService
{

View File

@ -1,13 +0,0 @@
package io.edurt.datacap.spi;
import com.google.inject.AbstractModule;
import com.google.inject.multibindings.Multibinder;
import lombok.extern.slf4j.Slf4j;
import java.util.ServiceLoader;
@Slf4j
public class PluginLoader
extends AbstractModule
{
}

View File

@ -1,6 +1,5 @@
package io.edurt.datacap.spi.connection;
import com.google.common.base.Preconditions;
import io.edurt.datacap.plugin.PluginContextManager;
import io.edurt.datacap.plugin.loader.PluginClassLoader;
import io.edurt.datacap.spi.model.Response;
@ -83,7 +82,6 @@ public class JdbcConnection
protected java.sql.Connection openConnection()
{
JdbcConfigure jdbcConfigure = getJdbcConfigure();
Preconditions.checkNotNull(jdbcConfigure.getPlugin(), "Plugin cannot be null");
try {
PluginClassLoader pluginClassLoader = jdbcConfigure.getPlugin().getPluginClassLoader();
@ -140,7 +138,10 @@ public class JdbcConnection
{
private final Driver driver;
DriverShim(Driver d) {this.driver = d;}
DriverShim(Driver d)
{
this.driver = d;
}
public Connection connect(String url, Properties info)
throws SQLException
@ -160,11 +161,20 @@ public class JdbcConnection
return driver.getPropertyInfo(url, info);
}
public int getMajorVersion() {return driver.getMajorVersion();}
public int getMajorVersion()
{
return driver.getMajorVersion();
}
public int getMinorVersion() {return driver.getMinorVersion();}
public int getMinorVersion()
{
return driver.getMinorVersion();
}
public boolean jdbcCompliant() {return driver.jdbcCompliant();}
public boolean jdbcCompliant()
{
return driver.jdbcCompliant();
}
@Override
public Logger getParentLogger()

View File

@ -18,7 +18,8 @@ import java.util.Optional;
@ToString
@NoArgsConstructor
@AllArgsConstructor
@SuppressFBWarnings(value = {"EI_EXPOSE_REP", "EI_EXPOSE_REP2"})
@SuppressFBWarnings(value = {"EI_EXPOSE_REP", "EI_EXPOSE_REP2", "RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE", "CT_CONSTRUCTOR_THROW",
"NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR"})
public class Configure
{
@NonNull

View File

@ -1,46 +0,0 @@
package io.edurt.datacap.spi.connection;
import io.edurt.datacap.spi.model.Response;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
public class JdbcConnectionTest
{
private JdbcConfigure jdbcConfigure;
private Response response;
@Before
public void before()
{
this.jdbcConfigure = new JdbcConfigure();
this.jdbcConfigure.setJdbcType("datacap");
this.jdbcConfigure.setJdbcDriver("io.edurt.datacap.DataCapDriver");
this.jdbcConfigure.setHost("127.0.0.1");
this.jdbcConfigure.setPort(9096);
this.response = new Response();
}
@Test
public void testFormatJdbcUrl()
{
Connection connection = new JdbcConnection(this.jdbcConfigure, this.response);
Assert.assertEquals(connection.formatJdbcUrl(), "jdbc:datacap://127.0.0.1:9096");
this.jdbcConfigure.setSsl(Optional.ofNullable(true));
connection = new JdbcConnection(this.jdbcConfigure, this.response);
Assert.assertEquals(connection.formatJdbcUrl(), "jdbc:datacap://127.0.0.1:9096?ssl=true");
Map<String, Object> env = new HashMap<>();
env.put("useUnicode", "true");
env.put("zeroDateTimeBehavior", "convertToNull");
this.jdbcConfigure.setEnv(Optional.ofNullable(env));
connection = new JdbcConnection(this.jdbcConfigure, this.response);
Assert.assertEquals(connection.formatJdbcUrl(), "jdbc:datacap://127.0.0.1:9096?ssl=true&useUnicode=true&zeroDateTimeBehavior=convertToNull");
}
}

View File

@ -1,28 +0,0 @@
package io.edurt.datacap.spi.connection.http;
import io.edurt.datacap.spi.connection.HttpConfigure;
import io.edurt.datacap.spi.connection.HttpConnection;
import io.edurt.datacap.spi.model.Response;
import org.junit.Assert;
import org.junit.Test;
public class HttpClientTest
{
@Test
public void test()
{
HttpConfigure httpConfigure = new HttpConfigure();
httpConfigure.setAutoConnected(Boolean.FALSE);
httpConfigure.setRetry(0);
httpConfigure.setParams(null);
httpConfigure.setProtocol("https");
httpConfigure.setHost("datacap.incubator.edurt.io");
httpConfigure.setPort(443);
httpConfigure.setMethod(HttpMethod.GET);
httpConfigure.setPath("blog/index.html");
HttpConnection httpConnection = new HttpConnection(httpConfigure, new Response());
HttpClient httpClient = HttpClient.getInstance(httpConfigure, httpConnection);
String response = httpClient.execute();
Assert.assertNotNull(response);
}
}

View File

@ -9,11 +9,6 @@ class PluginService
{
return new HttpUtils().get(`${ DEFAULT_PATH }`)
}
filterByType(type: string): Promise<ResponseModel>
{
return new HttpUtils().get(`${ DEFAULT_PATH }/filter`, { type })
}
}
export default new PluginService()

View File

@ -172,7 +172,7 @@ const loadMetadata = async () => {
const data = await response.json()
metadata.value = data
const installResponse = await PluginService.filterByType('plugin')
const installResponse = await PluginService.getPlugins()
if (!installResponse.status) {
// @ts-ignore
proxy?.$Message.error({ content: response.message, showIcon: true })

View File

@ -0,0 +1,9 @@
---
title: 安装插件
---
请求地址:`/api/v1/plugin/install`
请求方式:`POST`
## Request

View File

@ -0,0 +1,37 @@
---
title: 插件列表
---
请求地址:`/api/v1/plugin`
请求方式:`GET`
## Response
=== "示例"
```json
[
{
"name": "TxtConvert",
"version": "2024.4.0-SNAPSHOT",
"state": "CREATED",
"loadTimestamp": 1732353263492,
"loadTime": "2024-11-23 17:14:23",
"type": "CONVERT",
"loaderName": "io.edurt.datacap.plugin.loader.PropertiesPluginLoader"
}
]
```
=== "参数"
| 名称 | 类型 | 是否必须 | 描述 |
| --- | --- | --- | --- |
| `name` | String | 是 | 插件名称 |
| `version` | String | 是 | 插件版本 |
| `state` | String | 是 | 插件状态 |
| `loadTimestamp` | Long | 是 | 插件加载时间戳 |
| `loadTime` | String | 是 | 插件加载时间 |
| `type` | String | 是 | 插件类型 |
| `loaderName` | String | 是 | 插件加载器名称 |

View File

@ -131,6 +131,7 @@ plugins:
NavDeploy: 安装部署
ApiHome: 开放 API
ApiUser: 用户 API
ApiPlugin: 插件 API
- locale: en
name: English
build: true
@ -152,6 +153,7 @@ plugins:
NavDeploy: Install Deploy
ApiHome: Open API
ApiUser: User API
ApiPlugin: Plugin API
- search
- git-revision-date-localized:
enable_creation_date: true
@ -284,5 +286,8 @@ nav:
- ApiUser:
- api/user/register.md
- api/user/login.md
- ApiPlugin:
- api/plugin/plugin.md
- api/plugin/install.md
- useCases.md
- partners.md

View File

@ -1,30 +0,0 @@
package io.edurt.datacap.lib.http;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class HttpClientTest
{
private HttpConfigure configure;
@Before
public void before()
{
configure = new HttpConfigure();
configure.setAutoConnected(Boolean.FALSE);
configure.setRetry(0);
configure.setParams(null);
configure.setProtocol("https");
configure.setHost("datacap.edurt.io");
configure.setPort(443);
configure.setMethod(HttpMethod.GET);
}
@Test
public void testExecute()
{
HttpClient client = new HttpClient(this.configure);
Assert.assertNotNull(client.execute());
}
}

View File

@ -1,39 +0,0 @@
package io.edurt.datacap.lib.logger.logback;
import io.edurt.datacap.lib.logger.LoggerExecutor;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
public class LogbackExecutorTest
{
private final String directory = System.getProperty("user.dir");
private final String name = this.getClass().getName() + ".log";
private LoggerExecutor loggerExecutor;
private Logger logger;
@Before
public void before()
{
this.loggerExecutor = new LogbackExecutor(this.directory, this.name);
}
@Test
public void getLogger()
{
logger = (Logger) loggerExecutor.getLogger();
logger.info("info");
logger.debug("debug");
logger.error("error");
logger.warn("warn");
logger.trace("trace");
}
@Test
public void destroy()
{
loggerExecutor.destroy();
Assert.assertTrue(logger == null);
}
}

View File

@ -104,6 +104,7 @@
<module>test/datacap-test-plugin</module>
<module>test/datacap-test-convert</module>
<module>test/datacap-test-core</module>
<module>test/datacap-test-lib</module>
</modules>
<name>datacap</name>

View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.edurt.datacap</groupId>
<artifactId>datacap</artifactId>
<version>2024.4.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<artifactId>datacap-test-lib</artifactId>
<description>DataCap - Test - Lib</description>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.edurt.datacap</groupId>
<artifactId>datacap-http</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.edurt.datacap</groupId>
<artifactId>datacap-logger</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<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>

View File

@ -0,0 +1,30 @@
package io.edurt.datacap.test.lib.http
import io.edurt.datacap.lib.http.HttpClient
import io.edurt.datacap.lib.http.HttpConfigure
import io.edurt.datacap.lib.http.HttpMethod
import org.junit.Assert.assertNotNull
import org.junit.Test
class HttpClientTest
{
private val configure: HttpConfigure = HttpConfigure()
init
{
configure.autoConnected = java.lang.Boolean.FALSE
configure.retry = 0
configure.params = null
configure.protocol = "https"
configure.host = "datacap.devlive.org"
configure.port = 443
configure.method = HttpMethod.GET
}
@Test
fun test()
{
val client: HttpClient = HttpClient(this.configure)
assertNotNull(client.execute())
}
}

View File

@ -0,0 +1,31 @@
package io.edurt.datacap.test.lib.logger
import io.edurt.datacap.lib.logger.logback.LogbackExecutor
import org.junit.Test
import org.slf4j.Logger
import kotlin.test.assertNotNull
class LogbackExecutorTest
{
private val directory: String = System.getProperty("user.dir")
private val name = javaClass.name + ".log"
private val loggerExecutor: LogbackExecutor = LogbackExecutor(directory, name)
private var logger: Logger = loggerExecutor.getLogger()
@Test
fun getLogger()
{
logger.info("info")
logger.debug("debug")
logger.error("error")
logger.warn("warn")
logger.trace("trace")
}
@Test
fun destroy()
{
loggerExecutor.destroy()
assertNotNull(logger)
}
}