Slack recipients preview and recipient params; refactoring
This commit is contained in:
parent
299c159ca1
commit
dc3ca22e63
@ -47,6 +47,7 @@ import org.thingsboard.server.common.data.notification.targets.NotificationRecip
|
|||||||
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.NotificationTargetType;
|
import org.thingsboard.server.common.data.notification.targets.NotificationTargetType;
|
||||||
import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig;
|
import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig;
|
||||||
|
import org.thingsboard.server.common.data.notification.targets.slack.SlackNotificationTargetConfig;
|
||||||
import org.thingsboard.server.common.data.notification.template.DeliveryMethodNotificationTemplate;
|
import org.thingsboard.server.common.data.notification.template.DeliveryMethodNotificationTemplate;
|
||||||
import org.thingsboard.server.common.data.notification.template.NotificationTemplate;
|
import org.thingsboard.server.common.data.notification.template.NotificationTemplate;
|
||||||
import org.thingsboard.server.common.data.page.PageData;
|
import org.thingsboard.server.common.data.page.PageData;
|
||||||
@ -230,28 +231,34 @@ public class NotificationController extends BaseController {
|
|||||||
preview.setProcessedTemplates(processedTemplates);
|
preview.setProcessedTemplates(processedTemplates);
|
||||||
|
|
||||||
// generic permission
|
// generic permission
|
||||||
Set<User> recipientsPreview = new LinkedHashSet<>();
|
Set<String> recipientsPreview = new LinkedHashSet<>();
|
||||||
Map<String, Integer> recipientsCountByTarget = new HashMap<>();
|
Map<String, Integer> recipientsCountByTarget = new HashMap<>();
|
||||||
|
|
||||||
List<NotificationTarget> targets = notificationTargetService.findNotificationTargetsByTenantIdAndIds(user.getTenantId(),
|
List<NotificationTarget> targets = notificationTargetService.findNotificationTargetsByTenantIdAndIds(user.getTenantId(),
|
||||||
request.getTargets().stream().map(NotificationTargetId::new).collect(Collectors.toList()));
|
request.getTargets().stream().map(NotificationTargetId::new).collect(Collectors.toList()));
|
||||||
for (NotificationTarget target : targets) {
|
for (NotificationTarget target : targets) {
|
||||||
int recipientsCount;
|
int recipientsCount;
|
||||||
|
List<NotificationRecipient> recipientsPart;
|
||||||
if (target.getConfiguration().getType() == NotificationTargetType.PLATFORM_USERS) {
|
if (target.getConfiguration().getType() == NotificationTargetType.PLATFORM_USERS) {
|
||||||
PageData<User> recipients = notificationTargetService.findRecipientsForNotificationTargetConfig(user.getTenantId(),
|
PageData<User> recipients = notificationTargetService.findRecipientsForNotificationTargetConfig(user.getTenantId(),
|
||||||
(PlatformUsersNotificationTargetConfig) target.getConfiguration(), new PageLink(recipientsPreviewSize));
|
(PlatformUsersNotificationTargetConfig) target.getConfiguration(), new PageLink(recipientsPreviewSize));
|
||||||
recipientsCount = (int) recipients.getTotalElements();
|
recipientsCount = (int) recipients.getTotalElements();
|
||||||
for (User recipient : recipients.getData()) {
|
recipientsPart = recipients.getData().stream().map(r -> (NotificationRecipient) r).collect(Collectors.toList());
|
||||||
if (recipientsPreview.size() < recipientsPreviewSize) {
|
|
||||||
recipientsPreview.add(recipient);
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
recipientsCount = 1;
|
recipientsCount = 1;
|
||||||
|
recipientsPart = List.of(((SlackNotificationTargetConfig) target.getConfiguration()).getConversation());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (NotificationRecipient recipient : recipientsPart) {
|
||||||
|
if (recipientsPreview.size() < recipientsPreviewSize) {
|
||||||
|
recipientsPreview.add(recipient.getTitle());
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
recipientsCountByTarget.put(target.getName(), recipientsCount);
|
recipientsCountByTarget.put(target.getName(), recipientsCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
preview.setRecipientsPreview(recipientsPreview);
|
preview.setRecipientsPreview(recipientsPreview);
|
||||||
preview.setRecipientsCountByTarget(recipientsCountByTarget);
|
preview.setRecipientsCountByTarget(recipientsCountByTarget);
|
||||||
preview.setTotalRecipientsCount(recipientsCountByTarget.values().stream().mapToInt(Integer::intValue).sum());
|
preview.setTotalRecipientsCount(recipientsCountByTarget.values().stream().mapToInt(Integer::intValue).sum());
|
||||||
|
|||||||
@ -25,9 +25,10 @@ import org.thingsboard.rule.engine.api.NotificationCenter;
|
|||||||
import org.thingsboard.server.common.data.id.EntityId;
|
import org.thingsboard.server.common.data.id.EntityId;
|
||||||
import org.thingsboard.server.common.data.id.NotificationRequestId;
|
import org.thingsboard.server.common.data.id.NotificationRequestId;
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
import org.thingsboard.server.common.data.id.UserId;
|
|
||||||
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.NotificationRequestStatus;
|
||||||
import org.thingsboard.server.common.data.page.PageDataIterable;
|
import org.thingsboard.server.common.data.page.PageDataIterable;
|
||||||
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
|
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
|
||||||
import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
|
import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
|
||||||
@ -112,11 +113,9 @@ public class DefaultNotificationSchedulerService extends AbstractPartitionBasedS
|
|||||||
notificationCenter.processNotificationRequest(tenantId, notificationRequest);
|
notificationCenter.processNotificationRequest(tenantId, notificationRequest);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Failed to process scheduled notification request {}", notificationRequest.getId(), e);
|
log.error("Failed to process scheduled notification request {}", notificationRequest.getId(), e);
|
||||||
UserId senderId = notificationRequest.getSenderId();
|
NotificationRequestStats stats = new NotificationRequestStats();
|
||||||
if (senderId != null) {
|
stats.setError(e.getMessage());
|
||||||
notificationCenter.sendBasicNotification(tenantId, senderId, "Notification failure",
|
notificationRequestService.updateNotificationRequest(tenantId, request.getId(), NotificationRequestStatus.SENT, stats);
|
||||||
"Failed to process scheduled notification (request " + notificationRequest.getId() + "): " + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
scheduledNotificationRequests.remove(notificationRequest.getId());
|
scheduledNotificationRequests.remove(notificationRequest.getId());
|
||||||
|
|||||||
@ -20,13 +20,10 @@ import lombok.Builder;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.apache.commons.collections4.MapUtils;
|
import org.apache.commons.collections4.MapUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.thingsboard.server.common.data.User;
|
|
||||||
import org.thingsboard.server.common.data.id.CustomerId;
|
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
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.NotificationRequestStats;
|
import org.thingsboard.server.common.data.notification.NotificationRequestStats;
|
||||||
import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo;
|
|
||||||
import org.thingsboard.server.common.data.notification.settings.NotificationDeliveryMethodConfig;
|
import org.thingsboard.server.common.data.notification.settings.NotificationDeliveryMethodConfig;
|
||||||
import org.thingsboard.server.common.data.notification.settings.NotificationSettings;
|
import org.thingsboard.server.common.data.notification.settings.NotificationSettings;
|
||||||
import org.thingsboard.server.common.data.notification.targets.NotificationRecipient;
|
import org.thingsboard.server.common.data.notification.targets.NotificationRecipient;
|
||||||
@ -36,7 +33,6 @@ import org.thingsboard.server.common.data.notification.template.NotificationTemp
|
|||||||
import org.thingsboard.server.common.data.notification.template.NotificationTemplateConfig;
|
import org.thingsboard.server.common.data.notification.template.NotificationTemplateConfig;
|
||||||
import org.thingsboard.server.common.data.notification.template.WebDeliveryMethodNotificationTemplate;
|
import org.thingsboard.server.common.data.notification.template.WebDeliveryMethodNotificationTemplate;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -155,23 +151,11 @@ public class NotificationProcessingContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, String> createTemplateContextForRecipient(NotificationRecipient recipient) {
|
private Map<String, String> createTemplateContextForRecipient(NotificationRecipient recipient) {
|
||||||
if (recipient instanceof User) {
|
return Map.of(
|
||||||
User user = (User) recipient;
|
"recipientEmail", Strings.nullToEmpty(recipient.getEmail()),
|
||||||
return Map.of(
|
"recipientFirstName", Strings.nullToEmpty(recipient.getFirstName()),
|
||||||
"recipientEmail", user.getEmail(),
|
"recipientLastName", Strings.nullToEmpty(recipient.getLastName())
|
||||||
"recipientFirstName", Strings.nullToEmpty(user.getFirstName()),
|
);
|
||||||
"recipientLastName", Strings.nullToEmpty(user.getLastName())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return Collections.emptyMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
public CustomerId getCustomerId() {
|
|
||||||
if (request.getInfo() instanceof RuleOriginatedNotificationInfo) {
|
|
||||||
return ((RuleOriginatedNotificationInfo) request.getInfo()).getAffectedCustomerId();
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import com.google.common.util.concurrent.ListenableFuture;
|
|||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.thingsboard.rule.engine.api.MailService;
|
import org.thingsboard.rule.engine.api.MailService;
|
||||||
|
import org.thingsboard.rule.engine.api.TbEmail;
|
||||||
import org.thingsboard.server.common.data.User;
|
import org.thingsboard.server.common.data.User;
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
|
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
|
||||||
@ -36,7 +37,12 @@ public class EmailNotificationChannel implements NotificationChannel<User, Email
|
|||||||
@Override
|
@Override
|
||||||
public ListenableFuture<Void> sendNotification(User recipient, EmailDeliveryMethodNotificationTemplate processedTemplate, NotificationProcessingContext ctx) {
|
public ListenableFuture<Void> sendNotification(User recipient, EmailDeliveryMethodNotificationTemplate processedTemplate, NotificationProcessingContext ctx) {
|
||||||
return executor.submit(() -> {
|
return executor.submit(() -> {
|
||||||
mailService.sendEmail(recipient.getTenantId(), recipient.getEmail(), processedTemplate.getSubject(), processedTemplate.getBody());
|
mailService.send(recipient.getTenantId(), null, TbEmail.builder()
|
||||||
|
.to(recipient.getEmail())
|
||||||
|
.subject(processedTemplate.getSubject())
|
||||||
|
.body(processedTemplate.getBody())
|
||||||
|
.html(true)
|
||||||
|
.build());
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -44,6 +44,8 @@ import java.util.List;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isNotEmpty;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class DefaultSlackService implements SlackService {
|
public class DefaultSlackService implements SlackService {
|
||||||
@ -80,7 +82,14 @@ public class DefaultSlackService implements SlackService {
|
|||||||
.map(user -> {
|
.map(user -> {
|
||||||
SlackConversation conversation = new SlackConversation();
|
SlackConversation conversation = new SlackConversation();
|
||||||
conversation.setId(user.getId());
|
conversation.setId(user.getId());
|
||||||
conversation.setName(String.format("@%s (%s)", user.getName(), user.getRealName()));
|
conversation.setShortName(user.getName());
|
||||||
|
conversation.setWholeName(user.getProfile() != null ? user.getProfile().getRealNameNormalized() : user.getRealName());
|
||||||
|
conversation.setEmail(user.getProfile() != null ? user.getProfile().getEmail() : null);
|
||||||
|
String title = "@" + conversation.getShortName();
|
||||||
|
if (isNotEmpty(conversation.getWholeName()) && !conversation.getWholeName().equals(conversation.getShortName())) {
|
||||||
|
title += " (" + conversation.getWholeName() + ")";
|
||||||
|
}
|
||||||
|
conversation.setTitle(title);
|
||||||
return conversation;
|
return conversation;
|
||||||
})
|
})
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
@ -99,7 +108,9 @@ public class DefaultSlackService implements SlackService {
|
|||||||
.map(channel -> {
|
.map(channel -> {
|
||||||
SlackConversation conversation = new SlackConversation();
|
SlackConversation conversation = new SlackConversation();
|
||||||
conversation.setId(channel.getId());
|
conversation.setId(channel.getId());
|
||||||
conversation.setName("#" + channel.getName());
|
conversation.setShortName(channel.getName());
|
||||||
|
conversation.setWholeName(channel.getNameNormalized());
|
||||||
|
conversation.setTitle("#" + channel.getName());
|
||||||
return conversation;
|
return conversation;
|
||||||
})
|
})
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
@ -111,7 +122,7 @@ public class DefaultSlackService implements SlackService {
|
|||||||
public SlackConversation findConversation(TenantId tenantId, String token, SlackConversationType conversationType, String namePattern) {
|
public SlackConversation findConversation(TenantId tenantId, String token, SlackConversationType conversationType, String namePattern) {
|
||||||
List<SlackConversation> conversations = listConversations(tenantId, token, conversationType);
|
List<SlackConversation> conversations = listConversations(tenantId, token, conversationType);
|
||||||
return conversations.stream()
|
return conversations.stream()
|
||||||
.filter(conversation -> StringUtils.containsIgnoreCase(conversation.getName(), namePattern))
|
.filter(conversation -> StringUtils.containsIgnoreCase(conversation.getTitle(), namePattern))
|
||||||
.findFirst().orElse(null);
|
.findFirst().orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -498,7 +498,10 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
|
|||||||
notificationTarget.setTenantId(tenantId);
|
notificationTarget.setTenantId(tenantId);
|
||||||
notificationTarget.setName(conversationName + " in Slack");
|
notificationTarget.setName(conversationName + " in Slack");
|
||||||
SlackNotificationTargetConfig targetConfig = new SlackNotificationTargetConfig();
|
SlackNotificationTargetConfig targetConfig = new SlackNotificationTargetConfig();
|
||||||
targetConfig.setConversation(new SlackConversation(conversationId, conversationName));
|
targetConfig.setConversation(SlackConversation.builder()
|
||||||
|
.id(conversationId)
|
||||||
|
.title(conversationName)
|
||||||
|
.build());
|
||||||
notificationTarget.setConfiguration(targetConfig);
|
notificationTarget.setConfiguration(targetConfig);
|
||||||
notificationTarget = saveNotificationTarget(notificationTarget);
|
notificationTarget = saveNotificationTarget(notificationTarget);
|
||||||
|
|
||||||
|
|||||||
@ -30,6 +30,8 @@ import org.thingsboard.server.common.data.security.Authority;
|
|||||||
import org.thingsboard.server.common.data.validation.Length;
|
import org.thingsboard.server.common.data.validation.Length;
|
||||||
import org.thingsboard.server.common.data.validation.NoXss;
|
import org.thingsboard.server.common.data.validation.NoXss;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isNotEmpty;
|
||||||
|
|
||||||
@ApiModel
|
@ApiModel
|
||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
public class User extends SearchTextBasedWithAdditionalInfo<UserId> implements HasName, HasTenantId, HasCustomerId, NotificationRecipient {
|
public class User extends SearchTextBasedWithAdditionalInfo<UserId> implements HasName, HasTenantId, HasCustomerId, NotificationRecipient {
|
||||||
@ -165,6 +167,24 @@ public class User extends SearchTextBasedWithAdditionalInfo<UserId> implements H
|
|||||||
return getEmail();
|
return getEmail();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
public String getTitle() {
|
||||||
|
String title = "";
|
||||||
|
if (isNotEmpty(firstName)) {
|
||||||
|
title += firstName;
|
||||||
|
}
|
||||||
|
if (isNotEmpty(lastName)) {
|
||||||
|
if (!title.isEmpty()) {
|
||||||
|
title += " ";
|
||||||
|
}
|
||||||
|
title += lastName;
|
||||||
|
}
|
||||||
|
if (title.isEmpty()) {
|
||||||
|
title = email;
|
||||||
|
}
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
|
|||||||
@ -16,7 +16,6 @@
|
|||||||
package org.thingsboard.server.common.data.notification;
|
package org.thingsboard.server.common.data.notification;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.thingsboard.server.common.data.User;
|
|
||||||
import org.thingsboard.server.common.data.notification.template.DeliveryMethodNotificationTemplate;
|
import org.thingsboard.server.common.data.notification.template.DeliveryMethodNotificationTemplate;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@ -28,6 +27,6 @@ public class NotificationRequestPreview {
|
|||||||
private Map<NotificationDeliveryMethod, DeliveryMethodNotificationTemplate> processedTemplates;
|
private Map<NotificationDeliveryMethod, DeliveryMethodNotificationTemplate> processedTemplates;
|
||||||
private int totalRecipientsCount;
|
private int totalRecipientsCount;
|
||||||
private Map<String, Integer> recipientsCountByTarget;
|
private Map<String, Integer> recipientsCountByTarget;
|
||||||
private Collection<User> recipientsPreview;
|
private Collection<String> recipientsPreview;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,7 +19,6 @@ import com.fasterxml.jackson.annotation.JsonCreator;
|
|||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.thingsboard.server.common.data.User;
|
|
||||||
import org.thingsboard.server.common.data.notification.targets.NotificationRecipient;
|
import org.thingsboard.server.common.data.notification.targets.NotificationRecipient;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@ -33,6 +32,7 @@ public class NotificationRequestStats {
|
|||||||
|
|
||||||
private final Map<NotificationDeliveryMethod, AtomicInteger> sent;
|
private final Map<NotificationDeliveryMethod, AtomicInteger> sent;
|
||||||
private final Map<NotificationDeliveryMethod, Map<String, String>> errors;
|
private final Map<NotificationDeliveryMethod, Map<String, String>> errors;
|
||||||
|
private String error;
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
private final Map<NotificationDeliveryMethod, Set<Object>> processedRecipients;
|
private final Map<NotificationDeliveryMethod, Set<Object>> processedRecipients;
|
||||||
|
|
||||||
@ -44,9 +44,11 @@ public class NotificationRequestStats {
|
|||||||
|
|
||||||
@JsonCreator
|
@JsonCreator
|
||||||
public NotificationRequestStats(@JsonProperty("sent") Map<NotificationDeliveryMethod, AtomicInteger> sent,
|
public NotificationRequestStats(@JsonProperty("sent") Map<NotificationDeliveryMethod, AtomicInteger> sent,
|
||||||
@JsonProperty("errors") Map<NotificationDeliveryMethod, Map<String, String>> errors) {
|
@JsonProperty("errors") Map<NotificationDeliveryMethod, Map<String, String>> errors,
|
||||||
|
@JsonProperty("error") String error) {
|
||||||
this.sent = sent;
|
this.sent = sent;
|
||||||
this.errors = errors;
|
this.errors = errors;
|
||||||
|
this.error = error;
|
||||||
this.processedRecipients = Collections.emptyMap();
|
this.processedRecipients = Collections.emptyMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,13 +62,7 @@ public class NotificationRequestStats {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String errorMessage = error.getMessage();
|
String errorMessage = error.getMessage();
|
||||||
String key;
|
errors.computeIfAbsent(deliveryMethod, k -> new ConcurrentHashMap<>()).put(recipient.getTitle(), errorMessage);
|
||||||
if (recipient instanceof User) {
|
|
||||||
key = ((User) recipient).getEmail();
|
|
||||||
} else {
|
|
||||||
key = "";
|
|
||||||
}
|
|
||||||
errors.computeIfAbsent(deliveryMethod, k -> new ConcurrentHashMap<>()).put(key, errorMessage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean contains(NotificationDeliveryMethod deliveryMethod, Object recipientId) {
|
public boolean contains(NotificationDeliveryMethod deliveryMethod, Object recipientId) {
|
||||||
|
|||||||
@ -19,4 +19,12 @@ public interface NotificationRecipient {
|
|||||||
|
|
||||||
Object getId();
|
Object getId();
|
||||||
|
|
||||||
|
String getTitle();
|
||||||
|
|
||||||
|
String getFirstName();
|
||||||
|
|
||||||
|
String getLastName();
|
||||||
|
|
||||||
|
String getEmail();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,21 +15,47 @@
|
|||||||
*/
|
*/
|
||||||
package org.thingsboard.server.common.data.notification.targets.slack;
|
package org.thingsboard.server.common.data.notification.targets.slack;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.thingsboard.server.common.data.notification.targets.NotificationRecipient;
|
import org.thingsboard.server.common.data.notification.targets.NotificationRecipient;
|
||||||
|
|
||||||
import javax.validation.constraints.NotEmpty;
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isEmpty;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
public class SlackConversation implements NotificationRecipient {
|
public class SlackConversation implements NotificationRecipient {
|
||||||
|
|
||||||
@NotEmpty
|
@NotEmpty
|
||||||
private String id;
|
private String id;
|
||||||
@NotEmpty
|
@NotEmpty
|
||||||
private String name;
|
private String title;
|
||||||
|
|
||||||
|
private String shortName;
|
||||||
|
private String wholeName;
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
@Override
|
||||||
|
public String getFirstName() {
|
||||||
|
String firstName = StringUtils.contains(wholeName, " ") ? wholeName.split(" ")[0] : wholeName;
|
||||||
|
if (isEmpty(firstName)) {
|
||||||
|
firstName = shortName;
|
||||||
|
}
|
||||||
|
return firstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
@Override
|
||||||
|
public String getLastName() {
|
||||||
|
return StringUtils.contains(wholeName, " ") ? wholeName.split(" ")[1] : null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,7 @@ package org.thingsboard.server.dao.notification;
|
|||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.thingsboard.server.common.data.EntityType;
|
import org.thingsboard.server.common.data.EntityType;
|
||||||
import org.thingsboard.server.common.data.id.EntityId;
|
import org.thingsboard.server.common.data.id.EntityId;
|
||||||
import org.thingsboard.server.common.data.id.HasId;
|
import org.thingsboard.server.common.data.id.HasId;
|
||||||
@ -32,9 +33,11 @@ import org.thingsboard.server.dao.entity.EntityDaoService;
|
|||||||
import org.thingsboard.server.dao.notification.cache.NotificationRuleCacheKey;
|
import org.thingsboard.server.dao.notification.cache.NotificationRuleCacheKey;
|
||||||
import org.thingsboard.server.dao.notification.cache.NotificationRuleCacheValue;
|
import org.thingsboard.server.dao.notification.cache.NotificationRuleCacheValue;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@ -96,6 +99,7 @@ public class DefaultNotificationRuleService extends AbstractCachedEntityService<
|
|||||||
.getNotificationRules();
|
.getNotificationRules();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
public void deleteNotificationRuleById(TenantId tenantId, NotificationRuleId id) {
|
public void deleteNotificationRuleById(TenantId tenantId, NotificationRuleId id) {
|
||||||
NotificationRule notificationRule = findNotificationRuleById(tenantId, id);
|
NotificationRule notificationRule = findNotificationRuleById(tenantId, id);
|
||||||
@ -106,6 +110,14 @@ public class DefaultNotificationRuleService extends AbstractCachedEntityService<
|
|||||||
@Override
|
@Override
|
||||||
public void deleteNotificationRulesByTenantId(TenantId tenantId) {
|
public void deleteNotificationRulesByTenantId(TenantId tenantId) {
|
||||||
notificationRuleDao.removeByTenantId(tenantId);
|
notificationRuleDao.removeByTenantId(tenantId);
|
||||||
|
|
||||||
|
List<NotificationRuleCacheKey> cacheKeys = Arrays.stream(NotificationRuleTriggerType.values())
|
||||||
|
.map(triggerType -> NotificationRuleCacheKey.builder()
|
||||||
|
.tenantId(tenantId)
|
||||||
|
.triggerType(triggerType)
|
||||||
|
.build())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
cache.evict(cacheKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -216,8 +216,8 @@ public class DefaultNotificationSettingsService implements NotificationSettingsS
|
|||||||
List.of(affectedUser.getId()), "Send notification to user when any alarm was assigned to him");
|
List.of(affectedUser.getId()), "Send notification to user when any alarm was assigned to him");
|
||||||
|
|
||||||
NotificationTemplate ruleEngineComponentLifecycleFailureNotificationTemplate = createTemplate(tenantId, "Rule chain/node lifecycle failure notification", NotificationType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT,
|
NotificationTemplate ruleEngineComponentLifecycleFailureNotificationTemplate = createTemplate(tenantId, "Rule chain/node lifecycle failure notification", NotificationType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT,
|
||||||
|
"${action:capitalize} failure in Rule chain '${ruleChainName}'",
|
||||||
"${componentType} '${componentName}' failed to ${action}",
|
"${componentType} '${componentName}' failed to ${action}",
|
||||||
"Rule chain '${ruleChainName}' - ${action} failure:<br/>${error}",
|
|
||||||
"warning", "Go to rule chain", "/ruleChains/${ruleChainId}");
|
"warning", "Go to rule chain", "/ruleChains/${ruleChainId}");
|
||||||
RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig ruleEngineComponentLifecycleEventRuleTriggerConfig = new RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig();
|
RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig ruleEngineComponentLifecycleEventRuleTriggerConfig = new RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig();
|
||||||
ruleEngineComponentLifecycleEventRuleTriggerConfig.setRuleChains(null);
|
ruleEngineComponentLifecycleEventRuleTriggerConfig.setRuleChains(null);
|
||||||
|
|||||||
@ -343,11 +343,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<mat-divider class="divider"></mat-divider>
|
<mat-divider class="divider"></mat-divider>
|
||||||
<mat-chip-listbox>
|
<mat-chip-listbox>
|
||||||
<mat-chip *ngFor="let user of preview.recipientsPreview">
|
<mat-chip *ngFor="let recipientTitle of preview.recipientsPreview">
|
||||||
<span *ngIf="(user.firstName || user.lastName); else email">{{ user.firstName }} {{ user.lastName }}</span>
|
<span>{{ recipientTitle }}</span>
|
||||||
<ng-template #email>
|
|
||||||
{{ user.email }}
|
|
||||||
</ng-template>
|
|
||||||
</mat-chip>
|
</mat-chip>
|
||||||
</mat-chip-listbox>
|
</mat-chip-listbox>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@ -172,7 +172,7 @@ export class SentTableConfigResolver implements Resolve<EntityTableConfig<Notifi
|
|||||||
}
|
}
|
||||||
return `<div style="border-radius: 12px; height: 24px; line-height: 24px; padding: 0 10px; width: max-content; cursor: pointer;
|
return `<div style="border-radius: 12px; height: 24px; line-height: 24px; padding: 0 10px; width: max-content; cursor: pointer;
|
||||||
background-color: #D12730; color: #fff; font-weight: 500; margin-left: 8px" class="stats">
|
background-color: #D12730; color: #fff; font-weight: 500; margin-left: 8px" class="stats">
|
||||||
${countError} ${this.translate.instant('notification.fails')} >
|
${this.translate.instant('notification.fails', {count: countError})}
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -33,7 +33,7 @@
|
|||||||
[displayWith]="displaySlackConversationFn"
|
[displayWith]="displaySlackConversationFn"
|
||||||
>
|
>
|
||||||
<mat-option *ngFor="let template of slackConversation$ | async" [value]="template" class="template-option">
|
<mat-option *ngFor="let template of slackConversation$ | async" [value]="template" class="template-option">
|
||||||
<span [innerHTML]="template.name | highlight:slackSearchText"></span>
|
<span [innerHTML]="template.title | highlight:slackSearchText"></span>
|
||||||
</mat-option>
|
</mat-option>
|
||||||
<mat-option *ngIf="!(slackConversation$ | async)?.length" [value]="null" class="tb-not-found">
|
<mat-option *ngIf="!(slackConversation$ | async)?.length" [value]="null" class="tb-not-found">
|
||||||
<div class="tb-not-found-content" (click)="$event.stopPropagation()">
|
<div class="tb-not-found-content" (click)="$event.stopPropagation()">
|
||||||
|
|||||||
@ -177,7 +177,7 @@ export class SlackConversationAutocompleteComponent implements ControlValueAcces
|
|||||||
}
|
}
|
||||||
|
|
||||||
displaySlackConversationFn(slackConversation?: SlackConversation): string | undefined {
|
displaySlackConversationFn(slackConversation?: SlackConversation): string | undefined {
|
||||||
return slackConversation ? slackConversation.name : undefined;
|
return slackConversation ? slackConversation.title : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
private fetchSlackConversation(searchText?: string): Observable<Array<SlackConversation>> {
|
private fetchSlackConversation(searchText?: string): Observable<Array<SlackConversation>> {
|
||||||
@ -215,7 +215,7 @@ export class SlackConversationAutocompleteComponent implements ControlValueAcces
|
|||||||
|
|
||||||
private createSlackConversationFilter(query: string): (key: SlackConversation) => boolean {
|
private createSlackConversationFilter(query: string): (key: SlackConversation) => boolean {
|
||||||
const lowercaseQuery = query.toLowerCase();
|
const lowercaseQuery = query.toLowerCase();
|
||||||
return key => key.name.toLowerCase().includes(lowercaseQuery);
|
return key => key.title.toLowerCase().includes(lowercaseQuery);
|
||||||
}
|
}
|
||||||
|
|
||||||
private clearSlackCache(): void {
|
private clearSlackCache(): void {
|
||||||
|
|||||||
@ -72,7 +72,7 @@ export interface NotificationRequestPreview {
|
|||||||
totalRecipientsCount: number;
|
totalRecipientsCount: number;
|
||||||
recipientsCountByTarget: { [key in string]: number };
|
recipientsCountByTarget: { [key in string]: number };
|
||||||
processedTemplates: { [key in NotificationDeliveryMethod]: DeliveryMethodNotificationTemplate };
|
processedTemplates: { [key in NotificationDeliveryMethod]: DeliveryMethodNotificationTemplate };
|
||||||
recipientsPreview: Array<User>;
|
recipientsPreview: Array<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NotificationRequestStats {
|
export interface NotificationRequestStats {
|
||||||
@ -100,7 +100,10 @@ interface SlackNotificationDeliveryMethodConfig {
|
|||||||
|
|
||||||
export interface SlackConversation {
|
export interface SlackConversation {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
title: string;
|
||||||
|
shortName: string;
|
||||||
|
wholeName: string;
|
||||||
|
email: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NotificationRule extends Omit<BaseData<NotificationRuleId>, 'label'>{
|
export interface NotificationRule extends Omit<BaseData<NotificationRuleId>, 'label'>{
|
||||||
|
|||||||
@ -2784,16 +2784,16 @@
|
|||||||
"delivery-method": {
|
"delivery-method": {
|
||||||
"delivery-method": "Delivery method",
|
"delivery-method": "Delivery method",
|
||||||
"email": "Email",
|
"email": "Email",
|
||||||
"email-failed-sent": "Email messages failed sent",
|
"email-failed-sent": "Email",
|
||||||
"email-preview": "Email notification preview",
|
"email-preview": "Email notification preview",
|
||||||
"slack": "Slack",
|
"slack": "Slack",
|
||||||
"slack-failed-sent": "Slack messages failed sent",
|
"slack-failed-sent": "Slack",
|
||||||
"slack-preview": "Slack notification preview",
|
"slack-preview": "Slack notification preview",
|
||||||
"sms": "SMS",
|
"sms": "SMS",
|
||||||
"sms-failed-sent": "SMS messages failed sent",
|
"sms-failed-sent": "SMS",
|
||||||
"sms-preview": "SMS notification preview",
|
"sms-preview": "SMS notification preview",
|
||||||
"web": "Web",
|
"web": "Web",
|
||||||
"web-failed-sent": "Web messages failed sent",
|
"web-failed-sent": "Web",
|
||||||
"web-preview": "Web notification preview"
|
"web-preview": "Web notification preview"
|
||||||
},
|
},
|
||||||
"delivery-methods": "Delivery methods",
|
"delivery-methods": "Delivery methods",
|
||||||
@ -2811,8 +2811,8 @@
|
|||||||
"entity-action-trigger-settings": "Entity action trigger settings",
|
"entity-action-trigger-settings": "Entity action trigger settings",
|
||||||
"entity-type": "Entity type",
|
"entity-type": "Entity type",
|
||||||
"escalation-chain": "Escalation chain",
|
"escalation-chain": "Escalation chain",
|
||||||
"failed-send": "Failed send",
|
"failed-send": "Sending failures",
|
||||||
"fails": "Fails",
|
"fails": "{ count, plural, =1 {1 fail} other {# fails} }",
|
||||||
"filter": "Filter",
|
"filter": "Filter",
|
||||||
"first-recipient": "First recipient",
|
"first-recipient": "First recipient",
|
||||||
"inactive": "Inactive",
|
"inactive": "Inactive",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user