From c34b6623ce0121e14da8ffeb6a2d8cef3809da90 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Fri, 17 Mar 2023 17:17:34 +0200 Subject: [PATCH] Default notification configs; improvements --- .../install/ThingsboardInstallService.java | 9 +- .../DefaultSystemDataLoaderService.java | 19 +- .../update/DefaultDataUpdateService.java | 2 +- .../AlarmAssignmentTriggerProcessor.java | 5 +- .../trigger/AlarmCommentTriggerProcessor.java | 2 +- .../trigger/EntityActionTriggerProcessor.java | 2 +- ...mponentLifecycleEventTriggerProcessor.java | 3 + .../server/common/data/id/TenantId.java | 6 + .../NotificationProcessingContext.java | 18 +- .../info/AlarmAssignmentNotificationInfo.java | 2 +- .../info/AlarmCommentNotificationInfo.java | 2 +- .../info/AlarmNotificationInfo.java | 2 +- .../DeviceInactivityNotificationInfo.java | 1 - ...mponentLifecycleEventNotificationInfo.java | 2 + .../notification/rule/NotificationRule.java | 2 + ...signmentNotificationRuleTriggerConfig.java | 9 +- ...iesLimitNotificationRuleTriggerConfig.java | 2 + ...mVersionNotificationRuleTriggerConfig.java | 2 + ...cleEventNotificationRuleTriggerConfig.java | 4 +- .../targets/NotificationTargetConfig.java | 2 + .../DefaultNotificationSettingsService.java | 231 ++++++++++++++++-- .../server/dao/tenant/TenantServiceImpl.java | 6 +- 22 files changed, 285 insertions(+), 48 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java index a22b8854bb..cb9a3ef8c4 100644 --- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java +++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java @@ -34,6 +34,9 @@ import org.thingsboard.server.service.install.migrate.EntitiesMigrateService; import org.thingsboard.server.service.install.migrate.TsLatestMigrateService; import org.thingsboard.server.service.install.update.CacheCleanupService; import org.thingsboard.server.service.install.update.DataUpdateService; +import org.thingsboard.server.service.install.update.DefaultDataUpdateService; + +import static org.thingsboard.server.service.install.update.DefaultDataUpdateService.getEnv; @Service @Profile("install") @@ -242,7 +245,11 @@ public class ThingsboardInstallService { databaseEntitiesUpgradeService.upgradeDatabase("3.4.4"); log.info("Updating system data..."); systemDataLoaderService.updateSystemWidgets(); - systemDataLoaderService.createDefaultNotificationConfigs(); + if (!getEnv("SKIP_DEFAULT_NOTIFICATION_CONFIGS_CREATION", false)) { + systemDataLoaderService.createDefaultNotificationConfigs(); + } else { + log.info("Skipping default notification configs creation"); + } break; //TODO update CacheCleanupService on the next version upgrade default: diff --git a/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java b/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java index bdbf584a3e..a182a4d83f 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java @@ -22,6 +22,7 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; @@ -678,13 +679,27 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { @Override public void createDefaultNotificationConfigs() { - notificationSettingsService.createDefaultNotificationConfigs(TenantId.SYS_TENANT_ID); + try { + log.info("Creating default notification configs for system admin"); + notificationSettingsService.createDefaultNotificationConfigs(TenantId.SYS_TENANT_ID); + } catch (Exception e) { + if (StringUtils.contains(e.getMessage(), "already exists")) { + log.info("Default notification configs are already present for system admin, skipping"); + } else { + throw e; + } + } PageDataIterable tenants = new PageDataIterable<>(tenantService::findTenantsIds, 500); + log.info("Creating default notification configs for all tenants"); for (TenantId tenantId : tenants) { try { notificationSettingsService.createDefaultNotificationConfigs(tenantId); } catch (Exception e) { - log.warn("Failed to create default notification configs for tenant {}: {}", tenantId, e.getMessage()); + if (StringUtils.contains(e.getMessage(), "already exists")) { + log.info("Default notification configs are already present for tenant {}, skipping", tenantId); + } else { + throw e; + } } } } diff --git a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java index 43d6a4df0a..13943d5698 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java @@ -672,7 +672,7 @@ public class DefaultDataUpdateService implements DataUpdateService { return mainQueueConfiguration; } - private boolean getEnv(String name, boolean defaultValue) { + public static boolean getEnv(String name, boolean defaultValue) { String env = System.getenv(name); if (env == null) { return defaultValue; diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmAssignmentTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmAssignmentTriggerProcessor.java index e3ecef5ef1..bda6800e34 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmAssignmentTriggerProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmAssignmentTriggerProcessor.java @@ -25,6 +25,7 @@ import org.thingsboard.server.common.data.alarm.AlarmStatusFilter; import org.thingsboard.server.common.data.notification.info.AlarmAssignmentNotificationInfo; import org.thingsboard.server.common.data.notification.info.NotificationInfo; import org.thingsboard.server.common.data.notification.rule.trigger.AlarmAssignmentNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.AlarmAssignmentNotificationRuleTriggerConfig.Action; import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; import org.thingsboard.server.common.msg.TbMsg; @@ -37,8 +38,8 @@ public class AlarmAssignmentTriggerProcessor implements RuleEngineMsgNotificatio @Override public boolean matchesFilter(TbMsg ruleEngineMsg, AlarmAssignmentNotificationRuleTriggerConfig triggerConfig) { - if ((ruleEngineMsg.getType().equals(DataConstants.ALARM_ASSIGN) && !triggerConfig.isNotifyOnAssign()) - || (ruleEngineMsg.getType().equals(DataConstants.ALARM_UNASSIGN) && !triggerConfig.isNotifyOnUnassign())) { + Action action = ruleEngineMsg.getType().equals(DataConstants.ALARM_ASSIGN) ? Action.ASSIGNED : Action.UNASSIGNED; + if (!triggerConfig.getNotifyOn().contains(action)) { return false; } Alarm alarm = JacksonUtil.fromString(ruleEngineMsg.getData(), Alarm.class); diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmCommentTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmCommentTriggerProcessor.java index df9ff12c17..d389f22960 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmCommentTriggerProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmCommentTriggerProcessor.java @@ -62,7 +62,7 @@ public class AlarmCommentTriggerProcessor implements RuleEngineMsgNotificationRu AlarmInfo alarmInfo = JacksonUtil.fromString(ruleEngineMsg.getData(), AlarmInfo.class); return AlarmCommentNotificationInfo.builder() .comment(comment.getComment().get("text").asText()) - .action(ruleEngineMsg.getType().equals(DataConstants.COMMENT_CREATED) ? "created" : "updated") + .action(ruleEngineMsg.getType().equals(DataConstants.COMMENT_CREATED) ? "added" : "updated") .userName(ruleEngineMsg.getMetaData().getValue("userName")) .alarmId(alarmInfo.getUuidId()) .alarmType(alarmInfo.getType()) 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 60044dd96a..2c0d19f9bd 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 @@ -60,7 +60,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(entityId) + .entityId(actionType != ActionType.DELETED ? entityId : null) .entityName(ruleEngineMsg.getMetaData().getValue("entityName")) .actionType(actionType) .originatorUserId(UUID.fromString(ruleEngineMsg.getMetaData().getValue("userId"))) diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/RuleEngineComponentLifecycleEventTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/RuleEngineComponentLifecycleEventTriggerProcessor.java index 53b53ab0aa..d1e1e8820a 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/RuleEngineComponentLifecycleEventTriggerProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/RuleEngineComponentLifecycleEventTriggerProcessor.java @@ -76,6 +76,9 @@ public class RuleEngineComponentLifecycleEventTriggerProcessor implements Notifi .ruleChainName(triggerObject.getRuleChainName()) .componentId(triggerObject.getComponentId()) .componentName(triggerObject.getComponentName()) + .action(triggerObject.getEventType() == ComponentLifecycleEvent.STARTED ? "start" : + triggerObject.getEventType() == ComponentLifecycleEvent.UPDATED ? "update" : + triggerObject.getEventType() == ComponentLifecycleEvent.STOPPED ? "stop" : null) .eventType(triggerObject.getEventType()) .error(getErrorMsg(triggerObject.getError())) .build(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/TenantId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/TenantId.java index 4d7d95a18c..6f5fb09927 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/TenantId.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/TenantId.java @@ -45,9 +45,15 @@ public final class TenantId extends UUIDBased implements EntityId { super(id); } + @JsonIgnore + public boolean isSysTenantId() { + return this.equals(SYS_TENANT_ID); + } + @ApiModelProperty(position = 2, required = true, value = "string", example = "TENANT", allowableValues = "TENANT") @Override public EntityType getEntityType() { return EntityType.TENANT; } + } 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 a22f6a69d2..ab3e03191e 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 @@ -35,7 +35,6 @@ import org.thingsboard.server.common.data.notification.template.NotificationTemp import org.thingsboard.server.common.data.notification.template.NotificationTemplateConfig; import org.thingsboard.server.common.data.notification.template.WebDeliveryMethodNotificationTemplate; -import java.util.Collections; import java.util.EnumMap; import java.util.HashMap; import java.util.Map; @@ -114,9 +113,14 @@ public class NotificationProcessingContext { .map(config -> config.get("actionButtonConfig")).filter(JsonNode::isObject) .map(config -> (ObjectNode) config); if (buttonConfig.isPresent()) { + JsonNode text = buttonConfig.get().get("text"); + if (text != null && text.isTextual()) { + text = new TextNode(processTemplate(text.asText(), templateContext)); + buttonConfig.get().set("text", text); + } JsonNode link = buttonConfig.get().get("link"); if (link != null && link.isTextual()) { - link = new TextNode(processTemplate(link.asText(), templateContext, info != null ? info.getTemplateData() : Collections.emptyMap())); + link = new TextNode(processTemplate(link.asText(), templateContext).toLowerCase()); buttonConfig.get().set("link", link); } } @@ -124,14 +128,12 @@ public class NotificationProcessingContext { return template; } - private static String processTemplate(String template, Map... contexts) { + private static String processTemplate(String template, Map context) { if (template == null) return null; String result = template; - for (Map context : contexts) { - for (Map.Entry kv : context.entrySet()) { - String value = Strings.nullToEmpty(kv.getValue()); - result = result.replace("${" + kv.getKey() + '}', value); - } + for (Map.Entry kv : context.entrySet()) { + String value = Strings.nullToEmpty(kv.getValue()); + result = result.replace("${" + kv.getKey() + '}', value); } return result; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmAssignmentNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmAssignmentNotificationInfo.java index 1edd9621ce..c94122224f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmAssignmentNotificationInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmAssignmentNotificationInfo.java @@ -61,7 +61,7 @@ public class AlarmAssignmentNotificationInfo implements RuleOriginatedNotificati "userName", userName, "alarmType", alarmType, "alarmId", alarmId.toString(), - "alarmSeverity", alarmSeverity.toString(), + "alarmSeverity", alarmSeverity.name().toLowerCase(), "alarmStatus", alarmStatus.toString(), "alarmOriginatorEntityType", alarmOriginator.getEntityType().normalName(), "alarmOriginatorId", alarmOriginator.getId().toString(), diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmCommentNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmCommentNotificationInfo.java index 3afb5ff31f..d05b9066fd 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmCommentNotificationInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmCommentNotificationInfo.java @@ -54,7 +54,7 @@ public class AlarmCommentNotificationInfo implements RuleOriginatedNotificationI "userName", userName, "alarmType", alarmType, "alarmId", alarmId.toString(), - "alarmSeverity", alarmSeverity.toString(), + "alarmSeverity", alarmSeverity.name().toLowerCase(), "alarmStatus", alarmStatus.toString(), "alarmOriginatorEntityType", alarmOriginator.getEntityType().normalName(), "alarmOriginatorId", alarmOriginator.getId().toString(), diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmNotificationInfo.java index 449ea776af..163ac222d0 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmNotificationInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmNotificationInfo.java @@ -50,7 +50,7 @@ public class AlarmNotificationInfo implements RuleOriginatedNotificationInfo { "alarmType", alarmType, "action", action, "alarmId", alarmId.toString(), - "alarmSeverity", alarmSeverity.toString(), + "alarmSeverity", alarmSeverity.name().toLowerCase(), "alarmStatus", alarmStatus.toString(), "alarmOriginatorEntityType", alarmOriginator.getEntityType().normalName(), "alarmOriginatorName", alarmOriginatorName, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/DeviceInactivityNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/DeviceInactivityNotificationInfo.java index 6a4647ac62..bb77068cdf 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/DeviceInactivityNotificationInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/DeviceInactivityNotificationInfo.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.common.data.notification.info; -import com.google.common.base.Strings; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/RuleEngineComponentLifecycleEventNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/RuleEngineComponentLifecycleEventNotificationInfo.java index 80ceb8a332..3dc01c7ebd 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/RuleEngineComponentLifecycleEventNotificationInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/RuleEngineComponentLifecycleEventNotificationInfo.java @@ -37,6 +37,7 @@ public class RuleEngineComponentLifecycleEventNotificationInfo implements Notifi private String ruleChainName; private EntityId componentId; private String componentName; + private String action; private ComponentLifecycleEvent eventType; private String error; @@ -48,6 +49,7 @@ public class RuleEngineComponentLifecycleEventNotificationInfo implements Notifi "componentId", componentId.toString(), "componentType", componentId.getEntityType().normalName(), "componentName", componentName, + "action", action, "eventType", eventType.name().toLowerCase(), "error", error ); 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 949ab40ff7..fdcfb99da2 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 @@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.id.NotificationTemplateId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; +import org.thingsboard.server.common.data.validation.NoXss; import javax.validation.Valid; import javax.validation.constraints.AssertTrue; @@ -40,6 +41,7 @@ public class NotificationRule extends BaseData implements Ha private TenantId tenantId; @NotBlank + @NoXss private String name; @NotNull private NotificationTemplateId templateId; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmAssignmentNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmAssignmentNotificationRuleTriggerConfig.java index 2434e2ea0f..8bcb6387e2 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmAssignmentNotificationRuleTriggerConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmAssignmentNotificationRuleTriggerConfig.java @@ -19,6 +19,7 @@ import lombok.Data; import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; import org.thingsboard.server.common.data.alarm.AlarmSeverity; +import javax.validation.constraints.NotEmpty; import java.util.Set; @Data @@ -27,12 +28,16 @@ public class AlarmAssignmentNotificationRuleTriggerConfig implements Notificatio private Set alarmTypes; private Set alarmSeverities; private Set alarmStatuses; - private boolean notifyOnAssign; - private boolean notifyOnUnassign; + @NotEmpty + private Set notifyOn; @Override public NotificationRuleTriggerType getTriggerType() { return NotificationRuleTriggerType.ALARM_ASSIGNMENT; } + public enum Action { + ASSIGNED, UNASSIGNED + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EntitiesLimitNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EntitiesLimitNotificationRuleTriggerConfig.java index b799d4b81c..0382aaccf2 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EntitiesLimitNotificationRuleTriggerConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EntitiesLimitNotificationRuleTriggerConfig.java @@ -28,6 +28,8 @@ public class EntitiesLimitNotificationRuleTriggerConfig implements NotificationR @Max(1) private float threshold; // in percents, + // TODO: don't forget to create default notification configs + @Override public NotificationRuleTriggerType getTriggerType() { return NotificationRuleTriggerType.ENTITIES_LIMIT; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NewPlatformVersionNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NewPlatformVersionNotificationRuleTriggerConfig.java index 230be3b9d5..5473288f94 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NewPlatformVersionNotificationRuleTriggerConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NewPlatformVersionNotificationRuleTriggerConfig.java @@ -20,6 +20,8 @@ import lombok.Data; @Data public class NewPlatformVersionNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { + // TODO: don't forget to create default notification configs + @Override public NotificationRuleTriggerType getTriggerType() { return NotificationRuleTriggerType.NEW_PLATFORM_VERSION; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig.java index 6f271fbda7..130f6d4f61 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig.java @@ -24,6 +24,8 @@ import java.util.UUID; @Data public class RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { + private Set ruleChains; // if empty - all rule chains + private Set ruleChainEvents; // available options: STARTED, UPDATED, STOPPED. if empty - all events private boolean onlyRuleChainLifecycleFailures; @@ -31,8 +33,6 @@ public class RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig impl private Set ruleNodeEvents; // available options: STARTED, UPDATED, STOPPED. if empty - all events private boolean onlyRuleNodeLifecycleFailures; - private Set ruleChains; // if empty - all rule chains - @Override public NotificationRuleTriggerType getTriggerType() { return NotificationRuleTriggerType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT; 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 30bfa67183..2333062f65 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 @@ -23,6 +23,7 @@ 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; +import org.thingsboard.server.common.data.validation.NoXss; @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") @@ -33,6 +34,7 @@ import org.thingsboard.server.common.data.notification.targets.slack.SlackNotifi @Data public abstract class NotificationTargetConfig { + @NoXss private String description; @JsonIgnore 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 37a225c002..c43a606579 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 @@ -15,21 +15,56 @@ */ package org.thingsboard.server.dao.notification; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.AdminSettings; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; +import org.thingsboard.server.common.data.alarm.AlarmSeverity; +import org.thingsboard.server.common.data.id.NotificationTargetId; +import org.thingsboard.server.common.data.id.NotificationTemplateId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; +import org.thingsboard.server.common.data.notification.NotificationType; +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.NotificationRuleConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.AlarmAssignmentNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.AlarmCommentNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.AlarmNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.AlarmNotificationRuleTriggerConfig.AlarmAction; +import org.thingsboard.server.common.data.notification.rule.trigger.DeviceInactivityNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.EntityActionNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; +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.ActionTargetUserFilter; 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.TenantAdministratorsFilter; +import org.thingsboard.server.common.data.notification.targets.platform.UsersFilter; 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.WebDeliveryMethodNotificationTemplate; +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.dao.settings.AdminSettingsService; import java.util.Collections; +import java.util.List; +import java.util.Map; import java.util.Optional; +import java.util.Set; + +import static org.thingsboard.common.util.JacksonUtil.newObjectNode; +import static org.thingsboard.server.dao.DaoUtil.toUUIDs; @Service @RequiredArgsConstructor @@ -66,39 +101,189 @@ public class DefaultNotificationSettingsService implements NotificationSettingsS }); } + // TODO: validate that wasn't already created (catch exception unique name) + // FIXME: user that does not have access to some entity, gets notifications about this entity - check permissions? + @Transactional(propagation = Propagation.NOT_SUPPORTED) // so that parent transaction is not aborted on method failure @Override public void createDefaultNotificationConfigs(TenantId tenantId) { - if (tenantId.equals(TenantId.SYS_TENANT_ID)) { - // TODO + NotificationTarget allUsers = createTarget(tenantId, "All users", new AllUsersFilter(), + tenantId.isSysTenantId() ? "All platform users" : "All users in scope of the tenant"); + NotificationTarget tenantAdmins = createTarget(tenantId, "Tenant administrators", new TenantAdministratorsFilter(), + tenantId.isSysTenantId() ? "All tenant administrators" : "Tenant administrators"); + + if (tenantId.isSysTenantId()) { + createTemplate(tenantId, "Maintenance work notification", NotificationType.GENERAL, + "Infrastructure maintenance", + "Maintenance work is scheduled for tomorrow (7:00 a.m. - 9:00 a.m. UTC). You may face major service interruptions, sorry for the inconvenience"); return; } - NotificationTarget allUsersTarget = new NotificationTarget(); - allUsersTarget.setTenantId(tenantId); - allUsersTarget.setName("All users"); + NotificationTarget originatorEntityOwnerUsers = createTarget(tenantId, "Users of rule trigger entity's owner", new OriginatorEntityOwnerUsersFilter(), + "Customer users in case trigger entity (e.g. alarm) has customer, tenant admins otherwise"); + NotificationTarget actionTargetUser = createTarget(tenantId, "Action target", new ActionTargetUserFilter(), + "If rule trigger is an action that targets some user (e.g. alarm assigned to user) - this user"); + + NotificationTemplate alarmNotificationTemplate = createTemplate(tenantId, "Alarm notification", NotificationType.ALARM, + "Alarm '${alarmType}' - ${action}", + "Severity: ${alarmSeverity}, originator: ${alarmOriginatorEntityType} '${alarmOriginatorName}'", + "notifications", null, null); + AlarmNotificationRuleTriggerConfig alarmRuleTriggerConfig = new AlarmNotificationRuleTriggerConfig(); + alarmRuleTriggerConfig.setAlarmTypes(null); + alarmRuleTriggerConfig.setAlarmSeverities(Set.of(AlarmSeverity.MAJOR, AlarmSeverity.CRITICAL)); + alarmRuleTriggerConfig.setNotifyOn(Set.of(AlarmAction.CREATED, AlarmAction.SEVERITY_CHANGED, AlarmAction.ACKNOWLEDGED, AlarmAction.CLEARED)); + createRule(tenantId, "Major or critical alarm", alarmNotificationTemplate.getId(), alarmRuleTriggerConfig, + List.of(originatorEntityOwnerUsers.getId()), "Send notification to tenant admins or customer users " + + "when any major or critical alarm is created, updated or cleared"); + + NotificationTemplate deviceActionNotificationTemplate = createTemplate(tenantId, "Device action notification", NotificationType.ENTITY_ACTION, + "${entityType} was ${actionType}", + "${entityType} '${entityName}' was ${actionType} by user ${originatorUserName}", + "info","Go to Device", "/devices/${entityId}"); + EntityActionNotificationRuleTriggerConfig deviceActionRuleTriggerConfig = new EntityActionNotificationRuleTriggerConfig(); + deviceActionRuleTriggerConfig.setEntityType(EntityType.DEVICE); + deviceActionRuleTriggerConfig.setCreated(true); + deviceActionRuleTriggerConfig.setUpdated(false); + deviceActionRuleTriggerConfig.setDeleted(true); + createRule(tenantId, "Device created or deleted", deviceActionNotificationTemplate.getId(), deviceActionRuleTriggerConfig, + List.of(originatorEntityOwnerUsers.getId()), "Send notification to tenant admins or customer users " + + "when device is created or deleted"); + + NotificationTemplate deviceInactivityNotificationTemplate = createTemplate(tenantId, "Device inactivity notification", NotificationType.DEVICE_INACTIVITY, + "Device '${deviceName}' inactive", + "Device '${deviceName}' with type '${deviceType}' became inactive", + "info", "Go to Device", "/devices/${deviceId}"); + DeviceInactivityNotificationRuleTriggerConfig deviceInactivityRuleTriggerConfig = new DeviceInactivityNotificationRuleTriggerConfig(); + deviceInactivityRuleTriggerConfig.setDevices(null); + deviceInactivityRuleTriggerConfig.setDeviceProfiles(null); + createRule(tenantId, "Device inactivity", deviceInactivityNotificationTemplate.getId(), deviceInactivityRuleTriggerConfig, + List.of(originatorEntityOwnerUsers.getId()), "Send notification to tenant admins or customer users " + + "when any device became inactive"); + + NotificationTemplate alarmCommentNotificationTemplate = createTemplate(tenantId, "Alarm comment notification", NotificationType.ALARM_COMMENT, + "Comment on '${alarmType}' alarm", + "${userName} ${action} comment: ${comment}", + "people", null, null); + AlarmCommentNotificationRuleTriggerConfig alarmCommentRuleTriggerConfig = new AlarmCommentNotificationRuleTriggerConfig(); + alarmCommentRuleTriggerConfig.setAlarmTypes(null); + alarmCommentRuleTriggerConfig.setAlarmSeverities(null); + alarmCommentRuleTriggerConfig.setAlarmStatuses(Set.of(AlarmSearchStatus.ACTIVE)); + alarmCommentRuleTriggerConfig.setOnlyUserComments(true); + alarmCommentRuleTriggerConfig.setNotifyOnCommentUpdate(false); + createRule(tenantId, "Comment on active alarm", alarmCommentNotificationTemplate.getId(), alarmCommentRuleTriggerConfig, + List.of(originatorEntityOwnerUsers.getId()), "Send notification to tenant admins or customer users " + + "when comment is added by user on active alarm"); + + NotificationTemplate alarmAssignedNotificationTemplate = createTemplate(tenantId, "Alarm assigned notification", NotificationType.ALARM_ASSIGNMENT, + "Alarm '${alarmType}' (${alarmSeverity}) was assigned to user", + "${userName} assigned alarm on ${alarmOriginatorEntityType} '${alarmOriginatorName}' to ${assigneeEmail}", + "person", null, null); + AlarmAssignmentNotificationRuleTriggerConfig alarmAssignmentRuleTriggerConfig = new AlarmAssignmentNotificationRuleTriggerConfig(); + alarmAssignmentRuleTriggerConfig.setAlarmTypes(null); + alarmAssignmentRuleTriggerConfig.setAlarmSeverities(null); + alarmAssignmentRuleTriggerConfig.setAlarmStatuses(null); + alarmAssignmentRuleTriggerConfig.setNotifyOn(Set.of(AlarmAssignmentNotificationRuleTriggerConfig.Action.ASSIGNED)); + createRule(tenantId, "Alarm assigned", alarmAssignedNotificationTemplate.getId(), alarmAssignmentRuleTriggerConfig, + List.of(actionTargetUser.getId()), "Send notification to user when any alarm was assigned to him"); + + NotificationTemplate ruleEngineComponentLifecycleFailureNotificationTemplate = createTemplate(tenantId, "Rule chain/node lifecycle failure notification", NotificationType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT, + "${componentType} '${componentName}' failed to ${action}", + "Rule chain '${ruleChainName}' - ${action} failure:
${error}", + "warning", "Go to Rule chain", "/ruleChains/${ruleChainId}"); + RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig ruleEngineComponentLifecycleEventRuleTriggerConfig = new RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig(); + ruleEngineComponentLifecycleEventRuleTriggerConfig.setRuleChains(null); + ruleEngineComponentLifecycleEventRuleTriggerConfig.setRuleChainEvents(Set.of(ComponentLifecycleEvent.STARTED, ComponentLifecycleEvent.UPDATED, ComponentLifecycleEvent.STOPPED)); + ruleEngineComponentLifecycleEventRuleTriggerConfig.setOnlyRuleChainLifecycleFailures(true); + ruleEngineComponentLifecycleEventRuleTriggerConfig.setTrackRuleNodeEvents(true); + ruleEngineComponentLifecycleEventRuleTriggerConfig.setRuleNodeEvents(Set.of(ComponentLifecycleEvent.STARTED, ComponentLifecycleEvent.UPDATED, ComponentLifecycleEvent.STOPPED)); + ruleEngineComponentLifecycleEventRuleTriggerConfig.setOnlyRuleNodeLifecycleFailures(true); + createRule(tenantId, "Rule chain/node lifecycle failure", ruleEngineComponentLifecycleFailureNotificationTemplate.getId(), + ruleEngineComponentLifecycleEventRuleTriggerConfig, List.of(tenantAdmins.getId()), + "Send notification to tenant admins when any Rule chain or Rule node failed to start, update or stop"); + } + + private NotificationTarget createTarget(TenantId tenantId, String name, UsersFilter filter, String description) { + NotificationTarget target = new NotificationTarget(); + target.setTenantId(tenantId); + target.setName(name); + PlatformUsersNotificationTargetConfig targetConfig = new PlatformUsersNotificationTargetConfig(); - targetConfig.setUsersFilter(new AllUsersFilter()); - targetConfig.setDescription("All users in scope of tenant"); - allUsersTarget.setConfiguration(targetConfig); - allUsersTarget = notificationTargetService.saveNotificationTarget(tenantId, allUsersTarget); - - NotificationTarget originatorEntityOwnerUsers = new NotificationTarget(); - originatorEntityOwnerUsers.setTenantId(tenantId); - originatorEntityOwnerUsers.setName("Users of rule trigger entity's owner"); - targetConfig.setUsersFilter(new OriginatorEntityOwnerUsersFilter()); - targetConfig.setDescription("For usage with notification rules. For example, if alarm trigger type is chosen, " + - "notifications will be sent to alarm owner's users, e.g. it's customer's users"); - originatorEntityOwnerUsers.setConfiguration(targetConfig); - originatorEntityOwnerUsers = notificationTargetService.saveNotificationTarget(tenantId, originatorEntityOwnerUsers); - - NotificationTemplate alarmNotificationTemplate = new NotificationTemplate(); + targetConfig.setUsersFilter(filter); + targetConfig.setDescription(description); + target.setConfiguration(targetConfig); + return notificationTargetService.saveNotificationTarget(tenantId, target); + } + + private NotificationTemplate createTemplate(TenantId tenantId, String name, NotificationType notificationType, + String subjectTemplate, String textTemplate) { + return createTemplate(tenantId, name, notificationType, subjectTemplate, textTemplate, null, null, null); + } + + private NotificationTemplate createTemplate(TenantId tenantId, String name, NotificationType notificationType, + String subjectTemplate, String textTemplate, + String icon, String button, String link) { + NotificationTemplate template = new NotificationTemplate(); + template.setTenantId(tenantId); + template.setName(name); + template.setNotificationType(notificationType); + NotificationTemplateConfig templateConfig = new NotificationTemplateConfig(); + templateConfig.setNotificationSubject(subjectTemplate); + templateConfig.setDefaultTextTemplate(textTemplate); + + WebDeliveryMethodNotificationTemplate webTemplate = new WebDeliveryMethodNotificationTemplate(); + ObjectNode additionalConfig = newObjectNode(); + ObjectNode iconConfig = additionalConfig.set("icon", newObjectNode()); + ObjectNode buttonConfig = additionalConfig.set("actionButtonConfig", newObjectNode()); + if (icon != null) { + iconConfig.put("enabled", true) + .put("icon", icon) + .put("color", "#757575"); + } else { + iconConfig.put("enabled", false); + } + if (button != null) { + buttonConfig.put("enabled", true) + .put("text", button) + .put("linkType", "LINK") + .put("link", link); + } else { + buttonConfig.put("enabled", false); + } + webTemplate.setAdditionalConfig(additionalConfig); + templateConfig.setDeliveryMethodsTemplates(Map.of( + NotificationDeliveryMethod.WEB, webTemplate + )); /* - * TODO: - * rule chain start failure - * alarm + * {"icon":{"enabled":true,"icon":"info","color":"#757575"},"actionButtonConfig":{"enabled":true,"text":"Go to device","linkType":"LINK","link":"/devices/aa4a3660-c3e9-11ed-934f-bdfa9bfa7568"}} * */ + template.setConfiguration(templateConfig); + return notificationTemplateService.saveNotificationTemplate(tenantId, template); + } + private NotificationRule createRule(TenantId tenantId, String name, NotificationTemplateId templateId, + NotificationRuleTriggerConfig triggerConfig, List targets, + String description) { + NotificationRule rule = new NotificationRule(); + rule.setTenantId(tenantId); + rule.setName(name); + rule.setTemplateId(templateId); + rule.setTriggerType(triggerConfig.getTriggerType()); + rule.setTriggerConfig(triggerConfig); + if (rule.getTriggerType() == NotificationRuleTriggerType.ALARM) { + EscalatedNotificationRuleRecipientsConfig recipientsConfig = new EscalatedNotificationRuleRecipientsConfig(); + recipientsConfig.setTriggerType(rule.getTriggerType()); + recipientsConfig.setEscalationTable(Map.of(0, toUUIDs(targets))); + rule.setRecipientsConfig(recipientsConfig); + } else { + DefaultNotificationRuleRecipientsConfig recipientsConfig = new DefaultNotificationRuleRecipientsConfig(); + recipientsConfig.setTriggerType(rule.getTriggerType()); + recipientsConfig.setTargets(toUUIDs(targets)); + rule.setRecipientsConfig(recipientsConfig); + } + NotificationRuleConfig additionalConfig = new NotificationRuleConfig(); + additionalConfig.setDescription(description); + rule.setAdditionalConfig(additionalConfig); + return notificationRuleService.saveNotificationRule(tenantId, rule); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java index 428e886694..bf45034989 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java @@ -195,7 +195,11 @@ public class TenantServiceImpl extends AbstractCachedEntityService