diff --git a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java index 23aaba0e48..c3e9215d90 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java +++ b/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 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; } diff --git a/application/src/main/java/org/thingsboard/server/service/notification/NotificationProcessingContext.java b/application/src/main/java/org/thingsboard/server/service/notification/NotificationProcessingContext.java index 25ff1dd50e..27a9cabe43 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/NotificationProcessingContext.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/NotificationProcessingContext.java @@ -128,6 +128,7 @@ public class NotificationProcessingContext { private Map createTemplateContextForRecipient(NotificationRecipient recipient) { return Map.of( + "recipientTitle", recipient.getTitle(), "recipientEmail", Strings.nullToEmpty(recipient.getEmail()), "recipientFirstName", Strings.nullToEmpty(recipient.getFirstName()), "recipientLastName", Strings.nullToEmpty(recipient.getLastName()) diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmCommentTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmCommentTriggerProcessor.java index f10e395a78..4378b9b017 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmCommentTriggerProcessor.java +++ b/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()) diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EntityActionTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EntityActionTriggerProcessor.java index 8b8a90e10e..04a551c569 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EntityActionTriggerProcessor.java +++ b/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(); } diff --git a/application/src/main/java/org/thingsboard/server/service/slack/DefaultSlackService.java b/application/src/main/java/org/thingsboard/server/service/slack/DefaultSlackService.java index a36b12f0fc..ee54a02620 100644 --- a/application/src/main/java/org/thingsboard/server/service/slack/DefaultSlackService.java +++ b/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 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); diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java index f6a296ab7e..851550eee7 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java @@ -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); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/User.java b/common/data/src/main/java/org/thingsboard/server/common/data/User.java index 4dc71e8c44..f629151d18 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/User.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/User.java @@ -74,7 +74,7 @@ public class User extends SearchTextBasedWithAdditionalInfo 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 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; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationType.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationType.java index d976c36b00..de07d03c67 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationType.java +++ b/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 } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmAssignmentNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmAssignmentNotificationInfo.java index 4328885509..416ce73abe 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmAssignmentNotificationInfo.java +++ b/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 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, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmCommentNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmCommentNotificationInfo.java index 1122741888..00e8a0dc55 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmCommentNotificationInfo.java +++ b/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, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EntityActionNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EntityActionNotificationInfo.java index 43be1e3e2a..3cb3a0a739 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EntityActionNotificationInfo.java +++ b/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 diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/slack/SlackConversation.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/slack/SlackConversation.java index 076d51f62c..ed9dee71b6 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/slack/SlackConversation.java +++ b/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 ? "@" : "#"; + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java index 038ffe342a..b9ab9a322e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java +++ b/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") diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/slack/SlackService.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/slack/SlackService.java index 129fe6bc05..18411ade1d 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/slack/SlackService.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/slack/SlackService.java @@ -27,8 +27,6 @@ public interface SlackService { List listConversations(TenantId tenantId, String token, SlackConversationType conversationType); - SlackConversation findConversation(TenantId tenantId, String token, SlackConversationType conversationType, String namePattern); - String getToken(TenantId tenantId); } diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 9ca2fd416f..ff9add212d 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/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",