From 79670d9f9dc74c8a2ef33b3170f3e9ed4a6c8bc8 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Tue, 28 Feb 2023 19:19:59 +0200 Subject: [PATCH] Notification system improvements --- .../NotificationTargetController.java | 9 ---- .../DefaultNotificationCenter.java | 13 ++--- .../channels/SlackNotificationChannel.java | 6 --- .../channels/SmsNotificationChannel.java | 3 +- ...aultNotificationRuleProcessingService.java | 4 +- .../service/slack/DefaultSlackService.java | 6 +-- .../service/ws/DefaultWebSocketService.java | 6 ++- .../src/main/resources/thingsboard.yml | 3 ++ .../notification/NotificationApiTest.java | 33 ++++-------- .../notification/NotificationRuleApiTest.java | 1 - .../NotificationRequestService.java | 2 +- .../server/common/data/CacheConstants.java | 1 + .../server/common/data/alarm/Alarm.java | 1 - .../device/profile/DeviceProfileAlarm.java | 3 -- .../NotificationRequestStats.java | 5 -- .../notification/rule/NotificationRule.java | 2 +- .../template/NotificationTemplate.java | 2 +- .../dao/model/sql/AbstractAlarmEntity.java | 1 - .../DefaultNotificationRequestService.java | 53 ++++++++++++++++--- .../DefaultNotificationRuleService.java | 17 +++++- .../DefaultNotificationSettingsService.java | 6 +++ .../DefaultNotificationTargetService.java | 21 ++++++-- .../DefaultNotificationTemplateService.java | 19 ++++++- .../notification/NotificationRequestDao.java | 4 +- .../dao/notification/NotificationRuleDao.java | 2 +- .../cache/NotificationRequestCacheKey.java | 43 +++++++++++++++ .../cache/NotificationRequestCacheValue.java | 38 +++++++++++++ .../NotificationRequestCaffeineCache.java | 32 +++++++++++ .../cache/NotificationRequestRedisCache.java | 35 ++++++++++++ .../cache/NotificationRuleCaffeineCache.java | 2 +- .../cache/NotificationRuleRedisCache.java | 2 +- .../JpaNotificationRequestDao.java | 10 ++-- .../notification/JpaNotificationRuleDao.java | 6 +-- .../NotificationRequestRepository.java | 4 +- .../NotificationRuleRepository.java | 2 +- .../notification/TbNotificationNode.java | 4 +- .../rule/engine/notification/TbSlackNode.java | 1 + .../app/shared/decorators/coerce-boolean.ts | 1 + 38 files changed, 308 insertions(+), 95 deletions(-) create mode 100644 dao/src/main/java/org/thingsboard/server/dao/notification/cache/NotificationRequestCacheKey.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/notification/cache/NotificationRequestCacheValue.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/notification/cache/NotificationRequestCaffeineCache.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/notification/cache/NotificationRequestRedisCache.java 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 abc78c8efd..b07cff0849 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java @@ -68,15 +68,6 @@ public class NotificationTargetController extends BaseController { @ApiOperation(value = "Save notification target (saveNotificationTarget)", notes = "Create or update notification target.\n\n" + -// "Examples with different configuration types:\n" + -// "- USER_LIST:\n" + -// "```\n{\n \"name\": \"Special users\",\n \"configuration\": {\n \"type\": \"USER_LIST\",\n \"usersIds\": [\n \"ea31a460-3d85-11ed-9200-77fc04fa14fa\",\n \"86f7b260-3d88-11ed-ad72-ad2ee0f70ba1\"\n ]\n }\n}\n```\n" + -// "- CUSTOMER_USERS (not accessible to system administrator):\n" + -// "```\n{\n \"name\": \"Users of my customer\",\n \"configuration\": {\n \"type\": \"CUSTOMER_USERS\",\n \"customerId\": \"ea31a460-3d85-11ed-9200-77fc04fa14fa\"\n }\n}\n```\n" + -// "or if you would like to use the target in notification rule and get customerId from alarm:\n" + -// "```\n{\n \"name\": \"Alarm's customer users\",\n \"configuration\": {\n \"type\": \"CUSTOMER_USERS\",\n \"customerId\": null,\n \"getCustomerIdFromOriginatorEntity\": true\n }\n}\n```\n" + -// "- ALL_USERS:\n" + -// "```\n{\n \"name\": \"All my users\",\n \"configuration\": {\n \"type\": \"ALL_USERS\"\n }\n}\n```\n\n" + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PostMapping("/target") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") 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 9b1820331c..0f165fb0d8 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 @@ -172,7 +172,7 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple } else { int failures = stats.getErrors().values().stream().mapToInt(Map::size).sum(); sendBasicNotification(tenantId, senderId, "Notification failure", - "Some notifications were not sent (" + failures + ")"); // TODO: 'Go to request' button + "Some notifications were not sent (" + failures + ")"); // TODO: 'Go to' button } } }, dbCallbackExecutorService); @@ -333,10 +333,10 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple public NotificationRequest updateNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest) { log.debug("Updating notification request {}", notificationRequest.getId()); notificationRequest = notificationRequestService.saveNotificationRequest(tenantId, notificationRequest); - // marking related notifications as unread: FIXME: causes each subscription to fetch notifications on each request update + // marking related notifications as unread: TODO: causes each subscription to fetch notifications on each request update notificationService.updateNotificationsStatusByRequestId(tenantId, notificationRequest.getId(), NotificationStatus.SENT); - // TODO: no need to update request with other than PLATFORM_USERS target type + // TODO: no need to send request update for other than PLATFORM_USERS target type onNotificationRequestUpdate(tenantId, NotificationRequestUpdate.builder() .notificationRequestId(notificationRequest.getId()) .notificationInfo(notificationRequest.getInfo()) @@ -348,9 +348,10 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple @Override public void deleteNotificationRequest(TenantId tenantId, NotificationRequestId notificationRequestId) { log.debug("Deleting notification request {}", notificationRequestId); - NotificationRequest notificationRequest = notificationRequestService.findNotificationRequestById(tenantId, notificationRequestId);// TODO: add caching - notificationRequestService.deleteNotificationRequestById(tenantId, notificationRequestId); - // todo: check delivery method ? + NotificationRequest notificationRequest = notificationRequestService.findNotificationRequestById(tenantId, notificationRequestId); + notificationRequestService.deleteNotificationRequest(tenantId, notificationRequest); + + // TODO: no need to send request update for other than PLATFORM_USERS target type if (notificationRequest.isSent()) { onNotificationRequestUpdate(tenantId, NotificationRequestUpdate.builder() .notificationRequestId(notificationRequestId) diff --git a/application/src/main/java/org/thingsboard/server/service/notification/channels/SlackNotificationChannel.java b/application/src/main/java/org/thingsboard/server/service/notification/channels/SlackNotificationChannel.java index 735c5744c6..2c9d948fb8 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/channels/SlackNotificationChannel.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/channels/SlackNotificationChannel.java @@ -15,12 +15,10 @@ */ package org.thingsboard.server.service.notification.channels; -import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; import org.thingsboard.rule.engine.api.slack.SlackService; -import org.thingsboard.server.common.data.notification.AlreadySentException; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; import org.thingsboard.server.common.data.notification.NotificationProcessingContext; import org.thingsboard.server.common.data.notification.settings.SlackNotificationDeliveryMethodConfig; @@ -37,10 +35,6 @@ public class SlackNotificationChannel implements NotificationChannel sendNotification(SlackConversation conversation, SlackDeliveryMethodNotificationTemplate processedTemplate, NotificationProcessingContext ctx) { - if (ctx.getStats().contains(NotificationDeliveryMethod.SLACK)) { - return Futures.immediateFailedFuture(new AlreadySentException()); - } - SlackNotificationDeliveryMethodConfig config = ctx.getDeliveryMethodConfig(NotificationDeliveryMethod.SLACK); return executor.submit(() -> { slackService.sendMessage(ctx.getTenantId(), config.getBotToken(), conversation.getId(), processedTemplate.getBody()); diff --git a/application/src/main/java/org/thingsboard/server/service/notification/channels/SmsNotificationChannel.java b/application/src/main/java/org/thingsboard/server/service/notification/channels/SmsNotificationChannel.java index 5f12ea85d4..5d97d73a90 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/channels/SmsNotificationChannel.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/channels/SmsNotificationChannel.java @@ -37,8 +37,9 @@ public class SmsNotificationChannel implements NotificationChannel sendNotification(User recipient, SmsDeliveryMethodNotificationTemplate processedTemplate, NotificationProcessingContext ctx) { String phone = recipient.getPhone(); - if (StringUtils.isBlank(phone)) + if (StringUtils.isBlank(phone)) { return Futures.immediateFailedFuture(new RuntimeException("User does not have phone number")); + } return executor.submit(() -> { smsService.sendSms(recipient.getTenantId(), recipient.getCustomerId(), new String[]{phone}, processedTemplate.getBody()); diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessingService.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessingService.java index c1be803984..c17b8f3bcd 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessingService.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessingService.java @@ -122,7 +122,9 @@ public class DefaultNotificationRuleProcessingService implements NotificationRul processNotificationRule(rule, originatorEntityId, triggerObject); }); } - }, e -> {}); + }, e -> { + log.error("Failed to find notification rules by trigger type {}", triggerType, e); + }); } private void processNotificationRule(NotificationRule rule, EntityId originatorEntityId, Object triggerObject) { diff --git a/application/src/main/java/org/thingsboard/server/service/slack/DefaultSlackService.java b/application/src/main/java/org/thingsboard/server/service/slack/DefaultSlackService.java index d428ae8c8c..7bd5fb7817 100644 --- a/application/src/main/java/org/thingsboard/server/service/slack/DefaultSlackService.java +++ b/application/src/main/java/org/thingsboard/server/service/slack/DefaultSlackService.java @@ -55,7 +55,7 @@ public class DefaultSlackService implements SlackService { .expireAfterWrite(20, TimeUnit.SECONDS) .maximumSize(100) .build(); - private static final int CONVERSATIONS_LIMIT = 1000; + private static final int CONVERSATIONS_LOAD_LIMIT = 1000; @Override public void sendMessage(TenantId tenantId, String token, String conversationId, String message) { @@ -71,7 +71,7 @@ public class DefaultSlackService implements SlackService { return cache.get(conversationType + ":" + token, k -> { if (conversationType == SlackConversationType.DIRECT) { UsersListRequest request = UsersListRequest.builder() - .limit(CONVERSATIONS_LIMIT) + .limit(CONVERSATIONS_LOAD_LIMIT) .build(); UsersListResponse response = sendRequest(token, request, MethodsClient::usersList); @@ -89,7 +89,7 @@ public class DefaultSlackService implements SlackService { .types(List.of(conversationType == SlackConversationType.PUBLIC_CHANNEL ? ConversationType.PUBLIC_CHANNEL : ConversationType.PRIVATE_CHANNEL)) - .limit(CONVERSATIONS_LIMIT) + .limit(CONVERSATIONS_LOAD_LIMIT) .excludeArchived(true) .build(); diff --git a/application/src/main/java/org/thingsboard/server/service/ws/DefaultWebSocketService.java b/application/src/main/java/org/thingsboard/server/service/ws/DefaultWebSocketService.java index 0ca0508acc..7d71b09baf 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/DefaultWebSocketService.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/DefaultWebSocketService.java @@ -252,7 +252,11 @@ public class DefaultWebSocketService implements WebSocketService { if (cmd != null) { String sessionId = sessionRef.getSessionId(); if (validateSessionMetadata(sessionRef, cmd.getCmdId(), sessionId)) { - cmdHandler.handle(sessionRef, cmd); // todo: handle exceptions + try { + cmdHandler.handle(sessionRef, cmd); + } catch (Exception e) { + log.error("Failed to handle WS cmd: {}", cmd, e); + } } } } diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 24cf6143ea..aee1c1a32c 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -438,6 +438,9 @@ cache: notificationRules: timeToLiveInMinutes: "${CACHE_SPECS_NOTIFICATION_RULES_TTL:1440}" maxSize: "${CACHE_SPECS_NOTIFICATION_RULES_MAX_SIZE:10000}" + notificationRequests: + timeToLiveInMinutes: "${CACHE_SPECS_NOTIFICATION_RULES_TTL:1440}" + maxSize: "${CACHE_SPECS_NOTIFICATION_RULES_MAX_SIZE:10000}" attributes: timeToLiveInMinutes: "${CACHE_SPECS_ATTRIBUTES_TTL:1440}" maxSize: "${CACHE_SPECS_ATTRIBUTES_MAX_SIZE:100000}" diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java index 557c3e65dd..bb2bbce14a 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java @@ -38,7 +38,6 @@ import org.thingsboard.server.common.data.notification.info.UserOriginatedNotifi import org.thingsboard.server.common.data.notification.settings.NotificationSettings; import org.thingsboard.server.common.data.notification.settings.SlackNotificationDeliveryMethodConfig; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; -import org.thingsboard.server.common.data.notification.targets.NotificationTargetType; import org.thingsboard.server.common.data.notification.targets.platform.AllUsersFilter; import org.thingsboard.server.common.data.notification.targets.platform.CustomerUsersFilter; import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig; @@ -172,16 +171,12 @@ public class NotificationApiTest extends AbstractNotificationApiTest { @Test public void testMarkingAsRead_multipleSessions() throws Exception { connectOtherWsClient(); - wsClient.subscribeForUnreadNotifications(10); - otherWsClient.subscribeForUnreadNotifications(10); - wsClient.waitForReply(true); - otherWsClient.waitForReply(true); - otherWsClient.subscribeForUnreadNotificationsCount(); - otherWsClient.waitForReply(true); + wsClient.subscribeForUnreadNotifications(10).waitForReply(true); + otherWsClient.subscribeForUnreadNotifications(10).waitForReply(true); NotificationTarget notificationTarget = createNotificationTarget(customerUserId); wsClient.registerWaitForUpdate(); - otherWsClient.registerWaitForUpdate(2); + otherWsClient.registerWaitForUpdate(); String notificationText1 = "Notification 1"; submitNotificationRequest(notificationTarget.getId(), notificationText1); wsClient.waitForUpdate(true); @@ -189,24 +184,22 @@ public class NotificationApiTest extends AbstractNotificationApiTest { Notification notification1 = wsClient.getLastDataUpdate().getUpdate(); wsClient.registerWaitForUpdate(); - otherWsClient.registerWaitForUpdate(2); + otherWsClient.registerWaitForUpdate(); String notificationText2 = "Notification 2"; submitNotificationRequest(notificationTarget.getId(), notificationText2); wsClient.waitForUpdate(true); otherWsClient.waitForUpdate(true); assertThat(wsClient.getLastDataUpdate().getTotalUnreadCount()).isEqualTo(2); assertThat(otherWsClient.getLastDataUpdate().getTotalUnreadCount()).isEqualTo(2); - assertThat(otherWsClient.getLastCountUpdate().getTotalUnreadCount()).isEqualTo(2); wsClient.registerWaitForUpdate(); - otherWsClient.registerWaitForUpdate(2); + otherWsClient.registerWaitForUpdate(); wsClient.markNotificationAsRead(notification1.getUuidId()); wsClient.waitForUpdate(true); otherWsClient.waitForUpdate(true); checkFullNotificationsUpdate(wsClient.getLastDataUpdate(), notificationText2); checkFullNotificationsUpdate(otherWsClient.getLastDataUpdate(), notificationText2); - assertThat(otherWsClient.getLastCountUpdate().getTotalUnreadCount()).isOne(); } @Test @@ -300,8 +293,8 @@ public class NotificationApiTest extends AbstractNotificationApiTest { } @Test - public void testNotificationUpdatesForALotOfUsers() throws Exception { - int usersCount = 80; + public void testNotificationUpdatesForSeveralUsers() throws Exception { + int usersCount = 150; Map sessions = new HashMap<>(); List targets = new ArrayList<>(); @@ -317,23 +310,21 @@ public class NotificationApiTest extends AbstractNotificationApiTest { NotificationTarget notificationTarget = createNotificationTarget(user.getId()); targets.add(notificationTarget.getId()); - wsClient.registerWaitForUpdate(2); + wsClient.registerWaitForUpdate(); wsClient.subscribeForUnreadNotifications(10); - wsClient.subscribeForUnreadNotificationsCount(); } sessions.values().forEach(wsClient -> wsClient.waitForUpdate(true)); loginTenantAdmin(); - sessions.forEach((user, wsClient) -> wsClient.registerWaitForUpdate(2)); + sessions.forEach((user, wsClient) -> wsClient.registerWaitForUpdate()); NotificationRequest notificationRequest = submitNotificationRequest(targets, "Hello, ${recipientEmail}", 0, NotificationDeliveryMethod.PUSH); await().atMost(10, TimeUnit.SECONDS) .pollDelay(1, TimeUnit.SECONDS).pollInterval(500, TimeUnit.MILLISECONDS) .until(() -> { long receivedUpdate = sessions.values().stream() - .filter(wsClient -> wsClient.getLastDataUpdate() != null - && wsClient.getLastCountUpdate() != null) + .filter(wsClient -> wsClient.getLastDataUpdate() != null) .count(); System.err.println("WS sessions received update: " + receivedUpdate); return receivedUpdate == sessions.size(); @@ -341,7 +332,6 @@ public class NotificationApiTest extends AbstractNotificationApiTest { sessions.forEach((user, wsClient) -> { assertThat(wsClient.getLastDataUpdate().getTotalUnreadCount()).isOne(); - assertThat(wsClient.getLastCountUpdate().getTotalUnreadCount()).isOne(); Notification notification = wsClient.getLastDataUpdate().getUpdate(); assertThat(notification.getRecipientId()).isEqualTo(user.getId()); @@ -354,13 +344,12 @@ public class NotificationApiTest extends AbstractNotificationApiTest { NotificationRequestStats stats = getStats(notificationRequest.getId()); assertThat(stats.getSent().get(NotificationDeliveryMethod.PUSH)).hasValue(usersCount); - sessions.values().forEach(wsClient -> wsClient.registerWaitForUpdate(2)); + sessions.values().forEach(wsClient -> wsClient.registerWaitForUpdate()); deleteNotificationRequest(notificationRequest.getId()); sessions.values().forEach(wsClient -> { wsClient.waitForUpdate(true); assertThat(wsClient.getLastDataUpdate().getNotifications()).isEmpty(); assertThat(wsClient.getLastDataUpdate().getTotalUnreadCount()).isZero(); - assertThat(wsClient.getLastCountUpdate().getTotalUnreadCount()).isZero(); }); sessions.values().forEach(WebSocketClient::close); 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 f695160191..60915a2952 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 @@ -391,7 +391,6 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { DeviceProfileAlarm alarm = new DeviceProfileAlarm(); alarm.setAlarmType(alarmType); alarm.setId(alarmType); - alarm.setNotificationRuleId(notificationRuleId); AlarmRule alarmRule = new AlarmRule(); alarmRule.setAlarmDetails("Details"); AlarmCondition alarmCondition = new AlarmCondition(); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestService.java index bfdd87c534..c781f450cb 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestService.java @@ -45,7 +45,7 @@ public interface NotificationRequestService { List findNotificationRequestsByRuleIdAndOriginatorEntityId(TenantId tenantId, NotificationRuleId ruleId, EntityId originatorEntityId); - void deleteNotificationRequestById(TenantId tenantId, NotificationRequestId id); + void deleteNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest); PageData findScheduledNotificationRequests(PageLink pageLink); 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..766a431816 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_REQUESTS_CACHE = "notificationRequests"; 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/alarm/Alarm.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java index 5177c94c3d..9db30fd466 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java @@ -29,7 +29,6 @@ import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileAlarm.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileAlarm.java index 0cba866ed2..24fff47fc2 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileAlarm.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileAlarm.java @@ -19,7 +19,6 @@ import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import org.thingsboard.server.common.data.alarm.AlarmSeverity; -import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; @@ -60,6 +59,4 @@ public class DeviceProfileAlarm implements Serializable { "This parameter should be used only in case when 'propagate' parameter is set to true, otherwise, 'propagateRelationTypes' array will be ignored.") private List propagateRelationTypes; - private NotificationRuleId notificationRuleId; - } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestStats.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestStats.java index b9673c74a2..9d92e20f5d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestStats.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestStats.java @@ -20,7 +20,6 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; import org.thingsboard.server.common.data.User; -import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.targets.NotificationRecipient; import java.util.Collections; @@ -70,10 +69,6 @@ public class NotificationRequestStats { errors.computeIfAbsent(deliveryMethod, k -> new ConcurrentHashMap<>()).put(key, errorMessage); } - public boolean contains(NotificationDeliveryMethod deliveryMethod) { - return sent.containsKey(deliveryMethod) || errors.containsKey(deliveryMethod); - } - public boolean contains(NotificationDeliveryMethod deliveryMethod, Object recipientId) { Set processedRecipients = this.processedRecipients.get(deliveryMethod); return processedRecipients != null && processedRecipients.contains(recipientId); 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 990625a0a5..563f5c308b 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 @@ -50,7 +50,7 @@ public class NotificationRule extends BaseData implements Ha private NotificationRuleTriggerConfig triggerConfig; @NotNull @Valid - private NotificationRuleRecipientsConfig recipientsConfig; // todo: add pg_tgrm index (but index is 2.5x size of the column) + private NotificationRuleRecipientsConfig recipientsConfig; private NotificationRuleConfig additionalConfig; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/NotificationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/NotificationTemplate.java index afef32aa09..2eaff12a9b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/NotificationTemplate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/NotificationTemplate.java @@ -42,6 +42,6 @@ public class NotificationTemplate extends BaseData imple private NotificationType notificationType; @Valid @NotNull - private NotificationTemplateConfig configuration; // TODO: add pg_tgrm index + private NotificationTemplateConfig configuration; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractAlarmEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractAlarmEntity.java index 999c7d9dfe..cf05ccda02 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractAlarmEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractAlarmEntity.java @@ -29,7 +29,6 @@ import org.thingsboard.server.common.data.alarm.AlarmStatus; import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityIdFactory; -import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.model.BaseEntity; import org.thingsboard.server.dao.model.BaseSqlEntity; diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRequestService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRequestService.java index a23c923e7b..f891e1b118 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRequestService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRequestService.java @@ -20,6 +20,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.TenantId; @@ -29,14 +30,19 @@ import org.thingsboard.server.common.data.notification.NotificationRequestStats; import org.thingsboard.server.common.data.notification.NotificationRequestStatus; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.entity.AbstractCachedEntityService; +import org.thingsboard.server.dao.entity.EntityDaoService; +import org.thingsboard.server.dao.notification.cache.NotificationRequestCacheKey; +import org.thingsboard.server.dao.notification.cache.NotificationRequestCacheValue; import org.thingsboard.server.dao.service.DataValidator; import java.util.List; +import java.util.Optional; @Service @Slf4j @RequiredArgsConstructor -public class DefaultNotificationRequestService implements NotificationRequestService { +public class DefaultNotificationRequestService extends AbstractCachedEntityService implements NotificationRequestService, EntityDaoService { private final NotificationRequestDao notificationRequestDao; @@ -45,7 +51,14 @@ public class DefaultNotificationRequestService implements NotificationRequestSer @Override public NotificationRequest saveNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest) { notificationRequestValidator.validate(notificationRequest, NotificationRequest::getTenantId); - return notificationRequestDao.save(tenantId, notificationRequest); + try { + notificationRequest = notificationRequestDao.save(tenantId, notificationRequest); + publishEvictEvent(notificationRequest); + } catch (Exception e) { + handleEvictEvent(notificationRequest); + throw e; + } + return notificationRequest; } @Override @@ -75,14 +88,21 @@ public class DefaultNotificationRequestService implements NotificationRequestSer @Override public List findNotificationRequestsByRuleIdAndOriginatorEntityId(TenantId tenantId, NotificationRuleId ruleId, EntityId originatorEntityId) { - // FIXME: add caching - return notificationRequestDao.findByRuleIdAndOriginatorEntityId(tenantId, ruleId, originatorEntityId); + NotificationRequestCacheKey cacheKey = NotificationRequestCacheKey.builder() + .originatorEntityId(originatorEntityId) + .ruleId(ruleId) + .build(); + return cache.getAndPutInTransaction(cacheKey, () -> NotificationRequestCacheValue.builder() + .notificationRequests(notificationRequestDao.findByRuleIdAndOriginatorEntityId(tenantId, ruleId, originatorEntityId)) + .build(), false) + .getNotificationRequests(); } // ON DELETE CASCADE is used: notifications for request are deleted as well @Override - public void deleteNotificationRequestById(TenantId tenantId, NotificationRequestId id) { - notificationRequestDao.removeById(tenantId, id.getId()); + public void deleteNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest) { + publishEvictEvent(notificationRequest); + notificationRequestDao.removeById(tenantId, notificationRequest.getUuidId()); } @Override @@ -100,6 +120,27 @@ public class DefaultNotificationRequestService implements NotificationRequestSer notificationRequestDao.removeByTenantId(tenantId); } + @Override + public void handleEvictEvent(NotificationRequest notificationRequest) { + if (notificationRequest.getRuleId() == null) return; + + NotificationRequestCacheKey cacheKey = NotificationRequestCacheKey.builder() + .originatorEntityId(notificationRequest.getOriginatorEntityId()) + .ruleId(notificationRequest.getRuleId()) + .build(); + cache.evict(cacheKey); + } + + @Override + public Optional> findEntity(TenantId tenantId, EntityId entityId) { + return Optional.ofNullable(findNotificationRequestById(tenantId, new NotificationRequestId(entityId.getId()))); + } + + @Override + public EntityType getEntityType() { + return EntityType.NOTIFICATION_REQUEST; + } + private static class NotificationRequestValidator extends DataValidator { diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRuleService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRuleService.java index bd4e6c6b5f..322659388e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRuleService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRuleService.java @@ -17,6 +17,9 @@ package org.thingsboard.server.dao.notification; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.rule.NotificationRule; @@ -25,15 +28,17 @@ import org.thingsboard.server.common.data.notification.rule.trigger.Notification import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.entity.AbstractCachedEntityService; +import org.thingsboard.server.dao.entity.EntityDaoService; import org.thingsboard.server.dao.notification.cache.NotificationRuleCacheKey; import org.thingsboard.server.dao.notification.cache.NotificationRuleCacheValue; import java.util.List; import java.util.Map; +import java.util.Optional; @Service @RequiredArgsConstructor -public class DefaultNotificationRuleService extends AbstractCachedEntityService implements NotificationRuleService { +public class DefaultNotificationRuleService extends AbstractCachedEntityService implements NotificationRuleService, EntityDaoService { private final NotificationRuleDao notificationRuleDao; @@ -112,4 +117,14 @@ public class DefaultNotificationRuleService extends AbstractCachedEntityService< cache.evict(cacheKey); } + @Override + public Optional> findEntity(TenantId tenantId, EntityId entityId) { + return Optional.ofNullable(findNotificationRuleById(tenantId, new NotificationRuleId(entityId.getId()))); + } + + @Override + public EntityType getEntityType() { + return EntityType.NOTIFICATION_RULE; + } + } 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 465c1a3125..37a225c002 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 @@ -93,6 +93,12 @@ public class DefaultNotificationSettingsService implements NotificationSettingsS NotificationTemplate alarmNotificationTemplate = new NotificationTemplate(); + /* + * TODO: + * rule chain start failure + * alarm + * */ + } } 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 e7e8d9458b..bdf802dea0 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 @@ -18,8 +18,11 @@ package org.thingsboard.server.dao.notification; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; @@ -34,17 +37,19 @@ import org.thingsboard.server.common.data.notification.targets.platform.UsersFil import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.entity.AbstractEntityService; +import org.thingsboard.server.dao.entity.EntityDaoService; import org.thingsboard.server.dao.user.UserService; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.stream.Collectors; @Service @Slf4j @RequiredArgsConstructor -public class DefaultNotificationTargetService extends AbstractEntityService implements NotificationTargetService { +public class DefaultNotificationTargetService extends AbstractEntityService implements NotificationTargetService, EntityDaoService { private final NotificationTargetDao notificationTargetDao; private final NotificationRequestDao notificationRequestDao; @@ -133,10 +138,10 @@ public class DefaultNotificationTargetService extends AbstractEntityService impl @Override public void deleteNotificationTargetById(TenantId tenantId, NotificationTargetId id) { - if (notificationRequestDao.existsByStatusAndTargetId(tenantId, NotificationRequestStatus.SCHEDULED, id)) { + if (notificationRequestDao.existsByTenantIdAndStatusAndTargetId(tenantId, NotificationRequestStatus.SCHEDULED, id)) { throw new IllegalArgumentException("Notification target is referenced by scheduled notification request"); } - if (notificationRuleDao.existsByTargetId(tenantId, id)) { + if (notificationRuleDao.existsByTenantIdAndTargetId(tenantId, id)) { throw new IllegalArgumentException("Notification target is being used in notification rule"); } notificationTargetDao.removeById(tenantId, id.getId()); @@ -147,4 +152,14 @@ public class DefaultNotificationTargetService extends AbstractEntityService impl notificationTargetDao.removeByTenantId(tenantId); } + @Override + public Optional> findEntity(TenantId tenantId, EntityId entityId) { + return Optional.ofNullable(findNotificationTargetById(tenantId, new NotificationTargetId(entityId.getId()))); + } + + @Override + public EntityType getEntityType() { + return EntityType.NOTIFICATION_TARGET; + } + } 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 49f17d4de4..f1d0a79672 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 @@ -17,6 +17,9 @@ package org.thingsboard.server.dao.notification; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.NotificationTemplateId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.NotificationRequestStatus; @@ -25,13 +28,15 @@ 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 org.thingsboard.server.dao.entity.AbstractEntityService; +import org.thingsboard.server.dao.entity.EntityDaoService; import java.util.List; import java.util.Map; +import java.util.Optional; @Service @RequiredArgsConstructor -public class DefaultNotificationTemplateService extends AbstractEntityService implements NotificationTemplateService { +public class DefaultNotificationTemplateService extends AbstractEntityService implements NotificationTemplateService, EntityDaoService { private final NotificationTemplateDao notificationTemplateDao; private final NotificationRequestDao notificationRequestDao; @@ -60,7 +65,7 @@ public class DefaultNotificationTemplateService extends AbstractEntityService im @Override public void deleteNotificationTemplateById(TenantId tenantId, NotificationTemplateId id) { - if (notificationRequestDao.existsByStatusAndTemplateId(tenantId, NotificationRequestStatus.SCHEDULED, id)) { + if (notificationRequestDao.existsByTenantIdAndStatusAndTemplateId(tenantId, NotificationRequestStatus.SCHEDULED, id)) { throw new IllegalArgumentException("Notification template is referenced by scheduled notification request"); } try { @@ -78,4 +83,14 @@ public class DefaultNotificationTemplateService extends AbstractEntityService im notificationTemplateDao.removeByTenantId(tenantId); } + @Override + public Optional> findEntity(TenantId tenantId, EntityId entityId) { + return Optional.ofNullable(findNotificationTemplateById(tenantId, new NotificationTemplateId(entityId.getId()))); + } + + @Override + public EntityType getEntityType() { + return EntityType.NOTIFICATION_TEMPLATE; + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java index dfdd501dd0..26fd4fd2db 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java @@ -46,9 +46,9 @@ public interface NotificationRequestDao extends Dao { void updateById(TenantId tenantId, NotificationRequestId requestId, NotificationRequestStatus requestStatus, NotificationRequestStats stats); - boolean existsByStatusAndTargetId(TenantId tenantId, NotificationRequestStatus status, NotificationTargetId targetId); + boolean existsByTenantIdAndStatusAndTargetId(TenantId tenantId, NotificationRequestStatus status, NotificationTargetId targetId); - boolean existsByStatusAndTemplateId(TenantId tenantId, NotificationRequestStatus status, NotificationTemplateId templateId); + boolean existsByTenantIdAndStatusAndTemplateId(TenantId tenantId, NotificationRequestStatus status, NotificationTemplateId templateId); int removeAllByCreatedTimeBefore(long ts); diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleDao.java b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleDao.java index 049216ff63..a2cd2caeab 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleDao.java @@ -33,7 +33,7 @@ public interface NotificationRuleDao extends Dao { PageData findInfosByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink); - boolean existsByTargetId(TenantId tenantId, NotificationTargetId targetId); + boolean existsByTenantIdAndTargetId(TenantId tenantId, NotificationTargetId targetId); List findByTenantIdAndTriggerType(TenantId tenantId, NotificationRuleTriggerType triggerType); diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/cache/NotificationRequestCacheKey.java b/dao/src/main/java/org/thingsboard/server/dao/notification/cache/NotificationRequestCacheKey.java new file mode 100644 index 0000000000..403e192fbb --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/cache/NotificationRequestCacheKey.java @@ -0,0 +1,43 @@ +/** + * 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.dao.notification.cache; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.NotificationRuleId; + +import java.io.Serializable; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class NotificationRequestCacheKey implements Serializable { + + private static final long serialVersionUID = 59871139005482170L; + + private EntityId originatorEntityId; + private NotificationRuleId ruleId; + + @Override + public String toString() { + return ruleId + "_" + originatorEntityId; + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/cache/NotificationRequestCacheValue.java b/dao/src/main/java/org/thingsboard/server/dao/notification/cache/NotificationRequestCacheValue.java new file mode 100644 index 0000000000..cdd21720ca --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/cache/NotificationRequestCacheValue.java @@ -0,0 +1,38 @@ +/** + * 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.dao.notification.cache; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.notification.NotificationRequest; +import org.thingsboard.server.common.data.notification.rule.NotificationRule; + +import java.io.Serializable; +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class NotificationRequestCacheValue implements Serializable { + + private static final long serialVersionUID = 950211234585105415L; + + private List notificationRequests; + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/cache/NotificationRequestCaffeineCache.java b/dao/src/main/java/org/thingsboard/server/dao/notification/cache/NotificationRequestCaffeineCache.java new file mode 100644 index 0000000000..2b2dc91fc9 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/cache/NotificationRequestCaffeineCache.java @@ -0,0 +1,32 @@ +/** + * 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.dao.notification.cache; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cache.CacheManager; +import org.springframework.stereotype.Service; +import org.thingsboard.server.cache.CaffeineTbTransactionalCache; +import org.thingsboard.server.common.data.CacheConstants; + +@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true) +@Service +public class NotificationRequestCaffeineCache extends CaffeineTbTransactionalCache { + + public NotificationRequestCaffeineCache(CacheManager cacheManager) { + super(cacheManager, CacheConstants.NOTIFICATION_REQUESTS_CACHE); + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/cache/NotificationRequestRedisCache.java b/dao/src/main/java/org/thingsboard/server/dao/notification/cache/NotificationRequestRedisCache.java new file mode 100644 index 0000000000..0da04c15db --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/cache/NotificationRequestRedisCache.java @@ -0,0 +1,35 @@ +/** + * 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.dao.notification.cache; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.stereotype.Service; +import org.thingsboard.server.cache.CacheSpecsMap; +import org.thingsboard.server.cache.RedisTbTransactionalCache; +import org.thingsboard.server.cache.TBRedisCacheConfiguration; +import org.thingsboard.server.cache.TbFSTRedisSerializer; +import org.thingsboard.server.common.data.CacheConstants; + +@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis") +@Service +public class NotificationRequestRedisCache extends RedisTbTransactionalCache { + + public NotificationRequestRedisCache(CacheSpecsMap cacheSpecsMap, RedisConnectionFactory connectionFactory, TBRedisCacheConfiguration configuration) { + super(CacheConstants.NOTIFICATION_REQUESTS_CACHE, cacheSpecsMap, connectionFactory, configuration, new TbFSTRedisSerializer<>()); + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/cache/NotificationRuleCaffeineCache.java b/dao/src/main/java/org/thingsboard/server/dao/notification/cache/NotificationRuleCaffeineCache.java index 791eb253cd..13b9d5a77a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/cache/NotificationRuleCaffeineCache.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/cache/NotificationRuleCaffeineCache.java @@ -22,7 +22,7 @@ import org.thingsboard.server.cache.CaffeineTbTransactionalCache; import org.thingsboard.server.common.data.CacheConstants; @ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true) -@Service("notificationRuleCache") +@Service public class NotificationRuleCaffeineCache extends CaffeineTbTransactionalCache { public NotificationRuleCaffeineCache(CacheManager cacheManager) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/cache/NotificationRuleRedisCache.java b/dao/src/main/java/org/thingsboard/server/dao/notification/cache/NotificationRuleRedisCache.java index d4dfc1b231..230163a9eb 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/cache/NotificationRuleRedisCache.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/cache/NotificationRuleRedisCache.java @@ -25,7 +25,7 @@ import org.thingsboard.server.cache.TbFSTRedisSerializer; import org.thingsboard.server.common.data.CacheConstants; @ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis") -@Service("notificationRuleCache") +@Service public class NotificationRuleRedisCache extends RedisTbTransactionalCache { public NotificationRuleRedisCache(CacheSpecsMap cacheSpecsMap, RedisConnectionFactory connectionFactory, TBRedisCacheConfiguration configuration) { 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 f16ed5f48e..2cc300fb09 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 @@ -44,8 +44,6 @@ import java.util.List; import java.util.UUID; import java.util.stream.Collectors; -import static org.thingsboard.server.dao.DaoUtil.getId; - @Component @SqlDao @RequiredArgsConstructor @@ -87,13 +85,13 @@ public class JpaNotificationRequestDao extends JpaAbstractDao findAllByTenantIdAndTriggerType(UUID tenantId, NotificationRuleTriggerType triggerType); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbNotificationNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbNotificationNode.java index 5d37ab8ac7..f18d030612 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbNotificationNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbNotificationNode.java @@ -35,8 +35,8 @@ import java.util.concurrent.ExecutionException; type = ComponentType.EXTERNAL, name = "send notification", configClazz = TbNotificationNodeConfiguration.class, - nodeDescription = "Sends notification to a target", - nodeDetails = "Will send notification to the specified target", + nodeDescription = "Sends notification to targets using the template", + nodeDetails = "Will send notification to the specified targets", uiResources = {"static/rulenode/rulenode-core-config.js"} ) public class TbNotificationNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbSlackNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbSlackNode.java index 7cb8d9a450..0d402b7c42 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbSlackNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbSlackNode.java @@ -58,6 +58,7 @@ public class TbSlackNode implements TbNode { if (token == null) { throw new IllegalArgumentException("Slack token is missing"); } + String message = TbNodeUtils.processPattern(config.getMessageTemplate(), msg); ListenableFuture result; diff --git a/ui-ngx/src/app/shared/decorators/coerce-boolean.ts b/ui-ngx/src/app/shared/decorators/coerce-boolean.ts index 226135a42a..5f459ea817 100644 --- a/ui-ngx/src/app/shared/decorators/coerce-boolean.ts +++ b/ui-ngx/src/app/shared/decorators/coerce-boolean.ts @@ -13,6 +13,7 @@ /// See the License for the specific language governing permissions and /// limitations under the License. /// + import { coerceBooleanProperty } from '@angular/cdk/coercion'; export function coerceBoolean() {