Send notifications via Slack independently of the targets
This commit is contained in:
		
							parent
							
								
									6eea14bcf7
								
							
						
					
					
						commit
						91a4de7780
					
				@ -64,6 +64,7 @@ import org.thingsboard.server.service.ws.notification.sub.NotificationRequestUpd
 | 
			
		||||
import org.thingsboard.server.service.ws.notification.sub.NotificationUpdate;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
@ -130,27 +131,43 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
 | 
			
		||||
                .build();
 | 
			
		||||
        ctx.init();
 | 
			
		||||
 | 
			
		||||
        Set<NotificationDeliveryMethod> deliveryMethods = ctx.getDeliveryMethods();
 | 
			
		||||
        List<ListenableFuture<Void>> results = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
        for (NotificationTargetId targetId : notificationRequest.getTargets()) {
 | 
			
		||||
            DaoUtil.processBatches(pageLink -> {
 | 
			
		||||
                return notificationTargetService.findRecipientsForNotificationTarget(tenantId, ctx.getCustomerId(), targetId, pageLink);
 | 
			
		||||
            }, 200, recipientsBatch -> {
 | 
			
		||||
                for (NotificationDeliveryMethod deliveryMethod : ctx.getDeliveryMethods()) {
 | 
			
		||||
                for (NotificationDeliveryMethod deliveryMethod : deliveryMethods) {
 | 
			
		||||
                    if (deliveryMethod.isIndependent()) continue;
 | 
			
		||||
 | 
			
		||||
                    List<User> recipients = recipientsBatch.getData();
 | 
			
		||||
                    log.debug("Sending {} notifications for request {} to recipients batch ({})", deliveryMethod, savedNotificationRequest.getId(), recipients.size());
 | 
			
		||||
                    NotificationChannel notificationChannel = channels.get(deliveryMethod);
 | 
			
		||||
                    for (User recipient : recipients) {
 | 
			
		||||
                        ListenableFuture<Void> resultFuture = processForRecipient(notificationChannel, recipient, ctx);
 | 
			
		||||
                        ListenableFuture<Void> resultFuture = process(notificationChannel, recipient, ctx);
 | 
			
		||||
                        DonAsynchron.withCallback(resultFuture, result -> {
 | 
			
		||||
                            ctx.getStats().reportSent(deliveryMethod, recipient);
 | 
			
		||||
                        }, error -> {
 | 
			
		||||
                            ctx.getStats().reportError(deliveryMethod, recipient, error);
 | 
			
		||||
                            ctx.getStats().reportError(deliveryMethod, error, recipient);
 | 
			
		||||
                        });
 | 
			
		||||
                        results.add(resultFuture);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        for (NotificationDeliveryMethod deliveryMethod : deliveryMethods) {
 | 
			
		||||
            if (deliveryMethod.isIndependent()) {
 | 
			
		||||
                NotificationChannel notificationChannel = channels.get(deliveryMethod);
 | 
			
		||||
                ListenableFuture<Void> resultFuture = process(notificationChannel, null, ctx);
 | 
			
		||||
                DonAsynchron.withCallback(resultFuture, result -> {
 | 
			
		||||
                    ctx.getStats().reportSent(deliveryMethod, null);
 | 
			
		||||
                }, error -> {
 | 
			
		||||
                    ctx.getStats().reportError(deliveryMethod, error, null);
 | 
			
		||||
                });
 | 
			
		||||
                results.add(resultFuture);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Futures.whenAllComplete(results).run(() -> {
 | 
			
		||||
            NotificationRequestStats stats = ctx.getStats();
 | 
			
		||||
@ -177,33 +194,21 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
 | 
			
		||||
        return savedNotificationRequest;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private ListenableFuture<Void> processForRecipient(NotificationChannel notificationChannel, User recipient, NotificationProcessingContext ctx) {
 | 
			
		||||
    private ListenableFuture<Void> process(NotificationChannel notificationChannel, User recipient, NotificationProcessingContext ctx) {
 | 
			
		||||
        NotificationDeliveryMethod deliveryMethod = notificationChannel.getDeliveryMethod();
 | 
			
		||||
        if (ctx.getStats().contains(deliveryMethod, recipient.getId())) {
 | 
			
		||||
        if (recipient != null && ctx.getStats().contains(deliveryMethod, recipient.getId())) {
 | 
			
		||||
            return Futures.immediateFailedFuture(new AlreadySentException());
 | 
			
		||||
        }
 | 
			
		||||
        DeliveryMethodNotificationTemplate processedTemplate;
 | 
			
		||||
        try {
 | 
			
		||||
            processedTemplate = ctx.getProcessedTemplate(deliveryMethod, recipient);
 | 
			
		||||
            Map<String, String> templateContext = recipient != null ? ctx.createTemplateContext(recipient) : Collections.emptyMap();
 | 
			
		||||
            processedTemplate = ctx.getProcessedTemplate(deliveryMethod, templateContext);
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            return Futures.immediateFailedFuture(e);
 | 
			
		||||
        }
 | 
			
		||||
        return notificationChannel.sendNotification(recipient, processedTemplate, ctx);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void forwardToNotificationSchedulerService(TenantId tenantId, NotificationRequestId notificationRequestId) {
 | 
			
		||||
        TransportProtos.NotificationSchedulerServiceMsg.Builder msg = TransportProtos.NotificationSchedulerServiceMsg.newBuilder()
 | 
			
		||||
                .setTenantIdMSB(tenantId.getId().getMostSignificantBits())
 | 
			
		||||
                .setTenantIdLSB(tenantId.getId().getLeastSignificantBits())
 | 
			
		||||
                .setRequestIdMSB(notificationRequestId.getId().getMostSignificantBits())
 | 
			
		||||
                .setRequestIdLSB(notificationRequestId.getId().getLeastSignificantBits())
 | 
			
		||||
                .setTs(System.currentTimeMillis());
 | 
			
		||||
        TransportProtos.ToCoreMsg toCoreMsg = TransportProtos.ToCoreMsg.newBuilder()
 | 
			
		||||
                .setNotificationSchedulerServiceMsg(msg)
 | 
			
		||||
                .build();
 | 
			
		||||
        clusterService.pushMsgToCore(tenantId, notificationRequestId, toCoreMsg, null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public ListenableFuture<Void> sendNotification(User recipient, PushDeliveryMethodNotificationTemplate processedTemplate, NotificationProcessingContext ctx) {
 | 
			
		||||
        NotificationRequest request = ctx.getRequest();
 | 
			
		||||
@ -302,6 +307,19 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
 | 
			
		||||
        return notificationRequest;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void forwardToNotificationSchedulerService(TenantId tenantId, NotificationRequestId notificationRequestId) {
 | 
			
		||||
        TransportProtos.NotificationSchedulerServiceMsg.Builder msg = TransportProtos.NotificationSchedulerServiceMsg.newBuilder()
 | 
			
		||||
                .setTenantIdMSB(tenantId.getId().getMostSignificantBits())
 | 
			
		||||
                .setTenantIdLSB(tenantId.getId().getLeastSignificantBits())
 | 
			
		||||
                .setRequestIdMSB(notificationRequestId.getId().getMostSignificantBits())
 | 
			
		||||
                .setRequestIdLSB(notificationRequestId.getId().getLeastSignificantBits())
 | 
			
		||||
                .setTs(System.currentTimeMillis());
 | 
			
		||||
        TransportProtos.ToCoreMsg toCoreMsg = TransportProtos.ToCoreMsg.newBuilder()
 | 
			
		||||
                .setNotificationSchedulerServiceMsg(msg)
 | 
			
		||||
                .build();
 | 
			
		||||
        clusterService.pushMsgToCore(tenantId, notificationRequestId, toCoreMsg, null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private ListenableFuture<Void> onNotificationUpdate(TenantId tenantId, UserId recipientId, NotificationUpdate update) {
 | 
			
		||||
        log.trace("Submitting notification update for recipient {}: {}", recipientId, update);
 | 
			
		||||
        return Futures.submit(() -> {
 | 
			
		||||
 | 
			
		||||
@ -90,8 +90,11 @@ public class NotificationProcessingContext {
 | 
			
		||||
        return (C) settings.getDeliveryMethodsConfigs().get(deliveryMethod);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected <T extends DeliveryMethodNotificationTemplate> T getProcessedTemplate(NotificationDeliveryMethod deliveryMethod, User recipient) {
 | 
			
		||||
        Map<String, String> templateContext = createTemplateContext(recipient);
 | 
			
		||||
    protected <T extends DeliveryMethodNotificationTemplate> T getProcessedTemplate(NotificationDeliveryMethod deliveryMethod, Map<String, String> templateContext) {
 | 
			
		||||
        if (request.getInfo() != null) {
 | 
			
		||||
            templateContext = new HashMap<>(templateContext);
 | 
			
		||||
            templateContext.putAll(request.getInfo().getTemplateData());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        T template = (T) templates.get(deliveryMethod).copy();
 | 
			
		||||
        template.setBody(processTemplate(template.getBody(), templateContext));
 | 
			
		||||
@ -106,14 +109,11 @@ public class NotificationProcessingContext {
 | 
			
		||||
        return TbNodeUtils.processTemplate(template, context);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Map<String, String> createTemplateContext(User recipient) {
 | 
			
		||||
    public Map<String, String> createTemplateContext(User recipient) {
 | 
			
		||||
        Map<String, String> templateContext = new HashMap<>();
 | 
			
		||||
        templateContext.put("email", recipient.getEmail());
 | 
			
		||||
        templateContext.put("firstName", Strings.nullToEmpty(recipient.getFirstName()));
 | 
			
		||||
        templateContext.put("lastName", Strings.nullToEmpty(recipient.getLastName()));
 | 
			
		||||
        if (request.getInfo() != null) {
 | 
			
		||||
            templateContext.putAll(request.getInfo().getTemplateData());
 | 
			
		||||
        }
 | 
			
		||||
        return templateContext;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -25,13 +25,16 @@ import org.thingsboard.rule.engine.api.slack.SlackService;
 | 
			
		||||
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.UserId;
 | 
			
		||||
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.NotificationRequestStats;
 | 
			
		||||
import org.thingsboard.server.common.data.notification.NotificationType;
 | 
			
		||||
import org.thingsboard.server.common.data.notification.info.UserOriginatedNotificationInfo;
 | 
			
		||||
import org.thingsboard.server.common.data.notification.settings.NotificationSettings;
 | 
			
		||||
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;
 | 
			
		||||
@ -52,6 +55,7 @@ import java.util.List;
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThat;
 | 
			
		||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
 | 
			
		||||
 | 
			
		||||
public abstract class AbstractNotificationApiTest extends AbstractControllerTest {
 | 
			
		||||
 | 
			
		||||
@ -94,20 +98,28 @@ public abstract class AbstractNotificationApiTest extends AbstractControllerTest
 | 
			
		||||
            deliveryMethods = new NotificationDeliveryMethod[]{NotificationDeliveryMethod.PUSH};
 | 
			
		||||
        }
 | 
			
		||||
        NotificationTemplate notificationTemplate = createNotificationTemplate(DEFAULT_NOTIFICATION_TYPE, DEFAULT_NOTIFICATION_SUBJECT, text, deliveryMethods);
 | 
			
		||||
        return submitNotificationRequest(targets, notificationTemplate.getId(), delayInSec);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected NotificationRequest submitNotificationRequest(List<NotificationTargetId> targets, NotificationTemplateId notificationTemplateId, int delayInSec) {
 | 
			
		||||
        NotificationRequestConfig config = new NotificationRequestConfig();
 | 
			
		||||
        config.setSendingDelayInSec(delayInSec);
 | 
			
		||||
        UserOriginatedNotificationInfo notificationInfo = new UserOriginatedNotificationInfo();
 | 
			
		||||
        notificationInfo.setDescription("The text: " + text);
 | 
			
		||||
        notificationInfo.setDescription("My description");
 | 
			
		||||
        NotificationRequest notificationRequest = NotificationRequest.builder()
 | 
			
		||||
                .tenantId(tenantId)
 | 
			
		||||
                .targets(targets)
 | 
			
		||||
                .templateId(notificationTemplate.getId())
 | 
			
		||||
                .templateId(notificationTemplateId)
 | 
			
		||||
                .info(notificationInfo)
 | 
			
		||||
                .additionalConfig(config)
 | 
			
		||||
                .build();
 | 
			
		||||
        return doPost("/api/notification/request", notificationRequest, NotificationRequest.class);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected NotificationRequestStats getStats(NotificationRequestId notificationRequestId) throws Exception {
 | 
			
		||||
        return findNotificationRequest(notificationRequestId).getStats();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected NotificationTemplate createNotificationTemplate(NotificationType notificationType, String subject,
 | 
			
		||||
                                                              String text, NotificationDeliveryMethod... deliveryMethods) {
 | 
			
		||||
        NotificationTemplate notificationTemplate = new NotificationTemplate();
 | 
			
		||||
@ -143,9 +155,17 @@ public abstract class AbstractNotificationApiTest extends AbstractControllerTest
 | 
			
		||||
            config.getDeliveryMethodsTemplates().put(deliveryMethod, deliveryMethodNotificationTemplate);
 | 
			
		||||
        }
 | 
			
		||||
        notificationTemplate.setConfiguration(config);
 | 
			
		||||
        return saveNotificationTemplate(notificationTemplate);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected NotificationTemplate saveNotificationTemplate(NotificationTemplate notificationTemplate) {
 | 
			
		||||
        return doPost("/api/notification/template", notificationTemplate, NotificationTemplate.class);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected void saveNotificationSettings(NotificationSettings notificationSettings) throws Exception {
 | 
			
		||||
        doPost("/api/notification/settings", notificationSettings).andExpect(status().isOk());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected Pair<User, NotificationApiWsClient> createUserAndConnectWsClient(Authority authority) throws Exception {
 | 
			
		||||
        User user = new User();
 | 
			
		||||
        user.setTenantId(tenantId);
 | 
			
		||||
 | 
			
		||||
@ -30,9 +30,16 @@ import org.thingsboard.server.common.data.notification.NotificationDeliveryMetho
 | 
			
		||||
import org.thingsboard.server.common.data.notification.NotificationRequest;
 | 
			
		||||
import org.thingsboard.server.common.data.notification.NotificationRequestStats;
 | 
			
		||||
import org.thingsboard.server.common.data.notification.NotificationRequestStatus;
 | 
			
		||||
import org.thingsboard.server.common.data.notification.NotificationType;
 | 
			
		||||
import org.thingsboard.server.common.data.notification.info.UserOriginatedNotificationInfo;
 | 
			
		||||
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.NotificationTarget;
 | 
			
		||||
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.SlackConversation;
 | 
			
		||||
import org.thingsboard.server.common.data.notification.template.SlackDeliveryMethodNotificationTemplate;
 | 
			
		||||
import org.thingsboard.server.common.data.page.PageData;
 | 
			
		||||
import org.thingsboard.server.common.data.page.PageLink;
 | 
			
		||||
import org.thingsboard.server.common.data.security.Authority;
 | 
			
		||||
@ -43,6 +50,7 @@ import org.thingsboard.server.service.ws.notification.cmd.UnreadNotificationsCou
 | 
			
		||||
import org.thingsboard.server.service.ws.notification.cmd.UnreadNotificationsUpdate;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
@ -55,6 +63,7 @@ import static org.awaitility.Awaitility.await;
 | 
			
		||||
import static org.mockito.ArgumentMatchers.any;
 | 
			
		||||
import static org.mockito.ArgumentMatchers.eq;
 | 
			
		||||
import static org.mockito.ArgumentMatchers.startsWith;
 | 
			
		||||
import static org.mockito.Mockito.doThrow;
 | 
			
		||||
import static org.mockito.Mockito.timeout;
 | 
			
		||||
import static org.mockito.Mockito.verify;
 | 
			
		||||
 | 
			
		||||
@ -312,8 +321,8 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        await().atMost(2, TimeUnit.SECONDS)
 | 
			
		||||
                .until(() -> findNotificationRequest(notificationRequest.getId()).getStats() != null);
 | 
			
		||||
        NotificationRequestStats stats = findNotificationRequest(notificationRequest.getId()).getStats();
 | 
			
		||||
                .until(() -> getStats(notificationRequest.getId()) != null);
 | 
			
		||||
        NotificationRequestStats stats = getStats(notificationRequest.getId());
 | 
			
		||||
        assertThat(stats.getSent().get(NotificationDeliveryMethod.PUSH)).hasValue(usersCount);
 | 
			
		||||
        assertThat(stats.getSent().get(NotificationDeliveryMethod.EMAIL)).hasValue(usersCount);
 | 
			
		||||
 | 
			
		||||
@ -341,8 +350,8 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
 | 
			
		||||
        wsClient.waitForUpdate();
 | 
			
		||||
 | 
			
		||||
        await().atMost(2, TimeUnit.SECONDS)
 | 
			
		||||
                .until(() -> findNotificationRequest(notificationRequest.getId()).getStats() != null);
 | 
			
		||||
        NotificationRequestStats stats = findNotificationRequest(notificationRequest.getId()).getStats();
 | 
			
		||||
                .until(() -> getStats(notificationRequest.getId()) != null);
 | 
			
		||||
        NotificationRequestStats stats = getStats(notificationRequest.getId());
 | 
			
		||||
 | 
			
		||||
        assertThat(stats.getSent().get(NotificationDeliveryMethod.PUSH)).hasValue(1);
 | 
			
		||||
        assertThat(stats.getSent().get(NotificationDeliveryMethod.EMAIL)).hasValue(1);
 | 
			
		||||
@ -360,7 +369,6 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
 | 
			
		||||
            user.setAuthority(Authority.TENANT_ADMIN);
 | 
			
		||||
            user.setEmail("test-user-" + i + "@thingsboard.org");
 | 
			
		||||
            user = doPost("/api/user", user, User.class);
 | 
			
		||||
            System.err.println(i);
 | 
			
		||||
            users.add(user);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -389,10 +397,57 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
 | 
			
		||||
        assertThat(sentNotifications.getData()).extracting(Notification::getRecipientId)
 | 
			
		||||
                .containsAll(users.stream().map(User::getId).collect(Collectors.toSet()));
 | 
			
		||||
 | 
			
		||||
        NotificationRequestStats stats = findNotificationRequest(notificationRequest.getId()).getStats();
 | 
			
		||||
        NotificationRequestStats stats = getStats(notificationRequest.getId());
 | 
			
		||||
        assertThat(stats.getSent().values().stream().mapToInt(AtomicInteger::get).sum()).isGreaterThanOrEqualTo(usersCount);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testSlackNotifications() throws Exception {
 | 
			
		||||
        NotificationSettings settings = new NotificationSettings();
 | 
			
		||||
        SlackNotificationDeliveryMethodConfig slackConfig = new SlackNotificationDeliveryMethodConfig();
 | 
			
		||||
        slackConfig.setMethod(NotificationDeliveryMethod.SLACK);
 | 
			
		||||
        slackConfig.setEnabled(true);
 | 
			
		||||
        String slackToken = "xoxb-123123123";
 | 
			
		||||
        slackConfig.setBotToken(slackToken);
 | 
			
		||||
        settings.setDeliveryMethodsConfigs(Map.of(
 | 
			
		||||
                NotificationDeliveryMethod.SLACK, slackConfig
 | 
			
		||||
        ));
 | 
			
		||||
        saveNotificationSettings(settings);
 | 
			
		||||
 | 
			
		||||
        NotificationTemplate notificationTemplate = new NotificationTemplate();
 | 
			
		||||
        notificationTemplate.setName("Slack notification template");
 | 
			
		||||
        notificationTemplate.setNotificationType(NotificationType.GENERAL);
 | 
			
		||||
        NotificationTemplateConfig config = new NotificationTemplateConfig();
 | 
			
		||||
        config.setDefaultTextTemplate("To Slack :)");
 | 
			
		||||
 | 
			
		||||
        SlackDeliveryMethodNotificationTemplate slackNotificationTemplate = new SlackDeliveryMethodNotificationTemplate();
 | 
			
		||||
        slackNotificationTemplate.setEnabled(true);
 | 
			
		||||
        slackNotificationTemplate.setConversationType(SlackConversation.Type.PUBLIC_CHANNEL);
 | 
			
		||||
        String conversationId = "U154475415";
 | 
			
		||||
        slackNotificationTemplate.setConversationId(conversationId);
 | 
			
		||||
 | 
			
		||||
        config.setDeliveryMethodsTemplates(Map.of(
 | 
			
		||||
                NotificationDeliveryMethod.SLACK, slackNotificationTemplate
 | 
			
		||||
        ));
 | 
			
		||||
        notificationTemplate.setConfiguration(config);
 | 
			
		||||
        notificationTemplate = saveNotificationTemplate(notificationTemplate);
 | 
			
		||||
 | 
			
		||||
        NotificationRequest successfulNotificationRequest = submitNotificationRequest(Collections.emptyList(), notificationTemplate.getId(), 0);
 | 
			
		||||
        await().atMost(2, TimeUnit.SECONDS)
 | 
			
		||||
                .until(() -> getStats(successfulNotificationRequest.getId()) != null);
 | 
			
		||||
        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);
 | 
			
		||||
 | 
			
		||||
        String errorMessage = "Error!!!";
 | 
			
		||||
        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);
 | 
			
		||||
        stats = getStats(failedNotificationRequest.getId());
 | 
			
		||||
        assertThat(stats.getErrors().get(NotificationDeliveryMethod.SLACK).values()).containsExactly(errorMessage);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void checkFullNotificationsUpdate(UnreadNotificationsUpdate notificationsUpdate, String... expectedNotifications) {
 | 
			
		||||
        assertThat(notificationsUpdate.getNotifications()).extracting(Notification::getText).containsOnly(expectedNotifications);
 | 
			
		||||
        assertThat(notificationsUpdate.getNotifications()).extracting(Notification::getType).containsOnly(DEFAULT_NOTIFICATION_TYPE);
 | 
			
		||||
 | 
			
		||||
@ -15,6 +15,24 @@
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.server.common.data.notification;
 | 
			
		||||
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
 | 
			
		||||
public enum NotificationDeliveryMethod {
 | 
			
		||||
    PUSH, EMAIL, SMS, SLACK
 | 
			
		||||
 | 
			
		||||
    PUSH,
 | 
			
		||||
    EMAIL,
 | 
			
		||||
    SMS,
 | 
			
		||||
    SLACK(true);
 | 
			
		||||
 | 
			
		||||
    @Getter
 | 
			
		||||
    private final boolean independent; // TODO: think of a better name
 | 
			
		||||
 | 
			
		||||
    NotificationDeliveryMethod() {
 | 
			
		||||
        this(false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    NotificationDeliveryMethod(boolean independent) {
 | 
			
		||||
        this.independent = independent;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -46,7 +46,7 @@ import java.util.List;
 | 
			
		||||
public class NotificationRequest extends BaseData<NotificationRequestId> implements HasTenantId, HasName {
 | 
			
		||||
 | 
			
		||||
    private TenantId tenantId;
 | 
			
		||||
    @NotEmpty
 | 
			
		||||
    @NotNull
 | 
			
		||||
    private List<NotificationTargetId> targets;
 | 
			
		||||
 | 
			
		||||
    @NotNull
 | 
			
		||||
 | 
			
		||||
@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.id.UserId;
 | 
			
		||||
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Optional;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
import java.util.concurrent.ConcurrentHashMap;
 | 
			
		||||
import java.util.concurrent.atomic.AtomicInteger;
 | 
			
		||||
@ -52,15 +53,18 @@ public class NotificationRequestStats {
 | 
			
		||||
 | 
			
		||||
    public void reportSent(NotificationDeliveryMethod deliveryMethod, User recipient) {
 | 
			
		||||
        sent.computeIfAbsent(deliveryMethod, k -> new AtomicInteger()).incrementAndGet();
 | 
			
		||||
        processedRecipients.computeIfAbsent(deliveryMethod, k -> ConcurrentHashMap.newKeySet()).add(recipient.getId());
 | 
			
		||||
        if (recipient != null) {
 | 
			
		||||
            processedRecipients.computeIfAbsent(deliveryMethod, k -> ConcurrentHashMap.newKeySet()).add(recipient.getId());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void reportError(NotificationDeliveryMethod deliveryMethod, User recipient, Throwable error) {
 | 
			
		||||
    public void reportError(NotificationDeliveryMethod deliveryMethod, Throwable error, User recipient) {
 | 
			
		||||
        if (error instanceof AlreadySentException) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        String errorMessage = error.getMessage();
 | 
			
		||||
        errors.computeIfAbsent(deliveryMethod, k -> new ConcurrentHashMap<>()).put(recipient.getEmail(), errorMessage);
 | 
			
		||||
        String key = Optional.ofNullable(recipient).map(User::getEmail).orElse("");
 | 
			
		||||
        errors.computeIfAbsent(deliveryMethod, k -> new ConcurrentHashMap<>()).put(key, errorMessage);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean contains(NotificationDeliveryMethod deliveryMethod) {
 | 
			
		||||
 | 
			
		||||
@ -18,6 +18,7 @@ package org.thingsboard.server.common.data.notification.settings;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
 | 
			
		||||
 | 
			
		||||
import javax.validation.Valid;
 | 
			
		||||
import javax.validation.constraints.NotNull;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
@ -25,6 +26,7 @@ import java.util.Map;
 | 
			
		||||
public class NotificationSettings {
 | 
			
		||||
 | 
			
		||||
    @NotNull
 | 
			
		||||
    @Valid
 | 
			
		||||
    // location on the screen, shown notifications count, timings of displaying
 | 
			
		||||
    private Map<NotificationDeliveryMethod, NotificationDeliveryMethodConfig> deliveryMethodsConfigs;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -37,6 +37,7 @@ public class NotificationTemplateConfig {
 | 
			
		||||
    @JsonIgnore
 | 
			
		||||
    @AssertTrue(message = "defaultTextTemplate and notificationSubject must be specified if one absent for delivery method")
 | 
			
		||||
    public boolean isValid() {
 | 
			
		||||
        if (deliveryMethodsTemplates == null) return true;
 | 
			
		||||
        for (DeliveryMethodNotificationTemplate template : deliveryMethodsTemplates.values()) {
 | 
			
		||||
            if (StringUtils.isEmpty(template.getBody()) && StringUtils.isEmpty(defaultTextTemplate)) {
 | 
			
		||||
                return false;
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user