From 197207035bcbc8ab2153efb0e89a272a4d2da8a9 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Thu, 23 Mar 2023 18:27:45 +0200 Subject: [PATCH 1/2] Notification system fixes, new notification targets --- .../NotificationRuleController.java | 24 ++++++++++------ .../NotificationTargetController.java | 2 ++ .../limits/DefaultRateLimitService.java | 3 ++ .../DefaultNotificationCenter.java | 4 +-- ...aultNotificationRuleProcessingService.java | 21 +++++++------- .../EntitiesLimitTriggerProcessor.java | 7 +++++ .../trigger/EntityActionTriggerProcessor.java | 2 +- .../trigger/EntitiesLimitTrigger.java | 1 + .../server/dao/user/UserService.java | 2 ++ .../info/EntitiesLimitNotificationInfo.java | 7 ++++- .../trigger/NotificationRuleTriggerType.java | 17 +++++++++-- .../AffectedTenantAdministratorsFilter.java | 28 +++++++++++++++++++ .../platform/SystemAdministratorsFilter.java | 28 +++++++++++++++++++ .../targets/platform/UsersFilter.java | 2 ++ .../targets/platform/UsersFilterType.java | 2 ++ .../DefaultNotificationSettingsService.java | 9 ++++-- .../DefaultNotificationTargetService.java | 4 +++ .../usagerecord/DefaultApiLimitService.java | 1 + .../server/dao/user/UserServiceImpl.java | 5 ++++ 19 files changed, 143 insertions(+), 26 deletions(-) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/AffectedTenantAdministratorsFilter.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/SystemAdministratorsFilter.java 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 fe326ef6b8..4fdb0136f3 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationRuleController.java +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationRuleController.java @@ -32,6 +32,7 @@ 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.notification.rule.trigger.NotificationRuleTriggerType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; @@ -56,9 +57,16 @@ public class NotificationRuleController extends BaseController { @PostMapping("/rule") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - public NotificationRule saveNotificationRule(@RequestBody @Valid NotificationRule notificationRule) throws Exception { - notificationRule.setTenantId(getTenantId()); + public NotificationRule saveNotificationRule(@RequestBody @Valid NotificationRule notificationRule, + @AuthenticationPrincipal SecurityUser user) throws Exception { + notificationRule.setTenantId(user.getTenantId()); checkEntity(notificationRule.getId(), notificationRule, NOTIFICATION); + + NotificationRuleTriggerType triggerType = notificationRule.getTriggerType(); + if ((user.isTenantAdmin() && !triggerType.isTenantLevel()) || (user.isSystemAdmin() && triggerType.isTenantLevel())) { + throw new IllegalArgumentException("Trigger type " + triggerType + " is not available"); + } + return doSaveAndLog(EntityType.NOTIFICATION_RULE, notificationRule, notificationRuleService::saveNotificationRule); } @@ -66,17 +74,17 @@ public class NotificationRuleController extends BaseController { @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") public NotificationRuleInfo getNotificationRuleById(@PathVariable UUID id) throws ThingsboardException { NotificationRuleId notificationRuleId = new NotificationRuleId(id); - return checkEntityId(notificationRuleId, notificationRuleService::findNotificationRuleInfoById, Operation.READ); + return checkEntityId(notificationRuleId, notificationRuleService::findNotificationRuleInfoById, Operation.READ); } @GetMapping("/rules") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") 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 { + @RequestParam int page, + @RequestParam(required = false) String textSearch, + @RequestParam(required = false) String sortProperty, + @RequestParam(required = false) String sortOrder, + @AuthenticationPrincipal SecurityUser user) throws ThingsboardException { // generic permission PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); return notificationRuleService.findNotificationRulesInfosByTenantId(user.getTenantId(), pageLink); 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 7f7cc5afd1..3107435e2d 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java @@ -191,6 +191,8 @@ public class NotificationTargetController extends BaseController { throw new AccessDeniedException(""); } break; + case SYSTEM_ADMINISTRATORS: + throw new AccessDeniedException(""); } } diff --git a/application/src/main/java/org/thingsboard/server/service/apiusage/limits/DefaultRateLimitService.java b/application/src/main/java/org/thingsboard/server/service/apiusage/limits/DefaultRateLimitService.java index b75fb99c02..e995a8bd1f 100644 --- a/application/src/main/java/org/thingsboard/server/service/apiusage/limits/DefaultRateLimitService.java +++ b/application/src/main/java/org/thingsboard/server/service/apiusage/limits/DefaultRateLimitService.java @@ -35,6 +35,9 @@ public class DefaultRateLimitService implements RateLimitService { @Override public boolean checkRateLimit(TenantId tenantId, LimitedApi api) { + if (tenantId.isSysTenantId()) { + return true; + } String rateLimitConfig = tenantProfileCache.get(tenantId).getProfileConfiguration() .map(api::getLimitConfig).orElse(null); 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 619616bc8b..65469a65a0 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 @@ -123,8 +123,8 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple } if (notificationTemplate == null) throw new IllegalArgumentException("Template is missing"); - List targets = notificationTargetService.findNotificationTargetsByTenantIdAndIds(tenantId, - notificationRequest.getTargets().stream().map(NotificationTargetId::new).collect(Collectors.toList())); + List targets = notificationRequest.getTargets().stream().map(NotificationTargetId::new) + .map(id -> notificationTargetService.findNotificationTargetById(tenantId, id)).collect(Collectors.toList()); Set availableDeliveryMethods = getAvailableDeliveryMethods(tenantId); notificationTemplate.getConfiguration().getDeliveryMethodsTemplates().forEach((deliveryMethod, template) -> { diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessingService.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessingService.java index 682c97ede2..da61aa0c30 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessingService.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessingService.java @@ -73,11 +73,12 @@ public class DefaultNotificationRuleProcessingService implements NotificationRul @Override public void process(TenantId tenantId, NotificationRuleTrigger trigger) { - List rules = notificationRuleService.findNotificationRulesByTenantIdAndTriggerType(tenantId, trigger.getType()); + List rules = notificationRuleService.findNotificationRulesByTenantIdAndTriggerType( + trigger.getType().isTenantLevel() ? tenantId : TenantId.SYS_TENANT_ID, trigger.getType()); for (NotificationRule rule : rules) { notificationExecutor.submit(() -> { try { - processNotificationRule(rule, trigger); + processNotificationRule(tenantId, rule, trigger); } catch (Throwable e) { log.error("Failed to process notification rule {} for trigger type {} with trigger object {}", rule.getId(), rule.getTriggerType(), trigger, e); } @@ -97,12 +98,12 @@ public class DefaultNotificationRuleProcessingService implements NotificationRul .build()); } - private void processNotificationRule(NotificationRule rule, NotificationRuleTrigger trigger) { + private void processNotificationRule(TenantId tenantId, NotificationRule rule, NotificationRuleTrigger trigger) { NotificationRuleTriggerConfig triggerConfig = rule.getTriggerConfig(); log.debug("Processing notification rule '{}' for trigger type {}", rule.getName(), rule.getTriggerType()); if (matchesClearRule(trigger, triggerConfig)) { - List notificationRequests = notificationRequestService.findNotificationRequestsByRuleIdAndOriginatorEntityId(rule.getTenantId(), rule.getId(), trigger.getOriginatorEntityId()); + List notificationRequests = notificationRequestService.findNotificationRequestsByRuleIdAndOriginatorEntityId(tenantId, rule.getId(), trigger.getOriginatorEntityId()); if (notificationRequests.isEmpty()) { return; } @@ -112,11 +113,11 @@ public class DefaultNotificationRuleProcessingService implements NotificationRul .flatMap(notificationRequest -> notificationRequest.getTargets().stream()) .distinct().collect(Collectors.toList()); NotificationInfo notificationInfo = constructNotificationInfo(trigger, triggerConfig); - submitNotificationRequest(targets, rule, trigger.getOriginatorEntityId(), notificationInfo, 0); + submitNotificationRequest(tenantId, targets, rule, trigger.getOriginatorEntityId(), notificationInfo, 0); notificationRequests.forEach(notificationRequest -> { if (notificationRequest.isScheduled()) { - notificationCenter.deleteNotificationRequest(rule.getTenantId(), notificationRequest.getId()); + notificationCenter.deleteNotificationRequest(tenantId, notificationRequest.getId()); } }); return; @@ -125,7 +126,7 @@ public class DefaultNotificationRuleProcessingService implements NotificationRul if (matchesFilter(trigger, triggerConfig)) { NotificationInfo notificationInfo = constructNotificationInfo(trigger, triggerConfig); rule.getRecipientsConfig().getTargetsTable().forEach((delay, targets) -> { - submitNotificationRequest(targets, rule, trigger.getOriginatorEntityId(), notificationInfo, delay); + submitNotificationRequest(tenantId, targets, rule, trigger.getOriginatorEntityId(), notificationInfo, delay); }); } } @@ -142,14 +143,14 @@ public class DefaultNotificationRuleProcessingService implements NotificationRul return triggerProcessors.get(triggerConfig.getTriggerType()).constructNotificationInfo(trigger, triggerConfig); } - private void submitNotificationRequest(List targets, NotificationRule rule, + private void submitNotificationRequest(TenantId tenantId, List targets, NotificationRule rule, EntityId originatorEntityId, NotificationInfo notificationInfo, int delayInSec) { NotificationRequestConfig config = new NotificationRequestConfig(); if (delayInSec > 0) { config.setSendingDelayInSec(delayInSec); } NotificationRequest notificationRequest = NotificationRequest.builder() - .tenantId(rule.getTenantId()) + .tenantId(tenantId) .targets(targets) .templateId(rule.getTemplateId()) .additionalConfig(config) @@ -160,7 +161,7 @@ public class DefaultNotificationRuleProcessingService implements NotificationRul notificationExecutor.submit(() -> { try { log.debug("Submitting notification request for rule '{}' with delay of {} sec to targets {}", rule.getName(), delayInSec, targets); - notificationCenter.processNotificationRequest(rule.getTenantId(), notificationRequest); + notificationCenter.processNotificationRequest(tenantId, notificationRequest); } catch (Exception e) { log.error("Failed to process notification request for rule {}", rule.getId(), e); } diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EntitiesLimitTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EntitiesLimitTriggerProcessor.java index b7fd69a362..3b15a56f8b 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EntitiesLimitTriggerProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EntitiesLimitTriggerProcessor.java @@ -15,18 +15,23 @@ */ package org.thingsboard.server.service.notification.rule.trigger; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.notification.info.EntitiesLimitNotificationInfo; import org.thingsboard.server.common.data.notification.info.NotificationInfo; import org.thingsboard.server.common.data.notification.rule.trigger.EntitiesLimitNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; import org.thingsboard.server.dao.notification.trigger.EntitiesLimitTrigger; +import org.thingsboard.server.dao.tenant.TenantService; import static org.apache.commons.collections.CollectionUtils.isNotEmpty; @Service +@RequiredArgsConstructor public class EntitiesLimitTriggerProcessor implements NotificationRuleTriggerProcessor { + private final TenantService tenantService; + @Override public boolean matchesFilter(EntitiesLimitTrigger trigger, EntitiesLimitNotificationRuleTriggerConfig triggerConfig) { if (isNotEmpty(triggerConfig.getEntityTypes()) && !triggerConfig.getEntityTypes().contains(trigger.getEntityType())) { @@ -42,6 +47,8 @@ public class EntitiesLimitTriggerProcessor implements NotificationRuleTriggerPro .currentCount(trigger.getCurrentCount()) .limit(trigger.getLimit()) .percents((int) (((float)trigger.getCurrentCount() / trigger.getLimit()) * 100)) + .tenantId(trigger.getTenantId()) + .tenantName(tenantService.findTenantById(trigger.getTenantId()).getName()) .build(); } diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EntityActionTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EntityActionTriggerProcessor.java index bcace461f5..1fb6270ea5 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EntityActionTriggerProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EntityActionTriggerProcessor.java @@ -62,7 +62,7 @@ public class EntityActionTriggerProcessor implements RuleEngineMsgNotificationRu msgType.equals(DataConstants.ENTITY_UPDATED) ? ActionType.UPDATED : msgType.equals(DataConstants.ENTITY_DELETED) ? ActionType.DELETED : null; return EntityActionNotificationInfo.builder() - .entityId(actionType != ActionType.DELETED ? msg.getOriginator() : null) + .entityId(msg.getOriginator()) .entityName(msg.getMetaData().getValue("entityName")) .actionType(actionType) .originatorUserId(UUID.fromString(msg.getMetaData().getValue("userId"))) diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/trigger/EntitiesLimitTrigger.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/trigger/EntitiesLimitTrigger.java index 01255d2808..330e7de9d4 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/trigger/EntitiesLimitTrigger.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/trigger/EntitiesLimitTrigger.java @@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.notification.rule.trigger.Notification @Builder public class EntitiesLimitTrigger implements NotificationRuleTrigger { + private final TenantId tenantId; private final EntityType entityType; private final long currentCount; private final long limit; diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java index cb713bf20a..e299f4ecb5 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java @@ -63,6 +63,8 @@ public interface UserService extends EntityDaoService { PageData findTenantAdmins(TenantId tenantId, PageLink pageLink); + PageData findSysAdmins(PageLink pageLink); + PageData findAllTenantAdmins(PageLink pageLink); PageData findTenantAdminsByTenantsIds(List tenantsIds, PageLink pageLink); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EntitiesLimitNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EntitiesLimitNotificationInfo.java index 2c277492e3..2d783ac036 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EntitiesLimitNotificationInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EntitiesLimitNotificationInfo.java @@ -20,6 +20,7 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.TenantId; import java.util.Map; @@ -35,6 +36,8 @@ public class EntitiesLimitNotificationInfo implements NotificationInfo { private long currentCount; private long limit; private int percents; + private TenantId tenantId; + private String tenantName; @Override public Map getTemplateData() { @@ -42,7 +45,9 @@ public class EntitiesLimitNotificationInfo implements NotificationInfo { "entityType", entityType.getNormalName(), "currentCount", String.valueOf(currentCount), "limit", String.valueOf(limit), - "percents", String.valueOf(percents) + "percents", String.valueOf(percents), + "tenantId", tenantId.toString(), + "tenantName", tenantName ); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTriggerType.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTriggerType.java index d843347b5a..d6de7b4514 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTriggerType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTriggerType.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.notification.rule.trigger; +import lombok.Getter; + +@Getter public enum NotificationRuleTriggerType { ALARM, @@ -23,7 +26,17 @@ public enum NotificationRuleTriggerType { ENTITY_ACTION, RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT, ALARM_ASSIGNMENT, - NEW_PLATFORM_VERSION, - ENTITIES_LIMIT + NEW_PLATFORM_VERSION(false), + ENTITIES_LIMIT(false); + + private final boolean tenantLevel; + + NotificationRuleTriggerType(boolean tenantLevel) { + this.tenantLevel = tenantLevel; + } + + NotificationRuleTriggerType() { + this(true); + } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/AffectedTenantAdministratorsFilter.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/AffectedTenantAdministratorsFilter.java new file mode 100644 index 0000000000..cc3e1283f3 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/AffectedTenantAdministratorsFilter.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2023 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; + +@Data +public class AffectedTenantAdministratorsFilter implements UsersFilter { + + @Override + public UsersFilterType getType() { + return UsersFilterType.AFFECTED_TENANT_ADMINISTRATORS; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/SystemAdministratorsFilter.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/SystemAdministratorsFilter.java new file mode 100644 index 0000000000..7fc6049b6e --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/SystemAdministratorsFilter.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2023 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; + +@Data +public class SystemAdministratorsFilter implements UsersFilter { + + @Override + public UsersFilterType getType() { + return UsersFilterType.SYSTEM_ADMINISTRATORS; + } + +} 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 index 62773f6473..c0c3f5f45e 100644 --- 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 @@ -27,6 +27,8 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; @Type(value = UserListFilter.class, name = "USER_LIST"), @Type(value = CustomerUsersFilter.class, name = "CUSTOMER_USERS"), @Type(value = TenantAdministratorsFilter.class, name = "TENANT_ADMINISTRATORS"), + @Type(value = AffectedTenantAdministratorsFilter.class, name = "AFFECTED_TENANT_ADMINISTRATORS"), + @Type(value = SystemAdministratorsFilter.class, name = "SYSTEM_ADMINISTRATORS"), @Type(value = AllUsersFilter.class, name = "ALL_USERS"), @Type(value = OriginatorEntityOwnerUsersFilter.class, name = "ORIGINATOR_ENTITY_OWNER_USERS"), @Type(value = AffectedUserFilter.class, name = "AFFECTED_USER") diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UsersFilterType.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UsersFilterType.java index 10a4d3c34f..66b708cb86 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UsersFilterType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UsersFilterType.java @@ -27,6 +27,8 @@ public enum UsersFilterType { USER_LIST, CUSTOMER_USERS, TENANT_ADMINISTRATORS, + AFFECTED_TENANT_ADMINISTRATORS(true), + SYSTEM_ADMINISTRATORS, ALL_USERS, ORIGINATOR_ENTITY_OWNER_USERS(true), AFFECTED_USER(true); diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationSettingsService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationSettingsService.java index 1088347397..582e3fb981 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationSettingsService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationSettingsService.java @@ -48,10 +48,12 @@ import org.thingsboard.server.common.data.notification.rule.trigger.Notification import org.thingsboard.server.common.data.notification.rule.trigger.RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig; 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.platform.AffectedTenantAdministratorsFilter; import org.thingsboard.server.common.data.notification.targets.platform.AffectedUserFilter; import org.thingsboard.server.common.data.notification.targets.platform.AllUsersFilter; import org.thingsboard.server.common.data.notification.targets.platform.OriginatorEntityOwnerUsersFilter; import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig; +import org.thingsboard.server.common.data.notification.targets.platform.SystemAdministratorsFilter; import org.thingsboard.server.common.data.notification.targets.platform.TenantAdministratorsFilter; import org.thingsboard.server.common.data.notification.targets.platform.UsersFilter; import org.thingsboard.server.common.data.notification.template.NotificationTemplate; @@ -119,14 +121,17 @@ public class DefaultNotificationSettingsService implements NotificationSettingsS "Maintenance work is scheduled for tomorrow (7:00 a.m. - 9:00 a.m. UTC)"); if (tenantId.isSysTenantId()) { + NotificationTarget sysAdmins = createTarget(tenantId, "System administrators", new SystemAdministratorsFilter(), "All system administrators"); + NotificationTarget affectedTenantAdmins = createTarget(tenantId, "Affected tenant's administrators", new AffectedTenantAdministratorsFilter(), ""); + NotificationTemplate entitiesLimitNotificationTemplate = createTemplate(tenantId, "Entities limit notification", NotificationType.ENTITIES_LIMIT, - "${entityType}s limit will be reached soon", + "${entityType}s limit will be reached soon for tenant ${tenantName}", "${entityType}s usage: ${currentCount}/${limit} (${percents}%)"); EntitiesLimitNotificationRuleTriggerConfig entitiesLimitRuleTriggerConfig = new EntitiesLimitNotificationRuleTriggerConfig(); entitiesLimitRuleTriggerConfig.setEntityTypes(null); entitiesLimitRuleTriggerConfig.setThreshold(0.8f); createRule(tenantId, "Entities limit", entitiesLimitNotificationTemplate.getId(), entitiesLimitRuleTriggerConfig, - List.of(tenantAdmins.getId()), "Send notification to tenant admins when count of entities of some type reached 80% threshold of the limit"); + List.of(affectedTenantAdmins.getId(), sysAdmins.getId()), "Send notification to tenant admins when count of entities of some type reached 80% threshold of the limit"); return; } 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 13fa0cffd4..b97ddf6d83 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 @@ -142,6 +142,10 @@ public class DefaultNotificationTargetService extends AbstractEntityService impl } } } + case AFFECTED_TENANT_ADMINISTRATORS: + return userService.findTenantAdmins(tenantId, pageLink); + case SYSTEM_ADMINISTRATORS: + return userService.findSysAdmins(pageLink); case ALL_USERS: { if (!tenantId.equals(TenantId.SYS_TENANT_ID)) { return userService.findUsersByTenantId(tenantId, pageLink); diff --git a/dao/src/main/java/org/thingsboard/server/dao/usagerecord/DefaultApiLimitService.java b/dao/src/main/java/org/thingsboard/server/dao/usagerecord/DefaultApiLimitService.java index 510949e97c..4bb31f0425 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/usagerecord/DefaultApiLimitService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/usagerecord/DefaultApiLimitService.java @@ -49,6 +49,7 @@ public class DefaultApiLimitService implements ApiLimitService { long currentCount = entityService.countEntitiesByQuery(tenantId, new CustomerId(EntityId.NULL_UUID), new EntityCountQuery(filter)); if (notificationRuleProcessingService != null) { notificationRuleProcessingService.process(tenantId, EntitiesLimitTrigger.builder() + .tenantId(tenantId) .entityType(entityType) .currentCount(currentCount) .limit(limit) diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java index 18f413ab17..b9d7422ef0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java @@ -252,6 +252,11 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic return userDao.findTenantAdmins(tenantId.getId(), pageLink); } + @Override + public PageData findSysAdmins(PageLink pageLink) { + return userDao.findAllByAuthority(Authority.SYS_ADMIN, pageLink); + } + @Override public PageData findAllTenantAdmins(PageLink pageLink) { return userDao.findAllByAuthority(Authority.TENANT_ADMIN, pageLink); From 4673e812b076dbe8843dfae3c9d02f3c024080d4 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Fri, 24 Mar 2023 13:21:25 +0200 Subject: [PATCH 2/2] Fix notification system tests --- .../notification/NotificationApiTest.java | 10 ++++------ .../notification/NotificationRuleApiTest.java | 19 ++++++++----------- 2 files changed, 12 insertions(+), 17 deletions(-) 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 7d318f5522..08ac826c51 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 @@ -439,14 +439,14 @@ public class NotificationApiTest extends AbstractNotificationApiTest { assertThat(processedTemplates.get(NotificationDeliveryMethod.SLACK)).asInstanceOf(type(SlackDeliveryMethodNotificationTemplate.class)) .satisfies(template -> { assertThat(template.getBody()) - .isEqualTo("Message for SLACK: ${recipientEmail}"); // ${recipientEmail} should not be processed + .isEqualTo("Message for SLACK: "); // ${recipientEmail} should be removed }); } @Test public void testNotificationRequestInfo() throws Exception { NotificationDeliveryMethod[] deliveryMethods = new NotificationDeliveryMethod[]{ - NotificationDeliveryMethod.WEB, NotificationDeliveryMethod.EMAIL + NotificationDeliveryMethod.WEB }; NotificationTemplate template = createNotificationTemplate(NotificationType.GENERAL, "Test subject", "Test text", deliveryMethods); NotificationTarget target = createNotificationTarget(tenantAdminUserId); @@ -465,8 +465,7 @@ public class NotificationApiTest extends AbstractNotificationApiTest { wsClient.registerWaitForUpdate(); NotificationTarget notificationTarget = createNotificationTarget(customerUserId); - NotificationRequest notificationRequest = submitNotificationRequest(notificationTarget.getId(), "Test :)", - NotificationDeliveryMethod.WEB, NotificationDeliveryMethod.SMS); + NotificationRequest notificationRequest = submitNotificationRequest(notificationTarget.getId(), "Test :)", NotificationDeliveryMethod.WEB); wsClient.waitForUpdate(); await().atMost(2, TimeUnit.SECONDS) @@ -474,7 +473,6 @@ public class NotificationApiTest extends AbstractNotificationApiTest { NotificationRequestStats stats = getStats(notificationRequest.getId()); assertThat(stats.getSent().get(NotificationDeliveryMethod.WEB)).hasValue(1); - assertThat(stats.getErrors().get(NotificationDeliveryMethod.SMS)).size().isOne(); } @Test @@ -559,7 +557,7 @@ public class NotificationApiTest extends AbstractNotificationApiTest { 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(slackNotificationTemplate.getBody())); + verify(slackService).sendMessage(eq(tenantId), eq(slackToken), eq(conversationId), eq("To Slack :) ")); NotificationRequestStats stats = getStats(successfulNotificationRequest.getId()); assertThat(stats.getSent().get(NotificationDeliveryMethod.SLACK)).hasValue(1); 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 f355d9072c..fdcb5e754c 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 @@ -232,20 +232,17 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { double actualDelay = (double) (notification.getCreatedTime() - ts) / 1000; assertThat(actualDelay).isCloseTo(expectedDelay, offset(0.5)); - AlarmStatus expectedStatus = AlarmStatus.ACTIVE_UNACK; - String expectedSeverity = AlarmSeverity.CRITICAL.toString().toLowerCase(); - - assertThat(notification.getSubject()).isEqualTo("Alarm type: " + alarmType + ", status: " + expectedStatus + ", " + - "severity: " + expectedSeverity + ", deviceId: " + device.getId()); - assertThat(notification.getText()).isEqualTo("Status: " + expectedStatus + ", severity: " + expectedSeverity); + assertThat(notification.getSubject()).isEqualTo("Alarm type: " + alarmType + ", status: " + AlarmStatus.ACTIVE_UNACK + ", " + + "severity: " + AlarmSeverity.CRITICAL.toString().toLowerCase() + ", deviceId: " + device.getId()); + assertThat(notification.getText()).isEqualTo("Status: " + AlarmStatus.ACTIVE_UNACK + ", severity: " + AlarmSeverity.CRITICAL.toString().toLowerCase()); assertThat(notification.getType()).isEqualTo(NotificationType.ALARM); assertThat(notification.getInfo()).isInstanceOf(AlarmNotificationInfo.class); AlarmNotificationInfo info = (AlarmNotificationInfo) notification.getInfo(); assertThat(info.getAlarmId()).isEqualTo(alarm.getUuidId()); assertThat(info.getAlarmType()).isEqualTo(alarmType); - assertThat(info.getAlarmSeverity()).isEqualTo(expectedSeverity); - assertThat(info.getAlarmStatus()).isEqualTo(expectedStatus); + assertThat(info.getAlarmSeverity()).isEqualTo(AlarmSeverity.CRITICAL); + assertThat(info.getAlarmStatus()).isEqualTo(AlarmStatus.ACTIVE_UNACK); }); clients.values().forEach(wsClient -> wsClient.registerWaitForUpdate()); @@ -256,8 +253,8 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { wsClient.waitForUpdate(true); Notification updatedNotification = wsClient.getLastDataUpdate().getUpdate(); assertThat(updatedNotification.getSubject()).isEqualTo("Alarm type: " + alarmType + ", status: " + expectedStatus + ", " + - "severity: " + expectedSeverity + ", deviceId: " + device.getId()); - assertThat(updatedNotification.getText()).isEqualTo("Status: " + expectedStatus + ", severity: " + expectedSeverity); + "severity: " + expectedSeverity.toString().toLowerCase() + ", deviceId: " + device.getId()); + assertThat(updatedNotification.getText()).isEqualTo("Status: " + expectedStatus + ", severity: " + expectedSeverity.toString().toLowerCase()); wsClient.close(); }); @@ -325,7 +322,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { alarmSubscriptionService.clearAlarm(tenantId, alarm.getId(), System.currentTimeMillis(), null); getWsClient().waitForUpdate(true); notification = getWsClient().getLastDataUpdate().getUpdate(); - assertThat(notification.getSubject()).isEqualTo("CRITICAL alarm '" + alarmType + "' is CLEARED_UNACK"); + assertThat(notification.getSubject()).isEqualTo("critical alarm '" + alarmType + "' is CLEARED_UNACK"); assertThat(findNotificationRequests(EntityType.ALARM).getData()).filteredOn(NotificationRequest::isScheduled).isEmpty(); }