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/NotificationRuleController.java b/application/src/main/java/org/thingsboard/server/controller/NotificationRuleController.java index cca5fd433e..dc69ede817 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationRuleController.java +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationRuleController.java @@ -31,6 +31,7 @@ import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.notification.rule.NotificationRule; +import org.thingsboard.server.common.data.notification.rule.NotificationRuleInfo; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; @@ -62,21 +63,21 @@ public class NotificationRuleController extends BaseController { @GetMapping("/rule/{id}") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") - public NotificationRule getNotificationRuleById(@PathVariable UUID id) throws ThingsboardException { + public NotificationRuleInfo getNotificationRuleById(@PathVariable UUID id) throws ThingsboardException { NotificationRuleId notificationRuleId = new NotificationRuleId(id); - return checkEntityId(notificationRuleId, notificationRuleService::findNotificationRuleById, Operation.READ); + return checkEntityId(notificationRuleId, notificationRuleService::findNotificationRuleInfoById, Operation.READ); } @GetMapping("/rules") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") - public PageData getNotificationRules(@RequestParam int pageSize, + public PageData getNotificationRules(@RequestParam int pageSize, @RequestParam int page, @RequestParam(required = false) String textSearch, @RequestParam(required = false) String sortProperty, @RequestParam(required = false) String sortOrder, @AuthenticationPrincipal SecurityUser user) throws ThingsboardException { PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - return notificationRuleService.findNotificationRulesByTenantId(user.getTenantId(), pageLink); + return notificationRuleService.findNotificationRulesInfosByTenantId(user.getTenantId(), pageLink); } @DeleteMapping("/rule/{id}") 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..e2ae5031d6 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; @@ -44,7 +45,10 @@ import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; import javax.validation.Valid; +import java.util.Arrays; +import java.util.List; import java.util.UUID; +import java.util.stream.Collectors; import static org.thingsboard.server.controller.ControllerConstants.SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH; @@ -59,15 +63,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 +81,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); + } } } @@ -117,6 +123,14 @@ public class NotificationTargetController extends BaseController { return recipients; } + @GetMapping(value = "/targets", params = {"ids"}) + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + public List getNotificationTargetsByIds(@RequestParam("ids") UUID[] ids, + @AuthenticationPrincipal SecurityUser user) { + List targetsIds = Arrays.stream(ids).map(NotificationTargetId::new).collect(Collectors.toList()); + return notificationTargetService.findNotificationTargetsByTenantIdAndIds(user.getTenantId(), targetsIds); + } + @ApiOperation(value = "Get notification targets (getNotificationTargets)", notes = "Fetch the page of notification targets owned by sysadmin or tenant." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) 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/NotificationRuleApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java index 1a5fdba0be..da9acc9a1e 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.service.notification; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.BooleanNode; import org.junit.Before; @@ -40,20 +41,25 @@ import org.thingsboard.server.common.data.device.profile.SimpleAlarmConditionSpe import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.notification.Notification; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; +import org.thingsboard.server.common.data.notification.NotificationRequestInfo; import org.thingsboard.server.common.data.notification.NotificationType; import org.thingsboard.server.common.data.notification.info.AlarmNotificationInfo; import org.thingsboard.server.common.data.notification.rule.DefaultNotificationRuleRecipientsConfig; import org.thingsboard.server.common.data.notification.rule.EscalatedNotificationRuleRecipientsConfig; import org.thingsboard.server.common.data.notification.rule.NotificationRule; +import org.thingsboard.server.common.data.notification.rule.NotificationRuleInfo; import org.thingsboard.server.common.data.notification.rule.trigger.AlarmNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.EntityActionNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; import org.thingsboard.server.common.data.notification.template.NotificationTemplate; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.query.BooleanFilterPredicate; import org.thingsboard.server.common.data.query.EntityKeyValueType; import org.thingsboard.server.common.data.query.FilterPredicateValue; import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.dao.alarm.AlarmService; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.service.telemetry.AlarmSubscriptionService; @@ -79,6 +85,9 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { @SpyBean private AlarmSubscriptionService alarmSubscriptionService; + @SpyBean + private AlarmService alarmService; + @Before public void beforeEach() throws Exception { loginTenantAdmin(); @@ -183,7 +192,8 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { .set("bool", BooleanNode.TRUE); doPost("/api/plugins/telemetry/" + device.getId() + "/" + DataConstants.SHARED_SCOPE, attr); - verify(alarmSubscriptionService, timeout(2000)).createOrUpdateAlarm(argThat(alarm -> alarm.getType().equals(alarmType))); + await().atMost(2, TimeUnit.SECONDS) + .until(() -> alarmSubscriptionService.findLatestByOriginatorAndType(tenantId, device.getId(), alarmType).get() != null); Alarm alarm = alarmSubscriptionService.findLatestByOriginatorAndType(tenantId, device.getId(), alarmType).get(); long ts = System.currentTimeMillis(); @@ -278,6 +288,31 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { */ } + @Test + public void testNotificationRuleInfo() throws Exception { + NotificationDeliveryMethod[] deliveryMethods = {NotificationDeliveryMethod.PUSH, NotificationDeliveryMethod.EMAIL}; + NotificationTemplate template = createNotificationTemplate(NotificationType.ENTITY_ACTION, "Subject", "Text", deliveryMethods); + + NotificationRule rule = new NotificationRule(); + rule.setName("Test"); + rule.setTemplateId(template.getId()); + + rule.setTriggerType(NotificationRuleTriggerType.ENTITY_ACTION); + EntityActionNotificationRuleTriggerConfig triggerConfig = new EntityActionNotificationRuleTriggerConfig(); + rule.setTriggerConfig(triggerConfig); + + DefaultNotificationRuleRecipientsConfig recipientsConfig = new DefaultNotificationRuleRecipientsConfig(); + recipientsConfig.setTriggerType(NotificationRuleTriggerType.ENTITY_ACTION); + recipientsConfig.setTargets(List.of(createNotificationTarget(tenantAdminUserId).getUuidId())); + rule.setRecipientsConfig(recipientsConfig); + rule = saveNotificationRule(rule); + + NotificationRuleInfo ruleInfo = findNotificationRules().getData().get(0); + assertThat(ruleInfo.getId()).isEqualTo(ruleInfo.getId()); + assertThat(ruleInfo.getTemplateName()).isEqualTo(template.getName()); + assertThat(ruleInfo.getDeliveryMethods()).containsOnly(deliveryMethods); + } + private DeviceProfile createDeviceProfileWithAlarmRules(NotificationRuleId notificationRuleId, String alarmType) { DeviceProfile deviceProfile = createDeviceProfile("For notification rule test"); deviceProfile.setTenantId(tenantId); @@ -318,4 +353,8 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { return doPost("/api/notification/rule", notificationRule, NotificationRule.class); } + private PageData findNotificationRules() throws Exception { + PageLink pageLink = new PageLink(10); + return doGetTypedWithPageLink("/api/notification/rules?", new TypeReference>() {}, pageLink); + } } 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/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleService.java index b8f60f704a..39fec2595b 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleService.java @@ -18,6 +18,7 @@ package org.thingsboard.server.dao.notification; import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.rule.NotificationRule; +import org.thingsboard.server.common.data.notification.rule.NotificationRuleInfo; import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -30,6 +31,10 @@ public interface NotificationRuleService { NotificationRule findNotificationRuleById(TenantId tenantId, NotificationRuleId id); + NotificationRuleInfo findNotificationRuleInfoById(TenantId tenantId, NotificationRuleId id); + + PageData findNotificationRulesInfosByTenantId(TenantId tenantId, PageLink pageLink); + PageData findNotificationRulesByTenantId(TenantId tenantId, PageLink pageLink); List findNotificationRulesByTenantIdAndTriggerType(TenantId tenantId, NotificationRuleTriggerType triggerType); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java index 89199333b4..0791f46884 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java @@ -24,6 +24,8 @@ import org.thingsboard.server.common.data.notification.targets.NotificationTarge import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import java.util.List; + public interface NotificationTargetService { NotificationTarget saveNotificationTarget(TenantId tenantId, NotificationTarget notificationTarget); @@ -32,6 +34,8 @@ public interface NotificationTargetService { PageData findNotificationTargetsByTenantId(TenantId tenantId, PageLink pageLink); + List findNotificationTargetsByTenantIdAndIds(TenantId tenantId, List ids); + PageData findRecipientsForNotificationTarget(TenantId tenantId, CustomerId customerId, NotificationTargetId targetId, PageLink pageLink); int countRecipientsForNotificationTargetConfig(TenantId tenantId, NotificationTargetConfig targetConfig); 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/rule/NotificationRule.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRule.java index 1a570c2b20..e6e096454b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRule.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRule.java @@ -18,6 +18,7 @@ package org.thingsboard.server.common.data.notification.rule; import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasTenantId; @@ -33,6 +34,7 @@ import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; @Data +@NoArgsConstructor @EqualsAndHashCode(callSuper = true) public class NotificationRule extends BaseData implements HasTenantId, HasName { @@ -50,6 +52,16 @@ public class NotificationRule extends BaseData implements Ha @Valid private NotificationRuleRecipientsConfig recipientsConfig; // todo: add pg_tgrm index (but index is 2.5x size of the column) + public NotificationRule(NotificationRule other) { + super(other); + this.tenantId = other.tenantId; + this.name = other.name; + this.templateId = other.templateId; + this.triggerType = other.triggerType; + this.triggerConfig = other.triggerConfig; + this.recipientsConfig = other.recipientsConfig; + } + @JsonIgnore @AssertTrue(message = "trigger type not matching") public boolean isValid() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRuleInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRuleInfo.java new file mode 100644 index 0000000000..12ae232d7d --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRuleInfo.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.rule; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; + +import java.util.List; + +@Data +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class NotificationRuleInfo extends NotificationRule { + + private String templateName; + private List deliveryMethods; + + public NotificationRuleInfo(NotificationRule rule, String templateName, List deliveryMethods) { + super(rule); + this.templateName = templateName; + this.deliveryMethods = deliveryMethods; + } + +} 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/NotificationRuleEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRuleEntity.java index bca0627f76..a012fcf49d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRuleEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRuleEntity.java @@ -77,6 +77,17 @@ public class NotificationRuleEntity extends BaseSqlEntity { setRecipientsConfig(toJson(notificationRule.getRecipientsConfig())); } + public NotificationRuleEntity(NotificationRuleEntity other) { + this.id = other.id; + this.createdTime = other.createdTime; + this.tenantId = other.tenantId; + this.name = other.name; + this.templateId = other.templateId; + this.triggerType = other.triggerType; + this.triggerConfig = other.triggerConfig; + this.recipientsConfig = other.recipientsConfig; + } + @Override public NotificationRule toData() { NotificationRule notificationRule = new NotificationRule(); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRuleInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRuleInfoEntity.java new file mode 100644 index 0000000000..2ed1e20f44 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRuleInfoEntity.java @@ -0,0 +1,53 @@ +/** + * 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.dao.model.sql; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; +import org.thingsboard.server.common.data.notification.rule.NotificationRule; +import org.thingsboard.server.common.data.notification.rule.NotificationRuleInfo; +import org.thingsboard.server.common.data.notification.template.NotificationTemplateConfig; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Data +@EqualsAndHashCode(callSuper = true) +public class NotificationRuleInfoEntity extends NotificationRuleEntity { + + private String templateName; + private JsonNode templateConfig; + + public NotificationRuleInfoEntity(NotificationRuleEntity ruleEntity, String templateName, Object templateConfig) { + super(ruleEntity); + this.templateName = templateName; + this.templateConfig = (JsonNode) templateConfig; + } + + @Override + public NotificationRuleInfo toData() { + NotificationRule rule = super.toData(); + List deliveryMethods = fromJson(templateConfig, NotificationTemplateConfig.class) + .getDeliveryMethodsTemplates().entrySet().stream() + .filter(entry -> entry.getValue().isEnabled()) + .map(Map.Entry::getKey).collect(Collectors.toList()); + return new NotificationRuleInfo(rule, templateName, deliveryMethods); + } + +} 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/DefaultNotificationRuleService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRuleService.java index 5a6d1e58b6..76c563f7e8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRuleService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRuleService.java @@ -20,6 +20,7 @@ import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.rule.NotificationRule; +import org.thingsboard.server.common.data.notification.rule.NotificationRuleInfo; import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -63,6 +64,16 @@ public class DefaultNotificationRuleService extends AbstractCachedEntityService< return notificationRuleDao.findById(tenantId, id.getId()); } + @Override + public NotificationRuleInfo findNotificationRuleInfoById(TenantId tenantId, NotificationRuleId id) { + return notificationRuleDao.findInfoById(tenantId, id); + } + + @Override + public PageData findNotificationRulesInfosByTenantId(TenantId tenantId, PageLink pageLink) { + return notificationRuleDao.findInfosByTenantIdAndPageLink(tenantId, pageLink); + } + @Override public PageData findNotificationRulesByTenantId(TenantId tenantId, PageLink pageLink) { return notificationRuleDao.findByTenantIdAndPageLink(tenantId, pageLink); 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..10395ba18e 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; @@ -70,6 +72,11 @@ public class DefaultNotificationTargetService extends AbstractEntityService impl return notificationTargetDao.findByTenantIdAndPageLink(tenantId, pageLink); } + @Override + public List findNotificationTargetsByTenantIdAndIds(TenantId tenantId, List ids) { + return notificationTargetDao.findByTenantIdAndIds(tenantId, ids); + } + @Override public PageData findRecipientsForNotificationTarget(TenantId tenantId, CustomerId customerId, NotificationTargetId targetId, PageLink pageLink) { NotificationTarget notificationTarget = findNotificationTargetById(tenantId, targetId); @@ -85,9 +92,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 +107,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/java/org/thingsboard/server/dao/notification/NotificationRuleDao.java b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleDao.java index baf34b308c..0bc1d61010 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleDao.java @@ -15,9 +15,11 @@ */ package org.thingsboard.server.dao.notification; +import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.rule.NotificationRule; +import org.thingsboard.server.common.data.notification.rule.NotificationRuleInfo; import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -29,8 +31,12 @@ public interface NotificationRuleDao extends Dao { PageData findByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink); + PageData findInfosByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink); + boolean existsByTargetId(TenantId tenantId, NotificationTargetId targetId); List findByTenantIdAndTriggerType(TenantId tenantId, NotificationRuleTriggerType triggerType); + NotificationRuleInfo findInfoById(TenantId tenantId, NotificationRuleId id); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetDao.java b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetDao.java index 0bcae5c456..89b49bea61 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetDao.java @@ -15,14 +15,19 @@ */ package org.thingsboard.server.dao.notification; +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.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; +import java.util.List; + public interface NotificationTargetDao extends Dao { PageData findByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink); + List findByTenantIdAndIds(TenantId tenantId, List ids); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java index 8341002623..4176db9c8d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java @@ -20,14 +20,17 @@ import lombok.RequiredArgsConstructor; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.rule.NotificationRule; +import org.thingsboard.server.common.data.notification.rule.NotificationRuleInfo; import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.model.sql.NotificationRuleEntity; +import org.thingsboard.server.dao.model.sql.NotificationRuleInfoEntity; import org.thingsboard.server.dao.notification.NotificationRuleDao; import org.thingsboard.server.dao.sql.JpaAbstractDao; import org.thingsboard.server.dao.util.SqlDao; @@ -50,6 +53,12 @@ public class JpaNotificationRuleDao extends JpaAbstractDao findInfosByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink) { + return DaoUtil.pageToPageData(notificationRuleRepository.findInfosByTenantIdAndSearchText(getId(tenantId, true), + Strings.nullToEmpty(pageLink.getTextSearch()), DaoUtil.toPageable(pageLink))).mapData(NotificationRuleInfoEntity::toData); + } + @Override public boolean existsByTargetId(TenantId tenantId, NotificationTargetId targetId) { return notificationRuleRepository.existsByRecipientsConfigContaining(targetId.getId().toString()); @@ -60,6 +69,12 @@ public class JpaNotificationRuleDao extends JpaAbstractDao getEntityClass() { return NotificationRuleEntity.class; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java index 789574c3d0..b8577fdfc5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java @@ -20,7 +20,9 @@ import lombok.RequiredArgsConstructor; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -30,7 +32,9 @@ import org.thingsboard.server.dao.notification.NotificationTargetDao; import org.thingsboard.server.dao.sql.JpaAbstractDao; import org.thingsboard.server.dao.util.SqlDao; +import java.util.List; import java.util.UUID; +import java.util.stream.Collectors; import static org.thingsboard.server.dao.DaoUtil.getId; @@ -47,6 +51,11 @@ public class JpaNotificationTargetDao extends JpaAbstractDao findByTenantIdAndIds(TenantId tenantId, List ids) { + return DaoUtil.convertDataList(notificationTargetRepository.findByTenantIdAndIdIn(tenantId.getId(), DaoUtil.toUUIDs(ids))); + } + @Override protected Class getEntityClass() { return NotificationTargetEntity.class; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRuleRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRuleRepository.java index 27c7753ede..275924600a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRuleRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRuleRepository.java @@ -21,8 +21,10 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import org.thingsboard.server.common.data.notification.rule.NotificationRuleInfo; import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; import org.thingsboard.server.dao.model.sql.NotificationRuleEntity; +import org.thingsboard.server.dao.model.sql.NotificationRuleInfoEntity; import java.util.List; import java.util.UUID; @@ -30,6 +32,9 @@ import java.util.UUID; @Repository public interface NotificationRuleRepository extends JpaRepository { + String RULE_INFO_QUERY = "SELECT new org.thingsboard.server.dao.model.sql.NotificationRuleInfoEntity(r, t.name, t.configuration) " + + "FROM NotificationRuleEntity r INNER JOIN NotificationTemplateEntity t ON r.templateId = t.id"; + @Query("SELECT r FROM NotificationRuleEntity r WHERE r.tenantId = :tenantId " + "AND lower(r.name) LIKE lower(concat('%', :searchText, '%')) ") Page findByTenantIdAndSearchText(@Param("tenantId") UUID tenantId, @@ -40,4 +45,12 @@ public interface NotificationRuleRepository extends JpaRepository findAllByTenantIdAndTriggerType(UUID tenantId, NotificationRuleTriggerType triggerType); + @Query(RULE_INFO_QUERY + " WHERE r.id = :id") + NotificationRuleInfoEntity findInfoById(@Param("id") UUID id); + + @Query(RULE_INFO_QUERY + " WHERE r.tenantId = :tenantId AND lower(r.name) LIKE lower(concat('%', :searchText, '%'))") + Page findInfosByTenantIdAndSearchText(@Param("tenantId") UUID tenantId, + @Param("searchText") String searchText, + Pageable pageable); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTargetRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTargetRepository.java index a558ad5163..3156d355fa 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTargetRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTargetRepository.java @@ -23,6 +23,7 @@ import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import org.thingsboard.server.dao.model.sql.NotificationTargetEntity; +import java.util.List; import java.util.UUID; @Repository @@ -30,4 +31,6 @@ public interface NotificationTargetRepository extends JpaRepository findByTenantIdAndNameContainingIgnoreCase(UUID tenantId, String searchText, Pageable pageable); + List findByTenantIdAndIdIn(UUID tenantId, List ids); + } 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 this.widgetService.getWidgetsBundle(id, config), entityIds); break; + case EntityType.NOTIFICATION_TARGET: + observable = of([]); + break; } return observable; } diff --git a/ui-ngx/src/app/core/ws/notification-websocket.service.ts b/ui-ngx/src/app/core/ws/notification-websocket.service.ts index 5cced2a371..020bdf4615 100644 --- a/ui-ngx/src/app/core/ws/notification-websocket.service.ts +++ b/ui-ngx/src/app/core/ws/notification-websocket.service.ts @@ -161,6 +161,7 @@ export class NotificationWebsocketService implements NotificationWsService { private publishCommands() { while (this.isOpened && this.cmdsWrapper.hasCommands()) { this.dataStream.next(this.cmdsWrapper.preparePublishCommands()); + this.cmdsWrapper.clear(); this.checkToClose(); } this.tryOpenSocket(); diff --git a/ui-ngx/src/app/modules/home/pages/notification-center/notification-center.component.html b/ui-ngx/src/app/modules/home/pages/notification-center/notification-center.component.html index 056bb24737..eaa9a183a0 100644 --- a/ui-ngx/src/app/modules/home/pages/notification-center/notification-center.component.html +++ b/ui-ngx/src/app/modules/home/pages/notification-center/notification-center.component.html @@ -16,92 +16,47 @@ -->
- -
-
- - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
-
+
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
- -
-
- -
Notification settings
-
-
-
- Current user allow delivery method -
-
-
- - {{ notificationDeliveryMethodTranslateMap.get(deliveryMethods) | translate }} - -
-
- - Slack bot token - - - {{ 'notification.link-required' | translate }} - - -
-
-
-
- -
-
-
- +
diff --git a/ui-ngx/src/app/modules/home/pages/notification-center/notification-center.component.scss b/ui-ngx/src/app/modules/home/pages/notification-center/notification-center.component.scss index dae717255b..e3726e4c02 100644 --- a/ui-ngx/src/app/modules/home/pages/notification-center/notification-center.component.scss +++ b/ui-ngx/src/app/modules/home/pages/notification-center/notification-center.component.scss @@ -20,14 +20,11 @@ height: 100%; display: block; .tb-notification-center { + padding: 8px; width: 100%; height: 100%; display: block; - .tb-notification-draw-container { - margin: 8px; - } - .tb-notification-center-content { width: 100%; height: 100%; @@ -58,34 +55,52 @@ flex-direction: column; flex: 1 1 100%; } - } - - .notification-settings { - width: 100%; - padding: 24px; - - .title { - margin: 0; - line-height: 1.4rem; - } - .fields-group { - padding: 8px 16px 0; - margin: 10px 0; - border: 1px groove rgba(0, 0, 0, .25); - border-radius: 4px; - - legend { - color: rgba(0, 0, 0, .7); - } - - .delivery-method { - margin-bottom: 8px; - } - } + //.mat-toolbar-tools{ + // min-height: auto; + //} + // + //.title-container{ + // overflow: hidden; + //} + // + //.tb-entity-table-title { + // padding-right: 20px; + // white-space: nowrap; + // overflow: hidden; + // text-overflow: ellipsis; + //} + // + //.table-container { + // overflow: auto; + //} + // + //.tb-entity-table-info{ + // white-space: nowrap; + // overflow: hidden; + // text-overflow: ellipsis; + //} + // + //.button-widget-action{ + // margin-left: auto; + // overflow: hidden; + // text-overflow: ellipsis; + //} } } + //@media #{$mat-xs} { + // .mat-toolbar { + // height: auto; + // min-height: 100px; + // + // .tb-entity-table-title{ + // padding-bottom: 5px; + // width: 100%; + // } + // } + //} + @media #{$mat-xs} { .tb-notification-center { .tb-notification-center-content { diff --git a/ui-ngx/src/app/modules/home/pages/notification-center/notification-center.component.ts b/ui-ngx/src/app/modules/home/pages/notification-center/notification-center.component.ts index 6eceb003c6..64635cb470 100644 --- a/ui-ngx/src/app/modules/home/pages/notification-center/notification-center.component.ts +++ b/ui-ngx/src/app/modules/home/pages/notification-center/notification-center.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { AfterViewInit, Component, QueryList, ViewChild, ViewChildren } from '@angular/core'; +import { Component, QueryList, ViewChild, ViewChildren } from '@angular/core'; import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -23,30 +23,16 @@ import { NotificationTableComponent } from '@home/pages/notification-center/notification-table/notification-table.component'; import { EntityType } from '@shared/models/entity-type.models'; -import { MatDrawer } from '@angular/material/sidenav'; -import { NotificationService } from '@core/http/notification.service'; -import { - NotificationDeliveryMethod, - NotificationDeliveryMethodTranslateMap, - NotificationTemplateTypeTranslateMap -} from '@shared/models/notification.models'; -import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { deepTrim } from '@core/utils'; @Component({ selector: 'tb-notification-center', templateUrl: './notification-center.component.html', styleUrls: ['notification-center.component.scss'] }) -export class NotificationCenterComponent extends PageComponent implements AfterViewInit { +export class NotificationCenterComponent extends PageComponent { entityType = EntityType; - notificationSettingsForm: FormGroup; - notificationDeliveryMethods = Object.keys(NotificationDeliveryMethod) as NotificationDeliveryMethod[]; - notificationDeliveryMethodTranslateMap = NotificationDeliveryMethodTranslateMap; - - @ViewChild('notificationSettings', {static: true}) notificationSettings!: MatDrawer; @ViewChild('matTabGroup', {static: true}) matTabs: MatTabGroup; @ViewChild('requestTab', {static: true}) requestTab: MatTab; @ViewChild('notificationRequest', {static: true}) notificationRequestTable: NotificationTableComponent; @@ -54,38 +40,8 @@ export class NotificationCenterComponent extends PageComponent implements AfterV constructor( - protected store: Store, - private notificationService: NotificationService, - private fb: FormBuilder) { + protected store: Store) { super(store); - this.notificationSettingsForm = this.fb.group({ - deliveryMethodsConfigs: this.fb.group({}) - }); - this.notificationDeliveryMethods.forEach(method => { - (this.notificationSettingsForm.get('deliveryMethodsConfigs') as FormGroup) - .addControl(method, this.fb.group({method, enabled: method !== NotificationDeliveryMethod.SLACK}), {emitEvent: false}); - }); - (this.notificationSettingsForm.get('deliveryMethodsConfigs.SLACK') as FormGroup) - .addControl('botToken', this.fb.control({value: '', disabled: true}, Validators.required), {emitEvent: false}); - this.notificationSettingsForm.get('deliveryMethodsConfigs.SLACK.enabled').valueChanges.subscribe(value => { - if (value) { - this.notificationSettingsForm.get('deliveryMethodsConfigs.SLACK.botToken').enable({emitEvent: true}); - } else { - this.notificationSettingsForm.get('deliveryMethodsConfigs.SLACK.botToken').disable({emitEvent: true}); - - } - }); - } - - ngAfterViewInit() { - this.notificationSettings.openedStart.subscribe(() => { - this.notificationService.getNotificationSettings().subscribe( - value => { - this.notificationSettingsForm.patchValue(value, {emitEvent: false}); - this.notificationSettingsForm.get('deliveryMethodsConfigs.SLACK.enabled').updateValueAndValidity({onlySelf: true}); - } - ); - }); } updateData() { @@ -103,19 +59,4 @@ export class NotificationCenterComponent extends PageComponent implements AfterV sendNotification($event: Event) { this.notificationRequestTable.entityTableConfig.onEntityAction({event: $event, action: this.requestTab.isActive ? 'add' : 'add-without-update', entity: null}); } - - toggleNotificationSettings() { - this.notificationSettings.toggle().then(() => {}); - } - - saveNotificationSettigs($event: Event) { - if ($event) { - $event.stopPropagation(); - } - const formValue = deepTrim(this.notificationSettingsForm.getRawValue()); - this.notificationService.saveNotificationSettings(formValue).subscribe(value => { - this.notificationSettingsForm.patchValue(value, {emitEvent: false}); - this.notificationSettingsForm.markAsPristine(); - }); - } } diff --git a/ui-ngx/src/app/modules/home/pages/notification-center/notification-table/inbox-table-config.ts b/ui-ngx/src/app/modules/home/pages/notification-center/notification-table/inbox-table-config.ts index 247206ff68..482100b168 100644 --- a/ui-ngx/src/app/modules/home/pages/notification-center/notification-table/inbox-table-config.ts +++ b/ui-ngx/src/app/modules/home/pages/notification-center/notification-table/inbox-table-config.ts @@ -32,14 +32,12 @@ import { NotificationService } from '@core/http/notification.service'; import { InboxTableHeaderComponent } from '@home/pages/notification-center/inbox-table/inbox-table-header.component'; import { TranslateService } from '@ngx-translate/core'; import { take } from 'rxjs/operators'; -import { EventEmitter } from '@angular/core'; export class InboxTableConfig extends EntityTableConfig { constructor(private notificationService: NotificationService, private translate: TranslateService, - private datePipe: DatePipe, - private toggleSettings: EventEmitter) { + private datePipe: DatePipe) { super(); this.entitiesDeleteEnabled = false; this.entityTranslations = { @@ -60,20 +58,12 @@ export class InboxTableConfig extends EntityTableConfig { this.headerComponent = InboxTableHeaderComponent; - this.headerActionDescriptors = [ - { - name: 'Notification setting', - icon: 'settings', - isEnabled: () => true, - onAction: $event => this.togleNotificationSettings($event) - }, - { - name: this.translate.instant('notification.mark-all-as-read'), - icon: 'done_all', - isEnabled: () => true, - onAction: $event => this.markAllRead($event) - } - ]; + this.headerActionDescriptors = [{ + name: this.translate.instant('notification.mark-all-as-read'), + icon: 'done_all', + isEnabled: () => true, + onAction: $event => this.markAllRead($event) + }]; this.columns.push( new DateEntityTableColumn('createdTime', 'notification.created-time', this.datePipe, '150px'), @@ -107,13 +97,6 @@ export class InboxTableConfig extends EntityTableConfig { }); } - private togleNotificationSettings($event: Event) { - if ($event) { - $event.stopPropagation(); - } - this.toggleSettings.emit(); - } - private markAsRead($event, entity){ if ($event) { $event.stopPropagation(); diff --git a/ui-ngx/src/app/modules/home/pages/notification-center/notification-table/notification-table.component.ts b/ui-ngx/src/app/modules/home/pages/notification-center/notification-table/notification-table.component.ts index 0091aa528a..10c3134459 100644 --- a/ui-ngx/src/app/modules/home/pages/notification-center/notification-table/notification-table.component.ts +++ b/ui-ngx/src/app/modules/home/pages/notification-center/notification-table/notification-table.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; +import { Component, Input, OnInit, ViewChild } from '@angular/core'; import { EntitiesTableComponent } from '@home/components/entity/entities-table.component'; import { NotificationService } from '@core/http/notification.service'; import { TargetsTableConfig } from '@home/pages/notification-center/notification-table/targets-table-config'; @@ -37,9 +37,6 @@ export class NotificationTableComponent implements OnInit { @Input() notificationType = EntityType.NOTIFICATION; - @Output() - toggleSettings: EventEmitter = new EventEmitter(); - @ViewChild(EntitiesTableComponent, {static: true}) entitiesTable: EntitiesTableComponent; entityTableConfig: EntityTableConfig; @@ -74,8 +71,7 @@ export class NotificationTableComponent implements OnInit { return new InboxTableConfig( this.notificationService, this.translate, - this.datePipe, - this.toggleSettings + this.datePipe ); case EntityType.NOTIFICATION_TEMPLATE: return new TemplateTableConfig( diff --git a/ui-ngx/src/app/modules/home/pages/notification-center/notification-table/request-table-config.ts b/ui-ngx/src/app/modules/home/pages/notification-center/notification-table/request-table-config.ts index eb27a05426..23fa0ec3e0 100644 --- a/ui-ngx/src/app/modules/home/pages/notification-center/notification-table/request-table-config.ts +++ b/ui-ngx/src/app/modules/home/pages/notification-center/notification-table/request-table-config.ts @@ -81,7 +81,12 @@ export class RequestTableConfig extends EntityTableConfig> { - return []; + return [{ + name: this.translate.instant('notification.notify-again'), + mdiIcon: 'mdi:repeat-variant', + isEnabled: (request) => request.status !== NotificationRequestStatus.SCHEDULED, + onAction: ($event, entity) => this.createRequest($event, entity) + }]; } private createRequest($event: Event, request: NotificationRequest, isAdd = false, updateData = true) { diff --git a/ui-ngx/src/app/modules/home/pages/notification-center/notification-table/template-table-config.ts b/ui-ngx/src/app/modules/home/pages/notification-center/notification-table/template-table-config.ts index 4fb089f826..daa7cc0b93 100644 --- a/ui-ngx/src/app/modules/home/pages/notification-center/notification-table/template-table-config.ts +++ b/ui-ngx/src/app/modules/home/pages/notification-center/notification-table/template-table-config.ts @@ -21,7 +21,7 @@ import { } from '@home/models/entity/entities-table-config.models'; import { EntityTypeResource } from '@shared/models/entity-type.models'; import { Direction } from '@shared/models/page/sort-order'; -import { NotificationTemplate } from '@shared/models/notification.models'; +import { NotificationTemplate, NotificationTemplateTypeTranslateMap } from '@shared/models/notification.models'; import { NotificationService } from '@core/http/notification.service'; import { EntityAction } from '@home/models/entity/entity-component.models'; import { MatDialog } from '@angular/material/dialog'; @@ -61,7 +61,8 @@ export class TemplateTableConfig extends EntityTableConfig this.defaultSortOrder = {property: 'notificationType', direction: Direction.ASC}; this.columns.push( - new EntityTableColumn('notificationType', 'notification.type', '15%'), + new EntityTableColumn('notificationType', 'notification.type', '15%', + (template) => this.translate.instant(NotificationTemplateTypeTranslateMap.get(template.notificationType).name)), new EntityTableColumn('name', 'notification.template', '25%'), new EntityTableColumn('configuration.notificationSubject', 'notification.subject', '25%', (template) => template.configuration.notificationSubject, () => ({}), false), @@ -71,10 +72,22 @@ export class TemplateTableConfig extends EntityTableConfig } private configureCellActions(): Array> { - return []; + return [ + { + name: this.translate.instant('notification.copy-template'), + icon: 'content_copy', + isEnabled: () => true, + onAction: ($event, entity) => this.editTemplate($event, entity, false, true) + }, { + name: this.translate.instant('notification.edit-template'), + icon: 'edit', + isEnabled: () => true, + onAction: ($event, entity) => this.editTemplate($event, entity) + } + ]; } - private editTemplate($event: Event, template: NotificationTemplate, isAdd = false) { + private editTemplate($event: Event, template: NotificationTemplate, isAdd = false, isCopy = false) { if ($event) { $event.stopPropagation(); } @@ -84,6 +97,7 @@ export class TemplateTableConfig extends EntityTableConfig panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], data: { isAdd, + isCopy, template } }).afterClosed() diff --git a/ui-ngx/src/app/modules/home/pages/notification-center/request-table/request-notification-dialog.component.html b/ui-ngx/src/app/modules/home/pages/notification-center/request-table/request-notification-dialog.component.html index ecf3634799..5373217d56 100644 --- a/ui-ngx/src/app/modules/home/pages/notification-center/request-table/request-notification-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/notification-center/request-table/request-notification-dialog.component.html @@ -16,7 +16,7 @@ --> -

notification.new-notification

+

{{ dialogTitle | translate }}