chore: import upstream snapshot with attribution
MCP Bash Server CI / Test MCP Bash Server (dev) (push) Has been cancelled
MCP Bash Server CI / Test MCP Bash Server (release) (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:11:39 +08:00
commit c8cebdfeee
4654 changed files with 626756 additions and 0 deletions
+73
View File
@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Licensed to the Apache Software Foundation (ASF) under one or more
~ contributor license agreements. See the NOTICE file distributed with
~ this work for additional information regarding copyright ownership.
~ The ASF licenses this file to You under the Apache License, Version 2.0
~ (the "License"); you may not use this file except in compliance with
~ the License. You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<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">
<parent>
<artifactId>hertzbeat</artifactId>
<groupId>org.apache.hertzbeat</groupId>
<version>2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>hertzbeat-common-spring</artifactId>
<name>${project.artifactId}</name>
<description>Spring Boot, configuration, validation, and JPA integration built on common-core.</description>
<dependencies>
<!-- hertzbeat-common-core dependency -->
<dependency>
<groupId>org.apache.hertzbeat</groupId>
<artifactId>hertzbeat-common-core</artifactId>
</dependency>
<!-- spring -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<!-- jpa -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- validation -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- swagger -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,139 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.cache;
import java.time.Duration;
import java.util.List;
import org.apache.hertzbeat.common.constants.CommonConstants;
import org.apache.hertzbeat.common.entity.alerter.AlertDefine;
import org.apache.hertzbeat.common.entity.alerter.AlertSilence;
import org.apache.hertzbeat.common.entity.alerter.NoticeRule;
/**
* common cache factory
*/
public final class CacheFactory {
private CacheFactory() {}
private static final CommonCacheService<String, Object> COMMON_CACHE =
new CaffeineCacheServiceImpl<>(1, 1000, Duration.ofDays(1), false);
/**
* get notice cache
* @return caffeine cache
*/
@SuppressWarnings("unchecked")
public static List<NoticeRule> getNoticeCache() {
return (List<NoticeRule>) COMMON_CACHE.get(CommonConstants.CACHE_NOTICE_RULE);
}
/**
* set notice cache
* @param noticeRules notice rules
*/
public static void setNoticeCache(List<NoticeRule> noticeRules) {
COMMON_CACHE.put(CommonConstants.CACHE_NOTICE_RULE, noticeRules);
}
/**
* clear notice cache
*/
public static void clearNoticeCache() {
COMMON_CACHE.remove(CommonConstants.CACHE_NOTICE_RULE);
}
/**
* get alert silence cache
* @return caffeine cache
*/
@SuppressWarnings("unchecked")
public static List<AlertSilence> getAlertSilenceCache() {
return (List<AlertSilence>) COMMON_CACHE.get(CommonConstants.CACHE_ALERT_SILENCE);
}
/**
* set alert silence cache
* @param alertSilences alert silences
*/
public static void setAlertSilenceCache(List<AlertSilence> alertSilences) {
COMMON_CACHE.put(CommonConstants.CACHE_ALERT_SILENCE, alertSilences);
}
/**
* clear alert silence cache
*/
public static void clearAlertSilenceCache() {
COMMON_CACHE.remove(CommonConstants.CACHE_ALERT_SILENCE);
}
/**
* get metrics alert define cache
* @return caffeine cache
*/
@SuppressWarnings("unchecked")
public static List<AlertDefine> getMetricsAlertDefineCache() {
return (List<AlertDefine>) COMMON_CACHE.get(CommonConstants.METRIC_CACHE_ALERT_DEFINE);
}
/**
* set metrics alert define cache
* @param alertDefines alert defines
*/
public static void setMetricsAlertDefineCache(List<AlertDefine> alertDefines) {
COMMON_CACHE.put(CommonConstants.METRIC_CACHE_ALERT_DEFINE, alertDefines);
}
/**
* clear metrics alert define cache
*/
public static void clearMetricsAlertDefineCache() {
COMMON_CACHE.remove(CommonConstants.METRIC_CACHE_ALERT_DEFINE);
}
/**
* get log alert define cache
* @return caffeine cache
*/
@SuppressWarnings("unchecked")
public static List<AlertDefine> getLogAlertDefineCache() {
return (List<AlertDefine>) COMMON_CACHE.get(CommonConstants.LOG_CACHE_ALERT_DEFINE);
}
/**
* set log alert define cache
* @param alertDefines alert defines
*/
public static void setLogAlertDefineCache(List<AlertDefine> alertDefines) {
COMMON_CACHE.put(CommonConstants.LOG_CACHE_ALERT_DEFINE, alertDefines);
}
/**
* clear log alert define cache
*/
public static void clearLogAlertDefineCache() {
COMMON_CACHE.remove(CommonConstants.LOG_CACHE_ALERT_DEFINE);
}
/**
* clear alert define cache
*/
public static void clearAlertDefineCache() {
clearLogAlertDefineCache();
clearMetricsAlertDefineCache();
}
}
@@ -0,0 +1,83 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.cache;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import java.time.Duration;
/**
* caffeine cache impl
*/
public class CaffeineCacheServiceImpl<K, V> implements CommonCacheService<K, V> {
private final Cache<K, V> cache;
public CaffeineCacheServiceImpl(final int initialCapacity, final long maximumSize, final Duration expireAfterWrite, final boolean useWeakKey) {
if (useWeakKey) {
this.cache = Caffeine.newBuilder()
.weakKeys()
.initialCapacity(initialCapacity)
.maximumSize(maximumSize)
.expireAfterWrite(expireAfterWrite)
.build();
} else {
this.cache = Caffeine.newBuilder()
.initialCapacity(initialCapacity)
.maximumSize(maximumSize)
.expireAfterWrite(expireAfterWrite)
.build();
}
}
@Override
public V get(K key) {
return cache.getIfPresent(key);
}
@Override
public void put(K key, V value) {
cache.put(key, value);
}
@Override
public V putAndGetOld(K key, V value) {
V oldValue = cache.getIfPresent(key);
cache.put(key, value);
return oldValue;
}
@Override
public boolean containsKey(K key) {
return cache.asMap().containsKey(key);
}
@Override
public V remove(K key) {
V value = cache.getIfPresent(key);
this.cache.invalidate(key);
this.cache.cleanUp();
return value;
}
@Override
public boolean clear() {
this.cache.invalidateAll();
this.cache.cleanUp();
return true;
}
}
@@ -0,0 +1,42 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.config;
import org.apache.hertzbeat.common.constants.ConfigConstants;
import org.apache.hertzbeat.common.constants.SignConstants;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
/**
* common module config
*/
@AutoConfiguration
@ComponentScan(basePackages = ConfigConstants.PkgConstant.PKG
+ SignConstants.DOT
+ ConfigConstants.FunctionModuleConstants.COMMON)
@EnableConfigurationProperties({CommonProperties.class, VirtualThreadPropertiesBinding.class, SmsConfigBinding.class})
public class CommonConfig {
@Bean
public VirtualThreadProperties virtualThreadProperties(VirtualThreadPropertiesBinding binding) {
return binding.toRuntimeProperties();
}
}
@@ -0,0 +1,167 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.config;
import lombok.Getter;
import lombok.Setter;
import org.apache.hertzbeat.common.constants.ConfigConstants;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* common module properties
*/
@Getter
@Setter
@ConfigurationProperties(prefix =
ConfigConstants.FunctionModuleConstants.COMMON)
public class CommonProperties {
/**
* secret key for password aes entry, must 16 bits
*/
private String secret;
/**
* data queue impl
*/
private DataQueueProperties queue;
/**
* data queue properties
*/
@Getter
@Setter
public static class DataQueueProperties {
private QueueType type = QueueType.Memory;
private KafkaProperties kafka;
private RedisProperties redis;
}
/**
* data queue type
*/
public enum QueueType {
/** in memory **/
Memory,
/** kafka **/
Kafka,
/** with netty connect **/
Netty,
/** rabbit mq **/
Rabbit_Mq,
/** redis **/
Redis
}
/**
* redis data queue properties
*/
@Getter
@Setter
public static class RedisProperties {
/**
* redis server host.
*/
private String redisHost;
/**
* redis server port.
*/
private int redisPort;
/**
* Queue name for metrics data to alerter
*/
private String metricsDataQueueNameToAlerter;
/**
* Queue name for metrics data to persistent storage
*/
private String metricsDataQueueNameToPersistentStorage;
/**
* Queue name for metrics data to real-time storage
*/
private String metricsDataQueueNameToRealTimeStorage;
/**
* Queue name for service discovery
*/
private String metricsDataQueueNameForServiceDiscovery;
/**
* Queue name for alerts data
*/
private String alertsDataQueueName;
/**
* Queue name for log entry data
*/
private String logEntryQueueName;
/**
* Queue name for log entry data to storage
*/
private String logEntryToStorageQueueName;
/**
* Timeout for blocking wait in seconds (defaults to 1 second if not configured)
*/
private Long waitTimeout;
}
/**
* kafka data queue properties
*/
@Getter
@Setter
public static class KafkaProperties extends BaseKafkaProperties {
/**
* metrics data topic
*/
private String metricsDataTopic;
/**
* metrics data to storage topic
*/
private String metricsDataToStorageTopic;
/**
* service discovery data topic
*/
private String serviceDiscoveryDataTopic;
/**
* alerts data topic
*/
private String alertsDataTopic;
/**
* log entry data topic
*/
private String logEntryDataTopic;
/**
* log entry data to storage topic
*/
private String logEntryDataToStorageTopic;
}
}
@@ -0,0 +1,28 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.config;
import org.apache.hertzbeat.common.entity.dto.sms.SmsConfig;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Spring Boot binding adapter for {@link SmsConfig}.
*/
@ConfigurationProperties(prefix = "alerter.sms")
public class SmsConfigBinding extends SmsConfig {
}
@@ -0,0 +1,177 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.config;
import org.apache.hertzbeat.common.concurrent.AdmissionMode;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.ConstructorBinding;
import org.springframework.boot.context.properties.bind.DefaultValue;
import org.springframework.boot.context.properties.bind.Name;
/**
* Spring Boot binding adapter for {@link VirtualThreadProperties}.
*/
@ConfigurationProperties(prefix = "hertzbeat.vthreads")
public record VirtualThreadPropertiesBinding(
@DefaultValue("true") boolean enabled,
PoolProperties collector,
PoolProperties common,
PoolProperties manager,
AlerterProperties alerter,
PoolProperties warehouse,
AsyncProperties async) {
@ConstructorBinding
public VirtualThreadPropertiesBinding {
VirtualThreadProperties runtimeProperties = new VirtualThreadProperties(enabled,
toRuntimePool(collector),
toRuntimePool(common),
toRuntimePool(manager),
toRuntimeAlerter(alerter),
toRuntimePool(warehouse),
toRuntimeAsync(async));
enabled = runtimeProperties.enabled();
collector = PoolProperties.fromRuntime(runtimeProperties.collector());
common = PoolProperties.fromRuntime(runtimeProperties.common());
manager = PoolProperties.fromRuntime(runtimeProperties.manager());
alerter = AlerterProperties.fromRuntime(runtimeProperties.alerter());
warehouse = PoolProperties.fromRuntime(runtimeProperties.warehouse());
async = AsyncProperties.fromRuntime(runtimeProperties.async());
}
public VirtualThreadProperties toRuntimeProperties() {
return new VirtualThreadProperties(enabled,
toRuntimePool(collector),
toRuntimePool(common),
toRuntimePool(manager),
toRuntimeAlerter(alerter),
toRuntimePool(warehouse),
toRuntimeAsync(async));
}
/**
* Pool-level binding model.
*/
public record PoolProperties(
@DefaultValue("UNBOUNDED_VT") AdmissionMode mode,
@DefaultValue("0") int maxConcurrentJobs) {
@ConstructorBinding
public PoolProperties {
mode = mode == null ? AdmissionMode.UNBOUNDED_VT : mode;
}
static PoolProperties fromRuntime(VirtualThreadProperties.PoolProperties runtimeProperties) {
return runtimeProperties == null ? null
: new PoolProperties(runtimeProperties.mode(), runtimeProperties.maxConcurrentJobs());
}
}
/**
* Alerter-specific binding model.
*/
public record AlerterProperties(
@Name("notify") PoolProperties notifyPool,
@DefaultValue("10") int periodicMaxConcurrentJobs,
QueueProperties logWorker,
QueueProperties reduce,
QueueProperties windowEvaluator,
@DefaultValue("4") int notifyMaxConcurrentPerChannel) {
@ConstructorBinding
public AlerterProperties {
}
static AlerterProperties fromRuntime(VirtualThreadProperties.AlerterProperties runtimeProperties) {
return runtimeProperties == null ? null
: new AlerterProperties(
PoolProperties.fromRuntime(runtimeProperties.notifyPool()),
runtimeProperties.periodicMaxConcurrentJobs(),
QueueProperties.fromRuntime(runtimeProperties.logWorker()),
QueueProperties.fromRuntime(runtimeProperties.reduce()),
QueueProperties.fromRuntime(runtimeProperties.windowEvaluator()),
runtimeProperties.notifyMaxConcurrentPerChannel());
}
}
/**
* Queue-preserving binding model.
*/
public record QueueProperties(
@DefaultValue("0") int maxConcurrentJobs,
@DefaultValue("0") int queueCapacity) {
@ConstructorBinding
public QueueProperties {
}
static QueueProperties fromRuntime(VirtualThreadProperties.QueueProperties runtimeProperties) {
return runtimeProperties == null ? null
: new QueueProperties(runtimeProperties.maxConcurrentJobs(), runtimeProperties.queueCapacity());
}
}
/**
* Async executor binding model.
*/
public record AsyncProperties(
@DefaultValue("true") boolean enabled,
@DefaultValue("256") int concurrencyLimit,
@DefaultValue("true") boolean rejectWhenLimitReached,
@DefaultValue("5000") long taskTerminationTimeout) {
@ConstructorBinding
public AsyncProperties {
}
static AsyncProperties fromRuntime(VirtualThreadProperties.AsyncProperties runtimeProperties) {
return runtimeProperties == null ? null
: new AsyncProperties(runtimeProperties.enabled(), runtimeProperties.concurrencyLimit(),
runtimeProperties.rejectWhenLimitReached(), runtimeProperties.taskTerminationTimeout());
}
}
private static VirtualThreadProperties.PoolProperties toRuntimePool(PoolProperties poolProperties) {
return poolProperties == null ? null
: new VirtualThreadProperties.PoolProperties(poolProperties.mode(), poolProperties.maxConcurrentJobs());
}
private static VirtualThreadProperties.AlerterProperties toRuntimeAlerter(AlerterProperties alerterProperties) {
return alerterProperties == null ? null
: new VirtualThreadProperties.AlerterProperties(
toRuntimePool(alerterProperties.notifyPool()),
alerterProperties.periodicMaxConcurrentJobs(),
toRuntimeQueue(alerterProperties.logWorker()),
toRuntimeQueue(alerterProperties.reduce()),
toRuntimeQueue(alerterProperties.windowEvaluator()),
alerterProperties.notifyMaxConcurrentPerChannel());
}
private static VirtualThreadProperties.QueueProperties toRuntimeQueue(QueueProperties queueProperties) {
return queueProperties == null ? null
: new VirtualThreadProperties.QueueProperties(queueProperties.maxConcurrentJobs(),
queueProperties.queueCapacity());
}
private static VirtualThreadProperties.AsyncProperties toRuntimeAsync(AsyncProperties asyncProperties) {
return asyncProperties == null ? null
: new VirtualThreadProperties.AsyncProperties(asyncProperties.enabled(),
asyncProperties.concurrencyLimit(), asyncProperties.rejectWhenLimitReached(),
asyncProperties.taskTerminationTimeout());
}
}
@@ -0,0 +1,85 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.ai;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_ONLY;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
import java.util.List;
/**
* Entity for storing AI chat conversation metadata
*/
@Data
@Builder
@Entity
@EntityListeners(AuditingEntityListener.class)
@Table(name = "hzb_ai_conversation")
@AllArgsConstructor
@NoArgsConstructor
public class ChatConversation {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Schema(title = "conversation title")
private String title;
@Schema(title = "The creator of this record", example = "tom", accessMode = READ_ONLY)
@CreatedBy
private String creator;
@Schema(title = "The modifier of this record", example = "tom", accessMode = READ_ONLY)
@LastModifiedBy
private String modifier;
@Schema(title = "Record create time", example = "1612198922000", accessMode = READ_ONLY)
@CreatedDate
private LocalDateTime gmtCreate;
@Schema(title = "Record modify time", example = "1612198444000", accessMode = READ_ONLY)
@LastModifiedDate
private LocalDateTime gmtUpdate;
/**
* List of messages in this conversation (one-to-many relationship)
*/
@OneToMany(mappedBy = "conversation")
private List<ChatMessage> messages;
private String securityData;
}
@@ -0,0 +1,96 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.ai;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_ONLY;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Index;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.Lob;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotBlank;
import java.time.LocalDateTime;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* Entity for storing individual chat messages in conversations
*/
@Data
@Builder
@Entity
@EntityListeners(AuditingEntityListener.class)
@Table(name = "hzb_ai_message", indexes = {
@Index(name = "idx_message_conversation_id", columnList = "conversation_id")
})
@AllArgsConstructor
@NoArgsConstructor
public class ChatMessage {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Schema(title = "conversation id")
@Column(name = "conversation_id", insertable = false, updatable = false)
private Long conversationId;
@Schema(title = "conversation", hidden = true)
@ManyToOne
@JoinColumn(name = "conversation_id")
private ChatConversation conversation;
@Schema(title = "message content")
@Lob
@NotBlank
private String content;
@Schema(title = "message role: user, system")
private String role;
@Schema(title = "The creator of this record", example = "tom", accessMode = READ_ONLY)
@CreatedBy
private String creator;
@Schema(title = "The modifier of this record", example = "tom", accessMode = READ_ONLY)
@LastModifiedBy
private String modifier;
@Schema(title = "Record create time", example = "1612198922000", accessMode = READ_ONLY)
@CreatedDate
private LocalDateTime gmtCreate;
@Schema(title = "Record modify time", example = "1612198444000", accessMode = READ_ONLY)
@LastModifiedDate
private LocalDateTime gmtUpdate;
}
@@ -0,0 +1,111 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.ai;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_ONLY;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Index;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
/**
* Entity for storing scheduled SOP execution configurations.
* Allows users to schedule automatic SOP executions with results pushed to conversations.
*/
@Data
@Builder
@Entity
@EntityListeners(AuditingEntityListener.class)
@Table(name = "hzb_sop_schedule", indexes = {
@Index(name = "idx_schedule_conversation_id", columnList = "conversation_id"),
@Index(name = "idx_schedule_enabled_next", columnList = "enabled, next_run_time")
})
@AllArgsConstructor
@NoArgsConstructor
public class SopSchedule {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Schema(title = "Conversation ID to push results to")
@NotNull
@Column(name = "conversation_id")
private Long conversationId;
@Schema(title = "Name of the SOP skill to execute")
@NotBlank
@Column(name = "sop_name", length = 64)
private String sopName;
@Schema(title = "SOP execution parameters in JSON format")
@Column(name = "sop_params", length = 1024)
private String sopParams;
@Schema(title = "Cron expression for scheduling")
@NotBlank
@Column(name = "cron_expression", length = 64)
private String cronExpression;
@Schema(title = "Whether the schedule is enabled")
@Builder.Default
@Column(name = "enabled")
private Boolean enabled = true;
@Schema(title = "Last execution time")
@Column(name = "last_run_time")
private LocalDateTime lastRunTime;
@Schema(title = "Next scheduled execution time")
@Column(name = "next_run_time")
private LocalDateTime nextRunTime;
@Schema(title = "The creator of this record", accessMode = READ_ONLY)
@CreatedBy
private String creator;
@Schema(title = "The modifier of this record", accessMode = READ_ONLY)
@LastModifiedBy
private String modifier;
@Schema(title = "Record create time", accessMode = READ_ONLY)
@CreatedDate
private LocalDateTime gmtCreate;
@Schema(title = "Record modify time", accessMode = READ_ONLY)
@LastModifiedDate
private LocalDateTime gmtUpdate;
}
@@ -0,0 +1,121 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.alerter;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_ONLY;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_WRITE;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.Convert;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.time.LocalDateTime;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
/**
* Alarm Define Rule Entity
*/
@Entity
@Table(name = "hzb_alert_define")
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "Alarm Threshold Entity")
@EntityListeners(AuditingEntityListener.class)
public class AlertDefine {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Schema(title = "Threshold Id", example = "87584674384", accessMode = READ_ONLY)
private Long id;
@Schema(title = "Alert Rule Name", example = "high_cpu_usage", accessMode = READ_WRITE)
@Size(max = 100)
@NotNull
private String name;
@Schema(title = "Rule Type: realtime_metric, periodic_metric, realtime_log, periodic_log", example = "realtime_metric")
private String type;
@Schema(title = "Alarm Threshold Expr", example = "usage>90", accessMode = READ_WRITE)
@Size(max = 2048)
@Column(length = 2048)
private String expr;
@Schema(title = "Execution Period/ Window Size (seconds) - For periodic rules/ For log realtime", example = "300")
private Integer period;
@Schema(title = "Alarm Trigger Times.The alarm is triggered only after the required number of times is reached",
example = "3", accessMode = READ_WRITE)
private Integer times;
@Schema(description = "labels(status:success,env:prod,priority:critical)", example = "{name: key1, value: value1}",
accessMode = READ_WRITE)
@Convert(converter = JsonMapAttributeConverter.class)
@Column(length = 2048)
private Map<String, String> labels;
@Schema(title = "Annotations", example = "summary: High CPU usage")
@Convert(converter = JsonMapAttributeConverter.class)
@Column(length = 4096)
private Map<String, String> annotations;
@Schema(title = "Alert Content Template", example = "Instance {{ $labels.instance }} CPU usage is {{ $value }}%")
@Size(max = 2048)
@Column(length = 2048)
private String template;
@Schema(title = "Data Source Type", example = "PROMETHEUS")
@Size(max = 100)
private String datasource;
@Schema(title = "Is Enabled", example = "true")
private boolean enable = true;
@Schema(title = "The creator of this record", example = "tom", accessMode = READ_ONLY)
@CreatedBy
private String creator;
@Schema(title = "The modifier of this record", example = "tom", accessMode = READ_ONLY)
@LastModifiedBy
private String modifier;
@Schema(title = "Record create time", example = "1612198922000", accessMode = READ_ONLY)
@CreatedDate
private LocalDateTime gmtCreate;
@Schema(title = "Record modify time", example = "1612198444000", accessMode = READ_ONLY)
@LastModifiedDate
private LocalDateTime gmtUpdate;
}
@@ -0,0 +1,88 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.alerter;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_ONLY;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_WRITE;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.ConstraintMode;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.FetchType;
import jakarta.persistence.ForeignKey;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Index;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.hertzbeat.common.entity.manager.Monitor;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
/**
* Alarm Threshold Relate Monitor Entity
*/
@Entity
@Table(name = "hzb_alert_define_monitor_bind", indexes = {
@Index(name = "idx_alert_define_id", columnList = "alert_define_id"),
@Index(name = "idx_monitor_id", columnList = "monitor_id")
})
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "Alarm Threshold Relate Monitor Entity")
@EntityListeners(AuditingEntityListener.class)
public class AlertDefineMonitorBind {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Schema(title = "id", example = "74384", accessMode = READ_ONLY)
private Long id;
@Schema(title = "Alarm Define Id", example = "87432674384", accessMode = READ_WRITE)
private Long alertDefineId;
@Schema(title = "Monitor Id", example = "87432674336", accessMode = READ_WRITE)
@Column(name = "monitor_id")
private Long monitorId;
@Schema(title = "Record create time", example = "1612198922000", accessMode = READ_ONLY)
@CreatedDate
private LocalDateTime gmtCreate;
@Schema(title = "Record modify time", example = "1612198444000", accessMode = READ_ONLY)
@LastModifiedDate
private LocalDateTime gmtUpdate;
@OneToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "monitor_id", referencedColumnName = "id", foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT),
insertable = false, updatable = false)
// todo instead of @NotFound
// @NotFound(action = NotFoundAction.IGNORE)
private Monitor monitor;
}
@@ -0,0 +1,106 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.alerter;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.Convert;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Index;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.time.LocalDateTime;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.hertzbeat.common.entity.manager.JsonStringListAttributeConverter;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
/**
* Alert group converge strategy entity
*/
@Entity
@Table(name = "hzb_alert_group_converge", indexes = {
@Index(name = "idx_name", columnList = "name")
})
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "Alert Group Converge Policy Entity")
@EntityListeners(AuditingEntityListener.class)
public class AlertGroupConverge {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Schema(title = "Primary Key Index ID", example = "87584674384")
private Long id;
@Schema(title = "Policy name", example = "group-converge-1")
@Size(max = 100)
@NotNull
@Column(name = "name")
private String name;
@Schema(title = "Labels to group by", example = "[\"instance\"]")
@Convert(converter = JsonStringListAttributeConverter.class)
@Column(name = "group_labels", length = 1024)
private List<String> groupLabels;
@Schema(title = "Initial wait time before sending first group alert (s)", example = "30")
@Column(name = "group_wait")
private Long groupWait;
@Schema(title = "Interval between group alert sends (s)", example = "300")
@Column(name = "group_interval")
private Long groupInterval;
@Schema(title = "Interval for repeating firing alerts (s), set to 0 to disable repeating", example = "9000")
@Column(name = "repeat_interval")
private Long repeatInterval;
@Schema(title = "Whether to enable this policy", example = "true")
private Boolean enable;
@Schema(title = "The creator of this record", example = "tom")
@CreatedBy
private String creator;
@Schema(title = "This record was last modified by", example = "tom")
@LastModifiedBy
private String modifier;
@Schema(title = "This record creation time (millisecond timestamp)")
@CreatedDate
private LocalDateTime gmtCreate;
@Schema(title = "Record the latest modification time (timestamp in milliseconds)")
@LastModifiedDate
private LocalDateTime gmtUpdate;
}
@@ -0,0 +1,134 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.hertzbeat.common.entity.alerter;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.Convert;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.hertzbeat.common.entity.manager.JsonStringListAttributeConverter;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
/**
* alert inhibit rule
*/
@Entity
@Table(name = "hzb_alert_inhibit")
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "Alert Inhibit Rule Entity")
@EntityListeners(AuditingEntityListener.class)
public class AlertInhibit {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Schema(title = "Inhibit Rule ID", example = "1")
private Long id;
@Schema(title = "Inhibit Rule Name", example = "inhibit_high_cpu")
@Size(max = 100)
@NotNull
private String name;
/**
* Source alert labels that must match for the inhibit rule to be active
* Example: severity=critical,instance=web-01
*/
@Schema(title = "Source Alert Match Labels")
@Convert(converter = JsonMapAttributeConverter.class)
@Column(length = 2048)
private Map<String, String> sourceLabels;
/**
* Target alert labels that must match to be inhibited
* Example: severity=warning,instance=web-01
*/
@Schema(title = "Target Alert Match Labels")
@Convert(converter = JsonMapAttributeConverter.class)
@Column(length = 2048)
private Map<String, String> targetLabels;
/**
* Labels that must have the same value in the source and target alert for the inhibit rule to be active
* Example: ["instance", "job"]
*/
@Schema(title = "Equal Labels")
@Convert(converter = JsonStringListAttributeConverter.class)
@Column(length = 2048)
private List<String> equalLabels;
@Schema(title = "Whether to enable this policy", example = "true")
private Boolean enable;
@Schema(title = "The creator of this record", example = "tom")
@CreatedBy
private String creator;
@Schema(title = "This record was last modified by", example = "tom")
@LastModifiedBy
private String modifier;
@Schema(title = "This record creation time (millisecond timestamp)")
@CreatedDate
private LocalDateTime gmtCreate;
@Schema(title = "Record the latest modification time (timestamp in milliseconds)")
@LastModifiedDate
private LocalDateTime gmtUpdate;
}
@@ -0,0 +1,118 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.alerter;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.Convert;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.hertzbeat.common.entity.manager.JsonByteListAttributeConverter;
import org.apache.hertzbeat.common.entity.manager.ZonedDateTimeAttributeConverter;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
/**
* Alert Silence strategy entity
*/
@Entity
@Table(name = "hzb_alert_silence")
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "Alert Silence Policy Entity")
@EntityListeners(AuditingEntityListener.class)
public class AlertSilence {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Schema(title = "Primary Key Index ID",
example = "87584674384")
private Long id;
@Schema(title = "Policy name", example = "silence-1")
@Size(max = 100)
@NotNull
private String name;
@Schema(title = "Whether to enable this policy", example = "true")
private boolean enable = true;
@Schema(title = "Whether to match all", example = "true")
private boolean matchAll = true;
@Schema(title = "Silence type 0: once, 1:cyc", example = "1")
@NotNull
private Byte type;
@Schema(title = "Silenced alerts num", example = "3")
private Integer times;
@Schema(description = "Match the alarm information label", example = "{name: key1, value: value1}")
@Convert(converter = JsonMapAttributeConverter.class)
@Column(length = 2048)
private Map<String, String> labels;
@Schema(title = "The day of the WEEK is valid in periodic silence, multiple,"
+ " all or empty is daily 7: Sunday 1: Monday 2: Tuesday 3: Wednesday 4: Thursday 5: Friday 6: Saturday",
example = "[0,1]")
@Convert(converter = JsonByteListAttributeConverter.class)
private List<Byte> days;
@Schema(title = "Limit time period start", example = "00:00:00")
@Convert(converter = ZonedDateTimeAttributeConverter.class)
private ZonedDateTime periodStart;
@Schema(title = "Restricted time period end", example = "23:59:59")
@Convert(converter = ZonedDateTimeAttributeConverter.class)
private ZonedDateTime periodEnd;
@Schema(title = "The creator of this record", example = "tom")
@CreatedBy
private String creator;
@Schema(title = "This record was last modified by", example = "tom")
@LastModifiedBy
private String modifier;
@Schema(title = "This record creation time (millisecond timestamp)")
@CreatedDate
private LocalDateTime gmtCreate;
@Schema(title = "Record the latest modification time (timestamp in milliseconds)")
@LastModifiedDate
private LocalDateTime gmtUpdate;
}
@@ -0,0 +1,112 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.alerter;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_ONLY;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.Convert;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Index;
import jakarta.persistence.Table;
import jakarta.persistence.Transient;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.hertzbeat.common.entity.manager.JsonStringListAttributeConverter;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
/**
* Group Alert Content Entity
*/
@Entity
@Table(name = "hzb_alert_group", indexes = {@Index(name = "unique_group_key", columnList = "group_key", unique = true)})
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "Group Alarm Content Entity")
@EntityListeners(AuditingEntityListener.class)
public class GroupAlert {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Schema(title = "Threshold Id", example = "87584674384", accessMode = READ_ONLY)
private Long id;
@Schema(title = "Group Key", example = "HighCPUUsage{alertname=\"HighCPUUsage\", instance=\"server1\"}")
@Column(length = 2048)
private String groupKey;
@Schema(title = "Status", example = "resolved")
private String status;
@Schema(title = "Group Labels", example = "{\"alertname\": \"HighCPUUsage\"}")
@Convert(converter = JsonMapAttributeConverter.class)
@Column(length = 2048)
private Map<String, String> groupLabels;
@Schema(title = "Common Labels", example = "{\"alertname\": \"HighCPUUsage\", \"instance\": \"server1\", \"severity\": \"critical\"}")
@Convert(converter = JsonMapAttributeConverter.class)
@Column(length = 2048)
private Map<String, String> commonLabels;
@Schema(title = "Common Annotations", example = "{\"summary\": \"High CPU usage detected\", \"description\": \"CPU usage is back to normal for server1\"}")
@Convert(converter = JsonMapAttributeConverter.class)
@Column(columnDefinition = "TEXT")
private Map<String, String> commonAnnotations;
@Schema(title = "Alert Fingerprints", example = "[\"dxsdfdsf\"]")
@Convert(converter = JsonStringListAttributeConverter.class)
@Column(columnDefinition = "TEXT")
private List<String> alertFingerprints;
@Schema(title = "The creator of this record", example = "tom")
@CreatedBy
private String creator;
@Schema(title = "This record was last modified by", example = "tom")
@LastModifiedBy
private String modifier;
@Schema(title = "This record creation time (millisecond timestamp)")
@CreatedDate
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime gmtCreate;
@Schema(title = "Record the latest modification time (timestamp in milliseconds)")
@LastModifiedDate
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime gmtUpdate;
@Transient
private List<SingleAlert> alerts;
}
@@ -0,0 +1,47 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.alerter;
import tools.jackson.core.type.TypeReference;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import org.apache.hertzbeat.common.util.JsonUtil;
import org.springframework.stereotype.Component;
/**
* json map converter
*/
@Converter
@Component
public class JsonMapAttributeConverter implements AttributeConverter<Map<String, String>, String> {
@Override
public String convertToDatabaseColumn(Map<String, String> attribute) {
return JsonUtil.toJson(attribute);
}
@Override
public Map<String, String> convertToEntityAttribute(String dbData) {
TypeReference<Map<String, String>> typeReference = new TypeReference<>() {};
Map<String, String> map = JsonUtil.fromJson(dbData, typeReference);
return Objects.requireNonNullElseGet(map, HashMap::new);
}
}
@@ -0,0 +1,301 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.alerter;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_ONLY;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_WRITE;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
/**
* Message notification recipient entity
*/
@Entity
@Table(name = "hzb_notice_receiver")
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "Message notification recipient entity")
@EntityListeners(AuditingEntityListener.class)
public class NoticeReceiver {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Schema(title = "Recipient entity primary key index ID", description = "Recipient entity primary key index ID",
example = "87584674384", accessMode = READ_ONLY)
private Long id;
@Schema(title = "Recipient name", description = "Recipient name",
example = "tom", accessMode = READ_WRITE)
@Size(max = 100)
@NotBlank(message = "name can not null")
private String name;
@Schema(title = "Notification information method: 0-SMS 1-Email 2-webhook 3-WeChat Official Account 4-Enterprise WeChat Robot "
+ "5-DingTalk Robot 6-FeiShu Robot 7-Telegram Bot 8-SlackWebHook 9-Discord Bot 10-Enterprise WeChat app message "
+ "11-Huawei Cloud SMN 12-ServerChan 13-Gotify 14-FeiShu app message 15-Ntfy",
description = "Notification information method: "
+ "0-SMS 1-Email 2-webhook 3-WeChat Official Account "
+ "4-Enterprise WeChat Robot 5-DingTalk Robot 6-FeiShu Robot "
+ "7-Telegram Bot 8-SlackWebHook 9-Discord Bot 10-Enterprise "
+ "WeChat app message 11-Huawei Cloud SMN 12-ServerChan 13-Gotify 14-FeiShu app message 15-Ntfy",
accessMode = READ_WRITE)
@Min(0)
@NotNull(message = "type can not null")
private Byte type;
@Schema(title = "Mobile number: Valid when the notification method is SMS",
description = "Mobile number: Valid when the notification method is SMS",
example = "18923435643", accessMode = READ_WRITE)
@Size(max = 100)
private String phone;
@Schema(title = "Email account: Valid when the notification method is email",
description = "Email account: Valid when the notification method is email",
example = "tom@qq.com", accessMode = READ_WRITE)
@Size(max = 100)
private String email;
@Schema(title = "URL address: The notification method is valid for webhook",
description = "URL address: The notification method is valid for webhook",
example = "https://www.tancloud.cn", accessMode = READ_WRITE)
@Size(max = 1000)
@Column(length = 1000)
private String hookUrl;
@Schema(title = "Auth Type: WebHook Authorization Type",
description = "Auth Type: WebHook Authorization Type, one of the 'None', 'Basic' and 'Bearer'",
example = "None", accessMode = READ_ONLY)
@Size(max = 300)
@Column(length = 300)
private String hookAuthType;
@Schema(title = "Auth Token: WebHook Authorization Token",
description = "Auth Token: WebHook Authorization Token",
example = "YWxpY2U6c3VwZXJtYW4", accessMode = READ_WRITE)
@Size(max = 300)
@Column(length = 300)
private String hookAuthToken;
@Schema(title = "openId : The notification method is valid for WeChat official account, enterprise WeChat robot or FlyBook robot",
description = "openId : The notification method is valid for WeChat official account, enterprise WeChat robot or FlyBook robot",
example = "343432", accessMode = READ_WRITE)
@Size(max = 300)
@Column(length = 300)
private String wechatId;
@Schema(title = "FeiShu app id : The notification method is valid for FeiShu app message",
description = "FeiShu app id : The notification method is valid for FeiShu app message",
example = "34823984635647", accessMode = READ_WRITE)
@Size(max = 255)
private String appId;
@Schema(title = "Access token : The notification method is valid for DingTalk robot",
description = "Access token : The notification method is valid for DingTalk robot",
example = "34823984635647", accessMode = READ_WRITE)
@Size(max = 300)
@Column(length = 300)
private String accessToken;
@Schema(title = "Telegram bot token : The notification method is valid for Telegram Bot",
description = "Telegram bot token : The notification method is valid for Telegram Bot",
example = "1499012345:AAEOB_wEYS-DZyPM3h5NzI8voJMXXXXXX", accessMode = READ_WRITE)
private String tgBotToken;
@Schema(title = "Telegram user id: The notification method is valid for Telegram Bot",
description = "Telegram user id: The notification method is valid for Telegram Bot",
example = "779294123", accessMode = READ_WRITE)
private String tgUserId;
@Schema(title = "Telegram message thread id: The notification method is valid for Telegram Bot",
description = "TTelegram message thread id: The notification method is valid for Telegram Bot",
example = "779294123", accessMode = READ_WRITE)
private String tgMessageThreadId;
@Schema(title = "FeiShu app message receiveType: 0-user 1-chat 2-party 3-all",
description = "FeiShu app message receiveType: 0-user 1-chat 2-party 3-all",
example = "1", accessMode = READ_WRITE)
private Byte larkReceiveType;
@Schema(title = "DingTalk,FeiShu,WeWork user id: The notification method is valid for DingTalk,FeiShu,WeWork Bot",
description = "DingTalk,FeiShu,WeWork user id: The notification method is valid for DingTalk,FeiShu,WeWork Bot",
example = "779294123", accessMode = READ_WRITE)
private String userId;
@Schema(title = "FeiShu app message chatId: The notification method is valid for FeiShu app message",
description = "FeiShu app message chatId: The notification method is valid for FeiShu app message",
example = "779294123", accessMode = READ_WRITE)
private String chatId;
@Schema(title = "URL address: The notification method is valid for Slack",
description = "URL address: The notification method is valid for Slack",
example = "https://hooks.slack.com/services/XXXX/XXXX/XXXX", accessMode = READ_WRITE)
@Size(max = 300)
@Column(length = 300)
private String slackWebHookUrl;
@Schema(title = "Enterprise weChat message: The notification method is valid for Enterprise WeChat app message",
description = "Enterprise weChat message: The notification method is valid for Enterprise WeChat app message",
example = "ww1a603432123d0dc1", accessMode = READ_WRITE)
private String corpId;
@Schema(title = "Enterprise weChat appId: The notification method is valid for Enterprise WeChat app message",
description = "Enterprise weChat appId: The notification method is valid for Enterprise WeChat app message",
example = "1000001", accessMode = READ_WRITE)
private Integer agentId;
@Schema(title = "Enterprise weChat secret: The notification method is valid for Enterprise WeChat app message",
description = "Enterprise weChat secret: The notification method is valid for Enterprise WeChat app message",
example = "oUydwn92ey0lnuY02MixNa57eNK-20dJn5NEOG-u2uE", accessMode = READ_WRITE)
private String appSecret;
@Schema(title = "Enterprise weChat party id: The notification method is valid for Enterprise WeChat app message",
description = "Enterprise weChat party id: The notification method is valid for Enterprise WeChat app message",
example = "779294123", accessMode = READ_WRITE)
private String partyId;
@Schema(title = "Enterprise weChat tag id: The notification method is valid for Enterprise WeChat app message",
description = "Enterprise weChat tag id: The notification method is valid for Enterprise WeChat app message",
example = "779294123", accessMode = READ_WRITE)
private String tagId;
@Schema(title = "Discord channel id: The notification method is valid for Discord",
description = "Discord channel id: The notification method is valid for Discord",
example = "1065303416030642266", accessMode = READ_WRITE)
@Size(max = 300)
@Column(length = 300)
private String discordChannelId;
@Schema(title = "Discord bot token: The notification method is valid for Discord",
description = "Discord bot token: The notification method is valid for Discord",
example = "MTA2NTMwMzU0ODY4Mzg4MjUzNw.xxxxx.xxxxxxx", accessMode = READ_WRITE)
@Size(max = 300)
@Column(length = 300)
private String discordBotToken;
@Schema(title = "huawei cloud SMN ak: If the notification method is valid for huawei cloud SMN",
description = "huawei cloud SMN ak: If the notification method is valid for huawei cloud SMN",
example = "NCVBODJOEYHSW3VNXXXX", accessMode = READ_WRITE)
@Size(max = 22)
@Column(length = 22)
private String smnAk;
@Schema(title = "huawei cloud SMN sk: If the notification method is valid for huawei cloud SMN",
description = "huawei cloud SMN sk: If the notification method is valid for huawei cloud SMN",
example = "nmSNhUJN9MlpPl8lfCsgdA0KvHCL9JXXXX", accessMode = READ_WRITE)
@Size(max = 42)
@Column(length = 42)
private String smnSk;
@Schema(title = "huawei cloud SMN projectId: If the notification method is valid for huawei cloud SMN",
description = "huawei cloud SMN projectId: If the notification method is valid for huawei cloud SMN",
example = "320c2fb11edb47a481c299c1XXXXXX", accessMode = READ_WRITE)
@Size(max = 32)
@Column(length = 32)
private String smnProjectId;
@Schema(title = "huawei cloud SMN region: If the notification method is valid for huawei cloud SMN",
description = "huawei cloud SMN region: If the notification method is valid for huawei cloud SMN",
example = "cn-east-3", accessMode = READ_WRITE)
@Size(max = 32)
@Column(length = 32)
private String smnRegion;
@Schema(title = "huawei cloud SMN TopicUrn: If the notification method is valid for huawei cloud SMN",
description = "huawei cloud SMN TopicUrn: If the notification method is valid for huawei cloud SMN",
example = "urn:smn:cn-east-3:xxx:hertzbeat_test", accessMode = READ_WRITE)
@Size(max = 300)
@Column(length = 300)
private String smnTopicUrn;
@Schema(title = "serverChanToken : The notification method is valid for ServerChan",
description = "serverChanToken : The notification method is valid for ServerChan",
example = "SCT193569TSNm6xIabdjqeZPtOGOWcvU1e", accessMode = READ_WRITE)
@Size(max = 300)
@Column(length = 300)
private String serverChanToken;
@Schema(title = "Gotify token : The notification method is valid for Gotify",
description = "Gotify token : The notification method is valid for Gotify",
example = "A845h__ZMqDxZlO", accessMode = READ_WRITE)
@Size(max = 300)
@Column(length = 300)
private String gotifyToken;
@Schema(title = "Ntfy server URL : The notification method is valid for Ntfy",
description = "Ntfy server URL : The notification method is valid for Ntfy, default is https://ntfy.sh",
example = "https://ntfy.sh", accessMode = READ_WRITE)
@Size(max = 300)
@Column(length = 300)
private String ntfyServerUrl;
@Schema(title = "Ntfy topic : The notification method is valid for Ntfy",
description = "Ntfy topic : The notification method is valid for Ntfy",
example = "hertzbeat-alerts", accessMode = READ_WRITE)
@Size(max = 300)
@Column(length = 300)
private String ntfyTopic;
@Schema(title = "Ntfy access token : Bearer token for self-hosted ntfy servers with authentication",
description = "Ntfy access token : Bearer token for self-hosted ntfy servers with authentication",
example = "tk_AgQdq7mVBoFD37zQVN29RhuMzNIz2", accessMode = READ_WRITE)
@Size(max = 300)
@Column(length = 300)
private String ntfyToken;
@Schema(title = "The creator of this record", example = "tom",
accessMode = READ_ONLY)
@CreatedBy
private String creator;
@Schema(title = "This record was last modified by", example = "tom", accessMode = READ_ONLY)
@LastModifiedBy
private String modifier;
@Schema(title = "Record creation time (millisecond timestamp)",
example = "1612198922000", accessMode = READ_ONLY)
@CreatedDate
private LocalDateTime gmtCreate;
@Schema(title = "Record the latest modification time (timestamp in milliseconds)",
example = "1612198444000", accessMode = READ_ONLY)
@LastModifiedDate
private LocalDateTime gmtUpdate;
}
@@ -0,0 +1,146 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.alerter;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_ONLY;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_WRITE;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.Convert;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.Size;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.hertzbeat.common.entity.manager.JsonByteListAttributeConverter;
import org.apache.hertzbeat.common.entity.manager.JsonLongListAttributeConverter;
import org.apache.hertzbeat.common.entity.manager.JsonStringListAttributeConverter;
import org.apache.hertzbeat.common.entity.manager.ZonedDateTimeAttributeConverter;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
/**
* Notification strategy entity
*/
@Entity
@Table(name = "hzb_notice_rule")
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "Notify Policy Entity")
@EntityListeners(AuditingEntityListener.class)
public class NoticeRule {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Schema(title = "Notification Policy Entity Primary Key Index ID",
description = "Notification Policy Entity Primary Key Index ID",
example = "87584674384", accessMode = READ_ONLY)
private Long id;
@Schema(title = "Policy name",
description = "Policy name",
example = "dispatch-1", accessMode = READ_WRITE)
@Size(max = 100)
@NotBlank(message = "name can not null")
private String name;
@Schema(title = "Recipient ID",
description = "Recipient ID",
example = "4324324", accessMode = READ_WRITE)
@NotEmpty(message = "receiverId can not empty")
@Convert(converter = JsonLongListAttributeConverter.class)
private List<Long> receiverId;
@Schema(title = "Recipient identification",
description = "Recipient identification",
example = "tom", accessMode = READ_WRITE)
@Convert(converter = JsonStringListAttributeConverter.class)
private List<String> receiverName;
@Schema(title = "Template ID",
description = "Template ID",
example = "4324324", accessMode = READ_WRITE)
private Long templateId;
@Schema(title = "Template identification",
description = "Template identification",
example = "demo", accessMode = READ_WRITE)
@Size(max = 100)
private String templateName;
@Schema(title = "Whether to enable this policy",
description = "Whether to enable this policy",
example = "true", accessMode = READ_WRITE)
private boolean enable = true;
@Schema(title = "Whether to forward all",
description = "Whether to forward all",
example = "false", accessMode = READ_WRITE)
private boolean filterAll = true;
@Schema(title = "Labels", example = "{\"alertname\": \"HighCPUUsage\", \"priority\": \"critical\", \"instance\": \"343483943\"}")
@Convert(converter = JsonMapAttributeConverter.class)
@Column(length = 2048)
private Map<String, String> labels;
@Schema(title = "Day of the week, multiple, all or empty is daily 7: Sunday 1: Monday 2: Tuesday 3: Wednesday 4: Thursday 5: Friday 6: Saturday",
example = "[0,1]", accessMode = READ_WRITE)
@Convert(converter = JsonByteListAttributeConverter.class)
private List<Byte> days;
@Schema(title = "Limit time period start", example = "00:00:00", accessMode = READ_WRITE)
@Convert(converter = ZonedDateTimeAttributeConverter.class)
private ZonedDateTime periodStart;
@Schema(title = "Restricted time period end", example = "23:59:59", accessMode = READ_WRITE)
@Convert(converter = ZonedDateTimeAttributeConverter.class)
private ZonedDateTime periodEnd;
@Schema(title = "The creator of this record", example = "tom", accessMode = READ_ONLY)
@CreatedBy
private String creator;
@Schema(title = "This record was last modified by", example = "tom", accessMode = READ_ONLY)
@LastModifiedBy
private String modifier;
@Schema(title = "This record creation time (millisecond timestamp)", accessMode = READ_ONLY)
@CreatedDate
private LocalDateTime gmtCreate;
@Schema(title = "Record the latest modification time (timestamp in milliseconds)", accessMode = READ_ONLY)
@LastModifiedDate
private LocalDateTime gmtUpdate;
}
@@ -0,0 +1,121 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.alerter;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_ONLY;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_WRITE;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Lob;
import jakarta.persistence.Table;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
/**
* Notification template entity
*/
@Entity
@Table(name = "hzb_notice_template")
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "Notify Policy Template")
@EntityListeners(AuditingEntityListener.class)
public class NoticeTemplate {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Schema(title = "Notification Template Entity Primary Key Index ID",
description = "Notification Template Entity Primary Key Index ID",
example = "87584674384", accessMode = READ_ONLY)
private Long id;
@Schema(title = "Template name",
description = "Template name",
example = "dispatch-1", accessMode = READ_WRITE)
@Size(max = 100)
@NotBlank
private String name;
@Schema(title = "Notification information method: 0-SMS 1-Email 2-webhook 3-WeChat Official Account 4-Enterprise WeChat Robot "
+ "5-DingTalk Robot 6-FeiShu Robot 7-Telegram Bot 8-SlackWebHook 9-Discord Bot 10-Enterprise WeChat app message",
description = "Notification information method: "
+ "0-SMS 1-Email 2-webhook 3-WeChat Official Account "
+ "4-Enterprise WeChat Robot 5-DingTalk Robot 6-FeiShu Robot "
+ "7-Telegram Bot 8-SlackWebHook 9-Discord Bot 10-Enterprise WeChat app message",
accessMode = READ_WRITE)
@Min(0)
@NotNull
private Byte type;
@Schema(title = "Is it a preset template: true- preset template false- custom template.",
description = "Is it a preset template: true- preset template false- custom template.",
accessMode = READ_WRITE)
@Column(columnDefinition = "boolean default false")
private boolean preset = false;
@Schema(title = "Template content",
description = "Template content",
example = """
[${title}]
${targetLabel} : ${target}
<#if (monitorId??)>${monitorIdLabel} : ${monitorId} </#if>
<#if (monitorName??)>${monitorNameLabel} : ${monitorName} </#if>
<#if (monitorHost??)>${monitorHostLabel} : ${monitorHost} </#if>
${priorityLabel} : ${priority}
${triggerTimeLabel} : ${triggerTime}
${contentLabel} : ${content}""", accessMode = READ_WRITE)
@Size(max = 60000)
@Lob
@NotBlank
private String content;
@Schema(title = "The creator of this record", example = "tom", accessMode = READ_ONLY)
@CreatedBy
private String creator;
@Schema(title = "This record was last modified by", example = "tom", accessMode = READ_ONLY)
@LastModifiedBy
private String modifier;
@Schema(title = "This record creation time (millisecond timestamp)", accessMode = READ_ONLY)
@CreatedDate
private LocalDateTime gmtCreate;
@Schema(title = "Record the latest modification time (timestamp in milliseconds)", accessMode = READ_ONLY)
@LastModifiedDate
private LocalDateTime gmtUpdate;
}
@@ -0,0 +1,119 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.alerter;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_ONLY;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.Convert;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Index;
import jakarta.persistence.Table;
import java.time.LocalDateTime;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.hertzbeat.common.util.JsonUtil;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
/**
* Single Alert Content Entity
*/
@Entity
@Table(name = "hzb_alert_single", indexes = {@Index(name = "unique_fingerprint", columnList = "fingerprint", unique = true)})
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "Single Alarm Content Entity")
@EntityListeners(AuditingEntityListener.class)
public class SingleAlert {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Schema(title = "Threshold Id", example = "87584674384", accessMode = READ_ONLY)
private Long id;
@Schema(title = "Fingerprint", example = "alertname:demo")
@Column(length = 2048)
private String fingerprint;
@Schema(title = "Labels", example = "{\"alertname\": \"HighCPUUsage\", \"priority\": \"critical\", \"instance\": \"343483943\"}")
@Convert(converter = JsonMapAttributeConverter.class)
@Column(length = 2048)
private Map<String, String> labels;
@Schema(title = "Annotations", example = "{\"summary\": \"High CPU usage detected\"}")
@Convert(converter = JsonMapAttributeConverter.class)
@Column(length = 4096)
private Map<String, String> annotations;
@Schema(title = "Content", example = "CPU usage is above 80% for the last 5 minutes on instance server1.example.com.")
@Column(length = 4096)
private String content;
@Schema(title = "Status", example = "firing|resolved")
private String status;
@Schema(title = "Trigger Times", example = "1")
private Integer triggerTimes;
@Schema(title = "Start At", example = "1734005477630")
private Long startAt;
@Schema(title = "Active At", example = "1734005477630")
private Long activeAt;
@Schema(title = "End At, when status is resolved has", example = "null")
private Long endAt;
@Schema(title = "The creator of this record", example = "tom")
@CreatedBy
private String creator;
@Schema(title = "This record was last modified by", example = "tom")
@LastModifiedBy
private String modifier;
@Schema(title = "This record creation time (millisecond timestamp)")
@CreatedDate
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime gmtCreate;
@Schema(title = "Record the latest modification time (timestamp in milliseconds)")
@LastModifiedDate
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime gmtUpdate;
@Override
public SingleAlert clone() {
// deep clone
return JsonUtil.fromJson(JsonUtil.toJson(this), SingleAlert.class);
}
}
@@ -0,0 +1,71 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.grafana;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.persistence.Transient;
import java.io.Serializable;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* Grafana dashboard entity
*/
@Entity
@Table(name = "hzb_grafana_dashboard")
@Data
@Builder(toBuilder = true)
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "Grafana dashboard entity")
public class GrafanaDashboard implements Serializable {
@Id
@Schema(description = "Monitor id")
private Long monitorId;
@Schema(description = "Dashboard folderUid")
private String folderUid;
@Schema(description = "Dashboard slug")
private String slug;
@Schema(description = "Dashboard status")
private String status;
@Schema(description = "Dashboard uid")
private String uid;
@Schema(description = "Dashboard url")
private String url;
@Schema(description = "Dashboard version")
private Long version;
@Schema(description = "is enabled")
private boolean enabled;
@Schema(description = "template")
@Transient
private String template;
}
@@ -0,0 +1,94 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.manager;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import java.time.LocalDateTime;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_ONLY;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_WRITE;
/**
* API Token Entity - stores generated non-expiring API tokens for management and revocation.
* <p>
* Only "managed" tokens (those generated after this feature was introduced) are stored here.
* Legacy tokens are not tracked and continue to work without DB validation.
* </p>
*/
@Entity
@Table(name = "hzb_auth_token")
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "API Token entity")
@EntityListeners(AuditingEntityListener.class)
public class AuthToken {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Schema(title = "primary id", example = "1", accessMode = READ_ONLY)
private Long id;
@Schema(title = "Token name/description", example = "Alert integration token", accessMode = READ_WRITE)
@Column(length = 255)
private String name;
@Schema(title = "SHA-256 hash of the token value", accessMode = READ_ONLY)
@Column(length = 128, nullable = false, unique = true)
private String tokenHash;
@Schema(title = "Masked token for display", example = "eyJh****xMjM", accessMode = READ_ONLY)
@Column(length = 64)
private String tokenMask;
@Schema(title = "Token status: 0-active", accessMode = READ_ONLY)
@Column(nullable = false)
@Builder.Default
private Byte status = 0;
@Schema(title = "The creator of this token", accessMode = READ_ONLY)
@CreatedBy
private String creator;
@Schema(title = "Token creation time", accessMode = READ_ONLY)
@CreatedDate
private LocalDateTime gmtCreate;
@Schema(title = "Token expiration time, null means never expire", accessMode = READ_ONLY)
private LocalDateTime expireTime;
@Schema(title = "Token last used time", accessMode = READ_ONLY)
private LocalDateTime lastUsedTime;
}
@@ -0,0 +1,92 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.manager;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_WRITE;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.Convert;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
/**
* Bulletin
*/
@Entity
@Data
@Schema(description = "Bulletin")
@Builder
@AllArgsConstructor
@NoArgsConstructor
@EntityListeners(AuditingEntityListener.class)
@Table(name = "hzb_bulletin")
public class Bulletin {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Schema(description = "Bulletin ID", example = "1")
private Long id;
@Schema(description = "Bulletin Name", example = "Bulletin1", accessMode = READ_WRITE)
private String name;
@Schema(description = "Monitor IDs", example = "1")
@Column(name = "monitor_ids", length = 4096)
@Convert(converter = JsonLongListAttributeConverter.class)
private List<Long> monitorIds;
@Schema(description = "Monitor Type eg: jvm, tomcat", example = "jvm", accessMode = READ_WRITE)
private String app;
@Schema(description = "Monitor Fields")
@Column(name = "fields", length = 4096)
@Convert(converter = JsonMapListAttributeConverter.class)
private Map<String, List<String>> fields;
@Schema(title = "The creator of this record", example = "tom", accessMode = READ_WRITE)
@CreatedBy
private String creator;
@Schema(title = "The modifier of this record", example = "tom", accessMode = READ_WRITE)
@LastModifiedBy
private String modifier;
@Schema(title = "Record create time", example = "2024-07-02T20:09:34.903217", accessMode = READ_WRITE)
@CreatedDate
private LocalDateTime gmtCreate;
@Schema(title = "Record modify time", example = "2024-07-02T20:09:34.903217", accessMode = READ_WRITE)
@LastModifiedDate
private LocalDateTime gmtUpdate;
}
@@ -0,0 +1,92 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.manager;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
/**
* collector entity
*/
@Entity
@Table(name = "hzb_collector", uniqueConstraints = @UniqueConstraint(columnNames = {"name"}))
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "slave collector entity")
@EntityListeners(AuditingEntityListener.class)
public class Collector {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Schema(title = "primary id", example = "2")
private Long id;
@Schema(title = "collector identity name", description = "collector identity name")
@NotBlank(message = "name can not null")
private String name;
@Schema(title = "collector ip", description = "collector remote ip")
@NotBlank(message = "ip can not null")
private String ip;
@Schema(title = "collector version", description = "collector version")
private String version;
@Schema(title = "collector status: 0-online 1-offline")
@Min(0)
private byte status;
@Schema(title = "collector mode: public or private")
private String mode;
@Schema(title = "The creator of this record", example = "tom")
@CreatedBy
private String creator;
@Schema(title = "This record was last modified by")
@LastModifiedBy
private String modifier;
@Schema(title = "This record creation time (millisecond timestamp)")
@CreatedDate
private LocalDateTime gmtCreate;
@Schema(title = "Record the latest modification time (timestamp in milliseconds)")
@LastModifiedDate
private LocalDateTime gmtUpdate;
}
@@ -0,0 +1,83 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.manager;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Index;
import jakarta.persistence.Table;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
/**
* collector entity
*/
@Entity
@Table(name = "hzb_collector_monitor_bind", indexes = {
@Index(name = "idx_collector_monitor_collector", columnList = "collector"),
@Index(name = "idx_collector_monitor_monitor_id", columnList = "monitor_id")
})
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "slave collector monitor bind entity")
@EntityListeners(AuditingEntityListener.class)
public class CollectorMonitorBind {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Schema(title = "primary id", example = "23")
private Long id;
@Schema(title = "collector name", example = "87432674384")
private String collector;
@Schema(title = "monitor ID", example = "87432674336")
@Column(name = "monitor_id")
private Long monitorId;
@Schema(title = "The creator of this record", example = "tom")
@CreatedBy
private String creator;
@Schema(title = "This record was last modified by")
@LastModifiedBy
private String modifier;
@Schema(title = "This record creation time (millisecond timestamp)")
@CreatedDate
private LocalDateTime gmtCreate;
@Schema(title = "Record the latest modification time (timestamp in milliseconds)")
@LastModifiedDate
private LocalDateTime gmtUpdate;
}
@@ -0,0 +1,73 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.manager;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.Id;
import jakarta.persistence.Lob;
import jakarta.persistence.Table;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
/**
* monitor define entity
*/
@Entity
@Table(name = "hzb_define")
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "monitor define entity")
@EntityListeners(AuditingEntityListener.class)
public class Define {
@Id
@Schema(title = "app", example = "websocket")
private String app;
@Lob
@Schema(title = "define content", description = "define yml content")
private String content;
@Schema(title = "The creator of this record", example = "tom")
@CreatedBy
private String creator;
@Schema(title = "This record was last modified by")
@LastModifiedBy
private String modifier;
@Schema(title = "This record creation time (millisecond timestamp)")
@CreatedDate
private LocalDateTime gmtCreate;
@Schema(title = "Record the latest modification time (timestamp in milliseconds)")
@LastModifiedDate
private LocalDateTime gmtUpdate;
}
@@ -0,0 +1,78 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.manager;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_ONLY;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_WRITE;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotBlank;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
/**
* Common Config Entity
*/
@Entity
@Table(name = "hzb_config")
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "Common server config entity")
@EntityListeners(AuditingEntityListener.class)
public class GeneralConfig {
@Id
@Schema(title = "Config type: email sms, primary key ", description = "Config type: email sms, primary key ",
accessMode = READ_WRITE)
@NotBlank(message = "ip can not null")
private String type;
@Schema(title = "Config content", description = "Config contentformatjson", accessMode = READ_WRITE)
@Column(length = 8192)
private String content;
@Schema(title = "The creator of this record", example = "tom", accessMode = READ_ONLY)
@CreatedBy
private String creator;
@Schema(title = "This record was last modified by", example = "tom", accessMode = READ_ONLY)
@LastModifiedBy
private String modifier;
@Schema(title = "This record creation time (millisecond timestamp)", accessMode = READ_ONLY)
@CreatedDate
private LocalDateTime gmtCreate;
@Schema(title = "Record the latest modification time (timestamp in milliseconds)", accessMode = READ_ONLY)
@LastModifiedDate
private LocalDateTime gmtUpdate;
}
@@ -0,0 +1,44 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.manager;
import tools.jackson.core.type.TypeReference;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;
import java.util.List;
import org.apache.hertzbeat.common.util.JsonUtil;
import org.springframework.stereotype.Component;
/**
* json str to list Byte converter
*/
@Converter
@Component
public class JsonByteListAttributeConverter implements AttributeConverter<List<Byte>, String> {
@Override
public String convertToDatabaseColumn(List<Byte> attribute) {
return JsonUtil.toJson(attribute);
}
@Override
public List<Byte> convertToEntityAttribute(String dbData) {
TypeReference<List<Byte>> typeReference = new TypeReference<>() {};
return JsonUtil.fromJson(dbData, typeReference);
}
}
@@ -0,0 +1,61 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.manager;
import tools.jackson.core.type.TypeReference;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.apache.hertzbeat.common.util.JsonUtil;
import org.springframework.stereotype.Component;
/**
* json str to id list
*/
@Converter
@Component
public class JsonLongListAttributeConverter implements AttributeConverter<List<Long>, String> {
@Override
public String convertToDatabaseColumn(List<Long> attribute) {
return JsonUtil.toJson(attribute);
}
@Override
public List<Long> convertToEntityAttribute(String dbData) {
if (StringUtils.isBlank(dbData)) {
return List.of();
}
if (!JsonUtil.isJsonStr(dbData) && StringUtils.isNumeric(dbData)) {
return List.of(Long.parseLong(dbData));
}
TypeReference<List<Long>> typeReference = new TypeReference<>() {
};
List<Long> longList = JsonUtil.fromJson(dbData, typeReference);
if (longList == null && !dbData.isEmpty()) {
if (StringUtils.isNumeric(dbData)) {
return List.of(Long.parseLong(dbData));
} else {
throw new NumberFormatException("String convert to Long error");
}
} else {
return longList;
}
}
}
@@ -0,0 +1,45 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.manager;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;
import java.util.List;
import java.util.Map;
import org.apache.hertzbeat.common.util.JsonUtil;
import org.springframework.stereotype.Component;
import tools.jackson.core.type.TypeReference;
/**
* json map list str converter
*/
@Converter
@Component
public class JsonMapListAttributeConverter implements AttributeConverter<Map<String, List<String>>, String> {
@Override
public String convertToDatabaseColumn(Map<String, List<String>> attribute) {
return JsonUtil.toJson(attribute);
}
@Override
public Map<String, List<String>> convertToEntityAttribute(String dbData) {
TypeReference<Map<String, List<String>>> typeReference = new TypeReference<>() {};
return JsonUtil.fromJson(dbData, typeReference);
}
}
@@ -0,0 +1,44 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.manager;
import tools.jackson.core.type.TypeReference;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;
import java.util.List;
import org.apache.hertzbeat.common.util.JsonUtil;
import org.springframework.stereotype.Component;
/**
* json str to list paramDefine.Option
*/
@Converter
@Component
public class JsonOptionListAttributeConverter implements AttributeConverter<List<ParamDefine.Option>, String> {
@Override
public String convertToDatabaseColumn(List<ParamDefine.Option> attribute) {
return JsonUtil.toJson(attribute);
}
@Override
public List<ParamDefine.Option> convertToEntityAttribute(String dbData) {
TypeReference<List<ParamDefine.Option>> typeReference = new TypeReference<>() {};
return JsonUtil.fromJson(dbData, typeReference);
}
}
@@ -0,0 +1,57 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.manager;
import tools.jackson.core.type.TypeReference;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.apache.hertzbeat.common.util.JsonUtil;
import org.springframework.stereotype.Component;
/**
* Convert the list of strings to a JSON string
*/
@Converter
@Component
public class JsonStringListAttributeConverter implements AttributeConverter<List<String>, String> {
@Override
public String convertToDatabaseColumn(List<String> attribute) {
return JsonUtil.toJson(attribute);
}
@Override
public List<String> convertToEntityAttribute(String dbData) {
if (StringUtils.isBlank(dbData)) {
return List.of();
}
if (!JsonUtil.isJsonStr(dbData)) {
return List.of(dbData);
}
TypeReference<List<String>> typeReference = new TypeReference<>() {
};
List<String> stringList = JsonUtil.fromJson(dbData, typeReference);
if (stringList == null && !dbData.isEmpty()) {
return List.of(dbData);
} else {
return stringList;
}
}
}
@@ -0,0 +1,114 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.manager;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_ONLY;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_WRITE;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import java.time.LocalDateTime;
import java.util.Objects;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
/**
* Label Entity
*/
@Entity
@Table(name = "hzb_tag")
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "Label Entity")
@EntityListeners(AuditingEntityListener.class)
public class Label {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Schema(title = "Primary key index ID", example = "87584674384", accessMode = READ_ONLY)
private Long id;
@Schema(title = "Label Field", example = "app", accessMode = READ_WRITE)
@NotBlank(message = "name can not null")
private String name;
@Schema(title = "Label Value", example = "23", accessMode = READ_WRITE)
@Column(length = 2048)
private String tagValue;
@Schema(title = "Label Description", example = "Used for monitoring mysql", accessMode = READ_WRITE)
private String description;
@Schema(title = "Tag type 0: Auto-generated 1: user-generated 2: system preset",
accessMode = READ_WRITE)
@Min(0)
@Max(3)
private Byte type;
@Schema(title = "The creator of this record", example = "tom", accessMode = READ_ONLY)
@CreatedBy
private String creator;
@Schema(title = "The modifier of this record", example = "tom", accessMode = READ_ONLY)
@LastModifiedBy
private String modifier;
@Schema(title = "Record create time", example = "1612198922000", accessMode = READ_ONLY)
@CreatedDate
private LocalDateTime gmtCreate;
@Schema(title = "Record modify time", example = "1612198444000", accessMode = READ_ONLY)
@LastModifiedDate
private LocalDateTime gmtUpdate;
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Label tag = (Label) o;
return Objects.equals(name, tag.name) && Objects.equals(tagValue, tag.tagValue);
}
@Override
public int hashCode() {
int hash = 7;
hash = 13 * hash + (name == null ? 0 : name.hashCode()) + (tagValue == null ? 0 : tagValue.hashCode());
return hash;
}
}
@@ -0,0 +1,70 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.manager;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.CreatedDate;
/**
* Metrics Favorite Entity
*/
@Entity
@Table(name = "hzb_metrics_favorite",
uniqueConstraints = @UniqueConstraint(columnNames = {"creator", "monitor_id", "metrics_name"}))
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class MetricsFavorite {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank(message = "Creator cannot be null or blank")
@Size(max = 255, message = "Creator length cannot exceed 255 characters")
@Column(name = "creator", nullable = false)
private String creator;
@NotNull(message = "Monitor ID cannot be null")
@Column(name = "monitor_id", nullable = false)
private Long monitorId;
@NotBlank(message = "Metrics name cannot be null or blank")
@Size(max = 255, message = "Metrics name length cannot exceed 255 characters")
@Column(name = "metrics_name", nullable = false)
private String metricsName;
@CreatedDate
@Column(name = "create_time", updatable = false)
private LocalDateTime createTime;
}
@@ -0,0 +1,144 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.manager;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_ONLY;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_WRITE;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.Convert;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.Id;
import jakarta.persistence.Index;
import jakarta.persistence.Table;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.Size;
import java.time.LocalDateTime;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.hertzbeat.common.entity.alerter.JsonMapAttributeConverter;
import org.apache.hertzbeat.common.support.valid.HostValid;
import org.apache.hertzbeat.common.util.JsonUtil;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
/**
* Monitor Entity
*/
@Entity
@Table(name = "hzb_monitor", indexes = {
@Index(name = "idx_hzb_monitor_app", columnList = "app"),
@Index(name = "idx_hzb_monitor_instance", columnList = "instance"),
@Index(name = "idx_hzb_monitor_name", columnList = "name")
})
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "Monitor Entity")
@EntityListeners(AuditingEntityListener.class)
public class Monitor {
@Id
@Schema(title = "Monitor task ID", example = "87584674384", accessMode = READ_ONLY)
private Long id;
@Schema(title = "Collect task ID", example = "43243543543", accessMode = READ_ONLY)
private Long jobId;
@Schema(title = "task name", example = "Api-TanCloud.cn", accessMode = READ_WRITE)
@Size(max = 100)
private String name;
@Schema(title = "Type of monitoring", example = "TanCloud", accessMode = READ_WRITE)
@Size(max = 100)
private String app;
@Schema(title = "Scrape type: static | http_sd | dns_sd | zookeeper_sd", example = "static", accessMode = READ_WRITE)
@Size(max = 100)
private String scrape;
@Schema(title = "the monitor target: ip/domain+port or ip/domain", example = "192.167.25.11:8081", accessMode = READ_WRITE)
@Size(max = 100)
@HostValid
private String instance;
@Schema(title = "Monitoring of the acquisition interval time in seconds", example = "600", accessMode = READ_WRITE)
@Min(10)
private Integer intervals;
@Schema(title = "Schedule type: interval | cron", example = "interval", accessMode = READ_WRITE)
@Size(max = 20)
private String scheduleType;
@Schema(title = "Cron expression when scheduleType is cron", example = "0/5 * * * * ?", accessMode = READ_WRITE)
@Size(max = 100)
private String cronExpression;
@Schema(title = "Task status 0: Paused, 1: Up, 2: Down", accessMode = READ_WRITE)
@Min(0)
@Max(4)
private byte status;
@Schema(title = "Task type 0: Normal, 1: push auto create, 2: discovery auto create")
private byte type;
@Schema(title = "task label", example = "{env:test}", accessMode = READ_WRITE)
@Convert(converter = JsonMapAttributeConverter.class)
@Column(length = 4096)
private Map<String, String> labels;
@Schema(title = "task annotations", example = "{summary:this task looks good}", accessMode = READ_WRITE)
@Convert(converter = JsonMapAttributeConverter.class)
@Column(length = 4096)
private Map<String, String> annotations;
@Schema(title = "Monitor note description", example = "Availability monitoring of the SAAS website TanCloud", accessMode = READ_WRITE)
@Size(max = 255)
private String description;
@Schema(title = "The creator of this record", example = "tom", accessMode = READ_ONLY)
@CreatedBy
private String creator;
@Schema(title = "The modifier of this record", example = "tom", accessMode = READ_ONLY)
@LastModifiedBy
private String modifier;
@Schema(title = "Record create time", example = "2024-07-02T20:09:34.903217", accessMode = READ_ONLY)
@CreatedDate
private LocalDateTime gmtCreate;
@Schema(title = "Record modify time", example = "2024-07-02T20:09:34.903217", accessMode = READ_ONLY)
@LastModifiedDate
private LocalDateTime gmtUpdate;
@Override
public Monitor clone() {
return JsonUtil.fromJson(JsonUtil.toJson(this), getClass());
}
}
@@ -0,0 +1,85 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.manager;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Index;
import jakarta.persistence.Table;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
/**
* Monitor Bind
*/
@Entity
@Table(name = "hzb_monitor_bind", indexes = {
@Index(name = "index_monitor_bind", columnList = "biz_id"),
@Index(name = "index_monitor_bin", columnList = "monitor_id")
})
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "relation between monitor")
@EntityListeners(AuditingEntityListener.class)
public class MonitorBind {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Schema(title = "primary id", example = "23")
private Long id;
@Schema(title = "key string: ip:port")
private String keyStr;
@Schema(title = "connect bind id", example = "87432674384")
private Long bizId;
@Schema(title = "monitor ID", example = "87432674336")
@Column(name = "monitor_id")
private Long monitorId;
@Schema(title = "The creator of this record", example = "tom")
@CreatedBy
private String creator;
@Schema(title = "This record was last modified by")
@LastModifiedBy
private String modifier;
@Schema(title = "This record creation time (millisecond timestamp)")
@CreatedDate
private LocalDateTime gmtCreate;
@Schema(title = "Record the latest modification time (timestamp in milliseconds)")
@LastModifiedDate
private LocalDateTime gmtUpdate;
}
@@ -0,0 +1,121 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.manager;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_ONLY;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_WRITE;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Index;
import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.hertzbeat.common.util.JsonUtil;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
/**
* Monitor parameter values
*/
@Entity
@Table(
name = "hzb_param",
indexes = {@Index(name = "idx_hzb_param_monitor_id", columnList = "monitor_id")},
uniqueConstraints = {@UniqueConstraint(name = "uk_hzb_param_monitor_field", columnNames = {"monitor_id", "field"})}
)
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "Parameter Entity")
@EntityListeners(AuditingEntityListener.class)
public class Param {
/**
* Parameter primary key index ID
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Schema(title = "Parameter primary key index ID", example = "87584674384", accessMode = READ_ONLY)
private Long id;
/**
* Monitor ID
*/
@Schema(title = "Monitor task ID", example = "875846754543", accessMode = READ_WRITE)
@Column(name = "monitor_id")
private Long monitorId;
/**
* Parameter Field Identifier
*/
@Schema(title = "Parameter identifier field", example = "port", accessMode = READ_WRITE)
@Size(max = 100)
@Column(name = "field")
@NotBlank(message = "field can not null")
private String field;
/**
* Param Value
*/
@Schema(title = "parameter values", example = "8080", accessMode = READ_WRITE)
@Size(max = 8126)
@Column(length = 8126)
private String paramValue;
/**
* Parameter type 0: number 1: string 2: encrypted string 3: json string mapped by map
*/
@Schema(title = "Parameter types 0: number 1: string 2: encrypted string 3:map mapped json string 4:arrays string",
accessMode = READ_WRITE)
@Min(0)
private byte type;
/**
* Record create time
*/
@Schema(title = "Record create time", example = "1612198922000", accessMode = READ_ONLY)
@CreatedDate
private LocalDateTime gmtCreate;
/**
* Record the latest modification time
*/
@Schema(title = "Record modify time", example = "1612198444000", accessMode = READ_ONLY)
@LastModifiedDate
private LocalDateTime gmtUpdate;
@Override
public Param clone() {
// deep clone
return JsonUtil.fromJson(JsonUtil.toJson(this), getClass());
}
}
@@ -0,0 +1,212 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.manager;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_ONLY;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_WRITE;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.Convert;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.hertzbeat.common.entity.alerter.JsonMapAttributeConverter;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
/**
* Monitoring parameter definitions
*/
@Entity
@Table(name = "hzb_param_define")
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "Parameter structure definition entity")
@EntityListeners(AuditingEntityListener.class)
public class ParamDefine {
/**
* Parameter Structure ID
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Schema(title = "Parameter structure ID", example = "87584674384", accessMode = READ_ONLY)
private Long id;
/**
* Monitoring application type name
*/
@Schema(title = "Type of monitoring", example = "TanCloud", accessMode = READ_WRITE)
private String app;
/**
* Parameter field external display name
* Port
*/
@Schema(description = "The parameter field displays the internationalized name", example = "{zh-CN: '端口'}",
accessMode = READ_WRITE)
@Convert(converter = JsonMapAttributeConverter.class)
@SuppressWarnings("JpaAttributeTypeInspection")
@Column(length = 2048)
private Map<String, String> name;
/**
* Parameter Field Identifier
*/
@Schema(title = "Parameter field identifier", example = "port", accessMode = READ_WRITE)
private String field;
/**
* Field type, style (mostly map the input tag type attribute)
*/
@Schema(title = "Field type, style (mostly map the input tag type attribute)", example = "number", accessMode = READ_WRITE)
private String type;
/**
* Is it mandatory true-required false-optional
*/
@Schema(title = "Is it mandatory true-required false-optional", example = "true", accessMode = READ_WRITE)
private boolean required = false;
/**
* Parameter Default Value
*/
@Schema(title = "Parameter default values", example = "12", accessMode = READ_WRITE)
private String defaultValue;
/**
* Parameter input box prompt information
*/
@Schema(title = "Parameter input field prompt information", example = "enter your password", accessMode = READ_WRITE)
private String placeholder;
/**
* When type is number, use range to represent the range eg: 0-233
*/
@Schema(title = "When type is number, the range is represented by the range interval", example = "[0,233]", accessMode = READ_WRITE)
@Column(name = "param_range")
private String range;
/**
* When type is text, use limit to indicate the limit size of the string. The maximum is 255
*/
@Schema(title = "When type is text, use limit to indicate the limit size of the string. The maximum is 255",
example = "30", accessMode = READ_WRITE)
@Column(name = "param_limit")
private Short limit;
/**
* When the type is radio radio box, checkbox checkbox, options represents a list of optional values
* eg: {
* "key1":"value1",
* "key2":"value2"
* }
* key-Value display label
* value-True value
*/
@Schema(description = "When the type is radio radio box, checkbox checkbox, options represents a list of optional values",
example = "{key1,value1}", accessMode = READ_WRITE)
@Column(name = "param_options", length = 2048)
@Convert(converter = JsonOptionListAttributeConverter.class)
private List<Option> options;
/**
* Valid when type is key-value, indicating the alias description of the key
*/
@Schema(title = "Valid when type is key-value, indicating the alias description of the key", example = "Name", accessMode = READ_WRITE)
private String keyAlias;
/**
* Valid when type is key-value, indicating the alias description of value type
*/
@Schema(title = "Valid when type is key-value, indicating the alias description of value type", example = "Value", accessMode = READ_WRITE)
private String valueAlias;
/**
* Is it an advanced hidden parameter true-yes false-no
*/
@Schema(title = "Is it an advanced hidden parameter true-yes false-no", example = "true", accessMode = READ_WRITE)
private boolean hide = false;
/**
* The creator of this record
*/
@Schema(title = "The creator of this record", example = "tom", accessMode = READ_ONLY)
@CreatedBy
private String creator;
/**
* This record was last modified by
*/
@Schema(title = "The modifier of this record", example = "tom", accessMode = READ_ONLY)
@LastModifiedBy
private String modifier;
/**
* Record create time
*/
@Schema(title = "Record create time", example = "1612198922000", accessMode = READ_ONLY)
@CreatedDate
private LocalDateTime gmtCreate;
/**
* Record the latest modification time
*/
@Schema(title = "Record modify time", example = "1612198444000", accessMode = READ_ONLY)
@LastModifiedDate
private LocalDateTime gmtUpdate;
/**
* Depends on which parameters
*/
@Schema(title = "Depends on which parameters", example = "{field:[value1, value2, ...]}", accessMode = READ_WRITE)
@Convert(converter = JsonMapAttributeConverter.class)
private Map<String, List<Object>> depend;
/**
* Parameter option configuration
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public static final class Option {
/**
* value display label
*/
private String label;
/**
* optional value
*/
private String value;
}
}
@@ -0,0 +1,94 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.manager;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_ONLY;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_WRITE;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import java.util.Objects;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.hertzbeat.common.constants.PluginType;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
/**
* Plugin Entity
*/
@Entity
@Table(name = "hzb_plugin_item")
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "PluginItem Entity")
@EntityListeners(AuditingEntityListener.class)
public class PluginItem {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Schema(title = "Plugin Primary key index ID", example = "87584674384", accessMode = READ_ONLY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "metadata_id")
@JsonIgnore
private PluginMetadata pluginMetadata;
@Schema(title = "Plugin implementation class full path", example = "org.apache.hertzbeat.plugin.impl.DemoPluginImpl", accessMode = READ_WRITE)
private String classIdentifier;
@Schema(title = "Plugin type", example = "POST_ALERT", accessMode = READ_WRITE)
@Enumerated(EnumType.STRING)
private PluginType type;
public PluginItem(String classIdentifier, PluginType type) {
this.classIdentifier = classIdentifier;
this.type = type;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
PluginItem that = (PluginItem) o;
return Objects.equals(id, that.id) && Objects.equals(classIdentifier, that.classIdentifier) && type == that.type;
}
@Override
public int hashCode() {
return Objects.hash(id, classIdentifier, type);
}
}
@@ -0,0 +1,107 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.manager;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_ONLY;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_WRITE;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
/**
* Plugin Entity
*/
@Entity
@Table(name = "hzb_plugin_metadata")
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "Plugin Entity")
@EntityListeners(AuditingEntityListener.class)
public class PluginMetadata {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Schema(title = "Plugin Primary key index ID", example = "87584674384", accessMode = READ_ONLY)
private Long id;
@Schema(title = "plugin name", example = "notification plugin", accessMode = READ_WRITE)
@NotNull
private String name;
@Schema(title = "Plugin activation status", example = "true", accessMode = READ_WRITE)
private Boolean enableStatus;
@Schema(title = "Jar file path", example = "true", accessMode = READ_WRITE)
private String jarFilePath;
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
PluginMetadata that = (PluginMetadata) o;
return Objects.equals(id, that.id) && Objects.equals(name, that.name) && Objects.equals(enableStatus, that.enableStatus) && Objects.equals(jarFilePath,
that.jarFilePath) && Objects.equals(creator, that.creator) && Objects.equals(gmtCreate, that.gmtCreate);
}
@Override
public int hashCode() {
return Objects.hash(id, name, enableStatus, jarFilePath, creator, gmtCreate);
}
@Schema(title = "The creator of this record", example = "tom", accessMode = READ_ONLY)
@CreatedBy
private String creator;
@Schema(title = "Record create time", example = "1612198922000", accessMode = READ_ONLY)
@CreatedDate
private LocalDateTime gmtCreate;
@OneToMany(targetEntity = PluginItem.class, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinColumn(name = "metadata_id", referencedColumnName = "id")
private List<PluginItem> items;
@Schema(title = "Param count", example = "1", accessMode = READ_WRITE)
private Integer paramCount;
}
@@ -0,0 +1,101 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.manager;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_WRITE;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.Convert;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotBlank;
import java.time.LocalDateTime;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.hertzbeat.common.entity.alerter.JsonMapAttributeConverter;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
/**
* status page component entity
*/
@Entity
@Table(name = "hzb_status_page_component")
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "status page component entity")
@EntityListeners(AuditingEntityListener.class)
public class StatusPageComponent {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Schema(title = "ID", example = "87584674384")
private Long id;
@Schema(title = "org id", example = "1234")
private Long orgId;
@Schema(title = "component name", example = "Gateway")
@NotBlank
private String name;
@Schema(title = "component desc", example = "TanCloud Gateway")
private String description;
@Schema(title = "component label", example = "{env:test}", accessMode = READ_WRITE)
@Convert(converter = JsonMapAttributeConverter.class)
@Column(length = 4096)
private Map<String, String> labels;
@Schema(title = "calculate status method: 0-auto 1-manual", example = "0")
private byte method;
@Schema(title = "config state when use manual method: 0-Normal 1-Abnormal 2-unknown", example = "0")
private byte configState;
@Schema(title = "component current state: 0-Normal 1-Abnormal 2-unknown", example = "0")
private byte state;
@Schema(title = "The creator of this record", example = "tom")
@CreatedBy
private String creator;
@Schema(title = "The modifier of this record", example = "tom")
@LastModifiedBy
private String modifier;
@Schema(title = "Record create time", example = "1612198922000")
@CreatedDate
private LocalDateTime gmtCreate;
@Schema(title = "Record modify time", example = "1612198444000")
@LastModifiedDate
private LocalDateTime gmtUpdate;
}
@@ -0,0 +1,91 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.manager;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
/**
* status page history entity
*/
@Entity
@Table(name = "hzb_status_page_history")
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "status page component history entity")
@EntityListeners(AuditingEntityListener.class)
public class StatusPageHistory {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Schema(title = "ID", example = "87584674384")
private Long id;
@Schema(title = "component id", example = "1234")
private Long componentId;
@Schema(title = "component state: 0-Normal 1-Abnormal 2-unknown", example = "0")
private byte state;
@Schema(title = "state calculate timestamp", example = "4248574985744")
private Long timestamp;
@Schema(title = "state uptime percentage", example = "99.99")
private Double uptime;
@Schema(title = "state abnormal time(s)", example = "1000")
private Integer abnormal;
@Schema(title = "state unknown time(s)", example = "1000")
private Integer unknowing;
@Schema(title = "state normal tim(s)", example = "1000")
private Integer normal;
@Schema(title = "The creator of this record", example = "tom")
@CreatedBy
private String creator;
@Schema(title = "The modifier of this record", example = "tom")
@LastModifiedBy
private String modifier;
@Schema(title = "Record create time", example = "1612198922000")
@CreatedDate
private LocalDateTime gmtCreate;
@Schema(title = "Record modify time", example = "1612198444000")
@LastModifiedDate
private LocalDateTime gmtUpdate;
}
@@ -0,0 +1,112 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.manager;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.CascadeType;
import jakarta.persistence.ConstraintMode;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.FetchType;
import jakarta.persistence.ForeignKey;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.JoinTable;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotBlank;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Set;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
/**
* status page incident entity
*/
@Entity
@Table(name = "hzb_status_page_incident")
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "status page incident entity")
@EntityListeners(AuditingEntityListener.class)
public class StatusPageIncident {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Schema(title = "ID", example = "87584674384")
private Long id;
@Schema(title = "org id", example = "1234")
private Long orgId;
@Schema(title = "incident name", example = "Gateway")
@NotBlank
private String name;
@Schema(title = "incident current state: 0-Investigating 1-Identified 2-Monitoring 3-Resolved", example = "0")
private byte state;
@Schema(title = "incident start Investigating timestamp", example = "4248574985744")
private Long startTime;
@Schema(title = "incident end Resolved timestamp", example = "4248574985744")
private Long endTime;
@Schema(title = "The creator of this record", example = "tom")
@CreatedBy
private String creator;
@Schema(title = "The modifier of this record", example = "tom")
@LastModifiedBy
private String modifier;
@Schema(title = "Record create time", example = "1612198922000")
@CreatedDate
private LocalDateTime gmtCreate;
@Schema(title = "Record modify time", example = "1612198444000")
@LastModifiedDate
private LocalDateTime gmtUpdate;
@Schema(title = "status page components")
@ManyToMany(targetEntity = StatusPageComponent.class, cascade = CascadeType.MERGE, fetch = FetchType.EAGER)
@JoinTable(name = "hzb_status_page_incident_component_bind",
foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT),
inverseForeignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT),
joinColumns = {@JoinColumn(name = "incident_id", referencedColumnName = "id")},
inverseJoinColumns = {@JoinColumn(name = "component_id", referencedColumnName = "id")})
private List<StatusPageComponent> components;
@Schema(title = "status page incident content")
@OneToMany(targetEntity = StatusPageIncidentContent.class, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinColumn(name = "incident_id", referencedColumnName = "id")
private Set<StatusPageIncidentContent> contents;
}
@@ -0,0 +1,74 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.manager;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Index;
import jakarta.persistence.Table;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
/**
* status page incident component bind entity
*/
@Entity
@Table(name = "hzb_status_page_incident_component_bind", indexes = {
@Index(name = "index_incident_component", columnList = "incident_id"),
})
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "Incident Component Bind")
@EntityListeners(AuditingEntityListener.class)
public class StatusPageIncidentComponentBind {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Schema(title = "Identity", example = "87584674384")
private Long id;
@Schema(title = "Incident ID", example = "87432674384")
@Column(name = "incident_id")
private Long incidentId;
@Schema(title = "Component ID", example = "87432674336")
@Column(name = "component_id")
private Long componentId;
@Schema(title = "Record create time", example = "1612198922000")
@CreatedDate
private LocalDateTime gmtCreate;
@Schema(title = "Record modify time", example = "1612198444000")
@LastModifiedDate
private LocalDateTime gmtUpdate;
}
@@ -0,0 +1,87 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.manager;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotBlank;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
/**
* status page incident entity content.
*/
@Entity
@Table(name = "hzb_status_page_incident_content")
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "status page incident content entity")
@EntityListeners(AuditingEntityListener.class)
public class StatusPageIncidentContent {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Schema(title = "ID", example = "87584674384")
private Long id;
@Schema(title = "org id", example = "1234")
@Column(name = "incident_id")
private Long incidentId;
@Schema(title = "incident content message", example = "we find the gateway connection timeout")
@NotBlank
private String message;
@Schema(title = "incident state: 0-Investigating 1-Identified 2-Monitoring 3-Resolved", example = "0")
private byte state;
@Schema(title = "incident content message timestamp", example = "4248574985744")
private Long timestamp;
@Schema(title = "The creator of this record", example = "tom")
@CreatedBy
private String creator;
@Schema(title = "The modifier of this record", example = "tom")
@LastModifiedBy
private String modifier;
@Schema(title = "Record create time", example = "1612198922000")
@CreatedDate
private LocalDateTime gmtCreate;
@Schema(title = "Record modify time", example = "1612198444000")
@LastModifiedDate
private LocalDateTime gmtUpdate;
}
@@ -0,0 +1,98 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.manager;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotBlank;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
/**
* status page org entity
*/
@Entity
@Table(name = "hzb_status_page_org")
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "status page org entity")
@EntityListeners(AuditingEntityListener.class)
public class StatusPageOrg {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Schema(title = "ID", example = "87584674384")
private Long id;
@Schema(title = "org name", example = "TanCloud")
@NotBlank
private String name;
@Schema(title = "org desc", example = "TanCloud inc")
@NotBlank
private String description;
@Schema(title = "org home url", example = "https://tancloud.com")
@NotBlank
private String home;
@Schema(title = "org logo url", example = "logo.svg url")
@NotBlank
private String logo;
@Schema(title = "org feedback issue url", example = "contact@email.com")
private String feedback;
@Schema(title = "org theme background color", example = "#ffffff")
private String color;
@Schema(title = "org current state: 0-All Systems Operational 1-Some Systems Abnormal 2-All Systems Abnormal ",
example = "0")
private byte state;
@Schema(title = "The creator of this record", example = "tom")
@CreatedBy
private String creator;
@Schema(title = "The modifier of this record", example = "tom")
@LastModifiedBy
private String modifier;
@Schema(title = "Record create time", example = "1612198922000")
@CreatedDate
private LocalDateTime gmtCreate;
@Schema(title = "Record modify time", example = "1612198444000")
@LastModifiedDate
private LocalDateTime gmtUpdate;
}
@@ -0,0 +1,48 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.manager;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;
import java.time.OffsetDateTime;
import java.time.ZonedDateTime;
import org.springframework.stereotype.Component;
/**
* zone data time to OffsetDateTime
*/
@Converter
@Component
public class ZonedDateTimeAttributeConverter implements AttributeConverter<ZonedDateTime, OffsetDateTime> {
@Override
public OffsetDateTime convertToDatabaseColumn(ZonedDateTime attribute) {
if (attribute == null) {
return null;
}
return attribute.toOffsetDateTime();
}
@Override
public ZonedDateTime convertToEntityAttribute(OffsetDateTime dbData) {
if (dbData == null) {
return null;
}
return dbData.toZonedDateTime();
}
}
@@ -0,0 +1,53 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.push;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Index;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
/**
* push metrics entity
*/
@Entity
@Table(name = "hzb_push_metrics", indexes = {
@Index(name = "idx_push_metrics_monitor_id", columnList = "monitor_id"),
@Index(name = "idx_push_metrics_time", columnList = "time")
})
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@EntityListeners(AuditingEntityListener.class)
public class PushMetrics {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long monitorId;
private Long time;
private String metrics;
}
@@ -0,0 +1,88 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.warehouse;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_ONLY;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_WRITE;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Index;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* metrics history data entity
*/
@Entity
@Table(name = "hzb_history", indexes = {
@Index(name = "idx_hzb_history_instance", columnList = "instance"),
@Index(name = "idx_hzb_history_app", columnList = "app"),
@Index(name = "idx_hzb_history_metrics", columnList = "metrics"),
@Index(name = "idx_hzb_history_metric", columnList = "metric")
})
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "Metrics History Data Entity")
public class History {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Schema(description = "Metric data history entity primary key index ID", example = "87584674384", accessMode = READ_ONLY)
private Long id;
@Schema(title = "Monitoring instance", example = "127.0.0.1:8080", accessMode = READ_WRITE)
private String instance;
@Schema(title = "Monitoring Type mysql oracle db2")
private String app;
@Schema(title = "Monitoring Metrics innodb disk cpu")
private String metrics;
@Schema(title = "Monitoring Metric usage speed count")
private String metric;
@Column(length = 5000)
private String metricLabels;
@Schema(title = "Metric Type 0: Number 1String")
private Byte metricType;
@Schema(title = "Metric String Value")
@Column(length = 2048)
private String str;
@Schema(title = "Metric Integer Value")
private Integer int32;
@Schema(title = "Metric Number Value")
private Double dou;
@Schema(title = "Collect Time")
private Long time;
}
@@ -0,0 +1,173 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.queue.impl;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
import org.apache.hertzbeat.common.constants.DataQueueConstants;
import org.apache.hertzbeat.common.entity.log.LogEntry;
import org.apache.hertzbeat.common.entity.message.CollectRep;
import org.apache.hertzbeat.common.queue.CommonDataQueue;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
/**
* common data queue implement memory
*/
@Configuration
@ConditionalOnProperty(
prefix = DataQueueConstants.PREFIX,
name = DataQueueConstants.NAME,
havingValue = DataQueueConstants.IN_MEMORY,
matchIfMissing = true
)
@Slf4j
@Primary
public class InMemoryCommonDataQueue implements CommonDataQueue, DisposableBean {
private final LinkedBlockingQueue<CollectRep.MetricsData> metricsDataToAlertQueue;
private final LinkedBlockingQueue<CollectRep.MetricsData> metricsDataToStorageQueue;
private final LinkedBlockingQueue<CollectRep.MetricsData> serviceDiscoveryDataQueue;
private final LinkedBlockingQueue<LogEntry> logEntryQueue;
private final LinkedBlockingQueue<LogEntry> logEntryToStorageQueue;
public InMemoryCommonDataQueue() {
metricsDataToAlertQueue = new LinkedBlockingQueue<>();
metricsDataToStorageQueue = new LinkedBlockingQueue<>();
serviceDiscoveryDataQueue = new LinkedBlockingQueue<>();
logEntryQueue = new LinkedBlockingQueue<>();
logEntryToStorageQueue = new LinkedBlockingQueue<>();
}
public Map<String, Integer> getQueueSizeMetricsInfo() {
Map<String, Integer> metrics = new HashMap<>(8);
metrics.put("metricsDataToAlertQueue", metricsDataToAlertQueue.size());
metrics.put("metricsDataToStorageQueue", metricsDataToStorageQueue.size());
metrics.put("logEntryQueue", logEntryQueue.size());
metrics.put("logEntryToStorageQueue", logEntryToStorageQueue.size());
return metrics;
}
@Override
public CollectRep.MetricsData pollServiceDiscoveryData() throws InterruptedException {
return serviceDiscoveryDataQueue.take();
}
@Override
public CollectRep.MetricsData pollMetricsDataToAlerter() throws InterruptedException {
return metricsDataToAlertQueue.take();
}
@Override
public CollectRep.MetricsData pollMetricsDataToStorage() throws InterruptedException {
return metricsDataToStorageQueue.take();
}
@Override
public void sendMetricsData(CollectRep.MetricsData metricsData) {
metricsDataToAlertQueue.offer(metricsData);
}
@Override
public void sendMetricsDataToStorage(CollectRep.MetricsData metricsData) {
metricsDataToStorageQueue.offer(metricsData);
}
@Override
public void sendServiceDiscoveryData(CollectRep.MetricsData metricsData) {
serviceDiscoveryDataQueue.offer(metricsData);
}
@Override
public void sendLogEntry(LogEntry logEntry) {
logEntryQueue.offer(logEntry);
}
@Override
public LogEntry pollLogEntry() throws InterruptedException {
return logEntryQueue.take();
}
@Override
public void sendLogEntryToStorage(LogEntry logEntry) {
logEntryToStorageQueue.offer(logEntry);
}
@Override
public LogEntry pollLogEntryToStorage() throws InterruptedException {
return logEntryToStorageQueue.take();
}
@Override
public void sendLogEntryToAlertBatch(List<LogEntry> logEntries) {
if (logEntries == null || logEntries.isEmpty()) {
return;
}
for (LogEntry logEntry : logEntries) {
logEntryQueue.offer(logEntry);
}
}
@Override
public List<LogEntry> pollLogEntryToAlertBatch(int maxBatchSize) throws InterruptedException {
List<LogEntry> batch = new ArrayList<>(maxBatchSize);
LogEntry first = logEntryQueue.poll(1, TimeUnit.SECONDS);
if (first != null) {
batch.add(first);
logEntryQueue.drainTo(batch, maxBatchSize - 1);
}
return batch;
}
@Override
public void sendLogEntryToStorageBatch(List<LogEntry> logEntries) {
if (logEntries == null || logEntries.isEmpty()) {
return;
}
for (LogEntry logEntry : logEntries) {
logEntryToStorageQueue.offer(logEntry);
}
}
@Override
public List<LogEntry> pollLogEntryToStorageBatch(int maxBatchSize) throws InterruptedException {
List<LogEntry> batch = new ArrayList<>(maxBatchSize);
LogEntry first = logEntryToStorageQueue.poll(1, TimeUnit.SECONDS);
if (first != null) {
batch.add(first);
logEntryToStorageQueue.drainTo(batch, maxBatchSize - 1);
}
return batch;
}
@Override
public void destroy() {
metricsDataToAlertQueue.clear();
metricsDataToStorageQueue.clear();
serviceDiscoveryDataQueue.clear();
logEntryQueue.clear();
logEntryToStorageQueue.clear();
}
}
@@ -0,0 +1,376 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.queue.impl;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.locks.ReentrantLock;
import lombok.extern.slf4j.Slf4j;
import org.apache.hertzbeat.common.config.CommonProperties;
import org.apache.hertzbeat.common.constants.DataQueueConstants;
import org.apache.hertzbeat.common.entity.log.LogEntry;
import org.apache.hertzbeat.common.entity.message.CollectRep;
import org.apache.hertzbeat.common.queue.CommonDataQueue;
import org.apache.hertzbeat.common.serialize.KafkaLogEntryDeserializer;
import org.apache.hertzbeat.common.serialize.KafkaLogEntrySerializer;
import org.apache.hertzbeat.common.serialize.KafkaMetricsDataDeserializer;
import org.apache.hertzbeat.common.serialize.KafkaMetricsDataSerializer;
import org.apache.hertzbeat.common.support.exception.CommonDataQueueUnknownException;
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.common.serialization.LongDeserializer;
import org.apache.kafka.common.serialization.LongSerializer;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
/**
* common data queue implement kafka
*/
@Configuration
@ConditionalOnProperty(
prefix = DataQueueConstants.PREFIX,
name = DataQueueConstants.NAME,
havingValue = DataQueueConstants.KAFKA
)
@Slf4j
public class KafkaCommonDataQueue implements CommonDataQueue, DisposableBean {
private final ReentrantLock metricDataToAlertLock = new ReentrantLock();
private final ReentrantLock metricDataToStorageLock = new ReentrantLock();
private final ReentrantLock serviceDiscoveryDataLock = new ReentrantLock();
private final ReentrantLock logEntryLock = new ReentrantLock();
private final ReentrantLock logEntryToStorageLock = new ReentrantLock();
private final LinkedBlockingQueue<CollectRep.MetricsData> metricsDataToAlertQueue;
private final LinkedBlockingQueue<CollectRep.MetricsData> metricsDataToStorageQueue;
private final LinkedBlockingQueue<CollectRep.MetricsData> serviceDiscoveryDataQueue;
private final LinkedBlockingQueue<LogEntry> logEntryQueue;
private final LinkedBlockingQueue<LogEntry> logEntryToStorageQueue;
private final CommonProperties.KafkaProperties kafka;
private KafkaProducer<Long, CollectRep.MetricsData> metricsDataProducer;
private KafkaProducer<Long, LogEntry> logEntryProducer;
private KafkaConsumer<Long, CollectRep.MetricsData> metricsDataToAlertConsumer;
private KafkaConsumer<Long, CollectRep.MetricsData> metricsDataToStorageConsumer;
private KafkaConsumer<Long, CollectRep.MetricsData> serviceDiscoveryDataConsumer;
private KafkaConsumer<Long, LogEntry> logEntryConsumer;
private KafkaConsumer<Long, LogEntry> logEntryToStorageConsumer;
public KafkaCommonDataQueue(CommonProperties properties) {
if (properties == null || properties.getQueue() == null || properties.getQueue().getKafka() == null) {
log.error("init error, please config common.queue.kafka props in application.yml");
throw new IllegalArgumentException("please config common.queue.kafka props");
}
this.kafka = properties.getQueue().getKafka();
metricsDataToAlertQueue = new LinkedBlockingQueue<>();
metricsDataToStorageQueue = new LinkedBlockingQueue<>();
serviceDiscoveryDataQueue = new LinkedBlockingQueue<>();
logEntryQueue = new LinkedBlockingQueue<>();
logEntryToStorageQueue = new LinkedBlockingQueue<>();
initDataQueue();
}
private void initDataQueue() {
try {
Map<String, Object> producerConfig = new HashMap<>(3);
producerConfig.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafka.getServers());
producerConfig.put(ProducerConfig.ACKS_CONFIG, "all");
producerConfig.put(ProducerConfig.RETRIES_CONFIG, 3);
metricsDataProducer = new KafkaProducer<>(producerConfig, new LongSerializer(), new KafkaMetricsDataSerializer());
logEntryProducer = new KafkaProducer<>(producerConfig, new LongSerializer(), new KafkaLogEntrySerializer());
Map<String, Object> consumerConfig = new HashMap<>(4);
consumerConfig.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, kafka.getServers());
consumerConfig.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, "50");
consumerConfig.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false);
// 15 minute
consumerConfig.put(ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG, "900000");
consumerConfig.put("group.id", "default-consumer");
Map<String, Object> metricsToAlertConsumerConfig = new HashMap<>(consumerConfig);
metricsToAlertConsumerConfig.put("group.id", "metrics-alert-consumer");
metricsDataToAlertConsumer = new KafkaConsumer<>(metricsToAlertConsumerConfig, new LongDeserializer(), new KafkaMetricsDataDeserializer());
metricsDataToAlertConsumer.subscribe(Collections.singletonList(kafka.getMetricsDataTopic()));
Map<String, Object> metricsToStorageConsumerConfig = new HashMap<>(consumerConfig);
metricsToStorageConsumerConfig.put("group.id", "metrics-persistent-consumer");
metricsDataToStorageConsumer = new KafkaConsumer<>(metricsToStorageConsumerConfig, new LongDeserializer(), new KafkaMetricsDataDeserializer());
metricsDataToStorageConsumer.subscribe(Collections.singletonList(kafka.getMetricsDataToStorageTopic()));
Map<String, Object> serviceDiscoveryDataConsumerConfig = new HashMap<>(consumerConfig);
serviceDiscoveryDataConsumerConfig.put("group.id", "service-discovery-data-consumer");
serviceDiscoveryDataConsumer = new KafkaConsumer<>(serviceDiscoveryDataConsumerConfig, new LongDeserializer(),
new KafkaMetricsDataDeserializer());
serviceDiscoveryDataConsumer.subscribe(Collections.singletonList(kafka.getServiceDiscoveryDataTopic()));
Map<String, Object> logEntryConsumerConfig = new HashMap<>(consumerConfig);
logEntryConsumerConfig.put("group.id", "log-entry-consumer");
logEntryConsumer = new KafkaConsumer<>(logEntryConsumerConfig, new LongDeserializer(), new KafkaLogEntryDeserializer());
logEntryConsumer.subscribe(Collections.singletonList(kafka.getLogEntryDataTopic()));
Map<String, Object> logEntryToStorageConsumerConfig = new HashMap<>(consumerConfig);
logEntryToStorageConsumerConfig.put("group.id", "log-entry-storage-consumer");
logEntryToStorageConsumer = new KafkaConsumer<>(logEntryToStorageConsumerConfig, new LongDeserializer(), new KafkaLogEntryDeserializer());
logEntryToStorageConsumer.subscribe(Collections.singletonList(kafka.getLogEntryDataToStorageTopic()));
} catch (Exception e) {
log.error("please config common.queue.kafka props correctly", e);
throw e;
}
}
@Override
public CollectRep.MetricsData pollServiceDiscoveryData() throws InterruptedException {
return genericPollDataFunction(serviceDiscoveryDataQueue, serviceDiscoveryDataConsumer, serviceDiscoveryDataLock);
}
@Override
public CollectRep.MetricsData pollMetricsDataToAlerter() throws InterruptedException {
return genericPollDataFunction(metricsDataToAlertQueue, metricsDataToAlertConsumer, metricDataToAlertLock);
}
@Override
public CollectRep.MetricsData pollMetricsDataToStorage() throws InterruptedException {
return genericPollDataFunction(metricsDataToStorageQueue, metricsDataToStorageConsumer, metricDataToStorageLock);
}
public <T> T genericPollDataFunction(LinkedBlockingQueue<T> dataQueue, KafkaConsumer<Long, T> dataConsumer, ReentrantLock lock) throws InterruptedException {
T pollData = dataQueue.poll();
if (pollData != null) {
return pollData;
}
lock.lockInterruptibly();
try {
ConsumerRecords<Long, T> records = dataConsumer.poll(Duration.ofSeconds(1));
int index = 0;
for (ConsumerRecord<Long, T> record : records) {
if (index == 0) {
pollData = record.value();
} else {
dataQueue.offer(record.value());
}
index++;
}
dataConsumer.commitAsync();
} catch (Exception e) {
log.error(e.getMessage());
throw new CommonDataQueueUnknownException(e.getMessage(), e);
} finally {
lock.unlock();
}
return pollData;
}
@Override
public void sendMetricsData(CollectRep.MetricsData metricsData) {
if (metricsDataProducer != null) {
ProducerRecord<Long, CollectRep.MetricsData> record =
new ProducerRecord<>(kafka.getMetricsDataTopic(), metricsData);
metricsDataProducer.send(record);
} else {
log.error("metricsDataProducer is not enabled");
}
}
@Override
public void sendMetricsDataToStorage(CollectRep.MetricsData metricsData) {
if (metricsDataProducer != null) {
ProducerRecord<Long, CollectRep.MetricsData> record =
new ProducerRecord<>(kafka.getMetricsDataToStorageTopic(), metricsData);
metricsDataProducer.send(record);
} else {
log.error("metricsDataProducer is not enabled");
}
}
@Override
public void sendServiceDiscoveryData(CollectRep.MetricsData metricsData) {
if (metricsDataProducer != null) {
ProducerRecord<Long, CollectRep.MetricsData> record =
new ProducerRecord<>(kafka.getServiceDiscoveryDataTopic(), metricsData);
metricsDataProducer.send(record);
} else {
log.error("metricsDataProducer is not enabled");
}
}
@Override
public void sendLogEntry(LogEntry logEntry) {
if (logEntryProducer != null) {
try {
ProducerRecord<Long, LogEntry> record = new ProducerRecord<>(kafka.getLogEntryDataTopic(), logEntry);
logEntryProducer.send(record);
} catch (Exception e) {
log.error("Failed to send LogEntry to Kafka: {}", e.getMessage());
// Fallback to memory queue if Kafka fails
logEntryQueue.offer(logEntry);
}
} else {
log.warn("logEntryProducer is not enabled, using memory queue");
logEntryQueue.offer(logEntry);
}
}
@Override
public LogEntry pollLogEntry() throws InterruptedException {
return genericPollDataFunction(logEntryQueue, logEntryConsumer, logEntryLock);
}
@Override
public void sendLogEntryToStorage(LogEntry logEntry) {
if (logEntryProducer != null) {
try {
ProducerRecord<Long, LogEntry> record = new ProducerRecord<>(kafka.getLogEntryDataToStorageTopic(), logEntry);
logEntryProducer.send(record);
} catch (Exception e) {
log.error("Failed to send LogEntry to storage via Kafka: {}", e.getMessage());
// Fallback to memory queue if Kafka fails
logEntryToStorageQueue.offer(logEntry);
}
} else {
log.warn("logEntryProducer is not enabled, using memory queue for storage");
logEntryToStorageQueue.offer(logEntry);
}
}
@Override
public LogEntry pollLogEntryToStorage() throws InterruptedException {
return genericPollDataFunction(logEntryToStorageQueue, logEntryToStorageConsumer, logEntryToStorageLock);
}
@Override
public void sendLogEntryToAlertBatch(List<LogEntry> logEntries) {
if (logEntries == null || logEntries.isEmpty()) {
return;
}
if (logEntryProducer != null) {
try {
for (LogEntry logEntry : logEntries) {
ProducerRecord<Long, LogEntry> record = new ProducerRecord<>(kafka.getLogEntryDataTopic(), logEntry);
logEntryProducer.send(record);
}
} catch (Exception e) {
log.error("Failed to send LogEntry batch to Kafka: {}", e.getMessage());
for (LogEntry logEntry : logEntries) {
logEntryQueue.offer(logEntry);
}
}
} else {
log.warn("logEntryProducer is not enabled, using memory queue");
for (LogEntry logEntry : logEntries) {
logEntryQueue.offer(logEntry);
}
}
}
@Override
public List<LogEntry> pollLogEntryToAlertBatch(int maxBatchSize) throws InterruptedException {
return genericBatchPollDataFunction(logEntryQueue, logEntryConsumer, logEntryLock, maxBatchSize);
}
@Override
public void sendLogEntryToStorageBatch(List<LogEntry> logEntries) {
if (logEntries == null || logEntries.isEmpty()) {
return;
}
if (logEntryProducer != null) {
try {
for (LogEntry logEntry : logEntries) {
ProducerRecord<Long, LogEntry> record = new ProducerRecord<>(kafka.getLogEntryDataToStorageTopic(), logEntry);
logEntryProducer.send(record);
}
} catch (Exception e) {
log.error("Failed to send LogEntry batch to storage via Kafka: {}", e.getMessage());
for (LogEntry logEntry : logEntries) {
logEntryToStorageQueue.offer(logEntry);
}
}
} else {
log.warn("logEntryProducer is not enabled, using memory queue for storage");
for (LogEntry logEntry : logEntries) {
logEntryToStorageQueue.offer(logEntry);
}
}
}
@Override
public List<LogEntry> pollLogEntryToStorageBatch(int maxBatchSize) throws InterruptedException {
return genericBatchPollDataFunction(logEntryToStorageQueue, logEntryToStorageConsumer, logEntryToStorageLock, maxBatchSize);
}
public <T> List<T> genericBatchPollDataFunction(LinkedBlockingQueue<T> dataQueue, KafkaConsumer<Long, T> dataConsumer,
ReentrantLock lock, int maxBatchSize) throws InterruptedException {
List<T> batch = new ArrayList<>(maxBatchSize);
lock.lockInterruptibly();
try {
dataQueue.drainTo(batch, maxBatchSize);
if (batch.size() >= maxBatchSize) {
return batch;
}
ConsumerRecords<Long, T> records = dataConsumer.poll(Duration.ofSeconds(1));
for (ConsumerRecord<Long, T> record : records) {
if (batch.size() < maxBatchSize) {
batch.add(record.value());
} else {
dataQueue.offer(record.value());
}
}
dataConsumer.commitAsync();
} catch (Exception e) {
log.error(e.getMessage());
} finally {
lock.unlock();
}
return batch;
}
@Override
public void destroy() throws Exception {
if (metricsDataProducer != null) {
metricsDataProducer.close();
}
if (metricsDataToAlertConsumer != null) {
metricsDataToAlertConsumer.close();
}
if (metricsDataToStorageConsumer != null) {
metricsDataToStorageConsumer.close();
}
if (serviceDiscoveryDataConsumer != null) {
serviceDiscoveryDataConsumer.close();
}
if (logEntryProducer != null) {
logEntryProducer.close();
}
if (logEntryConsumer != null) {
logEntryConsumer.close();
}
if (logEntryToStorageConsumer != null) {
logEntryToStorageConsumer.close();
}
}
}
@@ -0,0 +1,242 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.queue.impl;
import java.util.ArrayList;
import java.util.List;
import io.lettuce.core.KeyValue;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.sync.RedisCommands;
import lombok.extern.slf4j.Slf4j;
import org.apache.hertzbeat.common.config.CommonProperties;
import org.apache.hertzbeat.common.constants.DataQueueConstants;
import org.apache.hertzbeat.common.entity.log.LogEntry;
import org.apache.hertzbeat.common.entity.message.CollectRep;
import org.apache.hertzbeat.common.queue.CommonDataQueue;
import org.apache.hertzbeat.common.serialize.RedisLogEntryCodec;
import org.apache.hertzbeat.common.serialize.RedisMetricsDataCodec;
import org.apache.hertzbeat.common.support.exception.CommonDataQueueUnknownException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import java.util.Objects;
/**
* common data queue implement redis.
*/
@Slf4j
@Configuration
@ConditionalOnProperty(
prefix = DataQueueConstants.PREFIX,
name = DataQueueConstants.NAME,
havingValue = DataQueueConstants.REDIS
)
public class RedisCommonDataQueue implements CommonDataQueue, DisposableBean {
private final RedisClient redisClient;
private final StatefulRedisConnection<String, CollectRep.MetricsData> connection;
private final RedisCommands<String, CollectRep.MetricsData> syncCommands;
private final StatefulRedisConnection<String, LogEntry> logEntryConnection;
private final RedisCommands<String, LogEntry> logEntrySyncCommands;
private final String metricsDataQueueNameToStorage;
private final String metricsDataQueueNameForServiceDiscovery;
private final String metricsDataQueueNameToAlerter;
private final String logEntryQueueName;
private final String logEntryToStorageQueueName;
private final CommonProperties.RedisProperties redisProperties;
private final Long waitTimeout;
public RedisCommonDataQueue(CommonProperties properties) {
if (properties == null || properties.getQueue() == null || properties.getQueue().getRedis() == null) {
log.error("init error, please config common.queue.redis props in application.yml");
throw new IllegalArgumentException("please config common.queue.redis props");
}
this.redisProperties = properties.getQueue().getRedis();
this.redisClient = RedisClient.create(
RedisURI.builder()
.withHost(redisProperties.getRedisHost())
.withPort(redisProperties.getRedisPort())
.build()
);
RedisMetricsDataCodec codec = new RedisMetricsDataCodec();
this.connection = redisClient.connect(codec);
this.syncCommands = connection.sync();
RedisLogEntryCodec logCodec = new RedisLogEntryCodec();
this.logEntryConnection = redisClient.connect(logCodec);
this.logEntrySyncCommands = logEntryConnection.sync();
this.metricsDataQueueNameToStorage = redisProperties.getMetricsDataQueueNameToPersistentStorage();
this.metricsDataQueueNameForServiceDiscovery = redisProperties.getMetricsDataQueueNameForServiceDiscovery();
this.metricsDataQueueNameToAlerter = redisProperties.getMetricsDataQueueNameToAlerter();
this.logEntryQueueName = redisProperties.getLogEntryQueueName();
this.logEntryToStorageQueueName = redisProperties.getLogEntryToStorageQueueName();
this.waitTimeout = Objects.requireNonNullElse(redisProperties.getWaitTimeout(), 1L);
}
@Override
public CollectRep.MetricsData pollMetricsDataToAlerter() throws InterruptedException {
return genericBlockingPollFunction(metricsDataQueueNameToAlerter, syncCommands);
}
@Override
public CollectRep.MetricsData pollMetricsDataToStorage() throws InterruptedException {
return genericBlockingPollFunction(metricsDataQueueNameToStorage, syncCommands);
}
@Override
public CollectRep.MetricsData pollServiceDiscoveryData() throws InterruptedException {
return genericBlockingPollFunction(metricsDataQueueNameForServiceDiscovery, syncCommands);
}
@Override
public void sendMetricsData(CollectRep.MetricsData metricsData) {
try {
syncCommands.lpush(metricsDataQueueNameToAlerter, metricsData);
} catch (Exception e) {
log.error(e.getMessage());
}
}
@Override
public void sendMetricsDataToStorage(CollectRep.MetricsData metricsData) {
try {
syncCommands.lpush(metricsDataQueueNameToStorage, metricsData);
} catch (Exception e) {
log.error(e.getMessage());
}
}
@Override
public void sendServiceDiscoveryData(CollectRep.MetricsData metricsData) {
try {
syncCommands.lpush(metricsDataQueueNameForServiceDiscovery, metricsData);
} catch (Exception e) {
log.error(e.getMessage());
}
}
@Override
public void sendLogEntry(LogEntry logEntry) {
try {
logEntrySyncCommands.lpush(logEntryQueueName, logEntry);
} catch (Exception e) {
log.error("Failed to send LogEntry to Redis: {}", e.getMessage());
}
}
@Override
public LogEntry pollLogEntry() throws InterruptedException {
return genericBlockingPollFunction(logEntryQueueName, logEntrySyncCommands);
}
@Override
public void sendLogEntryToStorage(LogEntry logEntry) {
try {
logEntrySyncCommands.lpush(logEntryToStorageQueueName, logEntry);
} catch (Exception e) {
log.error("Failed to send LogEntry to storage via Redis: {}", e.getMessage());
}
}
@Override
public LogEntry pollLogEntryToStorage() throws InterruptedException {
return genericBlockingPollFunction(logEntryToStorageQueueName, logEntrySyncCommands);
}
@Override
@SuppressWarnings("unchecked")
public void sendLogEntryToAlertBatch(List<LogEntry> logEntries) {
if (logEntries == null || logEntries.isEmpty()) {
return;
}
try {
logEntrySyncCommands.lpush(logEntryQueueName, logEntries.toArray(new LogEntry[0]));
} catch (Exception e) {
log.error("Failed to send LogEntry batch to Redis: {}", e.getMessage());
}
}
@Override
public List<LogEntry> pollLogEntryToAlertBatch(int maxBatchSize) throws InterruptedException {
return genericBatchPollFunction(logEntryQueueName, logEntrySyncCommands, maxBatchSize);
}
@Override
@SuppressWarnings("unchecked")
public void sendLogEntryToStorageBatch(List<LogEntry> logEntries) {
if (logEntries == null || logEntries.isEmpty()) {
return;
}
try {
logEntrySyncCommands.lpush(logEntryToStorageQueueName, logEntries.toArray(new LogEntry[0]));
} catch (Exception e) {
log.error("Failed to send LogEntry batch to storage via Redis: {}", e.getMessage());
}
}
@Override
public List<LogEntry> pollLogEntryToStorageBatch(int maxBatchSize) throws InterruptedException {
return genericBatchPollFunction(logEntryToStorageQueueName, logEntrySyncCommands, maxBatchSize);
}
@Override
public void destroy() {
connection.close();
logEntryConnection.close();
redisClient.shutdown();
}
private <T> T genericBlockingPollFunction(String key, RedisCommands<String, T> commands) throws InterruptedException {
try {
// Use BRPOP for blocking pop with the configured timeout.
// If data arrives, it returns immediately; if it times out, it returns null.
KeyValue<String, T> keyData = commands.brpop(waitTimeout, key);
if (keyData != null) {
return keyData.getValue();
} else {
// Returns null on timeout
return null;
}
} catch (Exception e) {
log.error("Redis BRPOP failed: {}", e.getMessage());
throw new CommonDataQueueUnknownException(e.getMessage(), e);
}
}
private List<LogEntry> genericBatchPollFunction(String key, RedisCommands<String, LogEntry> commands, int maxBatchSize) {
List<LogEntry> batch = new ArrayList<>(maxBatchSize);
try {
List<LogEntry> elements = commands.rpop(key, maxBatchSize);
if (elements != null) {
batch.addAll(elements);
}
} catch (Exception e) {
log.error("Redis batch poll failed: {}", e.getMessage());
}
return batch;
}
}
@@ -0,0 +1,123 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.support;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
import org.apache.hertzbeat.common.concurrent.BackgroundTaskExecutor;
import org.apache.hertzbeat.common.concurrent.ManagedExecutor;
import org.apache.hertzbeat.common.concurrent.ManagedExecutors;
import org.apache.hertzbeat.common.config.VirtualThreadProperties;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* common task worker thread pool
*/
@Component
@Slf4j
public class CommonThreadPool implements BackgroundTaskExecutor, DisposableBean {
private final ManagedExecutor workerExecutor;
private final ManagedExecutor longRunningExecutor;
public CommonThreadPool() {
this(VirtualThreadProperties.defaults());
}
@Autowired
public CommonThreadPool(VirtualThreadProperties virtualThreadProperties) {
VirtualThreadProperties properties =
virtualThreadProperties == null ? VirtualThreadProperties.defaults() : virtualThreadProperties;
this.workerExecutor = createWorkerExecutor(properties);
this.longRunningExecutor = createLongRunningExecutor(properties, workerExecutor);
}
private ManagedExecutor createWorkerExecutor(VirtualThreadProperties properties) {
Thread.UncaughtExceptionHandler handler = (thread, throwable) -> {
log.error("common executor has uncaughtException.");
log.error(throwable.getMessage(), throwable);
};
if (properties.enabled()) {
VirtualThreadProperties.PoolProperties poolProperties = properties.common();
return ManagedExecutors.newVirtualExecutor("common-worker", "common-worker-",
poolProperties.mode(), poolProperties.maxConcurrentJobs(), handler);
}
return ManagedExecutors.wrap("common-worker", createLegacyExecutor(handler));
}
private ManagedExecutor createLongRunningExecutor(VirtualThreadProperties properties, ManagedExecutor fallback) {
if (!properties.enabled()) {
return fallback;
}
return ManagedExecutors.newPlatformExecutor("common-long-running", "common-long-running-",
(thread, throwable) -> {
log.error("common longRunningExecutor has uncaughtException.");
log.error(throwable.getMessage(), throwable);
});
}
private ExecutorService createLegacyExecutor(Thread.UncaughtExceptionHandler handler) {
ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setUncaughtExceptionHandler(handler)
.setDaemon(true)
.setNameFormat("common-worker-%d")
.build();
return new ThreadPoolExecutor(1,
Integer.MAX_VALUE,
10,
TimeUnit.SECONDS,
new SynchronousQueue<>(),
threadFactory,
new ThreadPoolExecutor.AbortPolicy());
}
/**
* Run the task thread
* @param runnable Task
* @throws RejectedExecutionException when thread pool full
*/
public void execute(Runnable runnable) throws RejectedExecutionException {
workerExecutor.execute(runnable);
}
/**
* Run a long-lived task outside of the short-task execution lane.
*
* @param runnable task
*/
public void executeLongRunning(Runnable runnable) {
longRunningExecutor.execute(runnable);
}
@Override
public void destroy() throws Exception {
workerExecutor.close();
if (longRunningExecutor != workerExecutor) {
longRunningExecutor.close();
}
}
}
@@ -0,0 +1,82 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.support;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
/**
* Spring ApplicationContext Holder
*/
@Component
public class SpringContextHolder implements ApplicationContextAware {
private static ApplicationContext applicationContext;
private static ConfigurableApplicationContext configurableApplicationContext;
@Override
public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
set(applicationContext);
if (applicationContext instanceof ConfigurableApplicationContext context) {
configurableApplicationContext = context;
}
}
private static void set(ApplicationContext applicationContext) {
SpringContextHolder.applicationContext = applicationContext;
}
public static ApplicationContext getApplicationContext() {
assertApplicationContext();
return applicationContext;
}
@SuppressWarnings("unchecked")
public static <T> T getBean(String beanName) {
assertApplicationContext();
return (T) applicationContext.getBean(beanName);
}
public static <T> T getBean(Class<T> clazz) {
assertApplicationContext();
return (T) applicationContext.getBean(clazz);
}
public static void shutdown() {
assertApplicationContext();
configurableApplicationContext.close();
}
public static boolean isActive() {
if (configurableApplicationContext == null) {
return false;
}
return configurableApplicationContext.isActive();
}
private static void assertApplicationContext() {
if (null == applicationContext || null == configurableApplicationContext) {
throw new RuntimeException("applicationContext is null, please inject the springContextHolder");
}
}
}
@@ -0,0 +1,31 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.support.event;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
/**
* Ai Provider configuration change event
*/
public class AiProviderConfigChangeEvent extends ApplicationEvent {
public AiProviderConfigChangeEvent(ApplicationContext source) {
super(source);
}
}
@@ -0,0 +1,40 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.support.event;
import org.springframework.context.ApplicationEvent;
/**
* the event for monitor delete
*/
public class MonitorDeletedEvent extends ApplicationEvent {
/**
* monitoring id
*/
private final Long monitorId;
public MonitorDeletedEvent(Object source, Long monitorId) {
super(source);
this.monitorId = monitorId;
}
public Long getMonitorId() {
return monitorId;
}
}
@@ -0,0 +1,30 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.support.event;
import org.springframework.context.ApplicationEvent;
/**
* the event for sms config change
*/
public class SmsConfigChangeEvent extends ApplicationEvent {
public SmsConfigChangeEvent(Object source) {
super(source);
}
}
@@ -0,0 +1,30 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.support.event;
import org.springframework.context.ApplicationEvent;
/**
* the event for system config change
*/
public class SystemConfigChangeEvent extends ApplicationEvent {
public SystemConfigChangeEvent(Object source) {
super(source);
}
}
@@ -0,0 +1,34 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.support.valid;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import org.apache.hertzbeat.common.util.CommonUtil;
/**
* email validator
*/
public class EmailParamValidator implements ConstraintValidator<EmailValid, String> {
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
// validate email
return CommonUtil.validateEmail(value);
}
}
@@ -0,0 +1,43 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.support.valid;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
* EmailValid
*/
@Target({ FIELD, PARAMETER })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = EmailParamValidator.class)
public @interface EmailValid {
String message() default "Email value is invalid";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
@@ -0,0 +1,64 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.support.valid;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import org.apache.hertzbeat.common.util.IpDomainUtil;
import org.springframework.util.StringUtils;
/**
* Host Param Validator
*/
public class HostParamValidator implements ConstraintValidator<HostValid, String> {
public static final String HTTP = "http://";
public static final String HTTPS = "https://";
public static final String BLANK = "";
public static final String PATTERN_HTTP = "(?i)http://";
public static final String PATTERN_HTTPS = "(?i)https://";
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (!StringUtils.hasText(value)) {
return true;
}
if (value.toLowerCase().contains(HTTP)){
value = value.replaceFirst(PATTERN_HTTP, BLANK);
}
if (value.toLowerCase().contains(HTTPS)){
value = value.replaceFirst(PATTERN_HTTPS, BLANK);
}
String hostPart = value;
if (value.contains(":")) {
// if contains multiple ":", it may be IPv6 with port
if (value.lastIndexOf(":") > value.indexOf(":") && value.contains("[")) {
int portIndex = value.lastIndexOf(":");
hostPart = value.substring(0, portIndex);
} else if (value.split(":").length == 2) {
// it is IPv4 or domain with port
String[] parts = value.split(":");
hostPart = parts[0];
}
}
return IpDomainUtil.validateIpDomain(hostPart);
}
}
@@ -0,0 +1,43 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.support.valid;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
* Host Param Validator
*/
@Target({ FIELD, PARAMETER })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = HostParamValidator.class)
public @interface HostValid {
String message() default "Host need ipv4,ipv6,hostname or domain,<br>EG:127.0.0.1 hertzbeat.com";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
@@ -0,0 +1,33 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.support.valid;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import org.apache.hertzbeat.common.util.CommonUtil;
/**
* PhoneNum Param Validator
*/
public class PhoneNumParamValidator implements ConstraintValidator<PhoneNumValid, String> {
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return CommonUtil.validatePhoneNum(value);
}
}
@@ -0,0 +1,43 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.support.valid;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
* PhoneNum Param Validator
*/
@Target({ FIELD, PARAMETER })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = PhoneNumParamValidator.class)
public @interface PhoneNumValid {
String message() default "Phone num value is invalid";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
@@ -0,0 +1,150 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.support.valid;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.select.LateralSubSelect;
import net.sf.jsqlparser.statement.select.ParenthesedSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SetOperationList;
import net.sf.jsqlparser.statement.select.WithItem;
import net.sf.jsqlparser.util.TablesNamesFinder;
import org.springframework.util.CollectionUtils;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* SQL Security Validator using JSqlParser 5.1+.
* Security Policy:
* 1. Only SELECT statements are allowed.
* 2. All referenced tables must be in the whitelist.
* 3. Subqueries, UNION, CTE, LATERAL are blocked.
*/
@Slf4j
public class SqlSecurityValidator {
private final Set<String> allowedTables;
public SqlSecurityValidator(Collection<String> allowedTables) {
if (CollectionUtils.isEmpty(allowedTables)) {
this.allowedTables = new HashSet<>();
} else {
this.allowedTables = allowedTables.stream()
.map(this::normalizeIdentifier)
.collect(Collectors.toSet());
}
}
public void validate(String sql) throws SqlSecurityException {
if (sql == null || sql.trim().isEmpty()) {
throw new SqlSecurityException("SQL statement cannot be empty");
}
Statement statement;
try {
statement = CCJSqlParserUtil.parse(sql);
} catch (JSQLParserException e) {
log.warn("Failed to parse SQL: {}", sql, e);
throw new SqlSecurityException("Invalid SQL syntax: " + e.getMessage(), e);
}
if (!(statement instanceof Select select)) {
throw new SqlSecurityException("Only SELECT statements are allowed.");
}
// Check for CTE at top level
if (select.getWithItemsList() != null && !select.getWithItemsList().isEmpty()) {
throw new SqlSecurityException("CTE (WITH clause) is not allowed");
}
// Use custom TablesNamesFinder that throws on dangerous structures
SecurityTablesNamesFinder finder = new SecurityTablesNamesFinder();
List<String> tables;
try {
tables = finder.getTableList(statement);
} catch (SecurityViolationException e) {
throw new SqlSecurityException(e.getMessage());
}
validateTables(tables);
}
private void validateTables(List<String> tables) throws SqlSecurityException {
if (CollectionUtils.isEmpty(tables)) {
return;
}
if (allowedTables.isEmpty()) {
throw new SqlSecurityException("No access allowed: whitelist is empty.");
}
for (String table : tables) {
String normalizedTable = normalizeIdentifier(table);
if (!allowedTables.contains(normalizedTable)) {
throw new SqlSecurityException("Access to table '" + table + "' is not allowed. "
+ "Allowed tables: " + allowedTables);
}
}
}
private String normalizeIdentifier(String identifier) {
if (identifier == null) {
return "";
}
return identifier.replace("\"", "").replace("`", "").replace("'", "").toLowerCase();
}
private static class SecurityViolationException extends RuntimeException {
SecurityViolationException(String message) {
super(message);
}
}
/**
* Custom TablesNamesFinder that throws exceptions on dangerous SQL structures.
* Extends TablesNamesFinder with proper generic type to avoid raw type warnings.
*/
private static class SecurityTablesNamesFinder extends TablesNamesFinder<Void> {
@Override
public Void visit(ParenthesedSelect parenthesedSelect, Object context) {
throw new SecurityViolationException("Subqueries are not allowed");
}
@Override
public Void visit(SetOperationList setOpList, Object context) {
throw new SecurityViolationException("UNION and set operations are not allowed");
}
@Override
public Void visit(LateralSubSelect lateralSubSelect, Object context) {
throw new SecurityViolationException("LATERAL subqueries are not allowed");
}
@Override
public Void visit(WithItem withItem, Object context) {
throw new SecurityViolationException("CTE (WITH clause) is not allowed");
}
}
}
@@ -0,0 +1,80 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.util;
import java.util.Map;
import org.apache.hertzbeat.common.constants.ExportFileConstants;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
/**
* File utils.
*/
public final class FileUtil {
private FileUtil() {
}
private static final Map<String, String> fileTypes;
static {
fileTypes = Map.of(
ExportFileConstants.JsonFile.FILE_SUFFIX, ExportFileConstants.JsonFile.TYPE,
ExportFileConstants.YamlFile.FILE_SUFFIX, ExportFileConstants.YamlFile.TYPE,
ExportFileConstants.ExcelFile.FILE_SUFFIX, ExportFileConstants.ExcelFile.TYPE
);
}
/**
* Get file name.
* @param file {@link MultipartFile}
* @return file name
*/
public static String getFileName(MultipartFile file) {
var fileName = file.getOriginalFilename();
if (!StringUtils.hasText(fileName)) {
return "";
}
return fileName;
}
/**
* Get file type.
* @param file {@link MultipartFile}
* @return file type
*/
public static String getFileType(MultipartFile file) {
var fileName = getFileName(file);
if (!StringUtils.hasText(fileName)) {
return "";
}
var dotIndex = fileName.lastIndexOf('.');
if (dotIndex == -1 || dotIndex == fileName.length() - 1) {
return "";
}
var fileNameExtension = fileName.substring(dotIndex);
return fileTypes.get(fileNameExtension);
}
}
@@ -0,0 +1,79 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.util;
import javax.naming.AuthenticationException;
import org.apache.hertzbeat.common.constants.CommonConstants;
import org.apache.hertzbeat.common.entity.dto.Message;
import org.springframework.http.ResponseEntity;
/**
* A tool which make the restful response be easy to use
*/
public class ResponseUtil {
public static <T, E extends Exception> ResponseEntity<Message<T>> handle(Supplier<T, E> supplier) {
try {
T result = supplier.get();
return ResponseEntity.ok(Message.success(result));
} catch (Exception e) {
byte err = CommonConstants.FAIL_CODE;
if (e.getClass().equals(AuthenticationException.class)) {
err = CommonConstants.LOGIN_FAILED_CODE;
}
return ResponseEntity.ok(Message.fail(err, e.getMessage()));
}
}
public static <T, E extends Exception> ResponseEntity<Message<T>> handle(Runnable runner) {
try {
runner.run();
return ResponseEntity.ok(Message.success());
} catch (Exception e) {
byte err = CommonConstants.FAIL_CODE;
if (e.getClass().equals(AuthenticationException.class)) {
err = CommonConstants.LOGIN_FAILED_CODE;
}
return ResponseEntity.ok(Message.fail(err, e.getMessage()));
}
}
/**
* Supplier interface for getting result
*/
public interface Supplier<T, E extends Exception> {
/**
* Gets a result.
*
* @return a result
*/
T get() throws E;
}
/**
* Runnable interface for running
*/
public interface Runnable {
/**
* Run target method.
*/
void run() throws Exception;
}
}
@@ -0,0 +1,16 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
org.apache.hertzbeat.common.config.CommonConfig
@@ -0,0 +1,41 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.cache;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import java.util.LinkedList;
import org.junit.jupiter.api.Test;
/**
* Test case for {@link CacheFactory}
*/
public class CacheFactoryTest {
@Test
void common() {
CacheFactory.setNoticeCache(new LinkedList<>());
CacheFactory.setAlertSilenceCache(new LinkedList<>());
assertNotNull(CacheFactory.getAlertSilenceCache());
assertNotNull(CacheFactory.getNoticeCache());
CacheFactory.clearNoticeCache();
CacheFactory.clearAlertSilenceCache();
assertNull(CacheFactory.getAlertSilenceCache());
assertNull(CacheFactory.getNoticeCache());
}
}
@@ -0,0 +1,79 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.cache;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import java.time.Duration;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/**
* Test case for {@link CaffeineCacheServiceImpl}
*/
class CaffeineCacheTest {
private CommonCacheService<String, String> cacheService;
@BeforeEach
void setUp() {
cacheService = new CaffeineCacheServiceImpl<>(10, 100, Duration.ofMillis(3000), false);
}
@Test
void testCache() throws InterruptedException {
String key = "key";
String value = "value";
// test get & put
cacheService.put(key, value);
Assertions.assertEquals(value, cacheService.get(key));
Assertions.assertTrue(cacheService.containsKey(key));
// test remove
cacheService.remove(key);
Assertions.assertNull(cacheService.get(key));
// test expire time
cacheService.put(key, value);
Thread.sleep(3000);
Assertions.assertNull(cacheService.get(key));
Assertions.assertNull(cacheService.get(key));
// test clear
for (int i = 0; i < 10; i++) {
cacheService.put(key + i, value);
}
cacheService.clear();
for (int i = 0; i < 10; i++) {
Assertions.assertNull(cacheService.get(key + i));
}
// test new method : cacheService.putAndGetOld(key,newValue)
String oldValue = "oldOne";
String newValue = "newOne";
cacheService.put(key, oldValue);
Assertions.assertEquals(oldValue, cacheService.putAndGetOld(key, newValue));
Assertions.assertEquals(newValue, cacheService.get(key));
}
@Test
void weekCache() {
CommonCacheService<String, String> cache = new CaffeineCacheServiceImpl<>(10, 100, Duration.ofMillis(3000), true);
assertNotNull(cache);
}
}
@@ -0,0 +1,50 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.config;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.apache.hertzbeat.common.entity.dto.sms.SmsConfig;
import org.junit.jupiter.api.Test;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
class SmsConfigBindingTest {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withUserConfiguration(BindingConfig.class);
@Test
void bindsRuntimeSmsConfigWithoutSpringAnnotationsOnModel() {
contextRunner.withPropertyValues(
"alerter.sms.enable=true",
"alerter.sms.type=smslocal",
"alerter.sms.smslocal.api-key=test-key")
.run(context -> {
SmsConfig smsConfig = context.getBean(SmsConfig.class);
assertTrue(smsConfig.isEnable());
assertEquals("smslocal", smsConfig.getType());
assertEquals("test-key", smsConfig.getSmslocal().getApiKey());
});
}
@EnableConfigurationProperties(SmsConfigBinding.class)
static class BindingConfig {
}
}
@@ -0,0 +1,114 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.config;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.apache.hertzbeat.common.concurrent.AdmissionMode;
import org.junit.jupiter.api.Test;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
class VirtualThreadPropertiesTest {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withUserConfiguration(BindingConfig.class);
@Test
void defaultsRemainSafeWithoutExternalConfiguration() {
VirtualThreadProperties properties = VirtualThreadProperties.defaults();
assertTrue(properties.enabled());
assertEquals(AdmissionMode.LIMIT_AND_REJECT, properties.collector().mode());
assertEquals(512, properties.collector().maxConcurrentJobs());
assertEquals(AdmissionMode.UNBOUNDED_VT, properties.common().mode());
assertEquals(AdmissionMode.LIMIT_AND_REJECT, properties.manager().mode());
assertEquals(10, properties.manager().maxConcurrentJobs());
assertEquals(AdmissionMode.LIMIT_AND_REJECT, properties.alerter().notifyPool().mode());
assertEquals(64, properties.alerter().notifyPool().maxConcurrentJobs());
assertEquals(10, properties.alerter().periodicMaxConcurrentJobs());
assertEquals(10, properties.alerter().logWorker().maxConcurrentJobs());
assertEquals(1000, properties.alerter().logWorker().queueCapacity());
assertEquals(2, properties.alerter().reduce().maxConcurrentJobs());
assertEquals(0, properties.alerter().reduce().queueCapacity());
assertEquals(2, properties.alerter().windowEvaluator().maxConcurrentJobs());
assertEquals(0, properties.alerter().windowEvaluator().queueCapacity());
assertEquals(4, properties.alerter().notifyMaxConcurrentPerChannel());
assertEquals(AdmissionMode.UNBOUNDED_VT, properties.warehouse().mode());
assertTrue(properties.async().enabled());
assertEquals(256, properties.async().concurrencyLimit());
assertTrue(properties.async().rejectWhenLimitReached());
assertEquals(5000L, properties.async().taskTerminationTimeout());
}
@Test
void collectorModeOnlyBindingRetainsDefaultConcurrency() {
contextRunner.withPropertyValues("hertzbeat.vthreads.collector.mode=LIMIT_AND_REJECT")
.run(context -> {
VirtualThreadProperties properties = context.getBean(VirtualThreadProperties.class);
assertEquals(AdmissionMode.LIMIT_AND_REJECT, properties.collector().mode());
assertEquals(512, properties.collector().maxConcurrentJobs());
});
}
@Test
void collectorModeOverrideUsesConfiguredMode() {
contextRunner.withPropertyValues("hertzbeat.vthreads.collector.mode=LIMIT_AND_BLOCK")
.run(context -> {
VirtualThreadProperties properties = context.getBean(VirtualThreadProperties.class);
assertEquals(AdmissionMode.LIMIT_AND_BLOCK, properties.collector().mode());
assertEquals(512, properties.collector().maxConcurrentJobs());
});
}
@Test
void notifyModeOnlyBindingRetainsDefaultConcurrency() {
contextRunner.withPropertyValues("hertzbeat.vthreads.alerter.notify.mode=LIMIT_AND_BLOCK")
.run(context -> {
VirtualThreadProperties properties = context.getBean(VirtualThreadProperties.class);
assertEquals(AdmissionMode.LIMIT_AND_BLOCK, properties.alerter().notifyPool().mode());
assertEquals(64, properties.alerter().notifyPool().maxConcurrentJobs());
});
}
@Test
void logWorkerQueueOnlyBindingRetainsDefaultConcurrency() {
contextRunner.withPropertyValues("hertzbeat.vthreads.alerter.log-worker.queue-capacity=32")
.run(context -> {
VirtualThreadProperties properties = context.getBean(VirtualThreadProperties.class);
assertEquals(10, properties.alerter().logWorker().maxConcurrentJobs());
assertEquals(32, properties.alerter().logWorker().queueCapacity());
});
}
@EnableConfigurationProperties(VirtualThreadPropertiesBinding.class)
static class BindingConfig {
@Bean
VirtualThreadProperties virtualThreadProperties(VirtualThreadPropertiesBinding binding) {
return binding.toRuntimeProperties();
}
}
}
@@ -0,0 +1,323 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.manager;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;
import org.junit.jupiter.api.Test;
import java.time.LocalDateTime;
import java.util.Set;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* Test case for {@link MetricsFavorite}
*/
class MetricsFavoriteTest {
private final ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
private final Validator validator = factory.getValidator();
@Test
void testBuilder() {
String creator = "testUser";
Long monitorId = 1L;
String metricsName = "cpu";
LocalDateTime createTime = LocalDateTime.now();
MetricsFavorite favorite = MetricsFavorite.builder()
.id(1L)
.creator(creator)
.monitorId(monitorId)
.metricsName(metricsName)
.createTime(createTime)
.build();
assertNotNull(favorite);
assertEquals(1L, favorite.getId());
assertEquals(creator, favorite.getCreator());
assertEquals(monitorId, favorite.getMonitorId());
assertEquals(metricsName, favorite.getMetricsName());
assertEquals(createTime, favorite.getCreateTime());
}
@Test
void testBuilderWithoutOptionalFields() {
String creator = "testUser";
Long monitorId = 1L;
String metricsName = "cpu";
MetricsFavorite favorite = MetricsFavorite.builder()
.creator(creator)
.monitorId(monitorId)
.metricsName(metricsName)
.build();
assertNotNull(favorite);
assertNull(favorite.getId());
assertEquals(creator, favorite.getCreator());
assertEquals(monitorId, favorite.getMonitorId());
assertEquals(metricsName, favorite.getMetricsName());
assertNull(favorite.getCreateTime());
}
@Test
void testDefaultConstructor() {
MetricsFavorite favorite = new MetricsFavorite();
assertNotNull(favorite);
assertNull(favorite.getId());
assertNull(favorite.getCreator());
assertNull(favorite.getMonitorId());
assertNull(favorite.getMetricsName());
assertNull(favorite.getCreateTime());
}
@Test
void testSettersAndGetters() {
MetricsFavorite favorite = new MetricsFavorite();
String creator = "testUser";
Long monitorId = 1L;
String metricsName = "cpu";
LocalDateTime createTime = LocalDateTime.now();
favorite.setId(1L);
favorite.setCreator(creator);
favorite.setMonitorId(monitorId);
favorite.setMetricsName(metricsName);
favorite.setCreateTime(createTime);
assertEquals(1L, favorite.getId());
assertEquals(creator, favorite.getCreator());
assertEquals(monitorId, favorite.getMonitorId());
assertEquals(metricsName, favorite.getMetricsName());
assertEquals(createTime, favorite.getCreateTime());
}
@Test
void testValidation_ValidEntity() {
MetricsFavorite favorite = MetricsFavorite.builder()
.creator("testUser")
.monitorId(1L)
.metricsName("cpu")
.createTime(LocalDateTime.now())
.build();
Set<ConstraintViolation<MetricsFavorite>> violations = validator.validate(favorite);
assertTrue(violations.isEmpty());
}
@Test
void testValidation_NullCreator() {
MetricsFavorite favorite = MetricsFavorite.builder()
.creator(null)
.monitorId(1L)
.metricsName("cpu")
.createTime(LocalDateTime.now())
.build();
Set<ConstraintViolation<MetricsFavorite>> violations = validator.validate(favorite);
assertFalse(violations.isEmpty());
assertTrue(violations.stream().anyMatch(v -> v.getPropertyPath().toString().equals("creator")));
}
@Test
void testValidation_BlankCreator() {
MetricsFavorite favorite = MetricsFavorite.builder()
.creator(" ")
.monitorId(1L)
.metricsName("cpu")
.createTime(LocalDateTime.now())
.build();
Set<ConstraintViolation<MetricsFavorite>> violations = validator.validate(favorite);
assertFalse(violations.isEmpty());
assertTrue(violations.stream().anyMatch(v -> v.getPropertyPath().toString().equals("creator")));
}
@Test
void testValidation_CreatorTooLong() {
String longCreator = "a".repeat(256);
MetricsFavorite favorite = MetricsFavorite.builder()
.creator(longCreator)
.monitorId(1L)
.metricsName("cpu")
.createTime(LocalDateTime.now())
.build();
Set<ConstraintViolation<MetricsFavorite>> violations = validator.validate(favorite);
assertFalse(violations.isEmpty());
assertTrue(violations.stream().anyMatch(v -> v.getPropertyPath().toString().equals("creator")));
}
@Test
void testValidation_NullMonitorId() {
MetricsFavorite favorite = MetricsFavorite.builder()
.creator("testUser")
.monitorId(null)
.metricsName("cpu")
.createTime(LocalDateTime.now())
.build();
Set<ConstraintViolation<MetricsFavorite>> violations = validator.validate(favorite);
assertFalse(violations.isEmpty());
assertTrue(violations.stream().anyMatch(v -> v.getPropertyPath().toString().equals("monitorId")));
}
@Test
void testValidation_NullMetricsName() {
MetricsFavorite favorite = MetricsFavorite.builder()
.creator("testUser")
.monitorId(1L)
.metricsName(null)
.createTime(LocalDateTime.now())
.build();
Set<ConstraintViolation<MetricsFavorite>> violations = validator.validate(favorite);
assertFalse(violations.isEmpty());
assertTrue(violations.stream().anyMatch(v -> v.getPropertyPath().toString().equals("metricsName")));
}
@Test
void testValidation_BlankMetricsName() {
MetricsFavorite favorite = MetricsFavorite.builder()
.creator("testUser")
.monitorId(1L)
.metricsName(" ")
.createTime(LocalDateTime.now())
.build();
Set<ConstraintViolation<MetricsFavorite>> violations = validator.validate(favorite);
assertFalse(violations.isEmpty());
assertTrue(violations.stream().anyMatch(v -> v.getPropertyPath().toString().equals("metricsName")));
}
@Test
void testValidation_MetricsNameTooLong() {
String longMetricsName = "a".repeat(256);
MetricsFavorite favorite = MetricsFavorite.builder()
.creator("testUser")
.monitorId(1L)
.metricsName(longMetricsName)
.createTime(LocalDateTime.now())
.build();
Set<ConstraintViolation<MetricsFavorite>> violations = validator.validate(favorite);
assertFalse(violations.isEmpty());
assertTrue(violations.stream().anyMatch(v -> v.getPropertyPath().toString().equals("metricsName")));
}
@Test
void testEqualsAndHashCode() {
LocalDateTime now = LocalDateTime.now();
MetricsFavorite favorite1 = MetricsFavorite.builder()
.id(1L)
.creator("testUser")
.monitorId(1L)
.metricsName("cpu")
.createTime(now)
.build();
MetricsFavorite favorite2 = MetricsFavorite.builder()
.id(1L)
.creator("testUser")
.monitorId(1L)
.metricsName("cpu")
.createTime(now)
.build();
MetricsFavorite favorite3 = MetricsFavorite.builder()
.id(2L)
.creator("testUser")
.monitorId(1L)
.metricsName("cpu")
.createTime(now)
.build();
assertEquals(favorite1, favorite2);
assertEquals(favorite1.hashCode(), favorite2.hashCode());
assertNotEquals(favorite1, favorite3);
assertNotEquals(favorite1.hashCode(), favorite3.hashCode());
}
@Test
void testToString() {
MetricsFavorite favorite = MetricsFavorite.builder()
.id(1L)
.creator("testUser")
.monitorId(1L)
.metricsName("cpu")
.createTime(LocalDateTime.now())
.build();
String toString = favorite.toString();
assertNotNull(toString);
assertTrue(toString.contains("MetricsFavorite"));
assertTrue(toString.contains("testUser"));
assertTrue(toString.contains("cpu"));
}
@Test
void testCreatorMaxLength() {
String maxLengthCreator = "a".repeat(255);
MetricsFavorite favorite = MetricsFavorite.builder()
.creator(maxLengthCreator)
.monitorId(1L)
.metricsName("cpu")
.createTime(LocalDateTime.now())
.build();
Set<ConstraintViolation<MetricsFavorite>> violations = validator.validate(favorite);
assertTrue(violations.isEmpty());
assertEquals(255, favorite.getCreator().length());
}
@Test
void testMetricsNameMaxLength() {
String maxLengthMetricsName = "a".repeat(255);
MetricsFavorite favorite = MetricsFavorite.builder()
.creator("testUser")
.monitorId(1L)
.metricsName(maxLengthMetricsName)
.createTime(LocalDateTime.now())
.build();
Set<ConstraintViolation<MetricsFavorite>> violations = validator.validate(favorite);
assertTrue(violations.isEmpty());
assertEquals(255, favorite.getMetricsName().length());
}
}
@@ -0,0 +1,161 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.queue.impl;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
import org.apache.hertzbeat.common.entity.log.LogEntry;
import org.apache.hertzbeat.common.entity.message.CollectRep;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/**
* Test case for {@link InMemoryCommonDataQueue}
*/
class InMemoryCommonDataQueueTest {
private InMemoryCommonDataQueue queue;
@BeforeEach
void setUp() {
queue = new InMemoryCommonDataQueue();
}
@Test
void testMetricsData() throws InterruptedException {
var metricsData = CollectRep.MetricsData.newBuilder().build();
queue.sendMetricsData(metricsData);
CollectRep.MetricsData polledMetricsData = queue.pollMetricsDataToAlerter();
assertNotNull(polledMetricsData);
assertEquals(metricsData, polledMetricsData);
}
@Test
void testLogEntry() throws InterruptedException {
// Create a test log entry with comprehensive data
Map<String, Object> attributes = new HashMap<>();
attributes.put("service.name", "hertzbeat");
attributes.put("service.version", "1.0.0");
Map<String, Object> resource = new HashMap<>();
resource.put("host.name", "localhost");
resource.put("os.type", "linux");
LogEntry.InstrumentationScope scope = LogEntry.InstrumentationScope.builder()
.name("org.apache.hertzbeat.test")
.version("1.0.0")
.build();
LogEntry logEntry = LogEntry.builder()
.timeUnixNano(Instant.now().toEpochMilli() * 1_000_000L)
.observedTimeUnixNano(Instant.now().toEpochMilli() * 1_000_000L)
.severityNumber(9) // INFO level
.severityText("INFO")
.body("Test log message for hertzbeat queue")
.attributes(attributes)
.resource(resource)
.instrumentationScope(scope)
.traceId("1234567890abcdef1234567890abcdef")
.spanId("1234567890abcdef")
.traceFlags(1)
.build();
// Test sending and polling log entry
queue.sendLogEntry(logEntry);
LogEntry polledLogEntry = queue.pollLogEntry();
assertNotNull(polledLogEntry);
assertEquals(logEntry.getSeverityText(), polledLogEntry.getSeverityText());
assertEquals(logEntry.getBody(), polledLogEntry.getBody());
assertEquals(logEntry.getTraceId(), polledLogEntry.getTraceId());
assertEquals(logEntry.getSpanId(), polledLogEntry.getSpanId());
}
@Test
void testLogEntryToStorage() throws InterruptedException {
// Create a simple log entry for storage test
LogEntry logEntry = LogEntry.builder()
.timeUnixNano(Instant.now().toEpochMilli() * 1_000_000L)
.severityNumber(17) // ERROR level
.severityText("ERROR")
.body("Error log message for storage")
.build();
// Test sending and polling log entry to storage
queue.sendLogEntryToStorage(logEntry);
LogEntry polledLogEntry = queue.pollLogEntryToStorage();
assertNotNull(polledLogEntry);
assertEquals(logEntry.getSeverityText(), polledLogEntry.getSeverityText());
assertEquals(logEntry.getBody(), polledLogEntry.getBody());
assertEquals(logEntry.getSeverityNumber(), polledLogEntry.getSeverityNumber());
}
@Test
void testGetQueueSizeMetricsInfo() {
Map<String, Integer> metricsInfo = queue.getQueueSizeMetricsInfo();
assertEquals(0, metricsInfo.get("metricsDataToAlertQueue"));
assertEquals(0, metricsInfo.get("metricsDataToStorageQueue"));
assertEquals(0, metricsInfo.get("logEntryQueue"));
assertEquals(0, metricsInfo.get("logEntryToStorageQueue"));
// Add metrics data and log entries
queue.sendMetricsData(CollectRep.MetricsData.newBuilder().build());
queue.sendLogEntry(LogEntry.builder().body("Test log").build());
queue.sendLogEntryToStorage(LogEntry.builder().body("Storage log").build());
metricsInfo = queue.getQueueSizeMetricsInfo();
assertEquals(1, metricsInfo.get("metricsDataToAlertQueue"));
assertEquals(0, metricsInfo.get("metricsDataToStorageQueue"));
assertEquals(1, metricsInfo.get("logEntryQueue"));
assertEquals(1, metricsInfo.get("logEntryToStorageQueue"));
}
@Test
void testDestroy() {
// Add both metrics data and log entries before destroy
queue.sendMetricsData(CollectRep.MetricsData.newBuilder().build());
queue.sendLogEntry(LogEntry.builder().body("Test log").build());
queue.sendLogEntryToStorage(LogEntry.builder().body("Storage log").build());
queue.destroy();
Map<String, Integer> metricsInfo = queue.getQueueSizeMetricsInfo();
assertEquals(0, metricsInfo.get("metricsDataToAlertQueue"));
assertEquals(0, metricsInfo.get("metricsDataToStorageQueue"));
assertEquals(0, metricsInfo.get("logEntryQueue"));
assertEquals(0, metricsInfo.get("logEntryToStorageQueue"));
}
}
@@ -0,0 +1,260 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.queue.impl;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyCollection;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.withSettings;
import java.lang.reflect.Field;
import java.time.Duration;
import java.time.Instant;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.hertzbeat.common.config.CommonProperties;
import org.apache.hertzbeat.common.entity.log.LogEntry;
import org.apache.hertzbeat.common.entity.message.CollectRep;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.common.TopicPartition;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
/**
* Test case for {@link KafkaCommonDataQueue}
*/
@ExtendWith(MockitoExtension.class)
class KafkaCommonDataQueueTest {
@Mock(lenient = true)
private KafkaConsumer<Long, CollectRep.MetricsData> metricsDataToAlertConsumer;
@Mock(lenient = true)
private KafkaProducer<Long, CollectRep.MetricsData> metricsDataProducer;
@Mock(lenient = true)
private KafkaProducer<Long, LogEntry> logEntryProducer;
@Mock(lenient = true)
private KafkaConsumer<Long, LogEntry> logEntryConsumer;
@Mock(lenient = true)
private KafkaConsumer<Long, LogEntry> logEntryToStorageConsumer;
private KafkaCommonDataQueue kafkaCommonDataQueue;
private CommonProperties commonProperties;
@BeforeEach
void setUp() throws Exception {
commonProperties = mock(CommonProperties.class);
CommonProperties.DataQueueProperties dataQueueProperties = mock(CommonProperties.DataQueueProperties.class, withSettings().lenient());
CommonProperties.KafkaProperties kafkaProperties = mock(CommonProperties.KafkaProperties.class, withSettings().lenient());
when(commonProperties.getQueue()).thenReturn(dataQueueProperties);
when(dataQueueProperties.getKafka()).thenReturn(kafkaProperties);
// Set all required topics
when(kafkaProperties.getMetricsDataTopic()).thenReturn("metricsDataTopic");
when(kafkaProperties.getLogEntryDataTopic()).thenReturn("logEntryDataTopic");
when(kafkaProperties.getLogEntryDataToStorageTopic()).thenReturn("logEntryDataToStorageTopic");
when(kafkaProperties.getAlertsDataTopic()).thenReturn("alertsDataTopic");
when(kafkaProperties.getMetricsDataToStorageTopic()).thenReturn("metricsDataToStorageTopic");
when(kafkaProperties.getServiceDiscoveryDataTopic()).thenReturn("serviceDiscoveryDataTopic");
when(kafkaProperties.getServers()).thenReturn("localhost:9092");
// Simulate the subscribe method for consumers
doNothing().when(metricsDataToAlertConsumer).subscribe(anyCollection());
doNothing().when(logEntryConsumer).subscribe(anyCollection());
doNothing().when(logEntryToStorageConsumer).subscribe(anyCollection());
kafkaCommonDataQueue = new KafkaCommonDataQueue(commonProperties);
// Use reflection to set private fields
setPrivateField(kafkaCommonDataQueue, "metricsDataProducer", metricsDataProducer);
setPrivateField(kafkaCommonDataQueue, "metricsDataToAlertConsumer", metricsDataToAlertConsumer);
setPrivateField(kafkaCommonDataQueue, "logEntryProducer", logEntryProducer);
setPrivateField(kafkaCommonDataQueue, "logEntryConsumer", logEntryConsumer);
setPrivateField(kafkaCommonDataQueue, "logEntryToStorageConsumer", logEntryToStorageConsumer);
}
@Test
void testSendMetricsData() {
CollectRep.MetricsData metricsData = CollectRep.MetricsData.newBuilder()
.setMetrics("test metrics")
.build();
kafkaCommonDataQueue.sendMetricsData(metricsData);
verify(metricsDataProducer).send(any());
}
@Test
void testPollMetricsDataToAlerter() throws InterruptedException {
// Create a test data
CollectRep.MetricsData expectedData = CollectRep.MetricsData.newBuilder()
.setMetrics("test metrics")
.build();
// Create a ConsumerRecord containing test data
ConsumerRecord<Long, CollectRep.MetricsData> record =
new ConsumerRecord<>("metricsDataTopic", 0, 0L, 1L, expectedData);
// Create a ConsumerRecords containing a single record.
Map<TopicPartition, List<ConsumerRecord<Long, CollectRep.MetricsData>>> recordsMap =
Collections.singletonMap(
new TopicPartition("metricsDataTopic", 0),
Collections.singletonList(record));
ConsumerRecords<Long, CollectRep.MetricsData> records = new ConsumerRecords<>(recordsMap);
when(metricsDataToAlertConsumer.poll(any(Duration.class))).thenReturn(records);
CollectRep.MetricsData result = kafkaCommonDataQueue.pollMetricsDataToAlerter();
assertEquals(expectedData, result);
verify(metricsDataToAlertConsumer).commitAsync();
}
@Test
void testSendLogEntry() {
// Create a test log entry with comprehensive data
Map<String, Object> attributes = new HashMap<>();
attributes.put("service.name", "hertzbeat");
attributes.put("service.version", "1.0.0");
LogEntry logEntry = LogEntry.builder()
.timeUnixNano(Instant.now().toEpochMilli() * 1_000_000L)
.severityNumber(9) // INFO level
.severityText("INFO")
.body("Test log message for Kafka queue")
.attributes(attributes)
.traceId("1234567890abcdef1234567890abcdef")
.spanId("1234567890abcdef")
.build();
// Test sending log entry
kafkaCommonDataQueue.sendLogEntry(logEntry);
// Verify that the producer was called
verify(logEntryProducer).send(any());
}
@Test
void testSendLogEntryToStorage() {
// Create a test log entry for storage
LogEntry logEntry = LogEntry.builder()
.timeUnixNano(Instant.now().toEpochMilli() * 1_000_000L)
.severityNumber(17) // ERROR level
.severityText("ERROR")
.body("Error log message for storage via Kafka")
.build();
// Test sending log entry to storage
kafkaCommonDataQueue.sendLogEntryToStorage(logEntry);
// Verify that the producer was called
verify(logEntryProducer).send(any());
}
@Test
void testPollLogEntry() throws InterruptedException {
// Create test log entry
LogEntry expectedLogEntry = LogEntry.builder()
.timeUnixNano(Instant.now().toEpochMilli() * 1_000_000L)
.severityNumber(13) // WARN level
.severityText("WARN")
.body("Test warning log message")
.build();
// Create a ConsumerRecord containing test log entry
ConsumerRecord<Long, LogEntry> record =
new ConsumerRecord<>("logEntryDataTopic", 0, 0L, 1L, expectedLogEntry);
// Create ConsumerRecords containing the log entry record
Map<TopicPartition, List<ConsumerRecord<Long, LogEntry>>> recordsMap =
Collections.singletonMap(
new TopicPartition("logEntryDataTopic", 0),
Collections.singletonList(record));
ConsumerRecords<Long, LogEntry> records = new ConsumerRecords<>(recordsMap);
when(logEntryConsumer.poll(any(Duration.class))).thenReturn(records);
LogEntry result = kafkaCommonDataQueue.pollLogEntry();
assertEquals(expectedLogEntry, result);
verify(logEntryConsumer).commitAsync();
}
@Test
void testPollLogEntryToStorage() throws InterruptedException {
// Create test log entry for storage
LogEntry expectedLogEntry = LogEntry.builder()
.timeUnixNano(Instant.now().toEpochMilli() * 1_000_000L)
.severityNumber(21) // FATAL level
.severityText("FATAL")
.body("Critical error log for storage")
.build();
// Create a ConsumerRecord containing test log entry
ConsumerRecord<Long, LogEntry> record =
new ConsumerRecord<>("logEntryDataToStorageTopic", 0, 0L, 1L, expectedLogEntry);
// Create ConsumerRecords containing the log entry record
Map<TopicPartition, List<ConsumerRecord<Long, LogEntry>>> recordsMap =
Collections.singletonMap(
new TopicPartition("logEntryDataToStorageTopic", 0),
Collections.singletonList(record));
ConsumerRecords<Long, LogEntry> records = new ConsumerRecords<>(recordsMap);
when(logEntryToStorageConsumer.poll(any(Duration.class))).thenReturn(records);
LogEntry result = kafkaCommonDataQueue.pollLogEntryToStorage();
assertEquals(expectedLogEntry, result);
verify(logEntryToStorageConsumer).commitAsync();
}
@Test
void testDestroy() throws Exception {
kafkaCommonDataQueue.destroy();
// Verify that all producers and consumers are closed
verify(metricsDataToAlertConsumer).close();
verify(metricsDataProducer).close();
verify(logEntryProducer).close();
verify(logEntryConsumer).close();
verify(logEntryToStorageConsumer).close();
}
private void setPrivateField(Object object, String fieldName, Object value) throws Exception {
Field field = object.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object, value);
}
}
@@ -0,0 +1,204 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.queue.impl;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.sync.RedisCommands;
import io.lettuce.core.KeyValue;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
import org.apache.hertzbeat.common.config.CommonProperties;
import org.apache.hertzbeat.common.entity.log.LogEntry;
import org.apache.hertzbeat.common.entity.message.CollectRep;
import org.apache.hertzbeat.common.serialize.RedisLogEntryCodec;
import org.apache.hertzbeat.common.serialize.RedisMetricsDataCodec;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.junit.jupiter.MockitoExtension;
/**
* test for {@link RedisCommonDataQueue}
*/
@ExtendWith(MockitoExtension.class)
class RedisCommonDataQueueTest {
@Mock
private StatefulRedisConnection<String, CollectRep.MetricsData> connection;
@Mock
private StatefulRedisConnection<String, LogEntry> logEntryConnection;
@Mock
private RedisCommands<String, CollectRep.MetricsData> syncCommands;
@Mock
private RedisCommands<String, LogEntry> logEntrySyncCommands;
private RedisClient redisClient;
private CommonProperties commonProperties;
private CommonProperties.RedisProperties redisProperties;
private RedisCommonDataQueue redisCommonDataQueue;
@BeforeEach
public void setUp() {
redisClient = mock(RedisClient.class);
commonProperties = mock(CommonProperties.class);
redisProperties = mock(CommonProperties.RedisProperties.class);
CommonProperties.DataQueueProperties dataQueueProperties = mock(CommonProperties.DataQueueProperties.class);
when(commonProperties.getQueue()).thenReturn(dataQueueProperties);
when(dataQueueProperties.getRedis()).thenReturn(redisProperties);
when(redisProperties.getMetricsDataQueueNameToAlerter()).thenReturn("metricsDataQueueToAlerter");
when(redisProperties.getLogEntryQueueName()).thenReturn("logEntryQueue");
when(redisProperties.getLogEntryToStorageQueueName()).thenReturn("logEntryToStorageQueue");
when(redisProperties.getRedisHost()).thenReturn("localhost");
when(redisProperties.getRedisPort()).thenReturn(6379);
when(redisProperties.getWaitTimeout()).thenReturn(1L);
try (MockedStatic<RedisClient> mockedRedisClient = mockStatic(RedisClient.class)) {
mockedRedisClient.when(() -> RedisClient.create(any(RedisURI.class))).thenReturn(redisClient);
when(redisClient.connect(any(RedisMetricsDataCodec.class))).thenReturn(connection);
when(redisClient.connect(any(RedisLogEntryCodec.class))).thenReturn(logEntryConnection);
when(logEntryConnection.sync()).thenReturn(logEntrySyncCommands);
when(connection.sync()).thenReturn(syncCommands);
redisCommonDataQueue = new RedisCommonDataQueue(commonProperties);
}
}
@Test
public void testPollMetricsDataToAlerter() throws Exception {
CollectRep.MetricsData metricsData = CollectRep.MetricsData.newBuilder()
.setMetrics("test metrics")
.build();
String queueName = "metricsDataQueueToAlerter";
when(syncCommands.brpop(1L, queueName)).thenReturn(KeyValue.just(queueName, metricsData));
CollectRep.MetricsData actualMetricsData = redisCommonDataQueue.pollMetricsDataToAlerter();
assertEquals(metricsData, actualMetricsData);
verify(syncCommands).brpop(1L, queueName);
}
@Test
public void testSendMetricsData() throws Exception {
CollectRep.MetricsData metricsData = CollectRep.MetricsData.newBuilder()
.setMetrics("test metrics")
.build();
redisCommonDataQueue.sendMetricsData(metricsData);
verify(syncCommands).lpush("metricsDataQueueToAlerter", metricsData);
}
@Test
public void testSendLogEntry() {
// Create a test log entry with comprehensive data
Map<String, Object> attributes = new HashMap<>();
attributes.put("service.name", "hertzbeat");
attributes.put("service.version", "1.0.0");
LogEntry logEntry = LogEntry.builder()
.timeUnixNano(Instant.now().toEpochMilli() * 1_000_000L)
.severityNumber(9) // INFO level
.severityText("INFO")
.body("Test log message for Redis queue")
.attributes(attributes)
.traceId("1234567890abcdef1234567890abcdef")
.spanId("1234567890abcdef")
.build();
// Test sending log entry
redisCommonDataQueue.sendLogEntry(logEntry);
// Verify that the Redis commands were called
verify(logEntrySyncCommands).lpush("logEntryQueue", logEntry);
}
@Test
public void testSendLogEntryToStorage() {
// Create a test log entry for storage
LogEntry logEntry = LogEntry.builder()
.timeUnixNano(Instant.now().toEpochMilli() * 1_000_000L)
.severityNumber(17) // ERROR level
.severityText("ERROR")
.body("Error log message for storage via Redis")
.build();
// Test sending log entry to storage
redisCommonDataQueue.sendLogEntryToStorage(logEntry);
// Verify that the Redis commands were called
verify(logEntrySyncCommands).lpush("logEntryToStorageQueue", logEntry);
}
@Test
public void testPollLogEntry() throws Exception {
// Create test log entry
LogEntry expectedLogEntry = LogEntry.builder()
.timeUnixNano(Instant.now().toEpochMilli() * 1_000_000L)
.severityNumber(13) // WARN level
.severityText("WARN")
.body("Test warning log message")
.build();
String queueName = "logEntryQueue";
when(logEntrySyncCommands.brpop(1L, queueName)).thenReturn(KeyValue.just(queueName, expectedLogEntry));
LogEntry result = redisCommonDataQueue.pollLogEntry();
assertEquals(expectedLogEntry, result);
verify(logEntrySyncCommands).brpop(1L, "logEntryQueue");
}
@Test
public void testPollLogEntryToStorage() throws Exception {
// Create test log entry for storage
LogEntry expectedLogEntry = LogEntry.builder()
.timeUnixNano(Instant.now().toEpochMilli() * 1_000_000L)
.severityNumber(21) // FATAL level
.severityText("FATAL")
.body("Critical error log for storage")
.build();
String queueName = "logEntryToStorageQueue";
when(logEntrySyncCommands.brpop(1L, queueName)).thenReturn(KeyValue.just(queueName, expectedLogEntry));
LogEntry result = redisCommonDataQueue.pollLogEntryToStorage();
assertEquals(expectedLogEntry, result);
verify(logEntrySyncCommands).brpop(1L, "logEntryToStorageQueue");
}
@Test
public void testDestroy() {
redisCommonDataQueue.destroy();
verify(connection).close();
verify(logEntryConnection).close();
verify(redisClient).shutdown();
}
}
@@ -0,0 +1,108 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.support;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.hertzbeat.common.concurrent.AdmissionMode;
import org.apache.hertzbeat.common.config.VirtualThreadProperties;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
/**
* Test for {@link CommonThreadPool}.
*/
class CommonThreadPoolTest {
private CommonThreadPool commonThreadPool;
@AfterEach
void tearDown() throws Exception {
if (commonThreadPool != null) {
commonThreadPool.destroy();
}
}
@Test
void testExecuteRunsOnVirtualThread() throws Exception {
commonThreadPool = new CommonThreadPool();
CountDownLatch latch = new CountDownLatch(1);
AtomicBoolean virtualThread = new AtomicBoolean(false);
commonThreadPool.execute(() -> {
virtualThread.set(Thread.currentThread().isVirtual());
latch.countDown();
});
assertTrue(latch.await(5, TimeUnit.SECONDS));
assertTrue(virtualThread.get());
}
@Test
void testExecuteLongRunningRunsOnPlatformThread() throws Exception {
commonThreadPool = new CommonThreadPool();
CountDownLatch latch = new CountDownLatch(1);
AtomicBoolean virtualThread = new AtomicBoolean(true);
commonThreadPool.executeLongRunning(() -> {
virtualThread.set(Thread.currentThread().isVirtual());
latch.countDown();
});
assertTrue(latch.await(5, TimeUnit.SECONDS));
assertFalse(virtualThread.get());
}
@Test
void testExecuteRejectsWhenConcurrencyLimitReached() throws Exception {
VirtualThreadProperties properties = new VirtualThreadProperties(
true,
VirtualThreadProperties.PoolProperties.collectorDefaults(),
new VirtualThreadProperties.PoolProperties(AdmissionMode.LIMIT_AND_REJECT, 1),
VirtualThreadProperties.PoolProperties.managerDefaults(),
VirtualThreadProperties.AlerterProperties.defaults(),
VirtualThreadProperties.PoolProperties.warehouseDefaults(),
VirtualThreadProperties.AsyncProperties.defaults());
commonThreadPool = new CommonThreadPool(properties);
CountDownLatch started = new CountDownLatch(1);
CountDownLatch release = new CountDownLatch(1);
commonThreadPool.execute(() -> {
started.countDown();
try {
release.await(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
assertTrue(started.await(5, TimeUnit.SECONDS));
try {
assertThrows(RejectedExecutionException.class, () -> commonThreadPool.execute(() -> {
}));
} finally {
release.countDown();
}
}
}
@@ -0,0 +1,73 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.support;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import java.io.IOException;
import java.util.Locale;
import java.util.ResourceBundle;
import org.junit.jupiter.api.Test;
/**
* Test case for {@link ResourceBundleUtf8Control}
*/
class ResourceBundleUtf8ControlTest {
@Test
void testNewBundleWithPropertiesFormat() throws IllegalAccessException, InstantiationException, IOException {
ResourceBundle.Control control = new ResourceBundleUtf8Control();
ClassLoader loader = getClass().getClassLoader();
String baseName = "msg";
ResourceBundle bundle = control.newBundle(baseName, Locale.ENGLISH, "java.properties", loader, false);
assertNotNull(bundle);
assertEquals("Hello, World!", bundle.getString("hello"));
bundle = control.newBundle(baseName, Locale.ROOT, "java.properties", loader, false);
assertNotNull(bundle);
assertEquals("你好", bundle.getString("hello"));
}
@Test
void testNewBundleWithClassFormat() throws IllegalAccessException, InstantiationException, IOException {
ResourceBundle.Control control = new ResourceBundleUtf8Control();
ClassLoader loader = getClass().getClassLoader();
String baseName = "dummyClassBundle";
ResourceBundle bundle = control.newBundle(baseName, Locale.ENGLISH, "java.class", loader, false);
//because not have an actual class, bundle should be null
assertNull(bundle);
}
@Test
void testReloading() throws IllegalAccessException, InstantiationException, IOException {
ResourceBundle.Control control = new ResourceBundleUtf8Control();
ClassLoader loader = getClass().getClassLoader();
String baseName = "msg";
// Test with reload flag
ResourceBundle bundle = control.newBundle(baseName, Locale.ENGLISH, "java.properties", loader, true);
assertNotNull(bundle);
assertEquals("Hello, World!", bundle.getString("hello"));
}
}
@@ -0,0 +1,108 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.support;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
/**
* Test case for {@link SpringContextHolder}
*/
class SpringContextHolderTest {
private ApplicationContext applicationContext;
private ConfigurableApplicationContext configurableApplicationContext;
private SpringContextHolder springContextHolder;
@BeforeEach
public void setUp() {
applicationContext = mock(ApplicationContext.class);
configurableApplicationContext = mock(ConfigurableApplicationContext.class);
springContextHolder = new SpringContextHolder();
}
@Test
public void testSetApplicationContext() throws BeansException {
springContextHolder.setApplicationContext(configurableApplicationContext);
assertNotNull(SpringContextHolder.getApplicationContext());
}
@Test
public void testGetApplicationContext() {
springContextHolder.setApplicationContext(applicationContext);
assertNotNull(SpringContextHolder.getApplicationContext());
}
@Test
public void testGetBeanByClass() {
Class<String> beanClass = String.class;
String bean = "bean";
when(applicationContext.getBean(beanClass)).thenReturn(bean);
springContextHolder.setApplicationContext(applicationContext);
String retrievedBean = SpringContextHolder.getBean(beanClass);
assertEquals(bean, retrievedBean);
}
@Test
public void testShutdown() {
springContextHolder.setApplicationContext(configurableApplicationContext);
SpringContextHolder.shutdown();
verify(configurableApplicationContext, times(1)).close();
}
@Test
public void testIsActive() {
when(configurableApplicationContext.isActive()).thenReturn(true);
springContextHolder.setApplicationContext(configurableApplicationContext);
assertTrue(SpringContextHolder.isActive());
}
@Test
public void testAssertApplicationContextThrowsException() {
RuntimeException exception = assertThrows(RuntimeException.class, SpringContextHolder::getApplicationContext);
assertEquals(
"applicationContext is null, please inject the springContextHolder",
exception.getMessage()
);
}
}
@@ -0,0 +1,83 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.support.vaild;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import jakarta.validation.ConstraintValidatorContext;
import org.apache.hertzbeat.common.support.valid.EmailParamValidator;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
/**
* Test case for {@link EmailParamValidator}
*/
class EmailParamValidatorTest {
@InjectMocks
private EmailParamValidator emailParamValidator;
@Mock
private ConstraintValidatorContext context;
@Mock
private ConstraintValidatorContext.ConstraintViolationBuilder constraintViolationBuilder;
@BeforeEach
public void setUp() {
MockitoAnnotations.initMocks(this);
when(context.buildConstraintViolationWithTemplate(any())).thenReturn(constraintViolationBuilder);
}
@Test
public void testIsValid() {
boolean result = emailParamValidator.isValid(null, context);
assertFalse(result);
result = emailParamValidator.isValid("123456345@qq.com", context);
assertTrue(result);
result = emailParamValidator.isValid("", context);
assertFalse(result);
result = emailParamValidator.isValid(" ", context);
assertFalse(result);
result = emailParamValidator.isValid("test@example.com", context);
assertTrue(result);
result = emailParamValidator.isValid("test@example", context);
assertFalse(result);
result = emailParamValidator.isValid("test@sub.example.com", context);
assertTrue(result);
String longEmail = "verylongemailaddress@subdomain.domain.example.com";
result = emailParamValidator.isValid(longEmail, context);
assertTrue(result);
}
}
@@ -0,0 +1,83 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.support.vaild;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import jakarta.validation.ConstraintValidatorContext;
import org.apache.hertzbeat.common.support.valid.HostParamValidator;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
/**
* Test case for {@link HostParamValidator}
*/
class HostParamValidatorTest {
@InjectMocks
private HostParamValidator hostParamValidator;
@Mock
private ConstraintValidatorContext context;
@Mock
private ConstraintValidatorContext.ConstraintViolationBuilder constraintViolationBuilder;
@BeforeEach
public void setUp() {
MockitoAnnotations.initMocks(this);
when(context.buildConstraintViolationWithTemplate(any())).thenReturn(constraintViolationBuilder);
}
@Test
public void testIsValid() {
boolean result = hostParamValidator.isValid(null, context);
assertTrue(result);
result = hostParamValidator.isValid("", context);
assertTrue(result);
result = hostParamValidator.isValid(" ", context);
assertTrue(result);
result = hostParamValidator.isValid("192.168.1.1", context);
assertTrue(result);
result = hostParamValidator.isValid("2001:0db8:85a3:0000:0000:8a2e:0370:7334", context);
assertTrue(result);
result = hostParamValidator.isValid("www.example.com", context);
assertTrue(result);
result = hostParamValidator.isValid("http://www.example.com", context);
assertTrue(result);
result = hostParamValidator.isValid("https://www.baidu.com", context);
assertTrue(result);
result = hostParamValidator.isValid("ht!tp://www.example.com", context);
assertFalse(result);
}
}
@@ -0,0 +1,74 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.support.vaild;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import jakarta.validation.ConstraintValidatorContext;
import org.apache.hertzbeat.common.support.valid.PhoneNumParamValidator;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
/**
* Test case for {@link PhoneNumParamValidator}
*/
class PhoneNumParamValidatorTest {
@InjectMocks
private PhoneNumParamValidator phoneNumParamValidator;
@Mock
private ConstraintValidatorContext context;
@Mock
private ConstraintValidatorContext.ConstraintViolationBuilder constraintViolationBuilder;
@BeforeEach
public void setUp() {
MockitoAnnotations.initMocks(this);
when(context.buildConstraintViolationWithTemplate(any())).thenReturn(constraintViolationBuilder);
}
@Test
public void testIsValid() {
boolean result = phoneNumParamValidator.isValid(null, context);
assertFalse(result);
result = phoneNumParamValidator.isValid("", context);
assertFalse(result);
result = phoneNumParamValidator.isValid("123456", context);
assertFalse(result);
result = phoneNumParamValidator.isValid("abc123", context);
assertFalse(result);
result = phoneNumParamValidator.isValid("13900001234", context);
assertTrue(result);
result = phoneNumParamValidator.isValid("1234567890123456789", context);
assertFalse(result);
}
}
@@ -0,0 +1,267 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.support.valid;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.Collections;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertThrows;
/**
* Test for {@link SqlSecurityValidator}
*/
class SqlSecurityValidatorTest {
private SqlSecurityValidator validator;
@BeforeEach
void setUp() {
validator = new SqlSecurityValidator(Arrays.asList("hertzbeat_logs", "app_logs", "access_logs"));
}
@Test
void testValidSelectStatement() {
assertDoesNotThrow(() -> validator.validate("SELECT * FROM hertzbeat_logs"));
assertDoesNotThrow(() -> validator.validate("SELECT id, message FROM hertzbeat_logs WHERE level = 'ERROR'"));
assertDoesNotThrow(() -> validator.validate("SELECT COUNT(*) FROM app_logs"));
assertDoesNotThrow(() -> validator.validate("select * from HERTZBEAT_LOGS")); // case insensitive
}
@Test
void testSelectWithJoin() {
assertDoesNotThrow(() -> validator.validate(
"SELECT a.id, b.message FROM hertzbeat_logs a JOIN app_logs b ON a.id = b.id"));
}
@Test
void testSelectWithSubqueryNotAllowed() {
assertThrows(SqlSecurityException.class,
() -> validator.validate("SELECT * FROM hertzbeat_logs WHERE id IN (SELECT id FROM app_logs)"));
}
@Test
void testSelectWithSubqueryInFromNotAllowed() {
assertThrows(SqlSecurityException.class,
() -> validator.validate("SELECT * FROM (SELECT * FROM hertzbeat_logs) AS subq"));
}
@Test
void testEmptySql() {
assertThrows(SqlSecurityException.class, () -> validator.validate(null));
assertThrows(SqlSecurityException.class, () -> validator.validate(""));
assertThrows(SqlSecurityException.class, () -> validator.validate(" "));
}
@Test
void testInvalidSqlSyntax() {
assertThrows(SqlSecurityException.class,
() -> validator.validate("SELECT * FORM hertzbeat_logs")); // typo: FORM instead of FROM
}
@Test
void testInsertNotAllowed() {
assertThrows(SqlSecurityException.class,
() -> validator.validate("INSERT INTO hertzbeat_logs (message) VALUES ('test')"));
}
@Test
void testUpdateNotAllowed() {
assertThrows(SqlSecurityException.class,
() -> validator.validate("UPDATE hertzbeat_logs SET message = 'test' WHERE id = 1"));
}
@Test
void testDeleteNotAllowed() {
assertThrows(SqlSecurityException.class,
() -> validator.validate("DELETE FROM hertzbeat_logs WHERE id = 1"));
}
@Test
void testDropNotAllowed() {
assertThrows(SqlSecurityException.class,
() -> validator.validate("DROP TABLE hertzbeat_logs"));
}
@Test
void testTruncateNotAllowed() {
assertThrows(SqlSecurityException.class,
() -> validator.validate("TRUNCATE TABLE hertzbeat_logs"));
}
@Test
void testAlterNotAllowed() {
assertThrows(SqlSecurityException.class,
() -> validator.validate("ALTER TABLE hertzbeat_logs ADD COLUMN new_col VARCHAR(100)"));
}
@Test
void testCreateNotAllowed() {
assertThrows(SqlSecurityException.class,
() -> validator.validate("CREATE TABLE new_table (id INT)"));
}
@Test
void testUnauthorizedTable() {
assertThrows(SqlSecurityException.class,
() -> validator.validate("SELECT * FROM users"));
}
@Test
void testUnauthorizedTableInJoin() {
assertThrows(SqlSecurityException.class,
() -> validator.validate("SELECT * FROM hertzbeat_logs JOIN users ON hertzbeat_logs.user_id = users.id"));
}
@Test
void testUnauthorizedTableInSubquery() {
assertThrows(SqlSecurityException.class,
() -> validator.validate("SELECT * FROM hertzbeat_logs WHERE user_id IN (SELECT id FROM users)"));
}
@Test
void testTableWithQuotes() {
assertDoesNotThrow(() -> validator.validate("SELECT * FROM \"hertzbeat_logs\""));
assertDoesNotThrow(() -> validator.validate("SELECT * FROM `hertzbeat_logs`"));
}
@Test
void testEmptyAllowedTables() {
SqlSecurityValidator emptyValidator = new SqlSecurityValidator(Collections.emptyList());
assertThrows(SqlSecurityException.class,
() -> emptyValidator.validate("SELECT * FROM any_table"));
}
@Test
void testNullAllowedTables() {
SqlSecurityValidator nullValidator = new SqlSecurityValidator(null);
assertThrows(SqlSecurityException.class,
() -> nullValidator.validate("SELECT * FROM any_table"));
}
@Test
void testComplexSelectWithAggregation() {
assertDoesNotThrow(() -> validator.validate(
"SELECT level, COUNT(*) as cnt FROM hertzbeat_logs GROUP BY level HAVING COUNT(*) > 10 ORDER BY cnt DESC LIMIT 100"));
}
@Test
void testSelectWithUnionNotAllowed() {
assertThrows(SqlSecurityException.class,
() -> validator.validate("SELECT * FROM hertzbeat_logs UNION SELECT * FROM app_logs"));
}
@Test
void testSelectWithUnionAllNotAllowed() {
assertThrows(SqlSecurityException.class,
() -> validator.validate("SELECT * FROM hertzbeat_logs UNION ALL SELECT * FROM app_logs"));
}
@Test
void testSelectWithIntersectNotAllowed() {
assertThrows(SqlSecurityException.class,
() -> validator.validate("SELECT * FROM hertzbeat_logs INTERSECT SELECT * FROM app_logs"));
}
@Test
void testSelectWithExceptNotAllowed() {
assertThrows(SqlSecurityException.class,
() -> validator.validate("SELECT * FROM hertzbeat_logs EXCEPT SELECT * FROM app_logs"));
}
@Test
void testLateralSubqueryNotAllowed() {
assertThrows(SqlSecurityException.class,
() -> validator.validate("SELECT * FROM hertzbeat_logs, LATERAL (SELECT * FROM app_logs) AS t"));
}
@Test
void testWithClauseNotAllowed() {
assertThrows(SqlSecurityException.class,
() -> validator.validate("WITH cte AS (SELECT * FROM hertzbeat_logs) SELECT * FROM cte"));
}
@Test
void testSqlInjectionAttemptDropTable() {
assertThrows(SqlSecurityException.class,
() -> validator.validate("DROP TABLE users"));
}
@Test
void testSqlInjectionAttemptUnauthorizedTable() {
assertThrows(SqlSecurityException.class,
() -> validator.validate("SELECT * FROM users"));
}
@Test
void testSqlInjectionAttemptDeleteFrom() {
assertThrows(SqlSecurityException.class,
() -> validator.validate("DELETE FROM hertzbeat_logs WHERE 1=1"));
}
@Test
void testBypassInSelectItems() {
assertThrows(SqlSecurityException.class, () -> validator.validate(
"SELECT (SELECT password FROM secret_table) FROM hertzbeat_logs"));
}
@Test
void testBypassInWhereClauseAnd() {
assertThrows(SqlSecurityException.class, () -> validator.validate(
"SELECT * FROM hertzbeat_logs WHERE 1=1 AND id IN (SELECT id FROM secret_table)"));
}
@Test
void testBypassInFunction() {
assertThrows(SqlSecurityException.class, () -> validator.validate(
"SELECT * FROM hertzbeat_logs WHERE id = abs((SELECT count(*) FROM secret_table))"));
}
@Test
void testBypassInCaseWhen() {
assertThrows(SqlSecurityException.class, () -> validator.validate(
"SELECT * FROM hertzbeat_logs WHERE status = (CASE WHEN (SELECT 1 FROM secret_table)=1 THEN 1 ELSE 0 END)"));
}
@Test
void testBypassWithAndExpression() {
assertThrows(SqlSecurityException.class, () -> validator.validate(
"SELECT * FROM hertzbeat_logs WHERE 1=1 AND id = (SELECT id FROM secret_table)"));
}
@Test
void testBypassWithGreaterThan() {
assertThrows(SqlSecurityException.class, () -> validator.validate(
"SELECT * FROM hertzbeat_logs WHERE id > (SELECT count(*) FROM secret_table)"));
}
@Test
void testBypassWithBetween() {
assertThrows(SqlSecurityException.class, () -> validator.validate(
"SELECT * FROM hertzbeat_logs WHERE id BETWEEN 1 AND (SELECT id FROM secret_table)"));
}
@Test
void testBypassWithMathOperations() {
assertThrows(SqlSecurityException.class, () -> validator.validate(
"SELECT * FROM hertzbeat_logs WHERE id = 1 + (SELECT id FROM secret_table)"));
}
}
@@ -0,0 +1,73 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.util;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.apache.hertzbeat.common.constants.ExportFileConstants;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import java.nio.charset.StandardCharsets;
/**
* test case for {@link FileUtil}.
*/
class FileUtilTest {
private static final String EXCEL_TYPE = "application/vnd.ms-excel";
private static final String YAML_TYPE = "application/x-yaml";
private MockMultipartFile jsonFile;
private MockMultipartFile excelFile;
private MockMultipartFile yamlFile;
private MockMultipartFile emptyFile;
@BeforeEach
void setUp() {
jsonFile = new MockMultipartFile("file", "test.json", MediaType.APPLICATION_JSON_VALUE,
"test content".getBytes(StandardCharsets.UTF_8));
excelFile = new MockMultipartFile("file", "test.xlsx", EXCEL_TYPE,
"test content".getBytes(StandardCharsets.UTF_8));
yamlFile = new MockMultipartFile("file", "test.yaml", YAML_TYPE,
"test content".getBytes(StandardCharsets.UTF_8));
emptyFile = new MockMultipartFile("file", "", null, (byte[]) null);
}
@Test
void testGetFileName() {
assertEquals("test.json", FileUtil.getFileName(jsonFile));
assertEquals("test.xlsx", FileUtil.getFileName(excelFile));
assertEquals("test.yaml", FileUtil.getFileName(yamlFile));
assertEquals("", FileUtil.getFileName(emptyFile));
}
@Test
void testGetFileType() {
assertEquals(ExportFileConstants.JsonFile.TYPE, FileUtil.getFileType(jsonFile));
assertEquals(ExportFileConstants.ExcelFile.TYPE, FileUtil.getFileType(excelFile));
assertEquals(ExportFileConstants.YamlFile.TYPE, FileUtil.getFileType(yamlFile));
assertEquals("", FileUtil.getFileType(emptyFile));
}
}
@@ -0,0 +1,90 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.util;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.Mockito.mockStatic;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
/**
* Test case for {@link ResourceBundleUtil}
*/
@ExtendWith(MockitoExtension.class)
class ResourceBundleUtilTest {
private static final String BUNDLE_NAME = "TestBundle";
@BeforeEach
void setUp() {
Locale.setDefault(Locale.US);
}
@Test
void testGetBundleWithValidBundleName() {
try (MockedStatic<ResourceBundle> mockedResourceBundle = mockStatic(ResourceBundle.class)) {
ResourceBundle mockBundle = Mockito.mock(ResourceBundle.class);
mockedResourceBundle.when(
() -> ResourceBundle.getBundle(
Mockito.eq(BUNDLE_NAME),
Mockito.any(ResourceBundle.Control.class)
)
).thenReturn(mockBundle);
ResourceBundle bundle = ResourceBundleUtil.getBundle(BUNDLE_NAME);
assertNotNull(bundle);
assertEquals(mockBundle, bundle);
}
}
@Test
void testGetBundleByInvalidBundleName() {
try (MockedStatic<ResourceBundle> mockedResourceBundle = mockStatic(ResourceBundle.class)) {
mockedResourceBundle.when(
() -> ResourceBundle.getBundle(
Mockito.eq(BUNDLE_NAME),
Mockito.any(ResourceBundle.Control.class)
)
).thenThrow(new MissingResourceException("Missing bundle", "ResourceBundle", BUNDLE_NAME));
ResourceBundle mockDefaultBundle = Mockito.mock(ResourceBundle.class);
mockedResourceBundle.when(() -> ResourceBundle.getBundle(
Mockito.eq(BUNDLE_NAME),
Mockito.eq(Locale.US),
Mockito.any(ResourceBundle.Control.class))
).thenReturn(mockDefaultBundle);
ResourceBundle bundle = ResourceBundleUtil.getBundle(BUNDLE_NAME);
assertNotNull(bundle);
assertEquals(mockDefaultBundle, bundle);
}
}
}
@@ -0,0 +1,86 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.util;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import javax.naming.AuthenticationException;
import org.apache.hertzbeat.common.constants.CommonConstants;
import org.apache.hertzbeat.common.entity.dto.Message;
import org.junit.jupiter.api.Test;
import org.springframework.http.ResponseEntity;
/**
* Test for {@link ResponseUtil}
*/
public class ResponseUtilTest {
@Test
public void testHandle() {
assertDoesNotThrow(() -> {
ResponseUtil.handle(() -> "test");
});
assertDoesNotThrow(() -> {
ResponseEntity<Message<String>> resp = ResponseUtil.handle(() -> {
throw new RuntimeException("test");
});
assertEquals(CommonConstants.FAIL_CODE, resp.getBody().getCode());
});
assertDoesNotThrow(() -> {
ResponseEntity<Message<String>> resp = ResponseUtil.handle(() -> {
throw new AuthenticationException("test");
});
assertEquals(CommonConstants.LOGIN_FAILED_CODE, resp.getBody().getCode());
});
assertDoesNotThrow(() -> {
ResponseUtil.Runnable run = new ResponseUtil.Runnable() {
public void run() {
throw new UnsupportedOperationException("Unimplemented method 'run'");
}
};
ResponseEntity<Message<String>> resp = ResponseUtil.handle(run);
assertEquals(CommonConstants.FAIL_CODE, resp.getBody().getCode());
});
assertDoesNotThrow(() -> {
ResponseUtil.Runnable run = new ResponseUtil.Runnable() {
public void run() throws AuthenticationException {
throw new AuthenticationException("test");
}
};
ResponseEntity<Message<String>> resp = ResponseUtil.handle(run);
assertEquals(CommonConstants.LOGIN_FAILED_CODE, resp.getBody().getCode());
});
assertDoesNotThrow(() -> {
ResponseEntity<Message<String>> resp = ResponseUtil.handle(() -> {});
assertEquals(CommonConstants.SUCCESS_CODE, resp.getBody().getCode());
});
}
/**
* InnerResponseUtilTest
*/
public interface InnerResponseUtilTest {
void run() throws Exception;
}
}
@@ -0,0 +1,16 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
hello=你好
@@ -0,0 +1,16 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
hello=Hello, World!