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 6c27887e1a..d36eb9913d 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java @@ -64,7 +64,9 @@ import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; import javax.validation.Valid; +import java.util.Comparator; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -201,9 +203,6 @@ public class NotificationController extends BaseController { public NotificationRequestPreview getNotificationRequestPreview(@RequestBody @Valid NotificationRequest request, @RequestParam(defaultValue = "20") int recipientsPreviewSize, @AuthenticationPrincipal SecurityUser user) throws ThingsboardException { - NotificationRequestPreview preview = new NotificationRequestPreview(); - - request.setOriginatorEntityId(user.getId()); NotificationTemplate template; if (request.getTemplateId() != null) { template = checkEntityId(request.getTemplateId(), notificationTemplateService::findNotificationTemplateById, Operation.READ); @@ -213,33 +212,23 @@ public class NotificationController extends BaseController { if (template == null) { throw new IllegalArgumentException("Template is missing"); } - NotificationProcessingContext tmpProcessingCtx = NotificationProcessingContext.builder() - .tenantId(user.getTenantId()) - .request(request) - .template(template) - .settings(null) - .build(); + request.setOriginatorEntityId(user.getId()); + List targets = request.getTargets().stream() + .map(NotificationTargetId::new) + .map(targetId -> notificationTargetService.findNotificationTargetById(user.getTenantId(), targetId)) + .sorted(Comparator.comparing(target -> target.getConfiguration().getType())) + .collect(Collectors.toList()); - Map processedTemplates = tmpProcessingCtx.getDeliveryMethods().stream() - .collect(Collectors.toMap(m -> m, deliveryMethod -> { - NotificationRecipient recipient = null; - if (NotificationTargetType.PLATFORM_USERS.getSupportedDeliveryMethods().contains(deliveryMethod)) { - recipient = userService.findUserById(user.getTenantId(), user.getId()); - } - return tmpProcessingCtx.getProcessedTemplate(deliveryMethod, recipient); - })); - preview.setProcessedTemplates(processedTemplates); + NotificationRequestPreview preview = new NotificationRequestPreview(); - // generic permission Set recipientsPreview = new LinkedHashSet<>(); - Map recipientsCountByTarget = new HashMap<>(); - - List targets = notificationTargetService.findNotificationTargetsByTenantIdAndIds(user.getTenantId(), - request.getTargets().stream().map(NotificationTargetId::new).collect(Collectors.toList())); + Map recipientsCountByTarget = new LinkedHashMap<>(); + Map firstRecipient = new HashMap<>(); for (NotificationTarget target : targets) { int recipientsCount; List recipientsPart; - if (target.getConfiguration().getType() == NotificationTargetType.PLATFORM_USERS) { + NotificationTargetType targetType = target.getConfiguration().getType(); + if (targetType == NotificationTargetType.PLATFORM_USERS) { PageData recipients = notificationTargetService.findRecipientsForNotificationTargetConfig(user.getTenantId(), (PlatformUsersNotificationTargetConfig) target.getConfiguration(), new PageLink(recipientsPreviewSize)); recipientsCount = (int) recipients.getTotalElements(); @@ -248,7 +237,7 @@ public class NotificationController extends BaseController { recipientsCount = 1; recipientsPart = List.of(((SlackNotificationTargetConfig) target.getConfiguration()).getConversation()); } - + firstRecipient.putIfAbsent(targetType, !recipientsPart.isEmpty() ? recipientsPart.get(0) : null); for (NotificationRecipient recipient : recipientsPart) { if (recipientsPreview.size() < recipientsPreviewSize) { recipientsPreview.add(recipient.getTitle()); @@ -258,11 +247,23 @@ public class NotificationController extends BaseController { } recipientsCountByTarget.put(target.getName(), recipientsCount); } - preview.setRecipientsPreview(recipientsPreview); preview.setRecipientsCountByTarget(recipientsCountByTarget); preview.setTotalRecipientsCount(recipientsCountByTarget.values().stream().mapToInt(Integer::intValue).sum()); + NotificationProcessingContext ctx = NotificationProcessingContext.builder() + .tenantId(user.getTenantId()) + .request(request) + .template(template) + .settings(null) + .build(); + Map processedTemplates = ctx.getDeliveryMethods().stream() + .collect(Collectors.toMap(m -> m, deliveryMethod -> { + NotificationTargetType targetType = NotificationTargetType.forDeliveryMethod(deliveryMethod); + return ctx.getProcessedTemplate(deliveryMethod, firstRecipient.get(targetType)); + })); + preview.setProcessedTemplates(processedTemplates); + return preview; } 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 4fd6b93022..6374b65710 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java @@ -71,7 +71,7 @@ public class NotificationTargetController extends BaseController { private final NotificationTargetService notificationTargetService; @ApiOperation(value = "Save notification target (saveNotificationTarget)", - notes = "Create or update notification target.\n\n" + + notes = "Create or update notification target." + 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 b4fba04212..fa7e973843 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 @@ -130,7 +130,7 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple } if (ruleId == null) { if (targets.stream().noneMatch(target -> target.getConfiguration().getType().getSupportedDeliveryMethods().contains(deliveryMethod))) { - throw new IllegalArgumentException("Target for " + deliveryMethod.getName() + " delivery method is missing"); + throw new IllegalArgumentException("Recipients for " + deliveryMethod.getName() + " delivery method not chosen"); } } }); 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 9dd34296ef..f6a296ab7e 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 @@ -40,6 +40,7 @@ import org.thingsboard.server.common.data.notification.targets.platform.Customer import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig; import org.thingsboard.server.common.data.notification.targets.platform.UserListFilter; import org.thingsboard.server.common.data.notification.targets.slack.SlackConversation; +import org.thingsboard.server.common.data.notification.targets.slack.SlackConversationType; import org.thingsboard.server.common.data.notification.targets.slack.SlackNotificationTargetConfig; import org.thingsboard.server.common.data.notification.template.DeliveryMethodNotificationTemplate; import org.thingsboard.server.common.data.notification.template.EmailDeliveryMethodNotificationTemplate; @@ -49,7 +50,6 @@ import org.thingsboard.server.common.data.notification.template.SlackDeliveryMet import org.thingsboard.server.common.data.notification.template.SmsDeliveryMethodNotificationTemplate; import org.thingsboard.server.common.data.notification.template.WebDeliveryMethodNotificationTemplate; import org.thingsboard.server.common.data.security.Authority; -import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.notification.NotificationDao; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.service.executors.DbCallbackExecutorService; @@ -226,13 +226,13 @@ public class NotificationApiTest extends AbstractNotificationApiTest { NotificationRequest notificationRequest = submitNotificationRequest(notificationTarget.getId(), notificationText, 5); assertThat(notificationRequest.getStatus()).isEqualTo(NotificationRequestStatus.SCHEDULED); await().atLeast(4, TimeUnit.SECONDS) - .atMost(6, TimeUnit.SECONDS) + .atMost(15, TimeUnit.SECONDS) .until(() -> wsClient.getLastMsg() != null); Notification delayedNotification = wsClient.getLastDataUpdate().getUpdate(); assertThat(delayedNotification).extracting(Notification::getText).isEqualTo(notificationText); assertThat(delayedNotification.getCreatedTime() - notificationRequest.getCreatedTime()) - .isCloseTo(TimeUnit.SECONDS.toMillis(5), Offset.offset(500L)); + .isCloseTo(TimeUnit.SECONDS.toMillis(5), Offset.offset(10000L)); assertThat(findNotificationRequest(notificationRequest.getId()).getStatus()).isEqualTo(NotificationRequestStatus.SENT); } @@ -324,16 +324,17 @@ public class NotificationApiTest extends AbstractNotificationApiTest { @Test public void testNotificationRequestPreview() throws Exception { - NotificationTarget target1 = new NotificationTarget(); - target1.setName("Me"); - PlatformUsersNotificationTargetConfig target1Config = new PlatformUsersNotificationTargetConfig(); + NotificationTarget tenantAdminTarget = new NotificationTarget(); + tenantAdminTarget.setName("Me"); + PlatformUsersNotificationTargetConfig tenantAdminTargetConfig = new PlatformUsersNotificationTargetConfig(); UserListFilter userListFilter = new UserListFilter(); - userListFilter.setUsersIds(DaoUtil.toUUIDs(List.of(tenantAdminUserId))); - target1Config.setUsersFilter(userListFilter); - target1.setConfiguration(target1Config); - target1 = saveNotificationTarget(target1); + userListFilter.setUsersIds(List.of(tenantAdminUserId.getId())); + tenantAdminTargetConfig.setUsersFilter(userListFilter); + tenantAdminTarget.setConfiguration(tenantAdminTargetConfig); + tenantAdminTarget = saveNotificationTarget(tenantAdminTarget); List recipients = new ArrayList<>(); recipients.add(TENANT_ADMIN_EMAIL); + String firstRecipientEmail = TENANT_ADMIN_EMAIL; createDifferentCustomer(); loginTenantAdmin(); @@ -347,21 +348,32 @@ public class NotificationApiTest extends AbstractNotificationApiTest { customerUser = createUser(customerUser, "12345678"); recipients.add(customerUser.getEmail()); } - NotificationTarget target2 = new NotificationTarget(); - target2.setName("Other customer users"); - PlatformUsersNotificationTargetConfig target2Config = new PlatformUsersNotificationTargetConfig(); + NotificationTarget customerUsersTarget = new NotificationTarget(); + customerUsersTarget.setName("Other customer users"); + PlatformUsersNotificationTargetConfig customerUsersTargetConfig = new PlatformUsersNotificationTargetConfig(); CustomerUsersFilter customerUsersFilter = new CustomerUsersFilter(); customerUsersFilter.setCustomerId(differentCustomerId.getId()); - target2Config.setUsersFilter(customerUsersFilter); - target2.setConfiguration(target2Config); - target2 = saveNotificationTarget(target2); - + customerUsersTargetConfig.setUsersFilter(customerUsersFilter); + customerUsersTarget.setConfiguration(customerUsersTargetConfig); + customerUsersTarget = saveNotificationTarget(customerUsersTarget); + + NotificationTarget slackTarget = new NotificationTarget(); + slackTarget.setName("Slack user"); + SlackNotificationTargetConfig slackTargetConfig = new SlackNotificationTargetConfig(); + slackTargetConfig.setConversationType(SlackConversationType.DIRECT); + SlackConversation slackConversation = new SlackConversation(); + slackConversation.setId("U1234567"); + slackConversation.setTitle("@jdoe (John Doe)"); + slackConversation.setWholeName("John Doe"); + slackTargetConfig.setConversation(slackConversation); + slackTarget.setConfiguration(slackTargetConfig); + slackTarget = saveNotificationTarget(slackTarget); + recipients.add(slackConversation.getTitle()); NotificationTemplate notificationTemplate = new NotificationTemplate(); notificationTemplate.setNotificationType(NotificationType.GENERAL); notificationTemplate.setName("Test template"); - String requestorEmail = TENANT_ADMIN_EMAIL; NotificationTemplateConfig templateConfig = new NotificationTemplateConfig(); HashMap templates = new HashMap<>(); templateConfig.setDeliveryMethodsTemplates(templates); @@ -369,69 +381,59 @@ public class NotificationApiTest extends AbstractNotificationApiTest { WebDeliveryMethodNotificationTemplate webNotificationTemplate = new WebDeliveryMethodNotificationTemplate(); webNotificationTemplate.setEnabled(true); - webNotificationTemplate.setBody("Message for WEB: ${recipientEmail} ${unknownParam}"); - webNotificationTemplate.setSubject("Subject for WEB: ${recipientEmail}"); + webNotificationTemplate.setSubject("WEB SUBJECT: ${recipientEmail}"); + webNotificationTemplate.setBody("WEB: ${recipientEmail} ${unknownParam}"); templates.put(NotificationDeliveryMethod.WEB, webNotificationTemplate); SmsDeliveryMethodNotificationTemplate smsNotificationTemplate = new SmsDeliveryMethodNotificationTemplate(); smsNotificationTemplate.setEnabled(true); - smsNotificationTemplate.setBody("Message for SMS: ${recipientEmail}"); + smsNotificationTemplate.setBody("SMS: ${recipientEmail}"); templates.put(NotificationDeliveryMethod.SMS, smsNotificationTemplate); EmailDeliveryMethodNotificationTemplate emailNotificationTemplate = new EmailDeliveryMethodNotificationTemplate(); emailNotificationTemplate.setEnabled(true); - emailNotificationTemplate.setSubject("Subject for EMAIL: ${recipientEmail}"); - emailNotificationTemplate.setBody("Message for EMAIL: ${recipientEmail}"); + emailNotificationTemplate.setSubject("EMAIL SUBJECT: ${recipientEmail}"); + emailNotificationTemplate.setBody("EMAIL: ${recipientEmail}"); templates.put(NotificationDeliveryMethod.EMAIL, emailNotificationTemplate); SlackDeliveryMethodNotificationTemplate slackNotificationTemplate = new SlackDeliveryMethodNotificationTemplate(); slackNotificationTemplate.setEnabled(true); - slackNotificationTemplate.setBody("Message for SLACK: ${recipientEmail}"); + slackNotificationTemplate.setBody("SLACK: ${recipientFirstName} ${recipientLastName}"); templates.put(NotificationDeliveryMethod.SLACK, slackNotificationTemplate); notificationTemplate = saveNotificationTemplate(notificationTemplate); - NotificationRequest notificationRequest = new NotificationRequest(); - notificationRequest.setTargets(List.of(target1.getUuidId(), target2.getUuidId())); + notificationRequest.setTargets(List.of(tenantAdminTarget.getUuidId(), customerUsersTarget.getUuidId(), slackTarget.getUuidId())); notificationRequest.setTemplateId(notificationTemplate.getId()); notificationRequest.setAdditionalConfig(new NotificationRequestConfig()); NotificationRequestPreview preview = doPost("/api/notification/request/preview", notificationRequest, NotificationRequestPreview.class); - assertThat(preview.getRecipientsCountByTarget().get(target1.getName())).isEqualTo(1); - assertThat(preview.getRecipientsCountByTarget().get(target2.getName())).isEqualTo(customerUsersCount); - assertThat(preview.getTotalRecipientsCount()).isEqualTo(1 + customerUsersCount); + assertThat(preview.getRecipientsCountByTarget().get(tenantAdminTarget.getName())).isEqualTo(1); + assertThat(preview.getRecipientsCountByTarget().get(customerUsersTarget.getName())).isEqualTo(customerUsersCount); + assertThat(preview.getRecipientsCountByTarget().get(slackTarget.getName())).isEqualTo(1); + + assertThat(preview.getTotalRecipientsCount()).isEqualTo(2 + customerUsersCount); assertThat(preview.getRecipientsPreview()).containsAll(recipients); Map processedTemplates = preview.getProcessedTemplates(); assertThat(processedTemplates.get(NotificationDeliveryMethod.WEB)).asInstanceOf(type(WebDeliveryMethodNotificationTemplate.class)) .satisfies(template -> { - assertThat(template.getBody()) - .startsWith("Message for WEB") - .endsWith(requestorEmail + " ${unknownParam}"); - assertThat(template.getSubject()) - .startsWith("Subject for WEB") - .endsWith(requestorEmail); + assertThat(template.getSubject()).isEqualTo("WEB SUBJECT: " + firstRecipientEmail); + assertThat(template.getBody()).isEqualTo("WEB: " + firstRecipientEmail + " ${unknownParam}"); }); assertThat(processedTemplates.get(NotificationDeliveryMethod.SMS)).asInstanceOf(type(SmsDeliveryMethodNotificationTemplate.class)) .satisfies(template -> { - assertThat(template.getBody()) - .startsWith("Message for SMS") - .endsWith(requestorEmail); + assertThat(template.getBody()).isEqualTo("SMS: " + firstRecipientEmail); }); assertThat(processedTemplates.get(NotificationDeliveryMethod.EMAIL)).asInstanceOf(type(EmailDeliveryMethodNotificationTemplate.class)) .satisfies(template -> { - assertThat(template.getBody()) - .startsWith("Message for EMAIL") - .endsWith(requestorEmail); - assertThat(template.getSubject()) - .startsWith("Subject for EMAIL") - .endsWith(requestorEmail); + assertThat(template.getSubject()).isEqualTo("EMAIL SUBJECT: " + firstRecipientEmail); + assertThat(template.getBody()).isEqualTo("EMAIL: " + firstRecipientEmail); }); assertThat(processedTemplates.get(NotificationDeliveryMethod.SLACK)).asInstanceOf(type(SlackDeliveryMethodNotificationTemplate.class)) .satisfies(template -> { - assertThat(template.getBody()) - .isEqualTo("Message for SLACK: ${recipientEmail}"); // ${recipientEmail} should not be processed + assertThat(template.getBody()).isEqualTo("SLACK: John Doe"); }); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetType.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetType.java index 0c2441e37d..1254654ecf 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetType.java @@ -19,6 +19,7 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; +import java.util.Arrays; import java.util.Set; @RequiredArgsConstructor @@ -30,4 +31,10 @@ public enum NotificationTargetType { @Getter private final Set supportedDeliveryMethods; + public static NotificationTargetType forDeliveryMethod(NotificationDeliveryMethod deliveryMethod) { + return Arrays.stream(values()) + .filter(targetType -> targetType.getSupportedDeliveryMethods().contains(deliveryMethod)) + .findFirst().orElse(null); + } + }