From a4281fd6d3a72e85c8d227564475fc70126520ec Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Mon, 1 May 2023 19:28:11 +0300 Subject: [PATCH 1/5] Deduplication for new platform version notification --- .../DefaultNotificationRuleProcessor.java | 45 ++++++++++++++++++- .../NewPlatformVersionTriggerProcessor.java | 8 ---- .../src/main/resources/thingsboard.yml | 3 ++ .../AbstractNotificationApiTest.java | 2 +- .../notification/NotificationRuleApiTest.java | 34 ++++++++++++++ .../server/common/data/CacheConstants.java | 1 + .../trigger/NotificationRuleTriggerType.java | 15 +++---- 7 files changed, 90 insertions(+), 18 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessor.java index 57dba39063..8bfdc0e243 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessor.java @@ -15,13 +15,17 @@ */ package org.thingsboard.server.service.notification.rule; +import lombok.Data; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; import org.springframework.context.annotation.Lazy; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Service; import org.thingsboard.rule.engine.api.NotificationCenter; +import org.thingsboard.server.common.data.CacheConstants; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.NotificationRequestId; @@ -35,13 +39,13 @@ import org.thingsboard.server.common.data.notification.rule.NotificationRule; import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; -import org.thingsboard.server.queue.notification.NotificationRuleProcessor; import org.thingsboard.server.common.msg.notification.trigger.NotificationRuleTrigger; import org.thingsboard.server.common.msg.notification.trigger.RuleEngineMsgTrigger; import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.dao.notification.NotificationRequestService; import org.thingsboard.server.queue.discovery.PartitionService; +import org.thingsboard.server.queue.notification.NotificationRuleProcessor; import org.thingsboard.server.service.apiusage.limits.LimitedApi; import org.thingsboard.server.service.apiusage.limits.RateLimitService; import org.thingsboard.server.service.executors.NotificationExecutorService; @@ -49,6 +53,8 @@ import org.thingsboard.server.service.notification.rule.cache.NotificationRulesC import org.thingsboard.server.service.notification.rule.trigger.NotificationRuleTriggerProcessor; import org.thingsboard.server.service.notification.rule.trigger.RuleEngineMsgNotificationRuleTriggerProcessor; +import javax.annotation.PostConstruct; +import java.io.Serializable; import java.util.Collection; import java.util.EnumMap; import java.util.HashMap; @@ -71,9 +77,19 @@ public class DefaultNotificationRuleProcessor implements NotificationRuleProcess @Autowired @Lazy private NotificationCenter notificationCenter; private final NotificationExecutorService notificationExecutor; + private final CacheManager cacheManager; + private Cache sentNotifications; private final Map triggerProcessors = new EnumMap<>(NotificationRuleTriggerType.class); + @PostConstruct + private void init() { + sentNotifications = cacheManager.getCache(CacheConstants.SENT_NOTIFICATIONS_CACHE); + if (sentNotifications == null) { + throw new IllegalStateException("Sent notifications cache is not set up"); + } + } + @Override public void process(NotificationRuleTrigger trigger) { NotificationRuleTriggerType triggerType = trigger.getType(); @@ -126,6 +142,9 @@ public class DefaultNotificationRuleProcessor implements NotificationRuleProcess log.debug("[{}] Rate limit for notification requests per rule was exceeded (rule '{}')", rule.getTenantId(), rule.getName()); return; } + if (trigger.getType().isDeduplicate() && alreadySent(rule.getId(), trigger)) { + return; + } NotificationInfo notificationInfo = constructNotificationInfo(trigger, triggerConfig); rule.getRecipientsConfig().getTargetsTable().forEach((delay, targets) -> { @@ -175,6 +194,23 @@ public class DefaultNotificationRuleProcessor implements NotificationRuleProcess return triggerProcessors.get(triggerConfig.getTriggerType()).constructNotificationInfo(trigger); } + private boolean alreadySent(NotificationRuleId ruleId, NotificationRuleTrigger trigger) { + String key = ruleId + "_" + trigger.getOriginatorEntityId(); + SentNotification sent = sentNotifications.get(key, SentNotification.class); + boolean alreadySent; + if (sent != null && sent.getTrigger().equals(trigger)) { + alreadySent = true; + log.debug("Notification for {} trigger was already sent, ignoring", trigger.getType()); + // updating cache anyway so that the value is not removed by ttl + } else { + alreadySent = false; + sent = new SentNotification(trigger); + } + log.trace("[{}] Putting to sentNotifications cache: {}", ruleId, trigger); + sentNotifications.put(key, sent); + return alreadySent; + } + @EventListener(ComponentLifecycleMsg.class) public void onNotificationRuleDeleted(ComponentLifecycleMsg componentLifecycleMsg) { if (componentLifecycleMsg.getEvent() != ComponentLifecycleEvent.DELETED || @@ -209,4 +245,11 @@ public class DefaultNotificationRuleProcessor implements NotificationRuleProcess RuleEngineMsgTrigger.msgTypeToTriggerType = ruleEngineMsgTypeToTriggerType; } + @Data + private static class SentNotification implements Serializable { + private static final long serialVersionUID = 38973480405095422L; + + private final NotificationRuleTrigger trigger; + } + } diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/NewPlatformVersionTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/NewPlatformVersionTriggerProcessor.java index cb44e7b3ef..639378ad72 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/NewPlatformVersionTriggerProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/NewPlatformVersionTriggerProcessor.java @@ -18,26 +18,18 @@ package org.thingsboard.server.service.notification.rule.trigger; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.UpdateMessage; -import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.info.NewPlatformVersionNotificationInfo; import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo; import org.thingsboard.server.common.data.notification.rule.trigger.NewPlatformVersionNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; import org.thingsboard.server.common.msg.notification.trigger.NewPlatformVersionTrigger; -import org.thingsboard.server.common.msg.queue.ServiceType; -import org.thingsboard.server.queue.discovery.PartitionService; @Service @RequiredArgsConstructor public class NewPlatformVersionTriggerProcessor implements NotificationRuleTriggerProcessor { - private final PartitionService partitionService; - @Override public boolean matchesFilter(NewPlatformVersionTrigger trigger, NewPlatformVersionNotificationRuleTriggerConfig triggerConfig) { - if (!partitionService.isMyPartition(ServiceType.TB_CORE, TenantId.SYS_TENANT_ID, TenantId.SYS_TENANT_ID)) { - return false; - } return trigger.getUpdateInfo().isUpdateAvailable(); } diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 7d027c3836..e907b7b32d 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -445,6 +445,9 @@ cache: notificationSettings: timeToLiveInMinutes: "${CACHE_SPECS_NOTIFICATION_SETTINGS_TTL:10}" maxSize: "${CACHE_SPECS_NOTIFICATION_SETTINGS_MAX_SIZE:1000}" + sentNotifications: + timeToLiveInMinutes: "${CACHE_SPECS_SENT_NOTIFICATIONS_TTL:1440}" + maxSize: "${CACHE_SPECS_SENT_NOTIFICATIONS_MAX_SIZE:10000}" attributes: timeToLiveInMinutes: "${CACHE_SPECS_ATTRIBUTES_TTL:1440}" maxSize: "${CACHE_SPECS_ATTRIBUTES_MAX_SIZE:100000}" diff --git a/application/src/test/java/org/thingsboard/server/service/notification/AbstractNotificationApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/AbstractNotificationApiTest.java index b5238d675b..a22143ab1c 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/AbstractNotificationApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/AbstractNotificationApiTest.java @@ -204,7 +204,7 @@ public abstract class AbstractNotificationApiTest extends AbstractControllerTest NotificationTemplate template = createNotificationTemplate(NotificationType.valueOf(triggerConfig.getTriggerType().toString()), subject, text, NotificationDeliveryMethod.WEB); NotificationRule rule = new NotificationRule(); - rule.setName(triggerConfig.getTriggerType() + " [" + Arrays.toString(targets) + "]"); + rule.setName(triggerConfig.getTriggerType() + " " + Arrays.toString(targets)); rule.setTemplateId(template.getId()); rule.setTriggerType(triggerConfig.getTriggerType()); rule.setTriggerConfig(triggerConfig); diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java index dae2d06075..b03a914d1f 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java @@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.UpdateMessage; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; @@ -55,6 +56,7 @@ import org.thingsboard.server.common.data.notification.rule.trigger.AlarmNotific import org.thingsboard.server.common.data.notification.rule.trigger.AlarmNotificationRuleTriggerConfig.AlarmAction; import org.thingsboard.server.common.data.notification.rule.trigger.EntitiesLimitNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.EntityActionNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.NewPlatformVersionNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; import org.thingsboard.server.common.data.notification.template.NotificationTemplate; @@ -66,9 +68,11 @@ import org.thingsboard.server.common.data.query.FilterPredicateValue; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.common.msg.notification.trigger.NewPlatformVersionTrigger; import org.thingsboard.server.dao.notification.NotificationRequestService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.queue.notification.NotificationRuleProcessor; import org.thingsboard.server.service.apiusage.limits.LimitedApi; import org.thingsboard.server.service.apiusage.limits.RateLimitService; import org.thingsboard.server.service.telemetry.AlarmSubscriptionService; @@ -102,6 +106,8 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { private RateLimitService rateLimitService; @Autowired private RuleChainService ruleChainService; + @Autowired + private NotificationRuleProcessor notificationRuleProcessor; @Before public void beforeEach() throws Exception { @@ -433,6 +439,34 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { assertThat(getWsClient().getLastCountUpdate().getTotalUnreadCount()).isEqualTo(notificationRequestsLimit); } + @Test + public void testNotificationsDeduplication() throws Exception { + loginSysAdmin(); + NewPlatformVersionNotificationRuleTriggerConfig triggerConfig = new NewPlatformVersionNotificationRuleTriggerConfig(); + createNotificationRule(triggerConfig, "Test", "Test", createNotificationTarget(tenantAdminUserId).getId()); + loginTenantAdmin(); + + assertThat(getMyNotifications(false, 100)).size().isZero(); + for (int i = 1; i <= 10; i++) { + notificationRuleProcessor.process(NewPlatformVersionTrigger.builder() + .updateInfo(new UpdateMessage(true, "test", "test", + "test", "test", "test")) + .build()); + TimeUnit.MILLISECONDS.sleep(300); + } + TimeUnit.SECONDS.sleep(5); + assertThat(getMyNotifications(false, 100)).size().isOne(); + + notificationRuleProcessor.process(NewPlatformVersionTrigger.builder() + .updateInfo(new UpdateMessage(true, "CHANGED", "test", + "test", "test", "test")) + .build()); + await().atMost(5, TimeUnit.SECONDS) + .untilAsserted(() -> { + assertThat(getMyNotifications(false, 100)).size().isEqualTo(2); + }); + } + private R checkNotificationAfter(Callable action, BiConsumer check) throws Exception { if (getWsClient().getLastDataUpdate() == null) { getWsClient().subscribeForUnreadNotifications(10).waitForReply(true); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java b/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java index 80d9c4a873..ff0032f435 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java @@ -30,6 +30,7 @@ public class CacheConstants { public static final String TENANTS_EXIST_CACHE = "tenantsExist"; public static final String DEVICE_PROFILE_CACHE = "deviceProfiles"; public static final String NOTIFICATION_SETTINGS_CACHE = "notificationSettings"; + public static final String SENT_NOTIFICATIONS_CACHE = "sentNotifications"; public static final String ASSET_PROFILE_CACHE = "assetProfiles"; public static final String ATTRIBUTES_CACHE = "attributes"; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTriggerType.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTriggerType.java index 34e42e768c..e79e7f1195 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTriggerType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTriggerType.java @@ -16,8 +16,10 @@ package org.thingsboard.server.common.data.notification.rule.trigger; import lombok.Getter; +import lombok.RequiredArgsConstructor; @Getter +@RequiredArgsConstructor public enum NotificationRuleTriggerType { ENTITY_ACTION, @@ -26,18 +28,15 @@ public enum NotificationRuleTriggerType { ALARM_ASSIGNMENT, DEVICE_ACTIVITY, RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT, - NEW_PLATFORM_VERSION(false), - ENTITIES_LIMIT(false), - API_USAGE_LIMIT(false); + NEW_PLATFORM_VERSION(false, true), + ENTITIES_LIMIT(false, false), + API_USAGE_LIMIT(false, false); private final boolean tenantLevel; - - NotificationRuleTriggerType(boolean tenantLevel) { - this.tenantLevel = tenantLevel; - } + private final boolean deduplicate; NotificationRuleTriggerType() { - this(true); + this(true, false); } } From c39e8568aedf7d50bf60787dc796f333cf051faf Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Tue, 2 May 2023 12:46:50 +0300 Subject: [PATCH 2/5] Upgrade improvements for notification system --- .../DefaultSystemDataLoaderService.java | 22 +++++++++++++++---- .../NotificationTargetService.java | 3 ++- .../DefaultNotificationTargetService.java | 5 +++++ .../notification/NotificationTargetDao.java | 3 ++- .../JpaNotificationTargetDao.java | 5 +++++ .../NotificationTargetRepository.java | 2 ++ 6 files changed, 34 insertions(+), 6 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java b/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java index f1183bea8b..4952b486db 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java @@ -21,6 +21,7 @@ import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import lombok.Getter; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -110,6 +111,8 @@ import java.util.List; import java.util.TreeMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; @Service @Profile("install") @@ -682,18 +685,29 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { } @Override + @SneakyThrows public void createDefaultNotificationConfigs() { log.info("Creating default notification configs for system admin"); - if (notificationTargetService.findNotificationTargetsByTenantId(TenantId.SYS_TENANT_ID, new PageLink(1)).getTotalElements() == 0) { + if (notificationTargetService.countNotificationTargetsByTenantId(TenantId.SYS_TENANT_ID) == 0) { notificationSettingsService.createDefaultNotificationConfigs(TenantId.SYS_TENANT_ID); } PageDataIterable tenants = new PageDataIterable<>(tenantService::findTenantsIds, 500); + ExecutorService executor = Executors.newFixedThreadPool(Math.max(Runtime.getRuntime().availableProcessors(), 4)); log.info("Creating default notification configs for all tenants"); + AtomicInteger count = new AtomicInteger(); for (TenantId tenantId : tenants) { - if (notificationTargetService.findNotificationTargetsByTenantId(tenantId, new PageLink(1)).getTotalElements() == 0) { - notificationSettingsService.createDefaultNotificationConfigs(tenantId); - } + executor.submit(() -> { + if (notificationTargetService.countNotificationTargetsByTenantId(tenantId) == 0) { + notificationSettingsService.createDefaultNotificationConfigs(tenantId); + int n = count.incrementAndGet(); + if (n % 500 == 0) { + log.info("{} tenants processed", n); + } + } + }); } + executor.shutdown(); + executor.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS); } } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java index 3c52a53933..05299c1675 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java @@ -22,7 +22,6 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.NotificationType; import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; -import org.thingsboard.server.common.data.notification.targets.NotificationTargetConfig; import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -51,4 +50,6 @@ public interface NotificationTargetService { void deleteNotificationTargetsByTenantId(TenantId tenantId); + long countNotificationTargetsByTenantId(TenantId tenantId); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java index b0a2306408..aa56f657c4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java @@ -192,6 +192,11 @@ public class DefaultNotificationTargetService extends AbstractEntityService impl notificationTargetDao.removeByTenantId(tenantId); } + @Override + public long countNotificationTargetsByTenantId(TenantId tenantId) { + return notificationTargetDao.countByTenantId(tenantId); + } + @Override public Optional> findEntity(TenantId tenantId, EntityId entityId) { return Optional.ofNullable(findNotificationTargetById(tenantId, new NotificationTargetId(entityId.getId()))); diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetDao.java b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetDao.java index 55c4b7f77c..2edbfc580a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetDao.java @@ -22,10 +22,11 @@ import org.thingsboard.server.common.data.notification.targets.NotificationTarge import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; +import org.thingsboard.server.dao.TenantEntityDao; import java.util.List; -public interface NotificationTargetDao extends Dao { +public interface NotificationTargetDao extends Dao, TenantEntityDao { PageData findByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java index 8e4f3af97c..4bc6dada62 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java @@ -71,6 +71,11 @@ public class JpaNotificationTargetDao extends JpaAbstractDao getEntityClass() { return NotificationTargetEntity.class; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTargetRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTargetRepository.java index a958055a33..7ba92db7d9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTargetRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTargetRepository.java @@ -50,4 +50,6 @@ public interface NotificationTargetRepository extends JpaRepository Date: Wed, 3 May 2023 13:35:01 +0300 Subject: [PATCH 3/5] Send notification about new platform version only to sysadmins --- .../dao/notification/DefaultNotificationSettingsService.java | 2 +- .../server/dao/notification/DefaultNotifications.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationSettingsService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationSettingsService.java index 4ca140e274..0e25a0cab3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationSettingsService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationSettingsService.java @@ -98,7 +98,7 @@ public class DefaultNotificationSettingsService implements NotificationSettingsS defaultNotifications.create(tenantId, DefaultNotifications.apiFeatureDisabledForSysadmin, sysAdmins.getId()); defaultNotifications.create(tenantId, DefaultNotifications.apiFeatureDisabledForTenant, affectedTenantAdmins.getId()); - defaultNotifications.create(tenantId, DefaultNotifications.newPlatformVersion, sysAdmins.getId(), tenantAdmins.getId()); + defaultNotifications.create(tenantId, DefaultNotifications.newPlatformVersion, sysAdmins.getId()); return; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java index e5ab043f2d..00d57d1121 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java @@ -143,7 +143,7 @@ public class DefaultNotifications { .rule(DefaultRule.builder() .name("New platform version") .triggerConfig(new NewPlatformVersionNotificationRuleTriggerConfig()) - .description("Send notification to system admins and tenant admins when new platform version is available") + .description("Send notification to system admins when new platform version is available") .build()) .build(); From 36743eb5af288636cfe2aea5be88480cf215c40d Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 3 May 2023 16:31:31 +0300 Subject: [PATCH 4/5] Branch name validation --- .../server/controller/AdminController.java | 2 ++ .../DefaultEntitiesVersionControlService.java | 6 +++- .../server/common/data/sync/vc/VcUtils.java | 35 +++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/VcUtils.java diff --git a/application/src/main/java/org/thingsboard/server/controller/AdminController.java b/application/src/main/java/org/thingsboard/server/controller/AdminController.java index 5f47bcde2c..f2f4deeaa2 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AdminController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AdminController.java @@ -53,6 +53,7 @@ import org.thingsboard.server.common.data.sms.config.TestSmsRequest; import org.thingsboard.server.common.data.sync.vc.AutoCommitSettings; import org.thingsboard.server.common.data.sync.vc.RepositorySettings; import org.thingsboard.server.common.data.sync.vc.RepositorySettingsInfo; +import org.thingsboard.server.common.data.sync.vc.VcUtils; import org.thingsboard.server.dao.settings.AdminSettingsService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.auth.jwt.settings.JwtSettingsService; @@ -307,6 +308,7 @@ public class AdminController extends BaseController { @PreAuthorize("hasAuthority('TENANT_ADMIN')") @PostMapping("/autoCommitSettings") public AutoCommitSettings saveAutoCommitSettings(@RequestBody AutoCommitSettings settings) throws ThingsboardException { + settings.values().forEach(config -> VcUtils.checkBranchName(config.getBranch())); accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.WRITE); return autoCommitSettingsService.save(getTenantId(), settings); } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java index 0e08ee9c48..a63600619a 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java @@ -22,6 +22,7 @@ import com.google.common.util.concurrent.MoreExecutors; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.support.TransactionTemplate; @@ -32,7 +33,6 @@ import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.server.cache.TbTransactionalCache; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; -import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; @@ -101,6 +101,7 @@ import java.util.function.Function; import java.util.stream.Collectors; import static com.google.common.util.concurrent.Futures.transform; +import static org.thingsboard.server.common.data.sync.vc.VcUtils.checkBranchName; @Service @TbCoreComponent @@ -138,6 +139,7 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont @SuppressWarnings("UnstableApiUsage") @Override public ListenableFuture saveEntitiesVersion(User user, VersionCreateRequest request) throws Exception { + checkBranchName(request.getBranch()); var pendingCommit = gitServiceQueue.prepareCommit(user, request); DonAsynchron.withCallback(pendingCommit, commit -> { cachePut(commit.getTxId(), new VersionCreationResult()); @@ -504,6 +506,7 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont @Override public ListenableFuture saveVersionControlSettings(TenantId tenantId, RepositorySettings versionControlSettings) { + checkBranchName(versionControlSettings.getDefaultBranch()); var restoredSettings = this.repositorySettingsService.restore(tenantId, versionControlSettings); try { var future = gitServiceQueue.initRepository(tenantId, restoredSettings); @@ -525,6 +528,7 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont @Override public ListenableFuture checkVersionControlAccess(TenantId tenantId, RepositorySettings settings) throws ThingsboardException { + checkBranchName(settings.getDefaultBranch()); settings = this.repositorySettingsService.restore(tenantId, settings); try { return gitServiceQueue.testRepository(tenantId, settings); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/VcUtils.java b/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/VcUtils.java new file mode 100644 index 0000000000..4a61ae290f --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/VcUtils.java @@ -0,0 +1,35 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed 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.thingsboard.server.common.data.sync.vc; + +import org.apache.commons.lang3.StringUtils; + +public class VcUtils { + + private VcUtils() {} + + public static void checkBranchName(String branch) { + if (StringUtils.isEmpty(branch)) return; + + boolean invalid = StringUtils.containsWhitespace(branch) || + StringUtils.containsAny(branch, "..", "~", "^", ":", "\\") || + StringUtils.endsWithAny(branch, "/", ".lock"); + if (invalid) { + throw new IllegalArgumentException("Branch name is invalid"); + } + } + +} From 6c8f3c19de9b07f68439047556fff89759c99fa7 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 3 May 2023 16:32:33 +0300 Subject: [PATCH 5/5] Active devices count to debug --- .../server/service/state/DefaultDeviceStateService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java index cb08e855c2..2274f0783d 100644 --- a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java @@ -473,7 +473,7 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService 0) { - log.info("[{}] Active devices: {}, inactive devices: {}", tenantId, active, inactive); + log.debug("[{}] Active devices: {}, inactive devices: {}", tenantId, active, inactive); } }); } catch (Throwable t) {