Merge remote-tracking branch 'klimov/feature/notification-system' into feature/notification-system

This commit is contained in:
Vladyslav_Prykhodko 2023-03-14 12:08:04 +02:00
commit 10e34cc1ad
15 changed files with 100 additions and 32 deletions

View File

@ -19,18 +19,27 @@ import org.springframework.stereotype.Service;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmComment; import org.thingsboard.server.common.data.alarm.AlarmComment;
import org.thingsboard.server.common.data.alarm.AlarmStatusFilter;
import org.thingsboard.server.common.data.notification.info.AlarmCommentNotificationInfo; import org.thingsboard.server.common.data.notification.info.AlarmCommentNotificationInfo;
import org.thingsboard.server.common.data.notification.info.NotificationInfo; import org.thingsboard.server.common.data.notification.info.NotificationInfo;
import org.thingsboard.server.common.data.notification.rule.trigger.AlarmCommentNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.AlarmCommentNotificationRuleTriggerConfig;
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
import static org.apache.commons.collections.CollectionUtils.isEmpty;
@Service @Service
public class AlarmCommentTriggerProcessor implements NotificationRuleTriggerProcessor<TbMsg, AlarmCommentNotificationRuleTriggerConfig> { public class AlarmCommentTriggerProcessor implements NotificationRuleTriggerProcessor<TbMsg, AlarmCommentNotificationRuleTriggerConfig> {
@Override @Override
public boolean matchesFilter(TbMsg ruleEngineMsg, AlarmCommentNotificationRuleTriggerConfig triggerConfig) { public boolean matchesFilter(TbMsg ruleEngineMsg, AlarmCommentNotificationRuleTriggerConfig triggerConfig) {
return ruleEngineMsg.getMetaData().getValue("comment") != null; if (ruleEngineMsg.getMetaData().getValue("comment") == null) {
return false;
}
Alarm alarm = JacksonUtil.fromString(ruleEngineMsg.getData(), Alarm.class);
return (isEmpty(triggerConfig.getAlarmTypes()) || triggerConfig.getAlarmTypes().contains(alarm.getType())) &&
(isEmpty(triggerConfig.getAlarmSeverities()) || triggerConfig.getAlarmSeverities().contains(alarm.getSeverity())) &&
(isEmpty(triggerConfig.getAlarmStatuses()) || AlarmStatusFilter.from(triggerConfig.getAlarmStatuses()).matches(alarm));
} }
@Override @Override
@ -39,8 +48,11 @@ public class AlarmCommentTriggerProcessor implements NotificationRuleTriggerProc
Alarm alarm = JacksonUtil.fromString(ruleEngineMsg.getData(), Alarm.class); Alarm alarm = JacksonUtil.fromString(ruleEngineMsg.getData(), Alarm.class);
return AlarmCommentNotificationInfo.builder() return AlarmCommentNotificationInfo.builder()
.comment(comment.getComment().get("text").asText()) .comment(comment.getComment().get("text").asText())
.alarmId(alarm.getUuidId())
.alarmType(alarm.getType()) .alarmType(alarm.getType())
.alarmId(comment.getAlarmId().getId()) .alarmOriginator(alarm.getOriginator())
.alarmSeverity(alarm.getSeverity())
.alarmStatus(alarm.getStatus())
.build(); .build();
} }

View File

@ -17,9 +17,9 @@ package org.thingsboard.server.service.notification.rule.trigger;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmStatusFilter;
import org.thingsboard.server.common.data.notification.info.AlarmNotificationInfo; import org.thingsboard.server.common.data.notification.info.AlarmNotificationInfo;
import org.thingsboard.server.common.data.notification.info.NotificationInfo; import org.thingsboard.server.common.data.notification.info.NotificationInfo;
import org.thingsboard.server.common.data.notification.rule.trigger.AlarmNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.AlarmNotificationRuleTriggerConfig;
@ -27,14 +27,17 @@ import org.thingsboard.server.common.data.notification.rule.trigger.AlarmNotific
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType;
import org.thingsboard.server.service.notification.rule.trigger.AlarmTriggerProcessor.AlarmTriggerObject; import org.thingsboard.server.service.notification.rule.trigger.AlarmTriggerProcessor.AlarmTriggerObject;
import static org.apache.commons.collections.CollectionUtils.isEmpty;
import static org.apache.commons.collections.CollectionUtils.isNotEmpty;
@Service @Service
public class AlarmTriggerProcessor implements NotificationRuleTriggerProcessor<AlarmTriggerObject, AlarmNotificationRuleTriggerConfig> { public class AlarmTriggerProcessor implements NotificationRuleTriggerProcessor<AlarmTriggerObject, AlarmNotificationRuleTriggerConfig> {
@Override @Override
public boolean matchesFilter(AlarmTriggerObject triggerObject, AlarmNotificationRuleTriggerConfig triggerConfig) { public boolean matchesFilter(AlarmTriggerObject triggerObject, AlarmNotificationRuleTriggerConfig triggerConfig) {
Alarm alarm = triggerObject.getAlarm(); Alarm alarm = triggerObject.getAlarm();
return (CollectionUtils.isEmpty(triggerConfig.getAlarmTypes()) || triggerConfig.getAlarmTypes().contains(alarm.getType())) && return (isEmpty(triggerConfig.getAlarmTypes()) || triggerConfig.getAlarmTypes().contains(alarm.getType())) &&
(CollectionUtils.isEmpty(triggerConfig.getAlarmSeverities()) || triggerConfig.getAlarmSeverities().contains(alarm.getSeverity())); (isEmpty(triggerConfig.getAlarmSeverities()) || triggerConfig.getAlarmSeverities().contains(alarm.getSeverity()));
} }
@Override @Override
@ -45,8 +48,8 @@ public class AlarmTriggerProcessor implements NotificationRuleTriggerProcessor<A
Alarm alarm = triggerObject.getAlarm(); Alarm alarm = triggerObject.getAlarm();
ClearRule clearRule = triggerConfig.getClearRule(); ClearRule clearRule = triggerConfig.getClearRule();
if (clearRule != null) { if (clearRule != null) {
if (clearRule.getAlarmStatus() != null) { if (isNotEmpty(clearRule.getAlarmStatuses())) {
return clearRule.getAlarmStatus().equals(alarm.getStatus()); return AlarmStatusFilter.from(clearRule.getAlarmStatuses()).matches(alarm);
} }
} }
return false; return false;

View File

@ -59,8 +59,7 @@ public class EntityActionTriggerProcessor implements NotificationRuleTriggerProc
msgType.equals(DataConstants.ENTITY_UPDATED) ? ActionType.UPDATED : msgType.equals(DataConstants.ENTITY_UPDATED) ? ActionType.UPDATED :
msgType.equals(DataConstants.ENTITY_DELETED) ? ActionType.DELETED : null; msgType.equals(DataConstants.ENTITY_DELETED) ? ActionType.DELETED : null;
return EntityActionNotificationInfo.builder() return EntityActionNotificationInfo.builder()
.entityType(entityId.getEntityType()) .entityId(entityId)
.entityId(entityId.getId())
.entityName(ruleEngineMsg.getMetaData().getValue("entityName")) .entityName(ruleEngineMsg.getMetaData().getValue("entityName"))
.actionType(actionType) .actionType(actionType)
.originatorUserId(UUID.fromString(ruleEngineMsg.getMetaData().getValue("userId"))) .originatorUserId(UUID.fromString(ruleEngineMsg.getMetaData().getValue("userId")))

View File

@ -32,6 +32,7 @@ import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.alarm.AlarmStatus; import org.thingsboard.server.common.data.alarm.AlarmStatus;
import org.thingsboard.server.common.data.device.profile.AlarmCondition; import org.thingsboard.server.common.data.device.profile.AlarmCondition;
@ -234,7 +235,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
}); });
clients.values().forEach(wsClient -> wsClient.registerWaitForUpdate()); clients.values().forEach(wsClient -> wsClient.registerWaitForUpdate());
alarmSubscriptionService.ackAlarm(tenantId, alarm.getId(), System.currentTimeMillis()); alarmSubscriptionService.acknowledgeAlarm(tenantId, alarm.getId(), System.currentTimeMillis());
AlarmStatus expectedStatus = AlarmStatus.ACTIVE_ACK; AlarmStatus expectedStatus = AlarmStatus.ACTIVE_ACK;
AlarmSeverity expectedSeverity = AlarmSeverity.CRITICAL; AlarmSeverity expectedSeverity = AlarmSeverity.CRITICAL;
clients.values().forEach(wsClient -> { clients.values().forEach(wsClient -> {
@ -270,7 +271,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
triggerConfig.setAlarmSeverities(null); triggerConfig.setAlarmSeverities(null);
AlarmNotificationRuleTriggerConfig.ClearRule clearRule = new AlarmNotificationRuleTriggerConfig.ClearRule(); AlarmNotificationRuleTriggerConfig.ClearRule clearRule = new AlarmNotificationRuleTriggerConfig.ClearRule();
clearRule.setAlarmStatus(AlarmStatus.CLEARED_UNACK); clearRule.setAlarmStatuses(Set.of(AlarmSearchStatus.CLEARED, AlarmSearchStatus.UNACK));
triggerConfig.setClearRule(clearRule); triggerConfig.setClearRule(clearRule);
notificationRule.setTriggerConfig(triggerConfig); notificationRule.setTriggerConfig(triggerConfig);
@ -308,7 +309,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
assertThat(scheduledNotificationRequest).extracting(NotificationRequest::getInfo).isEqualTo(notification.getInfo()); assertThat(scheduledNotificationRequest).extracting(NotificationRequest::getInfo).isEqualTo(notification.getInfo());
getWsClient().registerWaitForUpdate(); getWsClient().registerWaitForUpdate();
alarmSubscriptionService.clearAlarm(tenantId, alarm.getId(), null, System.currentTimeMillis()); alarmSubscriptionService.clearAlarm(tenantId, alarm.getId(), System.currentTimeMillis(), null);
getWsClient().waitForUpdate(true); getWsClient().waitForUpdate(true);
notification = getWsClient().getLastDataUpdate().getNotifications().iterator().next(); notification = getWsClient().getLastDataUpdate().getNotifications().iterator().next();
assertThat(notification.getSubject()).isEqualTo("CRITICAL alarm '" + alarmType + "' is CLEARED_UNACK"); assertThat(notification.getSubject()).isEqualTo("CRITICAL alarm '" + alarmType + "' is CLEARED_UNACK");

View File

@ -15,7 +15,7 @@
*/ */
package org.thingsboard.server.common.data.alarm; package org.thingsboard.server.common.data.alarm;
import java.util.List; import java.util.Collection;
import java.util.Optional; import java.util.Optional;
public class AlarmStatusFilter { public class AlarmStatusFilter {
@ -94,19 +94,19 @@ public class AlarmStatusFilter {
} }
public static AlarmStatusFilter fromList(List<AlarmSearchStatus> list) { public static AlarmStatusFilter from(Collection<AlarmSearchStatus> statuses) {
if (list == null || list.isEmpty() || list.contains(AlarmSearchStatus.ANY)) { if (statuses == null || statuses.isEmpty() || statuses.contains(AlarmSearchStatus.ANY)) {
return EMPTY; return EMPTY;
} }
boolean clearFilter = list.contains(AlarmSearchStatus.CLEARED); boolean clearFilter = statuses.contains(AlarmSearchStatus.CLEARED);
boolean activeFilter = list.contains(AlarmSearchStatus.ACTIVE); boolean activeFilter = statuses.contains(AlarmSearchStatus.ACTIVE);
Optional<Boolean> clear = Optional.empty(); Optional<Boolean> clear = Optional.empty();
if (clearFilter && !activeFilter || !clearFilter && activeFilter) { if (clearFilter && !activeFilter || !clearFilter && activeFilter) {
clear = Optional.of(clearFilter); clear = Optional.of(clearFilter);
} }
boolean ackFilter = list.contains(AlarmSearchStatus.ACK); boolean ackFilter = statuses.contains(AlarmSearchStatus.ACK);
boolean unackFilter = list.contains(AlarmSearchStatus.UNACK); boolean unackFilter = statuses.contains(AlarmSearchStatus.UNACK);
Optional<Boolean> ack = Optional.empty(); Optional<Boolean> ack = Optional.empty();
if (ackFilter && !unackFilter || !ackFilter && unackFilter) { if (ackFilter && !unackFilter || !ackFilter && unackFilter) {
ack = Optional.of(ackFilter); ack = Optional.of(ackFilter);
@ -114,5 +114,9 @@ public class AlarmStatusFilter {
return new AlarmStatusFilter(clear, ack); return new AlarmStatusFilter(clear, ack);
} }
public boolean matches(Alarm alarm) {
return ackFilter.map(ackFilter -> ackFilter.equals(alarm.isAcknowledged())).orElse(true) &&
clearFilter.map(clearedFilter -> clearedFilter.equals(alarm.isCleared())).orElse(true);
}
} }

View File

@ -19,6 +19,9 @@ import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.alarm.AlarmStatus;
import org.thingsboard.server.common.data.id.EntityId;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
@ -32,14 +35,26 @@ public class AlarmCommentNotificationInfo implements NotificationInfo {
private String comment; private String comment;
private String alarmType; private String alarmType;
private UUID alarmId; private UUID alarmId;
private EntityId alarmOriginator;
private AlarmSeverity alarmSeverity;
private AlarmStatus alarmStatus;
@Override @Override
public Map<String, String> getTemplateData() { public Map<String, String> getTemplateData() {
return Map.of( return Map.of(
"comment", comment, "comment", comment,
"alarmType", alarmType, "alarmType", alarmType,
"alarmId", alarmId.toString() "alarmId", alarmId.toString(),
"alarmSeverity", alarmSeverity.toString(),
"alarmStatus", alarmStatus.toString(),
"alarmOriginatorEntityType", alarmOriginator.getEntityType().toString(),
"alarmOriginatorId", alarmOriginator.getId().toString()
); );
} }
@Override
public EntityId getStateEntityId() {
return alarmOriginator;
}
} }

View File

@ -57,4 +57,9 @@ public class AlarmNotificationInfo implements RuleOriginatedNotificationInfo {
); );
} }
@Override
public EntityId getStateEntityId() {
return alarmOriginator;
}
} }

View File

@ -20,6 +20,8 @@ import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityId;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
@ -49,4 +51,9 @@ public class DeviceInactivityNotificationInfo implements RuleOriginatedNotificat
); );
} }
@Override
public EntityId getStateEntityId() {
return new DeviceId(deviceId);
}
} }

View File

@ -19,9 +19,9 @@ import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
@ -32,8 +32,7 @@ import java.util.UUID;
@Builder @Builder
public class EntityActionNotificationInfo implements RuleOriginatedNotificationInfo { public class EntityActionNotificationInfo implements RuleOriginatedNotificationInfo {
private EntityType entityType; private EntityId entityId;
private UUID entityId;
private String entityName; private String entityName;
private ActionType actionType; private ActionType actionType;
private UUID originatorUserId; private UUID originatorUserId;
@ -48,7 +47,7 @@ public class EntityActionNotificationInfo implements RuleOriginatedNotificationI
@Override @Override
public Map<String, String> getTemplateData() { public Map<String, String> getTemplateData() {
return Map.of( return Map.of(
"entityType", entityType.name(), "entityType", entityId.getEntityType().name(),
"entityId", entityId.toString(), "entityId", entityId.toString(),
"entityName", entityName, "entityName", entityName,
"actionType", actionType.name().toLowerCase(), "actionType", actionType.name().toLowerCase(),
@ -57,4 +56,9 @@ public class EntityActionNotificationInfo implements RuleOriginatedNotificationI
); );
} }
@Override
public EntityId getStateEntityId() {
return entityId;
}
} }

View File

@ -18,6 +18,7 @@ package org.thingsboard.server.common.data.notification.info;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.thingsboard.server.common.data.id.EntityId;
import java.util.Map; import java.util.Map;
@ -28,4 +29,8 @@ public interface NotificationInfo {
@JsonIgnore @JsonIgnore
Map<String, String> getTemplateData(); Map<String, String> getTemplateData();
default EntityId getStateEntityId() {
return null;
}
} }

View File

@ -53,4 +53,9 @@ public class RuleEngineComponentLifecycleEventNotificationInfo implements Notifi
); );
} }
@Override
public EntityId getStateEntityId() {
return ruleChainId;
}
} }

View File

@ -43,4 +43,9 @@ public class RuleEngineOriginatedNotificationInfo implements NotificationInfo {
return templateData; return templateData;
} }
@Override
public EntityId getStateEntityId() {
return msgOriginator;
}
} }

View File

@ -16,10 +16,18 @@
package org.thingsboard.server.common.data.notification.rule.trigger; package org.thingsboard.server.common.data.notification.rule.trigger;
import lombok.Data; import lombok.Data;
import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import java.util.Set;
@Data @Data
public class AlarmCommentNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { public class AlarmCommentNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig {
private Set<String> alarmTypes;
private Set<AlarmSeverity> alarmSeverities;
private Set<AlarmSearchStatus> alarmStatuses;
@Override @Override
public NotificationRuleTriggerType getTriggerType() { public NotificationRuleTriggerType getTriggerType() {
return NotificationRuleTriggerType.ALARM_COMMENT; return NotificationRuleTriggerType.ALARM_COMMENT;

View File

@ -16,8 +16,8 @@
package org.thingsboard.server.common.data.notification.rule.trigger; package org.thingsboard.server.common.data.notification.rule.trigger;
import lombok.Data; import lombok.Data;
import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.alarm.AlarmStatus;
import java.util.Set; import java.util.Set;
@ -35,7 +35,7 @@ public class AlarmNotificationRuleTriggerConfig implements NotificationRuleTrigg
@Data @Data
public static class ClearRule { public static class ClearRule {
private AlarmStatus alarmStatus; private Set<AlarmSearchStatus> alarmStatuses;
} }
} }

View File

@ -19,12 +19,9 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
import org.springframework.transaction.support.TransactionTemplate; import org.springframework.transaction.support.TransactionTemplate;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.alarm.AlarmStatus;
import org.thingsboard.server.common.data.alarm.AlarmStatusFilter; import org.thingsboard.server.common.data.alarm.AlarmStatusFilter;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
@ -41,11 +38,9 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Repository @Repository
@ -248,7 +243,7 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
wherePart.append("a.severity in (:alarmSeverities)"); wherePart.append("a.severity in (:alarmSeverities)");
} }
AlarmStatusFilter asf = AlarmStatusFilter.fromList(pageLink.getStatusList()); AlarmStatusFilter asf = AlarmStatusFilter.from(pageLink.getStatusList());
if (asf.hasAnyFilter()) { if (asf.hasAnyFilter()) {
if (asf.hasAckFilter()) { if (asf.hasAckFilter()) {
addAndIfNeeded(wherePart, addAnd); addAndIfNeeded(wherePart, addAnd);