Browse Source

Notification system improvements

pull/7911/head
ViacheslavKlimov 3 years ago
parent
commit
79670d9f9d
  1. 9
      application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java
  2. 13
      application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java
  3. 6
      application/src/main/java/org/thingsboard/server/service/notification/channels/SlackNotificationChannel.java
  4. 3
      application/src/main/java/org/thingsboard/server/service/notification/channels/SmsNotificationChannel.java
  5. 4
      application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessingService.java
  6. 6
      application/src/main/java/org/thingsboard/server/service/slack/DefaultSlackService.java
  7. 6
      application/src/main/java/org/thingsboard/server/service/ws/DefaultWebSocketService.java
  8. 3
      application/src/main/resources/thingsboard.yml
  9. 33
      application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java
  10. 1
      application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java
  11. 2
      common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestService.java
  12. 1
      common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java
  13. 1
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java
  14. 3
      common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileAlarm.java
  15. 5
      common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestStats.java
  16. 2
      common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRule.java
  17. 2
      common/data/src/main/java/org/thingsboard/server/common/data/notification/template/NotificationTemplate.java
  18. 1
      dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractAlarmEntity.java
  19. 53
      dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRequestService.java
  20. 17
      dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRuleService.java
  21. 6
      dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationSettingsService.java
  22. 21
      dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java
  23. 19
      dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTemplateService.java
  24. 4
      dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java
  25. 2
      dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleDao.java
  26. 43
      dao/src/main/java/org/thingsboard/server/dao/notification/cache/NotificationRequestCacheKey.java
  27. 38
      dao/src/main/java/org/thingsboard/server/dao/notification/cache/NotificationRequestCacheValue.java
  28. 32
      dao/src/main/java/org/thingsboard/server/dao/notification/cache/NotificationRequestCaffeineCache.java
  29. 35
      dao/src/main/java/org/thingsboard/server/dao/notification/cache/NotificationRequestRedisCache.java
  30. 2
      dao/src/main/java/org/thingsboard/server/dao/notification/cache/NotificationRuleCaffeineCache.java
  31. 2
      dao/src/main/java/org/thingsboard/server/dao/notification/cache/NotificationRuleRedisCache.java
  32. 10
      dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java
  33. 6
      dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java
  34. 4
      dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRequestRepository.java
  35. 2
      dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRuleRepository.java
  36. 4
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbNotificationNode.java
  37. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbSlackNode.java
  38. 1
      ui-ngx/src/app/shared/decorators/coerce-boolean.ts

9
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')")

13
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)

6
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<SlackConver
@Override
public ListenableFuture<Void> 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());

3
application/src/main/java/org/thingsboard/server/service/notification/channels/SmsNotificationChannel.java

@ -37,8 +37,9 @@ public class SmsNotificationChannel implements NotificationChannel<User, SmsDeli
@Override
public ListenableFuture<Void> 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());

4
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) {

6
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();

6
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);
}
}
}
}

3
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}"

33
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<User, NotificationApiWsClient> sessions = new HashMap<>();
List<NotificationTargetId> 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);

1
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();

2
common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestService.java

@ -45,7 +45,7 @@ public interface NotificationRequestService {
List<NotificationRequest> findNotificationRequestsByRuleIdAndOriginatorEntityId(TenantId tenantId, NotificationRuleId ruleId, EntityId originatorEntityId);
void deleteNotificationRequestById(TenantId tenantId, NotificationRequestId id);
void deleteNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest);
PageData<NotificationRequest> findScheduledNotificationRequests(PageLink pageLink);

1
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";

1
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;

3
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<String> propagateRelationTypes;
private NotificationRuleId notificationRuleId;
}

5
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<Object> processedRecipients = this.processedRecipients.get(deliveryMethod);
return processedRecipients != null && processedRecipients.contains(recipientId);

2
common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRule.java

@ -50,7 +50,7 @@ public class NotificationRule extends BaseData<NotificationRuleId> 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;

2
common/data/src/main/java/org/thingsboard/server/common/data/notification/template/NotificationTemplate.java

@ -42,6 +42,6 @@ public class NotificationTemplate extends BaseData<NotificationTemplateId> imple
private NotificationType notificationType;
@Valid
@NotNull
private NotificationTemplateConfig configuration; // TODO: add pg_tgrm index
private NotificationTemplateConfig configuration;
}

1
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;

53
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<NotificationRequestCacheKey, NotificationRequestCacheValue, NotificationRequest> 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<NotificationRequest> 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<HasId<?>> 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<NotificationRequest> {

17
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<NotificationRuleCacheKey, NotificationRuleCacheValue, NotificationRule> implements NotificationRuleService {
public class DefaultNotificationRuleService extends AbstractCachedEntityService<NotificationRuleCacheKey, NotificationRuleCacheValue, NotificationRule> implements NotificationRuleService, EntityDaoService {
private final NotificationRuleDao notificationRuleDao;
@ -112,4 +117,14 @@ public class DefaultNotificationRuleService extends AbstractCachedEntityService<
cache.evict(cacheKey);
}
@Override
public Optional<HasId<?>> findEntity(TenantId tenantId, EntityId entityId) {
return Optional.ofNullable(findNotificationRuleById(tenantId, new NotificationRuleId(entityId.getId())));
}
@Override
public EntityType getEntityType() {
return EntityType.NOTIFICATION_RULE;
}
}

6
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
* */
}
}

21
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<HasId<?>> findEntity(TenantId tenantId, EntityId entityId) {
return Optional.ofNullable(findNotificationTargetById(tenantId, new NotificationTargetId(entityId.getId())));
}
@Override
public EntityType getEntityType() {
return EntityType.NOTIFICATION_TARGET;
}
}

19
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<HasId<?>> findEntity(TenantId tenantId, EntityId entityId) {
return Optional.ofNullable(findNotificationTemplateById(tenantId, new NotificationTemplateId(entityId.getId())));
}
@Override
public EntityType getEntityType() {
return EntityType.NOTIFICATION_TEMPLATE;
}
}

4
dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java

@ -46,9 +46,9 @@ public interface NotificationRequestDao extends Dao<NotificationRequest> {
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);

2
dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleDao.java

@ -33,7 +33,7 @@ public interface NotificationRuleDao extends Dao<NotificationRule> {
PageData<NotificationRuleInfo> findInfosByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink);
boolean existsByTargetId(TenantId tenantId, NotificationTargetId targetId);
boolean existsByTenantIdAndTargetId(TenantId tenantId, NotificationTargetId targetId);
List<NotificationRule> findByTenantIdAndTriggerType(TenantId tenantId, NotificationRuleTriggerType triggerType);

43
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;
}
}

38
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<NotificationRequest> notificationRequests;
}

32
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<NotificationRequestCacheKey, NotificationRequestCacheValue> {
public NotificationRequestCaffeineCache(CacheManager cacheManager) {
super(cacheManager, CacheConstants.NOTIFICATION_REQUESTS_CACHE);
}
}

35
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<NotificationRequestCacheKey, NotificationRequestCacheValue> {
public NotificationRequestRedisCache(CacheSpecsMap cacheSpecsMap, RedisConnectionFactory connectionFactory, TBRedisCacheConfiguration configuration) {
super(CacheConstants.NOTIFICATION_REQUESTS_CACHE, cacheSpecsMap, connectionFactory, configuration, new TbFSTRedisSerializer<>());
}
}

2
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<NotificationRuleCacheKey, NotificationRuleCacheValue> {
public NotificationRuleCaffeineCache(CacheManager cacheManager) {

2
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<NotificationRuleCacheKey, NotificationRuleCacheValue> {
public NotificationRuleRedisCache(CacheSpecsMap cacheSpecsMap, RedisConnectionFactory connectionFactory, TBRedisCacheConfiguration configuration) {

10
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<NotificationReques
}
@Override
public boolean existsByStatusAndTargetId(TenantId tenantId, NotificationRequestStatus status, NotificationTargetId targetId) {
return notificationRequestRepository.existsByStatusAndTargetsContaining(status, targetId.getId().toString());
public boolean existsByTenantIdAndStatusAndTargetId(TenantId tenantId, NotificationRequestStatus status, NotificationTargetId targetId) {
return notificationRequestRepository.existsByTenantIdAndStatusAndTargetsContaining(tenantId.getId(), status, targetId.getId().toString());
}
@Override
public boolean existsByStatusAndTemplateId(TenantId tenantId, NotificationRequestStatus status, NotificationTemplateId templateId) {
return notificationRequestRepository.existsByStatusAndTemplateId(status, templateId.getId());
public boolean existsByTenantIdAndStatusAndTemplateId(TenantId tenantId, NotificationRequestStatus status, NotificationTemplateId templateId) {
return notificationRequestRepository.existsByTenantIdAndStatusAndTemplateId(tenantId.getId(), status, templateId.getId());
}
@Override

6
dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java

@ -38,8 +38,6 @@ import org.thingsboard.server.dao.util.SqlDao;
import java.util.List;
import java.util.UUID;
import static org.thingsboard.server.dao.DaoUtil.getId;
@Component
@SqlDao
@RequiredArgsConstructor
@ -60,8 +58,8 @@ public class JpaNotificationRuleDao extends JpaAbstractDao<NotificationRuleEntit
}
@Override
public boolean existsByTargetId(TenantId tenantId, NotificationTargetId targetId) {
return notificationRuleRepository.existsByRecipientsConfigContaining(targetId.getId().toString());
public boolean existsByTenantIdAndTargetId(TenantId tenantId, NotificationTargetId targetId) {
return notificationRuleRepository.existsByTenantIdAndRecipientsConfigContaining(tenantId.getId(), targetId.getId().toString());
}
@Override

4
dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRequestRepository.java

@ -65,9 +65,9 @@ public interface NotificationRequestRepository extends JpaRepository<Notificatio
@Param("status") NotificationRequestStatus status,
@Param("stats") JsonNode stats);
boolean existsByStatusAndTargetsContaining(NotificationRequestStatus status, String targetIdStr);
boolean existsByTenantIdAndStatusAndTargetsContaining(UUID tenantId, NotificationRequestStatus status, String targetIdStr);
boolean existsByStatusAndTemplateId(NotificationRequestStatus status, UUID templateId);
boolean existsByTenantIdAndStatusAndTemplateId(UUID tenantId, NotificationRequestStatus status, UUID templateId);
@Transactional
int deleteAllByCreatedTimeBefore(long ts);

2
dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRuleRepository.java

@ -42,7 +42,7 @@ public interface NotificationRuleRepository extends JpaRepository<NotificationRu
@Param("searchText") String searchText,
Pageable pageable);
boolean existsByRecipientsConfigContaining(String string);
boolean existsByTenantIdAndRecipientsConfigContaining(UUID tenantId, String searchString);
List<NotificationRuleEntity> findAllByTenantIdAndTriggerType(UUID tenantId, NotificationRuleTriggerType triggerType);

4
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 {

1
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;

1
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() {

Loading…
Cancel
Save