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 c26e037b65..b5238d675b 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 @@ -35,6 +35,10 @@ import org.thingsboard.server.common.data.notification.NotificationRequestConfig import org.thingsboard.server.common.data.notification.NotificationRequestInfo; import org.thingsboard.server.common.data.notification.NotificationRequestStats; import org.thingsboard.server.common.data.notification.NotificationType; +import org.thingsboard.server.common.data.notification.rule.DefaultNotificationRuleRecipientsConfig; +import org.thingsboard.server.common.data.notification.rule.NotificationRule; +import org.thingsboard.server.common.data.notification.rule.NotificationRuleInfo; +import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerConfig; 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; @@ -52,6 +56,7 @@ import org.thingsboard.server.controller.AbstractControllerTest; import org.thingsboard.server.dao.DaoUtil; import java.net.URISyntaxException; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.concurrent.TimeUnit; @@ -195,6 +200,32 @@ public abstract class AbstractNotificationApiTest extends AbstractControllerTest new PageLink(limit, 0), unreadOnly).getData(); } + protected NotificationRule createNotificationRule(NotificationRuleTriggerConfig triggerConfig, String subject, String text, NotificationTargetId... targets) { + NotificationTemplate template = createNotificationTemplate(NotificationType.valueOf(triggerConfig.getTriggerType().toString()), subject, text, NotificationDeliveryMethod.WEB); + + NotificationRule rule = new NotificationRule(); + rule.setName(triggerConfig.getTriggerType() + " [" + Arrays.toString(targets) + "]"); + rule.setTemplateId(template.getId()); + rule.setTriggerType(triggerConfig.getTriggerType()); + rule.setTriggerConfig(triggerConfig); + + DefaultNotificationRuleRecipientsConfig recipientsConfig = new DefaultNotificationRuleRecipientsConfig(); + recipientsConfig.setTriggerType(triggerConfig.getTriggerType()); + recipientsConfig.setTargets(DaoUtil.toUUIDs(List.of(targets))); + rule.setRecipientsConfig(recipientsConfig); + + return saveNotificationRule(rule); + } + + protected NotificationRule saveNotificationRule(NotificationRule notificationRule) { + return doPost("/api/notification/rule", notificationRule, NotificationRule.class); + } + + protected PageData findNotificationRules() throws Exception { + PageLink pageLink = new PageLink(10); + return doGetTypedWithPageLink("/api/notification/rules?", new TypeReference>() {}, pageLink); + } + @Override protected NotificationApiWsClient buildAndConnectWebSocketClient() throws URISyntaxException, InterruptedException { NotificationApiWsClient wsClient = new NotificationApiWsClient(WS_URL + wsPort, token); diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java index 4b43ddaf65..998f00f8e2 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.service.notification; -import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.BooleanNode; import org.junit.Before; @@ -43,7 +42,6 @@ import org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType; import org.thingsboard.server.common.data.device.profile.AlarmRule; import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; import org.thingsboard.server.common.data.device.profile.SimpleAlarmConditionSpec; -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; @@ -59,7 +57,6 @@ import org.thingsboard.server.common.data.notification.rule.trigger.AlarmNotific import org.thingsboard.server.common.data.notification.rule.trigger.AlarmNotificationRuleTriggerConfig.AlarmAction; 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.targets.NotificationTarget; import org.thingsboard.server.common.data.notification.template.NotificationTemplate; @@ -73,7 +70,6 @@ import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; -import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.notification.NotificationRequestService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.service.DaoSqlTest; @@ -84,7 +80,6 @@ import org.thingsboard.server.service.entitiy.tenant.profile.TbTenantProfileServ import org.thingsboard.server.service.telemetry.AlarmSubscriptionService; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -474,21 +469,8 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { }, (notification, r) -> check.accept(notification)); } - private NotificationRule createNotificationRule(NotificationRuleTriggerConfig triggerConfig, String subject, String text, NotificationTargetId... targets) { - NotificationTemplate template = createNotificationTemplate(NotificationType.valueOf(triggerConfig.getTriggerType().toString()), subject, text, NotificationDeliveryMethod.WEB); - - NotificationRule rule = new NotificationRule(); - rule.setName(triggerConfig.getTriggerType() + " [" + Arrays.toString(targets) + "]"); - rule.setTemplateId(template.getId()); - rule.setTriggerType(triggerConfig.getTriggerType()); - rule.setTriggerConfig(triggerConfig); - - DefaultNotificationRuleRecipientsConfig recipientsConfig = new DefaultNotificationRuleRecipientsConfig(); - recipientsConfig.setTriggerType(triggerConfig.getTriggerType()); - recipientsConfig.setTargets(DaoUtil.toUUIDs(List.of(targets))); - rule.setRecipientsConfig(recipientsConfig); - - return saveNotificationRule(rule); + private PageData findNotificationRequests(EntityType originatorType) { + return notificationRequestService.findNotificationRequestsInfosByTenantIdAndOriginatorType(tenantId, originatorType, new PageLink(100)); } private DeviceProfile createDeviceProfileWithAlarmRules(String alarmType) { @@ -526,17 +508,4 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { return deviceProfile; } - private NotificationRule saveNotificationRule(NotificationRule notificationRule) { - return doPost("/api/notification/rule", notificationRule, NotificationRule.class); - } - - private PageData findNotificationRules() throws Exception { - PageLink pageLink = new PageLink(10); - return doGetTypedWithPageLink("/api/notification/rules?", new TypeReference>() {}, pageLink); - } - - private PageData findNotificationRequests(EntityType originatorType) { - return notificationRequestService.findNotificationRequestsInfosByTenantIdAndOriginatorType(tenantId, originatorType, new PageLink(100)); - } - } diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationTargetApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationTargetApiTest.java index 61e0531a39..480bbf939b 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationTargetApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationTargetApiTest.java @@ -23,6 +23,8 @@ import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.ResultMatcher; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; +import org.thingsboard.server.common.data.notification.rule.trigger.EntityActionNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; import org.thingsboard.server.common.data.notification.targets.platform.AllUsersFilter; import org.thingsboard.server.common.data.notification.targets.platform.CustomerUsersFilter; @@ -30,7 +32,6 @@ import org.thingsboard.server.common.data.notification.targets.platform.Platform import org.thingsboard.server.common.data.notification.targets.platform.UserListFilter; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.controller.AbstractControllerTest; import org.thingsboard.server.dao.notification.NotificationTargetDao; import org.thingsboard.server.dao.service.DaoSqlTest; @@ -41,7 +42,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @DaoSqlTest -public class NotificationTargetApiTest extends AbstractControllerTest { +public class NotificationTargetApiTest extends AbstractNotificationApiTest { @Autowired private NotificationTargetDao notificationTargetDao; @@ -148,6 +149,26 @@ public class NotificationTargetApiTest extends AbstractControllerTest { assertThat(notificationTargetDao.findByTenantIdAndPageLink(differentTenantId, new PageLink(10)).getData()).isEmpty(); } + @Test + public void whenDeletingTargetUsedByRule_thenReturnError() throws Exception { + NotificationTarget target = createNotificationTarget(tenantAdminUserId); + createNotificationRule(new EntityActionNotificationRuleTriggerConfig(), "Test", "Test", target.getId()); + + String error = getErrorMessage(doDelete("/api/notification/target/" + target.getId()) + .andExpect(status().isBadRequest())); + assertThat(error).containsIgnoringCase("used in notification rule"); + } + + @Test + public void whenDeletingTargetUsedByScheduledNotificationRequest_thenReturnError() throws Exception { + NotificationTarget target = createNotificationTarget(tenantAdminUserId); + submitNotificationRequest(target.getId(), "Test", 100, NotificationDeliveryMethod.WEB); + + String error = getErrorMessage(doDelete("/api/notification/target/" + target.getId()) + .andExpect(status().isBadRequest())); + assertThat(error).containsIgnoringCase("referenced by scheduled notification request"); + } + private String saveAndGetError(NotificationTarget notificationTarget, ResultMatcher statusMatcher) throws Exception { return getErrorMessage(save(notificationTarget, statusMatcher)); } 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 9ad71fe7e1..b0a2306408 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 @@ -67,7 +67,7 @@ public class DefaultNotificationTargetService extends AbstractEntityService impl return notificationTargetDao.saveAndFlush(tenantId, notificationTarget); } catch (Exception e) { checkConstraintViolation(e, Map.of( - "uq_notification_target_name", "Notification target with such name already exists" + "uq_notification_target_name", "Recipients group with such name already exists" )); throw e; } @@ -109,7 +109,7 @@ public class DefaultNotificationTargetService extends AbstractEntityService impl List users = ((UserListFilter) usersFilter).getUsersIds().stream() .limit(pageLink.getPageSize()) .map(UserId::new).map(userId -> userService.findUserById(tenantId, userId)) - .collect(Collectors.toList()); + .filter(Objects::nonNull).collect(Collectors.toList()); return new PageData<>(users, 1, users.size(), false); } case CUSTOMER_USERS: { @@ -179,10 +179,10 @@ public class DefaultNotificationTargetService extends AbstractEntityService impl @Override public void deleteNotificationTargetById(TenantId tenantId, NotificationTargetId id) { if (notificationRequestDao.existsByTenantIdAndStatusAndTargetId(tenantId, NotificationRequestStatus.SCHEDULED, id)) { - throw new IllegalArgumentException("Notification target is referenced by scheduled notification request"); + throw new IllegalArgumentException("Recipients group is referenced by scheduled notification request"); } if (notificationRuleDao.existsByTenantIdAndTargetId(tenantId, id)) { - throw new IllegalArgumentException("Notification target is being used in notification rule"); + throw new IllegalArgumentException("Recipients group is being used in notification rule"); } notificationTargetDao.removeById(tenantId, id.getId()); } 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 145905593f..32c6d5388e 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 @@ -22,7 +22,6 @@ import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; -import org.thingsboard.server.common.data.notification.rule.NotificationRuleInfo; import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; import org.thingsboard.server.dao.model.sql.NotificationRuleEntity; import org.thingsboard.server.dao.model.sql.NotificationRuleInfoEntity; @@ -42,7 +41,10 @@ public interface NotificationRuleRepository extends JpaRepository 0 FROM NotificationRuleEntity r WHERE r.tenantId = :tenantId " + + "AND CAST(r.recipientsConfig AS text) LIKE concat('%', :searchString, '%')") + boolean existsByTenantIdAndRecipientsConfigContaining(@Param("tenantId") UUID tenantId, + @Param("searchString") String searchString); List findAllByTenantIdAndTriggerType(UUID tenantId, NotificationRuleTriggerType triggerType);