diff --git a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java index a6f69d7079..24daab29f0 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java @@ -39,7 +39,6 @@ import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.Notification; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; -import org.thingsboard.server.service.notification.NotificationProcessingContext; import org.thingsboard.server.common.data.notification.NotificationRequest; import org.thingsboard.server.common.data.notification.NotificationRequestInfo; import org.thingsboard.server.common.data.notification.NotificationRequestPreview; @@ -57,8 +56,10 @@ import org.thingsboard.server.dao.notification.NotificationSettingsService; import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.dao.notification.NotificationTemplateService; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.notification.NotificationProcessingContext; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; +import org.thingsboard.server.service.security.permission.Resource; import javax.validation.Valid; import java.util.Collections; @@ -294,8 +295,8 @@ public class NotificationController extends BaseController { @PostMapping("/notification/settings") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") public NotificationSettings saveNotificationSettings(@RequestBody @Valid NotificationSettings notificationSettings, - @AuthenticationPrincipal SecurityUser user) { - // generic permission + @AuthenticationPrincipal SecurityUser user) throws ThingsboardException { + accessControlService.checkPermission(user, Resource.ADMIN_SETTINGS, Operation.WRITE); TenantId tenantId = user.isSystemAdmin() ? TenantId.SYS_TENANT_ID : user.getTenantId(); notificationSettingsService.saveNotificationSettings(tenantId, notificationSettings); return notificationSettings; @@ -303,10 +304,17 @@ public class NotificationController extends BaseController { @GetMapping("/notification/settings") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - public NotificationSettings getNotificationSettings(@AuthenticationPrincipal SecurityUser user) { - // generic permission + public NotificationSettings getNotificationSettings(@AuthenticationPrincipal SecurityUser user) throws ThingsboardException { + accessControlService.checkPermission(user, Resource.ADMIN_SETTINGS, Operation.READ); TenantId tenantId = user.isSystemAdmin() ? TenantId.SYS_TENANT_ID : user.getTenantId(); return notificationSettingsService.findNotificationSettings(tenantId); } + @GetMapping("/notification/deliveryMethods") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + public Set getAvailableDeliveryMethods(@AuthenticationPrincipal SecurityUser user) throws ThingsboardException { + accessControlService.checkPermission(user, Resource.ADMIN_SETTINGS, Operation.READ); + return notificationCenter.getAvailableDeliveryMethods(user.getTenantId()); + } + } 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 4f13072504..619616bc8b 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 @@ -22,7 +22,9 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.thingsboard.common.util.DonAsynchron; +import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.rule.engine.api.NotificationCenter; +import org.thingsboard.rule.engine.api.SmsService; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.id.NotificationId; @@ -101,6 +103,8 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple private final NotificationsTopicService notificationsTopicService; private final TbQueueProducerProvider producerProvider; private final RateLimitService rateLimitService; + private final MailService mailService; + private final SmsService smsService; private Map channels; @@ -121,13 +125,12 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple List targets = notificationTargetService.findNotificationTargetsByTenantIdAndIds(tenantId, notificationRequest.getTargets().stream().map(NotificationTargetId::new).collect(Collectors.toList())); + Set availableDeliveryMethods = getAvailableDeliveryMethods(tenantId); notificationTemplate.getConfiguration().getDeliveryMethodsTemplates().forEach((deliveryMethod, template) -> { if (!template.isEnabled()) return; - if (deliveryMethod == NotificationDeliveryMethod.SLACK) { - if (!settings.getDeliveryMethodsConfigs().containsKey(deliveryMethod)) { - throw new IllegalArgumentException("Slack must be configured in the settings"); - } + if (!availableDeliveryMethods.contains(deliveryMethod)) { + throw new IllegalArgumentException("Settings for " + deliveryMethod.getName() + " are missing"); } if (notificationRequest.getRuleId() == null) { if (targets.stream().noneMatch(target -> target.getConfiguration().getType().getSupportedDeliveryMethods().contains(deliveryMethod))) { @@ -340,6 +343,24 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple } } + @Override + public Set getAvailableDeliveryMethods(TenantId tenantId) { + Set deliveryMethods = new HashSet<>(); + deliveryMethods.add(NotificationDeliveryMethod.WEB); + NotificationSettings notificationSettings = notificationSettingsService.findNotificationSettings(tenantId); + if (notificationSettings.getDeliveryMethodsConfigs().containsKey(NotificationDeliveryMethod.SLACK)) { + deliveryMethods.add(NotificationDeliveryMethod.SLACK); + } + try { + mailService.testConnection(tenantId); + deliveryMethods.add(NotificationDeliveryMethod.EMAIL); + } catch (Exception e) {} + if (smsService.isConfigured(tenantId)) { + deliveryMethods.add(NotificationDeliveryMethod.SMS); + } + return deliveryMethods; + } + @Override public void deleteNotificationRequest(TenantId tenantId, NotificationRequestId notificationRequestId) { log.debug("Deleting notification request {}", notificationRequestId); diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index d890ed3135..d96e50ca7f 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -440,6 +440,9 @@ cache: notificationRules: timeToLiveInMinutes: "${CACHE_SPECS_NOTIFICATION_RULES_TTL:1440}" maxSize: "${CACHE_SPECS_NOTIFICATION_RULES_MAX_SIZE:10000}" + notificationSettings: + timeToLiveInMinutes: "${CACHE_SPECS_NOTIFICATION_SETTINGS_TTL:10}" + maxSize: "${CACHE_SPECS_NOTIFICATION_SETTINGS_MAX_SIZE:1000}" attributes: timeToLiveInMinutes: "${CACHE_SPECS_ATTRIBUTES_TTL:1440}" maxSize: "${CACHE_SPECS_ATTRIBUTES_MAX_SIZE:100000}" diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java b/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java index f59ea8d5d0..eedf925d61 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java @@ -30,6 +30,7 @@ public class CacheConstants { public static final String TENANTS_EXIST_CACHE = "tenantsExist"; public static final String DEVICE_PROFILE_CACHE = "deviceProfiles"; public static final String NOTIFICATION_RULES_CACHE = "notificationRules"; + public static final String NOTIFICATION_SETTINGS_CACHE = "notificationSettings"; public static final String ASSET_PROFILE_CACHE = "assetProfiles"; public static final String ATTRIBUTES_CACHE = "attributes"; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRule.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRule.java index fdcfb99da2..529bc9a3ab 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRule.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRule.java @@ -33,11 +33,12 @@ import javax.validation.Valid; import javax.validation.constraints.AssertTrue; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; +import java.io.Serializable; @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) -public class NotificationRule extends BaseData implements HasTenantId, HasName { +public class NotificationRule extends BaseData implements HasTenantId, HasName, Serializable { private TenantId tenantId; @NotBlank diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRuleConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRuleConfig.java index 68683afcee..73522c7108 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRuleConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRuleConfig.java @@ -17,8 +17,10 @@ package org.thingsboard.server.common.data.notification.rule; import lombok.Data; +import java.io.Serializable; + @Data -public class NotificationRuleConfig { +public class NotificationRuleConfig implements Serializable { private String description; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRuleRecipientsConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRuleRecipientsConfig.java index f2c6ca2863..d31ec098ca 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRuleRecipientsConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRuleRecipientsConfig.java @@ -24,6 +24,7 @@ import lombok.Data; import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; import javax.validation.constraints.NotNull; +import java.io.Serializable; import java.util.List; import java.util.Map; import java.util.UUID; @@ -34,7 +35,7 @@ import java.util.UUID; @Type(name = "ALARM", value = EscalatedNotificationRuleRecipientsConfig.class), }) @Data -public abstract class NotificationRuleRecipientsConfig { +public abstract class NotificationRuleRecipientsConfig implements Serializable { @NotNull private NotificationRuleTriggerType triggerType; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTriggerConfig.java index c60c72b8de..f67a144bed 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTriggerConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTriggerConfig.java @@ -20,6 +20,8 @@ import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonSubTypes.Type; import com.fasterxml.jackson.annotation.JsonTypeInfo; +import java.io.Serializable; + @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "triggerType") @JsonSubTypes({ @@ -32,7 +34,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; @Type(value = NewPlatformVersionNotificationRuleTriggerConfig.class, name = "NEW_PLATFORM_VERSION"), @Type(value = EntitiesLimitNotificationRuleTriggerConfig.class, name = "ENTITIES_LIMIT") }) -public interface NotificationRuleTriggerConfig { +public interface NotificationRuleTriggerConfig extends Serializable { NotificationRuleTriggerType getTriggerType(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/NotificationDeliveryMethodConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/NotificationDeliveryMethodConfig.java index ea3451338d..962eba6d06 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/NotificationDeliveryMethodConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/NotificationDeliveryMethodConfig.java @@ -22,12 +22,14 @@ import com.fasterxml.jackson.annotation.JsonSubTypes.Type; import com.fasterxml.jackson.annotation.JsonTypeInfo; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; +import java.io.Serializable; + @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "method") @JsonSubTypes({ @Type(name = "SLACK", value = SlackNotificationDeliveryMethodConfig.class) }) -public interface NotificationDeliveryMethodConfig { +public interface NotificationDeliveryMethodConfig extends Serializable { @JsonIgnore NotificationDeliveryMethod getMethod(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/NotificationSettings.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/NotificationSettings.java index face4ca017..4ed6cfc30d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/NotificationSettings.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/NotificationSettings.java @@ -20,10 +20,11 @@ import org.thingsboard.server.common.data.notification.NotificationDeliveryMetho import javax.validation.Valid; import javax.validation.constraints.NotNull; +import java.io.Serializable; import java.util.Map; @Data -public class NotificationSettings { +public class NotificationSettings implements Serializable { @NotNull @Valid 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 eb995c4d86..1088347397 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 @@ -17,14 +17,16 @@ package org.thingsboard.server.dao.notification; import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.AdminSettings; +import org.thingsboard.server.common.data.CacheConstants; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; -import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.id.NotificationTemplateId; import org.thingsboard.server.common.data.id.TenantId; @@ -78,6 +80,7 @@ public class DefaultNotificationSettingsService implements NotificationSettingsS private static final String SETTINGS_KEY = "notifications"; + @CacheEvict(cacheNames = CacheConstants.NOTIFICATION_SETTINGS_CACHE, key = "#tenantId") @Override public void saveNotificationSettings(TenantId tenantId, NotificationSettings settings) { AdminSettings adminSettings = Optional.ofNullable(adminSettingsService.findAdminSettingsByTenantIdAndKey(tenantId, SETTINGS_KEY)) @@ -91,6 +94,7 @@ public class DefaultNotificationSettingsService implements NotificationSettingsS adminSettingsService.saveAdminSettings(tenantId, adminSettings); } + @Cacheable(cacheNames = CacheConstants.NOTIFICATION_SETTINGS_CACHE, key = "#tenantId") @Override public NotificationSettings findNotificationSettings(TenantId tenantId) { return Optional.ofNullable(adminSettingsService.findAdminSettingsByTenantIdAndKey(tenantId, SETTINGS_KEY)) diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/NotificationCenter.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/NotificationCenter.java index eccc4f1423..b263e8da17 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/NotificationCenter.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/NotificationCenter.java @@ -19,8 +19,11 @@ import org.thingsboard.server.common.data.id.NotificationId; import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; import org.thingsboard.server.common.data.notification.NotificationRequest; +import java.util.Set; + public interface NotificationCenter { NotificationRequest processNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest); @@ -29,11 +32,12 @@ public interface NotificationCenter { void sendBasicNotification(TenantId tenantId, UserId recipientId, String subject, String text); - void markNotificationAsRead(TenantId tenantId, UserId recipientId, NotificationId notificationId); void markAllNotificationsAsRead(TenantId tenantId, UserId recipientId); void deleteNotification(TenantId tenantId, UserId recipientId, NotificationId notificationId); + Set getAvailableDeliveryMethods(TenantId tenantId); + }