Browse Source

Slack notification target; refactoring

pull/8046/head
ViacheslavKlimov 4 years ago
parent
commit
be5eed96dc
  1. 1
      application/src/main/data/upgrade/3.4.3/schema_update.sql
  2. 17
      application/src/main/java/org/thingsboard/server/controller/NotificationController.java
  3. 31
      application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java
  4. 5
      application/src/main/java/org/thingsboard/server/controller/NotificationTemplateController.java
  5. 137
      application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java
  6. 2
      application/src/main/java/org/thingsboard/server/service/notification/channels/EmailNotificationChannel.java
  7. 8
      application/src/main/java/org/thingsboard/server/service/notification/channels/NotificationChannel.java
  8. 10
      application/src/main/java/org/thingsboard/server/service/notification/channels/SlackNotificationChannel.java
  9. 2
      application/src/main/java/org/thingsboard/server/service/notification/channels/SmsNotificationChannel.java
  10. 11
      application/src/main/java/org/thingsboard/server/service/slack/DefaultSlackService.java
  11. 14
      application/src/test/java/org/thingsboard/server/service/notification/AbstractNotificationApiTest.java
  12. 61
      application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java
  13. 59
      application/src/test/java/org/thingsboard/server/service/notification/NotificationTargetApiTest.java
  14. 3
      common/data/src/main/java/org/thingsboard/server/common/data/User.java
  15. 15
      common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationDeliveryMethod.java
  16. 3
      common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationProcessingContext.java
  17. 23
      common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestStats.java
  18. 12
      common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationRecipient.java
  19. 2
      common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTarget.java
  20. 9
      common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfig.java
  21. 33
      common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetType.java
  22. 14
      common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/AllUsersFilter.java
  23. 10
      common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/CustomerUsersFilter.java
  24. 39
      common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/PlatformUsersNotificationTargetConfig.java
  25. 10
      common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UserListFilter.java
  26. 36
      common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UsersFilter.java
  27. 4
      common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UsersFilterType.java
  28. 29
      common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/slack/SlackConversation.java
  29. 22
      common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/slack/SlackConversationType.java
  30. 40
      common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/slack/SlackNotificationTargetConfig.java
  31. 1
      common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.java
  32. 10
      common/data/src/main/java/org/thingsboard/server/common/data/notification/template/SlackDeliveryMethodNotificationTemplate.java
  33. 1
      dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
  34. 10
      dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationTargetEntity.java
  35. 16
      dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java
  36. 1
      dao/src/main/resources/sql/schema-entities.sql
  37. 7
      rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/slack/SlackService.java
  38. 2
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbSlackNode.java
  39. 6
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbSlackNodeConfiguration.java

1
application/src/main/data/upgrade/3.4.3/schema_update.sql

@ -19,6 +19,7 @@ CREATE TABLE IF NOT EXISTS notification_target (
created_time BIGINT NOT NULL,
tenant_id UUID NULL CONSTRAINT fk_notification_target_tenant_id REFERENCES tenant(id) ON DELETE CASCADE,
name VARCHAR(255) NOT NULL,
type VARCHAR(32) NOT NULL,
configuration VARCHAR(10000) NOT NULL,
CONSTRAINT uq_notification_target_name UNIQUE (tenant_id, name)
);

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

@ -44,6 +44,7 @@ import org.thingsboard.server.common.data.notification.NotificationRequestInfo;
import org.thingsboard.server.common.data.notification.NotificationRequestPreview;
import org.thingsboard.server.common.data.notification.settings.NotificationSettings;
import org.thingsboard.server.common.data.notification.targets.NotificationTarget;
import org.thingsboard.server.common.data.notification.targets.NotificationTargetType;
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;
@ -59,6 +60,7 @@ import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
import javax.validation.Valid;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@ -198,11 +200,15 @@ public class NotificationController extends BaseController {
.settings(null)
.template(notificationTemplate)
.build();
mockProcessingCtx.init();
Map<String, String> templateContext = mockProcessingCtx.createTemplateContext(user);
Map<NotificationDeliveryMethod, DeliveryMethodNotificationTemplate> processedTemplates = mockProcessingCtx.getDeliveryMethods().stream()
.collect(Collectors.toMap(m -> m, deliveryMethod -> {
Map<String, String> templateContext;
if (NotificationTargetType.PLATFORM_USERS.getSupportedDeliveryMethods().contains(deliveryMethod)) {
templateContext = mockProcessingCtx.createTemplateContext(user);
} else {
templateContext = Collections.emptyMap();
}
return mockProcessingCtx.getProcessedTemplate(deliveryMethod, templateContext);
}));
preview.setProcessedTemplates(processedTemplates);
@ -214,7 +220,12 @@ public class NotificationController extends BaseController {
throw new IllegalArgumentException("Notification target with id " + targetId + " not found");
}
int recipientsCount = notificationTargetService.countRecipientsForNotificationTargetConfig(user.getTenantId(), notificationTarget.getConfiguration());
int recipientsCount;
if (notificationTarget.getType() == NotificationTargetType.PLATFORM_USERS) {
recipientsCount = notificationTargetService.countRecipientsForNotificationTargetConfig(user.getTenantId(), notificationTarget.getConfiguration());
} else {
recipientsCount = 1;
}
recipientsCountByTarget.put(notificationTarget.getName(), recipientsCount);
});
preview.setRecipientsCountByTarget(recipientsCountByTarget);

31
application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java

@ -34,6 +34,7 @@ import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.NotificationTargetId;
import org.thingsboard.server.common.data.notification.targets.NotificationTarget;
import org.thingsboard.server.common.data.notification.targets.NotificationTargetConfig;
import org.thingsboard.server.common.data.notification.targets.NotificationTargetType;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageDataIterable;
import org.thingsboard.server.common.data.page.PageLink;
@ -59,15 +60,15 @@ public class NotificationTargetController extends BaseController {
@ApiOperation(value = "Save notification target (saveNotificationTarget)",
notes = "Create or update notification target.\n\n" +
"Examples with different configuration types:\n" +
"- USER_LIST:\n" +
"```\n{\n \"name\": \"Special users\",\n \"configuration\": {\n \"type\": \"USER_LIST\",\n \"usersIds\": [\n \"ea31a460-3d85-11ed-9200-77fc04fa14fa\",\n \"86f7b260-3d88-11ed-ad72-ad2ee0f70ba1\"\n ]\n }\n}\n```\n" +
"- CUSTOMER_USERS (not accessible to system administrator):\n" +
"```\n{\n \"name\": \"Users of my customer\",\n \"configuration\": {\n \"type\": \"CUSTOMER_USERS\",\n \"customerId\": \"ea31a460-3d85-11ed-9200-77fc04fa14fa\"\n }\n}\n```\n" +
"or if you would like to use the target in notification rule and get customerId from alarm:\n" +
"```\n{\n \"name\": \"Alarm's customer users\",\n \"configuration\": {\n \"type\": \"CUSTOMER_USERS\",\n \"customerId\": null,\n \"getCustomerIdFromOriginatorEntity\": true\n }\n}\n```\n" +
"- ALL_USERS:\n" +
"```\n{\n \"name\": \"All my users\",\n \"configuration\": {\n \"type\": \"ALL_USERS\"\n }\n}\n```\n\n" +
// "Examples with different configuration types:\n" +
// "- USER_LIST:\n" +
// "```\n{\n \"name\": \"Special users\",\n \"configuration\": {\n \"type\": \"USER_LIST\",\n \"usersIds\": [\n \"ea31a460-3d85-11ed-9200-77fc04fa14fa\",\n \"86f7b260-3d88-11ed-ad72-ad2ee0f70ba1\"\n ]\n }\n}\n```\n" +
// "- CUSTOMER_USERS (not accessible to system administrator):\n" +
// "```\n{\n \"name\": \"Users of my customer\",\n \"configuration\": {\n \"type\": \"CUSTOMER_USERS\",\n \"customerId\": \"ea31a460-3d85-11ed-9200-77fc04fa14fa\"\n }\n}\n```\n" +
// "or if you would like to use the target in notification rule and get customerId from alarm:\n" +
// "```\n{\n \"name\": \"Alarm's customer users\",\n \"configuration\": {\n \"type\": \"CUSTOMER_USERS\",\n \"customerId\": null,\n \"getCustomerIdFromOriginatorEntity\": true\n }\n}\n```\n" +
// "- ALL_USERS:\n" +
// "```\n{\n \"name\": \"All my users\",\n \"configuration\": {\n \"type\": \"ALL_USERS\"\n }\n}\n```\n\n" +
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PostMapping("/target")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@ -77,11 +78,13 @@ public class NotificationTargetController extends BaseController {
checkEntity(notificationTarget.getId(), notificationTarget, Resource.NOTIFICATION_TARGET);
if (!user.isSystemAdmin()) {
NotificationTargetConfig targetConfig = notificationTarget.getConfiguration();
PageDataIterable<User> recipients = new PageDataIterable<>(pageLink -> {
return notificationTargetService.findRecipientsForNotificationTargetConfig(user.getTenantId(), null, targetConfig, pageLink);
}, 200);
for (User recipient : recipients) {
accessControlService.checkPermission(user, Resource.USER, Operation.READ, recipient.getId(), recipient);
if (targetConfig.getType() == NotificationTargetType.PLATFORM_USERS) {
PageDataIterable<User> recipients = new PageDataIterable<>(pageLink -> {
return notificationTargetService.findRecipientsForNotificationTargetConfig(user.getTenantId(), null, targetConfig, pageLink);
}, 200);
for (User recipient : recipients) {
accessControlService.checkPermission(user, Resource.USER, Operation.READ, recipient.getId(), recipient);
}
}
}

5
application/src/main/java/org/thingsboard/server/controller/NotificationTemplateController.java

@ -35,8 +35,9 @@ import org.thingsboard.server.common.data.notification.NotificationDeliveryMetho
import org.thingsboard.server.common.data.notification.NotificationType;
import org.thingsboard.server.common.data.notification.settings.NotificationSettings;
import org.thingsboard.server.common.data.notification.settings.SlackNotificationDeliveryMethodConfig;
import org.thingsboard.server.common.data.notification.targets.slack.SlackConversationType;
import org.thingsboard.server.common.data.notification.template.NotificationTemplate;
import org.thingsboard.server.common.data.notification.template.SlackConversation;
import org.thingsboard.server.common.data.notification.targets.slack.SlackConversation;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.notification.NotificationSettingsService;
@ -133,7 +134,7 @@ public class NotificationTemplateController extends BaseController {
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@GetMapping("/slack/conversations")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
public List<SlackConversation> listSlackConversations(@RequestParam SlackConversation.Type type,
public List<SlackConversation> listSlackConversations(@RequestParam SlackConversationType type,
@AuthenticationPrincipal SecurityUser user) {
NotificationSettings settings = notificationSettingsService.findNotificationSettings(user.getTenantId());
SlackNotificationDeliveryMethodConfig slackConfig = (SlackNotificationDeliveryMethodConfig)

137
application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java

@ -40,14 +40,18 @@ import org.thingsboard.server.common.data.notification.NotificationRequestStatus
import org.thingsboard.server.common.data.notification.NotificationStatus;
import org.thingsboard.server.common.data.notification.NotificationType;
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.NotificationTarget;
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.notification.template.PushDeliveryMethodNotificationTemplate;
import org.thingsboard.server.common.data.page.PageDataIterable;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.notification.NotificationRequestService;
import org.thingsboard.server.dao.notification.NotificationService;
import org.thingsboard.server.dao.notification.NotificationSettingsService;
@ -79,7 +83,7 @@ import java.util.stream.Collectors;
@Slf4j
@RequiredArgsConstructor
@SuppressWarnings({"UnstableApiUsage", "rawtypes"})
public class DefaultNotificationCenter extends AbstractSubscriptionService implements NotificationCenter, NotificationChannel<PushDeliveryMethodNotificationTemplate> {
public class DefaultNotificationCenter extends AbstractSubscriptionService implements NotificationCenter, NotificationChannel<User, PushDeliveryMethodNotificationTemplate> {
private final NotificationTargetService notificationTargetService;
private final NotificationRequestService notificationRequestService;
@ -95,9 +99,17 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
@Override
public NotificationRequest processNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest) {
notificationRequest.setTenantId(tenantId);
NotificationSettings settings = notificationSettingsService.findNotificationSettings(tenantId);
NotificationTemplate notificationTemplate = notificationTemplateService.findNotificationTemplateById(tenantId, notificationRequest.getTemplateId());
List<NotificationTarget> targets = notificationRequest.getTargets().stream()
.map(NotificationTargetId::new)
.map(targetId -> notificationTargetService.findNotificationTargetById(tenantId, targetId))
.peek(target -> {
if (target == null) {
throw new IllegalArgumentException("Some of the targets no longer exist");
}
})
.collect(Collectors.toList());
notificationTemplate.getConfiguration().getDeliveryMethodsTemplates().forEach((deliveryMethod, template) -> {
if (!template.isEnabled()) return;
@ -110,6 +122,9 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
throw new IllegalArgumentException("Slack must be configured in the settings");
}
}
if (targets.stream().noneMatch(target -> target.getType().getSupportedDeliveryMethods().contains(deliveryMethod))) {
throw new IllegalArgumentException("Target for " + deliveryMethod + " delivery method is missing");
}
});
if (notificationRequest.getAdditionalConfig() != null) {
@ -122,64 +137,33 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
}
}
log.debug("Processing notification request (tenant id: {}, notification targets: {})", tenantId, notificationRequest.getTargets());
log.debug("Processing notification request (tenantId: {}, targets: {})", tenantId, notificationRequest.getTargets());
notificationRequest.setStatus(NotificationRequestStatus.PROCESSING);
NotificationRequest savedNotificationRequest = notificationRequestService.saveNotificationRequest(tenantId, notificationRequest);
notificationExecutor.submit(() -> {
NotificationProcessingContext ctx = NotificationProcessingContext.builder()
.tenantId(tenantId)
.request(savedNotificationRequest)
.settings(settings)
.template(notificationTemplate)
.build();
ctx.init();
NotificationProcessingContext ctx = NotificationProcessingContext.builder()
.tenantId(tenantId)
.request(savedNotificationRequest)
.settings(settings)
.template(notificationTemplate)
.build();
Set<NotificationDeliveryMethod> deliveryMethods = ctx.getDeliveryMethods();
notificationExecutor.submit(() -> {
List<ListenableFuture<Void>> results = new ArrayList<>();
for (UUID targetId : notificationRequest.getTargets()) {
DaoUtil.processBatches(pageLink -> {
return notificationTargetService.findRecipientsForNotificationTarget(tenantId, ctx.getCustomerId(), new NotificationTargetId(targetId), pageLink);
}, 200, recipientsBatch -> {
for (NotificationDeliveryMethod deliveryMethod : deliveryMethods) {
if (deliveryMethod.isStandalone()) continue;
List<User> recipients = recipientsBatch.getData();
log.debug("Sending {} notifications for request {} to recipients batch ({})", deliveryMethod, savedNotificationRequest.getId(), recipients.size());
NotificationChannel notificationChannel = channels.get(deliveryMethod);
for (User recipient : recipients) {
ListenableFuture<Void> resultFuture = process(notificationChannel, recipient, ctx);
DonAsynchron.withCallback(resultFuture, result -> {
ctx.getStats().reportSent(deliveryMethod, recipient);
}, error -> {
ctx.getStats().reportError(deliveryMethod, error, recipient);
});
results.add(resultFuture);
}
}
});
}
for (NotificationDeliveryMethod deliveryMethod : deliveryMethods) {
if (deliveryMethod.isStandalone()) {
NotificationChannel notificationChannel = channels.get(deliveryMethod);
ListenableFuture<Void> resultFuture = process(notificationChannel, null, ctx);
DonAsynchron.withCallback(resultFuture, result -> {
ctx.getStats().reportSent(deliveryMethod, null);
}, error -> {
ctx.getStats().reportError(deliveryMethod, error, null);
});
results.add(resultFuture);
}
for (NotificationTarget target : targets) {
List<ListenableFuture<Void>> result = processForTarget(target, ctx);
results.addAll(result);
}
Futures.whenAllComplete(results).run(() -> {
NotificationRequestId requestId = savedNotificationRequest.getId();
log.debug("[{}] Notification request processing is finished", requestId);
NotificationRequestStats stats = ctx.getStats();
try {
notificationRequestService.updateNotificationRequest(tenantId, savedNotificationRequest.getId(),
NotificationRequestStatus.SENT, stats);
notificationRequestService.updateNotificationRequest(tenantId, requestId, NotificationRequestStatus.SENT, stats);
} catch (Exception e) {
log.error("Failed to update stats for notification request {}", savedNotificationRequest.getId(), e);
log.error("[{}] Failed to update stats for notification request", requestId, e);
}
UserId senderId = notificationRequest.getSenderId();
@ -200,25 +184,70 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
return savedNotificationRequest;
}
private ListenableFuture<Void> process(NotificationChannel notificationChannel, User recipient, NotificationProcessingContext ctx) {
NotificationDeliveryMethod deliveryMethod = notificationChannel.getDeliveryMethod();
if (recipient != null && ctx.getStats().contains(deliveryMethod, recipient.getId())) {
private List<ListenableFuture<Void>> processForTarget(NotificationTarget target, NotificationProcessingContext ctx) {
Iterable<? extends NotificationRecipient> recipients;
switch (target.getType()) {
case PLATFORM_USERS: {
recipients = new PageDataIterable<>(pageLink -> {
return notificationTargetService.findRecipientsForNotificationTargetConfig(ctx.getTenantId(), ctx.getCustomerId(), target.getConfiguration(), pageLink);
}, 200);
break;
}
case SLACK: {
SlackNotificationTargetConfig slackTargetConfig = (SlackNotificationTargetConfig) target.getConfiguration();
SlackConversation conversation = new SlackConversation(slackTargetConfig.getConversationId(), slackTargetConfig.getConversationName());
recipients = List.of(conversation);
break;
}
default: {
recipients = Collections.emptyList();
}
}
Set<NotificationDeliveryMethod> deliveryMethods = new HashSet<>(ctx.getDeliveryMethods());
deliveryMethods.removeIf(deliveryMethod -> !target.getType().getSupportedDeliveryMethods().contains(deliveryMethod));
log.debug("[{}] Processing notification request for {} target ({}) for delivery methods {}", ctx.getRequest().getId(), target.getType(), target.getId(), deliveryMethods);
List<ListenableFuture<Void>> results = new ArrayList<>();
for (NotificationRecipient recipient : recipients) {
for (NotificationDeliveryMethod deliveryMethod : deliveryMethods) {
ListenableFuture<Void> resultFuture = processForRecipient(deliveryMethod, recipient, ctx);
DonAsynchron.withCallback(resultFuture, result -> {
ctx.getStats().reportSent(deliveryMethod, recipient);
}, error -> {
ctx.getStats().reportError(deliveryMethod, error, recipient);
});
results.add(resultFuture);
}
}
return results;
}
private ListenableFuture<Void> processForRecipient(NotificationDeliveryMethod deliveryMethod, NotificationRecipient recipient, NotificationProcessingContext ctx) {
if (ctx.getStats().contains(deliveryMethod, recipient.getId())) {
return Futures.immediateFailedFuture(new AlreadySentException());
}
Map<String, String> templateContext;
if (recipient instanceof User) {
templateContext = ctx.createTemplateContext(((User) recipient));
} else {
templateContext = Collections.emptyMap();
}
DeliveryMethodNotificationTemplate processedTemplate;
try {
Map<String, String> templateContext = recipient != null ? ctx.createTemplateContext(recipient) : Collections.emptyMap();
processedTemplate = ctx.getProcessedTemplate(deliveryMethod, templateContext);
} catch (Exception e) {
return Futures.immediateFailedFuture(e);
}
NotificationChannel notificationChannel = channels.get(deliveryMethod);
log.trace("[{}] Sending {} notification for recipient {}", ctx.getRequest().getId(), deliveryMethod, recipient);
return notificationChannel.sendNotification(recipient, processedTemplate, ctx);
}
@Override
public ListenableFuture<Void> sendNotification(User recipient, PushDeliveryMethodNotificationTemplate processedTemplate, NotificationProcessingContext ctx) {
NotificationRequest request = ctx.getRequest();
log.trace("Creating notification for recipient {} (notification request id: {})", recipient.getId(), request.getId());
Notification notification = Notification.builder()
.requestId(request.getId())
.recipientId(recipient.getId())

2
application/src/main/java/org/thingsboard/server/service/notification/channels/EmailNotificationChannel.java

@ -27,7 +27,7 @@ import org.thingsboard.server.common.data.notification.NotificationProcessingCon
@Component
@RequiredArgsConstructor
public class EmailNotificationChannel implements NotificationChannel<EmailDeliveryMethodNotificationTemplate> {
public class EmailNotificationChannel implements NotificationChannel<User, EmailDeliveryMethodNotificationTemplate> {
private final MailService mailService;
private final MailExecutorService executor;

8
application/src/main/java/org/thingsboard/server/service/notification/channels/NotificationChannel.java

@ -16,14 +16,14 @@
package org.thingsboard.server.service.notification.channels;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
import org.thingsboard.server.common.data.notification.template.DeliveryMethodNotificationTemplate;
import org.thingsboard.server.common.data.notification.NotificationProcessingContext;
import org.thingsboard.server.common.data.notification.targets.NotificationRecipient;
import org.thingsboard.server.common.data.notification.template.DeliveryMethodNotificationTemplate;
public interface NotificationChannel<T extends DeliveryMethodNotificationTemplate> {
public interface NotificationChannel<R extends NotificationRecipient, T extends DeliveryMethodNotificationTemplate> {
ListenableFuture<Void> sendNotification(User recipient, T processedTemplate, NotificationProcessingContext ctx);
ListenableFuture<Void> sendNotification(R recipient, T processedTemplate, NotificationProcessingContext ctx);
NotificationDeliveryMethod getDeliveryMethod();

10
application/src/main/java/org/thingsboard/server/service/notification/channels/SlackNotificationChannel.java

@ -20,30 +20,30 @@ import com.google.common.util.concurrent.ListenableFuture;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.thingsboard.rule.engine.api.slack.SlackService;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.notification.AlreadySentException;
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
import org.thingsboard.server.common.data.notification.NotificationProcessingContext;
import org.thingsboard.server.common.data.notification.settings.SlackNotificationDeliveryMethodConfig;
import org.thingsboard.server.common.data.notification.targets.slack.SlackConversation;
import org.thingsboard.server.common.data.notification.template.SlackDeliveryMethodNotificationTemplate;
import org.thingsboard.server.service.executors.ExternalCallExecutorService;
import org.thingsboard.server.common.data.notification.NotificationProcessingContext;
@Component
@RequiredArgsConstructor
public class SlackNotificationChannel implements NotificationChannel<SlackDeliveryMethodNotificationTemplate> {
public class SlackNotificationChannel implements NotificationChannel<SlackConversation, SlackDeliveryMethodNotificationTemplate> {
private final SlackService slackService;
private final ExternalCallExecutorService executor;
@Override
public ListenableFuture<Void> sendNotification(User recipient, SlackDeliveryMethodNotificationTemplate processedTemplate, NotificationProcessingContext ctx) {
public ListenableFuture<Void> sendNotification(SlackConversation conversation, SlackDeliveryMethodNotificationTemplate processedTemplate, NotificationProcessingContext ctx) {
if (ctx.getStats().contains(NotificationDeliveryMethod.SLACK)) {
return Futures.immediateFailedFuture(new AlreadySentException());
}
SlackNotificationDeliveryMethodConfig config = ctx.getDeliveryMethodConfig(NotificationDeliveryMethod.SLACK);
return executor.submit(() -> {
slackService.sendMessage(ctx.getTenantId(), config.getBotToken(), processedTemplate.getConversationId(), processedTemplate.getBody());
slackService.sendMessage(ctx.getTenantId(), config.getBotToken(), conversation.getId(), processedTemplate.getBody());
return null;
});
}

2
application/src/main/java/org/thingsboard/server/service/notification/channels/SmsNotificationChannel.java

@ -29,7 +29,7 @@ import org.thingsboard.server.service.sms.SmsExecutorService;
@Component
@RequiredArgsConstructor
public class SmsNotificationChannel implements NotificationChannel<SmsDeliveryMethodNotificationTemplate> {
public class SmsNotificationChannel implements NotificationChannel<User, SmsDeliveryMethodNotificationTemplate> {
private final SmsService smsService;
private final SmsExecutorService executor;

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

@ -30,12 +30,13 @@ import com.slack.api.model.ConversationType;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.notification.template.SlackConversation;
import org.thingsboard.server.common.data.notification.targets.slack.SlackConversation;
import org.thingsboard.rule.engine.api.slack.SlackService;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
import org.thingsboard.server.common.data.notification.settings.NotificationSettings;
import org.thingsboard.server.common.data.notification.settings.SlackNotificationDeliveryMethodConfig;
import org.thingsboard.server.common.data.notification.targets.slack.SlackConversationType;
import org.thingsboard.server.common.data.util.ThrowingBiFunction;
import org.thingsboard.server.dao.notification.NotificationSettingsService;
@ -66,9 +67,9 @@ public class DefaultSlackService implements SlackService {
}
@Override
public List<SlackConversation> listConversations(TenantId tenantId, String token, SlackConversation.Type conversationType) {
public List<SlackConversation> listConversations(TenantId tenantId, String token, SlackConversationType conversationType) {
return cache.get(conversationType + ":" + token, k -> {
if (conversationType == SlackConversation.Type.DIRECT) {
if (conversationType == SlackConversationType.DIRECT) {
UsersListRequest request = UsersListRequest.builder()
.limit(CONVERSATIONS_LIMIT)
.build();
@ -85,7 +86,7 @@ public class DefaultSlackService implements SlackService {
.collect(Collectors.toList());
} else {
ConversationsListRequest request = ConversationsListRequest.builder()
.types(List.of(conversationType == SlackConversation.Type.PUBLIC_CHANNEL ?
.types(List.of(conversationType == SlackConversationType.PUBLIC_CHANNEL ?
ConversationType.PUBLIC_CHANNEL :
ConversationType.PRIVATE_CHANNEL))
.limit(CONVERSATIONS_LIMIT)
@ -107,7 +108,7 @@ public class DefaultSlackService implements SlackService {
}
@Override
public SlackConversation findConversation(TenantId tenantId, String token, SlackConversation.Type conversationType, String namePattern) {
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.getName(), namePattern))

14
application/src/test/java/org/thingsboard/server/service/notification/AbstractNotificationApiTest.java

@ -38,7 +38,10 @@ import org.thingsboard.server.common.data.notification.NotificationType;
import org.thingsboard.server.common.data.notification.info.UserOriginatedNotificationInfo;
import org.thingsboard.server.common.data.notification.settings.NotificationSettings;
import org.thingsboard.server.common.data.notification.targets.NotificationTarget;
import org.thingsboard.server.common.data.notification.targets.UserListNotificationTargetConfig;
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.UserListFilter;
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.EmailDeliveryMethodNotificationTemplate;
import org.thingsboard.server.common.data.notification.template.NotificationTemplate;
@ -78,9 +81,12 @@ public abstract class AbstractNotificationApiTest extends AbstractControllerTest
NotificationTarget notificationTarget = new NotificationTarget();
notificationTarget.setTenantId(tenantId);
notificationTarget.setName("Users " + List.of(usersIds));
UserListNotificationTargetConfig config = new UserListNotificationTargetConfig();
config.setUsersIds(DaoUtil.toUUIDs(List.of(usersIds)));
notificationTarget.setConfiguration(config);
notificationTarget.setType(NotificationTargetType.PLATFORM_USERS);
PlatformUsersNotificationTargetConfig targetConfig = new PlatformUsersNotificationTargetConfig();
UserListFilter filter = new UserListFilter();
filter.setUsersIds(DaoUtil.toUUIDs(List.of(usersIds)));
targetConfig.setUsersFilter(filter);
notificationTarget.setConfiguration(targetConfig);
return saveNotificationTarget(notificationTarget);
}

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

@ -22,7 +22,6 @@ import org.java_websocket.client.WebSocketClient;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.api.NotificationCenter;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.NotificationTargetId;
@ -38,16 +37,18 @@ import org.thingsboard.server.common.data.notification.NotificationType;
import org.thingsboard.server.common.data.notification.info.UserOriginatedNotificationInfo;
import org.thingsboard.server.common.data.notification.settings.NotificationSettings;
import org.thingsboard.server.common.data.notification.settings.SlackNotificationDeliveryMethodConfig;
import org.thingsboard.server.common.data.notification.targets.AllUsersNotificationTargetConfig;
import org.thingsboard.server.common.data.notification.targets.CustomerUsersNotificationTargetConfig;
import org.thingsboard.server.common.data.notification.targets.NotificationTarget;
import org.thingsboard.server.common.data.notification.targets.UserListNotificationTargetConfig;
import org.thingsboard.server.common.data.notification.targets.NotificationTargetType;
import org.thingsboard.server.common.data.notification.targets.platform.AllUsersFilter;
import org.thingsboard.server.common.data.notification.targets.platform.CustomerUsersFilter;
import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig;
import org.thingsboard.server.common.data.notification.targets.platform.UserListFilter;
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.EmailDeliveryMethodNotificationTemplate;
import org.thingsboard.server.common.data.notification.template.NotificationTemplate;
import org.thingsboard.server.common.data.notification.template.NotificationTemplateConfig;
import org.thingsboard.server.common.data.notification.template.PushDeliveryMethodNotificationTemplate;
import org.thingsboard.server.common.data.notification.template.SlackConversation;
import org.thingsboard.server.common.data.notification.template.SlackDeliveryMethodNotificationTemplate;
import org.thingsboard.server.common.data.notification.template.SmsDeliveryMethodNotificationTemplate;
import org.thingsboard.server.common.data.page.PageData;
@ -370,8 +371,11 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
public void testNotificationRequestPreview() throws Exception {
NotificationTarget target1 = new NotificationTarget();
target1.setName("Me");
UserListNotificationTargetConfig target1Config = new UserListNotificationTargetConfig();
target1Config.setUsersIds(DaoUtil.toUUIDs(List.of(tenantAdminUserId)));
target1.setType(NotificationTargetType.PLATFORM_USERS);
PlatformUsersNotificationTargetConfig target1Config = new PlatformUsersNotificationTargetConfig();
UserListFilter userListFilter = new UserListFilter();
userListFilter.setUsersIds(DaoUtil.toUUIDs(List.of(tenantAdminUserId)));
target1Config.setUsersFilter(userListFilter);
target1.setConfiguration(target1Config);
target1 = saveNotificationTarget(target1);
@ -388,9 +392,12 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
}
NotificationTarget target2 = new NotificationTarget();
target2.setName("Other customer users");
CustomerUsersNotificationTargetConfig target2Config = new CustomerUsersNotificationTargetConfig();
target2Config.setCustomerId(differentCustomerId.getId());
target2Config.setGetCustomerIdFromOriginatorEntity(false);
target2.setType(NotificationTargetType.PLATFORM_USERS);
PlatformUsersNotificationTargetConfig target2Config = new PlatformUsersNotificationTargetConfig();
CustomerUsersFilter customerUsersFilter = new CustomerUsersFilter();
customerUsersFilter.setCustomerId(differentCustomerId.getId());
customerUsersFilter.setGetCustomerIdFromOriginatorEntity(false);
target2Config.setUsersFilter(customerUsersFilter);
target2.setConfiguration(target2Config);
target2 = saveNotificationTarget(target2);
@ -426,8 +433,6 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
SlackDeliveryMethodNotificationTemplate slackNotificationTemplate = new SlackDeliveryMethodNotificationTemplate();
slackNotificationTemplate.setEnabled(true);
slackNotificationTemplate.setConversationType(SlackConversation.Type.PUBLIC_CHANNEL);
slackNotificationTemplate.setConversationId("U1234567");
slackNotificationTemplate.setBody("Message for SLACK: ${email}");
templates.put(NotificationDeliveryMethod.SLACK, slackNotificationTemplate);
@ -472,8 +477,7 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
assertThat(processedTemplates.get(NotificationDeliveryMethod.SLACK)).asInstanceOf(type(SlackDeliveryMethodNotificationTemplate.class))
.satisfies(template -> {
assertThat(template.getBody())
.startsWith("Message for SLACK")
.endsWith(requestorEmail);
.isEqualTo("Message for SLACK: ${email}"); // ${email} should not be processed
});
}
@ -507,6 +511,7 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
.until(() -> findNotificationRequest(notificationRequest.getId()).isSent());
NotificationRequestStats stats = getStats(notificationRequest.getId());
System.err.println("STATS: " + stats);
assertThat(stats.getSent().get(NotificationDeliveryMethod.PUSH)).hasValue(1);
assertThat(stats.getSent().get(NotificationDeliveryMethod.EMAIL)).hasValue(1);
assertThat(stats.getErrors().get(NotificationDeliveryMethod.SMS)).size().isOne();
@ -529,7 +534,10 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
NotificationTarget notificationTarget = new NotificationTarget();
notificationTarget.setTenantId(tenantId);
notificationTarget.setName("All my users");
AllUsersNotificationTargetConfig config = new AllUsersNotificationTargetConfig();
notificationTarget.setType(NotificationTargetType.PLATFORM_USERS);
PlatformUsersNotificationTargetConfig config = new PlatformUsersNotificationTargetConfig();
AllUsersFilter filter = new AllUsersFilter();
config.setUsersFilter(filter);
notificationTarget.setConfiguration(config);
notificationTarget = saveNotificationTarget(notificationTarget);
NotificationTargetId notificationTargetId = notificationTarget.getId();
@ -572,21 +580,28 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
notificationTemplate.setName("Slack notification template");
notificationTemplate.setNotificationType(NotificationType.GENERAL);
NotificationTemplateConfig config = new NotificationTemplateConfig();
config.setDefaultTextTemplate("To Slack :)");
config.setDefaultTextTemplate("To Slack :) ${email}");
SlackDeliveryMethodNotificationTemplate slackNotificationTemplate = new SlackDeliveryMethodNotificationTemplate();
slackNotificationTemplate.setEnabled(true);
slackNotificationTemplate.setConversationType(SlackConversation.Type.PUBLIC_CHANNEL);
String conversationId = "U154475415";
slackNotificationTemplate.setConversationId(conversationId);
config.setDeliveryMethodsTemplates(Map.of(
NotificationDeliveryMethod.SLACK, slackNotificationTemplate
));
notificationTemplate.setConfiguration(config);
notificationTemplate = saveNotificationTemplate(notificationTemplate);
NotificationRequest successfulNotificationRequest = submitNotificationRequest(Collections.emptyList(), notificationTemplate.getId(), 0);
String conversationId = "U154475415";
String conversationName = "#my-channel";
NotificationTarget notificationTarget = new NotificationTarget();
notificationTarget.setTenantId(tenantId);
notificationTarget.setName(conversationName + " in Slack");
notificationTarget.setType(NotificationTargetType.SLACK);
SlackNotificationTargetConfig targetConfig = new SlackNotificationTargetConfig();
targetConfig.setConversationId(conversationId);
targetConfig.setConversationName(conversationName);
notificationTarget.setConfiguration(targetConfig);
notificationTarget = saveNotificationTarget(notificationTarget);
NotificationRequest successfulNotificationRequest = submitNotificationRequest(List.of(notificationTarget.getId()), notificationTemplate.getId(), 0);
await().atMost(2, TimeUnit.SECONDS)
.until(() -> findNotificationRequest(successfulNotificationRequest.getId()).isSent());
verify(slackService).sendMessage(eq(tenantId), eq(slackToken), eq(conversationId), eq(config.getDefaultTextTemplate()));
@ -595,7 +610,7 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
String errorMessage = "Error!!!";
doThrow(new RuntimeException(errorMessage)).when(slackService).sendMessage(any(), any(), any(), any());
NotificationRequest failedNotificationRequest = submitNotificationRequest(Collections.emptyList(), notificationTemplate.getId(), 0);
NotificationRequest failedNotificationRequest = submitNotificationRequest(List.of(notificationTarget.getId()), notificationTemplate.getId(), 0);
await().atMost(2, TimeUnit.SECONDS)
.until(() -> findNotificationRequest(failedNotificationRequest.getId()).isSent());
stats = getStats(failedNotificationRequest.getId());

59
application/src/test/java/org/thingsboard/server/service/notification/NotificationTargetApiTest.java

@ -23,10 +23,12 @@ import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.ResultMatcher;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.notification.targets.AllUsersNotificationTargetConfig;
import org.thingsboard.server.common.data.notification.targets.CustomerUsersNotificationTargetConfig;
import org.thingsboard.server.common.data.notification.targets.NotificationTarget;
import org.thingsboard.server.common.data.notification.targets.UserListNotificationTargetConfig;
import org.thingsboard.server.common.data.notification.targets.NotificationTargetType;
import org.thingsboard.server.common.data.notification.targets.platform.AllUsersFilter;
import org.thingsboard.server.common.data.notification.targets.platform.CustomerUsersFilter;
import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig;
import org.thingsboard.server.common.data.notification.targets.platform.UserListFilter;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.controller.AbstractControllerTest;
import org.thingsboard.server.dao.notification.NotificationTargetDao;
@ -51,21 +53,25 @@ public class NotificationTargetApiTest extends AbstractControllerTest {
@Test
public void givenInvalidNotificationTarget_whenSaving_returnValidationError() throws Exception {
NotificationTarget notificationTarget = new NotificationTarget();
notificationTarget.setTenantId(null);
notificationTarget.setName(null);
notificationTarget.setConfiguration(null);
NotificationTarget target = new NotificationTarget();
target.setTenantId(null);
target.setName(null);
target.setType(null);
target.setConfiguration(null);
String validationError = saveAndGetError(notificationTarget, status().isBadRequest());
String validationError = saveAndGetError(target, status().isBadRequest());
assertThat(validationError)
.contains("name must not be")
.contains("type must not be")
.contains("configuration must not be");
UserListNotificationTargetConfig userListConfig = new UserListNotificationTargetConfig();
userListConfig.setUsersIds(Collections.emptyList());
notificationTarget.setConfiguration(userListConfig);
PlatformUsersNotificationTargetConfig targetConfig = new PlatformUsersNotificationTargetConfig();
UserListFilter userListFilter = new UserListFilter();
userListFilter.setUsersIds(Collections.emptyList());
targetConfig.setUsersFilter(userListFilter);
target.setConfiguration(targetConfig);
validationError = saveAndGetError(notificationTarget, status().isBadRequest());
validationError = saveAndGetError(target, status().isBadRequest());
assertThat(validationError)
.contains("usersIds must not be");
}
@ -76,16 +82,18 @@ public class NotificationTargetApiTest extends AbstractControllerTest {
NotificationTarget notificationTarget = new NotificationTarget();
notificationTarget.setTenantId(differentTenantId);
notificationTarget.setName("Target 1");
notificationTarget.setType(NotificationTargetType.PLATFORM_USERS);
UserListNotificationTargetConfig userListConfig = new UserListNotificationTargetConfig();
userListConfig.setUsersIds(List.of(customerUserId.getId(), tenantAdminUserId.getId()));
notificationTarget.setConfiguration(userListConfig);
PlatformUsersNotificationTargetConfig targetConfig = new PlatformUsersNotificationTargetConfig();
UserListFilter userListFilter = new UserListFilter();
userListFilter.setUsersIds(List.of(customerUserId.getId(), tenantAdminUserId.getId()));
targetConfig.setUsersFilter(userListFilter);
notificationTarget.setConfiguration(targetConfig);
saveAndGetError(notificationTarget, status().isForbidden());
loginSysAdmin();
notificationTarget.setTenantId(TenantId.SYS_TENANT_ID);
notificationTarget.setConfiguration(userListConfig);
save(notificationTarget, status().isOk());
}
@ -94,9 +102,13 @@ public class NotificationTargetApiTest extends AbstractControllerTest {
NotificationTarget notificationTarget = new NotificationTarget();
notificationTarget.setTenantId(tenantId);
notificationTarget.setName("Test target");
CustomerUsersNotificationTargetConfig customerUsersConfig = new CustomerUsersNotificationTargetConfig();
customerUsersConfig.setCustomerId(customerId.getId());
notificationTarget.setConfiguration(customerUsersConfig);
notificationTarget.setType(NotificationTargetType.PLATFORM_USERS);
PlatformUsersNotificationTargetConfig targetConfig = new PlatformUsersNotificationTargetConfig();
CustomerUsersFilter customerUsersFilter = new CustomerUsersFilter();
customerUsersFilter.setCustomerId(customerId.getId());
targetConfig.setUsersFilter(customerUsersFilter);
notificationTarget.setConfiguration(targetConfig);
List<User> recipients = getRecipients(notificationTarget);
assertThat(recipients).size().isNotZero();
@ -104,8 +116,8 @@ public class NotificationTargetApiTest extends AbstractControllerTest {
assertThat(recipient.getCustomerId()).isEqualTo(customerId);
});
AllUsersNotificationTargetConfig allUsersConfig = new AllUsersNotificationTargetConfig();
notificationTarget.setConfiguration(allUsersConfig);
AllUsersFilter allUsersFilter = new AllUsersFilter();
targetConfig.setUsersFilter(allUsersFilter);
recipients = getRecipients(notificationTarget);
assertThat(recipients).size().isGreaterThanOrEqualTo(2);
assertThat(recipients).allSatisfy(recipient -> {
@ -129,8 +141,11 @@ public class NotificationTargetApiTest extends AbstractControllerTest {
createDifferentTenant();
NotificationTarget notificationTarget = new NotificationTarget();
notificationTarget.setName("Test 1");
notificationTarget.setType(NotificationTargetType.PLATFORM_USERS);
notificationTarget.setTenantId(differentTenantId);
notificationTarget.setConfiguration(new AllUsersNotificationTargetConfig());
PlatformUsersNotificationTargetConfig targetConfig = new PlatformUsersNotificationTargetConfig();
targetConfig.setUsersFilter(new AllUsersFilter());
notificationTarget.setConfiguration(targetConfig);
save(notificationTarget, status().isOk());
assertThat(notificationTargetDao.find(TenantId.SYS_TENANT_ID)).isNotEmpty();

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

@ -25,13 +25,14 @@ import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.notification.targets.NotificationRecipient;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.validation.Length;
import org.thingsboard.server.common.data.validation.NoXss;
@ApiModel
@EqualsAndHashCode(callSuper = true)
public class User extends SearchTextBasedWithAdditionalInfo<UserId> implements HasName, HasTenantId, HasCustomerId {
public class User extends SearchTextBasedWithAdditionalInfo<UserId> implements HasName, HasTenantId, HasCustomerId, NotificationRecipient {
private static final long serialVersionUID = 8250339805336035966L;

15
common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationDeliveryMethod.java

@ -15,24 +15,11 @@
*/
package org.thingsboard.server.common.data.notification;
import lombok.Getter;
public enum NotificationDeliveryMethod {
PUSH,
EMAIL,
SMS,
SLACK(true);
@Getter
private final boolean standalone; // means that notifications for the delivery method are sent independently of specified recipients
NotificationDeliveryMethod() {
this(false);
}
NotificationDeliveryMethod(boolean standalone) {
this.standalone = standalone;
}
SLACK
}

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

@ -62,9 +62,10 @@ public class NotificationProcessingContext {
this.notificationTemplate = template;
this.templates = new EnumMap<>(NotificationDeliveryMethod.class);
this.stats = new NotificationRequestStats();
init();
}
public void init() {
private void init() {
NotificationTemplateConfig templateConfig = notificationTemplate.getConfiguration();
templateConfig.getDeliveryMethodsTemplates().forEach((deliveryMethod, template) -> {
if (!template.isEnabled()) return;

23
common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestStats.java

@ -21,10 +21,10 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.notification.targets.NotificationRecipient;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
@ -35,7 +35,7 @@ public class NotificationRequestStats {
private final Map<NotificationDeliveryMethod, AtomicInteger> sent;
private final Map<NotificationDeliveryMethod, Map<String, String>> errors;
@JsonIgnore
private final Map<NotificationDeliveryMethod, Set<UserId>> processedRecipients;
private final Map<NotificationDeliveryMethod, Set<Object>> processedRecipients;
public NotificationRequestStats() {
this.sent = new ConcurrentHashMap<>();
@ -51,19 +51,22 @@ public class NotificationRequestStats {
this.processedRecipients = Collections.emptyMap();
}
public void reportSent(NotificationDeliveryMethod deliveryMethod, User recipient) {
public void reportSent(NotificationDeliveryMethod deliveryMethod, NotificationRecipient recipient) {
sent.computeIfAbsent(deliveryMethod, k -> new AtomicInteger()).incrementAndGet();
if (recipient != null) {
processedRecipients.computeIfAbsent(deliveryMethod, k -> ConcurrentHashMap.newKeySet()).add(recipient.getId());
}
processedRecipients.computeIfAbsent(deliveryMethod, k -> ConcurrentHashMap.newKeySet()).add(recipient.getId());
}
public void reportError(NotificationDeliveryMethod deliveryMethod, Throwable error, User recipient) {
public void reportError(NotificationDeliveryMethod deliveryMethod, Throwable error, NotificationRecipient recipient) {
if (error instanceof AlreadySentException) {
return;
}
String errorMessage = error.getMessage();
String key = Optional.ofNullable(recipient).map(User::getEmail).orElse("");
String key;
if (recipient instanceof User) {
key = ((User) recipient).getEmail();
} else {
key = "";
}
errors.computeIfAbsent(deliveryMethod, k -> new ConcurrentHashMap<>()).put(key, errorMessage);
}
@ -71,8 +74,8 @@ public class NotificationRequestStats {
return sent.containsKey(deliveryMethod) || errors.containsKey(deliveryMethod);
}
public boolean contains(NotificationDeliveryMethod deliveryMethod, UserId recipientId) {
Set<UserId> processedRecipients = this.processedRecipients.get(deliveryMethod);
public boolean contains(NotificationDeliveryMethod deliveryMethod, Object recipientId) {
Set<Object> processedRecipients = this.processedRecipients.get(deliveryMethod);
return processedRecipients != null && processedRecipients.contains(recipientId);
}

12
common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/AllUsersNotificationTargetConfig.java → common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationRecipient.java

@ -15,16 +15,8 @@
*/
package org.thingsboard.server.common.data.notification.targets;
import lombok.Data;
import lombok.EqualsAndHashCode;
public interface NotificationRecipient {
@Data
@EqualsAndHashCode(callSuper = true)
public class AllUsersNotificationTargetConfig extends NotificationTargetConfig {
@Override
public NotificationTargetConfigType getType() {
return NotificationTargetConfigType.ALL_USERS;
}
Object getId();
}

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

@ -37,6 +37,8 @@ public class NotificationTarget extends BaseData<NotificationTargetId> implement
@NoXss
private String name;
@NotNull
private NotificationTargetType type;
@NotNull
@Valid
private NotificationTargetConfig configuration;

9
common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfig.java

@ -21,13 +21,14 @@ import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import lombok.Data;
import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig;
import org.thingsboard.server.common.data.notification.targets.slack.SlackNotificationTargetConfig;
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonSubTypes({
@Type(value = UserListNotificationTargetConfig.class, name = "USER_LIST"),
@Type(value = CustomerUsersNotificationTargetConfig.class, name = "CUSTOMER_USERS"),
@Type(value = AllUsersNotificationTargetConfig.class, name = "ALL_USERS")
@Type(value = PlatformUsersNotificationTargetConfig.class, name = "PLATFORM_USERS"),
@Type(value = SlackNotificationTargetConfig.class, name = "SLACK")
})
@Data
public abstract class NotificationTargetConfig {
@ -35,6 +36,6 @@ public abstract class NotificationTargetConfig {
private String description;
@JsonIgnore
public abstract NotificationTargetConfigType getType();
public abstract NotificationTargetType getType();
}

33
common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetType.java

@ -0,0 +1,33 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.notification.targets;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
import java.util.Set;
@RequiredArgsConstructor
public enum NotificationTargetType {
PLATFORM_USERS(Set.of(NotificationDeliveryMethod.PUSH, NotificationDeliveryMethod.EMAIL, NotificationDeliveryMethod.SMS)),
SLACK(Set.of(NotificationDeliveryMethod.SLACK));
@Getter
private final Set<NotificationDeliveryMethod> supportedDeliveryMethods;
}

14
common/data/src/main/java/org/thingsboard/server/common/data/notification/template/SlackConversation.java → common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/AllUsersFilter.java

@ -13,20 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.notification.template;
package org.thingsboard.server.common.data.notification.targets.platform;
import lombok.Data;
@Data
public class SlackConversation {
public class AllUsersFilter implements UsersFilter {
private String id;
private String name;
public enum Type {
DIRECT,
PUBLIC_CHANNEL,
PRIVATE_CHANNEL
@Override
public UsersFilterType getType() {
return UsersFilterType.ALL_USERS;
}
}

10
common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/CustomerUsersNotificationTargetConfig.java → common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/CustomerUsersFilter.java

@ -13,26 +13,24 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.notification.targets;
package org.thingsboard.server.common.data.notification.targets.platform;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.id.EntityId;
import javax.validation.constraints.AssertTrue;
import java.util.UUID;
@Data
@EqualsAndHashCode(callSuper = true)
public class CustomerUsersNotificationTargetConfig extends NotificationTargetConfig {
public class CustomerUsersFilter implements UsersFilter {
private UUID customerId; // might not be set if using with notification rule
private boolean getCustomerIdFromOriginatorEntity; // e.g. from alarm
@Override
public NotificationTargetConfigType getType() {
return NotificationTargetConfigType.CUSTOMER_USERS;
public UsersFilterType getType() {
return UsersFilterType.CUSTOMER_USERS;
}
@AssertTrue(message = "customerId is required")

39
common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/PlatformUsersNotificationTargetConfig.java

@ -0,0 +1,39 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.notification.targets.platform;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.notification.targets.NotificationTargetConfig;
import org.thingsboard.server.common.data.notification.targets.NotificationTargetType;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
@Data
@EqualsAndHashCode(callSuper = true)
public class PlatformUsersNotificationTargetConfig extends NotificationTargetConfig {
@NotNull
@Valid
private UsersFilter usersFilter;
@Override
public NotificationTargetType getType() {
return NotificationTargetType.PLATFORM_USERS;
}
}

10
common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/UserListNotificationTargetConfig.java → common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UserListFilter.java

@ -13,25 +13,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.notification.targets;
package org.thingsboard.server.common.data.notification.targets.platform;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotEmpty;
import java.util.List;
import java.util.UUID;
@Data
@EqualsAndHashCode(callSuper = true)
public class UserListNotificationTargetConfig extends NotificationTargetConfig {
public class UserListFilter implements UsersFilter {
@NotEmpty
private List<UUID> usersIds;
@Override
public NotificationTargetConfigType getType() {
return NotificationTargetConfigType.USER_LIST;
public UsersFilterType getType() {
return UsersFilterType.USER_LIST;
}
}

36
common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UsersFilter.java

@ -0,0 +1,36 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.notification.targets.platform;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonSubTypes({
@Type(value = UserListFilter.class, name = "USER_LIST"),
@Type(value = CustomerUsersFilter.class, name = "CUSTOMER_USERS"),
@Type(value = AllUsersFilter.class, name = "ALL_USERS")
})
public interface UsersFilter {
@JsonIgnore
UsersFilterType getType();
}

4
common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfigType.java → common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UsersFilterType.java

@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.notification.targets;
package org.thingsboard.server.common.data.notification.targets.platform;
public enum NotificationTargetConfigType {
public enum UsersFilterType {
USER_LIST,
CUSTOMER_USERS,
ALL_USERS

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

@ -0,0 +1,29 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.notification.targets.slack;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.thingsboard.server.common.data.notification.targets.NotificationRecipient;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SlackConversation implements NotificationRecipient {
private String id;
private String name;
}

22
common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/slack/SlackConversationType.java

@ -0,0 +1,22 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.notification.targets.slack;
public enum SlackConversationType {
DIRECT,
PUBLIC_CHANNEL,
PRIVATE_CHANNEL
}

40
common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/slack/SlackNotificationTargetConfig.java

@ -0,0 +1,40 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.notification.targets.slack;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.notification.targets.NotificationTargetConfig;
import org.thingsboard.server.common.data.notification.targets.NotificationTargetType;
import javax.validation.constraints.NotEmpty;
@Data
@EqualsAndHashCode(callSuper = true)
public class SlackNotificationTargetConfig extends NotificationTargetConfig {
private SlackConversationType conversationType;
@NotEmpty
private String conversationId;
@NotEmpty
private String conversationName;
@Override
public NotificationTargetType getType() {
return NotificationTargetType.SLACK;
}
}

1
common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.java

@ -44,6 +44,7 @@ public abstract class DeliveryMethodNotificationTemplate {
this.body = other.body;
}
@JsonIgnore
public abstract NotificationDeliveryMethod getMethod();
@JsonIgnore

10
common/data/src/main/java/org/thingsboard/server/common/data/notification/template/SlackDeliveryMethodNotificationTemplate.java

@ -21,22 +21,14 @@ import lombok.NoArgsConstructor;
import lombok.ToString;
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
import javax.validation.constraints.NotEmpty;
@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SlackDeliveryMethodNotificationTemplate extends DeliveryMethodNotificationTemplate {
private SlackConversation.Type conversationType;
@NotEmpty
private String conversationId;
public SlackDeliveryMethodNotificationTemplate(SlackDeliveryMethodNotificationTemplate other) {
public SlackDeliveryMethodNotificationTemplate(DeliveryMethodNotificationTemplate other) {
super(other);
this.conversationType = other.conversationType;
this.conversationId = other.conversationId;
}
@Override

1
dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java

@ -650,6 +650,7 @@ public class ModelConstants {
* */
public static final String NOTIFICATION_TARGET_TABLE_NAME = "notification_target";
public static final String NOTIFICATION_TARGET_TYPE_PROPERTY = "type";
public static final String NOTIFICATION_TARGET_CONFIGURATION_PROPERTY = "configuration";
public static final String NOTIFICATION_TABLE_NAME = "notification";

10
dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationTargetEntity.java

@ -21,15 +21,17 @@ import lombok.EqualsAndHashCode;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;
import org.thingsboard.server.common.data.id.NotificationTargetId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.notification.targets.NotificationTarget;
import org.thingsboard.server.common.data.notification.targets.NotificationTargetConfig;
import org.thingsboard.server.common.data.notification.targets.NotificationTargetType;
import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.util.mapping.JsonStringType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Table;
import java.util.UUID;
@ -46,6 +48,10 @@ public class NotificationTargetEntity extends BaseSqlEntity<NotificationTarget>
@Column(name = ModelConstants.NAME_PROPERTY, nullable = false)
private String name;
@Enumerated(EnumType.STRING)
@Column(name = ModelConstants.NOTIFICATION_TARGET_TYPE_PROPERTY, nullable = false)
private NotificationTargetType type;
@Type(type = "json")
@Column(name = ModelConstants.NOTIFICATION_TARGET_CONFIGURATION_PROPERTY, nullable = false)
private JsonNode configuration;
@ -57,6 +63,7 @@ public class NotificationTargetEntity extends BaseSqlEntity<NotificationTarget>
setCreatedTime(notificationTarget.getCreatedTime());
setTenantId(getTenantUuid(notificationTarget.getTenantId()));
setName(notificationTarget.getName());
setType(notificationTarget.getType());
setConfiguration(toJson(notificationTarget.getConfiguration()));
}
@ -67,6 +74,7 @@ public class NotificationTargetEntity extends BaseSqlEntity<NotificationTarget>
notificationTarget.setCreatedTime(createdTime);
notificationTarget.setTenantId(getTenantId(tenantId));
notificationTarget.setName(name);
notificationTarget.setType(type);
notificationTarget.setConfiguration(fromJson(configuration, NotificationTargetConfig.class));
return notificationTarget;
}

16
dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java

@ -24,10 +24,12 @@ import org.thingsboard.server.common.data.id.NotificationTargetId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.notification.NotificationRequestStatus;
import org.thingsboard.server.common.data.notification.targets.CustomerUsersNotificationTargetConfig;
import org.thingsboard.server.common.data.notification.targets.platform.CustomerUsersFilter;
import org.thingsboard.server.common.data.notification.targets.NotificationTarget;
import org.thingsboard.server.common.data.notification.targets.NotificationTargetConfig;
import org.thingsboard.server.common.data.notification.targets.UserListNotificationTargetConfig;
import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig;
import org.thingsboard.server.common.data.notification.targets.platform.UserListFilter;
import org.thingsboard.server.common.data.notification.targets.platform.UsersFilter;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.entity.AbstractEntityService;
@ -85,9 +87,13 @@ public class DefaultNotificationTargetService extends AbstractEntityService impl
@Override
public PageData<User> findRecipientsForNotificationTargetConfig(TenantId tenantId, CustomerId customerId, NotificationTargetConfig targetConfig, PageLink pageLink) {
switch (targetConfig.getType()) {
if (!(targetConfig instanceof PlatformUsersNotificationTargetConfig)) {
throw new IllegalArgumentException("Unsupported target type " + targetConfig.getType());
}
UsersFilter usersFilter = ((PlatformUsersNotificationTargetConfig) targetConfig).getUsersFilter();
switch (usersFilter.getType()) {
case USER_LIST: {
List<User> users = ((UserListNotificationTargetConfig) targetConfig).getUsersIds().stream()
List<User> users = ((UserListFilter) usersFilter).getUsersIds().stream()
.map(UserId::new).map(userId -> userService.findUserById(tenantId, userId))
.collect(Collectors.toList());
return new PageData<>(users, 1, users.size(), false);
@ -96,7 +102,7 @@ public class DefaultNotificationTargetService extends AbstractEntityService impl
if (tenantId.equals(TenantId.SYS_TENANT_ID)) {
throw new IllegalArgumentException("Customer users target is not supported for system administrator");
}
CustomerUsersNotificationTargetConfig customerUsersConfig = (CustomerUsersNotificationTargetConfig) targetConfig;
CustomerUsersFilter customerUsersConfig = (CustomerUsersFilter) usersFilter;
if (!customerUsersConfig.isGetCustomerIdFromOriginatorEntity()) {
customerId = new CustomerId(customerUsersConfig.getCustomerId());
}

1
dao/src/main/resources/sql/schema-entities.sql

@ -784,6 +784,7 @@ CREATE TABLE IF NOT EXISTS notification_target (
created_time BIGINT NOT NULL,
tenant_id UUID NULL CONSTRAINT fk_notification_target_tenant_id REFERENCES tenant(id) ON DELETE CASCADE,
name VARCHAR(255) NOT NULL,
type VARCHAR(32) NOT NULL,
configuration VARCHAR(10000) NOT NULL,
CONSTRAINT uq_notification_target_name UNIQUE (tenant_id, name)
);

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

@ -16,7 +16,8 @@
package org.thingsboard.rule.engine.api.slack;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.notification.template.SlackConversation;
import org.thingsboard.server.common.data.notification.targets.slack.SlackConversation;
import org.thingsboard.server.common.data.notification.targets.slack.SlackConversationType;
import java.util.List;
@ -24,9 +25,9 @@ public interface SlackService {
void sendMessage(TenantId tenantId, String token, String conversationId, String message);
List<SlackConversation> listConversations(TenantId tenantId, String token, SlackConversation.Type conversationType);
List<SlackConversation> listConversations(TenantId tenantId, String token, SlackConversationType conversationType);
SlackConversation findConversation(TenantId tenantId, String token, SlackConversation.Type conversationType, String namePattern);
SlackConversation findConversation(TenantId tenantId, String token, SlackConversationType conversationType, String namePattern);
String getToken(TenantId tenantId);

2
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbSlackNode.java

@ -23,7 +23,7 @@ import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.rule.engine.api.TbNode;
import org.thingsboard.rule.engine.api.TbNodeConfiguration;
import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.server.common.data.notification.template.SlackConversation;
import org.thingsboard.server.common.data.notification.targets.slack.SlackConversation;
import org.thingsboard.rule.engine.api.util.TbNodeUtils;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.msg.TbMsg;

6
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbSlackNodeConfiguration.java

@ -17,7 +17,7 @@ package org.thingsboard.rule.engine.notification;
import lombok.Data;
import org.thingsboard.rule.engine.api.NodeConfiguration;
import org.thingsboard.server.common.data.notification.template.SlackConversation;
import org.thingsboard.server.common.data.notification.targets.slack.SlackConversationType;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@ -31,7 +31,7 @@ public class TbSlackNodeConfiguration implements NodeConfiguration<TbSlackNodeCo
private String messageTemplate;
@NotNull
private SlackConversation.Type conversationType;
private SlackConversationType conversationType;
private String conversationId; // if not set, need to specify conversationNamePattern
private String conversationNamePattern;
@ -40,7 +40,7 @@ public class TbSlackNodeConfiguration implements NodeConfiguration<TbSlackNodeCo
TbSlackNodeConfiguration config = new TbSlackNodeConfiguration();
config.setBotToken("xoxb-");
config.setMessageTemplate("Device ${deviceId}: temperature is $[temperature]");
config.setConversationType(SlackConversation.Type.PUBLIC_CHANNEL);
config.setConversationType(SlackConversationType.PUBLIC_CHANNEL);
return config;
}

Loading…
Cancel
Save