From be5eed96dc8abe261acafa3649a54c2d8310db3e Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Mon, 30 Jan 2023 14:50:44 +0200 Subject: [PATCH] Slack notification target; refactoring --- .../main/data/upgrade/3.4.3/schema_update.sql | 1 + .../controller/NotificationController.java | 17 ++- .../NotificationTargetController.java | 31 ++-- .../NotificationTemplateController.java | 5 +- .../DefaultNotificationCenter.java | 137 +++++++++++------- .../channels/EmailNotificationChannel.java | 2 +- .../channels/NotificationChannel.java | 8 +- .../channels/SlackNotificationChannel.java | 10 +- .../channels/SmsNotificationChannel.java | 2 +- .../service/slack/DefaultSlackService.java | 11 +- .../AbstractNotificationApiTest.java | 14 +- .../notification/NotificationApiTest.java | 61 +++++--- .../NotificationTargetApiTest.java | 59 +++++--- .../thingsboard/server/common/data/User.java | 3 +- .../NotificationDeliveryMethod.java | 15 +- .../NotificationProcessingContext.java | 3 +- .../NotificationRequestStats.java | 23 +-- ...Config.java => NotificationRecipient.java} | 12 +- .../targets/NotificationTarget.java | 2 + .../targets/NotificationTargetConfig.java | 9 +- .../targets/NotificationTargetType.java | 33 +++++ .../platform/AllUsersFilter.java} | 14 +- .../CustomerUsersFilter.java} | 10 +- ...PlatformUsersNotificationTargetConfig.java | 39 +++++ .../UserListFilter.java} | 10 +- .../targets/platform/UsersFilter.java | 36 +++++ .../UsersFilterType.java} | 4 +- .../targets/slack/SlackConversation.java | 29 ++++ .../targets/slack/SlackConversationType.java | 22 +++ .../slack/SlackNotificationTargetConfig.java | 40 +++++ .../DeliveryMethodNotificationTemplate.java | 1 + ...ackDeliveryMethodNotificationTemplate.java | 10 +- .../server/dao/model/ModelConstants.java | 1 + .../model/sql/NotificationTargetEntity.java | 10 +- .../DefaultNotificationTargetService.java | 16 +- .../main/resources/sql/schema-entities.sql | 1 + .../rule/engine/api/slack/SlackService.java | 7 +- .../rule/engine/notification/TbSlackNode.java | 2 +- .../TbSlackNodeConfiguration.java | 6 +- 39 files changed, 493 insertions(+), 223 deletions(-) rename common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/{AllUsersNotificationTargetConfig.java => NotificationRecipient.java} (69%) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetType.java rename common/data/src/main/java/org/thingsboard/server/common/data/notification/{template/SlackConversation.java => targets/platform/AllUsersFilter.java} (73%) rename common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/{CustomerUsersNotificationTargetConfig.java => platform/CustomerUsersFilter.java} (79%) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/PlatformUsersNotificationTargetConfig.java rename common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/{UserListNotificationTargetConfig.java => platform/UserListFilter.java} (71%) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UsersFilter.java rename common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/{NotificationTargetConfigType.java => platform/UsersFilterType.java} (87%) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/slack/SlackConversation.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/slack/SlackConversationType.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/slack/SlackNotificationTargetConfig.java diff --git a/application/src/main/data/upgrade/3.4.3/schema_update.sql b/application/src/main/data/upgrade/3.4.3/schema_update.sql index 05a2e643fa..2cfe5980cf 100644 --- a/application/src/main/data/upgrade/3.4.3/schema_update.sql +++ b/application/src/main/data/upgrade/3.4.3/schema_update.sql @@ -19,6 +19,7 @@ CREATE TABLE IF NOT EXISTS notification_target ( created_time BIGINT NOT NULL, tenant_id UUID NULL CONSTRAINT fk_notification_target_tenant_id REFERENCES tenant(id) ON DELETE CASCADE, name VARCHAR(255) NOT NULL, + type VARCHAR(32) NOT NULL, configuration VARCHAR(10000) NOT NULL, CONSTRAINT uq_notification_target_name UNIQUE (tenant_id, name) ); diff --git a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java index a57fe18264..759d4249a4 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java @@ -44,6 +44,7 @@ import org.thingsboard.server.common.data.notification.NotificationRequestInfo; import org.thingsboard.server.common.data.notification.NotificationRequestPreview; import org.thingsboard.server.common.data.notification.settings.NotificationSettings; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; +import org.thingsboard.server.common.data.notification.targets.NotificationTargetType; import org.thingsboard.server.common.data.notification.template.DeliveryMethodNotificationTemplate; import org.thingsboard.server.common.data.notification.template.NotificationTemplate; import org.thingsboard.server.common.data.page.PageData; @@ -59,6 +60,7 @@ import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; import javax.validation.Valid; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.UUID; @@ -198,11 +200,15 @@ public class NotificationController extends BaseController { .settings(null) .template(notificationTemplate) .build(); - mockProcessingCtx.init(); - Map templateContext = mockProcessingCtx.createTemplateContext(user); Map processedTemplates = mockProcessingCtx.getDeliveryMethods().stream() .collect(Collectors.toMap(m -> m, deliveryMethod -> { + Map templateContext; + if (NotificationTargetType.PLATFORM_USERS.getSupportedDeliveryMethods().contains(deliveryMethod)) { + templateContext = mockProcessingCtx.createTemplateContext(user); + } else { + templateContext = Collections.emptyMap(); + } return mockProcessingCtx.getProcessedTemplate(deliveryMethod, templateContext); })); preview.setProcessedTemplates(processedTemplates); @@ -214,7 +220,12 @@ public class NotificationController extends BaseController { throw new IllegalArgumentException("Notification target with id " + targetId + " not found"); } - int recipientsCount = notificationTargetService.countRecipientsForNotificationTargetConfig(user.getTenantId(), notificationTarget.getConfiguration()); + int recipientsCount; + if (notificationTarget.getType() == NotificationTargetType.PLATFORM_USERS) { + recipientsCount = notificationTargetService.countRecipientsForNotificationTargetConfig(user.getTenantId(), notificationTarget.getConfiguration()); + } else { + recipientsCount = 1; + } recipientsCountByTarget.put(notificationTarget.getName(), recipientsCount); }); preview.setRecipientsCountByTarget(recipientsCountByTarget); diff --git a/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java b/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java index a48a7300e4..6bd656bca3 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java @@ -34,6 +34,7 @@ import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; import org.thingsboard.server.common.data.notification.targets.NotificationTargetConfig; +import org.thingsboard.server.common.data.notification.targets.NotificationTargetType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageDataIterable; import org.thingsboard.server.common.data.page.PageLink; @@ -59,15 +60,15 @@ public class NotificationTargetController extends BaseController { @ApiOperation(value = "Save notification target (saveNotificationTarget)", notes = "Create or update notification target.\n\n" + - "Examples with different configuration types:\n" + - "- USER_LIST:\n" + - "```\n{\n \"name\": \"Special users\",\n \"configuration\": {\n \"type\": \"USER_LIST\",\n \"usersIds\": [\n \"ea31a460-3d85-11ed-9200-77fc04fa14fa\",\n \"86f7b260-3d88-11ed-ad72-ad2ee0f70ba1\"\n ]\n }\n}\n```\n" + - "- CUSTOMER_USERS (not accessible to system administrator):\n" + - "```\n{\n \"name\": \"Users of my customer\",\n \"configuration\": {\n \"type\": \"CUSTOMER_USERS\",\n \"customerId\": \"ea31a460-3d85-11ed-9200-77fc04fa14fa\"\n }\n}\n```\n" + - "or if you would like to use the target in notification rule and get customerId from alarm:\n" + - "```\n{\n \"name\": \"Alarm's customer users\",\n \"configuration\": {\n \"type\": \"CUSTOMER_USERS\",\n \"customerId\": null,\n \"getCustomerIdFromOriginatorEntity\": true\n }\n}\n```\n" + - "- ALL_USERS:\n" + - "```\n{\n \"name\": \"All my users\",\n \"configuration\": {\n \"type\": \"ALL_USERS\"\n }\n}\n```\n\n" + +// "Examples with different configuration types:\n" + +// "- USER_LIST:\n" + +// "```\n{\n \"name\": \"Special users\",\n \"configuration\": {\n \"type\": \"USER_LIST\",\n \"usersIds\": [\n \"ea31a460-3d85-11ed-9200-77fc04fa14fa\",\n \"86f7b260-3d88-11ed-ad72-ad2ee0f70ba1\"\n ]\n }\n}\n```\n" + +// "- CUSTOMER_USERS (not accessible to system administrator):\n" + +// "```\n{\n \"name\": \"Users of my customer\",\n \"configuration\": {\n \"type\": \"CUSTOMER_USERS\",\n \"customerId\": \"ea31a460-3d85-11ed-9200-77fc04fa14fa\"\n }\n}\n```\n" + +// "or if you would like to use the target in notification rule and get customerId from alarm:\n" + +// "```\n{\n \"name\": \"Alarm's customer users\",\n \"configuration\": {\n \"type\": \"CUSTOMER_USERS\",\n \"customerId\": null,\n \"getCustomerIdFromOriginatorEntity\": true\n }\n}\n```\n" + +// "- ALL_USERS:\n" + +// "```\n{\n \"name\": \"All my users\",\n \"configuration\": {\n \"type\": \"ALL_USERS\"\n }\n}\n```\n\n" + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PostMapping("/target") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @@ -77,11 +78,13 @@ public class NotificationTargetController extends BaseController { checkEntity(notificationTarget.getId(), notificationTarget, Resource.NOTIFICATION_TARGET); if (!user.isSystemAdmin()) { NotificationTargetConfig targetConfig = notificationTarget.getConfiguration(); - PageDataIterable recipients = new PageDataIterable<>(pageLink -> { - return notificationTargetService.findRecipientsForNotificationTargetConfig(user.getTenantId(), null, targetConfig, pageLink); - }, 200); - for (User recipient : recipients) { - accessControlService.checkPermission(user, Resource.USER, Operation.READ, recipient.getId(), recipient); + if (targetConfig.getType() == NotificationTargetType.PLATFORM_USERS) { + PageDataIterable recipients = new PageDataIterable<>(pageLink -> { + return notificationTargetService.findRecipientsForNotificationTargetConfig(user.getTenantId(), null, targetConfig, pageLink); + }, 200); + for (User recipient : recipients) { + accessControlService.checkPermission(user, Resource.USER, Operation.READ, recipient.getId(), recipient); + } } } diff --git a/application/src/main/java/org/thingsboard/server/controller/NotificationTemplateController.java b/application/src/main/java/org/thingsboard/server/controller/NotificationTemplateController.java index 92543e4424..bcb1232e80 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationTemplateController.java +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationTemplateController.java @@ -35,8 +35,9 @@ import org.thingsboard.server.common.data.notification.NotificationDeliveryMetho import org.thingsboard.server.common.data.notification.NotificationType; import org.thingsboard.server.common.data.notification.settings.NotificationSettings; import org.thingsboard.server.common.data.notification.settings.SlackNotificationDeliveryMethodConfig; +import org.thingsboard.server.common.data.notification.targets.slack.SlackConversationType; import org.thingsboard.server.common.data.notification.template.NotificationTemplate; -import org.thingsboard.server.common.data.notification.template.SlackConversation; +import org.thingsboard.server.common.data.notification.targets.slack.SlackConversation; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.notification.NotificationSettingsService; @@ -133,7 +134,7 @@ public class NotificationTemplateController extends BaseController { SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @GetMapping("/slack/conversations") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - public List listSlackConversations(@RequestParam SlackConversation.Type type, + public List listSlackConversations(@RequestParam SlackConversationType type, @AuthenticationPrincipal SecurityUser user) { NotificationSettings settings = notificationSettingsService.findNotificationSettings(user.getTenantId()); SlackNotificationDeliveryMethodConfig slackConfig = (SlackNotificationDeliveryMethodConfig) diff --git a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java index 54726044b3..276ae4643d 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java @@ -40,14 +40,18 @@ import org.thingsboard.server.common.data.notification.NotificationRequestStatus import org.thingsboard.server.common.data.notification.NotificationStatus; import org.thingsboard.server.common.data.notification.NotificationType; import org.thingsboard.server.common.data.notification.settings.NotificationSettings; +import org.thingsboard.server.common.data.notification.targets.NotificationRecipient; +import org.thingsboard.server.common.data.notification.targets.NotificationTarget; +import org.thingsboard.server.common.data.notification.targets.slack.SlackConversation; +import org.thingsboard.server.common.data.notification.targets.slack.SlackNotificationTargetConfig; import org.thingsboard.server.common.data.notification.template.DeliveryMethodNotificationTemplate; import org.thingsboard.server.common.data.notification.template.NotificationTemplate; import org.thingsboard.server.common.data.notification.template.PushDeliveryMethodNotificationTemplate; +import org.thingsboard.server.common.data.page.PageDataIterable; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; -import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.notification.NotificationRequestService; import org.thingsboard.server.dao.notification.NotificationService; import org.thingsboard.server.dao.notification.NotificationSettingsService; @@ -79,7 +83,7 @@ import java.util.stream.Collectors; @Slf4j @RequiredArgsConstructor @SuppressWarnings({"UnstableApiUsage", "rawtypes"}) -public class DefaultNotificationCenter extends AbstractSubscriptionService implements NotificationCenter, NotificationChannel { +public class DefaultNotificationCenter extends AbstractSubscriptionService implements NotificationCenter, NotificationChannel { private final NotificationTargetService notificationTargetService; private final NotificationRequestService notificationRequestService; @@ -95,9 +99,17 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple @Override public NotificationRequest processNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest) { - notificationRequest.setTenantId(tenantId); NotificationSettings settings = notificationSettingsService.findNotificationSettings(tenantId); NotificationTemplate notificationTemplate = notificationTemplateService.findNotificationTemplateById(tenantId, notificationRequest.getTemplateId()); + List targets = notificationRequest.getTargets().stream() + .map(NotificationTargetId::new) + .map(targetId -> notificationTargetService.findNotificationTargetById(tenantId, targetId)) + .peek(target -> { + if (target == null) { + throw new IllegalArgumentException("Some of the targets no longer exist"); + } + }) + .collect(Collectors.toList()); notificationTemplate.getConfiguration().getDeliveryMethodsTemplates().forEach((deliveryMethod, template) -> { if (!template.isEnabled()) return; @@ -110,6 +122,9 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple throw new IllegalArgumentException("Slack must be configured in the settings"); } } + if (targets.stream().noneMatch(target -> target.getType().getSupportedDeliveryMethods().contains(deliveryMethod))) { + throw new IllegalArgumentException("Target for " + deliveryMethod + " delivery method is missing"); + } }); if (notificationRequest.getAdditionalConfig() != null) { @@ -122,64 +137,33 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple } } - log.debug("Processing notification request (tenant id: {}, notification targets: {})", tenantId, notificationRequest.getTargets()); + log.debug("Processing notification request (tenantId: {}, targets: {})", tenantId, notificationRequest.getTargets()); notificationRequest.setStatus(NotificationRequestStatus.PROCESSING); NotificationRequest savedNotificationRequest = notificationRequestService.saveNotificationRequest(tenantId, notificationRequest); - notificationExecutor.submit(() -> { - NotificationProcessingContext ctx = NotificationProcessingContext.builder() - .tenantId(tenantId) - .request(savedNotificationRequest) - .settings(settings) - .template(notificationTemplate) - .build(); - ctx.init(); + NotificationProcessingContext ctx = NotificationProcessingContext.builder() + .tenantId(tenantId) + .request(savedNotificationRequest) + .settings(settings) + .template(notificationTemplate) + .build(); - Set deliveryMethods = ctx.getDeliveryMethods(); + notificationExecutor.submit(() -> { List> results = new ArrayList<>(); - for (UUID targetId : notificationRequest.getTargets()) { - DaoUtil.processBatches(pageLink -> { - return notificationTargetService.findRecipientsForNotificationTarget(tenantId, ctx.getCustomerId(), new NotificationTargetId(targetId), pageLink); - }, 200, recipientsBatch -> { - for (NotificationDeliveryMethod deliveryMethod : deliveryMethods) { - if (deliveryMethod.isStandalone()) continue; - - List 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 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 resultFuture = process(notificationChannel, null, ctx); - DonAsynchron.withCallback(resultFuture, result -> { - ctx.getStats().reportSent(deliveryMethod, null); - }, error -> { - ctx.getStats().reportError(deliveryMethod, error, null); - }); - results.add(resultFuture); - } + for (NotificationTarget target : targets) { + List> result = processForTarget(target, ctx); + results.addAll(result); } Futures.whenAllComplete(results).run(() -> { + NotificationRequestId requestId = savedNotificationRequest.getId(); + log.debug("[{}] Notification request processing is finished", requestId); NotificationRequestStats stats = ctx.getStats(); try { - notificationRequestService.updateNotificationRequest(tenantId, savedNotificationRequest.getId(), - NotificationRequestStatus.SENT, stats); + notificationRequestService.updateNotificationRequest(tenantId, requestId, NotificationRequestStatus.SENT, stats); } catch (Exception e) { - log.error("Failed to update stats for notification request {}", savedNotificationRequest.getId(), e); + log.error("[{}] Failed to update stats for notification request", requestId, e); } UserId senderId = notificationRequest.getSenderId(); @@ -200,25 +184,70 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple return savedNotificationRequest; } - private ListenableFuture process(NotificationChannel notificationChannel, User recipient, NotificationProcessingContext ctx) { - NotificationDeliveryMethod deliveryMethod = notificationChannel.getDeliveryMethod(); - if (recipient != null && ctx.getStats().contains(deliveryMethod, recipient.getId())) { + private List> processForTarget(NotificationTarget target, NotificationProcessingContext ctx) { + Iterable recipients; + switch (target.getType()) { + case PLATFORM_USERS: { + recipients = new PageDataIterable<>(pageLink -> { + return notificationTargetService.findRecipientsForNotificationTargetConfig(ctx.getTenantId(), ctx.getCustomerId(), target.getConfiguration(), pageLink); + }, 200); + break; + } + case SLACK: { + SlackNotificationTargetConfig slackTargetConfig = (SlackNotificationTargetConfig) target.getConfiguration(); + SlackConversation conversation = new SlackConversation(slackTargetConfig.getConversationId(), slackTargetConfig.getConversationName()); + recipients = List.of(conversation); + break; + } + default: { + recipients = Collections.emptyList(); + } + } + + Set 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> results = new ArrayList<>(); + for (NotificationRecipient recipient : recipients) { + for (NotificationDeliveryMethod deliveryMethod : deliveryMethods) { + ListenableFuture 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 processForRecipient(NotificationDeliveryMethod deliveryMethod, NotificationRecipient recipient, NotificationProcessingContext ctx) { + if (ctx.getStats().contains(deliveryMethod, recipient.getId())) { return Futures.immediateFailedFuture(new AlreadySentException()); } + Map templateContext; + if (recipient instanceof User) { + templateContext = ctx.createTemplateContext(((User) recipient)); + } else { + templateContext = Collections.emptyMap(); + } DeliveryMethodNotificationTemplate processedTemplate; try { - Map templateContext = recipient != null ? ctx.createTemplateContext(recipient) : Collections.emptyMap(); processedTemplate = ctx.getProcessedTemplate(deliveryMethod, templateContext); } catch (Exception e) { return Futures.immediateFailedFuture(e); } + + NotificationChannel notificationChannel = channels.get(deliveryMethod); + log.trace("[{}] Sending {} notification for recipient {}", ctx.getRequest().getId(), deliveryMethod, recipient); return notificationChannel.sendNotification(recipient, processedTemplate, ctx); } @Override public ListenableFuture sendNotification(User recipient, PushDeliveryMethodNotificationTemplate processedTemplate, NotificationProcessingContext ctx) { NotificationRequest request = ctx.getRequest(); - log.trace("Creating notification for recipient {} (notification request id: {})", recipient.getId(), request.getId()); Notification notification = Notification.builder() .requestId(request.getId()) .recipientId(recipient.getId()) diff --git a/application/src/main/java/org/thingsboard/server/service/notification/channels/EmailNotificationChannel.java b/application/src/main/java/org/thingsboard/server/service/notification/channels/EmailNotificationChannel.java index 4616988cf1..324e40143d 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/channels/EmailNotificationChannel.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/channels/EmailNotificationChannel.java @@ -27,7 +27,7 @@ import org.thingsboard.server.common.data.notification.NotificationProcessingCon @Component @RequiredArgsConstructor -public class EmailNotificationChannel implements NotificationChannel { +public class EmailNotificationChannel implements NotificationChannel { private final MailService mailService; private final MailExecutorService executor; diff --git a/application/src/main/java/org/thingsboard/server/service/notification/channels/NotificationChannel.java b/application/src/main/java/org/thingsboard/server/service/notification/channels/NotificationChannel.java index f1e172d2e5..6a13cebc9d 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/channels/NotificationChannel.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/channels/NotificationChannel.java @@ -16,14 +16,14 @@ package org.thingsboard.server.service.notification.channels; import com.google.common.util.concurrent.ListenableFuture; -import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; -import org.thingsboard.server.common.data.notification.template.DeliveryMethodNotificationTemplate; import org.thingsboard.server.common.data.notification.NotificationProcessingContext; +import org.thingsboard.server.common.data.notification.targets.NotificationRecipient; +import org.thingsboard.server.common.data.notification.template.DeliveryMethodNotificationTemplate; -public interface NotificationChannel { +public interface NotificationChannel { - ListenableFuture sendNotification(User recipient, T processedTemplate, NotificationProcessingContext ctx); + ListenableFuture sendNotification(R recipient, T processedTemplate, NotificationProcessingContext ctx); NotificationDeliveryMethod getDeliveryMethod(); diff --git a/application/src/main/java/org/thingsboard/server/service/notification/channels/SlackNotificationChannel.java b/application/src/main/java/org/thingsboard/server/service/notification/channels/SlackNotificationChannel.java index eb4126bccb..f1b81f082e 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/channels/SlackNotificationChannel.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/channels/SlackNotificationChannel.java @@ -20,30 +20,30 @@ import com.google.common.util.concurrent.ListenableFuture; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; import org.thingsboard.rule.engine.api.slack.SlackService; -import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.notification.AlreadySentException; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; +import org.thingsboard.server.common.data.notification.NotificationProcessingContext; import org.thingsboard.server.common.data.notification.settings.SlackNotificationDeliveryMethodConfig; +import org.thingsboard.server.common.data.notification.targets.slack.SlackConversation; import org.thingsboard.server.common.data.notification.template.SlackDeliveryMethodNotificationTemplate; import org.thingsboard.server.service.executors.ExternalCallExecutorService; -import org.thingsboard.server.common.data.notification.NotificationProcessingContext; @Component @RequiredArgsConstructor -public class SlackNotificationChannel implements NotificationChannel { +public class SlackNotificationChannel implements NotificationChannel { private final SlackService slackService; private final ExternalCallExecutorService executor; @Override - public ListenableFuture sendNotification(User recipient, SlackDeliveryMethodNotificationTemplate processedTemplate, NotificationProcessingContext ctx) { + public ListenableFuture sendNotification(SlackConversation conversation, SlackDeliveryMethodNotificationTemplate processedTemplate, NotificationProcessingContext ctx) { if (ctx.getStats().contains(NotificationDeliveryMethod.SLACK)) { return Futures.immediateFailedFuture(new AlreadySentException()); } SlackNotificationDeliveryMethodConfig config = ctx.getDeliveryMethodConfig(NotificationDeliveryMethod.SLACK); return executor.submit(() -> { - slackService.sendMessage(ctx.getTenantId(), config.getBotToken(), processedTemplate.getConversationId(), processedTemplate.getBody()); + slackService.sendMessage(ctx.getTenantId(), config.getBotToken(), conversation.getId(), processedTemplate.getBody()); return null; }); } diff --git a/application/src/main/java/org/thingsboard/server/service/notification/channels/SmsNotificationChannel.java b/application/src/main/java/org/thingsboard/server/service/notification/channels/SmsNotificationChannel.java index a4dceb851a..b6d58b30b4 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/channels/SmsNotificationChannel.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/channels/SmsNotificationChannel.java @@ -29,7 +29,7 @@ import org.thingsboard.server.service.sms.SmsExecutorService; @Component @RequiredArgsConstructor -public class SmsNotificationChannel implements NotificationChannel { +public class SmsNotificationChannel implements NotificationChannel { private final SmsService smsService; private final SmsExecutorService executor; diff --git a/application/src/main/java/org/thingsboard/server/service/slack/DefaultSlackService.java b/application/src/main/java/org/thingsboard/server/service/slack/DefaultSlackService.java index e05857f57d..66027555c3 100644 --- a/application/src/main/java/org/thingsboard/server/service/slack/DefaultSlackService.java +++ b/application/src/main/java/org/thingsboard/server/service/slack/DefaultSlackService.java @@ -30,12 +30,13 @@ import com.slack.api.model.ConversationType; import lombok.RequiredArgsConstructor; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.notification.template.SlackConversation; +import org.thingsboard.server.common.data.notification.targets.slack.SlackConversation; import org.thingsboard.rule.engine.api.slack.SlackService; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; import org.thingsboard.server.common.data.notification.settings.NotificationSettings; import org.thingsboard.server.common.data.notification.settings.SlackNotificationDeliveryMethodConfig; +import org.thingsboard.server.common.data.notification.targets.slack.SlackConversationType; import org.thingsboard.server.common.data.util.ThrowingBiFunction; import org.thingsboard.server.dao.notification.NotificationSettingsService; @@ -66,9 +67,9 @@ public class DefaultSlackService implements SlackService { } @Override - public List listConversations(TenantId tenantId, String token, SlackConversation.Type conversationType) { + public List listConversations(TenantId tenantId, String token, SlackConversationType conversationType) { return cache.get(conversationType + ":" + token, k -> { - if (conversationType == SlackConversation.Type.DIRECT) { + if (conversationType == SlackConversationType.DIRECT) { UsersListRequest request = UsersListRequest.builder() .limit(CONVERSATIONS_LIMIT) .build(); @@ -85,7 +86,7 @@ public class DefaultSlackService implements SlackService { .collect(Collectors.toList()); } else { ConversationsListRequest request = ConversationsListRequest.builder() - .types(List.of(conversationType == SlackConversation.Type.PUBLIC_CHANNEL ? + .types(List.of(conversationType == SlackConversationType.PUBLIC_CHANNEL ? ConversationType.PUBLIC_CHANNEL : ConversationType.PRIVATE_CHANNEL)) .limit(CONVERSATIONS_LIMIT) @@ -107,7 +108,7 @@ public class DefaultSlackService implements SlackService { } @Override - public SlackConversation findConversation(TenantId tenantId, String token, SlackConversation.Type conversationType, String namePattern) { + public SlackConversation findConversation(TenantId tenantId, String token, SlackConversationType conversationType, String namePattern) { List conversations = listConversations(tenantId, token, conversationType); return conversations.stream() .filter(conversation -> StringUtils.containsIgnoreCase(conversation.getName(), namePattern)) diff --git a/application/src/test/java/org/thingsboard/server/service/notification/AbstractNotificationApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/AbstractNotificationApiTest.java index 2b1c34d974..c076c33827 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/AbstractNotificationApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/AbstractNotificationApiTest.java @@ -38,7 +38,10 @@ import org.thingsboard.server.common.data.notification.NotificationType; import org.thingsboard.server.common.data.notification.info.UserOriginatedNotificationInfo; import org.thingsboard.server.common.data.notification.settings.NotificationSettings; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; -import org.thingsboard.server.common.data.notification.targets.UserListNotificationTargetConfig; +import org.thingsboard.server.common.data.notification.targets.NotificationTargetType; +import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig; +import org.thingsboard.server.common.data.notification.targets.platform.UserListFilter; +import org.thingsboard.server.common.data.notification.targets.slack.SlackNotificationTargetConfig; import org.thingsboard.server.common.data.notification.template.DeliveryMethodNotificationTemplate; import org.thingsboard.server.common.data.notification.template.EmailDeliveryMethodNotificationTemplate; import org.thingsboard.server.common.data.notification.template.NotificationTemplate; @@ -78,9 +81,12 @@ public abstract class AbstractNotificationApiTest extends AbstractControllerTest NotificationTarget notificationTarget = new NotificationTarget(); notificationTarget.setTenantId(tenantId); notificationTarget.setName("Users " + List.of(usersIds)); - UserListNotificationTargetConfig config = new UserListNotificationTargetConfig(); - config.setUsersIds(DaoUtil.toUUIDs(List.of(usersIds))); - notificationTarget.setConfiguration(config); + notificationTarget.setType(NotificationTargetType.PLATFORM_USERS); + PlatformUsersNotificationTargetConfig targetConfig = new PlatformUsersNotificationTargetConfig(); + UserListFilter filter = new UserListFilter(); + filter.setUsersIds(DaoUtil.toUUIDs(List.of(usersIds))); + targetConfig.setUsersFilter(filter); + notificationTarget.setConfiguration(targetConfig); return saveNotificationTarget(notificationTarget); } diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java index 44c02ce36a..2ce562f4a4 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java @@ -22,7 +22,6 @@ import org.java_websocket.client.WebSocketClient; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.api.NotificationCenter; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.id.NotificationTargetId; @@ -38,16 +37,18 @@ import org.thingsboard.server.common.data.notification.NotificationType; import org.thingsboard.server.common.data.notification.info.UserOriginatedNotificationInfo; import org.thingsboard.server.common.data.notification.settings.NotificationSettings; import org.thingsboard.server.common.data.notification.settings.SlackNotificationDeliveryMethodConfig; -import org.thingsboard.server.common.data.notification.targets.AllUsersNotificationTargetConfig; -import org.thingsboard.server.common.data.notification.targets.CustomerUsersNotificationTargetConfig; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; -import org.thingsboard.server.common.data.notification.targets.UserListNotificationTargetConfig; +import org.thingsboard.server.common.data.notification.targets.NotificationTargetType; +import org.thingsboard.server.common.data.notification.targets.platform.AllUsersFilter; +import org.thingsboard.server.common.data.notification.targets.platform.CustomerUsersFilter; +import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig; +import org.thingsboard.server.common.data.notification.targets.platform.UserListFilter; +import org.thingsboard.server.common.data.notification.targets.slack.SlackNotificationTargetConfig; import org.thingsboard.server.common.data.notification.template.DeliveryMethodNotificationTemplate; import org.thingsboard.server.common.data.notification.template.EmailDeliveryMethodNotificationTemplate; import org.thingsboard.server.common.data.notification.template.NotificationTemplate; import org.thingsboard.server.common.data.notification.template.NotificationTemplateConfig; import org.thingsboard.server.common.data.notification.template.PushDeliveryMethodNotificationTemplate; -import org.thingsboard.server.common.data.notification.template.SlackConversation; import org.thingsboard.server.common.data.notification.template.SlackDeliveryMethodNotificationTemplate; import org.thingsboard.server.common.data.notification.template.SmsDeliveryMethodNotificationTemplate; import org.thingsboard.server.common.data.page.PageData; @@ -370,8 +371,11 @@ public class NotificationApiTest extends AbstractNotificationApiTest { public void testNotificationRequestPreview() throws Exception { NotificationTarget target1 = new NotificationTarget(); target1.setName("Me"); - UserListNotificationTargetConfig target1Config = new UserListNotificationTargetConfig(); - target1Config.setUsersIds(DaoUtil.toUUIDs(List.of(tenantAdminUserId))); + target1.setType(NotificationTargetType.PLATFORM_USERS); + PlatformUsersNotificationTargetConfig target1Config = new PlatformUsersNotificationTargetConfig(); + UserListFilter userListFilter = new UserListFilter(); + userListFilter.setUsersIds(DaoUtil.toUUIDs(List.of(tenantAdminUserId))); + target1Config.setUsersFilter(userListFilter); target1.setConfiguration(target1Config); target1 = saveNotificationTarget(target1); @@ -388,9 +392,12 @@ public class NotificationApiTest extends AbstractNotificationApiTest { } NotificationTarget target2 = new NotificationTarget(); target2.setName("Other customer users"); - CustomerUsersNotificationTargetConfig target2Config = new CustomerUsersNotificationTargetConfig(); - target2Config.setCustomerId(differentCustomerId.getId()); - target2Config.setGetCustomerIdFromOriginatorEntity(false); + target2.setType(NotificationTargetType.PLATFORM_USERS); + PlatformUsersNotificationTargetConfig target2Config = new PlatformUsersNotificationTargetConfig(); + CustomerUsersFilter customerUsersFilter = new CustomerUsersFilter(); + customerUsersFilter.setCustomerId(differentCustomerId.getId()); + customerUsersFilter.setGetCustomerIdFromOriginatorEntity(false); + target2Config.setUsersFilter(customerUsersFilter); target2.setConfiguration(target2Config); target2 = saveNotificationTarget(target2); @@ -426,8 +433,6 @@ public class NotificationApiTest extends AbstractNotificationApiTest { SlackDeliveryMethodNotificationTemplate slackNotificationTemplate = new SlackDeliveryMethodNotificationTemplate(); slackNotificationTemplate.setEnabled(true); - slackNotificationTemplate.setConversationType(SlackConversation.Type.PUBLIC_CHANNEL); - slackNotificationTemplate.setConversationId("U1234567"); slackNotificationTemplate.setBody("Message for SLACK: ${email}"); templates.put(NotificationDeliveryMethod.SLACK, slackNotificationTemplate); @@ -472,8 +477,7 @@ public class NotificationApiTest extends AbstractNotificationApiTest { assertThat(processedTemplates.get(NotificationDeliveryMethod.SLACK)).asInstanceOf(type(SlackDeliveryMethodNotificationTemplate.class)) .satisfies(template -> { assertThat(template.getBody()) - .startsWith("Message for SLACK") - .endsWith(requestorEmail); + .isEqualTo("Message for SLACK: ${email}"); // ${email} should not be processed }); } @@ -507,6 +511,7 @@ public class NotificationApiTest extends AbstractNotificationApiTest { .until(() -> findNotificationRequest(notificationRequest.getId()).isSent()); NotificationRequestStats stats = getStats(notificationRequest.getId()); + System.err.println("STATS: " + stats); assertThat(stats.getSent().get(NotificationDeliveryMethod.PUSH)).hasValue(1); assertThat(stats.getSent().get(NotificationDeliveryMethod.EMAIL)).hasValue(1); assertThat(stats.getErrors().get(NotificationDeliveryMethod.SMS)).size().isOne(); @@ -529,7 +534,10 @@ public class NotificationApiTest extends AbstractNotificationApiTest { NotificationTarget notificationTarget = new NotificationTarget(); notificationTarget.setTenantId(tenantId); notificationTarget.setName("All my users"); - AllUsersNotificationTargetConfig config = new AllUsersNotificationTargetConfig(); + notificationTarget.setType(NotificationTargetType.PLATFORM_USERS); + PlatformUsersNotificationTargetConfig config = new PlatformUsersNotificationTargetConfig(); + AllUsersFilter filter = new AllUsersFilter(); + config.setUsersFilter(filter); notificationTarget.setConfiguration(config); notificationTarget = saveNotificationTarget(notificationTarget); NotificationTargetId notificationTargetId = notificationTarget.getId(); @@ -572,21 +580,28 @@ public class NotificationApiTest extends AbstractNotificationApiTest { notificationTemplate.setName("Slack notification template"); notificationTemplate.setNotificationType(NotificationType.GENERAL); NotificationTemplateConfig config = new NotificationTemplateConfig(); - config.setDefaultTextTemplate("To Slack :)"); - + config.setDefaultTextTemplate("To Slack :) ${email}"); SlackDeliveryMethodNotificationTemplate slackNotificationTemplate = new SlackDeliveryMethodNotificationTemplate(); slackNotificationTemplate.setEnabled(true); - slackNotificationTemplate.setConversationType(SlackConversation.Type.PUBLIC_CHANNEL); - String conversationId = "U154475415"; - slackNotificationTemplate.setConversationId(conversationId); - config.setDeliveryMethodsTemplates(Map.of( NotificationDeliveryMethod.SLACK, slackNotificationTemplate )); notificationTemplate.setConfiguration(config); notificationTemplate = saveNotificationTemplate(notificationTemplate); - NotificationRequest successfulNotificationRequest = submitNotificationRequest(Collections.emptyList(), notificationTemplate.getId(), 0); + String conversationId = "U154475415"; + String conversationName = "#my-channel"; + NotificationTarget notificationTarget = new NotificationTarget(); + notificationTarget.setTenantId(tenantId); + notificationTarget.setName(conversationName + " in Slack"); + notificationTarget.setType(NotificationTargetType.SLACK); + SlackNotificationTargetConfig targetConfig = new SlackNotificationTargetConfig(); + targetConfig.setConversationId(conversationId); + targetConfig.setConversationName(conversationName); + notificationTarget.setConfiguration(targetConfig); + notificationTarget = saveNotificationTarget(notificationTarget); + + NotificationRequest successfulNotificationRequest = submitNotificationRequest(List.of(notificationTarget.getId()), notificationTemplate.getId(), 0); await().atMost(2, TimeUnit.SECONDS) .until(() -> findNotificationRequest(successfulNotificationRequest.getId()).isSent()); verify(slackService).sendMessage(eq(tenantId), eq(slackToken), eq(conversationId), eq(config.getDefaultTextTemplate())); @@ -595,7 +610,7 @@ public class NotificationApiTest extends AbstractNotificationApiTest { String errorMessage = "Error!!!"; doThrow(new RuntimeException(errorMessage)).when(slackService).sendMessage(any(), any(), any(), any()); - NotificationRequest failedNotificationRequest = submitNotificationRequest(Collections.emptyList(), notificationTemplate.getId(), 0); + NotificationRequest failedNotificationRequest = submitNotificationRequest(List.of(notificationTarget.getId()), notificationTemplate.getId(), 0); await().atMost(2, TimeUnit.SECONDS) .until(() -> findNotificationRequest(failedNotificationRequest.getId()).isSent()); stats = getStats(failedNotificationRequest.getId()); diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationTargetApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationTargetApiTest.java index e9869b372b..fdc4fb8a6c 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationTargetApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationTargetApiTest.java @@ -23,10 +23,12 @@ import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.ResultMatcher; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.notification.targets.AllUsersNotificationTargetConfig; -import org.thingsboard.server.common.data.notification.targets.CustomerUsersNotificationTargetConfig; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; -import org.thingsboard.server.common.data.notification.targets.UserListNotificationTargetConfig; +import org.thingsboard.server.common.data.notification.targets.NotificationTargetType; +import org.thingsboard.server.common.data.notification.targets.platform.AllUsersFilter; +import org.thingsboard.server.common.data.notification.targets.platform.CustomerUsersFilter; +import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig; +import org.thingsboard.server.common.data.notification.targets.platform.UserListFilter; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.controller.AbstractControllerTest; import org.thingsboard.server.dao.notification.NotificationTargetDao; @@ -51,21 +53,25 @@ public class NotificationTargetApiTest extends AbstractControllerTest { @Test public void givenInvalidNotificationTarget_whenSaving_returnValidationError() throws Exception { - NotificationTarget notificationTarget = new NotificationTarget(); - notificationTarget.setTenantId(null); - notificationTarget.setName(null); - notificationTarget.setConfiguration(null); + NotificationTarget target = new NotificationTarget(); + target.setTenantId(null); + target.setName(null); + target.setType(null); + target.setConfiguration(null); - String validationError = saveAndGetError(notificationTarget, status().isBadRequest()); + String validationError = saveAndGetError(target, status().isBadRequest()); assertThat(validationError) .contains("name must not be") + .contains("type must not be") .contains("configuration must not be"); - UserListNotificationTargetConfig userListConfig = new UserListNotificationTargetConfig(); - userListConfig.setUsersIds(Collections.emptyList()); - notificationTarget.setConfiguration(userListConfig); + PlatformUsersNotificationTargetConfig targetConfig = new PlatformUsersNotificationTargetConfig(); + UserListFilter userListFilter = new UserListFilter(); + userListFilter.setUsersIds(Collections.emptyList()); + targetConfig.setUsersFilter(userListFilter); + target.setConfiguration(targetConfig); - validationError = saveAndGetError(notificationTarget, status().isBadRequest()); + validationError = saveAndGetError(target, status().isBadRequest()); assertThat(validationError) .contains("usersIds must not be"); } @@ -76,16 +82,18 @@ public class NotificationTargetApiTest extends AbstractControllerTest { NotificationTarget notificationTarget = new NotificationTarget(); notificationTarget.setTenantId(differentTenantId); notificationTarget.setName("Target 1"); + notificationTarget.setType(NotificationTargetType.PLATFORM_USERS); - UserListNotificationTargetConfig userListConfig = new UserListNotificationTargetConfig(); - userListConfig.setUsersIds(List.of(customerUserId.getId(), tenantAdminUserId.getId())); - notificationTarget.setConfiguration(userListConfig); + PlatformUsersNotificationTargetConfig targetConfig = new PlatformUsersNotificationTargetConfig(); + UserListFilter userListFilter = new UserListFilter(); + userListFilter.setUsersIds(List.of(customerUserId.getId(), tenantAdminUserId.getId())); + targetConfig.setUsersFilter(userListFilter); + notificationTarget.setConfiguration(targetConfig); saveAndGetError(notificationTarget, status().isForbidden()); loginSysAdmin(); notificationTarget.setTenantId(TenantId.SYS_TENANT_ID); - notificationTarget.setConfiguration(userListConfig); save(notificationTarget, status().isOk()); } @@ -94,9 +102,13 @@ public class NotificationTargetApiTest extends AbstractControllerTest { NotificationTarget notificationTarget = new NotificationTarget(); notificationTarget.setTenantId(tenantId); notificationTarget.setName("Test target"); - CustomerUsersNotificationTargetConfig customerUsersConfig = new CustomerUsersNotificationTargetConfig(); - customerUsersConfig.setCustomerId(customerId.getId()); - notificationTarget.setConfiguration(customerUsersConfig); + notificationTarget.setType(NotificationTargetType.PLATFORM_USERS); + + PlatformUsersNotificationTargetConfig targetConfig = new PlatformUsersNotificationTargetConfig(); + CustomerUsersFilter customerUsersFilter = new CustomerUsersFilter(); + customerUsersFilter.setCustomerId(customerId.getId()); + targetConfig.setUsersFilter(customerUsersFilter); + notificationTarget.setConfiguration(targetConfig); List recipients = getRecipients(notificationTarget); assertThat(recipients).size().isNotZero(); @@ -104,8 +116,8 @@ public class NotificationTargetApiTest extends AbstractControllerTest { assertThat(recipient.getCustomerId()).isEqualTo(customerId); }); - AllUsersNotificationTargetConfig allUsersConfig = new AllUsersNotificationTargetConfig(); - notificationTarget.setConfiguration(allUsersConfig); + AllUsersFilter allUsersFilter = new AllUsersFilter(); + targetConfig.setUsersFilter(allUsersFilter); recipients = getRecipients(notificationTarget); assertThat(recipients).size().isGreaterThanOrEqualTo(2); assertThat(recipients).allSatisfy(recipient -> { @@ -129,8 +141,11 @@ public class NotificationTargetApiTest extends AbstractControllerTest { createDifferentTenant(); NotificationTarget notificationTarget = new NotificationTarget(); notificationTarget.setName("Test 1"); + notificationTarget.setType(NotificationTargetType.PLATFORM_USERS); notificationTarget.setTenantId(differentTenantId); - notificationTarget.setConfiguration(new AllUsersNotificationTargetConfig()); + PlatformUsersNotificationTargetConfig targetConfig = new PlatformUsersNotificationTargetConfig(); + targetConfig.setUsersFilter(new AllUsersFilter()); + notificationTarget.setConfiguration(targetConfig); save(notificationTarget, status().isOk()); assertThat(notificationTargetDao.find(TenantId.SYS_TENANT_ID)).isNotEmpty(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/User.java b/common/data/src/main/java/org/thingsboard/server/common/data/User.java index 859f982d9c..94fa5eb034 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/User.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/User.java @@ -25,13 +25,14 @@ import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.notification.targets.NotificationRecipient; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; @ApiModel @EqualsAndHashCode(callSuper = true) -public class User extends SearchTextBasedWithAdditionalInfo implements HasName, HasTenantId, HasCustomerId { +public class User extends SearchTextBasedWithAdditionalInfo implements HasName, HasTenantId, HasCustomerId, NotificationRecipient { private static final long serialVersionUID = 8250339805336035966L; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationDeliveryMethod.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationDeliveryMethod.java index abb184ec9f..59a0c05aab 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationDeliveryMethod.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationDeliveryMethod.java @@ -15,24 +15,11 @@ */ package org.thingsboard.server.common.data.notification; -import lombok.Getter; - public enum NotificationDeliveryMethod { PUSH, EMAIL, SMS, - SLACK(true); - - @Getter - private final boolean standalone; // means that notifications for the delivery method are sent independently of specified recipients - - NotificationDeliveryMethod() { - this(false); - } - - NotificationDeliveryMethod(boolean standalone) { - this.standalone = standalone; - } + SLACK } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationProcessingContext.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationProcessingContext.java index 4c41b45b15..5177c32489 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationProcessingContext.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationProcessingContext.java @@ -62,9 +62,10 @@ public class NotificationProcessingContext { this.notificationTemplate = template; this.templates = new EnumMap<>(NotificationDeliveryMethod.class); this.stats = new NotificationRequestStats(); + init(); } - public void init() { + private void init() { NotificationTemplateConfig templateConfig = notificationTemplate.getConfiguration(); templateConfig.getDeliveryMethodsTemplates().forEach((deliveryMethod, template) -> { if (!template.isEnabled()) return; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestStats.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestStats.java index 925c401b89..d29e417ec6 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestStats.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestStats.java @@ -21,10 +21,10 @@ import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.notification.targets.NotificationRecipient; import java.util.Collections; import java.util.Map; -import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; @@ -35,7 +35,7 @@ public class NotificationRequestStats { private final Map sent; private final Map> errors; @JsonIgnore - private final Map> processedRecipients; + private final Map> processedRecipients; public NotificationRequestStats() { this.sent = new ConcurrentHashMap<>(); @@ -51,19 +51,22 @@ public class NotificationRequestStats { this.processedRecipients = Collections.emptyMap(); } - public void reportSent(NotificationDeliveryMethod deliveryMethod, User recipient) { + public void reportSent(NotificationDeliveryMethod deliveryMethod, NotificationRecipient recipient) { sent.computeIfAbsent(deliveryMethod, k -> new AtomicInteger()).incrementAndGet(); - if (recipient != null) { - processedRecipients.computeIfAbsent(deliveryMethod, k -> ConcurrentHashMap.newKeySet()).add(recipient.getId()); - } + processedRecipients.computeIfAbsent(deliveryMethod, k -> ConcurrentHashMap.newKeySet()).add(recipient.getId()); } - public void reportError(NotificationDeliveryMethod deliveryMethod, Throwable error, User recipient) { + public void reportError(NotificationDeliveryMethod deliveryMethod, Throwable error, NotificationRecipient recipient) { if (error instanceof AlreadySentException) { return; } String errorMessage = error.getMessage(); - String key = Optional.ofNullable(recipient).map(User::getEmail).orElse(""); + String key; + if (recipient instanceof User) { + key = ((User) recipient).getEmail(); + } else { + key = ""; + } errors.computeIfAbsent(deliveryMethod, k -> new ConcurrentHashMap<>()).put(key, errorMessage); } @@ -71,8 +74,8 @@ public class NotificationRequestStats { return sent.containsKey(deliveryMethod) || errors.containsKey(deliveryMethod); } - public boolean contains(NotificationDeliveryMethod deliveryMethod, UserId recipientId) { - Set processedRecipients = this.processedRecipients.get(deliveryMethod); + public boolean contains(NotificationDeliveryMethod deliveryMethod, Object recipientId) { + Set processedRecipients = this.processedRecipients.get(deliveryMethod); return processedRecipients != null && processedRecipients.contains(recipientId); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/AllUsersNotificationTargetConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationRecipient.java similarity index 69% rename from common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/AllUsersNotificationTargetConfig.java rename to common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationRecipient.java index c5ab241f18..bfe7faa5ad 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/AllUsersNotificationTargetConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationRecipient.java @@ -15,16 +15,8 @@ */ package org.thingsboard.server.common.data.notification.targets; -import lombok.Data; -import lombok.EqualsAndHashCode; +public interface NotificationRecipient { -@Data -@EqualsAndHashCode(callSuper = true) -public class AllUsersNotificationTargetConfig extends NotificationTargetConfig { - - @Override - public NotificationTargetConfigType getType() { - return NotificationTargetConfigType.ALL_USERS; - } + Object getId(); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTarget.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTarget.java index 4513c0d18b..9011a6e949 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTarget.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTarget.java @@ -37,6 +37,8 @@ public class NotificationTarget extends BaseData implement @NoXss private String name; @NotNull + private NotificationTargetType type; + @NotNull @Valid private NotificationTargetConfig configuration; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfig.java index 225665c776..1c61e609b1 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfig.java @@ -21,13 +21,14 @@ import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonSubTypes.Type; import com.fasterxml.jackson.annotation.JsonTypeInfo; import lombok.Data; +import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig; +import org.thingsboard.server.common.data.notification.targets.slack.SlackNotificationTargetConfig; @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") @JsonSubTypes({ - @Type(value = UserListNotificationTargetConfig.class, name = "USER_LIST"), - @Type(value = CustomerUsersNotificationTargetConfig.class, name = "CUSTOMER_USERS"), - @Type(value = AllUsersNotificationTargetConfig.class, name = "ALL_USERS") + @Type(value = PlatformUsersNotificationTargetConfig.class, name = "PLATFORM_USERS"), + @Type(value = SlackNotificationTargetConfig.class, name = "SLACK") }) @Data public abstract class NotificationTargetConfig { @@ -35,6 +36,6 @@ public abstract class NotificationTargetConfig { private String description; @JsonIgnore - public abstract NotificationTargetConfigType getType(); + public abstract NotificationTargetType getType(); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetType.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetType.java new file mode 100644 index 0000000000..3310a4ab3a --- /dev/null +++ b/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 supportedDeliveryMethods; + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/SlackConversation.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/AllUsersFilter.java similarity index 73% rename from common/data/src/main/java/org/thingsboard/server/common/data/notification/template/SlackConversation.java rename to common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/AllUsersFilter.java index b9d242bd2c..9bafa44902 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/SlackConversation.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/AllUsersFilter.java @@ -13,20 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.notification.template; +package org.thingsboard.server.common.data.notification.targets.platform; import lombok.Data; @Data -public class SlackConversation { +public class AllUsersFilter implements UsersFilter { - private String id; - private String name; - - public enum Type { - DIRECT, - PUBLIC_CHANNEL, - PRIVATE_CHANNEL + @Override + public UsersFilterType getType() { + return UsersFilterType.ALL_USERS; } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/CustomerUsersNotificationTargetConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/CustomerUsersFilter.java similarity index 79% rename from common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/CustomerUsersNotificationTargetConfig.java rename to common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/CustomerUsersFilter.java index 123cee9e80..18b0d5bc47 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/CustomerUsersNotificationTargetConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/CustomerUsersFilter.java @@ -13,26 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.notification.targets; +package org.thingsboard.server.common.data.notification.targets.platform; import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Data; -import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.id.EntityId; import javax.validation.constraints.AssertTrue; import java.util.UUID; @Data -@EqualsAndHashCode(callSuper = true) -public class CustomerUsersNotificationTargetConfig extends NotificationTargetConfig { +public class CustomerUsersFilter implements UsersFilter { private UUID customerId; // might not be set if using with notification rule private boolean getCustomerIdFromOriginatorEntity; // e.g. from alarm @Override - public NotificationTargetConfigType getType() { - return NotificationTargetConfigType.CUSTOMER_USERS; + public UsersFilterType getType() { + return UsersFilterType.CUSTOMER_USERS; } @AssertTrue(message = "customerId is required") diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/PlatformUsersNotificationTargetConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/PlatformUsersNotificationTargetConfig.java new file mode 100644 index 0000000000..f8734e57a1 --- /dev/null +++ b/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; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/UserListNotificationTargetConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UserListFilter.java similarity index 71% rename from common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/UserListNotificationTargetConfig.java rename to common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UserListFilter.java index c851242c10..30d03ec135 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/UserListNotificationTargetConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UserListFilter.java @@ -13,25 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.notification.targets; +package org.thingsboard.server.common.data.notification.targets.platform; import lombok.Data; -import lombok.EqualsAndHashCode; import javax.validation.constraints.NotEmpty; import java.util.List; import java.util.UUID; @Data -@EqualsAndHashCode(callSuper = true) -public class UserListNotificationTargetConfig extends NotificationTargetConfig { +public class UserListFilter implements UsersFilter { @NotEmpty private List usersIds; @Override - public NotificationTargetConfigType getType() { - return NotificationTargetConfigType.USER_LIST; + public UsersFilterType getType() { + return UsersFilterType.USER_LIST; } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UsersFilter.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UsersFilter.java new file mode 100644 index 0000000000..7a47c73081 --- /dev/null +++ b/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(); + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfigType.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UsersFilterType.java similarity index 87% rename from common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfigType.java rename to common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UsersFilterType.java index 228b9e2e25..88a826a9a4 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfigType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UsersFilterType.java @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.notification.targets; +package org.thingsboard.server.common.data.notification.targets.platform; -public enum NotificationTargetConfigType { +public enum UsersFilterType { USER_LIST, CUSTOMER_USERS, ALL_USERS diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/slack/SlackConversation.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/slack/SlackConversation.java new file mode 100644 index 0000000000..2126a6b60a --- /dev/null +++ b/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; +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/slack/SlackConversationType.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/slack/SlackConversationType.java new file mode 100644 index 0000000000..2d075d857e --- /dev/null +++ b/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 +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/slack/SlackNotificationTargetConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/slack/SlackNotificationTargetConfig.java new file mode 100644 index 0000000000..ecae9a73f9 --- /dev/null +++ b/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; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.java index 74054db839..6622ebcd52 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.java @@ -44,6 +44,7 @@ public abstract class DeliveryMethodNotificationTemplate { this.body = other.body; } + @JsonIgnore public abstract NotificationDeliveryMethod getMethod(); @JsonIgnore diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/SlackDeliveryMethodNotificationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/SlackDeliveryMethodNotificationTemplate.java index ad025c1773..0ce3207d3e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/SlackDeliveryMethodNotificationTemplate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/SlackDeliveryMethodNotificationTemplate.java @@ -21,22 +21,14 @@ import lombok.NoArgsConstructor; import lombok.ToString; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; -import javax.validation.constraints.NotEmpty; - @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) public class SlackDeliveryMethodNotificationTemplate extends DeliveryMethodNotificationTemplate { - private SlackConversation.Type conversationType; - @NotEmpty - private String conversationId; - - public SlackDeliveryMethodNotificationTemplate(SlackDeliveryMethodNotificationTemplate other) { + public SlackDeliveryMethodNotificationTemplate(DeliveryMethodNotificationTemplate other) { super(other); - this.conversationType = other.conversationType; - this.conversationId = other.conversationId; } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index 72b564bfec..bb01f53727 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -650,6 +650,7 @@ public class ModelConstants { * */ public static final String NOTIFICATION_TARGET_TABLE_NAME = "notification_target"; + public static final String NOTIFICATION_TARGET_TYPE_PROPERTY = "type"; public static final String NOTIFICATION_TARGET_CONFIGURATION_PROPERTY = "configuration"; public static final String NOTIFICATION_TABLE_NAME = "notification"; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationTargetEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationTargetEntity.java index 22fd3ac657..436c62d57a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationTargetEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationTargetEntity.java @@ -21,15 +21,17 @@ import lombok.EqualsAndHashCode; import org.hibernate.annotations.Type; import org.hibernate.annotations.TypeDef; import org.thingsboard.server.common.data.id.NotificationTargetId; -import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; import org.thingsboard.server.common.data.notification.targets.NotificationTargetConfig; +import org.thingsboard.server.common.data.notification.targets.NotificationTargetType; import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.util.mapping.JsonStringType; import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; import javax.persistence.Table; import java.util.UUID; @@ -46,6 +48,10 @@ public class NotificationTargetEntity extends BaseSqlEntity @Column(name = ModelConstants.NAME_PROPERTY, nullable = false) private String name; + @Enumerated(EnumType.STRING) + @Column(name = ModelConstants.NOTIFICATION_TARGET_TYPE_PROPERTY, nullable = false) + private NotificationTargetType type; + @Type(type = "json") @Column(name = ModelConstants.NOTIFICATION_TARGET_CONFIGURATION_PROPERTY, nullable = false) private JsonNode configuration; @@ -57,6 +63,7 @@ public class NotificationTargetEntity extends BaseSqlEntity setCreatedTime(notificationTarget.getCreatedTime()); setTenantId(getTenantUuid(notificationTarget.getTenantId())); setName(notificationTarget.getName()); + setType(notificationTarget.getType()); setConfiguration(toJson(notificationTarget.getConfiguration())); } @@ -67,6 +74,7 @@ public class NotificationTargetEntity extends BaseSqlEntity notificationTarget.setCreatedTime(createdTime); notificationTarget.setTenantId(getTenantId(tenantId)); notificationTarget.setName(name); + notificationTarget.setType(type); notificationTarget.setConfiguration(fromJson(configuration, NotificationTargetConfig.class)); return notificationTarget; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java index dda4145a31..1aab941d4d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java @@ -24,10 +24,12 @@ import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.NotificationRequestStatus; -import org.thingsboard.server.common.data.notification.targets.CustomerUsersNotificationTargetConfig; +import org.thingsboard.server.common.data.notification.targets.platform.CustomerUsersFilter; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; import org.thingsboard.server.common.data.notification.targets.NotificationTargetConfig; -import org.thingsboard.server.common.data.notification.targets.UserListNotificationTargetConfig; +import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig; +import org.thingsboard.server.common.data.notification.targets.platform.UserListFilter; +import org.thingsboard.server.common.data.notification.targets.platform.UsersFilter; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.entity.AbstractEntityService; @@ -85,9 +87,13 @@ public class DefaultNotificationTargetService extends AbstractEntityService impl @Override public PageData findRecipientsForNotificationTargetConfig(TenantId tenantId, CustomerId customerId, NotificationTargetConfig targetConfig, PageLink pageLink) { - switch (targetConfig.getType()) { + if (!(targetConfig instanceof PlatformUsersNotificationTargetConfig)) { + throw new IllegalArgumentException("Unsupported target type " + targetConfig.getType()); + } + UsersFilter usersFilter = ((PlatformUsersNotificationTargetConfig) targetConfig).getUsersFilter(); + switch (usersFilter.getType()) { case USER_LIST: { - List users = ((UserListNotificationTargetConfig) targetConfig).getUsersIds().stream() + List users = ((UserListFilter) usersFilter).getUsersIds().stream() .map(UserId::new).map(userId -> userService.findUserById(tenantId, userId)) .collect(Collectors.toList()); return new PageData<>(users, 1, users.size(), false); @@ -96,7 +102,7 @@ public class DefaultNotificationTargetService extends AbstractEntityService impl if (tenantId.equals(TenantId.SYS_TENANT_ID)) { throw new IllegalArgumentException("Customer users target is not supported for system administrator"); } - CustomerUsersNotificationTargetConfig customerUsersConfig = (CustomerUsersNotificationTargetConfig) targetConfig; + CustomerUsersFilter customerUsersConfig = (CustomerUsersFilter) usersFilter; if (!customerUsersConfig.isGetCustomerIdFromOriginatorEntity()) { customerId = new CustomerId(customerUsersConfig.getCustomerId()); } diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 1a6ab00d2b..e541047fd6 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -784,6 +784,7 @@ CREATE TABLE IF NOT EXISTS notification_target ( created_time BIGINT NOT NULL, tenant_id UUID NULL CONSTRAINT fk_notification_target_tenant_id REFERENCES tenant(id) ON DELETE CASCADE, name VARCHAR(255) NOT NULL, + type VARCHAR(32) NOT NULL, configuration VARCHAR(10000) NOT NULL, CONSTRAINT uq_notification_target_name UNIQUE (tenant_id, name) ); diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/slack/SlackService.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/slack/SlackService.java index a698a15828..c9affd4ca3 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/slack/SlackService.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/slack/SlackService.java @@ -16,7 +16,8 @@ package org.thingsboard.rule.engine.api.slack; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.notification.template.SlackConversation; +import org.thingsboard.server.common.data.notification.targets.slack.SlackConversation; +import org.thingsboard.server.common.data.notification.targets.slack.SlackConversationType; import java.util.List; @@ -24,9 +25,9 @@ public interface SlackService { void sendMessage(TenantId tenantId, String token, String conversationId, String message); - List listConversations(TenantId tenantId, String token, SlackConversation.Type conversationType); + List 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); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbSlackNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbSlackNode.java index 22edc57282..9d12f78e92 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbSlackNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbSlackNode.java @@ -23,7 +23,7 @@ import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; -import org.thingsboard.server.common.data.notification.template.SlackConversation; +import org.thingsboard.server.common.data.notification.targets.slack.SlackConversation; import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.msg.TbMsg; diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbSlackNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbSlackNodeConfiguration.java index aa10145b6d..2a9d188f84 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbSlackNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbSlackNodeConfiguration.java @@ -17,7 +17,7 @@ package org.thingsboard.rule.engine.notification; import lombok.Data; import org.thingsboard.rule.engine.api.NodeConfiguration; -import org.thingsboard.server.common.data.notification.template.SlackConversation; +import org.thingsboard.server.common.data.notification.targets.slack.SlackConversationType; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; @@ -31,7 +31,7 @@ public class TbSlackNodeConfiguration implements NodeConfiguration