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 org.thingsboard.server.service.ws.notification.sub.NotificationUpdate;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -130,27 +131,43 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
|
|||||||
.build();
|
.build();
|
||||||
ctx.init();
|
ctx.init();
|
||||||
|
|
||||||
|
Set<NotificationDeliveryMethod> deliveryMethods = ctx.getDeliveryMethods();
|
||||||
List<ListenableFuture<Void>> results = new ArrayList<>();
|
List<ListenableFuture<Void>> results = new ArrayList<>();
|
||||||
|
|
||||||
for (NotificationTargetId targetId : notificationRequest.getTargets()) {
|
for (NotificationTargetId targetId : notificationRequest.getTargets()) {
|
||||||
DaoUtil.processBatches(pageLink -> {
|
DaoUtil.processBatches(pageLink -> {
|
||||||
return notificationTargetService.findRecipientsForNotificationTarget(tenantId, ctx.getCustomerId(), targetId, pageLink);
|
return notificationTargetService.findRecipientsForNotificationTarget(tenantId, ctx.getCustomerId(), targetId, pageLink);
|
||||||
}, 200, recipientsBatch -> {
|
}, 200, recipientsBatch -> {
|
||||||
for (NotificationDeliveryMethod deliveryMethod : ctx.getDeliveryMethods()) {
|
for (NotificationDeliveryMethod deliveryMethod : deliveryMethods) {
|
||||||
|
if (deliveryMethod.isIndependent()) continue;
|
||||||
|
|
||||||
List<User> recipients = recipientsBatch.getData();
|
List<User> recipients = recipientsBatch.getData();
|
||||||
log.debug("Sending {} notifications for request {} to recipients batch ({})", deliveryMethod, savedNotificationRequest.getId(), recipients.size());
|
log.debug("Sending {} notifications for request {} to recipients batch ({})", deliveryMethod, savedNotificationRequest.getId(), recipients.size());
|
||||||
NotificationChannel notificationChannel = channels.get(deliveryMethod);
|
NotificationChannel notificationChannel = channels.get(deliveryMethod);
|
||||||
for (User recipient : recipients) {
|
for (User recipient : recipients) {
|
||||||
ListenableFuture<Void> resultFuture = processForRecipient(notificationChannel, recipient, ctx);
|
ListenableFuture<Void> resultFuture = process(notificationChannel, recipient, ctx);
|
||||||
DonAsynchron.withCallback(resultFuture, result -> {
|
DonAsynchron.withCallback(resultFuture, result -> {
|
||||||
ctx.getStats().reportSent(deliveryMethod, recipient);
|
ctx.getStats().reportSent(deliveryMethod, recipient);
|
||||||
}, error -> {
|
}, error -> {
|
||||||
ctx.getStats().reportError(deliveryMethod, recipient, error);
|
ctx.getStats().reportError(deliveryMethod, error, recipient);
|
||||||
});
|
});
|
||||||
results.add(resultFuture);
|
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(() -> {
|
Futures.whenAllComplete(results).run(() -> {
|
||||||
NotificationRequestStats stats = ctx.getStats();
|
NotificationRequestStats stats = ctx.getStats();
|
||||||
@ -177,33 +194,21 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
|
|||||||
return savedNotificationRequest;
|
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();
|
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());
|
return Futures.immediateFailedFuture(new AlreadySentException());
|
||||||
}
|
}
|
||||||
DeliveryMethodNotificationTemplate processedTemplate;
|
DeliveryMethodNotificationTemplate processedTemplate;
|
||||||
try {
|
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) {
|
} catch (Exception e) {
|
||||||
return Futures.immediateFailedFuture(e);
|
return Futures.immediateFailedFuture(e);
|
||||||
}
|
}
|
||||||
return notificationChannel.sendNotification(recipient, processedTemplate, ctx);
|
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
|
@Override
|
||||||
public ListenableFuture<Void> sendNotification(User recipient, PushDeliveryMethodNotificationTemplate processedTemplate, NotificationProcessingContext ctx) {
|
public ListenableFuture<Void> sendNotification(User recipient, PushDeliveryMethodNotificationTemplate processedTemplate, NotificationProcessingContext ctx) {
|
||||||
NotificationRequest request = ctx.getRequest();
|
NotificationRequest request = ctx.getRequest();
|
||||||
@ -302,6 +307,19 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
|
|||||||
return notificationRequest;
|
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) {
|
private ListenableFuture<Void> onNotificationUpdate(TenantId tenantId, UserId recipientId, NotificationUpdate update) {
|
||||||
log.trace("Submitting notification update for recipient {}: {}", recipientId, update);
|
log.trace("Submitting notification update for recipient {}: {}", recipientId, update);
|
||||||
return Futures.submit(() -> {
|
return Futures.submit(() -> {
|
||||||
|
|||||||
@ -90,8 +90,11 @@ public class NotificationProcessingContext {
|
|||||||
return (C) settings.getDeliveryMethodsConfigs().get(deliveryMethod);
|
return (C) settings.getDeliveryMethodsConfigs().get(deliveryMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <T extends DeliveryMethodNotificationTemplate> T getProcessedTemplate(NotificationDeliveryMethod deliveryMethod, User recipient) {
|
protected <T extends DeliveryMethodNotificationTemplate> T getProcessedTemplate(NotificationDeliveryMethod deliveryMethod, Map<String, String> templateContext) {
|
||||||
Map<String, String> templateContext = createTemplateContext(recipient);
|
if (request.getInfo() != null) {
|
||||||
|
templateContext = new HashMap<>(templateContext);
|
||||||
|
templateContext.putAll(request.getInfo().getTemplateData());
|
||||||
|
}
|
||||||
|
|
||||||
T template = (T) templates.get(deliveryMethod).copy();
|
T template = (T) templates.get(deliveryMethod).copy();
|
||||||
template.setBody(processTemplate(template.getBody(), templateContext));
|
template.setBody(processTemplate(template.getBody(), templateContext));
|
||||||
@ -106,14 +109,11 @@ public class NotificationProcessingContext {
|
|||||||
return TbNodeUtils.processTemplate(template, context);
|
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<>();
|
Map<String, String> templateContext = new HashMap<>();
|
||||||
templateContext.put("email", recipient.getEmail());
|
templateContext.put("email", recipient.getEmail());
|
||||||
templateContext.put("firstName", Strings.nullToEmpty(recipient.getFirstName()));
|
templateContext.put("firstName", Strings.nullToEmpty(recipient.getFirstName()));
|
||||||
templateContext.put("lastName", Strings.nullToEmpty(recipient.getLastName()));
|
templateContext.put("lastName", Strings.nullToEmpty(recipient.getLastName()));
|
||||||
if (request.getInfo() != null) {
|
|
||||||
templateContext.putAll(request.getInfo().getTemplateData());
|
|
||||||
}
|
|
||||||
return templateContext;
|
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.User;
|
||||||
import org.thingsboard.server.common.data.id.NotificationRequestId;
|
import org.thingsboard.server.common.data.id.NotificationRequestId;
|
||||||
import org.thingsboard.server.common.data.id.NotificationTargetId;
|
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.id.UserId;
|
||||||
import org.thingsboard.server.common.data.notification.Notification;
|
import org.thingsboard.server.common.data.notification.Notification;
|
||||||
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
|
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
|
||||||
import org.thingsboard.server.common.data.notification.NotificationRequest;
|
import org.thingsboard.server.common.data.notification.NotificationRequest;
|
||||||
import org.thingsboard.server.common.data.notification.NotificationRequestConfig;
|
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.NotificationType;
|
||||||
import org.thingsboard.server.common.data.notification.info.UserOriginatedNotificationInfo;
|
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.NotificationTarget;
|
||||||
import org.thingsboard.server.common.data.notification.targets.UserListNotificationTargetConfig;
|
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.DeliveryMethodNotificationTemplate;
|
||||||
@ -52,6 +55,7 @@ import java.util.List;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
public abstract class AbstractNotificationApiTest extends AbstractControllerTest {
|
public abstract class AbstractNotificationApiTest extends AbstractControllerTest {
|
||||||
|
|
||||||
@ -94,20 +98,28 @@ public abstract class AbstractNotificationApiTest extends AbstractControllerTest
|
|||||||
deliveryMethods = new NotificationDeliveryMethod[]{NotificationDeliveryMethod.PUSH};
|
deliveryMethods = new NotificationDeliveryMethod[]{NotificationDeliveryMethod.PUSH};
|
||||||
}
|
}
|
||||||
NotificationTemplate notificationTemplate = createNotificationTemplate(DEFAULT_NOTIFICATION_TYPE, DEFAULT_NOTIFICATION_SUBJECT, text, deliveryMethods);
|
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();
|
NotificationRequestConfig config = new NotificationRequestConfig();
|
||||||
config.setSendingDelayInSec(delayInSec);
|
config.setSendingDelayInSec(delayInSec);
|
||||||
UserOriginatedNotificationInfo notificationInfo = new UserOriginatedNotificationInfo();
|
UserOriginatedNotificationInfo notificationInfo = new UserOriginatedNotificationInfo();
|
||||||
notificationInfo.setDescription("The text: " + text);
|
notificationInfo.setDescription("My description");
|
||||||
NotificationRequest notificationRequest = NotificationRequest.builder()
|
NotificationRequest notificationRequest = NotificationRequest.builder()
|
||||||
.tenantId(tenantId)
|
.tenantId(tenantId)
|
||||||
.targets(targets)
|
.targets(targets)
|
||||||
.templateId(notificationTemplate.getId())
|
.templateId(notificationTemplateId)
|
||||||
.info(notificationInfo)
|
.info(notificationInfo)
|
||||||
.additionalConfig(config)
|
.additionalConfig(config)
|
||||||
.build();
|
.build();
|
||||||
return doPost("/api/notification/request", notificationRequest, NotificationRequest.class);
|
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,
|
protected NotificationTemplate createNotificationTemplate(NotificationType notificationType, String subject,
|
||||||
String text, NotificationDeliveryMethod... deliveryMethods) {
|
String text, NotificationDeliveryMethod... deliveryMethods) {
|
||||||
NotificationTemplate notificationTemplate = new NotificationTemplate();
|
NotificationTemplate notificationTemplate = new NotificationTemplate();
|
||||||
@ -143,9 +155,17 @@ public abstract class AbstractNotificationApiTest extends AbstractControllerTest
|
|||||||
config.getDeliveryMethodsTemplates().put(deliveryMethod, deliveryMethodNotificationTemplate);
|
config.getDeliveryMethodsTemplates().put(deliveryMethod, deliveryMethodNotificationTemplate);
|
||||||
}
|
}
|
||||||
notificationTemplate.setConfiguration(config);
|
notificationTemplate.setConfiguration(config);
|
||||||
|
return saveNotificationTemplate(notificationTemplate);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected NotificationTemplate saveNotificationTemplate(NotificationTemplate notificationTemplate) {
|
||||||
return doPost("/api/notification/template", notificationTemplate, NotificationTemplate.class);
|
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 {
|
protected Pair<User, NotificationApiWsClient> createUserAndConnectWsClient(Authority authority) throws Exception {
|
||||||
User user = new User();
|
User user = new User();
|
||||||
user.setTenantId(tenantId);
|
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.NotificationRequest;
|
||||||
import org.thingsboard.server.common.data.notification.NotificationRequestStats;
|
import org.thingsboard.server.common.data.notification.NotificationRequestStats;
|
||||||
import org.thingsboard.server.common.data.notification.NotificationRequestStatus;
|
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.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.AllUsersNotificationTargetConfig;
|
||||||
import org.thingsboard.server.common.data.notification.targets.NotificationTarget;
|
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.PageData;
|
||||||
import org.thingsboard.server.common.data.page.PageLink;
|
import org.thingsboard.server.common.data.page.PageLink;
|
||||||
import org.thingsboard.server.common.data.security.Authority;
|
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 org.thingsboard.server.service.ws.notification.cmd.UnreadNotificationsUpdate;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
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.any;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.ArgumentMatchers.startsWith;
|
import static org.mockito.ArgumentMatchers.startsWith;
|
||||||
|
import static org.mockito.Mockito.doThrow;
|
||||||
import static org.mockito.Mockito.timeout;
|
import static org.mockito.Mockito.timeout;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
@ -312,8 +321,8 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await().atMost(2, TimeUnit.SECONDS)
|
await().atMost(2, TimeUnit.SECONDS)
|
||||||
.until(() -> findNotificationRequest(notificationRequest.getId()).getStats() != null);
|
.until(() -> getStats(notificationRequest.getId()) != null);
|
||||||
NotificationRequestStats stats = findNotificationRequest(notificationRequest.getId()).getStats();
|
NotificationRequestStats stats = getStats(notificationRequest.getId());
|
||||||
assertThat(stats.getSent().get(NotificationDeliveryMethod.PUSH)).hasValue(usersCount);
|
assertThat(stats.getSent().get(NotificationDeliveryMethod.PUSH)).hasValue(usersCount);
|
||||||
assertThat(stats.getSent().get(NotificationDeliveryMethod.EMAIL)).hasValue(usersCount);
|
assertThat(stats.getSent().get(NotificationDeliveryMethod.EMAIL)).hasValue(usersCount);
|
||||||
|
|
||||||
@ -341,8 +350,8 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
|
|||||||
wsClient.waitForUpdate();
|
wsClient.waitForUpdate();
|
||||||
|
|
||||||
await().atMost(2, TimeUnit.SECONDS)
|
await().atMost(2, TimeUnit.SECONDS)
|
||||||
.until(() -> findNotificationRequest(notificationRequest.getId()).getStats() != null);
|
.until(() -> getStats(notificationRequest.getId()) != null);
|
||||||
NotificationRequestStats stats = findNotificationRequest(notificationRequest.getId()).getStats();
|
NotificationRequestStats stats = getStats(notificationRequest.getId());
|
||||||
|
|
||||||
assertThat(stats.getSent().get(NotificationDeliveryMethod.PUSH)).hasValue(1);
|
assertThat(stats.getSent().get(NotificationDeliveryMethod.PUSH)).hasValue(1);
|
||||||
assertThat(stats.getSent().get(NotificationDeliveryMethod.EMAIL)).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.setAuthority(Authority.TENANT_ADMIN);
|
||||||
user.setEmail("test-user-" + i + "@thingsboard.org");
|
user.setEmail("test-user-" + i + "@thingsboard.org");
|
||||||
user = doPost("/api/user", user, User.class);
|
user = doPost("/api/user", user, User.class);
|
||||||
System.err.println(i);
|
|
||||||
users.add(user);
|
users.add(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -389,10 +397,57 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
|
|||||||
assertThat(sentNotifications.getData()).extracting(Notification::getRecipientId)
|
assertThat(sentNotifications.getData()).extracting(Notification::getRecipientId)
|
||||||
.containsAll(users.stream().map(User::getId).collect(Collectors.toSet()));
|
.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);
|
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) {
|
private void checkFullNotificationsUpdate(UnreadNotificationsUpdate notificationsUpdate, String... expectedNotifications) {
|
||||||
assertThat(notificationsUpdate.getNotifications()).extracting(Notification::getText).containsOnly(expectedNotifications);
|
assertThat(notificationsUpdate.getNotifications()).extracting(Notification::getText).containsOnly(expectedNotifications);
|
||||||
assertThat(notificationsUpdate.getNotifications()).extracting(Notification::getType).containsOnly(DEFAULT_NOTIFICATION_TYPE);
|
assertThat(notificationsUpdate.getNotifications()).extracting(Notification::getType).containsOnly(DEFAULT_NOTIFICATION_TYPE);
|
||||||
|
|||||||
@ -15,6 +15,24 @@
|
|||||||
*/
|
*/
|
||||||
package org.thingsboard.server.common.data.notification;
|
package org.thingsboard.server.common.data.notification;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
public enum NotificationDeliveryMethod {
|
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 {
|
public class NotificationRequest extends BaseData<NotificationRequestId> implements HasTenantId, HasName {
|
||||||
|
|
||||||
private TenantId tenantId;
|
private TenantId tenantId;
|
||||||
@NotEmpty
|
@NotNull
|
||||||
private List<NotificationTargetId> targets;
|
private List<NotificationTargetId> targets;
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
|||||||
@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.id.UserId;
|
|||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
@ -52,15 +53,18 @@ public class NotificationRequestStats {
|
|||||||
|
|
||||||
public void reportSent(NotificationDeliveryMethod deliveryMethod, User recipient) {
|
public void reportSent(NotificationDeliveryMethod deliveryMethod, User recipient) {
|
||||||
sent.computeIfAbsent(deliveryMethod, k -> new AtomicInteger()).incrementAndGet();
|
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) {
|
if (error instanceof AlreadySentException) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String errorMessage = error.getMessage();
|
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) {
|
public boolean contains(NotificationDeliveryMethod deliveryMethod) {
|
||||||
|
|||||||
@ -18,6 +18,7 @@ package org.thingsboard.server.common.data.notification.settings;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
|
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
|
||||||
|
|
||||||
|
import javax.validation.Valid;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@ -25,6 +26,7 @@ import java.util.Map;
|
|||||||
public class NotificationSettings {
|
public class NotificationSettings {
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
@Valid
|
||||||
// location on the screen, shown notifications count, timings of displaying
|
// location on the screen, shown notifications count, timings of displaying
|
||||||
private Map<NotificationDeliveryMethod, NotificationDeliveryMethodConfig> deliveryMethodsConfigs;
|
private Map<NotificationDeliveryMethod, NotificationDeliveryMethodConfig> deliveryMethodsConfigs;
|
||||||
|
|
||||||
|
|||||||
@ -37,6 +37,7 @@ public class NotificationTemplateConfig {
|
|||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
@AssertTrue(message = "defaultTextTemplate and notificationSubject must be specified if one absent for delivery method")
|
@AssertTrue(message = "defaultTextTemplate and notificationSubject must be specified if one absent for delivery method")
|
||||||
public boolean isValid() {
|
public boolean isValid() {
|
||||||
|
if (deliveryMethodsTemplates == null) return true;
|
||||||
for (DeliveryMethodNotificationTemplate template : deliveryMethodsTemplates.values()) {
|
for (DeliveryMethodNotificationTemplate template : deliveryMethodsTemplates.values()) {
|
||||||
if (StringUtils.isEmpty(template.getBody()) && StringUtils.isEmpty(defaultTextTemplate)) {
|
if (StringUtils.isEmpty(template.getBody()) && StringUtils.isEmpty(defaultTextTemplate)) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user