Browse Source

User title template param, improve notification preview, improve default notifications

pull/8423/head
ViacheslavKlimov 3 years ago
parent
commit
b71257ba23
  1. 15
      application/src/main/java/org/thingsboard/server/controller/NotificationController.java
  2. 1
      application/src/main/java/org/thingsboard/server/service/notification/NotificationProcessingContext.java
  3. 6
      application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmCommentTriggerProcessor.java
  4. 16
      application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EntityActionTriggerProcessor.java
  5. 23
      application/src/main/java/org/thingsboard/server/service/slack/DefaultSlackService.java
  6. 8
      application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java
  7. 6
      common/data/src/main/java/org/thingsboard/server/common/data/User.java
  8. 2
      common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationType.java
  9. 3
      common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmAssignmentNotificationInfo.java
  10. 2
      common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmCommentNotificationInfo.java
  11. 2
      common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EntityActionNotificationInfo.java
  12. 24
      common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/slack/SlackConversation.java
  13. 8
      dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java
  14. 2
      rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/slack/SlackService.java
  15. 2
      ui-ngx/src/assets/locale/locale.constant-en_US.json

15
application/src/main/java/org/thingsboard/server/controller/NotificationController.java

@ -47,11 +47,13 @@ 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.NotificationTargetType;
import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig;
import org.thingsboard.server.common.data.notification.targets.slack.SlackConversation;
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.NotificationTemplate;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.SortOrder;
import org.thingsboard.server.dao.notification.NotificationRequestService;
import org.thingsboard.server.dao.notification.NotificationService;
import org.thingsboard.server.dao.notification.NotificationSettingsService;
@ -230,7 +232,8 @@ public class NotificationController extends BaseController {
NotificationTargetType targetType = target.getConfiguration().getType();
if (targetType == NotificationTargetType.PLATFORM_USERS) {
PageData<User> recipients = notificationTargetService.findRecipientsForNotificationTargetConfig(user.getTenantId(),
(PlatformUsersNotificationTargetConfig) target.getConfiguration(), new PageLink(recipientsPreviewSize));
(PlatformUsersNotificationTargetConfig) target.getConfiguration(), new PageLink(recipientsPreviewSize, 0, null,
new SortOrder("createdTime", SortOrder.Direction.DESC)));
recipientsCount = (int) recipients.getTotalElements();
recipientsPart = recipients.getData().stream().map(r -> (NotificationRecipient) r).collect(Collectors.toList());
} else {
@ -240,7 +243,15 @@ public class NotificationController extends BaseController {
firstRecipient.putIfAbsent(targetType, !recipientsPart.isEmpty() ? recipientsPart.get(0) : null);
for (NotificationRecipient recipient : recipientsPart) {
if (recipientsPreview.size() < recipientsPreviewSize) {
recipientsPreview.add(recipient.getTitle());
String title = recipient.getTitle();
if (recipient instanceof SlackConversation) {
title = ((SlackConversation) recipient).getPointer() + title;
} else if (recipient instanceof User) {
if (!title.equals(recipient.getEmail())) {
title += " (" + recipient.getEmail() + ")";
}
}
recipientsPreview.add(title);
} else {
break;
}

1
application/src/main/java/org/thingsboard/server/service/notification/NotificationProcessingContext.java

@ -128,6 +128,7 @@ public class NotificationProcessingContext {
private Map<String, String> createTemplateContextForRecipient(NotificationRecipient recipient) {
return Map.of(
"recipientTitle", recipient.getTitle(),
"recipientEmail", Strings.nullToEmpty(recipient.getEmail()),
"recipientFirstName", Strings.nullToEmpty(recipient.getFirstName()),
"recipientLastName", Strings.nullToEmpty(recipient.getLastName())

6
application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmCommentTriggerProcessor.java

@ -66,9 +66,9 @@ public class AlarmCommentTriggerProcessor implements RuleEngineMsgNotificationRu
return AlarmCommentNotificationInfo.builder()
.comment(comment.getComment().get("text").asText())
.action(msg.getType().equals(DataConstants.COMMENT_CREATED) ? "added" : "updated")
.userEmail(trigger.getMsg().getMetaData().getValue("userEmail"))
.userFirstName(trigger.getMsg().getMetaData().getValue("userFirstName"))
.userLastName(trigger.getMsg().getMetaData().getValue("userLastName"))
.userEmail(msg.getMetaData().getValue("userEmail"))
.userFirstName(msg.getMetaData().getValue("userFirstName"))
.userLastName(msg.getMetaData().getValue("userLastName"))
.alarmId(alarmInfo.getUuidId())
.alarmType(alarmInfo.getType())
.alarmOriginator(alarmInfo.getOriginator())

16
application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EntityActionTriggerProcessor.java

@ -19,11 +19,13 @@ import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.notification.info.EntityActionNotificationInfo;
import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo;
import org.thingsboard.server.common.data.notification.rule.trigger.EntityActionNotificationRuleTriggerConfig;
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.common.msg.notification.trigger.RuleEngineMsgTrigger;
import java.util.Optional;
@ -63,15 +65,17 @@ public class EntityActionTriggerProcessor implements RuleEngineMsgNotificationRu
ActionType actionType = msgType.equals(DataConstants.ENTITY_CREATED) ? ActionType.ADDED :
msgType.equals(DataConstants.ENTITY_UPDATED) ? ActionType.UPDATED :
msgType.equals(DataConstants.ENTITY_DELETED) ? ActionType.DELETED : null;
TbMsgMetaData metaData = msg.getMetaData();
return EntityActionNotificationInfo.builder()
.entityId(msg.getOriginator())
.entityName(msg.getMetaData().getValue("entityName"))
.entityName(metaData.getValue("entityName"))
.actionType(actionType)
.userId(UUID.fromString(msg.getMetaData().getValue("userId")))
.userEmail(trigger.getMsg().getMetaData().getValue("userEmail"))
.userFirstName(trigger.getMsg().getMetaData().getValue("userFirstName"))
.userLastName(trigger.getMsg().getMetaData().getValue("userLastName"))
.entityCustomerId(msg.getCustomerId())
.userId(UUID.fromString(metaData.getValue("userId")))
.userEmail(metaData.getValue("userEmail"))
.userFirstName(metaData.getValue("userFirstName"))
.userLastName(metaData.getValue("userLastName"))
.entityCustomerId(Optional.ofNullable(metaData.getValue("customerId"))
.map(UUID::fromString).map(CustomerId::new).orElse(null))
.build();
}

23
application/src/main/java/org/thingsboard/server/service/slack/DefaultSlackService.java

@ -28,7 +28,6 @@ import com.slack.api.methods.response.conversations.ConversationsListResponse;
import com.slack.api.methods.response.users.UsersListResponse;
import com.slack.api.model.ConversationType;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.thingsboard.rule.engine.api.slack.SlackService;
import org.thingsboard.server.common.data.id.TenantId;
@ -44,8 +43,6 @@ import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static org.apache.commons.lang3.StringUtils.isNotEmpty;
@Service
@RequiredArgsConstructor
public class DefaultSlackService implements SlackService {
@ -81,15 +78,11 @@ public class DefaultSlackService implements SlackService {
.filter(user -> !user.isDeleted() && !user.isStranger() && !user.isBot())
.map(user -> {
SlackConversation conversation = new SlackConversation();
conversation.setType(conversationType);
conversation.setId(user.getId());
conversation.setShortName(user.getName());
conversation.setName(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;
})
.collect(Collectors.toList());
@ -107,10 +100,10 @@ public class DefaultSlackService implements SlackService {
.filter(channel -> !channel.isArchived())
.map(channel -> {
SlackConversation conversation = new SlackConversation();
conversation.setType(conversationType);
conversation.setId(channel.getId());
conversation.setShortName(channel.getName());
conversation.setName(channel.getName());
conversation.setWholeName(channel.getNameNormalized());
conversation.setTitle("#" + channel.getName());
return conversation;
})
.collect(Collectors.toList());
@ -118,14 +111,6 @@ public class DefaultSlackService implements SlackService {
});
}
@Override
public SlackConversation findConversation(TenantId tenantId, String token, SlackConversationType conversationType, String namePattern) {
List<SlackConversation> conversations = listConversations(tenantId, token, conversationType);
return conversations.stream()
.filter(conversation -> StringUtils.containsIgnoreCase(conversation.getTitle(), namePattern))
.findFirst().orElse(null);
}
@Override
public String getToken(TenantId tenantId) {
NotificationSettings settings = notificationSettingsService.findNotificationSettings(tenantId);

8
application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java

@ -362,13 +362,14 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
SlackNotificationTargetConfig slackTargetConfig = new SlackNotificationTargetConfig();
slackTargetConfig.setConversationType(SlackConversationType.DIRECT);
SlackConversation slackConversation = new SlackConversation();
slackConversation.setType(SlackConversationType.DIRECT);
slackConversation.setId("U1234567");
slackConversation.setTitle("@jdoe (John Doe)");
slackConversation.setName("jdoe");
slackConversation.setWholeName("John Doe");
slackTargetConfig.setConversation(slackConversation);
slackTarget.setConfiguration(slackTargetConfig);
slackTarget = saveNotificationTarget(slackTarget);
recipients.add(slackConversation.getTitle());
recipients.add("@" + slackConversation.getWholeName());
NotificationTemplate notificationTemplate = new NotificationTemplate();
notificationTemplate.setNotificationType(NotificationType.GENERAL);
@ -500,8 +501,9 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
notificationTarget.setName(conversationName + " in Slack");
SlackNotificationTargetConfig targetConfig = new SlackNotificationTargetConfig();
targetConfig.setConversation(SlackConversation.builder()
.type(SlackConversationType.DIRECT)
.id(conversationId)
.title(conversationName)
.name(conversationName)
.build());
notificationTarget.setConfiguration(targetConfig);
notificationTarget = saveNotificationTarget(notificationTarget);

6
common/data/src/main/java/org/thingsboard/server/common/data/User.java

@ -74,7 +74,7 @@ public class User extends SearchTextBasedWithAdditionalInfo<UserId> implements H
@ApiModelProperty(position = 1, value = "JSON object with the User Id. " +
"Specify this field to update the device. " +
"Referencing non-existing User Id will cause error. " +
"Omit this field to create new customer." )
"Omit this field to create new customer.")
@Override
public UserId getId() {
return super.getId();
@ -169,6 +169,10 @@ public class User extends SearchTextBasedWithAdditionalInfo<UserId> implements H
@JsonIgnore
public String getTitle() {
return getTitle(email, firstName, lastName);
}
public static String getTitle(String email, String firstName, String lastName) {
String title = "";
if (isNotEmpty(firstName)) {
title += firstName;

2
common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationType.java

@ -27,6 +27,6 @@ public enum NotificationType {
NEW_PLATFORM_VERSION,
ENTITIES_LIMIT,
API_USAGE_LIMIT,
RULE_ENGINE
RULE_NODE
}

3
common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmAssignmentNotificationInfo.java

@ -19,6 +19,7 @@ import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.alarm.AlarmStatus;
import org.thingsboard.server.common.data.id.CustomerId;
@ -59,10 +60,12 @@ public class AlarmAssignmentNotificationInfo implements RuleOriginatedNotificati
public Map<String, String> getTemplateData() {
return mapOf(
"action", action,
"assigneeTitle", User.getTitle(assigneeEmail, assigneeFirstName, assigneeLastName),
"assigneeFirstName", assigneeFirstName,
"assigneeLastName", assigneeLastName,
"assigneeEmail", assigneeEmail,
"assigneeId", assigneeId != null ? assigneeId.toString() : null,
"userTitle", User.getTitle(userEmail, userFirstName, userLastName),
"userEmail", userEmail,
"userFirstName", userFirstName,
"userLastName", userLastName,

2
common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmCommentNotificationInfo.java

@ -19,6 +19,7 @@ import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.alarm.AlarmStatus;
import org.thingsboard.server.common.data.id.CustomerId;
@ -55,6 +56,7 @@ public class AlarmCommentNotificationInfo implements RuleOriginatedNotificationI
return mapOf(
"comment", comment,
"action", action,
"userTitle", User.getTitle(userEmail, userFirstName, userLastName),
"userEmail", userEmail,
"userFirstName", userFirstName,
"userLastName", userLastName,

2
common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EntityActionNotificationInfo.java

@ -19,6 +19,7 @@ import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
@ -52,6 +53,7 @@ public class EntityActionNotificationInfo implements RuleOriginatedNotificationI
"entityName", entityName,
"actionType", actionType.name().toLowerCase(),
"userId", userId.toString(),
"userTitle", User.getTitle(userEmail, userFirstName, userLastName),
"userEmail", userEmail,
"userFirstName", userFirstName,
"userLastName", userLastName

24
common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/slack/SlackConversation.java

@ -16,6 +16,7 @@
package org.thingsboard.server.common.data.notification.targets.slack;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
@ -24,6 +25,7 @@ import org.apache.commons.lang3.StringUtils;
import org.thingsboard.server.common.data.notification.targets.NotificationRecipient;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import static org.apache.commons.lang3.StringUtils.isEmpty;
@ -31,23 +33,34 @@ import static org.apache.commons.lang3.StringUtils.isEmpty;
@NoArgsConstructor
@AllArgsConstructor
@Builder
@JsonIgnoreProperties(ignoreUnknown = true)
public class SlackConversation implements NotificationRecipient {
@NotNull
private SlackConversationType type;
@NotEmpty
private String id;
@NotEmpty
private String title;
private String name;
private String shortName;
private String wholeName;
private String email;
@Override
public String getTitle() {
if (type == SlackConversationType.DIRECT) {
return StringUtils.defaultIfEmpty(wholeName, name);
} else {
return name;
}
}
@JsonIgnore
@Override
public String getFirstName() {
String firstName = StringUtils.contains(wholeName, " ") ? wholeName.split(" ")[0] : wholeName;
if (isEmpty(firstName)) {
firstName = shortName;
firstName = name;
}
return firstName;
}
@ -58,4 +71,9 @@ public class SlackConversation implements NotificationRecipient {
return StringUtils.contains(wholeName, " ") ? wholeName.split(" ")[1] : null;
}
@JsonIgnore
public String getPointer() {
return type == SlackConversationType.DIRECT ? "@" : "#";
}
}

8
dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java

@ -71,7 +71,7 @@ public class DefaultNotifications {
.type(NotificationType.ENTITIES_LIMIT)
.subject("${entityType}s limit will be reached soon for tenant ${tenantName}")
.text("${entityType}s usage: ${currentCount}/${limit} (${percents}%)")
.icon("warning").color("#D6D634")
.icon("warning").color("#F9D916")
.rule(DefaultRule.builder()
.name("Entities count limit (sysadmin)")
.triggerConfig(EntitiesLimitNotificationRuleTriggerConfig.builder()
@ -82,7 +82,7 @@ public class DefaultNotifications {
.build();
public static final DefaultNotification entitiesLimitForTenant = entitiesLimitForSysadmin.toBuilder()
.name("Entities count limit notification for tenant")
.subject("${entityType}s limit will be reached soon")
.subject("WARNING: ${entityType}s limit will be reached soon")
.rule(entitiesLimitForSysadmin.getRule().toBuilder()
.name("Entities count limit")
.description("Send notification to tenant admins when count of entities of some type reached 80% threshold of the limit")
@ -93,7 +93,7 @@ public class DefaultNotifications {
.type(NotificationType.API_USAGE_LIMIT)
.subject("${feature} feature will be disabled soon for tenant ${tenantName}")
.text("Usage: ${currentValue} out of ${limit} ${unitLabel}s")
.icon("warning").color("#D6D634")
.icon("warning").color("#F9D916")
.rule(DefaultRule.builder()
.name("API feature warning (sysadmin)")
.triggerConfig(ApiUsageLimitNotificationRuleTriggerConfig.builder()
@ -105,7 +105,7 @@ public class DefaultNotifications {
.build();
public static final DefaultNotification apiFeatureWarningForTenant = apiFeatureWarningForSysadmin.toBuilder()
.name("API feature warning notification for tenant")
.subject("${feature} feature will be disabled soon")
.subject("WARNING: ${feature} feature will be disabled soon")
.rule(apiFeatureWarningForSysadmin.getRule().toBuilder()
.name("API feature warning")
.description("Send notification to tenant admins on API feature usage WARNING state")

2
rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/slack/SlackService.java

@ -27,8 +27,6 @@ public interface SlackService {
List<SlackConversation> listConversations(TenantId tenantId, String token, SlackConversationType conversationType);
SlackConversation findConversation(TenantId tenantId, String token, SlackConversationType conversationType, String namePattern);
String getToken(TenantId tenantId);
}

2
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -2960,7 +2960,7 @@
"entity-action": "Entity action",
"general": "General",
"rule-engine-lifecycle-event": "Rule engine lifecycle event",
"rule-engine": "Rule engine",
"rule-node": "Rule node",
"new-platform-version": "New platform version"
},
"templates": "Templates",

Loading…
Cancel
Save