From e9053c0cae6b6453c94c382dfe6cc20f031afeef Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Mon, 18 Nov 2024 12:43:14 +0200 Subject: [PATCH] System notification type --- .../server/controller/BaseController.java | 10 +++ .../controller/NotificationController.java | 8 ++ .../NotificationTargetController.java | 4 +- .../DefaultNotificationCenter.java | 19 +++- .../server/controller/AbstractWebTest.java | 90 +++++++++++++++++++ .../server/edge/NotificationEdgeTest.java | 8 -- .../AbstractNotificationApiTest.java | 86 ------------------ .../NotificationSettingsService.java | 6 ++ .../NotificationTemplateService.java | 8 +- .../data/notification/NotificationType.java | 11 ++- .../DefaultNotificationSettingsService.java | 51 +++++++++++ .../DefaultNotificationTargetService.java | 2 +- .../DefaultNotificationTemplateService.java | 37 +++++++- .../notification/NotificationTemplateDao.java | 3 +- .../JpaNotificationTemplateDao.java | 3 +- .../NotificationTemplateRepository.java | 3 +- .../rule/engine/api/NotificationCenter.java | 5 ++ 17 files changed, 247 insertions(+), 107 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index cc9c989cfa..8470566af3 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -90,6 +90,7 @@ import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.MobileAppBundleId; import org.thingsboard.server.common.data.id.MobileAppId; +import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.id.OAuth2ClientId; import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.QueueId; @@ -105,6 +106,7 @@ import org.thingsboard.server.common.data.id.WidgetTypeId; import org.thingsboard.server.common.data.id.WidgetsBundleId; import org.thingsboard.server.common.data.mobile.app.MobileApp; import org.thingsboard.server.common.data.mobile.bundle.MobileAppBundle; +import org.thingsboard.server.common.data.notification.targets.NotificationTarget; import org.thingsboard.server.common.data.oauth2.OAuth2Client; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.SortOrder; @@ -141,6 +143,7 @@ import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.mobile.MobileAppBundleService; import org.thingsboard.server.dao.mobile.MobileAppService; import org.thingsboard.server.dao.model.ModelConstants; +import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.dao.oauth2.OAuth2ClientService; import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService; import org.thingsboard.server.dao.ota.OtaPackageService; @@ -355,6 +358,9 @@ public abstract class BaseController { @Autowired protected TbServiceInfoProvider serviceInfoProvider; + @Autowired + protected NotificationTargetService notificationTargetService; + @Value("${server.log_controller_error_stack_trace}") @Getter private boolean logControllerErrorStackTrace; @@ -852,6 +858,10 @@ public abstract class BaseController { return checkEntityId(mobileAppBundleId, mobileAppBundleService::findMobileAppBundleById, operation); } + NotificationTarget checkNotificationTargetId(NotificationTargetId notificationTargetId, Operation operation) throws ThingsboardException { + return checkEntityId(notificationTargetId, notificationTargetService::findNotificationTargetById, operation); + } + protected I emptyId(EntityType entityType) { return (I) EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID); } 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 c1248a1fbf..e055c6d6ae 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java @@ -266,6 +266,12 @@ public class NotificationController extends BaseController { } notificationRequest.setTenantId(user.getTenantId()); checkEntity(notificationRequest.getId(), notificationRequest, NOTIFICATION); + List targets = notificationRequest.getTargets().stream() + .map(NotificationTargetId::new) + .toList(); + for (NotificationTargetId targetId : targets) { + checkNotificationTargetId(targetId, Operation.READ); + } notificationRequest.setOriginatorEntityId(user.getId()); notificationRequest.setInfo(null); @@ -316,6 +322,8 @@ public class NotificationController extends BaseController { Map recipientsCountByTarget = new LinkedHashMap<>(); Map firstRecipient = new HashMap<>(); for (NotificationTarget target : targets) { + checkEntity(getCurrentUser(), target, Operation.READ); + int recipientsCount; List recipientsPart; NotificationTargetType targetType = target.getConfiguration().getType(); diff --git a/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java b/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java index 7634819e39..6a45b9e4e5 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java @@ -127,7 +127,7 @@ public class NotificationTargetController extends BaseController { @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") public NotificationTarget getNotificationTargetById(@PathVariable UUID id) throws ThingsboardException { NotificationTargetId notificationTargetId = new NotificationTargetId(id); - return checkEntityId(notificationTargetId, notificationTargetService::findNotificationTargetById, Operation.READ); + return checkNotificationTargetId(notificationTargetId, Operation.READ); } @ApiOperation(value = "Get recipients for notification target config (getRecipientsForNotificationTargetConfig)", @@ -214,7 +214,7 @@ public class NotificationTargetController extends BaseController { @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") public void deleteNotificationTargetById(@PathVariable UUID id) throws Exception { NotificationTargetId notificationTargetId = new NotificationTargetId(id); - NotificationTarget notificationTarget = checkEntityId(notificationTargetId, notificationTargetService::findNotificationTargetById, Operation.DELETE); + NotificationTarget notificationTarget = checkNotificationTargetId(notificationTargetId, Operation.DELETE); doDeleteAndLog(EntityType.NOTIFICATION_TARGET, notificationTarget, notificationTargetService::deleteNotificationTargetById); } 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 71e2b576ed..311fe6cb2c 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 @@ -40,7 +40,9 @@ import org.thingsboard.server.common.data.notification.NotificationRequestConfig import org.thingsboard.server.common.data.notification.NotificationRequestStats; import org.thingsboard.server.common.data.notification.NotificationRequestStatus; import org.thingsboard.server.common.data.notification.NotificationStatus; +import org.thingsboard.server.common.data.notification.NotificationType; import org.thingsboard.server.common.data.notification.info.GeneralNotificationInfo; +import org.thingsboard.server.common.data.notification.info.NotificationInfo; import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo; import org.thingsboard.server.common.data.notification.settings.NotificationSettings; import org.thingsboard.server.common.data.notification.settings.UserNotificationSettings; @@ -217,6 +219,21 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple } } + @Override + public void sendSystemNotification(TenantId tenantId, NotificationTargetId targetId, NotificationType type, NotificationInfo info) { + log.debug("[{}] Sending {} system notification to {}: {}", tenantId, type, targetId, info); + NotificationTemplate notificationTemplate = notificationTemplateService.findTenantOrSystemNotificationTemplate(tenantId, type) + .orElseThrow(() -> new IllegalArgumentException("No notification template found for type " + type)); + NotificationRequest notificationRequest = NotificationRequest.builder() + .tenantId(TenantId.SYS_TENANT_ID) + .targets(List.of(targetId.getId())) + .templateId(notificationTemplate.getId()) + .info(info) + .originatorEntityId(TenantId.SYS_TENANT_ID) + .build(); + processNotificationRequest(TenantId.SYS_TENANT_ID, notificationRequest, null); + } + private void processNotificationRequestAsync(NotificationProcessingContext ctx, List targets, FutureCallback callback) { notificationExecutor.submit(() -> { long startTs = System.currentTimeMillis(); @@ -271,7 +288,7 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple }, 256); } else { recipients = new PageDataIterable<>(pageLink -> { - return notificationTargetService.findRecipientsForNotificationTargetConfig(ctx.getTenantId(), targetConfig, pageLink); + return notificationTargetService.findRecipientsForNotificationTargetConfig(target.getTenantId(), targetConfig, pageLink); }, 256); } } diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java index af20c71e5b..9acc5426be 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java @@ -23,6 +23,7 @@ import com.google.common.util.concurrent.ListeningExecutorService; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jws; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.RandomStringUtils; import org.awaitility.Awaitility; import org.hamcrest.Matcher; import org.hibernate.exception.ConstraintViolationException; @@ -99,6 +100,21 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.notification.Notification; +import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; +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.PlatformUsersNotificationTargetConfig; +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.template.DeliveryMethodNotificationTemplate; +import org.thingsboard.server.common.data.notification.template.EmailDeliveryMethodNotificationTemplate; +import org.thingsboard.server.common.data.notification.template.HasSubject; +import org.thingsboard.server.common.data.notification.template.MobileAppDeliveryMethodNotificationTemplate; +import org.thingsboard.server.common.data.notification.template.NotificationTemplate; +import org.thingsboard.server.common.data.notification.template.NotificationTemplateConfig; +import org.thingsboard.server.common.data.notification.template.SmsDeliveryMethodNotificationTemplate; +import org.thingsboard.server.common.data.notification.template.WebDeliveryMethodNotificationTemplate; import org.thingsboard.server.common.data.oauth2.MapperType; import org.thingsboard.server.common.data.oauth2.OAuth2Client; import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig; @@ -116,6 +132,7 @@ import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; import org.thingsboard.server.common.msg.session.FeatureType; import org.thingsboard.server.config.ThingsboardSecurityConfiguration; import org.thingsboard.server.dao.Dao; +import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.device.ClaimDevicesService; import org.thingsboard.server.dao.tenant.TenantProfileService; @@ -136,6 +153,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; @@ -1162,4 +1180,76 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { return oAuth2Client; } + protected NotificationTarget createNotificationTarget(UserId... usersIds) { + UserListFilter filter = new UserListFilter(); + filter.setUsersIds(DaoUtil.toUUIDs(List.of(usersIds))); + return createNotificationTarget(filter); + } + + protected NotificationTarget createNotificationTarget(UsersFilter usersFilter) { + NotificationTarget notificationTarget = new NotificationTarget(); + notificationTarget.setName(usersFilter.toString() + RandomStringUtils.randomNumeric(5)); + PlatformUsersNotificationTargetConfig targetConfig = new PlatformUsersNotificationTargetConfig(); + targetConfig.setUsersFilter(usersFilter); + notificationTarget.setConfiguration(targetConfig); + return saveNotificationTarget(notificationTarget); + } + + protected NotificationTarget saveNotificationTarget(NotificationTarget notificationTarget) { + return doPost("/api/notification/target", notificationTarget, NotificationTarget.class); + } + + protected NotificationTemplate createNotificationTemplate(NotificationType notificationType, String subject, + String text, NotificationDeliveryMethod... deliveryMethods) { + NotificationTemplate notificationTemplate = new NotificationTemplate(); + notificationTemplate.setTenantId(tenantId); + notificationTemplate.setName("Notification template: " + text); + notificationTemplate.setNotificationType(notificationType); + NotificationTemplateConfig config = new NotificationTemplateConfig(); + config.setDeliveryMethodsTemplates(new HashMap<>()); + for (NotificationDeliveryMethod deliveryMethod : deliveryMethods) { + DeliveryMethodNotificationTemplate deliveryMethodNotificationTemplate; + switch (deliveryMethod) { + case WEB: { + deliveryMethodNotificationTemplate = new WebDeliveryMethodNotificationTemplate(); + break; + } + case EMAIL: { + deliveryMethodNotificationTemplate = new EmailDeliveryMethodNotificationTemplate(); + break; + } + case SMS: { + deliveryMethodNotificationTemplate = new SmsDeliveryMethodNotificationTemplate(); + break; + } + case MOBILE_APP: + deliveryMethodNotificationTemplate = new MobileAppDeliveryMethodNotificationTemplate(); + break; + default: + throw new IllegalArgumentException("Unsupported delivery method " + deliveryMethod); + } + deliveryMethodNotificationTemplate.setEnabled(true); + deliveryMethodNotificationTemplate.setBody(text); + if (deliveryMethodNotificationTemplate instanceof HasSubject) { + ((HasSubject) deliveryMethodNotificationTemplate).setSubject(subject); + } + config.getDeliveryMethodsTemplates().put(deliveryMethod, deliveryMethodNotificationTemplate); + } + notificationTemplate.setConfiguration(config); + return saveNotificationTemplate(notificationTemplate); + } + + protected NotificationTemplate saveNotificationTemplate(NotificationTemplate notificationTemplate) { + return doPost("/api/notification/template", notificationTemplate, NotificationTemplate.class); + } + + protected List getMyNotifications(boolean unreadOnly, int limit) throws Exception { + return getMyNotifications(NotificationDeliveryMethod.WEB, unreadOnly, limit); + } + + protected List getMyNotifications(NotificationDeliveryMethod deliveryMethod, boolean unreadOnly, int limit) throws Exception { + return doGetTypedWithPageLink("/api/notifications?unreadOnly={unreadOnly}&deliveryMethod={deliveryMethod}&", new TypeReference>() {}, + new PageLink(limit, 0), unreadOnly, deliveryMethod).getData(); + } + } diff --git a/application/src/test/java/org/thingsboard/server/edge/NotificationEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/NotificationEdgeTest.java index 6d4ebb3676..3dab0a369a 100644 --- a/application/src/test/java/org/thingsboard/server/edge/NotificationEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/NotificationEdgeTest.java @@ -274,14 +274,6 @@ public class NotificationEdgeTest extends AbstractEdgeTest { return saveNotificationRule(notificationRule); } - private NotificationTemplate saveNotificationTemplate(NotificationTemplate notificationTemplate) { - return doPost("/api/notification/template", notificationTemplate, NotificationTemplate.class); - } - - private NotificationTarget saveNotificationTarget(NotificationTarget notificationTarget) { - return doPost("/api/notification/target", notificationTarget, NotificationTarget.class); - } - private NotificationRule saveNotificationRule(NotificationRule notificationRule) { return doPost("/api/notification/rule", notificationRule, NotificationRule.class); } diff --git a/application/src/test/java/org/thingsboard/server/service/notification/AbstractNotificationApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/AbstractNotificationApiTest.java index bbe31f3e94..fcdf82bd52 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/AbstractNotificationApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/AbstractNotificationApiTest.java @@ -30,8 +30,6 @@ import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.id.NotificationTemplateId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UUIDBased; -import org.thingsboard.server.common.data.id.UserId; -import org.thingsboard.server.common.data.notification.Notification; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; import org.thingsboard.server.common.data.notification.NotificationRequest; import org.thingsboard.server.common.data.notification.NotificationRequestConfig; @@ -44,18 +42,7 @@ import org.thingsboard.server.common.data.notification.rule.NotificationRuleInfo import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.settings.NotificationDeliveryMethodConfig; 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.PlatformUsersNotificationTargetConfig; -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.template.DeliveryMethodNotificationTemplate; -import org.thingsboard.server.common.data.notification.template.EmailDeliveryMethodNotificationTemplate; -import org.thingsboard.server.common.data.notification.template.HasSubject; -import org.thingsboard.server.common.data.notification.template.MobileAppDeliveryMethodNotificationTemplate; import org.thingsboard.server.common.data.notification.template.NotificationTemplate; -import org.thingsboard.server.common.data.notification.template.NotificationTemplateConfig; -import org.thingsboard.server.common.data.notification.template.SmsDeliveryMethodNotificationTemplate; -import org.thingsboard.server.common.data.notification.template.WebDeliveryMethodNotificationTemplate; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; @@ -71,7 +58,6 @@ import org.thingsboard.server.dao.sqlts.insert.sql.SqlPartitioningRepository; import java.net.URISyntaxException; import java.util.Arrays; -import java.util.HashMap; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -120,25 +106,6 @@ public abstract class AbstractNotificationApiTest extends AbstractControllerTest notificationSettingsService.deleteNotificationSettings(TenantId.SYS_TENANT_ID); } - protected NotificationTarget createNotificationTarget(UserId... usersIds) { - UserListFilter filter = new UserListFilter(); - filter.setUsersIds(DaoUtil.toUUIDs(List.of(usersIds))); - return createNotificationTarget(filter); - } - - protected NotificationTarget createNotificationTarget(UsersFilter usersFilter) { - NotificationTarget notificationTarget = new NotificationTarget(); - notificationTarget.setName(usersFilter.toString()); - PlatformUsersNotificationTargetConfig targetConfig = new PlatformUsersNotificationTargetConfig(); - targetConfig.setUsersFilter(usersFilter); - notificationTarget.setConfiguration(targetConfig); - return saveNotificationTarget(notificationTarget); - } - - protected NotificationTarget saveNotificationTarget(NotificationTarget notificationTarget) { - return doPost("/api/notification/target", notificationTarget, NotificationTarget.class); - } - protected NotificationRequest submitNotificationRequest(NotificationTargetId targetId, String text, NotificationDeliveryMethod... deliveryMethods) { return submitNotificationRequest(targetId, text, 0, deliveryMethods); } @@ -178,50 +145,6 @@ public abstract class AbstractNotificationApiTest extends AbstractControllerTest return findNotificationRequest(notificationRequestId).getStats(); } - protected NotificationTemplate createNotificationTemplate(NotificationType notificationType, String subject, - String text, NotificationDeliveryMethod... deliveryMethods) { - NotificationTemplate notificationTemplate = new NotificationTemplate(); - notificationTemplate.setTenantId(tenantId); - notificationTemplate.setName("Notification template: " + text); - notificationTemplate.setNotificationType(notificationType); - NotificationTemplateConfig config = new NotificationTemplateConfig(); - config.setDeliveryMethodsTemplates(new HashMap<>()); - for (NotificationDeliveryMethod deliveryMethod : deliveryMethods) { - DeliveryMethodNotificationTemplate deliveryMethodNotificationTemplate; - switch (deliveryMethod) { - case WEB: { - deliveryMethodNotificationTemplate = new WebDeliveryMethodNotificationTemplate(); - break; - } - case EMAIL: { - deliveryMethodNotificationTemplate = new EmailDeliveryMethodNotificationTemplate(); - break; - } - case SMS: { - deliveryMethodNotificationTemplate = new SmsDeliveryMethodNotificationTemplate(); - break; - } - case MOBILE_APP: - deliveryMethodNotificationTemplate = new MobileAppDeliveryMethodNotificationTemplate(); - break; - default: - throw new IllegalArgumentException("Unsupported delivery method " + deliveryMethod); - } - deliveryMethodNotificationTemplate.setEnabled(true); - deliveryMethodNotificationTemplate.setBody(text); - if (deliveryMethodNotificationTemplate instanceof HasSubject) { - ((HasSubject) deliveryMethodNotificationTemplate).setSubject(subject); - } - config.getDeliveryMethodsTemplates().put(deliveryMethod, deliveryMethodNotificationTemplate); - } - notificationTemplate.setConfiguration(config); - return saveNotificationTemplate(notificationTemplate); - } - - protected NotificationTemplate saveNotificationTemplate(NotificationTemplate notificationTemplate) { - return doPost("/api/notification/template", notificationTemplate, NotificationTemplate.class); - } - protected void saveNotificationSettings(NotificationSettings notificationSettings) throws Exception { doPost("/api/notification/settings", notificationSettings).andExpect(status().isOk()); } @@ -258,15 +181,6 @@ public abstract class AbstractNotificationApiTest extends AbstractControllerTest doDelete("/api/notification/request/" + id); } - protected List getMyNotifications(boolean unreadOnly, int limit) throws Exception { - return getMyNotifications(NotificationDeliveryMethod.WEB, unreadOnly, limit); - } - - protected List getMyNotifications(NotificationDeliveryMethod deliveryMethod, boolean unreadOnly, int limit) throws Exception { - return doGetTypedWithPageLink("/api/notifications?unreadOnly={unreadOnly}&deliveryMethod={deliveryMethod}&", new TypeReference>() {}, - new PageLink(limit, 0), unreadOnly, deliveryMethod).getData(); - } - protected NotificationRule createNotificationRule(NotificationRuleTriggerConfig triggerConfig, String subject, String text, NotificationTargetId... targets) { return createNotificationRule(triggerConfig, subject, text, List.of(targets), NotificationDeliveryMethod.WEB); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationSettingsService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationSettingsService.java index 9221dcb2cf..09443e8c63 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationSettingsService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationSettingsService.java @@ -15,11 +15,15 @@ */ package org.thingsboard.server.dao.notification; +import com.fasterxml.jackson.databind.JsonNode; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.notification.NotificationType; import org.thingsboard.server.common.data.notification.settings.NotificationSettings; import org.thingsboard.server.common.data.notification.settings.UserNotificationSettings; +import java.util.Map; + public interface NotificationSettingsService { void saveNotificationSettings(TenantId tenantId, NotificationSettings settings); @@ -36,4 +40,6 @@ public interface NotificationSettingsService { void updateDefaultNotificationConfigs(TenantId tenantId); + void moveMailTemplatesToNotificationCenter(TenantId tenantId, JsonNode mailTemplates, Map mailTemplatesNames); + } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTemplateService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTemplateService.java index 664f976904..d41295f907 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTemplateService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTemplateService.java @@ -22,7 +22,9 @@ import org.thingsboard.server.common.data.notification.template.NotificationTemp import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import java.util.Collection; import java.util.List; +import java.util.Optional; public interface NotificationTemplateService { @@ -32,7 +34,11 @@ public interface NotificationTemplateService { PageData findNotificationTemplatesByTenantIdAndNotificationTypes(TenantId tenantId, List notificationTypes, PageLink pageLink); - int countNotificationTemplatesByTenantIdAndNotificationTypes(TenantId tenantId, List notificationTypes); + Optional findTenantOrSystemNotificationTemplate(TenantId tenantId, NotificationType notificationType); + + Optional findNotificationTemplateByTenantIdAndType(TenantId tenantId, NotificationType notificationType); + + int countNotificationTemplatesByTenantIdAndNotificationTypes(TenantId tenantId, Collection notificationTypes); void deleteNotificationTemplateById(TenantId tenantId, NotificationTemplateId id); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationType.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationType.java index ec66a3d53a..1ddc17f36a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationType.java @@ -15,6 +15,12 @@ */ package org.thingsboard.server.common.data.notification; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@AllArgsConstructor +@NoArgsConstructor public enum NotificationType { GENERAL, @@ -31,6 +37,9 @@ public enum NotificationType { RATE_LIMITS, EDGE_CONNECTION, EDGE_COMMUNICATION_FAILURE, - TASK_PROCESSING_FAILURE + TASK_PROCESSING_FAILURE; + + @Getter + private boolean system; // for future use and compatibility with PE } 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 10438c484a..1404eaff67 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 @@ -15,8 +15,11 @@ */ package org.thingsboard.server.dao.notification; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; @@ -41,7 +44,9 @@ import org.thingsboard.server.common.data.notification.targets.platform.SystemAd import org.thingsboard.server.common.data.notification.targets.platform.TenantAdministratorsFilter; 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.notification.template.EmailDeliveryMethodNotificationTemplate; import org.thingsboard.server.common.data.notification.template.NotificationTemplate; +import org.thingsboard.server.common.data.notification.template.NotificationTemplateConfig; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.settings.UserSettings; import org.thingsboard.server.common.data.settings.UserSettingsType; @@ -249,6 +254,52 @@ public class DefaultNotificationSettingsService implements NotificationSettingsS } } + @Override + public void moveMailTemplatesToNotificationCenter(TenantId tenantId, JsonNode mailTemplates, Map mailTemplatesNames) { + mailTemplatesNames.forEach((mailTemplateName, notificationType) -> { + moveMailTemplateToNotificationCenter(tenantId, mailTemplates, mailTemplateName, notificationType); + }); + } + + private void moveMailTemplateToNotificationCenter(TenantId tenantId, JsonNode mailTemplates, String mailTemplateName, NotificationType notificationType) { + JsonNode mailTemplate = mailTemplates.get(mailTemplateName); + if (mailTemplate == null || mailTemplate.isNull() || !mailTemplate.has("subject") || !mailTemplate.has("body")) { + return; + } + + String subject = mailTemplate.get("subject").asText(); + String body = mailTemplate.get("body").asText(); + body = body.replace("targetEmail", "recipientEmail"); + + NotificationTemplate notificationTemplate = null; + if (tenantId.isSysTenantId()) { + // updating system notification template, not touching tenants' templates + notificationTemplate = notificationTemplateService.findNotificationTemplateByTenantIdAndType(tenantId, notificationType).orElse(null); + } + if (notificationTemplate == null) { + log.debug("[{}] Creating {} template", tenantId, notificationType); + notificationTemplate = new NotificationTemplate(); + } else { + log.debug("[{}] Updating {} template", tenantId, notificationType); + } + String name = StringUtils.capitalize(notificationType.name().toLowerCase().replaceAll("_", " ")) + " notification"; + notificationTemplate.setName(name); + notificationTemplate.setTenantId(tenantId); + notificationTemplate.setNotificationType(notificationType); + NotificationTemplateConfig notificationTemplateConfig = new NotificationTemplateConfig(); + + EmailDeliveryMethodNotificationTemplate emailNotificationTemplate = new EmailDeliveryMethodNotificationTemplate(); + emailNotificationTemplate.setEnabled(true); + emailNotificationTemplate.setSubject(subject); + emailNotificationTemplate.setBody(body); + + notificationTemplateConfig.setDeliveryMethodsTemplates(Map.of(NotificationDeliveryMethod.EMAIL, emailNotificationTemplate)); + notificationTemplate.setConfiguration(notificationTemplateConfig); + notificationTemplateService.saveNotificationTemplate(tenantId, notificationTemplate); + + ((ObjectNode) mailTemplates).remove(mailTemplateName); + } + private boolean isNotificationConfigured(TenantId tenantId, NotificationType... notificationTypes) { return notificationTemplateService.countNotificationTemplatesByTenantIdAndNotificationTypes(tenantId, List.of(notificationTypes)) > 0; } 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 a6ab6e08ff..9b5b9ca85b 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 @@ -109,7 +109,7 @@ public class DefaultNotificationTargetService extends AbstractEntityService impl NotificationTarget notificationTarget = findNotificationTargetById(tenantId, targetId); Objects.requireNonNull(notificationTarget, "Notification target [" + targetId + "] not found"); NotificationTargetConfig configuration = notificationTarget.getConfiguration(); - return findRecipientsForNotificationTargetConfig(tenantId, (PlatformUsersNotificationTargetConfig) configuration, pageLink); + return findRecipientsForNotificationTargetConfig(notificationTarget.getTenantId(), (PlatformUsersNotificationTargetConfig) configuration, pageLink); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTemplateService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTemplateService.java index 14b9d176df..ed2a8d0c91 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTemplateService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTemplateService.java @@ -32,6 +32,7 @@ import org.thingsboard.server.dao.entity.EntityDaoService; import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent; import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Optional; @@ -50,11 +51,19 @@ public class DefaultNotificationTemplateService extends AbstractEntityService im @Override public NotificationTemplate saveNotificationTemplate(TenantId tenantId, NotificationTemplate notificationTemplate) { + NotificationType notificationType = notificationTemplate.getNotificationType(); if (notificationTemplate.getId() != null) { NotificationTemplate oldNotificationTemplate = findNotificationTemplateById(tenantId, notificationTemplate.getId()); - if (notificationTemplate.getNotificationType() != oldNotificationTemplate.getNotificationType()) { + if (notificationType != oldNotificationTemplate.getNotificationType()) { throw new IllegalArgumentException("Notification type cannot be updated"); } + } else { + if (notificationType.isSystem()) { + int systemTemplatesCount = countNotificationTemplatesByTenantIdAndNotificationTypes(tenantId, List.of(notificationType)); + if (systemTemplatesCount > 0) { + throw new IllegalArgumentException("There can only be one notification template of this type"); + } + } } try { NotificationTemplate savedTemplate = notificationTemplateDao.saveAndFlush(tenantId, notificationTemplate); @@ -75,7 +84,19 @@ public class DefaultNotificationTemplateService extends AbstractEntityService im } @Override - public int countNotificationTemplatesByTenantIdAndNotificationTypes(TenantId tenantId, List notificationTypes) { + public Optional findTenantOrSystemNotificationTemplate(TenantId tenantId, NotificationType notificationType) { + return findNotificationTemplateByTenantIdAndType(tenantId, notificationType) + .or(() -> findNotificationTemplateByTenantIdAndType(TenantId.SYS_TENANT_ID, notificationType)); + } + + @Override + public Optional findNotificationTemplateByTenantIdAndType(TenantId tenantId, NotificationType notificationType) { + return findNotificationTemplatesByTenantIdAndNotificationTypes(tenantId, List.of(notificationType), new PageLink(1)).getData() + .stream().findFirst(); + } + + @Override + public int countNotificationTemplatesByTenantIdAndNotificationTypes(TenantId tenantId, Collection notificationTypes) { return notificationTemplateDao.countByTenantIdAndNotificationTypes(tenantId, notificationTypes); } @@ -86,8 +107,16 @@ public class DefaultNotificationTemplateService extends AbstractEntityService im @Override public void deleteEntity(TenantId tenantId, EntityId id, boolean force) { - if (!force && notificationRequestDao.existsByTenantIdAndStatusAndTemplateId(tenantId, NotificationRequestStatus.SCHEDULED, (NotificationTemplateId) id)) { - throw new IllegalArgumentException("Notification template is referenced by scheduled notification request"); + if (!force) { + if (notificationRequestDao.existsByTenantIdAndStatusAndTemplateId(tenantId, NotificationRequestStatus.SCHEDULED, (NotificationTemplateId) id)) { + throw new IllegalArgumentException("Notification template is referenced by scheduled notification request"); + } + if (tenantId.isSysTenantId()) { + NotificationTemplate notificationTemplate = findNotificationTemplateById(tenantId, (NotificationTemplateId) id); + if (notificationTemplate.getNotificationType().isSystem()) { + throw new IllegalArgumentException("System notification template cannot be deleted"); + } + } } try { notificationTemplateDao.removeById(tenantId, id.getId()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTemplateDao.java b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTemplateDao.java index 136bc4d472..d3f79f0ed2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTemplateDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTemplateDao.java @@ -24,13 +24,14 @@ import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.ExportableEntityDao; +import java.util.Collection; import java.util.List; public interface NotificationTemplateDao extends Dao, ExportableEntityDao { PageData findByTenantIdAndNotificationTypesAndPageLink(TenantId tenantId, List notificationTypes, PageLink pageLink); - int countByTenantIdAndNotificationTypes(TenantId tenantId, List notificationTypes); + int countByTenantIdAndNotificationTypes(TenantId tenantId, Collection notificationTypes); void removeByTenantId(TenantId tenantId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTemplateDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTemplateDao.java index f0ca725951..ae46efb2f7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTemplateDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTemplateDao.java @@ -31,6 +31,7 @@ import org.thingsboard.server.dao.notification.NotificationTemplateDao; import org.thingsboard.server.dao.sql.JpaAbstractDao; import org.thingsboard.server.dao.util.SqlDao; +import java.util.Collection; import java.util.List; import java.util.UUID; @@ -53,7 +54,7 @@ public class JpaNotificationTemplateDao extends JpaAbstractDao notificationTypes) { + public int countByTenantIdAndNotificationTypes(TenantId tenantId, Collection notificationTypes) { return notificationTemplateRepository.countByTenantIdAndNotificationTypes(tenantId.getId(), notificationTypes); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTemplateRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTemplateRepository.java index bb45c10d06..bffdf98d43 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTemplateRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTemplateRepository.java @@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.notification.NotificationType; import org.thingsboard.server.dao.ExportableEntityRepository; import org.thingsboard.server.dao.model.sql.NotificationTemplateEntity; +import java.util.Collection; import java.util.List; import java.util.UUID; @@ -45,7 +46,7 @@ public interface NotificationTemplateRepository extends JpaRepository notificationTypes); + @Param("notificationTypes") Collection notificationTypes); @Transactional @Modifying 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 ce2ff08734..02ee53f3ad 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 @@ -18,12 +18,15 @@ package org.thingsboard.rule.engine.api; import com.google.common.util.concurrent.FutureCallback; import org.thingsboard.server.common.data.id.NotificationId; import org.thingsboard.server.common.data.id.NotificationRequestId; +import org.thingsboard.server.common.data.id.NotificationTargetId; 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 org.thingsboard.server.common.data.notification.NotificationRequestStats; +import org.thingsboard.server.common.data.notification.NotificationType; import org.thingsboard.server.common.data.notification.info.GeneralNotificationInfo; +import org.thingsboard.server.common.data.notification.info.NotificationInfo; import org.thingsboard.server.common.data.notification.targets.platform.UsersFilter; import org.thingsboard.server.common.data.notification.template.NotificationTemplate; @@ -35,6 +38,8 @@ public interface NotificationCenter { void sendGeneralWebNotification(TenantId tenantId, UsersFilter recipients, NotificationTemplate template, GeneralNotificationInfo info); + void sendSystemNotification(TenantId tenantId, NotificationTargetId targetId, NotificationType type, NotificationInfo info); // for future use and compatibility with PE + void deleteNotificationRequest(TenantId tenantId, NotificationRequestId notificationRequestId); void markNotificationAsRead(TenantId tenantId, UserId recipientId, NotificationId notificationId);