diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleEngineComponentActor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleEngineComponentActor.java index 327478367b..d330dfceec 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleEngineComponentActor.java +++ b/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> extends ComponentActor { @@ -33,6 +35,21 @@ public abstract class RuleEngineComponentActor 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." + diff --git a/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultRateLimitService.java b/application/src/main/java/org/thingsboard/server/service/apiusage/limits/DefaultRateLimitService.java similarity index 66% rename from application/src/main/java/org/thingsboard/server/service/apiusage/DefaultRateLimitService.java rename to application/src/main/java/org/thingsboard/server/service/apiusage/limits/DefaultRateLimitService.java index dd3f4c5150..b75fb99c02 100644 --- a/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultRateLimitService.java +++ b/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> rateLimits = new ConcurrentHashMap<>(); + private final Map> 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 rateLimitConfigExtractor) { + public boolean checkRateLimit(TenantId tenantId, LimitedApi api) { String rateLimitConfig = tenantProfileCache.get(tenantId).getProfileConfiguration() - .map(rateLimitConfigExtractor).orElse(null); + .map(api::getLimitConfig).orElse(null); - Map rateLimits = this.rateLimits.get(rateLimitsKey); + Map 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)) { diff --git a/application/src/main/java/org/thingsboard/server/service/apiusage/limits/LimitedApi.java b/application/src/main/java/org/thingsboard/server/service/apiusage/limits/LimitedApi.java new file mode 100644 index 0000000000..f69ece6661 --- /dev/null +++ b/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 configExtractor; + + public String getLimitConfig(DefaultTenantProfileConfiguration profileConfiguration) { + return configExtractor.apply(profileConfiguration); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/apiusage/RateLimitService.java b/application/src/main/java/org/thingsboard/server/service/apiusage/limits/RateLimitService.java similarity index 81% rename from application/src/main/java/org/thingsboard/server/service/apiusage/RateLimitService.java rename to application/src/main/java/org/thingsboard/server/service/apiusage/limits/RateLimitService.java index af5debac2e..c984ec8fa5 100644 --- a/application/src/main/java/org/thingsboard/server/service/apiusage/RateLimitService.java +++ b/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); } diff --git a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java index a2521743de..05fa07af51 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java +++ b/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 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) { diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/RuleEngineComponentLifecycleEventTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/RuleEngineComponentLifecycleEventTriggerProcessor.java index b31371e9be..afda3cc001 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/RuleEngineComponentLifecycleEventTriggerProcessor.java +++ b/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(); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java index 77d8c916e1..b646cb7ef6 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java +++ b/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 , I extends EntityId> EntityExportData 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 , I extends EntityId> EntityImportResult importEntity(EntitiesImportCtx ctx, EntityExportData 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) { diff --git a/common/actor/src/main/java/org/thingsboard/server/actors/DefaultTbActorSystem.java b/common/actor/src/main/java/org/thingsboard/server/actors/DefaultTbActorSystem.java index e0b63c84ec..26ef4e8822 100644 --- a/common/actor/src/main/java/org/thingsboard/server/actors/DefaultTbActorSystem.java +++ b/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); } } diff --git a/common/actor/src/main/java/org/thingsboard/server/actors/TbActor.java b/common/actor/src/main/java/org/thingsboard/server/actors/TbActor.java index c042e2bec5..3b6456ab18 100644 --- a/common/actor/src/main/java/org/thingsboard/server/actors/TbActor.java +++ b/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) { diff --git a/common/actor/src/main/java/org/thingsboard/server/actors/TbActorMailbox.java b/common/actor/src/main/java/org/thingsboard/server/actors/TbActorMailbox.java index bc0d8835ba..587da8f3b1 100644 --- a/common/actor/src/main/java/org/thingsboard/server/actors/TbActorMailbox.java +++ b/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) { diff --git a/common/actor/src/test/java/org/thingsboard/server/actors/TestRootActor.java b/common/actor/src/test/java/org/thingsboard/server/actors/TestRootActor.java index e91038d41b..7a79fb4872 100644 --- a/common/actor/src/test/java/org/thingsboard/server/actors/TestRootActor.java +++ b/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) { } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java index 7852a64f0d..b67edd123c 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java @@ -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 findNotificationTargetsByTenantId(TenantId tenantId, PageLink pageLink); + PageData findNotificationTargetsByTenantIdAndSupportedNotificationType(TenantId tenantId, NotificationType notificationType, PageLink pageLink); + List findNotificationTargetsByTenantIdAndIds(TenantId tenantId, List ids); PageData findRecipientsForNotificationTarget(TenantId tenantId, CustomerId customerId, NotificationTargetId targetId, PageLink pageLink); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/trigger/RuleEngineComponentLifecycleEventTrigger.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/trigger/RuleEngineComponentLifecycleEventTrigger.java index 6f096d2b40..4ba438a385 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/trigger/RuleEngineComponentLifecycleEventTrigger.java +++ b/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() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java index 2763479cfe..a4b4c1e5fb 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java +++ b/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("_", " ")); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationProcessingContext.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationProcessingContext.java index ab3e03191e..119b4a473e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationProcessingContext.java +++ b/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(); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmAssignmentNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmAssignmentNotificationInfo.java index c94122224f..46de48840e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmAssignmentNotificationInfo.java +++ b/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 ); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmCommentNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmCommentNotificationInfo.java index d05b9066fd..59026a5666 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmCommentNotificationInfo.java +++ b/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 ); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmNotificationInfo.java index 163ac222d0..ed179ad4a4 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmNotificationInfo.java +++ b/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() ); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EntitiesLimitNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EntitiesLimitNotificationInfo.java index c29620c6c5..2c277492e3 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EntitiesLimitNotificationInfo.java +++ b/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 getTemplateData() { return mapOf( - "entityType", entityType.normalName(), + "entityType", entityType.getNormalName(), "currentCount", String.valueOf(currentCount), "limit", String.valueOf(limit), "percents", String.valueOf(percents) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EntityActionNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EntityActionNotificationInfo.java index 4ad2172ac6..825148e52c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EntityActionNotificationInfo.java +++ b/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 getTemplateData() { return mapOf( - "entityType", entityId.getEntityType().normalName(), + "entityType", entityId.getEntityType().getNormalName(), "entityId", entityId.toString(), "entityName", entityName, "actionType", actionType.name().toLowerCase(), diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/RuleEngineComponentLifecycleEventNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/RuleEngineComponentLifecycleEventNotificationInfo.java index 3dc01c7ebd..9baac13c79 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/RuleEngineComponentLifecycleEventNotificationInfo.java +++ b/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(), diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/RuleEngineOriginatedNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/RuleEngineOriginatedNotificationInfo.java index 924028da36..8a40b3c1b0 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/RuleEngineOriginatedNotificationInfo.java +++ b/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 getTemplateData() { Map 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; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/ActionTargetUserFilter.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/AffectedUserFilter.java similarity index 87% rename from common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/ActionTargetUserFilter.java rename to common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/AffectedUserFilter.java index 54d88ddccf..26f0d0295e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/ActionTargetUserFilter.java +++ b/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; } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UsersFilter.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UsersFilter.java index 7131a060cb..62773f6473 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UsersFilter.java +++ b/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 { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UsersFilterType.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UsersFilterType.java index e205c4c3dc..10a4d3c34f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UsersFilterType.java +++ b/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; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.java index ba098304d6..9a3d0775e9 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.java +++ b/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) { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/EmailDeliveryMethodNotificationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/EmailDeliveryMethodNotificationTemplate.java index ab86c82023..70c991481b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/EmailDeliveryMethodNotificationTemplate.java +++ b/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) { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/NotificationTemplateConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/NotificationTemplateConfig.java index eaebe37c0b..1dcb4aec0c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/NotificationTemplateConfig.java +++ b/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 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; - } - } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/WebDeliveryMethodNotificationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/WebDeliveryMethodNotificationTemplate.java index 3b9b852eff..8740e3a403 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/WebDeliveryMethodNotificationTemplate.java +++ b/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; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java index 8790017b7d..030587f50f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java +++ b/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; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRequestInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRequestInfoEntity.java index af2574935b..803ebc0b79 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRequestInfoEntity.java +++ b/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 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 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); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationSettingsService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationSettingsService.java index 7cc5dad289..f0f3a11d3a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationSettingsService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationSettingsService.java @@ -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); diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java index fe85e4c6fc..13fa0cffd4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java @@ -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 findNotificationTargetsByTenantIdAndSupportedNotificationType(TenantId tenantId, NotificationType notificationType, PageLink pageLink) { + return notificationTargetDao.findByTenantIdAndSupportedNotificationTypeAndPageLink(tenantId, notificationType, pageLink); + } + @Override public List findNotificationTargetsByTenantIdAndIds(TenantId tenantId, List ids) { return notificationTargetDao.findByTenantIdAndIds(tenantId, ids); diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetDao.java b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetDao.java index b3b0b6939c..55c4b7f77c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetDao.java @@ -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 { PageData findByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink); + PageData findByTenantIdAndSupportedNotificationTypeAndPageLink(TenantId tenantId, NotificationType notificationType, PageLink pageLink); + List findByTenantIdAndIds(TenantId tenantId, List ids); void removeByTenantId(TenantId tenantId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java index 131a6a619e..01edf184fd 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java +++ b/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> { private static final Pattern EMAIL_PATTERN = @@ -106,7 +104,7 @@ public abstract class DataValidator> { 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"); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java index c557b9c93a..53b8318d3f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java +++ b/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 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 diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java index ab6f263aed..46910f119f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java +++ b/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 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 diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java index 01869ee4d2..8e4f3af97c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java @@ -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 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 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 findByTenantIdAndIds(TenantId tenantId, List ids) { return DaoUtil.convertDataList(notificationTargetRepository.findByTenantIdAndIdIn(tenantId.getId(), DaoUtil.toUUIDs(ids))); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRuleRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRuleRepository.java index c44a4baa0b..145905593f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRuleRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRuleRepository.java @@ -49,7 +49,7 @@ public interface NotificationRuleRepository extends JpaRepository findInfosByTenantIdAndSearchText(@Param("tenantId") UUID tenantId, @Param("searchText") String searchText, Pageable pageable); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTargetRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTargetRepository.java index 1b72f8d64a..a958055a33 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTargetRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTargetRepository.java @@ -30,7 +30,20 @@ import java.util.UUID; @Repository public interface NotificationTargetRepository extends JpaRepository { - Page 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 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 findByTenantIdAndSearchTextAndUsersFilterTypeIfPresent(@Param("tenantId") UUID tenantId, + @Param("searchText") String searchText, + @Param("usersFilterTypes") List usersFilterTypes, + Pageable pageable); List findByTenantIdAndIdIn(UUID tenantId, List ids); diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index c127224749..9ab4294146 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/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",