New feature (#775)

This commit is contained in:
qianmoQ 2024-05-23 23:25:58 +08:00 committed by GitHub
commit 0bb96c4d62
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
32 changed files with 603 additions and 12 deletions

View File

@ -0,0 +1,14 @@
package io.edurt.datacap.common.response;
import lombok.Builder;
import lombok.Data;
import lombok.ToString;
@Data
@Builder
@ToString
public class SignResponse
{
private Long timestamp;
private String sign;
}

View File

@ -0,0 +1,39 @@
package io.edurt.datacap.common.utils;
import io.edurt.datacap.common.response.SignResponse;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class SignUtils
{
private SignUtils()
{}
/**
* Generates a signature using the provided secret.
*
* @param secret the secret key used for signing
* @return the SignResponse object containing the generated signature and timestamp
* @throws Exception if an error occurs during the signing process
*/
public static SignResponse sign(String secret)
throws Exception
{
Long timestamp = System.currentTimeMillis();
String stringToSign = timestamp + "\n" + secret;
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
Base64.Encoder encoder = Base64.getEncoder();
String sign = URLEncoder.encode(encoder.encodeToString(signData), "UTF-8");
return SignResponse.builder()
.timestamp(timestamp)
.sign(sign)
.build();
}
}

View File

@ -0,0 +1,15 @@
package io.edurt.datacap.common.utils;
import org.junit.Assert;
import org.junit.Test;
public class SignUtilsTest
{
@Test
public void testSign()
throws Exception
{
Assert.assertNotNull(SignUtils.sign("SSSS")
.getSign());
}
}

View File

@ -404,6 +404,12 @@
<artifactId>datacap-fs-alioss</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Notify -->
<dependency>
<groupId>io.edurt.datacap</groupId>
<artifactId>datacap-notify-dingtalk</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>

View File

@ -4,6 +4,7 @@ import com.google.inject.Guice;
import com.google.inject.Injector;
import io.edurt.datacap.executor.ExecutorManager;
import io.edurt.datacap.fs.FsManager;
import io.edurt.datacap.notify.NotifyManager;
import io.edurt.datacap.parser.ParserManager;
import io.edurt.datacap.scheduler.ScheduleManager;
import io.edurt.datacap.spi.PluginLoader;
@ -20,6 +21,7 @@ public class PluginConfigure
new FsManager(),
new ParserManager(),
new ScheduleManager(),
new ExecutorManager());
new ExecutorManager(),
new NotifyManager());
}
}

View File

@ -143,7 +143,10 @@ export default defineComponent({
captchaImage.value = response.data.image
}
})
.finally(() => captchaLoading.value = false)
.finally(() => {
captchaLoading.value = false
loading.value = false
})
}
const onSubmit = handleSubmit(() => {
@ -198,6 +201,7 @@ export default defineComponent({
methods: {
handlerInitialize()
{
this.loading = true
this.refererCaptcha()
}
}

View File

@ -157,7 +157,10 @@ export default defineComponent({
captchaImage.value = response.data.image
}
})
.finally(() => captchaLoading.value = false)
.finally(() => {
captchaLoading.value = false
loading.value = false
})
}
const onSubmit = handleSubmit(() => {
@ -193,6 +196,7 @@ export default defineComponent({
methods: {
handlerInitialize()
{
this.loading = true
this.refererCaptcha()
}
}

View File

@ -1,6 +1,7 @@
package io.edurt.datacap.lib.http;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import lombok.extern.slf4j.Slf4j;
import okhttp3.ConnectionPool;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
@ -13,7 +14,8 @@ import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
@SuppressFBWarnings(value = {"NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE"},
@Slf4j
@SuppressFBWarnings(value = {"NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", "RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE"},
justification = "I prefer to suppress these FindBugs warnings")
public class HttpClient
{
@ -66,25 +68,31 @@ public class HttpClient
builder.addQueryParameter(key, configure.getParams().get(key));
}
}
log.info("Request url {}", builder.build());
Request request = new Request.Builder()
.addHeader("Accept-Encoding", "identity")
.url(builder.build().toString()).build();
.url(builder.build().toString())
.build();
return execute(request);
}
private String post()
{
RequestBody requestBody = RequestBody.create(configure.getMediaType(), configure.getBody());
HttpUrl.Builder builder = HttpUrl.parse(this.formatJdbcUrl()).newBuilder();
HttpUrl.Builder builder = HttpUrl.parse(this.formatJdbcUrl())
.newBuilder();
// Adding Path Parameters
if (ObjectUtils.isNotEmpty(configure.getParams())) {
for (String key : configure.getParams().keySet()) {
builder.addQueryParameter(key, configure.getParams().get(key));
builder.addEncodedQueryParameter(key, configure.getParams()
.get(key));
}
}
Request request = new Request.Builder().post(requestBody)
log.info("Request url {}", builder.build());
Request request = new Request.Builder()
.post(requestBody)
.addHeader("Accept-Encoding", "identity")
.url(builder.build().toString()).build();
return execute(request);
@ -93,12 +101,13 @@ public class HttpClient
private String execute(Request request)
{
String responseBody = null;
try {
Response response = okHttpClient.newCall(request).execute();
responseBody = response.body().string();
try (Response response = okHttpClient.newCall(request)
.execute()) {
responseBody = response.body()
.string();
}
catch (IOException | NullPointerException e) {
e.printStackTrace();
log.warn("Request error", e);
}
return responseBody;
}

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>io.edurt.datacap</groupId>
<artifactId>datacap</artifactId>
<version>2024.03.5-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>datacap-notify-dingtalk</artifactId>
<description>DataCap - Parser for dingtalk</description>
<dependencies>
<dependency>
<groupId>io.edurt.datacap</groupId>
<artifactId>datacap-notify-spi</artifactId>
</dependency>
<dependency>
<groupId>io.edurt.datacap</groupId>
<artifactId>datacap-http</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.jetbrains.dokka</groupId>
<artifactId>dokka-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,15 @@
package io.edurt.datacap.notify.dingtalk
import com.google.inject.multibindings.Multibinder
import io.edurt.datacap.notify.Notify
import io.edurt.datacap.notify.NotifyModule
class DingTalkModule : NotifyModule()
{
override fun configure()
{
Multibinder.newSetBinder(this.binder(), Notify::class.java)
.addBinding()
.to(DingTalkNotify::class.java)
}
}

View File

@ -0,0 +1,13 @@
package io.edurt.datacap.notify.dingtalk
import io.edurt.datacap.notify.Notify
import io.edurt.datacap.notify.model.NotifyRequest
import io.edurt.datacap.notify.model.NotifyResponse
class DingTalkNotify : Notify
{
override fun send(request: NotifyRequest): NotifyResponse
{
return DingTalkUtils.send(request)
}
}

View File

@ -0,0 +1,76 @@
package io.edurt.datacap.notify.dingtalk
import io.edurt.datacap.common.utils.JsonUtils
import io.edurt.datacap.common.utils.SignUtils
import io.edurt.datacap.lib.http.HttpClient
import io.edurt.datacap.lib.http.HttpConfigure
import io.edurt.datacap.lib.http.HttpMethod
import io.edurt.datacap.notify.NotifyType
import io.edurt.datacap.notify.dingtalk.model.ReturnModel
import io.edurt.datacap.notify.dingtalk.model.TextModel
import io.edurt.datacap.notify.model.NotifyRequest
import io.edurt.datacap.notify.model.NotifyResponse
import org.apache.commons.lang3.StringUtils.isNotEmpty
import org.slf4j.Logger
import org.slf4j.LoggerFactory.getLogger
object DingTalkUtils
{
private val log: Logger = getLogger(DingTalkUtils::class.java)
@JvmStatic
fun send(request: NotifyRequest): NotifyResponse
{
val configure = HttpConfigure()
configure.autoConnected = false
configure.retry = 0
configure.protocol = "https"
configure.host = "oapi.dingtalk.com"
configure.port = 443
configure.path = "robot/send"
configure.method = HttpMethod.POST
val params = mutableMapOf("access_token" to request.access)
if (isNotEmpty(request.secret))
{
val signResponse = SignUtils.sign(request.secret)
log.info("Sign response: ${JsonUtils.toJSON(signResponse)}")
params["sign"] = signResponse.sign
params["timestamp"] = signResponse.timestamp.toString()
}
configure.params = params
log.info("Notify request params: ${JsonUtils.toJSON(params)}")
val text = TextModel()
text.content = request.content
configure.body = JsonUtils.toJSON(mapOf("text" to text, "msgtype" to formatMessageType(request)))
log.info("Notify request body: ${configure.body}")
val client = HttpClient(configure)
val returnModel = JsonUtils.toObject(client.execute(), ReturnModel::class.java)
val response = NotifyResponse()
if (returnModel.code == 0)
{
response.successful = true
response.message = null
}
else
{
response.successful = false
response.message = returnModel.message
}
return response
}
private fun formatMessageType(request: NotifyRequest): String
{
if (request.type == NotifyType.TEXT)
{
return "text"
}
else
{
return "markdown"
}
}
}

View File

@ -0,0 +1,12 @@
package io.edurt.datacap.notify.dingtalk.model
import com.fasterxml.jackson.annotation.JsonProperty
class ReturnModel
{
@JsonProperty(value = "errcode")
var code: Number? = null
@JsonProperty(value = "errmsg")
var message: String? = null
}

View File

@ -0,0 +1,6 @@
package io.edurt.datacap.notify.dingtalk.model
class TextModel
{
var content: String? = null
}

View File

@ -0,0 +1 @@
io.edurt.datacap.notify.dingtalk.DingTalkModule

View File

@ -0,0 +1,32 @@
package io.edurt.datacap.notify.dingtalk
import com.google.inject.Guice
import com.google.inject.Injector
import com.google.inject.Key
import com.google.inject.TypeLiteral
import io.edurt.datacap.notify.Notify
import io.edurt.datacap.notify.NotifyManager
import org.junit.Assert.assertNotNull
import org.junit.Before
import org.junit.Test
class DingTalkModuleTest
{
private val name = "DingTalk"
private var injector: Injector? = null
@Before
fun before()
{
injector = Guice.createInjector(NotifyManager())
}
@Test
fun test()
{
val notify: Notify? = injector?.getInstance(Key.get(object : TypeLiteral<Set<Notify>>()
{}))
?.first { it.name() == name }
assertNotNull(notify)
}
}

View File

@ -0,0 +1,38 @@
package io.edurt.datacap.notify.dingtalk
import com.google.inject.Guice
import com.google.inject.Injector
import com.google.inject.Key
import com.google.inject.TypeLiteral
import io.edurt.datacap.notify.Notify
import io.edurt.datacap.notify.NotifyManager
import io.edurt.datacap.notify.model.NotifyRequest
import org.junit.Assert.assertNotNull
import org.junit.Before
import org.junit.Test
class DingTalkNotifyTest
{
private val name = "DingTalk"
private var injector: Injector? = null
private val request: NotifyRequest = NotifyRequest()
@Before
fun before()
{
injector = Guice.createInjector(NotifyManager())
request.access = "ACCESS"
request.content = "Test Message"
request.secret = "SECRET"
}
@Test
fun test()
{
val notify: Notify? = injector?.getInstance(Key.get(object : TypeLiteral<Set<Notify>>()
{}))
?.first { it.name() == name }
assertNotNull(notify?.send(request))
}
}

View File

@ -0,0 +1,28 @@
package io.edurt.datacap.notify.dingtalk
import io.edurt.datacap.notify.model.NotifyRequest
import org.junit.Assert
import org.junit.Before
import org.junit.Test
class DingTalkUtilsTest
{
private val request: NotifyRequest = NotifyRequest()
@Before
fun before()
{
request.access = "ACCESS"
request.content = "Test Message"
request.secret = "SECRET"
}
@Test
fun testSend()
{
Assert.assertFalse(
DingTalkUtils.send(request)
.successful
)
}
}

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>io.edurt.datacap</groupId>
<artifactId>datacap</artifactId>
<version>2024.03.5-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>datacap-notify-spi</artifactId>
<description>DataCap - Notify spi</description>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
</dependency>
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.jetbrains.dokka</groupId>
<artifactId>dokka-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,21 @@
package io.edurt.datacap.notify
import io.edurt.datacap.notify.model.NotifyRequest
import io.edurt.datacap.notify.model.NotifyResponse
interface Notify
{
fun name(): String
{
return this.javaClass
.simpleName
.removeSuffix("Notify")
}
fun description(): String
{
return "Integrate ${name()} notify"
}
fun send(request: NotifyRequest): NotifyResponse
}

View File

@ -0,0 +1,26 @@
package io.edurt.datacap.notify
import com.google.inject.Injector
import com.google.inject.Key
import com.google.inject.TypeLiteral
import java.util.*
object NotifyFilter
{
/**
* Finds a Notify object by name using the given injector.
*
* @param injector the Guice injector used for dependency injection
* @param name the name of the Notify object to find
* @return an Optional containing the found Notify object, or an empty Optional if not found
*/
@JvmStatic
fun findNotify(injector: Injector, name: String): Optional<Notify>
{
return injector.getInstance(Key.get(object : TypeLiteral<Set<Notify>>()
{}))
.stream()
.filter { it.name() == name }
.findFirst()
}
}

View File

@ -0,0 +1,22 @@
package io.edurt.datacap.notify
import com.google.inject.AbstractModule
import org.slf4j.Logger
import org.slf4j.LoggerFactory.getLogger
import java.util.*
class NotifyManager : AbstractModule()
{
private val log: Logger = getLogger(NotifyManager::class.java)
private var externalModules: Iterable<NotifyModule> = ServiceLoader.load(NotifyModule::class.java)
override fun configure()
{
log.info("========== Loading notify start ==========")
externalModules.forEach { module ->
log.info("Installing notify [ {} ] join time [ {} ]", module.name(), System.currentTimeMillis())
install(module)
}
log.info("========== Loading notify end ==========")
}
}

View File

@ -0,0 +1,11 @@
package io.edurt.datacap.notify
import com.google.inject.AbstractModule
abstract class NotifyModule : AbstractModule()
{
fun name(): String = this.javaClass
.simpleName
.removeSuffix("Notify")
.removeSuffix("Module")
}

View File

@ -0,0 +1,6 @@
package io.edurt.datacap.notify
enum class NotifyType
{
TEXT
}

View File

@ -0,0 +1,13 @@
package io.edurt.datacap.notify.model
import io.edurt.datacap.notify.NotifyType
class NotifyRequest
{
var title: String? = null
var content: String? = null
var sendTo: String? = null
var access: String? = null
var secret: String? = null
var type: NotifyType = NotifyType.TEXT
}

View File

@ -0,0 +1,12 @@
package io.edurt.datacap.notify.model
class NotifyResponse
{
var successful: Boolean = false
var message: String? = null
override fun toString(): String
{
return "NotifyResponse(successful=$successful, message=$message)"
}
}

View File

@ -0,0 +1,16 @@
package io.edurt.datacap.notify
import com.google.inject.Guice
import org.junit.Assert
import org.junit.Test
class NotifyFilterTest
{
private val injector = Guice.createInjector(NotifyManager())
@Test
fun testFindNotify()
{
Assert.assertNotNull(NotifyFilter.findNotify(injector, "Test"))
}
}

View File

@ -0,0 +1,36 @@
package io.edurt.datacap.notify
import com.google.inject.Guice
import com.google.inject.Injector
import com.google.inject.Key
import com.google.inject.TypeLiteral
import io.edurt.datacap.notify.model.NotifyRequest
import org.junit.Assert
import org.junit.Test
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.util.*
class NotifyModuleTest
{
private val log: Logger = LoggerFactory.getLogger(TestNotify::class.java)
private val injector: Injector = Guice.createInjector(NotifyManager())
@Test
fun test()
{
val optional: Optional<Notify> = injector.getInstance(Key.get(object : TypeLiteral<Set<Notify>>()
{}))
.stream()
.findFirst()
optional.ifPresent {
val request = NotifyRequest()
request.title = "This is title"
request.content = "This is content"
request.sendTo = "community@devlive.org"
log.info("Notify response: {}", it.send(request))
Assert.assertEquals("Test", it.name())
}
}
}

View File

@ -0,0 +1,13 @@
package io.edurt.datacap.notify
import io.edurt.datacap.notify.model.NotifyRequest
import io.edurt.datacap.notify.model.NotifyResponse
class TestNotify : Notify
{
override fun send(request: NotifyRequest): NotifyResponse
{
val response = NotifyResponse()
return response
}
}

View File

@ -0,0 +1,13 @@
package io.edurt.datacap.notify
import com.google.inject.multibindings.Multibinder
class TestNotifyModule : NotifyModule()
{
override fun configure()
{
Multibinder.newSetBinder(this.binder(), Notify::class.java)
.addBinding()
.to(TestNotify::class.java)
}
}

View File

@ -0,0 +1 @@
io.edurt.datacap.notify.TestNotifyModule

12
pom.xml
View File

@ -87,6 +87,8 @@
<module>parser/datacap-parser-mysql</module>
<module>scheduler/datacap-scheduler-spi</module>
<module>scheduler/datacap-scheduler-local</module>
<module>notify/datacap-notify-spi</module>
<module>notify/datacap-notify-dingtalk</module>
</modules>
<name>datacap</name>
@ -215,6 +217,11 @@
<artifactId>datacap-common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.edurt.datacap</groupId>
<artifactId>datacap-http</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
@ -305,6 +312,11 @@
<artifactId>datacap-parser-spi</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.edurt.datacap</groupId>
<artifactId>datacap-notify-spi</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>