From 33dca7af710828cc17e8d538f7b4ec2ae2281c7c Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Thu, 30 Mar 2023 15:29:39 +0300 Subject: [PATCH] Notification system fixes and improvements --- .../controller/NotificationController.java | 12 ++- .../DefaultNotificationCenter.java | 13 +-- .../NotificationProcessingContext.java | 84 +++++++++++-------- .../DefaultNotificationCommandsHandler.java | 26 ++++-- .../notification/sub/NotificationUpdate.java | 5 +- .../notification/NotificationApiTest.java | 62 +------------- .../DeliveryMethodNotificationTemplate.java | 5 +- ...ailDeliveryMethodNotificationTemplate.java | 6 ++ ...WebDeliveryMethodNotificationTemplate.java | 50 ++++++++++- .../notification/NotificationRepository.java | 3 +- 10 files changed, 143 insertions(+), 123 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java index 2da8f86156..d6d9b1d6ee 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java @@ -43,6 +43,7 @@ import org.thingsboard.server.common.data.notification.NotificationRequest; import org.thingsboard.server.common.data.notification.NotificationRequestInfo; import org.thingsboard.server.common.data.notification.NotificationRequestPreview; import org.thingsboard.server.common.data.notification.settings.NotificationSettings; +import org.thingsboard.server.common.data.notification.targets.NotificationRecipient; 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.PlatformUsersNotificationTargetConfig; @@ -62,7 +63,6 @@ import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; import javax.validation.Valid; -import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; @@ -215,19 +215,17 @@ public class NotificationController extends BaseController { NotificationProcessingContext tmpProcessingCtx = NotificationProcessingContext.builder() .tenantId(user.getTenantId()) .request(request) - .settings(null) .template(template) + .settings(null) .build(); Map processedTemplates = tmpProcessingCtx.getDeliveryMethods().stream() .collect(Collectors.toMap(m -> m, deliveryMethod -> { - Map templateContext; + NotificationRecipient recipient = null; if (NotificationTargetType.PLATFORM_USERS.getSupportedDeliveryMethods().contains(deliveryMethod)) { - templateContext = tmpProcessingCtx.createTemplateContext(user); - } else { - templateContext = Collections.emptyMap(); + recipient = userService.findUserById(user.getTenantId(), user.getId()); } - return tmpProcessingCtx.getProcessedTemplate(deliveryMethod, templateContext); + return tmpProcessingCtx.getProcessedTemplate(deliveryMethod, recipient); })); preview.setProcessedTemplates(processedTemplates); 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 ef3c4f8706..b58ab8ebe9 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 @@ -152,8 +152,8 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple NotificationProcessingContext ctx = NotificationProcessingContext.builder() .tenantId(tenantId) .request(request) - .settings(settings) .template(notificationTemplate) + .settings(settings) .build(); notificationExecutor.submit(() -> { @@ -230,15 +230,10 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple if (ctx.getStats().contains(deliveryMethod, recipient.getId())) { return Futures.immediateFailedFuture(new AlreadySentException()); } - Map templateContext; - if (recipient instanceof User) { - templateContext = ctx.createTemplateContext(((User) recipient)); - } else { - templateContext = Collections.emptyMap(); - } + DeliveryMethodNotificationTemplate processedTemplate; try { - processedTemplate = ctx.getProcessedTemplate(deliveryMethod, templateContext); + processedTemplate = ctx.getProcessedTemplate(deliveryMethod, recipient); } catch (Exception e) { return Futures.immediateFailedFuture(e); } @@ -300,7 +295,7 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple log.trace("Marked notification {} as read (recipient id: {}, tenant id: {})", notificationId, recipientId, tenantId); NotificationUpdate update = NotificationUpdate.builder() .updated(true) - .notificationId(notificationId) + .notificationId(notificationId.getId()) .newStatus(NotificationStatus.READ) .build(); onNotificationUpdate(tenantId, recipientId, update); diff --git a/application/src/main/java/org/thingsboard/server/service/notification/NotificationProcessingContext.java b/application/src/main/java/org/thingsboard/server/service/notification/NotificationProcessingContext.java index c9e34227ee..39f339a6ec 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/NotificationProcessingContext.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/NotificationProcessingContext.java @@ -15,12 +15,10 @@ */ package org.thingsboard.server.service.notification; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.fasterxml.jackson.databind.node.TextNode; import com.google.common.base.Strings; import lombok.Builder; import lombok.Getter; +import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang3.StringUtils; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.id.CustomerId; @@ -28,23 +26,25 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; import org.thingsboard.server.common.data.notification.NotificationRequest; import org.thingsboard.server.common.data.notification.NotificationRequestStats; -import org.thingsboard.server.common.data.notification.info.NotificationInfo; import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo; import org.thingsboard.server.common.data.notification.settings.NotificationDeliveryMethodConfig; import org.thingsboard.server.common.data.notification.settings.NotificationSettings; +import org.thingsboard.server.common.data.notification.targets.NotificationRecipient; import org.thingsboard.server.common.data.notification.template.DeliveryMethodNotificationTemplate; import org.thingsboard.server.common.data.notification.template.HasSubject; import org.thingsboard.server.common.data.notification.template.NotificationTemplate; import org.thingsboard.server.common.data.notification.template.NotificationTemplateConfig; import org.thingsboard.server.common.data.notification.template.WebDeliveryMethodNotificationTemplate; +import java.util.Collections; import java.util.EnumMap; import java.util.HashMap; import java.util.Map; -import java.util.Optional; import java.util.Set; import java.util.regex.Pattern; +import static org.apache.commons.lang3.StringUtils.isNotEmpty; + @SuppressWarnings("unchecked") public class NotificationProcessingContext { @@ -65,8 +65,7 @@ public class NotificationProcessingContext { private static final Pattern TEMPLATE_PARAM_PATTERN = Pattern.compile("\\$\\{([a-zA-Z]+)(:[a-zA-Z]+)?}"); @Builder - public NotificationProcessingContext(TenantId tenantId, NotificationRequest request, NotificationSettings settings, - NotificationTemplate template) { + public NotificationProcessingContext(TenantId tenantId, NotificationRequest request, NotificationTemplate template, NotificationSettings settings) { this.tenantId = tenantId; this.request = request; this.settings = settings; @@ -80,6 +79,7 @@ public class NotificationProcessingContext { NotificationTemplateConfig templateConfig = notificationTemplate.getConfiguration(); templateConfig.getDeliveryMethodsTemplates().forEach((deliveryMethod, template) -> { if (template.isEnabled()) { + template = processTemplate(template, null); // processing template with immutable params templates.put(deliveryMethod, template); } }); @@ -90,36 +90,43 @@ public class NotificationProcessingContext { return (C) settings.getDeliveryMethodsConfigs().get(deliveryMethod); } - public T getProcessedTemplate(NotificationDeliveryMethod deliveryMethod, Map templateContext) { - NotificationInfo info = request.getInfo(); - if (info != null) { - templateContext = new HashMap<>(templateContext); - templateContext.putAll(info.getTemplateData()); + public T getProcessedTemplate(NotificationDeliveryMethod deliveryMethod, NotificationRecipient recipient) { + T template = (T) templates.get(deliveryMethod); + Map additionalTemplateContext = null; + if (recipient != null) { + additionalTemplateContext = createTemplateContextForRecipient(recipient); } + if (MapUtils.isNotEmpty(additionalTemplateContext) && template.containsAny(additionalTemplateContext.keySet().toArray(String[]::new))) { + template = processTemplate(template, additionalTemplateContext); + } + return template; + } - T template = (T) templates.get(deliveryMethod).copy(); + private T processTemplate(T template, Map additionalTemplateContext) { + Map templateContext = new HashMap<>(); + if (request.getInfo() != null) { + templateContext.putAll(request.getInfo().getTemplateData()); + } + if (additionalTemplateContext != null) { + templateContext.putAll(additionalTemplateContext); + } + if (templateContext.isEmpty()) return template; + + template = (T) template.copy(); template.setBody(processTemplate(template.getBody(), templateContext)); if (template instanceof HasSubject) { String subject = ((HasSubject) template).getSubject(); ((HasSubject) template).setSubject(processTemplate(subject, templateContext)); } - - if (deliveryMethod == NotificationDeliveryMethod.WEB) { + if (template instanceof WebDeliveryMethodNotificationTemplate) { WebDeliveryMethodNotificationTemplate webNotificationTemplate = (WebDeliveryMethodNotificationTemplate) template; - Optional buttonConfig = Optional.ofNullable(webNotificationTemplate.getAdditionalConfig()) - .map(config -> config.get("actionButtonConfig")).filter(JsonNode::isObject) - .map(config -> (ObjectNode) config); - if (buttonConfig.isPresent()) { - JsonNode text = buttonConfig.get().get("text"); - if (text != null && text.isTextual()) { - text = new TextNode(processTemplate(text.asText(), templateContext)); - buttonConfig.get().set("text", text); - } - JsonNode link = buttonConfig.get().get("link"); - if (link != null && link.isTextual()) { - link = new TextNode(processTemplate(link.asText(), templateContext)); - buttonConfig.get().set("link", link); - } + String buttonText = webNotificationTemplate.getButtonText(); + if (isNotEmpty(buttonText)) { + webNotificationTemplate.setButtonText(processTemplate(buttonText, templateContext)); + } + String buttonLink = webNotificationTemplate.getButtonLink(); + if (isNotEmpty(buttonLink)) { + webNotificationTemplate.setButtonLink(processTemplate(buttonLink, templateContext)); } } return template; @@ -128,6 +135,9 @@ public class NotificationProcessingContext { private static String processTemplate(String template, Map context) { return TEMPLATE_PARAM_PATTERN.matcher(template).replaceAll(matchResult -> { String key = matchResult.group(1); + if (!context.containsKey(key)) { + return "\\" + matchResult.group(); // adding escape char due to special meaning of '$' to matcher + } String value = Strings.nullToEmpty(context.get(key)); String function = matchResult.group(2); if (function != null) { @@ -144,12 +154,16 @@ public class NotificationProcessingContext { }); } - public Map createTemplateContext(User recipient) { - Map templateContext = new HashMap<>(); - templateContext.put("recipientEmail", recipient.getEmail()); - templateContext.put("recipientFirstName", Strings.nullToEmpty(recipient.getFirstName())); - templateContext.put("recipientLastName", Strings.nullToEmpty(recipient.getLastName())); - return templateContext; + private Map createTemplateContextForRecipient(NotificationRecipient recipient) { + if (recipient instanceof User) { + User user = (User) recipient; + return Map.of( + "recipientEmail", user.getEmail(), + "recipientFirstName", Strings.nullToEmpty(user.getFirstName()), + "recipientLastName", Strings.nullToEmpty(user.getLastName()) + ); + } + return Collections.emptyMap(); } public CustomerId getCustomerId() { diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java index 1430519abf..012808b253 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java @@ -120,17 +120,21 @@ public class DefaultNotificationCommandsHandler implements NotificationCommandsH /* Notifications subscription update handling */ private void handleNotificationsSubscriptionUpdate(NotificationsSubscription subscription, NotificationsSubscriptionUpdate subscriptionUpdate) { - if (subscriptionUpdate.getNotificationUpdate() != null) { - handleNotificationUpdate(subscription, subscriptionUpdate.getNotificationUpdate()); - } else if (subscriptionUpdate.getNotificationRequestUpdate() != null) { - handleNotificationRequestUpdate(subscription, subscriptionUpdate.getNotificationRequestUpdate()); + try { + if (subscriptionUpdate.getNotificationUpdate() != null) { + handleNotificationUpdate(subscription, subscriptionUpdate.getNotificationUpdate()); + } else if (subscriptionUpdate.getNotificationRequestUpdate() != null) { + handleNotificationRequestUpdate(subscription, subscriptionUpdate.getNotificationRequestUpdate()); + } + } catch (Exception e) { + log.error("[{}, subId: {}] Failed to handle update for notifications subscription: {}", subscription.getSessionId(), subscription.getSubscriptionId(), subscriptionUpdate, e); } } private void handleNotificationUpdate(NotificationsSubscription subscription, NotificationUpdate update) { log.trace("[{}, subId: {}] Handling notification update: {}", subscription.getSessionId(), subscription.getSubscriptionId(), update); Notification notification = update.getNotification(); - UUID notificationId = notification != null ? notification.getUuidId() : update.getNotificationId().getId(); + UUID notificationId = notification != null ? notification.getUuidId() : update.getNotificationId(); if (update.isCreated()) { subscription.getLatestUnreadNotifications().put(notificationId, notification); subscription.getTotalUnreadCounter().incrementAndGet(); @@ -175,10 +179,14 @@ public class DefaultNotificationCommandsHandler implements NotificationCommandsH /* Notifications count subscription update handling */ private void handleNotificationsCountSubscriptionUpdate(NotificationsCountSubscription subscription, NotificationsSubscriptionUpdate subscriptionUpdate) { - if (subscriptionUpdate.getNotificationUpdate() != null) { - handleNotificationUpdate(subscription, subscriptionUpdate.getNotificationUpdate()); - } else if (subscriptionUpdate.getNotificationRequestUpdate() != null) { - handleNotificationRequestUpdate(subscription, subscriptionUpdate.getNotificationRequestUpdate()); + try { + if (subscriptionUpdate.getNotificationUpdate() != null) { + handleNotificationUpdate(subscription, subscriptionUpdate.getNotificationUpdate()); + } else if (subscriptionUpdate.getNotificationRequestUpdate() != null) { + handleNotificationRequestUpdate(subscription, subscriptionUpdate.getNotificationRequestUpdate()); + } + } catch (Exception e) { + log.error("[{}, subId: {}] Failed to handle update for notifications count subscription: {}", subscription.getSessionId(), subscription.getSubscriptionId(), subscriptionUpdate, e); } } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationUpdate.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationUpdate.java index 6913ef7cb7..b00231a5c9 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationUpdate.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationUpdate.java @@ -19,17 +19,18 @@ import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import org.thingsboard.server.common.data.id.NotificationId; import org.thingsboard.server.common.data.notification.Notification; import org.thingsboard.server.common.data.notification.NotificationStatus; +import java.util.UUID; + @Data @NoArgsConstructor @AllArgsConstructor @Builder public class NotificationUpdate { - private NotificationId notificationId; + private UUID notificationId; private boolean created; private Notification notification; 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 9f905c3c4f..a9191c07fe 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 @@ -15,12 +15,10 @@ */ package org.thingsboard.server.service.notification; -import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.assertj.core.data.Offset; import org.java_websocket.client.WebSocketClient; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.thingsboard.rule.engine.api.NotificationCenter; @@ -39,7 +37,6 @@ import org.thingsboard.server.common.data.notification.NotificationType; import org.thingsboard.server.common.data.notification.settings.NotificationSettings; import org.thingsboard.server.common.data.notification.settings.SlackNotificationDeliveryMethodConfig; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; -import org.thingsboard.server.common.data.notification.targets.platform.AllUsersFilter; import org.thingsboard.server.common.data.notification.targets.platform.CustomerUsersFilter; import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig; import org.thingsboard.server.common.data.notification.targets.platform.UserListFilter; @@ -52,8 +49,6 @@ import org.thingsboard.server.common.data.notification.template.NotificationTemp import org.thingsboard.server.common.data.notification.template.SlackDeliveryMethodNotificationTemplate; import org.thingsboard.server.common.data.notification.template.SmsDeliveryMethodNotificationTemplate; import org.thingsboard.server.common.data.notification.template.WebDeliveryMethodNotificationTemplate; -import org.thingsboard.server.common.data.page.PageData; -import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.notification.NotificationDao; @@ -66,8 +61,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.InstanceOfAssertFactories.type; @@ -377,7 +370,7 @@ public class NotificationApiTest extends AbstractNotificationApiTest { WebDeliveryMethodNotificationTemplate webNotificationTemplate = new WebDeliveryMethodNotificationTemplate(); webNotificationTemplate.setEnabled(true); - webNotificationTemplate.setBody("Message for WEB: ${recipientEmail}"); + webNotificationTemplate.setBody("Message for WEB: ${recipientEmail} ${unknownParam}"); webNotificationTemplate.setSubject("Subject for WEB: ${recipientEmail}"); templates.put(NotificationDeliveryMethod.WEB, webNotificationTemplate); @@ -416,7 +409,7 @@ public class NotificationApiTest extends AbstractNotificationApiTest { .satisfies(template -> { assertThat(template.getBody()) .startsWith("Message for WEB") - .endsWith(requestorEmail); + .endsWith(requestorEmail + " ${unknownParam}"); assertThat(template.getSubject()) .startsWith("Subject for WEB") .endsWith(requestorEmail); @@ -439,7 +432,7 @@ public class NotificationApiTest extends AbstractNotificationApiTest { assertThat(processedTemplates.get(NotificationDeliveryMethod.SLACK)).asInstanceOf(type(SlackDeliveryMethodNotificationTemplate.class)) .satisfies(template -> { assertThat(template.getBody()) - .isEqualTo("Message for SLACK: "); // ${recipientEmail} should be removed + .isEqualTo("Message for SLACK: ${recipientEmail}"); // ${recipientEmail} should not be processed }); } @@ -476,53 +469,6 @@ public class NotificationApiTest extends AbstractNotificationApiTest { } @Test - @Ignore - public void testNotificationsForALotOfUsers() throws Exception { - int usersCount = 5000; - - List users = new ArrayList<>(); - for (int i = 1; i <= usersCount; i++) { - User user = new User(); - user.setTenantId(tenantId); - user.setAuthority(Authority.TENANT_ADMIN); - user.setEmail("test-user-" + i + "@thingsboard.org"); - user = doPost("/api/user", user, User.class); - users.add(user); - } - - NotificationTarget notificationTarget = new NotificationTarget(); - notificationTarget.setTenantId(tenantId); - notificationTarget.setName("All my users"); - PlatformUsersNotificationTargetConfig config = new PlatformUsersNotificationTargetConfig(); - AllUsersFilter filter = new AllUsersFilter(); - config.setUsersFilter(filter); - notificationTarget.setConfiguration(config); - notificationTarget = saveNotificationTarget(notificationTarget); - NotificationTargetId notificationTargetId = notificationTarget.getId(); - - ListenableFuture request = executor.submit(() -> { - return submitNotificationRequest(notificationTargetId, "Hello, ${recipientEmail}", 0, NotificationDeliveryMethod.WEB); - }); - await().atMost(10, TimeUnit.SECONDS).until(request::isDone); - NotificationRequest notificationRequest = request.get(); - - await().atMost(5, TimeUnit.SECONDS) - .pollInterval(200, TimeUnit.MILLISECONDS) - .until(() -> { - PageData sentNotifications = notificationDao.findByRequestId(tenantId, notificationRequest.getId(), new PageLink(1)); - return sentNotifications.getTotalElements() >= usersCount; - }); - - PageData sentNotifications = notificationDao.findByRequestId(tenantId, notificationRequest.getId(), new PageLink(Integer.MAX_VALUE)); - assertThat(sentNotifications.getData()).extracting(Notification::getRecipientId) - .containsAll(users.stream().map(User::getId).collect(Collectors.toSet())); - - NotificationRequestStats stats = getStats(notificationRequest.getId()); - assertThat(stats.getSent().values().stream().mapToInt(AtomicInteger::get).sum()).isGreaterThanOrEqualTo(usersCount); - } - - @Test - @Ignore public void testSlackNotifications() throws Exception { NotificationSettings settings = new NotificationSettings(); SlackNotificationDeliveryMethodConfig slackConfig = new SlackNotificationDeliveryMethodConfig(); @@ -559,7 +505,7 @@ public class NotificationApiTest extends AbstractNotificationApiTest { NotificationRequest successfulNotificationRequest = submitNotificationRequest(List.of(notificationTarget.getId()), notificationTemplate.getId(), 0); await().atMost(2, TimeUnit.SECONDS) .until(() -> findNotificationRequest(successfulNotificationRequest.getId()).isSent()); - verify(slackService).sendMessage(eq(tenantId), eq(slackToken), eq(conversationId), eq("To Slack :) ")); + verify(slackService).sendMessage(eq(tenantId), eq(slackToken), eq(conversationId), eq(slackNotificationTemplate.getBody())); NotificationRequestStats stats = getStats(successfulNotificationRequest.getId()); assertThat(stats.getSent().get(NotificationDeliveryMethod.SLACK)).hasValue(1); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.java index 9a3d0775e9..ecb7f2d2b6 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.java @@ -25,7 +25,6 @@ import lombok.NoArgsConstructor; import org.apache.commons.lang3.StringUtils; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; -import javax.validation.constraints.AssertTrue; import javax.validation.constraints.NotEmpty; @JsonIgnoreProperties(ignoreUnknown = true) @@ -55,4 +54,8 @@ public abstract class DeliveryMethodNotificationTemplate { @JsonIgnore public abstract DeliveryMethodNotificationTemplate copy(); + public boolean containsAny(String... params) { + return StringUtils.containsAny(body, params); + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/EmailDeliveryMethodNotificationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/EmailDeliveryMethodNotificationTemplate.java index 70c991481b..bf8aca7913 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/EmailDeliveryMethodNotificationTemplate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/EmailDeliveryMethodNotificationTemplate.java @@ -19,6 +19,7 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.ToString; +import org.apache.commons.lang3.StringUtils; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; import javax.validation.constraints.NotEmpty; @@ -47,4 +48,9 @@ public class EmailDeliveryMethodNotificationTemplate extends DeliveryMethodNotif return new EmailDeliveryMethodNotificationTemplate(this); } + @Override + public boolean containsAny(String... params) { + return super.containsAny(params) || StringUtils.containsAny(subject, params); + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/WebDeliveryMethodNotificationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/WebDeliveryMethodNotificationTemplate.java index 8740e3a403..ac1e0cfd8c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/WebDeliveryMethodNotificationTemplate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/WebDeliveryMethodNotificationTemplate.java @@ -15,14 +15,19 @@ */ package org.thingsboard.server.common.data.notification.template; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.ToString; +import org.apache.commons.lang3.StringUtils; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; import javax.validation.constraints.NotEmpty; +import java.util.Optional; @Data @NoArgsConstructor @@ -37,7 +42,44 @@ public class WebDeliveryMethodNotificationTemplate extends DeliveryMethodNotific public WebDeliveryMethodNotificationTemplate(WebDeliveryMethodNotificationTemplate other) { super(other); this.subject = other.subject; - this.additionalConfig = other.additionalConfig; + this.additionalConfig = other.additionalConfig != null ? other.additionalConfig.deepCopy() : null; + } + + @JsonIgnore + public String getButtonText() { + return getButtonConfigProperty("text"); + } + + @JsonIgnore + public void setButtonText(String buttonText) { + getButtonConfig().ifPresent(buttonConfig -> { + buttonConfig.set("text", new TextNode(buttonText)); + }); + } + + @JsonIgnore + public String getButtonLink() { + return getButtonConfigProperty("link"); + } + + @JsonIgnore + public void setButtonLink(String buttonLink) { + getButtonConfig().ifPresent(buttonConfig -> { + buttonConfig.set("link", new TextNode(buttonLink)); + }); + } + + private String getButtonConfigProperty(String property) { + return getButtonConfig() + .map(buttonConfig -> buttonConfig.get(property)) + .filter(JsonNode::isTextual) + .map(JsonNode::asText).orElse(null); + } + + private Optional getButtonConfig() { + return Optional.ofNullable(additionalConfig) + .map(config -> config.get("actionButtonConfig")).filter(JsonNode::isObject) + .map(config -> (ObjectNode) config); } @Override @@ -50,4 +92,10 @@ public class WebDeliveryMethodNotificationTemplate extends DeliveryMethodNotific return new WebDeliveryMethodNotificationTemplate(this); } + @Override + public boolean containsAny(String... params) { + return super.containsAny(params) || StringUtils.containsAny(subject, params) + || StringUtils.containsAny(getButtonText(), params) || StringUtils.containsAny(getButtonLink(), params); + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRepository.java index 4ab88095e5..c08a3c290e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRepository.java @@ -63,7 +63,8 @@ public interface NotificationRepository extends JpaRepository :status") int updateStatusByRecipientId(@Param("recipientId") UUID recipientId, @Param("status") NotificationStatus status);