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 8fd4688151..0ab6cdf898 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java @@ -33,22 +33,32 @@ import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.NotificationId; import org.thingsboard.server.common.data.id.NotificationRequestId; +import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.Notification; +import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; import org.thingsboard.server.common.data.notification.NotificationRequest; +import org.thingsboard.server.common.data.notification.NotificationRequestPreview; import org.thingsboard.server.common.data.notification.settings.NotificationSettings; +import org.thingsboard.server.common.data.notification.template.DeliveryMethodNotificationTemplate; +import org.thingsboard.server.common.data.notification.template.NotificationTemplate; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.notification.NotificationRequestService; import org.thingsboard.server.dao.notification.NotificationService; import org.thingsboard.server.dao.notification.NotificationSettingsService; +import org.thingsboard.server.dao.notification.NotificationTargetService; +import org.thingsboard.server.dao.notification.NotificationTemplateService; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.notification.NotificationProcessingContext; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; import javax.validation.Valid; +import java.util.Map; import java.util.UUID; +import java.util.stream.Collectors; @RestController @TbCoreComponent @@ -59,6 +69,8 @@ public class NotificationController extends BaseController { private final NotificationService notificationService; private final NotificationRequestService notificationRequestService; + private final NotificationTemplateService notificationTemplateService; + private final NotificationTargetService notificationTargetService; private final NotificationCenter notificationCenter; private final NotificationSettingsService notificationSettingsService; @@ -112,6 +124,39 @@ public class NotificationController extends BaseController { return doSaveAndLog(EntityType.NOTIFICATION_REQUEST, notificationRequest, notificationCenter::processNotificationRequest); } + @PostMapping("/notification/request/preview") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + public NotificationRequestPreview getNotificationRequestPreview(@RequestBody @Valid NotificationRequest notificationRequest, + @AuthenticationPrincipal SecurityUser user) { + NotificationRequestPreview preview = new NotificationRequestPreview(); + + notificationRequest.setOriginatorEntityId(user.getId()); + NotificationTemplate notificationTemplate = notificationTemplateService.findNotificationTemplateById(user.getTenantId(), notificationRequest.getTemplateId()); + NotificationProcessingContext mockProcessingCtx = NotificationProcessingContext.builder() + .tenantId(user.getTenantId()) + .request(notificationRequest) + .settings(null) + .template(notificationTemplate) + .build(); + mockProcessingCtx.init(); + + Map templateContext = mockProcessingCtx.createTemplateContext(user); + Map processedTemplates = mockProcessingCtx.getDeliveryMethods().stream() + .collect(Collectors.toMap(m -> m, deliveryMethod -> { + return mockProcessingCtx.getProcessedTemplate(deliveryMethod, templateContext); + })); + preview.setProcessedTemplates(processedTemplates); + + Map recipientsCountByTarget = notificationRequest.getTargets().stream() + .collect(Collectors.toMap(id -> id, targetId -> { + return notificationTargetService.countRecipientsForNotificationTarget(user.getTenantId(), new NotificationTargetId(targetId)); + })); + preview.setRecipientsCountByTarget(recipientsCountByTarget); + preview.setTotalRecipientsCount(recipientsCountByTarget.values().stream().mapToInt(Integer::intValue).sum()); + + return preview; + } + @GetMapping("/notification/request/{id}") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") public NotificationRequest getNotificationRequestById(@PathVariable UUID id) throws ThingsboardException { 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 502bded58c..e8edac2e6c 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 @@ -120,7 +120,7 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple } } - notificationRequest.setStatus(NotificationRequestStatus.SENT); + notificationRequest.setStatus(NotificationRequestStatus.PROCESSING); NotificationRequest savedNotificationRequest = notificationRequestService.saveNotificationRequest(tenantId, notificationRequest); NotificationProcessingContext ctx = NotificationProcessingContext.builder() @@ -134,9 +134,9 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple Set deliveryMethods = ctx.getDeliveryMethods(); List> results = new ArrayList<>(); - for (NotificationTargetId targetId : notificationRequest.getTargets()) { + for (UUID targetId : notificationRequest.getTargets()) { DaoUtil.processBatches(pageLink -> { - return notificationTargetService.findRecipientsForNotificationTarget(tenantId, ctx.getCustomerId(), targetId, pageLink); + return notificationTargetService.findRecipientsForNotificationTarget(tenantId, ctx.getCustomerId(), new NotificationTargetId(targetId), pageLink); }, 200, recipientsBatch -> { for (NotificationDeliveryMethod deliveryMethod : deliveryMethods) { if (deliveryMethod.isIndependent()) continue; @@ -172,7 +172,8 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple Futures.whenAllComplete(results).run(() -> { NotificationRequestStats stats = ctx.getStats(); try { - notificationRequestService.updateNotificationRequestStats(tenantId, savedNotificationRequest.getId(), stats); + notificationRequestService.updateNotificationRequest(tenantId, savedNotificationRequest.getId(), + NotificationRequestStatus.SENT, stats); } catch (Exception e) { log.error("Failed to update stats for notification request {}", savedNotificationRequest.getId(), e); } diff --git a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationRuleProcessingService.java b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationRuleProcessingService.java index 588a333caf..9a32a5d458 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationRuleProcessingService.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationRuleProcessingService.java @@ -30,6 +30,7 @@ import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.common.data.notification.info.AlarmOriginatedNotificationInfo; import org.thingsboard.server.common.data.notification.info.NotificationInfo; import org.thingsboard.server.common.data.notification.NotificationRequest; @@ -46,6 +47,7 @@ import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.executors.NotificationExecutorService; import java.util.List; +import java.util.stream.Collectors; @Service @TbCoreComponent @@ -131,7 +133,7 @@ public class DefaultNotificationRuleProcessingService implements NotificationRul NotificationInfo notificationInfo = constructNotificationInfo(alarm); NotificationRequest notificationRequest = NotificationRequest.builder() .tenantId(tenantId) - .targets(List.of(targetId)) + .targets(List.of(targetId).stream().map(UUIDBased::getId).collect(Collectors.toList())) .templateId(notificationRule.getTemplateId()) .additionalConfig(config) .info(notificationInfo) 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 39188679e3..cf190423b6 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 @@ -90,7 +90,7 @@ public class NotificationProcessingContext { return (C) settings.getDeliveryMethodsConfigs().get(deliveryMethod); } - protected T getProcessedTemplate(NotificationDeliveryMethod deliveryMethod, Map templateContext) { + public T getProcessedTemplate(NotificationDeliveryMethod deliveryMethod, Map templateContext) { if (request.getInfo() != null) { templateContext = new HashMap<>(templateContext); templateContext.putAll(request.getInfo().getTemplateData()); diff --git a/application/src/test/java/org/thingsboard/server/service/notification/AbstractNotificationApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/AbstractNotificationApiTest.java index 348fdf0e11..90d81af64c 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/AbstractNotificationApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/AbstractNotificationApiTest.java @@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.id.NotificationTemplateId; +import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.Notification; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; @@ -53,6 +54,7 @@ import java.net.URISyntaxException; import java.util.HashMap; import java.util.List; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -107,8 +109,7 @@ public abstract class AbstractNotificationApiTest extends AbstractControllerTest UserOriginatedNotificationInfo notificationInfo = new UserOriginatedNotificationInfo(); notificationInfo.setDescription("My description"); NotificationRequest notificationRequest = NotificationRequest.builder() - .tenantId(tenantId) - .targets(targets) + .targets(targets.stream().map(UUIDBased::getId).collect(Collectors.toList())) .templateId(notificationTemplateId) .info(notificationInfo) .additionalConfig(config) 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 4282091ecd..820650d911 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 @@ -28,6 +28,8 @@ import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.notification.Notification; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; import org.thingsboard.server.common.data.notification.NotificationRequest; +import org.thingsboard.server.common.data.notification.NotificationRequestConfig; +import org.thingsboard.server.common.data.notification.NotificationRequestPreview; import org.thingsboard.server.common.data.notification.NotificationRequestStats; import org.thingsboard.server.common.data.notification.NotificationRequestStatus; import org.thingsboard.server.common.data.notification.NotificationType; @@ -35,14 +37,21 @@ 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.AllUsersNotificationTargetConfig; +import org.thingsboard.server.common.data.notification.targets.CustomerUsersNotificationTargetConfig; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; +import org.thingsboard.server.common.data.notification.targets.UserListNotificationTargetConfig; +import org.thingsboard.server.common.data.notification.template.DeliveryMethodNotificationTemplate; +import org.thingsboard.server.common.data.notification.template.EmailDeliveryMethodNotificationTemplate; 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.PushDeliveryMethodNotificationTemplate; import org.thingsboard.server.common.data.notification.template.SlackConversation; import org.thingsboard.server.common.data.notification.template.SlackDeliveryMethodNotificationTemplate; +import org.thingsboard.server.common.data.notification.template.SmsDeliveryMethodNotificationTemplate; 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; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.service.executors.DbCallbackExecutorService; @@ -59,6 +68,7 @@ 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; import static org.awaitility.Awaitility.await; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -271,7 +281,7 @@ public class NotificationApiTest extends AbstractNotificationApiTest { @Test public void testNotificationUpdatesForALotOfUsers() throws Exception { - int usersCount = 200; // FIXME: sometimes if set e.g. to 150, up to 5 WS sessions don't receive update + int usersCount = 100; Map sessions = new HashMap<>(); List targets = new ArrayList<>(); @@ -321,7 +331,7 @@ public class NotificationApiTest extends AbstractNotificationApiTest { }); await().atMost(2, TimeUnit.SECONDS) - .until(() -> getStats(notificationRequest.getId()) != null); + .until(() -> findNotificationRequest(notificationRequest.getId()).isSent()); NotificationRequestStats stats = getStats(notificationRequest.getId()); assertThat(stats.getSent().get(NotificationDeliveryMethod.PUSH)).hasValue(usersCount); assertThat(stats.getSent().get(NotificationDeliveryMethod.EMAIL)).hasValue(usersCount); @@ -338,6 +348,117 @@ public class NotificationApiTest extends AbstractNotificationApiTest { sessions.values().forEach(WebSocketClient::close); } + @Test + public void testNotificationRequestPreview() throws Exception { + NotificationTarget target1 = new NotificationTarget(); + target1.setName("Me"); + UserListNotificationTargetConfig target1Config = new UserListNotificationTargetConfig(); + target1Config.setUsersIds(DaoUtil.toUUIDs(List.of(tenantAdminUserId))); + target1.setConfiguration(target1Config); + target1 = saveNotificationTarget(target1); + + createDifferentCustomer(); + loginTenantAdmin(); + int customerUsersCount = 10; + for (int i = 0; i < customerUsersCount; i++) { + User customerUser = new User(); + customerUser.setAuthority(Authority.CUSTOMER_USER); + customerUser.setTenantId(tenantId); + customerUser.setCustomerId(differentCustomerId); + customerUser.setEmail("other-customer-" + i + "@thingsboard.org"); + customerUser = createUser(customerUser, "12345678"); + } + NotificationTarget target2 = new NotificationTarget(); + target2.setName("Other customer users"); + CustomerUsersNotificationTargetConfig target2Config = new CustomerUsersNotificationTargetConfig(); + target2Config.setCustomerId(differentCustomerId.getId()); + target2Config.setGetCustomerIdFromOriginatorEntity(false); + target2.setConfiguration(target2Config); + target2 = saveNotificationTarget(target2); + + + NotificationTemplate notificationTemplate = new NotificationTemplate(); + notificationTemplate.setNotificationType(NotificationType.GENERAL); + notificationTemplate.setName("Test template"); + + String requestorEmail = TENANT_ADMIN_EMAIL; + NotificationTemplateConfig templateConfig = new NotificationTemplateConfig(); + templateConfig.setDefaultTextTemplate("Default message for SMS and PUSH: ${email}"); + templateConfig.setNotificationSubject("Default subject for EMAIL: ${email}"); + HashMap templates = new HashMap<>(); + templateConfig.setDeliveryMethodsTemplates(templates); + notificationTemplate.setConfiguration(templateConfig); + + PushDeliveryMethodNotificationTemplate pushNotificationTemplate = new PushDeliveryMethodNotificationTemplate(); + pushNotificationTemplate.setEnabled(true); + // using default message for push + pushNotificationTemplate.setSubject("Subject for PUSH: ${email}"); + templates.put(NotificationDeliveryMethod.PUSH, pushNotificationTemplate); + + SmsDeliveryMethodNotificationTemplate smsNotificationTemplate = new SmsDeliveryMethodNotificationTemplate(); + smsNotificationTemplate.setEnabled(true); + // using default message for sms + templates.put(NotificationDeliveryMethod.SMS, smsNotificationTemplate); + + EmailDeliveryMethodNotificationTemplate emailNotificationTemplate = new EmailDeliveryMethodNotificationTemplate(); + emailNotificationTemplate.setEnabled(true); + emailNotificationTemplate.setBody("Message for EMAIL: ${email}"); + // using default subject for email + templates.put(NotificationDeliveryMethod.EMAIL, emailNotificationTemplate); + + SlackDeliveryMethodNotificationTemplate slackNotificationTemplate = new SlackDeliveryMethodNotificationTemplate(); + slackNotificationTemplate.setEnabled(true); + slackNotificationTemplate.setConversationType(SlackConversation.Type.PUBLIC_CHANNEL); + slackNotificationTemplate.setConversationId("U1234567"); + slackNotificationTemplate.setBody("Message for SLACK: ${email}"); + templates.put(NotificationDeliveryMethod.SLACK, slackNotificationTemplate); + + notificationTemplate = saveNotificationTemplate(notificationTemplate); + + + NotificationRequest notificationRequest = new NotificationRequest(); + notificationRequest.setTargets(List.of(target1.getUuidId(), target2.getUuidId())); + notificationRequest.setTemplateId(notificationTemplate.getId()); + notificationRequest.setAdditionalConfig(new NotificationRequestConfig()); + + NotificationRequestPreview preview = doPost("/api/notification/request/preview", notificationRequest, NotificationRequestPreview.class); + assertThat(preview.getRecipientsCountByTarget().get(target1.getUuidId())).isEqualTo(1); + assertThat(preview.getRecipientsCountByTarget().get(target2.getUuidId())).isEqualTo(customerUsersCount); + assertThat(preview.getTotalRecipientsCount()).isEqualTo(1 + customerUsersCount); + + Map processedTemplates = preview.getProcessedTemplates(); + assertThat(processedTemplates.get(NotificationDeliveryMethod.PUSH)).asInstanceOf(type(PushDeliveryMethodNotificationTemplate.class)) + .satisfies(template -> { + assertThat(template.getBody()) + .startsWith("Default message for SMS and PUSH") + .endsWith(requestorEmail); + assertThat(template.getSubject()) + .startsWith("Subject for PUSH") + .endsWith(requestorEmail); + }); + assertThat(processedTemplates.get(NotificationDeliveryMethod.SMS)).asInstanceOf(type(SmsDeliveryMethodNotificationTemplate.class)) + .satisfies(template -> { + assertThat(template.getBody()) + .startsWith("Default message for SMS and PUSH") + .endsWith(requestorEmail); + }); + assertThat(processedTemplates.get(NotificationDeliveryMethod.EMAIL)).asInstanceOf(type(EmailDeliveryMethodNotificationTemplate.class)) + .satisfies(template -> { + assertThat(template.getBody()) + .startsWith("Message for EMAIL") + .endsWith(requestorEmail); + assertThat(template.getSubject()) + .startsWith("Default subject for EMAIL") + .endsWith(requestorEmail); + }); + assertThat(processedTemplates.get(NotificationDeliveryMethod.SLACK)).asInstanceOf(type(SlackDeliveryMethodNotificationTemplate.class)) + .satisfies(template -> { + assertThat(template.getBody()) + .startsWith("Message for SLACK") + .endsWith(requestorEmail); + }); + } + @Test public void testNotificationRequestStats() throws Exception { wsClient.subscribeForUnreadNotifications(10); @@ -350,7 +471,7 @@ public class NotificationApiTest extends AbstractNotificationApiTest { wsClient.waitForUpdate(); await().atMost(2, TimeUnit.SECONDS) - .until(() -> getStats(notificationRequest.getId()) != null); + .until(() -> findNotificationRequest(notificationRequest.getId()).isSent()); NotificationRequestStats stats = getStats(notificationRequest.getId()); assertThat(stats.getSent().get(NotificationDeliveryMethod.PUSH)).hasValue(1); @@ -434,7 +555,7 @@ public class NotificationApiTest extends AbstractNotificationApiTest { NotificationRequest successfulNotificationRequest = submitNotificationRequest(Collections.emptyList(), notificationTemplate.getId(), 0); await().atMost(2, TimeUnit.SECONDS) - .until(() -> getStats(successfulNotificationRequest.getId()) != null); + .until(() -> findNotificationRequest(successfulNotificationRequest.getId()).isSent()); verify(slackService).sendMessage(eq(tenantId), eq(slackToken), eq(conversationId), eq(config.getDefaultTextTemplate())); NotificationRequestStats stats = getStats(successfulNotificationRequest.getId()); assertThat(stats.getSent().get(NotificationDeliveryMethod.SLACK)).hasValue(1); @@ -443,7 +564,7 @@ public class NotificationApiTest extends AbstractNotificationApiTest { doThrow(new RuntimeException(errorMessage)).when(slackService).sendMessage(any(), any(), any(), any()); NotificationRequest failedNotificationRequest = submitNotificationRequest(Collections.emptyList(), notificationTemplate.getId(), 0); await().atMost(2, TimeUnit.SECONDS) - .until(() -> getStats(failedNotificationRequest.getId()) != null); + .until(() -> findNotificationRequest(failedNotificationRequest.getId()).isSent()); stats = getStats(failedNotificationRequest.getId()); assertThat(stats.getErrors().get(NotificationDeliveryMethod.SLACK).values()).containsExactly(errorMessage); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestService.java index db847a3e73..cbfbe8a408 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestService.java @@ -44,6 +44,6 @@ public interface NotificationRequestService { PageData findScheduledNotificationRequests(PageLink pageLink); - void updateNotificationRequestStats(TenantId tenantId, NotificationRequestId notificationRequestId, NotificationRequestStats stats); + void updateNotificationRequest(TenantId tenantId, NotificationRequestId requestId, NotificationRequestStatus requestStatus, NotificationRequestStats stats); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java index 8ed3565646..d7eb5a8427 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java @@ -32,7 +32,9 @@ public interface NotificationTargetService { PageData findNotificationTargetsByTenantId(TenantId tenantId, PageLink pageLink); - PageData findRecipientsForNotificationTarget(TenantId tenantId, CustomerId customerId, NotificationTargetId notificationTargetId, PageLink pageLink); + PageData findRecipientsForNotificationTarget(TenantId tenantId, CustomerId customerId, NotificationTargetId targetId, PageLink pageLink); + + int countRecipientsForNotificationTarget(TenantId tenantId, NotificationTargetId targetId); PageData findRecipientsForNotificationTargetConfig(TenantId tenantId, CustomerId customerId, NotificationTargetConfig targetConfig, PageLink pageLink); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java index e8e9e967e2..852adbdc60 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java @@ -27,16 +27,15 @@ import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.NotificationRuleId; -import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.id.NotificationTemplateId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.info.NotificationInfo; import javax.validation.Valid; -import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import java.util.List; +import java.util.UUID; @Data @EqualsAndHashCode(callSuper = true) @@ -47,7 +46,7 @@ public class NotificationRequest extends BaseData impleme private TenantId tenantId; @NotNull - private List targets; + private List targets; @NotNull private NotificationTemplateId templateId; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestPreview.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestPreview.java new file mode 100644 index 0000000000..c98afe0c36 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestPreview.java @@ -0,0 +1,31 @@ +/** + * Copyright © 2016-2022 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.common.data.notification; + +import lombok.Data; +import org.thingsboard.server.common.data.notification.template.DeliveryMethodNotificationTemplate; + +import java.util.Map; +import java.util.UUID; + +@Data +public class NotificationRequestPreview { + + private Map processedTemplates; + private int totalRecipientsCount; + private Map recipientsCountByTarget; + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestStatus.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestStatus.java index 7fedd8cdb1..d348fcfde4 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestStatus.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestStatus.java @@ -16,6 +16,7 @@ package org.thingsboard.server.common.data.notification; public enum NotificationRequestStatus { + PROCESSING, SENT, SCHEDULED } 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 a043087730..d243d2ccc0 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 @@ -18,11 +18,13 @@ package org.thingsboard.server.common.data.notification.template; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; +import lombok.ToString; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) public class EmailDeliveryMethodNotificationTemplate extends DeliveryMethodNotificationTemplate implements HasSubject { private String subject; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/PushDeliveryMethodNotificationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/PushDeliveryMethodNotificationTemplate.java index ad0f261afa..408b8086ba 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/PushDeliveryMethodNotificationTemplate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/PushDeliveryMethodNotificationTemplate.java @@ -18,11 +18,13 @@ package org.thingsboard.server.common.data.notification.template; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; +import lombok.ToString; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) public class PushDeliveryMethodNotificationTemplate extends DeliveryMethodNotificationTemplate implements HasSubject { private String subject; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/SlackDeliveryMethodNotificationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/SlackDeliveryMethodNotificationTemplate.java index ac5020bf4b..ad025c1773 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/SlackDeliveryMethodNotificationTemplate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/SlackDeliveryMethodNotificationTemplate.java @@ -18,6 +18,7 @@ package org.thingsboard.server.common.data.notification.template; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; +import lombok.ToString; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; import javax.validation.constraints.NotEmpty; @@ -25,6 +26,7 @@ import javax.validation.constraints.NotEmpty; @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) public class SlackDeliveryMethodNotificationTemplate extends DeliveryMethodNotificationTemplate { private SlackConversation.Type conversationType; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/SmsDeliveryMethodNotificationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/SmsDeliveryMethodNotificationTemplate.java index 7bb937d17f..7d0db7579c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/SmsDeliveryMethodNotificationTemplate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/SmsDeliveryMethodNotificationTemplate.java @@ -18,11 +18,13 @@ package org.thingsboard.server.common.data.notification.template; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; +import lombok.ToString; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) public class SmsDeliveryMethodNotificationTemplate extends DeliveryMethodNotificationTemplate { public SmsDeliveryMethodNotificationTemplate(SmsDeliveryMethodNotificationTemplate other) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRequestEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRequestEntity.java index 2b8f1b32ad..9cf631a118 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRequestEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRequestEntity.java @@ -109,7 +109,7 @@ public class NotificationRequestEntity extends BaseSqlEntity new NotificationTargetId(UUID.fromString(uuid)))); + notificationRequest.setTargets(listFromString(targets, UUID::fromString)); notificationRequest.setTemplateId(getEntityId(templateId, NotificationTemplateId::new)); notificationRequest.setInfo(fromJson(info, NotificationInfo.class)); notificationRequest.setAdditionalConfig(fromJson(additionalConfig, NotificationRequestConfig.class)); diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRequestService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRequestService.java index 5530c4774e..90a7b5148a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRequestService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRequestService.java @@ -79,8 +79,8 @@ public class DefaultNotificationRequestService implements NotificationRequestSer } @Override - public void updateNotificationRequestStats(TenantId tenantId, NotificationRequestId notificationRequestId, NotificationRequestStats stats) { - notificationRequestDao.updateStatsById(tenantId, notificationRequestId, stats); + public void updateNotificationRequest(TenantId tenantId, NotificationRequestId requestId, NotificationRequestStatus requestStatus, NotificationRequestStats stats) { + notificationRequestDao.updateById(tenantId, requestId, requestStatus, stats); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java index e81e165599..89442e5b46 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java @@ -62,13 +62,18 @@ public class DefaultNotificationTargetService implements NotificationTargetServi } @Override - public PageData findRecipientsForNotificationTarget(TenantId tenantId, CustomerId customerId, NotificationTargetId notificationTargetId, PageLink pageLink) { - NotificationTarget notificationTarget = findNotificationTargetById(tenantId, notificationTargetId); - Objects.requireNonNull(notificationTarget, "Notification target [" + notificationTargetId + "] not found"); + public PageData findRecipientsForNotificationTarget(TenantId tenantId, CustomerId customerId, NotificationTargetId targetId, PageLink pageLink) { + NotificationTarget notificationTarget = findNotificationTargetById(tenantId, targetId); + Objects.requireNonNull(notificationTarget, "Notification target [" + targetId + "] not found"); NotificationTargetConfig configuration = notificationTarget.getConfiguration(); return findRecipientsForNotificationTargetConfig(tenantId, customerId, configuration, pageLink); } + @Override + public int countRecipientsForNotificationTarget(TenantId tenantId, NotificationTargetId targetId) { + return (int) findRecipientsForNotificationTarget(tenantId, null, targetId, new PageLink(1)).getTotalElements(); + } + @Override public PageData findRecipientsForNotificationTargetConfig(TenantId tenantId, CustomerId customerId, NotificationTargetConfig targetConfig, PageLink pageLink) { switch (targetConfig.getType()) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java index f415f2737a..ee1e6ab683 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java @@ -41,7 +41,7 @@ public interface NotificationRequestDao extends Dao { PageData findAllByStatus(NotificationRequestStatus status, PageLink pageLink); - void updateStatsById(TenantId tenantId, NotificationRequestId notificationRequestId, NotificationRequestStats stats); + void updateById(TenantId tenantId, NotificationRequestId requestId, NotificationRequestStatus requestStatus, NotificationRequestStats stats); boolean existsByStatusAndTargetId(TenantId tenantId, NotificationRequestStatus status, NotificationTargetId targetId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java index 0b869efd89..2b30d5d8fe 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java @@ -72,8 +72,8 @@ public class JpaNotificationRequestDao extends JpaAbstractDao { @NotEmpty - private List targets; + private List targets; @NotNull private NotificationTemplateId templateId; private NotificationRequestConfig additionalConfig;