Browse Source

Merge remote-tracking branch 'klimov/feature/notification-system' into feature/notification-system

pull/8046/head
Vladyslav_Prykhodko 4 years ago
parent
commit
8b2ee4b413
  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, created_time BIGINT NOT NULL,
tenant_id UUID NULL CONSTRAINT fk_notification_target_tenant_id REFERENCES tenant(id) ON DELETE CASCADE, tenant_id UUID NULL CONSTRAINT fk_notification_target_tenant_id REFERENCES tenant(id) ON DELETE CASCADE,
name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL,
type VARCHAR(32) NOT NULL,
configuration VARCHAR(10000) NOT NULL, configuration VARCHAR(10000) NOT NULL,
CONSTRAINT uq_notification_target_name UNIQUE (tenant_id, name) 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.NotificationRequestPreview;
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.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.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;
@ -59,6 +60,7 @@ import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource; import org.thingsboard.server.service.security.permission.Resource;
import javax.validation.Valid; import javax.validation.Valid;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
@ -198,11 +200,15 @@ public class NotificationController extends BaseController {
.settings(null) .settings(null)
.template(notificationTemplate) .template(notificationTemplate)
.build(); .build();
mockProcessingCtx.init();
Map<String, String> templateContext = mockProcessingCtx.createTemplateContext(user);
Map<NotificationDeliveryMethod, DeliveryMethodNotificationTemplate> processedTemplates = mockProcessingCtx.getDeliveryMethods().stream() Map<NotificationDeliveryMethod, DeliveryMethodNotificationTemplate> processedTemplates = mockProcessingCtx.getDeliveryMethods().stream()
.collect(Collectors.toMap(m -> m, deliveryMethod -> { .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); return mockProcessingCtx.getProcessedTemplate(deliveryMethod, templateContext);
})); }));
preview.setProcessedTemplates(processedTemplates); preview.setProcessedTemplates(processedTemplates);
@ -214,7 +220,12 @@ public class NotificationController extends BaseController {
throw new IllegalArgumentException("Notification target with id " + targetId + " not found"); 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); recipientsCountByTarget.put(notificationTarget.getName(), recipientsCount);
}); });
preview.setRecipientsCountByTarget(recipientsCountByTarget); 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.id.NotificationTargetId;
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.NotificationTargetConfig; 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.PageData;
import org.thingsboard.server.common.data.page.PageDataIterable; import org.thingsboard.server.common.data.page.PageDataIterable;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
@ -59,15 +60,15 @@ public class NotificationTargetController extends BaseController {
@ApiOperation(value = "Save notification target (saveNotificationTarget)", @ApiOperation(value = "Save notification target (saveNotificationTarget)",
notes = "Create or update notification target.\n\n" + notes = "Create or update notification target.\n\n" +
"Examples with different configuration types:\n" + // "Examples with different configuration types:\n" +
"- USER_LIST:\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" + // "```\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" + // "- 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" + // "```\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" + // "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" + // "```\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" + // "- ALL_USERS:\n" +
"```\n{\n \"name\": \"All my users\",\n \"configuration\": {\n \"type\": \"ALL_USERS\"\n }\n}\n```\n\n" + // "```\n{\n \"name\": \"All my users\",\n \"configuration\": {\n \"type\": \"ALL_USERS\"\n }\n}\n```\n\n" +
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PostMapping("/target") @PostMapping("/target")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@ -77,11 +78,13 @@ public class NotificationTargetController extends BaseController {
checkEntity(notificationTarget.getId(), notificationTarget, Resource.NOTIFICATION_TARGET); checkEntity(notificationTarget.getId(), notificationTarget, Resource.NOTIFICATION_TARGET);
if (!user.isSystemAdmin()) { if (!user.isSystemAdmin()) {
NotificationTargetConfig targetConfig = notificationTarget.getConfiguration(); NotificationTargetConfig targetConfig = notificationTarget.getConfiguration();
PageDataIterable<User> recipients = new PageDataIterable<>(pageLink -> { if (targetConfig.getType() == NotificationTargetType.PLATFORM_USERS) {
return notificationTargetService.findRecipientsForNotificationTargetConfig(user.getTenantId(), null, targetConfig, pageLink); PageDataIterable<User> recipients = new PageDataIterable<>(pageLink -> {
}, 200); return notificationTargetService.findRecipientsForNotificationTargetConfig(user.getTenantId(), null, targetConfig, pageLink);
for (User recipient : recipients) { }, 200);
accessControlService.checkPermission(user, Resource.USER, Operation.READ, recipient.getId(), recipient); 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.NotificationType;
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.settings.SlackNotificationDeliveryMethodConfig; 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.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.PageData;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.notification.NotificationSettingsService; import org.thingsboard.server.dao.notification.NotificationSettingsService;
@ -133,7 +134,7 @@ public class NotificationTemplateController extends BaseController {
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@GetMapping("/slack/conversations") @GetMapping("/slack/conversations")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
public List<SlackConversation> listSlackConversations(@RequestParam SlackConversation.Type type, public List<SlackConversation> listSlackConversations(@RequestParam SlackConversationType type,
@AuthenticationPrincipal SecurityUser user) { @AuthenticationPrincipal SecurityUser user) {
NotificationSettings settings = notificationSettingsService.findNotificationSettings(user.getTenantId()); NotificationSettings settings = notificationSettingsService.findNotificationSettings(user.getTenantId());
SlackNotificationDeliveryMethodConfig slackConfig = (SlackNotificationDeliveryMethodConfig) 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.NotificationStatus;
import org.thingsboard.server.common.data.notification.NotificationType; 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.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.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.notification.template.PushDeliveryMethodNotificationTemplate; 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.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; 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.NotificationRequestService;
import org.thingsboard.server.dao.notification.NotificationService; import org.thingsboard.server.dao.notification.NotificationService;
import org.thingsboard.server.dao.notification.NotificationSettingsService; import org.thingsboard.server.dao.notification.NotificationSettingsService;
@ -79,7 +83,7 @@ import java.util.stream.Collectors;
@Slf4j @Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
@SuppressWarnings({"UnstableApiUsage", "rawtypes"}) @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 NotificationTargetService notificationTargetService;
private final NotificationRequestService notificationRequestService; private final NotificationRequestService notificationRequestService;
@ -95,9 +99,17 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
@Override @Override
public NotificationRequest processNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest) { public NotificationRequest processNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest) {
notificationRequest.setTenantId(tenantId);
NotificationSettings settings = notificationSettingsService.findNotificationSettings(tenantId); NotificationSettings settings = notificationSettingsService.findNotificationSettings(tenantId);
NotificationTemplate notificationTemplate = notificationTemplateService.findNotificationTemplateById(tenantId, notificationRequest.getTemplateId()); 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) -> { notificationTemplate.getConfiguration().getDeliveryMethodsTemplates().forEach((deliveryMethod, template) -> {
if (!template.isEnabled()) return; if (!template.isEnabled()) return;
@ -110,6 +122,9 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
throw new IllegalArgumentException("Slack must be configured in the settings"); 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) { 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.setStatus(NotificationRequestStatus.PROCESSING);
NotificationRequest savedNotificationRequest = notificationRequestService.saveNotificationRequest(tenantId, notificationRequest); NotificationRequest savedNotificationRequest = notificationRequestService.saveNotificationRequest(tenantId, notificationRequest);
notificationExecutor.submit(() -> { NotificationProcessingContext ctx = NotificationProcessingContext.builder()
NotificationProcessingContext ctx = NotificationProcessingContext.builder() .tenantId(tenantId)
.tenantId(tenantId) .request(savedNotificationRequest)
.request(savedNotificationRequest) .settings(settings)
.settings(settings) .template(notificationTemplate)
.template(notificationTemplate) .build();
.build();
ctx.init();
Set<NotificationDeliveryMethod> deliveryMethods = ctx.getDeliveryMethods(); notificationExecutor.submit(() -> {
List<ListenableFuture<Void>> results = new ArrayList<>(); List<ListenableFuture<Void>> results = new ArrayList<>();
for (UUID targetId : notificationRequest.getTargets()) { for (NotificationTarget target : targets) {
DaoUtil.processBatches(pageLink -> { List<ListenableFuture<Void>> result = processForTarget(target, ctx);
return notificationTargetService.findRecipientsForNotificationTarget(tenantId, ctx.getCustomerId(), new NotificationTargetId(targetId), pageLink); results.addAll(result);
}, 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);
}
} }
Futures.whenAllComplete(results).run(() -> { Futures.whenAllComplete(results).run(() -> {
NotificationRequestId requestId = savedNotificationRequest.getId();
log.debug("[{}] Notification request processing is finished", requestId);
NotificationRequestStats stats = ctx.getStats(); NotificationRequestStats stats = ctx.getStats();
try { try {
notificationRequestService.updateNotificationRequest(tenantId, savedNotificationRequest.getId(), notificationRequestService.updateNotificationRequest(tenantId, requestId, NotificationRequestStatus.SENT, stats);
NotificationRequestStatus.SENT, stats);
} catch (Exception e) { } 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(); UserId senderId = notificationRequest.getSenderId();
@ -200,25 +184,70 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
return savedNotificationRequest; return savedNotificationRequest;
} }
private ListenableFuture<Void> process(NotificationChannel notificationChannel, User recipient, NotificationProcessingContext ctx) { private List<ListenableFuture<Void>> processForTarget(NotificationTarget target, NotificationProcessingContext ctx) {
NotificationDeliveryMethod deliveryMethod = notificationChannel.getDeliveryMethod(); Iterable<? extends NotificationRecipient> recipients;
if (recipient != null && ctx.getStats().contains(deliveryMethod, recipient.getId())) { 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()); return Futures.immediateFailedFuture(new AlreadySentException());
} }
Map<String, String> templateContext;
if (recipient instanceof User) {
templateContext = ctx.createTemplateContext(((User) recipient));
} else {
templateContext = Collections.emptyMap();
}
DeliveryMethodNotificationTemplate processedTemplate; DeliveryMethodNotificationTemplate processedTemplate;
try { try {
Map<String, String> templateContext = recipient != null ? ctx.createTemplateContext(recipient) : Collections.emptyMap();
processedTemplate = ctx.getProcessedTemplate(deliveryMethod, templateContext); processedTemplate = ctx.getProcessedTemplate(deliveryMethod, templateContext);
} catch (Exception e) { } catch (Exception e) {
return Futures.immediateFailedFuture(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); return notificationChannel.sendNotification(recipient, processedTemplate, ctx);
} }
@Override @Override
public ListenableFuture<Void> sendNotification(User recipient, PushDeliveryMethodNotificationTemplate processedTemplate, NotificationProcessingContext ctx) { public ListenableFuture<Void> sendNotification(User recipient, PushDeliveryMethodNotificationTemplate processedTemplate, NotificationProcessingContext ctx) {
NotificationRequest request = ctx.getRequest(); NotificationRequest request = ctx.getRequest();
log.trace("Creating notification for recipient {} (notification request id: {})", recipient.getId(), request.getId());
Notification notification = Notification.builder() Notification notification = Notification.builder()
.requestId(request.getId()) .requestId(request.getId())
.recipientId(recipient.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 @Component
@RequiredArgsConstructor @RequiredArgsConstructor
public class EmailNotificationChannel implements NotificationChannel<EmailDeliveryMethodNotificationTemplate> { public class EmailNotificationChannel implements NotificationChannel<User, EmailDeliveryMethodNotificationTemplate> {
private final MailService mailService; private final MailService mailService;
private final MailExecutorService executor; 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; package org.thingsboard.server.service.notification.channels;
import com.google.common.util.concurrent.ListenableFuture; 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.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.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(); 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 lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.rule.engine.api.slack.SlackService; 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.AlreadySentException;
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; 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.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.common.data.notification.template.SlackDeliveryMethodNotificationTemplate;
import org.thingsboard.server.service.executors.ExternalCallExecutorService; import org.thingsboard.server.service.executors.ExternalCallExecutorService;
import org.thingsboard.server.common.data.notification.NotificationProcessingContext;
@Component @Component
@RequiredArgsConstructor @RequiredArgsConstructor
public class SlackNotificationChannel implements NotificationChannel<SlackDeliveryMethodNotificationTemplate> { public class SlackNotificationChannel implements NotificationChannel<SlackConversation, SlackDeliveryMethodNotificationTemplate> {
private final SlackService slackService; private final SlackService slackService;
private final ExternalCallExecutorService executor; private final ExternalCallExecutorService executor;
@Override @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)) { if (ctx.getStats().contains(NotificationDeliveryMethod.SLACK)) {
return Futures.immediateFailedFuture(new AlreadySentException()); return Futures.immediateFailedFuture(new AlreadySentException());
} }
SlackNotificationDeliveryMethodConfig config = ctx.getDeliveryMethodConfig(NotificationDeliveryMethod.SLACK); SlackNotificationDeliveryMethodConfig config = ctx.getDeliveryMethodConfig(NotificationDeliveryMethod.SLACK);
return executor.submit(() -> { 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; 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 @Component
@RequiredArgsConstructor @RequiredArgsConstructor
public class SmsNotificationChannel implements NotificationChannel<SmsDeliveryMethodNotificationTemplate> { public class SmsNotificationChannel implements NotificationChannel<User, SmsDeliveryMethodNotificationTemplate> {
private final SmsService smsService; private final SmsService smsService;
private final SmsExecutorService executor; 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 lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service; 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.rule.engine.api.slack.SlackService;
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.settings.NotificationSettings; 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.settings.SlackNotificationDeliveryMethodConfig;
import org.thingsboard.server.common.data.notification.targets.slack.SlackConversationType;
import org.thingsboard.server.common.data.util.ThrowingBiFunction; import org.thingsboard.server.common.data.util.ThrowingBiFunction;
import org.thingsboard.server.dao.notification.NotificationSettingsService; import org.thingsboard.server.dao.notification.NotificationSettingsService;
@ -66,9 +67,9 @@ public class DefaultSlackService implements SlackService {
} }
@Override @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 -> { return cache.get(conversationType + ":" + token, k -> {
if (conversationType == SlackConversation.Type.DIRECT) { if (conversationType == SlackConversationType.DIRECT) {
UsersListRequest request = UsersListRequest.builder() UsersListRequest request = UsersListRequest.builder()
.limit(CONVERSATIONS_LIMIT) .limit(CONVERSATIONS_LIMIT)
.build(); .build();
@ -85,7 +86,7 @@ public class DefaultSlackService implements SlackService {
.collect(Collectors.toList()); .collect(Collectors.toList());
} else { } else {
ConversationsListRequest request = ConversationsListRequest.builder() ConversationsListRequest request = ConversationsListRequest.builder()
.types(List.of(conversationType == SlackConversation.Type.PUBLIC_CHANNEL ? .types(List.of(conversationType == SlackConversationType.PUBLIC_CHANNEL ?
ConversationType.PUBLIC_CHANNEL : ConversationType.PUBLIC_CHANNEL :
ConversationType.PRIVATE_CHANNEL)) ConversationType.PRIVATE_CHANNEL))
.limit(CONVERSATIONS_LIMIT) .limit(CONVERSATIONS_LIMIT)
@ -107,7 +108,7 @@ public class DefaultSlackService implements SlackService {
} }
@Override @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); 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.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.info.UserOriginatedNotificationInfo;
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.NotificationTarget; 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.DeliveryMethodNotificationTemplate;
import org.thingsboard.server.common.data.notification.template.EmailDeliveryMethodNotificationTemplate; 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.NotificationTemplate;
@ -78,9 +81,12 @@ public abstract class AbstractNotificationApiTest extends AbstractControllerTest
NotificationTarget notificationTarget = new NotificationTarget(); NotificationTarget notificationTarget = new NotificationTarget();
notificationTarget.setTenantId(tenantId); notificationTarget.setTenantId(tenantId);
notificationTarget.setName("Users " + List.of(usersIds)); notificationTarget.setName("Users " + List.of(usersIds));
UserListNotificationTargetConfig config = new UserListNotificationTargetConfig(); notificationTarget.setType(NotificationTargetType.PLATFORM_USERS);
config.setUsersIds(DaoUtil.toUUIDs(List.of(usersIds))); PlatformUsersNotificationTargetConfig targetConfig = new PlatformUsersNotificationTargetConfig();
notificationTarget.setConfiguration(config); UserListFilter filter = new UserListFilter();
filter.setUsersIds(DaoUtil.toUUIDs(List.of(usersIds)));
targetConfig.setUsersFilter(filter);
notificationTarget.setConfiguration(targetConfig);
return saveNotificationTarget(notificationTarget); 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.Before;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.api.NotificationCenter; import org.thingsboard.rule.engine.api.NotificationCenter;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.NotificationTargetId; 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.info.UserOriginatedNotificationInfo;
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.settings.SlackNotificationDeliveryMethodConfig; 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.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.DeliveryMethodNotificationTemplate;
import org.thingsboard.server.common.data.notification.template.EmailDeliveryMethodNotificationTemplate; 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.NotificationTemplate;
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.PushDeliveryMethodNotificationTemplate; 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.SlackDeliveryMethodNotificationTemplate;
import org.thingsboard.server.common.data.notification.template.SmsDeliveryMethodNotificationTemplate; import org.thingsboard.server.common.data.notification.template.SmsDeliveryMethodNotificationTemplate;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
@ -370,8 +371,11 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
public void testNotificationRequestPreview() throws Exception { public void testNotificationRequestPreview() throws Exception {
NotificationTarget target1 = new NotificationTarget(); NotificationTarget target1 = new NotificationTarget();
target1.setName("Me"); target1.setName("Me");
UserListNotificationTargetConfig target1Config = new UserListNotificationTargetConfig(); target1.setType(NotificationTargetType.PLATFORM_USERS);
target1Config.setUsersIds(DaoUtil.toUUIDs(List.of(tenantAdminUserId))); PlatformUsersNotificationTargetConfig target1Config = new PlatformUsersNotificationTargetConfig();
UserListFilter userListFilter = new UserListFilter();
userListFilter.setUsersIds(DaoUtil.toUUIDs(List.of(tenantAdminUserId)));
target1Config.setUsersFilter(userListFilter);
target1.setConfiguration(target1Config); target1.setConfiguration(target1Config);
target1 = saveNotificationTarget(target1); target1 = saveNotificationTarget(target1);
@ -388,9 +392,12 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
} }
NotificationTarget target2 = new NotificationTarget(); NotificationTarget target2 = new NotificationTarget();
target2.setName("Other customer users"); target2.setName("Other customer users");
CustomerUsersNotificationTargetConfig target2Config = new CustomerUsersNotificationTargetConfig(); target2.setType(NotificationTargetType.PLATFORM_USERS);
target2Config.setCustomerId(differentCustomerId.getId()); PlatformUsersNotificationTargetConfig target2Config = new PlatformUsersNotificationTargetConfig();
target2Config.setGetCustomerIdFromOriginatorEntity(false); CustomerUsersFilter customerUsersFilter = new CustomerUsersFilter();
customerUsersFilter.setCustomerId(differentCustomerId.getId());
customerUsersFilter.setGetCustomerIdFromOriginatorEntity(false);
target2Config.setUsersFilter(customerUsersFilter);
target2.setConfiguration(target2Config); target2.setConfiguration(target2Config);
target2 = saveNotificationTarget(target2); target2 = saveNotificationTarget(target2);
@ -426,8 +433,6 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
SlackDeliveryMethodNotificationTemplate slackNotificationTemplate = new SlackDeliveryMethodNotificationTemplate(); SlackDeliveryMethodNotificationTemplate slackNotificationTemplate = new SlackDeliveryMethodNotificationTemplate();
slackNotificationTemplate.setEnabled(true); slackNotificationTemplate.setEnabled(true);
slackNotificationTemplate.setConversationType(SlackConversation.Type.PUBLIC_CHANNEL);
slackNotificationTemplate.setConversationId("U1234567");
slackNotificationTemplate.setBody("Message for SLACK: ${email}"); slackNotificationTemplate.setBody("Message for SLACK: ${email}");
templates.put(NotificationDeliveryMethod.SLACK, slackNotificationTemplate); templates.put(NotificationDeliveryMethod.SLACK, slackNotificationTemplate);
@ -472,8 +477,7 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
assertThat(processedTemplates.get(NotificationDeliveryMethod.SLACK)).asInstanceOf(type(SlackDeliveryMethodNotificationTemplate.class)) assertThat(processedTemplates.get(NotificationDeliveryMethod.SLACK)).asInstanceOf(type(SlackDeliveryMethodNotificationTemplate.class))
.satisfies(template -> { .satisfies(template -> {
assertThat(template.getBody()) assertThat(template.getBody())
.startsWith("Message for SLACK") .isEqualTo("Message for SLACK: ${email}"); // ${email} should not be processed
.endsWith(requestorEmail);
}); });
} }
@ -507,6 +511,7 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
.until(() -> findNotificationRequest(notificationRequest.getId()).isSent()); .until(() -> findNotificationRequest(notificationRequest.getId()).isSent());
NotificationRequestStats stats = getStats(notificationRequest.getId()); NotificationRequestStats stats = getStats(notificationRequest.getId());
System.err.println("STATS: " + stats);
assertThat(stats.getSent().get(NotificationDeliveryMethod.PUSH)).hasValue(1); assertThat(stats.getSent().get(NotificationDeliveryMethod.PUSH)).hasValue(1);
assertThat(stats.getSent().get(NotificationDeliveryMethod.EMAIL)).hasValue(1); assertThat(stats.getSent().get(NotificationDeliveryMethod.EMAIL)).hasValue(1);
assertThat(stats.getErrors().get(NotificationDeliveryMethod.SMS)).size().isOne(); assertThat(stats.getErrors().get(NotificationDeliveryMethod.SMS)).size().isOne();
@ -529,7 +534,10 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
NotificationTarget notificationTarget = new NotificationTarget(); NotificationTarget notificationTarget = new NotificationTarget();
notificationTarget.setTenantId(tenantId); notificationTarget.setTenantId(tenantId);
notificationTarget.setName("All my users"); 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.setConfiguration(config);
notificationTarget = saveNotificationTarget(notificationTarget); notificationTarget = saveNotificationTarget(notificationTarget);
NotificationTargetId notificationTargetId = notificationTarget.getId(); NotificationTargetId notificationTargetId = notificationTarget.getId();
@ -572,21 +580,28 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
notificationTemplate.setName("Slack notification template"); notificationTemplate.setName("Slack notification template");
notificationTemplate.setNotificationType(NotificationType.GENERAL); notificationTemplate.setNotificationType(NotificationType.GENERAL);
NotificationTemplateConfig config = new NotificationTemplateConfig(); NotificationTemplateConfig config = new NotificationTemplateConfig();
config.setDefaultTextTemplate("To Slack :)"); config.setDefaultTextTemplate("To Slack :) ${email}");
SlackDeliveryMethodNotificationTemplate slackNotificationTemplate = new SlackDeliveryMethodNotificationTemplate(); SlackDeliveryMethodNotificationTemplate slackNotificationTemplate = new SlackDeliveryMethodNotificationTemplate();
slackNotificationTemplate.setEnabled(true); slackNotificationTemplate.setEnabled(true);
slackNotificationTemplate.setConversationType(SlackConversation.Type.PUBLIC_CHANNEL);
String conversationId = "U154475415";
slackNotificationTemplate.setConversationId(conversationId);
config.setDeliveryMethodsTemplates(Map.of( config.setDeliveryMethodsTemplates(Map.of(
NotificationDeliveryMethod.SLACK, slackNotificationTemplate NotificationDeliveryMethod.SLACK, slackNotificationTemplate
)); ));
notificationTemplate.setConfiguration(config); notificationTemplate.setConfiguration(config);
notificationTemplate = saveNotificationTemplate(notificationTemplate); 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) await().atMost(2, TimeUnit.SECONDS)
.until(() -> findNotificationRequest(successfulNotificationRequest.getId()).isSent()); .until(() -> findNotificationRequest(successfulNotificationRequest.getId()).isSent());
verify(slackService).sendMessage(eq(tenantId), eq(slackToken), eq(conversationId), eq(config.getDefaultTextTemplate())); verify(slackService).sendMessage(eq(tenantId), eq(slackToken), eq(conversationId), eq(config.getDefaultTextTemplate()));
@ -595,7 +610,7 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
String errorMessage = "Error!!!"; String errorMessage = "Error!!!";
doThrow(new RuntimeException(errorMessage)).when(slackService).sendMessage(any(), any(), any(), any()); 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) await().atMost(2, TimeUnit.SECONDS)
.until(() -> findNotificationRequest(failedNotificationRequest.getId()).isSent()); .until(() -> findNotificationRequest(failedNotificationRequest.getId()).isSent());
stats = getStats(failedNotificationRequest.getId()); 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.springframework.test.web.servlet.ResultMatcher;
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.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.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.common.data.page.PageData;
import org.thingsboard.server.controller.AbstractControllerTest; import org.thingsboard.server.controller.AbstractControllerTest;
import org.thingsboard.server.dao.notification.NotificationTargetDao; import org.thingsboard.server.dao.notification.NotificationTargetDao;
@ -51,21 +53,25 @@ public class NotificationTargetApiTest extends AbstractControllerTest {
@Test @Test
public void givenInvalidNotificationTarget_whenSaving_returnValidationError() throws Exception { public void givenInvalidNotificationTarget_whenSaving_returnValidationError() throws Exception {
NotificationTarget notificationTarget = new NotificationTarget(); NotificationTarget target = new NotificationTarget();
notificationTarget.setTenantId(null); target.setTenantId(null);
notificationTarget.setName(null); target.setName(null);
notificationTarget.setConfiguration(null); target.setType(null);
target.setConfiguration(null);
String validationError = saveAndGetError(notificationTarget, status().isBadRequest()); String validationError = saveAndGetError(target, status().isBadRequest());
assertThat(validationError) assertThat(validationError)
.contains("name must not be") .contains("name must not be")
.contains("type must not be")
.contains("configuration must not be"); .contains("configuration must not be");
UserListNotificationTargetConfig userListConfig = new UserListNotificationTargetConfig(); PlatformUsersNotificationTargetConfig targetConfig = new PlatformUsersNotificationTargetConfig();
userListConfig.setUsersIds(Collections.emptyList()); UserListFilter userListFilter = new UserListFilter();
notificationTarget.setConfiguration(userListConfig); userListFilter.setUsersIds(Collections.emptyList());
targetConfig.setUsersFilter(userListFilter);
target.setConfiguration(targetConfig);
validationError = saveAndGetError(notificationTarget, status().isBadRequest()); validationError = saveAndGetError(target, status().isBadRequest());
assertThat(validationError) assertThat(validationError)
.contains("usersIds must not be"); .contains("usersIds must not be");
} }
@ -76,16 +82,18 @@ public class NotificationTargetApiTest extends AbstractControllerTest {
NotificationTarget notificationTarget = new NotificationTarget(); NotificationTarget notificationTarget = new NotificationTarget();
notificationTarget.setTenantId(differentTenantId); notificationTarget.setTenantId(differentTenantId);
notificationTarget.setName("Target 1"); notificationTarget.setName("Target 1");
notificationTarget.setType(NotificationTargetType.PLATFORM_USERS);
UserListNotificationTargetConfig userListConfig = new UserListNotificationTargetConfig(); PlatformUsersNotificationTargetConfig targetConfig = new PlatformUsersNotificationTargetConfig();
userListConfig.setUsersIds(List.of(customerUserId.getId(), tenantAdminUserId.getId())); UserListFilter userListFilter = new UserListFilter();
notificationTarget.setConfiguration(userListConfig); userListFilter.setUsersIds(List.of(customerUserId.getId(), tenantAdminUserId.getId()));
targetConfig.setUsersFilter(userListFilter);
notificationTarget.setConfiguration(targetConfig);
saveAndGetError(notificationTarget, status().isForbidden()); saveAndGetError(notificationTarget, status().isForbidden());
loginSysAdmin(); loginSysAdmin();
notificationTarget.setTenantId(TenantId.SYS_TENANT_ID); notificationTarget.setTenantId(TenantId.SYS_TENANT_ID);
notificationTarget.setConfiguration(userListConfig);
save(notificationTarget, status().isOk()); save(notificationTarget, status().isOk());
} }
@ -94,9 +102,13 @@ public class NotificationTargetApiTest extends AbstractControllerTest {
NotificationTarget notificationTarget = new NotificationTarget(); NotificationTarget notificationTarget = new NotificationTarget();
notificationTarget.setTenantId(tenantId); notificationTarget.setTenantId(tenantId);
notificationTarget.setName("Test target"); notificationTarget.setName("Test target");
CustomerUsersNotificationTargetConfig customerUsersConfig = new CustomerUsersNotificationTargetConfig(); notificationTarget.setType(NotificationTargetType.PLATFORM_USERS);
customerUsersConfig.setCustomerId(customerId.getId());
notificationTarget.setConfiguration(customerUsersConfig); PlatformUsersNotificationTargetConfig targetConfig = new PlatformUsersNotificationTargetConfig();
CustomerUsersFilter customerUsersFilter = new CustomerUsersFilter();
customerUsersFilter.setCustomerId(customerId.getId());
targetConfig.setUsersFilter(customerUsersFilter);
notificationTarget.setConfiguration(targetConfig);
List<User> recipients = getRecipients(notificationTarget); List<User> recipients = getRecipients(notificationTarget);
assertThat(recipients).size().isNotZero(); assertThat(recipients).size().isNotZero();
@ -104,8 +116,8 @@ public class NotificationTargetApiTest extends AbstractControllerTest {
assertThat(recipient.getCustomerId()).isEqualTo(customerId); assertThat(recipient.getCustomerId()).isEqualTo(customerId);
}); });
AllUsersNotificationTargetConfig allUsersConfig = new AllUsersNotificationTargetConfig(); AllUsersFilter allUsersFilter = new AllUsersFilter();
notificationTarget.setConfiguration(allUsersConfig); targetConfig.setUsersFilter(allUsersFilter);
recipients = getRecipients(notificationTarget); recipients = getRecipients(notificationTarget);
assertThat(recipients).size().isGreaterThanOrEqualTo(2); assertThat(recipients).size().isGreaterThanOrEqualTo(2);
assertThat(recipients).allSatisfy(recipient -> { assertThat(recipients).allSatisfy(recipient -> {
@ -129,8 +141,11 @@ public class NotificationTargetApiTest extends AbstractControllerTest {
createDifferentTenant(); createDifferentTenant();
NotificationTarget notificationTarget = new NotificationTarget(); NotificationTarget notificationTarget = new NotificationTarget();
notificationTarget.setName("Test 1"); notificationTarget.setName("Test 1");
notificationTarget.setType(NotificationTargetType.PLATFORM_USERS);
notificationTarget.setTenantId(differentTenantId); notificationTarget.setTenantId(differentTenantId);
notificationTarget.setConfiguration(new AllUsersNotificationTargetConfig()); PlatformUsersNotificationTargetConfig targetConfig = new PlatformUsersNotificationTargetConfig();
targetConfig.setUsersFilter(new AllUsersFilter());
notificationTarget.setConfiguration(targetConfig);
save(notificationTarget, status().isOk()); save(notificationTarget, status().isOk());
assertThat(notificationTargetDao.find(TenantId.SYS_TENANT_ID)).isNotEmpty(); 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.EntityId;
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.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.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;
@ApiModel @ApiModel
@EqualsAndHashCode(callSuper = true) @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; 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; package org.thingsboard.server.common.data.notification;
import lombok.Getter;
public enum NotificationDeliveryMethod { public enum NotificationDeliveryMethod {
PUSH, PUSH,
EMAIL, EMAIL,
SMS, SMS,
SLACK(true); SLACK
@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;
}
} }

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.notificationTemplate = template;
this.templates = new EnumMap<>(NotificationDeliveryMethod.class); this.templates = new EnumMap<>(NotificationDeliveryMethod.class);
this.stats = new NotificationRequestStats(); this.stats = new NotificationRequestStats();
init();
} }
public void init() { private void init() {
NotificationTemplateConfig templateConfig = notificationTemplate.getConfiguration(); NotificationTemplateConfig templateConfig = notificationTemplate.getConfiguration();
templateConfig.getDeliveryMethodsTemplates().forEach((deliveryMethod, template) -> { templateConfig.getDeliveryMethodsTemplates().forEach((deliveryMethod, template) -> {
if (!template.isEnabled()) return; 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 lombok.Data;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.notification.targets.NotificationRecipient;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@ -35,7 +35,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;
@JsonIgnore @JsonIgnore
private final Map<NotificationDeliveryMethod, Set<UserId>> processedRecipients; private final Map<NotificationDeliveryMethod, Set<Object>> processedRecipients;
public NotificationRequestStats() { public NotificationRequestStats() {
this.sent = new ConcurrentHashMap<>(); this.sent = new ConcurrentHashMap<>();
@ -51,19 +51,22 @@ public class NotificationRequestStats {
this.processedRecipients = Collections.emptyMap(); 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(); 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) { if (error instanceof AlreadySentException) {
return; return;
} }
String errorMessage = error.getMessage(); 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); errors.computeIfAbsent(deliveryMethod, k -> new ConcurrentHashMap<>()).put(key, errorMessage);
} }
@ -71,8 +74,8 @@ public class NotificationRequestStats {
return sent.containsKey(deliveryMethod) || errors.containsKey(deliveryMethod); return sent.containsKey(deliveryMethod) || errors.containsKey(deliveryMethod);
} }
public boolean contains(NotificationDeliveryMethod deliveryMethod, UserId recipientId) { public boolean contains(NotificationDeliveryMethod deliveryMethod, Object recipientId) {
Set<UserId> processedRecipients = this.processedRecipients.get(deliveryMethod); Set<Object> processedRecipients = this.processedRecipients.get(deliveryMethod);
return processedRecipients != null && processedRecipients.contains(recipientId); 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; package org.thingsboard.server.common.data.notification.targets;
import lombok.Data; public interface NotificationRecipient {
import lombok.EqualsAndHashCode;
@Data Object getId();
@EqualsAndHashCode(callSuper = true)
public class AllUsersNotificationTargetConfig extends NotificationTargetConfig {
@Override
public NotificationTargetConfigType getType() {
return NotificationTargetConfigType.ALL_USERS;
}
} }

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 @NoXss
private String name; private String name;
@NotNull @NotNull
private NotificationTargetType type;
@NotNull
@Valid @Valid
private NotificationTargetConfig configuration; 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.JsonSubTypes.Type;
import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo;
import lombok.Data; 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) @JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonSubTypes({ @JsonSubTypes({
@Type(value = UserListNotificationTargetConfig.class, name = "USER_LIST"), @Type(value = PlatformUsersNotificationTargetConfig.class, name = "PLATFORM_USERS"),
@Type(value = CustomerUsersNotificationTargetConfig.class, name = "CUSTOMER_USERS"), @Type(value = SlackNotificationTargetConfig.class, name = "SLACK")
@Type(value = AllUsersNotificationTargetConfig.class, name = "ALL_USERS")
}) })
@Data @Data
public abstract class NotificationTargetConfig { public abstract class NotificationTargetConfig {
@ -35,6 +36,6 @@ public abstract class NotificationTargetConfig {
private String description; private String description;
@JsonIgnore @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 * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.common.data.notification.template; package org.thingsboard.server.common.data.notification.targets.platform;
import lombok.Data; import lombok.Data;
@Data @Data
public class SlackConversation { public class AllUsersFilter implements UsersFilter {
private String id; @Override
private String name; public UsersFilterType getType() {
return UsersFilterType.ALL_USERS;
public enum Type {
DIRECT,
PUBLIC_CHANNEL,
PRIVATE_CHANNEL
} }
} }

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 * See the License for the specific language governing permissions and
* limitations under the License. * 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 com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import javax.validation.constraints.AssertTrue; import javax.validation.constraints.AssertTrue;
import java.util.UUID; import java.util.UUID;
@Data @Data
@EqualsAndHashCode(callSuper = true) public class CustomerUsersFilter implements UsersFilter {
public class CustomerUsersNotificationTargetConfig extends NotificationTargetConfig {
private UUID customerId; // might not be set if using with notification rule private UUID customerId; // might not be set if using with notification rule
private boolean getCustomerIdFromOriginatorEntity; // e.g. from alarm private boolean getCustomerIdFromOriginatorEntity; // e.g. from alarm
@Override @Override
public NotificationTargetConfigType getType() { public UsersFilterType getType() {
return NotificationTargetConfigType.CUSTOMER_USERS; return UsersFilterType.CUSTOMER_USERS;
} }
@AssertTrue(message = "customerId is required") @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 * See the License for the specific language governing permissions and
* limitations under the License. * 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.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotEmpty;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@Data @Data
@EqualsAndHashCode(callSuper = true) public class UserListFilter implements UsersFilter {
public class UserListNotificationTargetConfig extends NotificationTargetConfig {
@NotEmpty @NotEmpty
private List<UUID> usersIds; private List<UUID> usersIds;
@Override @Override
public NotificationTargetConfigType getType() { public UsersFilterType getType() {
return NotificationTargetConfigType.USER_LIST; 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 * See the License for the specific language governing permissions and
* limitations under the License. * 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, USER_LIST,
CUSTOMER_USERS, CUSTOMER_USERS,
ALL_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; this.body = other.body;
} }
@JsonIgnore
public abstract NotificationDeliveryMethod getMethod(); public abstract NotificationDeliveryMethod getMethod();
@JsonIgnore @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 lombok.ToString;
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
import javax.validation.constraints.NotEmpty;
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true) @ToString(callSuper = true)
public class SlackDeliveryMethodNotificationTemplate extends DeliveryMethodNotificationTemplate { public class SlackDeliveryMethodNotificationTemplate extends DeliveryMethodNotificationTemplate {
private SlackConversation.Type conversationType; public SlackDeliveryMethodNotificationTemplate(DeliveryMethodNotificationTemplate other) {
@NotEmpty
private String conversationId;
public SlackDeliveryMethodNotificationTemplate(SlackDeliveryMethodNotificationTemplate other) {
super(other); super(other);
this.conversationType = other.conversationType;
this.conversationId = other.conversationId;
} }
@Override @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_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_TARGET_CONFIGURATION_PROPERTY = "configuration";
public static final String NOTIFICATION_TABLE_NAME = "notification"; 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.Type;
import org.hibernate.annotations.TypeDef; import org.hibernate.annotations.TypeDef;
import org.thingsboard.server.common.data.id.NotificationTargetId; 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.NotificationTarget;
import org.thingsboard.server.common.data.notification.targets.NotificationTargetConfig; 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.BaseSqlEntity;
import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.util.mapping.JsonStringType; import org.thingsboard.server.dao.util.mapping.JsonStringType;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Table; import javax.persistence.Table;
import java.util.UUID; import java.util.UUID;
@ -46,6 +48,10 @@ public class NotificationTargetEntity extends BaseSqlEntity<NotificationTarget>
@Column(name = ModelConstants.NAME_PROPERTY, nullable = false) @Column(name = ModelConstants.NAME_PROPERTY, nullable = false)
private String name; private String name;
@Enumerated(EnumType.STRING)
@Column(name = ModelConstants.NOTIFICATION_TARGET_TYPE_PROPERTY, nullable = false)
private NotificationTargetType type;
@Type(type = "json") @Type(type = "json")
@Column(name = ModelConstants.NOTIFICATION_TARGET_CONFIGURATION_PROPERTY, nullable = false) @Column(name = ModelConstants.NOTIFICATION_TARGET_CONFIGURATION_PROPERTY, nullable = false)
private JsonNode configuration; private JsonNode configuration;
@ -57,6 +63,7 @@ public class NotificationTargetEntity extends BaseSqlEntity<NotificationTarget>
setCreatedTime(notificationTarget.getCreatedTime()); setCreatedTime(notificationTarget.getCreatedTime());
setTenantId(getTenantUuid(notificationTarget.getTenantId())); setTenantId(getTenantUuid(notificationTarget.getTenantId()));
setName(notificationTarget.getName()); setName(notificationTarget.getName());
setType(notificationTarget.getType());
setConfiguration(toJson(notificationTarget.getConfiguration())); setConfiguration(toJson(notificationTarget.getConfiguration()));
} }
@ -67,6 +74,7 @@ public class NotificationTargetEntity extends BaseSqlEntity<NotificationTarget>
notificationTarget.setCreatedTime(createdTime); notificationTarget.setCreatedTime(createdTime);
notificationTarget.setTenantId(getTenantId(tenantId)); notificationTarget.setTenantId(getTenantId(tenantId));
notificationTarget.setName(name); notificationTarget.setName(name);
notificationTarget.setType(type);
notificationTarget.setConfiguration(fromJson(configuration, NotificationTargetConfig.class)); notificationTarget.setConfiguration(fromJson(configuration, NotificationTargetConfig.class));
return notificationTarget; 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.TenantId;
import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.notification.NotificationRequestStatus; 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.NotificationTarget;
import org.thingsboard.server.common.data.notification.targets.NotificationTargetConfig; 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.PageData;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.entity.AbstractEntityService;
@ -85,9 +87,13 @@ public class DefaultNotificationTargetService extends AbstractEntityService impl
@Override @Override
public PageData<User> findRecipientsForNotificationTargetConfig(TenantId tenantId, CustomerId customerId, NotificationTargetConfig targetConfig, PageLink pageLink) { 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: { 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)) .map(UserId::new).map(userId -> userService.findUserById(tenantId, userId))
.collect(Collectors.toList()); .collect(Collectors.toList());
return new PageData<>(users, 1, users.size(), false); 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)) { if (tenantId.equals(TenantId.SYS_TENANT_ID)) {
throw new IllegalArgumentException("Customer users target is not supported for system administrator"); throw new IllegalArgumentException("Customer users target is not supported for system administrator");
} }
CustomerUsersNotificationTargetConfig customerUsersConfig = (CustomerUsersNotificationTargetConfig) targetConfig; CustomerUsersFilter customerUsersConfig = (CustomerUsersFilter) usersFilter;
if (!customerUsersConfig.isGetCustomerIdFromOriginatorEntity()) { if (!customerUsersConfig.isGetCustomerIdFromOriginatorEntity()) {
customerId = new CustomerId(customerUsersConfig.getCustomerId()); 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, created_time BIGINT NOT NULL,
tenant_id UUID NULL CONSTRAINT fk_notification_target_tenant_id REFERENCES tenant(id) ON DELETE CASCADE, tenant_id UUID NULL CONSTRAINT fk_notification_target_tenant_id REFERENCES tenant(id) ON DELETE CASCADE,
name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL,
type VARCHAR(32) NOT NULL,
configuration VARCHAR(10000) NOT NULL, configuration VARCHAR(10000) NOT NULL,
CONSTRAINT uq_notification_target_name UNIQUE (tenant_id, name) 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; package org.thingsboard.rule.engine.api.slack;
import org.thingsboard.server.common.data.id.TenantId; 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; import java.util.List;
@ -24,9 +25,9 @@ public interface SlackService {
void sendMessage(TenantId tenantId, String token, String conversationId, String message); 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); 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.TbNode;
import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeConfiguration;
import org.thingsboard.rule.engine.api.TbNodeException; 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.rule.engine.api.util.TbNodeUtils;
import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.msg.TbMsg; 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 lombok.Data;
import org.thingsboard.rule.engine.api.NodeConfiguration; 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.NotEmpty;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
@ -31,7 +31,7 @@ public class TbSlackNodeConfiguration implements NodeConfiguration<TbSlackNodeCo
private String messageTemplate; private String messageTemplate;
@NotNull @NotNull
private SlackConversation.Type conversationType; private SlackConversationType conversationType;
private String conversationId; // if not set, need to specify conversationNamePattern private String conversationId; // if not set, need to specify conversationNamePattern
private String conversationNamePattern; private String conversationNamePattern;
@ -40,7 +40,7 @@ public class TbSlackNodeConfiguration implements NodeConfiguration<TbSlackNodeCo
TbSlackNodeConfiguration config = new TbSlackNodeConfiguration(); TbSlackNodeConfiguration config = new TbSlackNodeConfiguration();
config.setBotToken("xoxb-"); config.setBotToken("xoxb-");
config.setMessageTemplate("Device ${deviceId}: temperature is $[temperature]"); config.setMessageTemplate("Device ${deviceId}: temperature is $[temperature]");
config.setConversationType(SlackConversation.Type.PUBLIC_CHANNEL); config.setConversationType(SlackConversationType.PUBLIC_CHANNEL);
return config; return config;
} }

Loading…
Cancel
Save