Browse Source

Notification system improvements:

- rate limits
- notify on rule node start failure after attempts ended
- default general template for tenants + entities limit rule
- rename ACTION_TARGET_USER to AFFECTED_USER
pull/7911/head
ViacheslavKlimov 3 years ago
parent
commit
8705a2324e
  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;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.TbRuleNodeUpdateException;
import org.thingsboard.server.actors.service.ComponentActor;
import org.thingsboard.server.actors.shared.ComponentMsgProcessor;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.msg.TbActorStopReason;
import org.thingsboard.server.dao.notification.trigger.RuleEngineComponentLifecycleEventTrigger;
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
protected void logLifecycleEvent(ComponentLifecycleEvent event, Exception 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()
.ruleChainId(getRuleChainId())
.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.TenantId;
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.queue.PartitionChangeMsg;
@ -82,7 +83,7 @@ public abstract class ComponentActor<T extends EntityId, P extends ComponentMsgP
}
@Override
public void destroy() {
public void destroy(TbActorStopReason stopReason, Throwable cause) {
try {
log.debug("[{}][{}][{}] Stopping processor.", tenantId, id, id.getEntityType());
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.msg.MsgType;
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.aware.DeviceAwareMsg;
import org.thingsboard.server.common.msg.aware.RuleChainAwareMsg;
@ -105,7 +106,7 @@ public class TenantActor extends RuleChainManagerActor {
}
@Override
public void destroy() {
public void destroy(TbActorStopReason stopReason, Throwable cause) {
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.NotificationTargetId;
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.NotificationTargetConfig;
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.UserListFilter;
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.PageLink;
import org.thingsboard.server.dao.notification.NotificationTargetService;
@ -144,6 +144,19 @@ public class NotificationTargetController extends BaseController {
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)",
notes = "Delete notification target by its id.\n\n" +
"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
* limitations under the License.
*/
package org.thingsboard.server.service.apiusage;
package org.thingsboard.server.service.apiusage.limits;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.StringUtils;
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.dao.tenant.TbTenantProfileCache;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
@Service
@RequiredArgsConstructor
@ -33,28 +31,19 @@ public class DefaultRateLimitService implements RateLimitService {
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
public boolean checkEntityExportLimit(TenantId tenantId) {
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) {
public boolean checkRateLimit(TenantId tenantId, LimitedApi api) {
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 (rateLimits != null) {
rateLimits.remove(tenantId);
if (rateLimits.isEmpty()) {
this.rateLimits.remove(rateLimitsKey);
this.rateLimits.remove(api);
}
}
return true;
@ -62,7 +51,7 @@ public class DefaultRateLimitService implements RateLimitService {
if (rateLimits == null) {
rateLimits = new ConcurrentHashMap<>();
this.rateLimits.put(rateLimitsKey, rateLimits);
this.rateLimits.put(api, rateLimits);
}
TbRateLimits rateLimit = rateLimits.get(tenantId);
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
* 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;
public interface RateLimitService {
boolean checkEntityExportLimit(TenantId tenantId);
boolean checkEntityImportLimit(TenantId tenantId);
boolean checkRateLimit(TenantId tenantId, LimitedApi api);
}

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.thingsboard.common.util.DonAsynchron;
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.id.NotificationId;
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.TbCallback;
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.NotificationService;
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.discovery.NotificationsTopicService;
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.NotificationExecutorService;
import org.thingsboard.server.service.notification.channels.NotificationChannel;
@ -97,12 +101,16 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
private final DbCallbackExecutorService dbCallbackExecutorService;
private final NotificationsTopicService notificationsTopicService;
private final TbQueueProducerProvider producerProvider;
private final RateLimitService rateLimitService;
private Map<NotificationDeliveryMethod, NotificationChannel> channels;
@Override
public NotificationRequest processNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest) {
if (!rateLimitService.checkRateLimit(tenantId, LimitedApi.NOTIFICATION_REQUEST)) {
throw new TbRateLimitsException(EntityType.TENANT);
}
NotificationSettings settings = notificationSettingsService.findNotificationSettings(tenantId);
NotificationTemplate notificationTemplate;
if (notificationRequest.getTemplateId() != null) {
@ -178,7 +186,7 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
switch (target.getConfiguration().getType()) {
case PLATFORM_USERS: {
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) {
UserId targetUserId = ((RuleOriginatedNotificationInfo) ctx.getRequest().getInfo()).getTargetUserId();
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();
}
private String getErrorMsg(Exception error) {
private String getErrorMsg(Throwable error) {
if (error == null) return null;
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.relation.RelationService;
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.sync.ie.exporting.EntityExportService;
import org.thingsboard.server.service.sync.ie.exporting.impl.BaseEntityExportService;
@ -72,7 +73,7 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS
@Override
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);
}
@ -84,7 +85,7 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS
@Override
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);
}
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);
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;
import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.TbActorStopReason;
public interface TbActor {
@ -26,7 +27,7 @@ public interface TbActor {
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) {

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())) {
log.info("[{}] Failed to init actor, attempt {}, going to stop attempts.", selfId, attempt, t);
stopReason = TbActorStopReason.INIT_FAILED;
destroy();
destroy(t.getCause());
} else if (strategy.getRetryDelay() > 0) {
log.info("[{}] Failed to init actor, attempt {}, going to retry in attempts in {}ms", selfId, attempt, strategy.getRetryDelay());
log.debug("[{}] Error", selfId, t);
@ -142,7 +142,7 @@ public final class TbActorMailbox implements TbActorCtx {
actor.process(msg);
} catch (TbRuleNodeUpdateException updateException) {
stopReason = TbActorStopReason.INIT_FAILED;
destroy();
destroy(updateException.getCause());
} catch (Throwable t) {
log.debug("[{}] Failed to process message: {}", selfId, msg, 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) {
stopReason = TbActorStopReason.STOPPED;
}
@ -216,7 +216,7 @@ public final class TbActorMailbox implements TbActorCtx {
dispatcher.getExecutor().execute(() -> {
try {
ready.set(NOT_READY);
actor.destroy();
actor.destroy(stopReason, cause);
highPriorityMsgs.forEach(msg -> msg.onTbActorStopped(stopReason));
normalPriorityMsgs.forEach(msg -> msg.onTbActorStopped(stopReason));
} 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.extern.slf4j.Slf4j;
import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.TbActorStopReason;
@Slf4j
public class TestRootActor extends AbstractTbActor {
@ -60,7 +61,7 @@ public class TestRootActor extends AbstractTbActor {
}
@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.NotificationTargetId;
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.NotificationTargetConfig;
import org.thingsboard.server.common.data.page.PageData;
@ -34,6 +35,8 @@ public interface NotificationTargetService {
PageData<NotificationTarget> findNotificationTargetsByTenantId(TenantId tenantId, PageLink pageLink);
PageData<NotificationTarget> findNotificationTargetsByTenantIdAndSupportedNotificationType(TenantId tenantId, NotificationType notificationType, PageLink pageLink);
List<NotificationTarget> findNotificationTargetsByTenantIdAndIds(TenantId tenantId, List<NotificationTargetId> ids);
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 String componentName;
private final ComponentLifecycleEvent eventType;
private final Exception error;
private final Throwable error;
@Override
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;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;
/**
@ -48,8 +49,7 @@ public enum EntityType {
NOTIFICATION,
NOTIFICATION_RULE;
public String normalName() {
return StringUtils.capitalize(name().toLowerCase().replaceAll("_", " "));
}
@Getter
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() {
NotificationTemplateConfig templateConfig = notificationTemplate.getConfiguration();
templateConfig.getDeliveryMethodsTemplates().forEach((deliveryMethod, template) -> {
if (!template.isEnabled()) return;
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());
}
if (template.isEnabled()) {
templates.put(deliveryMethod, template);
}
templates.put(deliveryMethod, template);
});
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,
"assigneeLastName", assigneeLastName,
"assigneeEmail", assigneeEmail,
"assigneeId", assigneeId != null ? assigneeId.toString() : null,
"userName", userName,
"alarmType", alarmType,
"alarmId", alarmId.toString(),
"alarmSeverity", alarmSeverity.name().toLowerCase(),
"alarmStatus", alarmStatus.toString(),
"alarmOriginatorEntityType", alarmOriginator.getEntityType().normalName(),
"alarmOriginatorEntityType", alarmOriginator.getEntityType().getNormalName(),
"alarmOriginatorId", alarmOriginator.getId().toString(),
"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 action;
private String userName;
private String alarmType;
private UUID alarmId;
private EntityId alarmOriginator;
@ -56,7 +57,7 @@ public class AlarmCommentNotificationInfo implements RuleOriginatedNotificationI
"alarmId", alarmId.toString(),
"alarmSeverity", alarmSeverity.name().toLowerCase(),
"alarmStatus", alarmStatus.toString(),
"alarmOriginatorEntityType", alarmOriginator.getEntityType().normalName(),
"alarmOriginatorEntityType", alarmOriginator.getEntityType().getNormalName(),
"alarmOriginatorId", alarmOriginator.getId().toString(),
"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(),
"alarmSeverity", alarmSeverity.name().toLowerCase(),
"alarmStatus", alarmStatus.toString(),
"alarmOriginatorEntityType", alarmOriginator.getEntityType().normalName(),
"alarmOriginatorEntityType", alarmOriginator.getEntityType().getNormalName(),
"alarmOriginatorName", alarmOriginatorName,
"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
public Map<String, String> getTemplateData() {
return mapOf(
"entityType", entityType.normalName(),
"entityType", entityType.getNormalName(),
"currentCount", String.valueOf(currentCount),
"limit", String.valueOf(limit),
"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
public Map<String, String> getTemplateData() {
return mapOf(
"entityType", entityId.getEntityType().normalName(),
"entityType", entityId.getEntityType().getNormalName(),
"entityId", entityId.toString(),
"entityName", entityName,
"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(),
"ruleChainName", ruleChainName,
"componentId", componentId.toString(),
"componentType", componentId.getEntityType().normalName(),
"componentType", componentId.getEntityType().getNormalName(),
"componentName", componentName,
"action", action,
"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
public Map<String, String> getTemplateData() {
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("msgType", msgType);
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;
@Data
public class ActionTargetUserFilter implements UsersFilter {
public class AffectedUserFilter implements UsersFilter {
@Override
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 = AllUsersFilter.class, name = "ALL_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 {

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;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
@Getter
public enum UsersFilterType {
USER_LIST,
CUSTOMER_USERS,
TENANT_ADMINISTRATORS,
ALL_USERS,
ORIGINATOR_ENTITY_OWNER_USERS,
ACTION_TARGET_USER
ORIGINATOR_ENTITY_OWNER_USERS(true),
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 lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.NotEmpty;
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "method")
@JsonSubTypes({
@ -37,6 +41,7 @@ import org.thingsboard.server.common.data.notification.NotificationDeliveryMetho
public abstract class DeliveryMethodNotificationTemplate {
private boolean enabled;
@NotEmpty
private String body;
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 org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
import javax.validation.constraints.NotEmpty;
@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class EmailDeliveryMethodNotificationTemplate extends DeliveryMethodNotificationTemplate implements HasSubject {
@NotEmpty
private String subject;
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;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
import javax.validation.Valid;
import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.NotEmpty;
import java.util.Map;
@Data
public class NotificationTemplateConfig {
private String notificationSubject;
private String defaultTextTemplate;
@Valid
@NotEmpty
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 org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
import javax.validation.constraints.NotEmpty;
@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class WebDeliveryMethodNotificationTemplate extends DeliveryMethodNotificationTemplate implements HasSubject {
@NotEmpty
private String subject;
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 tenantEntityImportRateLimit;
private String tenantNotificationRequestsRateLimit;
private long maxTransportMessages;
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();
List<NotificationDeliveryMethod> deliveryMethods;
NotificationTemplateConfig templateConfig = fromJson(this.templateConfig, NotificationTemplateConfig.class);
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) {
if (request.getStats() != null) {
Set<NotificationDeliveryMethod> methods = new HashSet<>(request.getStats().getSent().keySet());
methods.addAll(request.getStats().getErrors().keySet());
deliveryMethods = new ArrayList<>(methods);
} 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);
}

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.AlarmAction;
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.NotificationRuleTriggerConfig;
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.settings.NotificationSettings;
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.OriginatorEntityOwnerUsersFilter;
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(),
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()) {
createTemplate(tenantId, "Maintenance work notification", NotificationType.GENERAL,
"Infrastructure maintenance",
"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");
NotificationTemplate entitiesLimitNotificationTemplate = createTemplate(tenantId, "Entities limit notification", NotificationType.ENTITIES_LIMIT,
"${entityType}s limit will be reached soon",
"${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;
}
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");
NotificationTarget actionTargetUser = createTarget(tenantId, "Action target", new ActionTargetUserFilter(),
"If rule trigger is an action that targets some user (e.g. alarm assigned to user) - this user");
NotificationTarget affectedUser = createTarget(tenantId, "Affected user", new AffectedUserFilter(),
"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,
"Alarm '${alarmType}' - ${action}",
@ -181,7 +191,7 @@ public class DefaultNotificationSettingsService implements NotificationSettingsS
alarmAssignmentRuleTriggerConfig.setAlarmStatuses(null);
alarmAssignmentRuleTriggerConfig.setNotifyOn(Set.of(AlarmAssignmentNotificationRuleTriggerConfig.Action.ASSIGNED));
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,
"${componentType} '${componentName}' failed to ${action}",
@ -225,10 +235,9 @@ public class DefaultNotificationSettingsService implements NotificationSettingsS
template.setNotificationType(notificationType);
NotificationTemplateConfig templateConfig = new NotificationTemplateConfig();
templateConfig.setNotificationSubject(subjectTemplate);
templateConfig.setDefaultTextTemplate(textTemplate);
WebDeliveryMethodNotificationTemplate webTemplate = new WebDeliveryMethodNotificationTemplate();
webTemplate.setSubject(subjectTemplate);
webTemplate.setBody(textTemplate);
ObjectNode additionalConfig = newObjectNode();
ObjectNode iconConfig = newObjectNode();
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.UserId;
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.NotificationTargetConfig;
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);
}
@Override
public PageData<NotificationTarget> findNotificationTargetsByTenantIdAndSupportedNotificationType(TenantId tenantId, NotificationType notificationType, PageLink pageLink) {
return notificationTargetDao.findByTenantIdAndSupportedNotificationTypeAndPageLink(tenantId, notificationType, pageLink);
}
@Override
public List<NotificationTarget> findNotificationTargetsByTenantIdAndIds(TenantId tenantId, List<NotificationTargetId> 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.TenantId;
import org.thingsboard.server.common.data.notification.NotificationType;
import org.thingsboard.server.common.data.notification.targets.NotificationTarget;
import org.thingsboard.server.common.data.page.PageData;
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> findByTenantIdAndSupportedNotificationTypeAndPageLink(TenantId tenantId, NotificationType notificationType, PageLink pageLink);
List<NotificationTarget> findByTenantIdAndIds(TenantId tenantId, List<NotificationTargetId> ids);
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.Pattern;
import static org.apache.commons.lang3.StringUtils.capitalize;
@Slf4j
public abstract class DataValidator<D extends BaseData<?>> {
private static final Pattern EMAIL_PATTERN =
@ -106,7 +104,7 @@ public abstract class DataValidator<D extends BaseData<?>> {
protected void validateNumberOfEntitiesPerTenant(TenantId tenantId,
EntityType 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 java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
@ -60,7 +61,10 @@ public class JpaNotificationRequestDao extends JpaAbstractDao<NotificationReques
@Override
public PageData<NotificationRequestInfo> findInfosByTenantIdAndOriginatorTypeAndPageLink(TenantId tenantId, EntityType originatorType, PageLink pageLink) {
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

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 java.util.List;
import java.util.Map;
import java.util.UUID;
@Component
@ -54,7 +55,10 @@ public class JpaNotificationRuleDao extends JpaAbstractDao<NotificationRuleEntit
@Override
public PageData<NotificationRuleInfo> findInfosByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink) {
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

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.id.NotificationTargetId;
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.platform.UsersFilterType;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
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.util.SqlDao;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import static org.thingsboard.server.dao.DaoUtil.getId;
@Component
@SqlDao
@RequiredArgsConstructor
@ -47,10 +47,20 @@ public class JpaNotificationTargetDao extends JpaAbstractDao<NotificationTargetE
@Override
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)));
}
@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
public List<NotificationTarget> findByTenantIdAndIds(TenantId tenantId, List<NotificationTargetId> 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")
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,
@Param("searchText") String searchText,
Pageable pageable);

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

@ -30,7 +30,20 @@ import java.util.UUID;
@Repository
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);

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

@ -2818,7 +2818,7 @@
"no-severity-matching": "'{{severity}}' not found.",
"no-targets-notification": "No recipients notification",
"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-center": "Notification center",
"notification-chain": "Notification chain",
@ -2842,7 +2842,7 @@
"recipient": "Recipient",
"recipients": "Recipients",
"recipient-type": "Recipient type",
"recipients-count": "{ count, plural, =1 {1 Recipient} other {# Recipients} }",
"recipients-count": "{ count, plural, =1 {1 recipient} other {# recipients} }",
"request-status": {
"processing": "Processing",
"scheduled": "Scheduled",
@ -2858,7 +2858,7 @@
"rule-node-filter": "Rule node filter",
"rules": "Rules",
"scheduler-later": "Schedule for later",
"search-notification": "Search notification",
"search-notification": "Search notifications",
"search-rules": "Search rules",
"search-targets": "Search recipients",
"search-templates": "Search templates",
@ -2867,11 +2867,11 @@
"settings": "Notification settings",
"set-entity-from-notification": "Set entity from notification to dashboard state",
"slack": "Slack",
"slack-chanel-type": "Slack chanel type",
"slack-chanel-type": "Slack channel type",
"slack-chanel-types": {
"direct": "Direct message",
"private-channel": "Private Channel",
"public-channel": "Public Channel"
"private-channel": "Private channel",
"public-channel": "Public channel"
},
"slack-settings": "Slack settings",
"sms-settings": "SMS settings",

Loading…
Cancel
Save