Notification system fixes, new notification targets
This commit is contained in:
parent
b10e162b19
commit
197207035b
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -191,6 +191,8 @@ public class NotificationTargetController extends BaseController {
|
||||
throw new AccessDeniedException("");
|
||||
}
|
||||
break;
|
||||
case SYSTEM_ADMINISTRATORS:
|
||||
throw new AccessDeniedException("");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -123,8 +123,8 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
|
||||
}
|
||||
if (notificationTemplate == null) throw new IllegalArgumentException("Template is missing");
|
||||
|
||||
List<NotificationTarget> targets = notificationTargetService.findNotificationTargetsByTenantIdAndIds(tenantId,
|
||||
notificationRequest.getTargets().stream().map(NotificationTargetId::new).collect(Collectors.toList()));
|
||||
List<NotificationTarget> targets = notificationRequest.getTargets().stream().map(NotificationTargetId::new)
|
||||
.map(id -> notificationTargetService.findNotificationTargetById(tenantId, id)).collect(Collectors.toList());
|
||||
Set<NotificationDeliveryMethod> availableDeliveryMethods = getAvailableDeliveryMethods(tenantId);
|
||||
|
||||
notificationTemplate.getConfiguration().getDeliveryMethodsTemplates().forEach((deliveryMethod, template) -> {
|
||||
|
||||
@ -73,11 +73,12 @@ public class DefaultNotificationRuleProcessingService implements NotificationRul
|
||||
|
||||
@Override
|
||||
public void process(TenantId tenantId, NotificationRuleTrigger trigger) {
|
||||
List<NotificationRule> rules = notificationRuleService.findNotificationRulesByTenantIdAndTriggerType(tenantId, trigger.getType());
|
||||
List<NotificationRule> 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<NotificationRequest> notificationRequests = notificationRequestService.findNotificationRequestsByRuleIdAndOriginatorEntityId(rule.getTenantId(), rule.getId(), trigger.getOriginatorEntityId());
|
||||
List<NotificationRequest> 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<UUID> targets, NotificationRule rule,
|
||||
private void submitNotificationRequest(TenantId tenantId, List<UUID> 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);
|
||||
}
|
||||
|
||||
@ -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<EntitiesLimitTrigger, EntitiesLimitNotificationRuleTriggerConfig> {
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
@ -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")))
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -63,6 +63,8 @@ public interface UserService extends EntityDaoService {
|
||||
|
||||
PageData<User> findTenantAdmins(TenantId tenantId, PageLink pageLink);
|
||||
|
||||
PageData<User> findSysAdmins(PageLink pageLink);
|
||||
|
||||
PageData<User> findAllTenantAdmins(PageLink pageLink);
|
||||
|
||||
PageData<User> findTenantAdminsByTenantsIds(List<TenantId> tenantsIds, PageLink pageLink);
|
||||
|
||||
@ -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<String, String> 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
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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")
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -252,6 +252,11 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic
|
||||
return userDao.findTenantAdmins(tenantId.getId(), pageLink);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageData<User> findSysAdmins(PageLink pageLink) {
|
||||
return userDao.findAllByAuthority(Authority.SYS_ADMIN, pageLink);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageData<User> findAllTenantAdmins(PageLink pageLink) {
|
||||
return userDao.findAllByAuthority(Authority.TENANT_ADMIN, pageLink);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user