Browse Source

Merge remote-tracking branch 'klimov/feature/notification-system' into feature/notification-system

pull/7911/head
Vladyslav_Prykhodko 4 years ago
parent
commit
ce97166222
  1. 17
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleEngineComponentActor.java
  2. 3
      application/src/main/java/org/thingsboard/server/actors/service/ComponentActor.java
  3. 3
      application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java
  4. 15
      application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java
  5. 25
      application/src/main/java/org/thingsboard/server/service/apiusage/limits/DefaultRateLimitService.java
  6. 36
      application/src/main/java/org/thingsboard/server/service/apiusage/limits/LimitedApi.java
  7. 6
      application/src/main/java/org/thingsboard/server/service/apiusage/limits/RateLimitService.java
  8. 10
      application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java
  9. 2
      application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/RuleEngineComponentLifecycleEventTriggerProcessor.java
  10. 7
      application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java
  11. 2
      common/actor/src/main/java/org/thingsboard/server/actors/DefaultTbActorSystem.java
  12. 3
      common/actor/src/main/java/org/thingsboard/server/actors/TbActor.java
  13. 8
      common/actor/src/main/java/org/thingsboard/server/actors/TbActorMailbox.java
  14. 3
      common/actor/src/test/java/org/thingsboard/server/actors/TestRootActor.java
  15. 3
      common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java
  16. 2
      common/dao-api/src/main/java/org/thingsboard/server/dao/notification/trigger/RuleEngineComponentLifecycleEventTrigger.java
  17. 6
      common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java
  18. 13
      common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationProcessingContext.java
  19. 3
      common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmAssignmentNotificationInfo.java
  20. 3
      common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmCommentNotificationInfo.java
  21. 2
      common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmNotificationInfo.java
  22. 2
      common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EntitiesLimitNotificationInfo.java
  23. 2
      common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EntityActionNotificationInfo.java
  24. 2
      common/data/src/main/java/org/thingsboard/server/common/data/notification/info/RuleEngineComponentLifecycleEventNotificationInfo.java
  25. 2
      common/data/src/main/java/org/thingsboard/server/common/data/notification/info/RuleEngineOriginatedNotificationInfo.java
  26. 4
      common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/AffectedUserFilter.java
  27. 2
      common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UsersFilter.java
  28. 13
      common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UsersFilterType.java
  29. 5
      common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.java
  30. 3
      common/data/src/main/java/org/thingsboard/server/common/data/notification/template/EmailDeliveryMethodNotificationTemplate.java
  31. 23
      common/data/src/main/java/org/thingsboard/server/common/data/notification/template/NotificationTemplateConfig.java
  32. 3
      common/data/src/main/java/org/thingsboard/server/common/data/notification/template/WebDeliveryMethodNotificationTemplate.java
  33. 1
      common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java
  34. 23
      dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRequestInfoEntity.java
  35. 29
      dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationSettingsService.java
  36. 6
      dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java
  37. 3
      dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetDao.java
  38. 4
      dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java
  39. 6
      dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java
  40. 6
      dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java
  41. 18
      dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java
  42. 2
      dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRuleRepository.java
  43. 15
      dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTargetRepository.java
  44. 12
      ui-ngx/src/assets/locale/locale.constant-en_US.json

17
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleEngineComponentActor.java

@ -16,12 +16,14 @@
package org.thingsboard.server.actors.ruleChain; package org.thingsboard.server.actors.ruleChain;
import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.TbRuleNodeUpdateException;
import org.thingsboard.server.actors.service.ComponentActor; import org.thingsboard.server.actors.service.ComponentActor;
import org.thingsboard.server.actors.shared.ComponentMsgProcessor; import org.thingsboard.server.actors.shared.ComponentMsgProcessor;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.msg.TbActorStopReason;
import org.thingsboard.server.dao.notification.trigger.RuleEngineComponentLifecycleEventTrigger; import org.thingsboard.server.dao.notification.trigger.RuleEngineComponentLifecycleEventTrigger;
public abstract class RuleEngineComponentActor<T extends EntityId, P extends ComponentMsgProcessor<T>> extends ComponentActor<T, P> { public abstract class RuleEngineComponentActor<T extends EntityId, P extends ComponentMsgProcessor<T>> extends ComponentActor<T, P> {
@ -33,6 +35,21 @@ public abstract class RuleEngineComponentActor<T extends EntityId, P extends Com
@Override @Override
protected void logLifecycleEvent(ComponentLifecycleEvent event, Exception e) { protected void logLifecycleEvent(ComponentLifecycleEvent event, Exception e) {
super.logLifecycleEvent(event, e); super.logLifecycleEvent(event, e);
if (e instanceof TbRuleNodeUpdateException || (event == ComponentLifecycleEvent.STARTED && e != null)) {
return;
}
processNotificationRule(event, e);
}
@Override
public void destroy(TbActorStopReason stopReason, Throwable cause) {
super.destroy(stopReason, cause);
if (stopReason == TbActorStopReason.INIT_FAILED && cause != null) {
processNotificationRule(ComponentLifecycleEvent.STARTED, cause);
}
}
private void processNotificationRule(ComponentLifecycleEvent event, Throwable e) {
systemContext.getNotificationRuleProcessingService().process(tenantId, RuleEngineComponentLifecycleEventTrigger.builder() systemContext.getNotificationRuleProcessingService().process(tenantId, RuleEngineComponentLifecycleEventTrigger.builder()
.ruleChainId(getRuleChainId()) .ruleChainId(getRuleChainId())
.ruleChainName(getRuleChainName()) .ruleChainName(getRuleChainName())

3
application/src/main/java/org/thingsboard/server/actors/service/ComponentActor.java

@ -25,6 +25,7 @@ import org.thingsboard.server.actors.stats.StatsPersistMsg;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.msg.TbActorStopReason;
import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
@ -82,7 +83,7 @@ public abstract class ComponentActor<T extends EntityId, P extends ComponentMsgP
} }
@Override @Override
public void destroy() { public void destroy(TbActorStopReason stopReason, Throwable cause) {
try { try {
log.debug("[{}][{}][{}] Stopping processor.", tenantId, id, id.getEntityType()); log.debug("[{}][{}][{}] Stopping processor.", tenantId, id, id.getEntityType());
if (processor != null) { if (processor != null) {

3
application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java

@ -44,6 +44,7 @@ import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.common.msg.MsgType; import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.TbActorStopReason;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.aware.DeviceAwareMsg; import org.thingsboard.server.common.msg.aware.DeviceAwareMsg;
import org.thingsboard.server.common.msg.aware.RuleChainAwareMsg; import org.thingsboard.server.common.msg.aware.RuleChainAwareMsg;
@ -105,7 +106,7 @@ public class TenantActor extends RuleChainManagerActor {
} }
@Override @Override
public void destroy() { public void destroy(TbActorStopReason stopReason, Throwable cause) {
log.info("[{}] Stopping tenant actor.", tenantId); log.info("[{}] Stopping tenant actor.", tenantId);
} }

15
application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java

@ -36,6 +36,7 @@ import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.id.NotificationTargetId;
import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.notification.NotificationType;
import org.thingsboard.server.common.data.notification.targets.NotificationTarget; 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.NotificationTargetConfig;
import org.thingsboard.server.common.data.notification.targets.NotificationTargetType; import org.thingsboard.server.common.data.notification.targets.NotificationTargetType;
@ -44,7 +45,6 @@ import org.thingsboard.server.common.data.notification.targets.platform.Platform
import org.thingsboard.server.common.data.notification.targets.platform.TenantAdministratorsFilter; import org.thingsboard.server.common.data.notification.targets.platform.TenantAdministratorsFilter;
import org.thingsboard.server.common.data.notification.targets.platform.UserListFilter; import org.thingsboard.server.common.data.notification.targets.platform.UserListFilter;
import org.thingsboard.server.common.data.notification.targets.platform.UsersFilter; import org.thingsboard.server.common.data.notification.targets.platform.UsersFilter;
import org.thingsboard.server.common.data.notification.targets.platform.UsersFilterType;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.dao.notification.NotificationTargetService;
@ -144,6 +144,19 @@ public class NotificationTargetController extends BaseController {
return notificationTargetService.findNotificationTargetsByTenantId(user.getTenantId(), pageLink); return notificationTargetService.findNotificationTargetsByTenantId(user.getTenantId(), pageLink);
} }
@GetMapping(value = "/targets", params = "notificationType")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
public PageData<NotificationTarget> getNotificationTargetsBySupportedNotificationType(@RequestParam int pageSize,
@RequestParam int page,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder,
@RequestParam(required = false) NotificationType notificationType,
@AuthenticationPrincipal SecurityUser user) throws ThingsboardException {
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
return notificationTargetService.findNotificationTargetsByTenantIdAndSupportedNotificationType(user.getTenantId(), notificationType, pageLink);
}
@ApiOperation(value = "Delete notification target by id (deleteNotificationTargetById)", @ApiOperation(value = "Delete notification target by id (deleteNotificationTargetById)",
notes = "Delete notification target by its id.\n\n" + notes = "Delete notification target by its id.\n\n" +
"This target cannot be referenced by existing scheduled notification requests or any notification rules." + "This target cannot be referenced by existing scheduled notification requests or any notification rules." +

25
application/src/main/java/org/thingsboard/server/service/apiusage/DefaultRateLimitService.java → application/src/main/java/org/thingsboard/server/service/apiusage/limits/DefaultRateLimitService.java

@ -13,19 +13,17 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.apiusage; package org.thingsboard.server.service.apiusage.limits;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
import org.thingsboard.server.common.msg.tools.TbRateLimits; import org.thingsboard.server.common.msg.tools.TbRateLimits;
import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
@ -33,28 +31,19 @@ public class DefaultRateLimitService implements RateLimitService {
private final TbTenantProfileCache tenantProfileCache; private final TbTenantProfileCache tenantProfileCache;
private final Map<String, Map<TenantId, TbRateLimits>> rateLimits = new ConcurrentHashMap<>(); private final Map<LimitedApi, Map<TenantId, TbRateLimits>> rateLimits = new ConcurrentHashMap<>();
@Override @Override
public boolean checkEntityExportLimit(TenantId tenantId) { public boolean checkRateLimit(TenantId tenantId, LimitedApi api) {
return checkLimit(tenantId, "entityExport", DefaultTenantProfileConfiguration::getTenantEntityExportRateLimit);
}
@Override
public boolean checkEntityImportLimit(TenantId tenantId) {
return checkLimit(tenantId, "entityImport", DefaultTenantProfileConfiguration::getTenantEntityImportRateLimit);
}
private boolean checkLimit(TenantId tenantId, String rateLimitsKey, Function<DefaultTenantProfileConfiguration, String> rateLimitConfigExtractor) {
String rateLimitConfig = tenantProfileCache.get(tenantId).getProfileConfiguration() String rateLimitConfig = tenantProfileCache.get(tenantId).getProfileConfiguration()
.map(rateLimitConfigExtractor).orElse(null); .map(api::getLimitConfig).orElse(null);
Map<TenantId, TbRateLimits> rateLimits = this.rateLimits.get(rateLimitsKey); Map<TenantId, TbRateLimits> rateLimits = this.rateLimits.get(api);
if (StringUtils.isEmpty(rateLimitConfig)) { if (StringUtils.isEmpty(rateLimitConfig)) {
if (rateLimits != null) { if (rateLimits != null) {
rateLimits.remove(tenantId); rateLimits.remove(tenantId);
if (rateLimits.isEmpty()) { if (rateLimits.isEmpty()) {
this.rateLimits.remove(rateLimitsKey); this.rateLimits.remove(api);
} }
} }
return true; return true;
@ -62,7 +51,7 @@ public class DefaultRateLimitService implements RateLimitService {
if (rateLimits == null) { if (rateLimits == null) {
rateLimits = new ConcurrentHashMap<>(); rateLimits = new ConcurrentHashMap<>();
this.rateLimits.put(rateLimitsKey, rateLimits); this.rateLimits.put(api, rateLimits);
} }
TbRateLimits rateLimit = rateLimits.get(tenantId); TbRateLimits rateLimit = rateLimits.get(tenantId);
if (rateLimit == null || !rateLimit.getConfiguration().equals(rateLimitConfig)) { if (rateLimit == null || !rateLimit.getConfiguration().equals(rateLimitConfig)) {

36
application/src/main/java/org/thingsboard/server/service/apiusage/limits/LimitedApi.java

@ -0,0 +1,36 @@
/**
* 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.service.apiusage.limits;
import lombok.RequiredArgsConstructor;
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
import java.util.function.Function;
@RequiredArgsConstructor
public enum LimitedApi {
ENTITY_EXPORT(DefaultTenantProfileConfiguration::getTenantEntityExportRateLimit),
ENTITY_IMPORT(DefaultTenantProfileConfiguration::getTenantEntityImportRateLimit),
NOTIFICATION_REQUEST(DefaultTenantProfileConfiguration::getTenantNotificationRequestsRateLimit);
private final Function<DefaultTenantProfileConfiguration, String> configExtractor;
public String getLimitConfig(DefaultTenantProfileConfiguration profileConfiguration) {
return configExtractor.apply(profileConfiguration);
}
}

6
application/src/main/java/org/thingsboard/server/service/apiusage/RateLimitService.java → application/src/main/java/org/thingsboard/server/service/apiusage/limits/RateLimitService.java

@ -13,14 +13,12 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.apiusage; package org.thingsboard.server.service.apiusage.limits;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
public interface RateLimitService { public interface RateLimitService {
boolean checkEntityExportLimit(TenantId tenantId); boolean checkRateLimit(TenantId tenantId, LimitedApi api);
boolean checkEntityImportLimit(TenantId tenantId);
} }

10
application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java

@ -23,6 +23,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.common.util.DonAsynchron; import org.thingsboard.common.util.DonAsynchron;
import org.thingsboard.rule.engine.api.NotificationCenter; import org.thingsboard.rule.engine.api.NotificationCenter;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.NotificationId; import org.thingsboard.server.common.data.id.NotificationId;
import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.NotificationRequestId;
@ -54,6 +55,7 @@ import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.common.msg.tools.TbRateLimitsException;
import org.thingsboard.server.dao.notification.NotificationRequestService; import org.thingsboard.server.dao.notification.NotificationRequestService;
import org.thingsboard.server.dao.notification.NotificationService; import org.thingsboard.server.dao.notification.NotificationService;
import org.thingsboard.server.dao.notification.NotificationSettingsService; import org.thingsboard.server.dao.notification.NotificationSettingsService;
@ -64,6 +66,8 @@ import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.common.TbProtoQueueMsg;
import org.thingsboard.server.queue.discovery.NotificationsTopicService; import org.thingsboard.server.queue.discovery.NotificationsTopicService;
import org.thingsboard.server.queue.provider.TbQueueProducerProvider; import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
import org.thingsboard.server.service.apiusage.limits.LimitedApi;
import org.thingsboard.server.service.apiusage.limits.RateLimitService;
import org.thingsboard.server.service.executors.DbCallbackExecutorService; import org.thingsboard.server.service.executors.DbCallbackExecutorService;
import org.thingsboard.server.service.executors.NotificationExecutorService; import org.thingsboard.server.service.executors.NotificationExecutorService;
import org.thingsboard.server.service.notification.channels.NotificationChannel; import org.thingsboard.server.service.notification.channels.NotificationChannel;
@ -97,12 +101,16 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
private final DbCallbackExecutorService dbCallbackExecutorService; private final DbCallbackExecutorService dbCallbackExecutorService;
private final NotificationsTopicService notificationsTopicService; private final NotificationsTopicService notificationsTopicService;
private final TbQueueProducerProvider producerProvider; private final TbQueueProducerProvider producerProvider;
private final RateLimitService rateLimitService;
private Map<NotificationDeliveryMethod, NotificationChannel> channels; private Map<NotificationDeliveryMethod, NotificationChannel> channels;
@Override @Override
public NotificationRequest processNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest) { public NotificationRequest processNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest) {
if (!rateLimitService.checkRateLimit(tenantId, LimitedApi.NOTIFICATION_REQUEST)) {
throw new TbRateLimitsException(EntityType.TENANT);
}
NotificationSettings settings = notificationSettingsService.findNotificationSettings(tenantId); NotificationSettings settings = notificationSettingsService.findNotificationSettings(tenantId);
NotificationTemplate notificationTemplate; NotificationTemplate notificationTemplate;
if (notificationRequest.getTemplateId() != null) { if (notificationRequest.getTemplateId() != null) {
@ -178,7 +186,7 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
switch (target.getConfiguration().getType()) { switch (target.getConfiguration().getType()) {
case PLATFORM_USERS: { case PLATFORM_USERS: {
PlatformUsersNotificationTargetConfig platformUsersTargetConfig = (PlatformUsersNotificationTargetConfig) target.getConfiguration(); PlatformUsersNotificationTargetConfig platformUsersTargetConfig = (PlatformUsersNotificationTargetConfig) target.getConfiguration();
if (platformUsersTargetConfig.getUsersFilter().getType() == UsersFilterType.ACTION_TARGET_USER) { if (platformUsersTargetConfig.getUsersFilter().getType() == UsersFilterType.AFFECTED_USER) {
if (ctx.getRequest().getInfo() instanceof RuleOriginatedNotificationInfo) { if (ctx.getRequest().getInfo() instanceof RuleOriginatedNotificationInfo) {
UserId targetUserId = ((RuleOriginatedNotificationInfo) ctx.getRequest().getInfo()).getTargetUserId(); UserId targetUserId = ((RuleOriginatedNotificationInfo) ctx.getRequest().getInfo()).getTargetUserId();
if (targetUserId != null) { if (targetUserId != null) {

2
application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/RuleEngineComponentLifecycleEventTriggerProcessor.java

@ -82,7 +82,7 @@ public class RuleEngineComponentLifecycleEventTriggerProcessor implements Notifi
.build(); .build();
} }
private String getErrorMsg(Exception error) { private String getErrorMsg(Throwable error) {
if (error == null) return null; if (error == null) return null;
StringWriter sw = new StringWriter(); StringWriter sw = new StringWriter();

7
application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java

@ -32,7 +32,8 @@ import org.thingsboard.server.common.data.sync.ie.EntityImportResult;
import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.apiusage.RateLimitService; import org.thingsboard.server.service.apiusage.limits.LimitedApi;
import org.thingsboard.server.service.apiusage.limits.RateLimitService;
import org.thingsboard.server.service.entitiy.TbNotificationEntityService; import org.thingsboard.server.service.entitiy.TbNotificationEntityService;
import org.thingsboard.server.service.sync.ie.exporting.EntityExportService; import org.thingsboard.server.service.sync.ie.exporting.EntityExportService;
import org.thingsboard.server.service.sync.ie.exporting.impl.BaseEntityExportService; import org.thingsboard.server.service.sync.ie.exporting.impl.BaseEntityExportService;
@ -72,7 +73,7 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS
@Override @Override
public <E extends ExportableEntity<I>, I extends EntityId> EntityExportData<E> exportEntity(EntitiesExportCtx<?> ctx, I entityId) throws ThingsboardException { public <E extends ExportableEntity<I>, I extends EntityId> EntityExportData<E> exportEntity(EntitiesExportCtx<?> ctx, I entityId) throws ThingsboardException {
if (!rateLimitService.checkEntityExportLimit(ctx.getTenantId())) { if (!rateLimitService.checkRateLimit(ctx.getTenantId(), LimitedApi.ENTITY_EXPORT)) {
throw new ThingsboardException("Rate limit for entities export is exceeded", ThingsboardErrorCode.TOO_MANY_REQUESTS); throw new ThingsboardException("Rate limit for entities export is exceeded", ThingsboardErrorCode.TOO_MANY_REQUESTS);
} }
@ -84,7 +85,7 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS
@Override @Override
public <E extends ExportableEntity<I>, I extends EntityId> EntityImportResult<E> importEntity(EntitiesImportCtx ctx, EntityExportData<E> exportData) throws ThingsboardException { public <E extends ExportableEntity<I>, I extends EntityId> EntityImportResult<E> importEntity(EntitiesImportCtx ctx, EntityExportData<E> exportData) throws ThingsboardException {
if (!rateLimitService.checkEntityImportLimit(ctx.getTenantId())) { if (!rateLimitService.checkRateLimit(ctx.getTenantId(), LimitedApi.ENTITY_IMPORT)) {
throw new ThingsboardException("Rate limit for entities import is exceeded", ThingsboardErrorCode.TOO_MANY_REQUESTS); throw new ThingsboardException("Rate limit for entities import is exceeded", ThingsboardErrorCode.TOO_MANY_REQUESTS);
} }
if (exportData.getEntity() == null || exportData.getEntity().getId() == null) { if (exportData.getEntity() == null || exportData.getEntity().getId() == null) {

2
common/actor/src/main/java/org/thingsboard/server/actors/DefaultTbActorSystem.java

@ -192,7 +192,7 @@ public class DefaultTbActorSystem implements TbActorSystem {
} }
TbActorMailbox mailbox = actors.remove(actorId); TbActorMailbox mailbox = actors.remove(actorId);
if (mailbox != null) { if (mailbox != null) {
mailbox.destroy(); mailbox.destroy(null);
} }
} }

3
common/actor/src/main/java/org/thingsboard/server/actors/TbActor.java

@ -16,6 +16,7 @@
package org.thingsboard.server.actors; package org.thingsboard.server.actors;
import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.TbActorStopReason;
public interface TbActor { public interface TbActor {
@ -26,7 +27,7 @@ public interface TbActor {
default void init(TbActorCtx ctx) throws TbActorException { default void init(TbActorCtx ctx) throws TbActorException {
} }
default void destroy() throws TbActorException { default void destroy(TbActorStopReason stopReason, Throwable cause) throws TbActorException {
} }
default InitFailureStrategy onInitFailure(int attempt, Throwable t) { default InitFailureStrategy onInitFailure(int attempt, Throwable t) {

8
common/actor/src/main/java/org/thingsboard/server/actors/TbActorMailbox.java

@ -75,7 +75,7 @@ public final class TbActorMailbox implements TbActorCtx {
if (strategy.isStop() || (settings.getMaxActorInitAttempts() > 0 && attemptIdx > settings.getMaxActorInitAttempts())) { if (strategy.isStop() || (settings.getMaxActorInitAttempts() > 0 && attemptIdx > settings.getMaxActorInitAttempts())) {
log.info("[{}] Failed to init actor, attempt {}, going to stop attempts.", selfId, attempt, t); log.info("[{}] Failed to init actor, attempt {}, going to stop attempts.", selfId, attempt, t);
stopReason = TbActorStopReason.INIT_FAILED; stopReason = TbActorStopReason.INIT_FAILED;
destroy(); destroy(t.getCause());
} else if (strategy.getRetryDelay() > 0) { } else if (strategy.getRetryDelay() > 0) {
log.info("[{}] Failed to init actor, attempt {}, going to retry in attempts in {}ms", selfId, attempt, strategy.getRetryDelay()); log.info("[{}] Failed to init actor, attempt {}, going to retry in attempts in {}ms", selfId, attempt, strategy.getRetryDelay());
log.debug("[{}] Error", selfId, t); log.debug("[{}] Error", selfId, t);
@ -142,7 +142,7 @@ public final class TbActorMailbox implements TbActorCtx {
actor.process(msg); actor.process(msg);
} catch (TbRuleNodeUpdateException updateException) { } catch (TbRuleNodeUpdateException updateException) {
stopReason = TbActorStopReason.INIT_FAILED; stopReason = TbActorStopReason.INIT_FAILED;
destroy(); destroy(updateException.getCause());
} catch (Throwable t) { } catch (Throwable t) {
log.debug("[{}] Failed to process message: {}", selfId, msg, t); log.debug("[{}] Failed to process message: {}", selfId, msg, t);
ProcessFailureStrategy strategy = actor.onProcessFailure(t); ProcessFailureStrategy strategy = actor.onProcessFailure(t);
@ -208,7 +208,7 @@ public final class TbActorMailbox implements TbActorCtx {
} }
} }
public void destroy() { public void destroy(Throwable cause) {
if (stopReason == null) { if (stopReason == null) {
stopReason = TbActorStopReason.STOPPED; stopReason = TbActorStopReason.STOPPED;
} }
@ -216,7 +216,7 @@ public final class TbActorMailbox implements TbActorCtx {
dispatcher.getExecutor().execute(() -> { dispatcher.getExecutor().execute(() -> {
try { try {
ready.set(NOT_READY); ready.set(NOT_READY);
actor.destroy(); actor.destroy(stopReason, cause);
highPriorityMsgs.forEach(msg -> msg.onTbActorStopped(stopReason)); highPriorityMsgs.forEach(msg -> msg.onTbActorStopped(stopReason));
normalPriorityMsgs.forEach(msg -> msg.onTbActorStopped(stopReason)); normalPriorityMsgs.forEach(msg -> msg.onTbActorStopped(stopReason));
} catch (Throwable t) { } catch (Throwable t) {

3
common/actor/src/test/java/org/thingsboard/server/actors/TestRootActor.java

@ -18,6 +18,7 @@ package org.thingsboard.server.actors;
import lombok.Getter; import lombok.Getter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.TbActorStopReason;
@Slf4j @Slf4j
public class TestRootActor extends AbstractTbActor { public class TestRootActor extends AbstractTbActor {
@ -60,7 +61,7 @@ public class TestRootActor extends AbstractTbActor {
} }
@Override @Override
public void destroy() { public void destroy(TbActorStopReason stopReason, Throwable cause) {
} }

3
common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java

@ -19,6 +19,7 @@ import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.id.NotificationTargetId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.notification.NotificationType;
import org.thingsboard.server.common.data.notification.targets.NotificationTarget; 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.NotificationTargetConfig;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
@ -34,6 +35,8 @@ public interface NotificationTargetService {
PageData<NotificationTarget> findNotificationTargetsByTenantId(TenantId tenantId, PageLink pageLink); PageData<NotificationTarget> findNotificationTargetsByTenantId(TenantId tenantId, PageLink pageLink);
PageData<NotificationTarget> findNotificationTargetsByTenantIdAndSupportedNotificationType(TenantId tenantId, NotificationType notificationType, PageLink pageLink);
List<NotificationTarget> findNotificationTargetsByTenantIdAndIds(TenantId tenantId, List<NotificationTargetId> ids); List<NotificationTarget> findNotificationTargetsByTenantIdAndIds(TenantId tenantId, List<NotificationTargetId> ids);
PageData<User> findRecipientsForNotificationTarget(TenantId tenantId, CustomerId customerId, NotificationTargetId targetId, PageLink pageLink); PageData<User> findRecipientsForNotificationTarget(TenantId tenantId, CustomerId customerId, NotificationTargetId targetId, PageLink pageLink);

2
common/dao-api/src/main/java/org/thingsboard/server/dao/notification/trigger/RuleEngineComponentLifecycleEventTrigger.java

@ -32,7 +32,7 @@ public class RuleEngineComponentLifecycleEventTrigger implements NotificationRul
private final EntityId componentId; private final EntityId componentId;
private final String componentName; private final String componentName;
private final ComponentLifecycleEvent eventType; private final ComponentLifecycleEvent eventType;
private final Exception error; private final Throwable error;
@Override @Override
public NotificationRuleTriggerType getType() { public NotificationRuleTriggerType getType() {

6
common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java

@ -15,6 +15,7 @@
*/ */
package org.thingsboard.server.common.data; package org.thingsboard.server.common.data;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
/** /**
@ -48,8 +49,7 @@ public enum EntityType {
NOTIFICATION, NOTIFICATION,
NOTIFICATION_RULE; NOTIFICATION_RULE;
public String normalName() { @Getter
return StringUtils.capitalize(name().toLowerCase().replaceAll("_", " ")); private final String normalName = StringUtils.capitalize(name().toLowerCase().replaceAll("_", " "));
}
} }

13
common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationProcessingContext.java

@ -73,18 +73,9 @@ public class NotificationProcessingContext {
private void init() { private void init() {
NotificationTemplateConfig templateConfig = notificationTemplate.getConfiguration(); NotificationTemplateConfig templateConfig = notificationTemplate.getConfiguration();
templateConfig.getDeliveryMethodsTemplates().forEach((deliveryMethod, template) -> { templateConfig.getDeliveryMethodsTemplates().forEach((deliveryMethod, template) -> {
if (!template.isEnabled()) return; if (template.isEnabled()) {
templates.put(deliveryMethod, template);
template = template.copy();
if (StringUtils.isEmpty(template.getBody())) {
template.setBody(templateConfig.getDefaultTextTemplate());
}
if (template instanceof HasSubject) {
if (StringUtils.isEmpty(((HasSubject) template).getSubject())) {
((HasSubject) template).setSubject(templateConfig.getNotificationSubject());
}
} }
templates.put(deliveryMethod, template);
}); });
deliveryMethods = templates.keySet(); deliveryMethods = templates.keySet();
} }

3
common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmAssignmentNotificationInfo.java

@ -58,12 +58,13 @@ public class AlarmAssignmentNotificationInfo implements RuleOriginatedNotificati
"assigneeFirstName", assigneeFirstName, "assigneeFirstName", assigneeFirstName,
"assigneeLastName", assigneeLastName, "assigneeLastName", assigneeLastName,
"assigneeEmail", assigneeEmail, "assigneeEmail", assigneeEmail,
"assigneeId", assigneeId != null ? assigneeId.toString() : null,
"userName", userName, "userName", userName,
"alarmType", alarmType, "alarmType", alarmType,
"alarmId", alarmId.toString(), "alarmId", alarmId.toString(),
"alarmSeverity", alarmSeverity.name().toLowerCase(), "alarmSeverity", alarmSeverity.name().toLowerCase(),
"alarmStatus", alarmStatus.toString(), "alarmStatus", alarmStatus.toString(),
"alarmOriginatorEntityType", alarmOriginator.getEntityType().normalName(), "alarmOriginatorEntityType", alarmOriginator.getEntityType().getNormalName(),
"alarmOriginatorId", alarmOriginator.getId().toString(), "alarmOriginatorId", alarmOriginator.getId().toString(),
"alarmOriginatorName", alarmOriginatorName "alarmOriginatorName", alarmOriginatorName
); );

3
common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmCommentNotificationInfo.java

@ -38,6 +38,7 @@ public class AlarmCommentNotificationInfo implements RuleOriginatedNotificationI
private String comment; private String comment;
private String action; private String action;
private String userName; private String userName;
private String alarmType; private String alarmType;
private UUID alarmId; private UUID alarmId;
private EntityId alarmOriginator; private EntityId alarmOriginator;
@ -56,7 +57,7 @@ public class AlarmCommentNotificationInfo implements RuleOriginatedNotificationI
"alarmId", alarmId.toString(), "alarmId", alarmId.toString(),
"alarmSeverity", alarmSeverity.name().toLowerCase(), "alarmSeverity", alarmSeverity.name().toLowerCase(),
"alarmStatus", alarmStatus.toString(), "alarmStatus", alarmStatus.toString(),
"alarmOriginatorEntityType", alarmOriginator.getEntityType().normalName(), "alarmOriginatorEntityType", alarmOriginator.getEntityType().getNormalName(),
"alarmOriginatorId", alarmOriginator.getId().toString(), "alarmOriginatorId", alarmOriginator.getId().toString(),
"alarmOriginatorName", alarmOriginatorName "alarmOriginatorName", alarmOriginatorName
); );

2
common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmNotificationInfo.java

@ -52,7 +52,7 @@ public class AlarmNotificationInfo implements RuleOriginatedNotificationInfo {
"alarmId", alarmId.toString(), "alarmId", alarmId.toString(),
"alarmSeverity", alarmSeverity.name().toLowerCase(), "alarmSeverity", alarmSeverity.name().toLowerCase(),
"alarmStatus", alarmStatus.toString(), "alarmStatus", alarmStatus.toString(),
"alarmOriginatorEntityType", alarmOriginator.getEntityType().normalName(), "alarmOriginatorEntityType", alarmOriginator.getEntityType().getNormalName(),
"alarmOriginatorName", alarmOriginatorName, "alarmOriginatorName", alarmOriginatorName,
"alarmOriginatorId", alarmOriginator.getId().toString() "alarmOriginatorId", alarmOriginator.getId().toString()
); );

2
common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EntitiesLimitNotificationInfo.java

@ -39,7 +39,7 @@ public class EntitiesLimitNotificationInfo implements NotificationInfo {
@Override @Override
public Map<String, String> getTemplateData() { public Map<String, String> getTemplateData() {
return mapOf( return mapOf(
"entityType", entityType.normalName(), "entityType", entityType.getNormalName(),
"currentCount", String.valueOf(currentCount), "currentCount", String.valueOf(currentCount),
"limit", String.valueOf(limit), "limit", String.valueOf(limit),
"percents", String.valueOf(percents) "percents", String.valueOf(percents)

2
common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EntityActionNotificationInfo.java

@ -49,7 +49,7 @@ public class EntityActionNotificationInfo implements RuleOriginatedNotificationI
@Override @Override
public Map<String, String> getTemplateData() { public Map<String, String> getTemplateData() {
return mapOf( return mapOf(
"entityType", entityId.getEntityType().normalName(), "entityType", entityId.getEntityType().getNormalName(),
"entityId", entityId.toString(), "entityId", entityId.toString(),
"entityName", entityName, "entityName", entityName,
"actionType", actionType.name().toLowerCase(), "actionType", actionType.name().toLowerCase(),

2
common/data/src/main/java/org/thingsboard/server/common/data/notification/info/RuleEngineComponentLifecycleEventNotificationInfo.java

@ -47,7 +47,7 @@ public class RuleEngineComponentLifecycleEventNotificationInfo implements Notifi
"ruleChainId", ruleChainId.toString(), "ruleChainId", ruleChainId.toString(),
"ruleChainName", ruleChainName, "ruleChainName", ruleChainName,
"componentId", componentId.toString(), "componentId", componentId.toString(),
"componentType", componentId.getEntityType().normalName(), "componentType", componentId.getEntityType().getNormalName(),
"componentName", componentName, "componentName", componentName,
"action", action, "action", action,
"eventType", eventType.name().toLowerCase(), "eventType", eventType.name().toLowerCase(),

2
common/data/src/main/java/org/thingsboard/server/common/data/notification/info/RuleEngineOriginatedNotificationInfo.java

@ -37,7 +37,7 @@ public class RuleEngineOriginatedNotificationInfo implements NotificationInfo {
@Override @Override
public Map<String, String> getTemplateData() { public Map<String, String> getTemplateData() {
Map<String, String> templateData = new HashMap<>(msgMetadata); Map<String, String> templateData = new HashMap<>(msgMetadata);
templateData.put("originatorType", msgOriginator.getEntityType().toString()); templateData.put("originatorType", msgOriginator.getEntityType().getNormalName());
templateData.put("originatorId", msgOriginator.getId().toString()); templateData.put("originatorId", msgOriginator.getId().toString());
templateData.put("msgType", msgType); templateData.put("msgType", msgType);
return templateData; return templateData;

4
common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/ActionTargetUserFilter.java → common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/AffectedUserFilter.java

@ -18,11 +18,11 @@ package org.thingsboard.server.common.data.notification.targets.platform;
import lombok.Data; import lombok.Data;
@Data @Data
public class ActionTargetUserFilter implements UsersFilter { public class AffectedUserFilter implements UsersFilter {
@Override @Override
public UsersFilterType getType() { public UsersFilterType getType() {
return UsersFilterType.ACTION_TARGET_USER; return UsersFilterType.AFFECTED_USER;
} }
} }

2
common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UsersFilter.java

@ -29,7 +29,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo;
@Type(value = TenantAdministratorsFilter.class, name = "TENANT_ADMINISTRATORS"), @Type(value = TenantAdministratorsFilter.class, name = "TENANT_ADMINISTRATORS"),
@Type(value = AllUsersFilter.class, name = "ALL_USERS"), @Type(value = AllUsersFilter.class, name = "ALL_USERS"),
@Type(value = OriginatorEntityOwnerUsersFilter.class, name = "ORIGINATOR_ENTITY_OWNER_USERS"), @Type(value = OriginatorEntityOwnerUsersFilter.class, name = "ORIGINATOR_ENTITY_OWNER_USERS"),
@Type(value = ActionTargetUserFilter.class, name = "ACTION_TARGET_USER") @Type(value = AffectedUserFilter.class, name = "AFFECTED_USER")
}) })
public interface UsersFilter { public interface UsersFilter {

13
common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UsersFilterType.java

@ -15,13 +15,22 @@
*/ */
package org.thingsboard.server.common.data.notification.targets.platform; package org.thingsboard.server.common.data.notification.targets.platform;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
@Getter
public enum UsersFilterType { public enum UsersFilterType {
USER_LIST, USER_LIST,
CUSTOMER_USERS, CUSTOMER_USERS,
TENANT_ADMINISTRATORS, TENANT_ADMINISTRATORS,
ALL_USERS, ALL_USERS,
ORIGINATOR_ENTITY_OWNER_USERS, ORIGINATOR_ENTITY_OWNER_USERS(true),
ACTION_TARGET_USER AFFECTED_USER(true);
private boolean forRules;
} }

5
common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.java

@ -22,8 +22,12 @@ import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.NotEmpty;
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "method") @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "method")
@JsonSubTypes({ @JsonSubTypes({
@ -37,6 +41,7 @@ import org.thingsboard.server.common.data.notification.NotificationDeliveryMetho
public abstract class DeliveryMethodNotificationTemplate { public abstract class DeliveryMethodNotificationTemplate {
private boolean enabled; private boolean enabled;
@NotEmpty
private String body; private String body;
public DeliveryMethodNotificationTemplate(DeliveryMethodNotificationTemplate other) { public DeliveryMethodNotificationTemplate(DeliveryMethodNotificationTemplate other) {

3
common/data/src/main/java/org/thingsboard/server/common/data/notification/template/EmailDeliveryMethodNotificationTemplate.java

@ -21,12 +21,15 @@ import lombok.NoArgsConstructor;
import lombok.ToString; import lombok.ToString;
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
import javax.validation.constraints.NotEmpty;
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true) @ToString(callSuper = true)
public class EmailDeliveryMethodNotificationTemplate extends DeliveryMethodNotificationTemplate implements HasSubject { public class EmailDeliveryMethodNotificationTemplate extends DeliveryMethodNotificationTemplate implements HasSubject {
@NotEmpty
private String subject; private String subject;
public EmailDeliveryMethodNotificationTemplate(EmailDeliveryMethodNotificationTemplate other) { public EmailDeliveryMethodNotificationTemplate(EmailDeliveryMethodNotificationTemplate other) {

23
common/data/src/main/java/org/thingsboard/server/common/data/notification/template/NotificationTemplateConfig.java

@ -15,41 +15,18 @@
*/ */
package org.thingsboard.server.common.data.notification.template; package org.thingsboard.server.common.data.notification.template;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data; import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
import javax.validation.Valid; import javax.validation.Valid;
import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotEmpty;
import java.util.Map; import java.util.Map;
@Data @Data
public class NotificationTemplateConfig { public class NotificationTemplateConfig {
private String notificationSubject;
private String defaultTextTemplate;
@Valid @Valid
@NotEmpty @NotEmpty
private Map<NotificationDeliveryMethod, DeliveryMethodNotificationTemplate> deliveryMethodsTemplates; private Map<NotificationDeliveryMethod, DeliveryMethodNotificationTemplate> deliveryMethodsTemplates;
@JsonIgnore
@AssertTrue(message = "defaultTextTemplate and notificationSubject must be specified if one absent for delivery method")
public boolean isValid() {
if (deliveryMethodsTemplates == null) return true;
for (DeliveryMethodNotificationTemplate template : deliveryMethodsTemplates.values()) {
if (StringUtils.isEmpty(template.getBody()) && StringUtils.isEmpty(defaultTextTemplate)) {
return false;
}
if (template instanceof HasSubject) {
String subject = ((HasSubject) template).getSubject();
if (StringUtils.isEmpty(subject) && StringUtils.isEmpty(notificationSubject)) {
return false;
}
}
}
return true;
}
} }

3
common/data/src/main/java/org/thingsboard/server/common/data/notification/template/WebDeliveryMethodNotificationTemplate.java

@ -22,12 +22,15 @@ import lombok.NoArgsConstructor;
import lombok.ToString; import lombok.ToString;
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
import javax.validation.constraints.NotEmpty;
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true) @ToString(callSuper = true)
public class WebDeliveryMethodNotificationTemplate extends DeliveryMethodNotificationTemplate implements HasSubject { public class WebDeliveryMethodNotificationTemplate extends DeliveryMethodNotificationTemplate implements HasSubject {
@NotEmpty
private String subject; private String subject;
private JsonNode additionalConfig; private JsonNode additionalConfig;

1
common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java

@ -47,6 +47,7 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura
private String tenantEntityExportRateLimit; private String tenantEntityExportRateLimit;
private String tenantEntityImportRateLimit; private String tenantEntityImportRateLimit;
private String tenantNotificationRequestsRateLimit;
private long maxTransportMessages; private long maxTransportMessages;
private long maxTransportDataPoints; private long maxTransportDataPoints;

23
dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRequestInfoEntity.java

@ -49,21 +49,22 @@ public class NotificationRequestInfoEntity extends NotificationRequestEntity {
NotificationRequest request = super.toData(); NotificationRequest request = super.toData();
List<NotificationDeliveryMethod> deliveryMethods; List<NotificationDeliveryMethod> deliveryMethods;
NotificationTemplateConfig templateConfig = fromJson(this.templateConfig, NotificationTemplateConfig.class); if (request.getStats() != null) {
String templateName = this.templateName;
if (templateConfig == null && request.getTemplate() != null) {
templateConfig = request.getTemplate().getConfiguration();
}
if (templateConfig != null) {
deliveryMethods = templateConfig.getDeliveryMethodsTemplates().entrySet().stream()
.filter(entry -> entry.getValue().isEnabled())
.map(Map.Entry::getKey).collect(Collectors.toList());
} else if (request.getStats() != null) {
Set<NotificationDeliveryMethod> methods = new HashSet<>(request.getStats().getSent().keySet()); Set<NotificationDeliveryMethod> methods = new HashSet<>(request.getStats().getSent().keySet());
methods.addAll(request.getStats().getErrors().keySet()); methods.addAll(request.getStats().getErrors().keySet());
deliveryMethods = new ArrayList<>(methods); deliveryMethods = new ArrayList<>(methods);
} else { } else {
deliveryMethods = Collections.emptyList(); NotificationTemplateConfig templateConfig = fromJson(this.templateConfig, NotificationTemplateConfig.class);
if (templateConfig == null && request.getTemplate() != null) {
templateConfig = request.getTemplate().getConfiguration();
}
if (templateConfig != null) {
deliveryMethods = templateConfig.getDeliveryMethodsTemplates().entrySet().stream()
.filter(entry -> entry.getValue().isEnabled())
.map(Map.Entry::getKey).collect(Collectors.toList());
} else {
deliveryMethods = Collections.emptyList();
}
} }
return new NotificationRequestInfo(request, templateName, deliveryMethods); return new NotificationRequestInfo(request, templateName, deliveryMethods);
} }

29
dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationSettingsService.java

@ -39,13 +39,14 @@ import org.thingsboard.server.common.data.notification.rule.trigger.AlarmComment
import org.thingsboard.server.common.data.notification.rule.trigger.AlarmNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.AlarmNotificationRuleTriggerConfig;
import org.thingsboard.server.common.data.notification.rule.trigger.AlarmNotificationRuleTriggerConfig.AlarmAction; import org.thingsboard.server.common.data.notification.rule.trigger.AlarmNotificationRuleTriggerConfig.AlarmAction;
import org.thingsboard.server.common.data.notification.rule.trigger.DeviceInactivityNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.DeviceInactivityNotificationRuleTriggerConfig;
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.EntityActionNotificationRuleTriggerConfig;
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerConfig; 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.notification.rule.trigger.NotificationRuleTriggerType;
import org.thingsboard.server.common.data.notification.rule.trigger.RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig;
import org.thingsboard.server.common.data.notification.settings.NotificationSettings; import org.thingsboard.server.common.data.notification.settings.NotificationSettings;
import org.thingsboard.server.common.data.notification.targets.NotificationTarget; import org.thingsboard.server.common.data.notification.targets.NotificationTarget;
import org.thingsboard.server.common.data.notification.targets.platform.ActionTargetUserFilter; import org.thingsboard.server.common.data.notification.targets.platform.AffectedUserFilter;
import org.thingsboard.server.common.data.notification.targets.platform.AllUsersFilter; import org.thingsboard.server.common.data.notification.targets.platform.AllUsersFilter;
import org.thingsboard.server.common.data.notification.targets.platform.OriginatorEntityOwnerUsersFilter; import org.thingsboard.server.common.data.notification.targets.platform.OriginatorEntityOwnerUsersFilter;
import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig; import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig;
@ -109,17 +110,26 @@ public class DefaultNotificationSettingsService implements NotificationSettingsS
NotificationTarget tenantAdmins = createTarget(tenantId, "Tenant administrators", new TenantAdministratorsFilter(), NotificationTarget tenantAdmins = createTarget(tenantId, "Tenant administrators", new TenantAdministratorsFilter(),
tenantId.isSysTenantId() ? "All tenant administrators" : "Tenant administrators"); tenantId.isSysTenantId() ? "All tenant administrators" : "Tenant administrators");
createTemplate(tenantId, "Maintenance work notification", NotificationType.GENERAL,
"Infrastructure maintenance",
"Maintenance work is scheduled for tomorrow (7:00 a.m. - 9:00 a.m. UTC)");
if (tenantId.isSysTenantId()) { if (tenantId.isSysTenantId()) {
createTemplate(tenantId, "Maintenance work notification", NotificationType.GENERAL, NotificationTemplate entitiesLimitNotificationTemplate = createTemplate(tenantId, "Entities limit notification", NotificationType.ENTITIES_LIMIT,
"Infrastructure maintenance", "${entityType}s limit will be reached soon",
"Maintenance work is scheduled for tomorrow (7:00 a.m. - 9:00 a.m. UTC). You may face major service interruptions, sorry for the inconvenience"); "${entityType}s usage: ${currentCount}/${limit} (${percents}%)");
EntitiesLimitNotificationRuleTriggerConfig entitiesLimitRuleTriggerConfig = new EntitiesLimitNotificationRuleTriggerConfig();
entitiesLimitRuleTriggerConfig.setEntityTypes(null);
entitiesLimitRuleTriggerConfig.setThreshold(0.8f);
createRule(tenantId, "Entities limit", entitiesLimitNotificationTemplate.getId(), entitiesLimitRuleTriggerConfig,
List.of(tenantAdmins.getId()), "Send notification to tenant admins when count of entities of some type reached 80% threshold of the limit");
return; return;
} }
NotificationTarget originatorEntityOwnerUsers = createTarget(tenantId, "Users of rule trigger entity's owner", new OriginatorEntityOwnerUsersFilter(), NotificationTarget originatorEntityOwnerUsers = createTarget(tenantId, "Users of rule trigger entity's owner", new OriginatorEntityOwnerUsersFilter(),
"Customer users in case trigger entity (e.g. alarm) has customer, tenant admins otherwise"); "Customer users in case trigger entity (e.g. alarm) has customer, tenant admins otherwise");
NotificationTarget actionTargetUser = createTarget(tenantId, "Action target", new ActionTargetUserFilter(), NotificationTarget affectedUser = createTarget(tenantId, "Affected user", new AffectedUserFilter(),
"If rule trigger is an action that targets some user (e.g. alarm assigned to user) - this user"); "If rule trigger is an action that affects some user (e.g. alarm assigned to user) - this user");
NotificationTemplate alarmNotificationTemplate = createTemplate(tenantId, "Alarm notification", NotificationType.ALARM, NotificationTemplate alarmNotificationTemplate = createTemplate(tenantId, "Alarm notification", NotificationType.ALARM,
"Alarm '${alarmType}' - ${action}", "Alarm '${alarmType}' - ${action}",
@ -181,7 +191,7 @@ public class DefaultNotificationSettingsService implements NotificationSettingsS
alarmAssignmentRuleTriggerConfig.setAlarmStatuses(null); alarmAssignmentRuleTriggerConfig.setAlarmStatuses(null);
alarmAssignmentRuleTriggerConfig.setNotifyOn(Set.of(AlarmAssignmentNotificationRuleTriggerConfig.Action.ASSIGNED)); alarmAssignmentRuleTriggerConfig.setNotifyOn(Set.of(AlarmAssignmentNotificationRuleTriggerConfig.Action.ASSIGNED));
createRule(tenantId, "Alarm assigned", alarmAssignedNotificationTemplate.getId(), alarmAssignmentRuleTriggerConfig, createRule(tenantId, "Alarm assigned", alarmAssignedNotificationTemplate.getId(), alarmAssignmentRuleTriggerConfig,
List.of(actionTargetUser.getId()), "Send notification to user when any alarm was assigned to him"); List.of(affectedUser.getId()), "Send notification to user when any alarm was assigned to him");
NotificationTemplate ruleEngineComponentLifecycleFailureNotificationTemplate = createTemplate(tenantId, "Rule chain/node lifecycle failure notification", NotificationType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT, NotificationTemplate ruleEngineComponentLifecycleFailureNotificationTemplate = createTemplate(tenantId, "Rule chain/node lifecycle failure notification", NotificationType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT,
"${componentType} '${componentName}' failed to ${action}", "${componentType} '${componentName}' failed to ${action}",
@ -225,10 +235,9 @@ public class DefaultNotificationSettingsService implements NotificationSettingsS
template.setNotificationType(notificationType); template.setNotificationType(notificationType);
NotificationTemplateConfig templateConfig = new NotificationTemplateConfig(); NotificationTemplateConfig templateConfig = new NotificationTemplateConfig();
templateConfig.setNotificationSubject(subjectTemplate);
templateConfig.setDefaultTextTemplate(textTemplate);
WebDeliveryMethodNotificationTemplate webTemplate = new WebDeliveryMethodNotificationTemplate(); WebDeliveryMethodNotificationTemplate webTemplate = new WebDeliveryMethodNotificationTemplate();
webTemplate.setSubject(subjectTemplate);
webTemplate.setBody(textTemplate);
ObjectNode additionalConfig = newObjectNode(); ObjectNode additionalConfig = newObjectNode();
ObjectNode iconConfig = newObjectNode(); ObjectNode iconConfig = newObjectNode();
additionalConfig.set("icon", iconConfig); additionalConfig.set("icon", iconConfig);

6
dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java

@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.common.data.id.TenantProfileId;
import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.notification.NotificationRequestStatus; import org.thingsboard.server.common.data.notification.NotificationRequestStatus;
import org.thingsboard.server.common.data.notification.NotificationType;
import org.thingsboard.server.common.data.notification.targets.NotificationTarget; 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.NotificationTargetConfig;
import org.thingsboard.server.common.data.notification.targets.platform.CustomerUsersFilter; import org.thingsboard.server.common.data.notification.targets.platform.CustomerUsersFilter;
@ -81,6 +82,11 @@ public class DefaultNotificationTargetService extends AbstractEntityService impl
return notificationTargetDao.findByTenantIdAndPageLink(tenantId, pageLink); return notificationTargetDao.findByTenantIdAndPageLink(tenantId, pageLink);
} }
@Override
public PageData<NotificationTarget> findNotificationTargetsByTenantIdAndSupportedNotificationType(TenantId tenantId, NotificationType notificationType, PageLink pageLink) {
return notificationTargetDao.findByTenantIdAndSupportedNotificationTypeAndPageLink(tenantId, notificationType, pageLink);
}
@Override @Override
public List<NotificationTarget> findNotificationTargetsByTenantIdAndIds(TenantId tenantId, List<NotificationTargetId> ids) { public List<NotificationTarget> findNotificationTargetsByTenantIdAndIds(TenantId tenantId, List<NotificationTargetId> ids) {
return notificationTargetDao.findByTenantIdAndIds(tenantId, ids); return notificationTargetDao.findByTenantIdAndIds(tenantId, ids);

3
dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetDao.java

@ -17,6 +17,7 @@ package org.thingsboard.server.dao.notification;
import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.id.NotificationTargetId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.notification.NotificationType;
import org.thingsboard.server.common.data.notification.targets.NotificationTarget; import org.thingsboard.server.common.data.notification.targets.NotificationTarget;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
@ -28,6 +29,8 @@ public interface NotificationTargetDao extends Dao<NotificationTarget> {
PageData<NotificationTarget> findByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink); PageData<NotificationTarget> findByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink);
PageData<NotificationTarget> findByTenantIdAndSupportedNotificationTypeAndPageLink(TenantId tenantId, NotificationType notificationType, PageLink pageLink);
List<NotificationTarget> findByTenantIdAndIds(TenantId tenantId, List<NotificationTargetId> ids); List<NotificationTarget> findByTenantIdAndIds(TenantId tenantId, List<NotificationTargetId> ids);
void removeByTenantId(TenantId tenantId); void removeByTenantId(TenantId tenantId);

4
dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java

@ -34,8 +34,6 @@ import java.util.function.Function;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import static org.apache.commons.lang3.StringUtils.capitalize;
@Slf4j @Slf4j
public abstract class DataValidator<D extends BaseData<?>> { public abstract class DataValidator<D extends BaseData<?>> {
private static final Pattern EMAIL_PATTERN = private static final Pattern EMAIL_PATTERN =
@ -106,7 +104,7 @@ public abstract class DataValidator<D extends BaseData<?>> {
protected void validateNumberOfEntitiesPerTenant(TenantId tenantId, protected void validateNumberOfEntitiesPerTenant(TenantId tenantId,
EntityType entityType) { EntityType entityType) {
if (!apiLimitService.checkEntitiesLimit(tenantId, entityType)) { if (!apiLimitService.checkEntitiesLimit(tenantId, entityType)) {
throw new DataValidationException(entityType.normalName() + "s limit reached"); throw new DataValidationException(entityType.getNormalName() + "s limit reached");
} }
} }

6
dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java

@ -41,6 +41,7 @@ import org.thingsboard.server.dao.sql.JpaAbstractDao;
import org.thingsboard.server.dao.util.SqlDao; import org.thingsboard.server.dao.util.SqlDao;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -60,7 +61,10 @@ public class JpaNotificationRequestDao extends JpaAbstractDao<NotificationReques
@Override @Override
public PageData<NotificationRequestInfo> findInfosByTenantIdAndOriginatorTypeAndPageLink(TenantId tenantId, EntityType originatorType, PageLink pageLink) { public PageData<NotificationRequestInfo> findInfosByTenantIdAndOriginatorTypeAndPageLink(TenantId tenantId, EntityType originatorType, PageLink pageLink) {
return DaoUtil.pageToPageData(notificationRequestRepository.findInfosByTenantIdAndOriginatorEntityTypeAndSearchText(tenantId.getId(), return DaoUtil.pageToPageData(notificationRequestRepository.findInfosByTenantIdAndOriginatorEntityTypeAndSearchText(tenantId.getId(),
originatorType, Strings.nullToEmpty(pageLink.getTextSearch()), DaoUtil.toPageable(pageLink))).mapData(NotificationRequestInfoEntity::toData); originatorType, Strings.nullToEmpty(pageLink.getTextSearch()), DaoUtil.toPageable(pageLink, Map.of(
"templateName", "t.name"
))))
.mapData(NotificationRequestInfoEntity::toData);
} }
@Override @Override

6
dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java

@ -36,6 +36,7 @@ import org.thingsboard.server.dao.sql.JpaAbstractDao;
import org.thingsboard.server.dao.util.SqlDao; import org.thingsboard.server.dao.util.SqlDao;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.UUID; import java.util.UUID;
@Component @Component
@ -54,7 +55,10 @@ public class JpaNotificationRuleDao extends JpaAbstractDao<NotificationRuleEntit
@Override @Override
public PageData<NotificationRuleInfo> findInfosByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink) { public PageData<NotificationRuleInfo> findInfosByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink) {
return DaoUtil.pageToPageData(notificationRuleRepository.findInfosByTenantIdAndSearchText(tenantId.getId(), return DaoUtil.pageToPageData(notificationRuleRepository.findInfosByTenantIdAndSearchText(tenantId.getId(),
Strings.nullToEmpty(pageLink.getTextSearch()), DaoUtil.toPageable(pageLink))).mapData(NotificationRuleInfoEntity::toData); Strings.nullToEmpty(pageLink.getTextSearch()), DaoUtil.toPageable(pageLink, Map.of(
"templateName", "t.name"
))))
.mapData(NotificationRuleInfoEntity::toData);
} }
@Override @Override

18
dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java

@ -22,8 +22,9 @@ import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.id.NotificationTargetId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.common.data.notification.NotificationType;
import org.thingsboard.server.common.data.notification.targets.NotificationTarget; import org.thingsboard.server.common.data.notification.targets.NotificationTarget;
import org.thingsboard.server.common.data.notification.targets.platform.UsersFilterType;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.DaoUtil;
@ -32,12 +33,11 @@ import org.thingsboard.server.dao.notification.NotificationTargetDao;
import org.thingsboard.server.dao.sql.JpaAbstractDao; import org.thingsboard.server.dao.sql.JpaAbstractDao;
import org.thingsboard.server.dao.util.SqlDao; import org.thingsboard.server.dao.util.SqlDao;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.thingsboard.server.dao.DaoUtil.getId;
@Component @Component
@SqlDao @SqlDao
@RequiredArgsConstructor @RequiredArgsConstructor
@ -47,10 +47,20 @@ public class JpaNotificationTargetDao extends JpaAbstractDao<NotificationTargetE
@Override @Override
public PageData<NotificationTarget> findByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink) { public PageData<NotificationTarget> findByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink) {
return DaoUtil.toPageData(notificationTargetRepository.findByTenantIdAndNameContainingIgnoreCase(tenantId.getId(), return DaoUtil.toPageData(notificationTargetRepository.findByTenantIdAndSearchText(tenantId.getId(),
Strings.nullToEmpty(pageLink.getTextSearch()), DaoUtil.toPageable(pageLink))); Strings.nullToEmpty(pageLink.getTextSearch()), DaoUtil.toPageable(pageLink)));
} }
@Override
public PageData<NotificationTarget> findByTenantIdAndSupportedNotificationTypeAndPageLink(TenantId tenantId, NotificationType notificationType, PageLink pageLink) {
return DaoUtil.toPageData(notificationTargetRepository.findByTenantIdAndSearchTextAndUsersFilterTypeIfPresent(tenantId.getId(),
Strings.nullToEmpty(pageLink.getTextSearch()),
Arrays.stream(UsersFilterType.values())
.filter(type -> notificationType != NotificationType.GENERAL || !type.isForRules())
.map(Enum::name).collect(Collectors.toList()),
DaoUtil.toPageable(pageLink)));
}
@Override @Override
public List<NotificationTarget> findByTenantIdAndIds(TenantId tenantId, List<NotificationTargetId> ids) { public List<NotificationTarget> findByTenantIdAndIds(TenantId tenantId, List<NotificationTargetId> ids) {
return DaoUtil.convertDataList(notificationTargetRepository.findByTenantIdAndIdIn(tenantId.getId(), DaoUtil.toUUIDs(ids))); return DaoUtil.convertDataList(notificationTargetRepository.findByTenantIdAndIdIn(tenantId.getId(), DaoUtil.toUUIDs(ids)));

2
dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRuleRepository.java

@ -49,7 +49,7 @@ public interface NotificationRuleRepository extends JpaRepository<NotificationRu
@Query(RULE_INFO_QUERY + " WHERE r.id = :id") @Query(RULE_INFO_QUERY + " WHERE r.id = :id")
NotificationRuleInfoEntity findInfoById(@Param("id") UUID id); NotificationRuleInfoEntity findInfoById(@Param("id") UUID id);
@Query(RULE_INFO_QUERY + " WHERE r.tenantId = :tenantId AND lower(r.name) LIKE lower(concat('%', :searchText, '%'))") @Query(RULE_INFO_QUERY + " WHERE r.tenantId = :tenantId AND (:searchText = '' OR lower(r.name) LIKE lower(concat('%', :searchText, '%')))")
Page<NotificationRuleInfoEntity> findInfosByTenantIdAndSearchText(@Param("tenantId") UUID tenantId, Page<NotificationRuleInfoEntity> findInfosByTenantIdAndSearchText(@Param("tenantId") UUID tenantId,
@Param("searchText") String searchText, @Param("searchText") String searchText,
Pageable pageable); Pageable pageable);

15
dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTargetRepository.java

@ -30,7 +30,20 @@ import java.util.UUID;
@Repository @Repository
public interface NotificationTargetRepository extends JpaRepository<NotificationTargetEntity, UUID> { public interface NotificationTargetRepository extends JpaRepository<NotificationTargetEntity, UUID> {
Page<NotificationTargetEntity> findByTenantIdAndNameContainingIgnoreCase(UUID tenantId, String searchText, Pageable pageable); @Query("SELECT t FROM NotificationTargetEntity t WHERE t.tenantId = :tenantId " +
"AND (:searchText = '' OR lower(t.name) LIKE lower(concat('%', :searchText, '%')))")
Page<NotificationTargetEntity> findByTenantIdAndSearchText(@Param("tenantId") UUID tenantId,
@Param("searchText") String searchText,
Pageable pageable);
@Query(value = "SELECT * FROM notification_target t WHERE t.tenant_id = :tenantId " +
"AND (:searchText = '' OR lower(t.name) LIKE lower(concat('%', :searchText, '%'))) " +
"AND (cast(t.configuration as json) ->> 'type' <> 'PLATFORM_USERS' OR " +
"cast(t.configuration as json) -> 'usersFilter' ->> 'type' IN :usersFilterTypes)", nativeQuery = true)
Page<NotificationTargetEntity> findByTenantIdAndSearchTextAndUsersFilterTypeIfPresent(@Param("tenantId") UUID tenantId,
@Param("searchText") String searchText,
@Param("usersFilterTypes") List<String> usersFilterTypes,
Pageable pageable);
List<NotificationTargetEntity> findByTenantIdAndIdIn(UUID tenantId, List<UUID> ids); List<NotificationTargetEntity> findByTenantIdAndIdIn(UUID tenantId, List<UUID> ids);

12
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -2824,7 +2824,7 @@
"no-severity-matching": "'{{severity}}' not found.", "no-severity-matching": "'{{severity}}' not found.",
"no-targets-notification": "No recipients notification", "no-targets-notification": "No recipients notification",
"no-template-matching": "No resource matching '{{template}}' were found.", "no-template-matching": "No resource matching '{{template}}' were found.",
"not-found-slack-recipient": "Not found slack recipient", "not-found-slack-recipient": "Slack recipient not found",
"notification": "Notification", "notification": "Notification",
"notification-center": "Notification center", "notification-center": "Notification center",
"notification-chain": "Notification chain", "notification-chain": "Notification chain",
@ -2848,7 +2848,7 @@
"recipient": "Recipient", "recipient": "Recipient",
"recipients": "Recipients", "recipients": "Recipients",
"recipient-type": "Recipient type", "recipient-type": "Recipient type",
"recipients-count": "{ count, plural, =1 {1 Recipient} other {# Recipients} }", "recipients-count": "{ count, plural, =1 {1 recipient} other {# recipients} }",
"request-search": "Request search", "request-search": "Request search",
"request-status": { "request-status": {
"processing": "Processing", "processing": "Processing",
@ -2865,7 +2865,7 @@
"rule-node-filter": "Rule node filter", "rule-node-filter": "Rule node filter",
"rules": "Rules", "rules": "Rules",
"scheduler-later": "Schedule for later", "scheduler-later": "Schedule for later",
"search-notification": "Search notification", "search-notification": "Search notifications",
"search-rules": "Search rules", "search-rules": "Search rules",
"search-targets": "Search recipients", "search-targets": "Search recipients",
"search-templates": "Search templates", "search-templates": "Search templates",
@ -2876,11 +2876,11 @@
"settings": "Notification settings", "settings": "Notification settings",
"set-entity-from-notification": "Set entity from notification to dashboard state", "set-entity-from-notification": "Set entity from notification to dashboard state",
"slack": "Slack", "slack": "Slack",
"slack-chanel-type": "Slack chanel type", "slack-chanel-type": "Slack channel type",
"slack-chanel-types": { "slack-chanel-types": {
"direct": "Direct message", "direct": "Direct message",
"private-channel": "Private Channel", "private-channel": "Private channel",
"public-channel": "Public Channel" "public-channel": "Public channel"
}, },
"slack-settings": "Slack settings", "slack-settings": "Slack settings",
"sms-settings": "SMS settings", "sms-settings": "SMS settings",

Loading…
Cancel
Save