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.server.common.data.alarm.Alarm;
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.NotificationInfo;
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.msg.TbMsg;
import static org.apache.commons.collections.CollectionUtils.isEmpty;
@Service
public class AlarmCommentTriggerProcessor implements NotificationRuleTriggerProcessor<TbMsg, AlarmCommentNotificationRuleTriggerConfig> {
@Override
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
@ -39,8 +48,11 @@ public class AlarmCommentTriggerProcessor implements NotificationRuleTriggerProc
Alarm alarm = JacksonUtil.fromString(ruleEngineMsg.getData(), Alarm.class);
return AlarmCommentNotificationInfo.builder()
.comment(comment.getComment().get("text").asText())
.alarmId(alarm.getUuidId())
.alarmType(alarm.getType())
.alarmId(comment.getAlarmId().getId())
.alarmOriginator(alarm.getOriginator())
.alarmSeverity(alarm.getSeverity())
.alarmStatus(alarm.getStatus())
.build();
}

View File

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

View File

@ -59,8 +59,7 @@ public class EntityActionTriggerProcessor implements NotificationRuleTriggerProc
msgType.equals(DataConstants.ENTITY_UPDATED) ? ActionType.UPDATED :
msgType.equals(DataConstants.ENTITY_DELETED) ? ActionType.DELETED : null;
return EntityActionNotificationInfo.builder()
.entityType(entityId.getEntityType())
.entityId(entityId.getId())
.entityId(entityId)
.entityName(ruleEngineMsg.getMetaData().getValue("entityName"))
.actionType(actionType)
.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.User;
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.AlarmStatus;
import org.thingsboard.server.common.data.device.profile.AlarmCondition;
@ -234,7 +235,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
});
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;
AlarmSeverity expectedSeverity = AlarmSeverity.CRITICAL;
clients.values().forEach(wsClient -> {
@ -270,7 +271,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
triggerConfig.setAlarmSeverities(null);
AlarmNotificationRuleTriggerConfig.ClearRule clearRule = new AlarmNotificationRuleTriggerConfig.ClearRule();
clearRule.setAlarmStatus(AlarmStatus.CLEARED_UNACK);
clearRule.setAlarmStatuses(Set.of(AlarmSearchStatus.CLEARED, AlarmSearchStatus.UNACK));
triggerConfig.setClearRule(clearRule);
notificationRule.setTriggerConfig(triggerConfig);
@ -308,7 +309,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
assertThat(scheduledNotificationRequest).extracting(NotificationRequest::getInfo).isEqualTo(notification.getInfo());
getWsClient().registerWaitForUpdate();
alarmSubscriptionService.clearAlarm(tenantId, alarm.getId(), null, System.currentTimeMillis());
alarmSubscriptionService.clearAlarm(tenantId, alarm.getId(), System.currentTimeMillis(), null);
getWsClient().waitForUpdate(true);
notification = getWsClient().getLastDataUpdate().getNotifications().iterator().next();
assertThat(notification.getSubject()).isEqualTo("CRITICAL alarm '" + alarmType + "' is CLEARED_UNACK");

View File

@ -15,7 +15,7 @@
*/
package org.thingsboard.server.common.data.alarm;
import java.util.List;
import java.util.Collection;
import java.util.Optional;
public class AlarmStatusFilter {
@ -94,19 +94,19 @@ public class AlarmStatusFilter {
}
public static AlarmStatusFilter fromList(List<AlarmSearchStatus> list) {
if (list == null || list.isEmpty() || list.contains(AlarmSearchStatus.ANY)) {
public static AlarmStatusFilter from(Collection<AlarmSearchStatus> statuses) {
if (statuses == null || statuses.isEmpty() || statuses.contains(AlarmSearchStatus.ANY)) {
return EMPTY;
}
boolean clearFilter = list.contains(AlarmSearchStatus.CLEARED);
boolean activeFilter = list.contains(AlarmSearchStatus.ACTIVE);
boolean clearFilter = statuses.contains(AlarmSearchStatus.CLEARED);
boolean activeFilter = statuses.contains(AlarmSearchStatus.ACTIVE);
Optional<Boolean> clear = Optional.empty();
if (clearFilter && !activeFilter || !clearFilter && activeFilter) {
clear = Optional.of(clearFilter);
}
boolean ackFilter = list.contains(AlarmSearchStatus.ACK);
boolean unackFilter = list.contains(AlarmSearchStatus.UNACK);
boolean ackFilter = statuses.contains(AlarmSearchStatus.ACK);
boolean unackFilter = statuses.contains(AlarmSearchStatus.UNACK);
Optional<Boolean> ack = Optional.empty();
if (ackFilter && !unackFilter || !ackFilter && unackFilter) {
ack = Optional.of(ackFilter);
@ -114,5 +114,9 @@ public class AlarmStatusFilter {
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.Data;
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.UUID;
@ -32,14 +35,26 @@ public class AlarmCommentNotificationInfo implements NotificationInfo {
private String comment;
private String alarmType;
private UUID alarmId;
private EntityId alarmOriginator;
private AlarmSeverity alarmSeverity;
private AlarmStatus alarmStatus;
@Override
public Map<String, String> getTemplateData() {
return Map.of(
"comment", comment,
"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.NoArgsConstructor;
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.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.Data;
import lombok.NoArgsConstructor;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
import java.util.Map;
import java.util.UUID;
@ -32,8 +32,7 @@ import java.util.UUID;
@Builder
public class EntityActionNotificationInfo implements RuleOriginatedNotificationInfo {
private EntityType entityType;
private UUID entityId;
private EntityId entityId;
private String entityName;
private ActionType actionType;
private UUID originatorUserId;
@ -48,7 +47,7 @@ public class EntityActionNotificationInfo implements RuleOriginatedNotificationI
@Override
public Map<String, String> getTemplateData() {
return Map.of(
"entityType", entityType.name(),
"entityType", entityId.getEntityType().name(),
"entityId", entityId.toString(),
"entityName", entityName,
"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.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.thingsboard.server.common.data.id.EntityId;
import java.util.Map;
@ -28,4 +29,8 @@ public interface NotificationInfo {
@JsonIgnore
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;
}
@Override
public EntityId getStateEntityId() {
return msgOriginator;
}
}

View File

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

View File

@ -16,8 +16,8 @@
package org.thingsboard.server.common.data.notification.rule.trigger;
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.AlarmStatus;
import java.util.Set;
@ -35,7 +35,7 @@ public class AlarmNotificationRuleTriggerConfig implements NotificationRuleTrigg
@Data
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.stereotype.Repository;
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.StringUtils;
import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
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.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
@ -41,11 +38,9 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
@Repository
@ -248,7 +243,7 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
wherePart.append("a.severity in (:alarmSeverities)");
}
AlarmStatusFilter asf = AlarmStatusFilter.fromList(pageLink.getStatusList());
AlarmStatusFilter asf = AlarmStatusFilter.from(pageLink.getStatusList());
if (asf.hasAnyFilter()) {
if (asf.hasAckFilter()) {
addAndIfNeeded(wherePart, addAnd);