Slack recipients preview and recipient params; refactoring

This commit is contained in:
ViacheslavKlimov 2023-04-03 12:21:09 +03:00
parent 299c159ca1
commit dc3ca22e63
19 changed files with 141 additions and 70 deletions

View File

@ -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());
} else {
recipientsCount = 1;
recipientsPart = List.of(((SlackNotificationTargetConfig) target.getConfiguration()).getConversation());
}
for (NotificationRecipient recipient : recipientsPart) {
if (recipientsPreview.size() < recipientsPreviewSize) { if (recipientsPreview.size() < recipientsPreviewSize) {
recipientsPreview.add(recipient); recipientsPreview.add(recipient.getTitle());
} else { } else {
break; break;
} }
} }
} else {
recipientsCount = 1;
}
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());

View File

@ -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());

View File

@ -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) {
User user = (User) recipient;
return Map.of( return Map.of(
"recipientEmail", user.getEmail(), "recipientEmail", Strings.nullToEmpty(recipient.getEmail()),
"recipientFirstName", Strings.nullToEmpty(user.getFirstName()), "recipientFirstName", Strings.nullToEmpty(recipient.getFirstName()),
"recipientLastName", Strings.nullToEmpty(user.getLastName()) "recipientLastName", Strings.nullToEmpty(recipient.getLastName())
); );
} }
return Collections.emptyMap();
}
public CustomerId getCustomerId() {
if (request.getInfo() instanceof RuleOriginatedNotificationInfo) {
return ((RuleOriginatedNotificationInfo) request.getInfo()).getAffectedCustomerId();
} else {
return null;
}
}
} }

View File

@ -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;
}); });
} }

View File

@ -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);
} }

View File

@ -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);

View File

@ -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();

View File

@ -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;
} }

View File

@ -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) {

View File

@ -19,4 +19,12 @@ public interface NotificationRecipient {
Object getId(); Object getId();
String getTitle();
String getFirstName();
String getLastName();
String getEmail();
} }

View File

@ -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;
}
} }

View File

@ -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

View File

@ -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);

View File

@ -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 }}&nbsp;{{ 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>

View File

@ -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>`;
} }

View File

@ -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()">

View File

@ -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 {

View File

@ -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'>{

View File

@ -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",