67 changed files with 1246 additions and 356 deletions
@ -1,173 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2022 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.service.notification; |
|||
|
|||
import com.google.common.util.concurrent.Futures; |
|||
import com.google.common.util.concurrent.ListenableFuture; |
|||
import lombok.RequiredArgsConstructor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.context.annotation.Lazy; |
|||
import org.springframework.context.event.EventListener; |
|||
import org.springframework.stereotype.Service; |
|||
import org.thingsboard.rule.engine.api.NotificationCenter; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
import org.thingsboard.server.common.data.alarm.Alarm; |
|||
import org.thingsboard.server.common.data.id.NotificationRequestId; |
|||
import org.thingsboard.server.common.data.id.NotificationRuleId; |
|||
import org.thingsboard.server.common.data.id.NotificationTargetId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.id.UUIDBased; |
|||
import org.thingsboard.server.common.data.notification.info.AlarmOriginatedNotificationInfo; |
|||
import org.thingsboard.server.common.data.notification.info.NotificationInfo; |
|||
import org.thingsboard.server.common.data.notification.NotificationRequest; |
|||
import org.thingsboard.server.common.data.notification.NotificationRequestConfig; |
|||
import org.thingsboard.server.common.data.notification.NotificationRequestStatus; |
|||
import org.thingsboard.server.common.data.notification.rule.NotificationEscalation; |
|||
import org.thingsboard.server.common.data.notification.rule.NotificationRule; |
|||
import org.thingsboard.server.common.data.notification.rule.NotificationRuleConfig; |
|||
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; |
|||
import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; |
|||
import org.thingsboard.server.dao.notification.NotificationRequestService; |
|||
import org.thingsboard.server.dao.notification.NotificationRuleService; |
|||
import org.thingsboard.server.queue.util.TbCoreComponent; |
|||
import org.thingsboard.server.service.executors.NotificationExecutorService; |
|||
|
|||
import java.util.List; |
|||
import java.util.stream.Collectors; |
|||
|
|||
@Service |
|||
@TbCoreComponent |
|||
@RequiredArgsConstructor |
|||
@Slf4j |
|||
public class DefaultNotificationRuleProcessingService implements NotificationRuleProcessingService { |
|||
|
|||
private final NotificationRuleService notificationRuleService; |
|||
private final NotificationRequestService notificationRequestService; |
|||
@Autowired @Lazy |
|||
private NotificationCenter notificationCenter; |
|||
private final NotificationExecutorService notificationExecutor; |
|||
|
|||
@Override |
|||
public ListenableFuture<Void> onAlarmCreatedOrUpdated(TenantId tenantId, Alarm alarm) { |
|||
return processAlarmUpdate(tenantId, alarm, false); |
|||
} |
|||
|
|||
@Override |
|||
public ListenableFuture<Void> onAlarmDeleted(TenantId tenantId, Alarm alarm) { |
|||
return processAlarmUpdate(tenantId, alarm, true); |
|||
} |
|||
|
|||
private ListenableFuture<Void> processAlarmUpdate(TenantId tenantId, Alarm alarm, boolean deleted) { |
|||
NotificationRuleId ruleId = alarm.getNotificationRuleId(); |
|||
if (ruleId == null) return Futures.immediateFuture(null); |
|||
return notificationExecutor.submit(() -> { |
|||
try { |
|||
onAlarmUpdate(tenantId, ruleId, alarm, deleted); |
|||
} catch (Exception e) { |
|||
log.error("Failed to process notification rule {} for alarm {}", ruleId, alarm.getId(), e); |
|||
throw e; |
|||
} |
|||
return null; |
|||
}); |
|||
} |
|||
|
|||
private void onAlarmUpdate(TenantId tenantId, NotificationRuleId notificationRuleId, Alarm alarm, boolean deleted) { |
|||
log.debug("Processing alarm update ({}) with notification rule {}", alarm.getId(), notificationRuleId); |
|||
List<NotificationRequest> notificationRequests = notificationRequestService.findNotificationRequestsByRuleIdAndOriginatorEntityId(tenantId, notificationRuleId, alarm.getId()); |
|||
NotificationRule notificationRule = notificationRuleService.findNotificationRuleById(tenantId, notificationRuleId); |
|||
if (notificationRule == null) return; |
|||
|
|||
if (alarmAcknowledged(alarm) || deleted) { |
|||
if (notificationRequests.isEmpty()) { |
|||
return; |
|||
} |
|||
for (NotificationRequest notificationRequest : notificationRequests) { |
|||
if (notificationRequest.getStatus() == NotificationRequestStatus.SCHEDULED) { |
|||
notificationCenter.deleteNotificationRequest(tenantId, notificationRequest.getId()); |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (notificationRequests.isEmpty()) { |
|||
NotificationRuleConfig config = notificationRule.getConfiguration(); |
|||
for (NotificationEscalation escalation : config.getEscalations()) { |
|||
for (NotificationTargetId targetId : escalation.getNotificationTargets()) { |
|||
submitNotificationRequest(tenantId, targetId, notificationRule, alarm, escalation.getDelayInSec()); |
|||
} |
|||
} |
|||
} else { |
|||
NotificationInfo newNotificationInfo = constructNotificationInfo(alarm); |
|||
for (NotificationRequest notificationRequest : notificationRequests) { |
|||
NotificationInfo previousNotificationInfo = notificationRequest.getInfo(); |
|||
if (!previousNotificationInfo.equals(newNotificationInfo)) { |
|||
notificationRequest.setInfo(newNotificationInfo); |
|||
notificationCenter.updateNotificationRequest(tenantId, notificationRequest); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
private boolean alarmAcknowledged(Alarm alarm) { |
|||
return alarm.getStatus().isAck() && alarm.getStatus().isCleared(); |
|||
} |
|||
|
|||
private void submitNotificationRequest(TenantId tenantId, NotificationTargetId targetId, NotificationRule notificationRule, Alarm alarm, int delayInSec) { |
|||
NotificationRequestConfig config = new NotificationRequestConfig(); |
|||
if (delayInSec > 0) { |
|||
config.setSendingDelayInSec(delayInSec); |
|||
} |
|||
NotificationInfo notificationInfo = constructNotificationInfo(alarm); |
|||
NotificationRequest notificationRequest = NotificationRequest.builder() |
|||
.tenantId(tenantId) |
|||
.targets(List.of(targetId).stream().map(UUIDBased::getId).collect(Collectors.toList())) |
|||
.templateId(notificationRule.getTemplateId()) |
|||
.additionalConfig(config) |
|||
.info(notificationInfo) |
|||
.ruleId(notificationRule.getId()) |
|||
.originatorEntityId(alarm.getId()) |
|||
.build(); |
|||
notificationCenter.processNotificationRequest(tenantId, notificationRequest); |
|||
} |
|||
|
|||
private NotificationInfo constructNotificationInfo(Alarm alarm) { |
|||
// TODO: add info about assignee
|
|||
return AlarmOriginatedNotificationInfo.builder() |
|||
.alarmId(alarm.getId()) |
|||
.alarmType(alarm.getType()) |
|||
.alarmOriginator(alarm.getOriginator()) |
|||
.alarmSeverity(alarm.getSeverity()) |
|||
.alarmStatus(alarm.getStatus()) |
|||
.customerId(alarm.getCustomerId()) |
|||
.build(); |
|||
} |
|||
|
|||
@EventListener(ComponentLifecycleMsg.class) |
|||
public void onNotificationRuleDeleted(ComponentLifecycleMsg componentLifecycleMsg) { |
|||
if (componentLifecycleMsg.getEvent() != ComponentLifecycleEvent.DELETED || |
|||
componentLifecycleMsg.getEntityId().getEntityType() != EntityType.NOTIFICATION_RULE) { |
|||
return; |
|||
} |
|||
|
|||
TenantId tenantId = componentLifecycleMsg.getTenantId(); |
|||
NotificationRuleId notificationRuleId = (NotificationRuleId) componentLifecycleMsg.getEntityId(); |
|||
List<NotificationRequestId> scheduledForRule = notificationRequestService.findNotificationRequestsIdsByStatusAndRuleId(tenantId, NotificationRequestStatus.SCHEDULED, notificationRuleId); |
|||
for (NotificationRequestId notificationRequestId : scheduledForRule) { |
|||
notificationCenter.deleteNotificationRequest(tenantId, notificationRequestId); |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,233 @@ |
|||
/** |
|||
* Copyright © 2016-2022 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.service.notification.rule; |
|||
|
|||
import com.google.common.util.concurrent.ListenableFuture; |
|||
import lombok.RequiredArgsConstructor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.context.annotation.Lazy; |
|||
import org.springframework.context.event.EventListener; |
|||
import org.springframework.stereotype.Service; |
|||
import org.thingsboard.common.util.DonAsynchron; |
|||
import org.thingsboard.rule.engine.api.NotificationCenter; |
|||
import org.thingsboard.server.common.data.DataConstants; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
import org.thingsboard.server.common.data.alarm.Alarm; |
|||
import org.thingsboard.server.common.data.id.EntityId; |
|||
import org.thingsboard.server.common.data.id.NotificationRequestId; |
|||
import org.thingsboard.server.common.data.id.NotificationRuleId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.notification.NotificationRequest; |
|||
import org.thingsboard.server.common.data.notification.NotificationRequestConfig; |
|||
import org.thingsboard.server.common.data.notification.NotificationRequestStatus; |
|||
import org.thingsboard.server.common.data.notification.info.AlarmOriginatedNotificationInfo; |
|||
import org.thingsboard.server.common.data.notification.info.NotificationInfo; |
|||
import org.thingsboard.server.common.data.notification.info.RuleEngineOriginatedNotificationInfo; |
|||
import org.thingsboard.server.common.data.notification.rule.NotificationRule; |
|||
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.plugin.ComponentLifecycleEvent; |
|||
import org.thingsboard.server.common.msg.TbMsg; |
|||
import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; |
|||
import org.thingsboard.server.dao.notification.NotificationRequestService; |
|||
import org.thingsboard.server.dao.notification.NotificationRuleService; |
|||
import org.thingsboard.server.service.executors.DbCallbackExecutorService; |
|||
import org.thingsboard.server.service.executors.NotificationExecutorService; |
|||
import org.thingsboard.server.service.notification.rule.trigger.NotificationRuleTriggerProcessor; |
|||
|
|||
import java.util.Collection; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.UUID; |
|||
import java.util.function.Supplier; |
|||
import java.util.stream.Collectors; |
|||
|
|||
@Service |
|||
@RequiredArgsConstructor |
|||
@Slf4j |
|||
public class DefaultNotificationRuleProcessingService implements NotificationRuleProcessingService { |
|||
|
|||
private final NotificationRuleService notificationRuleService; |
|||
private final NotificationRequestService notificationRequestService; |
|||
@Autowired @Lazy |
|||
private NotificationCenter notificationCenter; |
|||
private Map<NotificationRuleTriggerType, NotificationRuleTriggerProcessor> triggerProcessors; |
|||
|
|||
private final NotificationExecutorService notificationExecutor; |
|||
private final DbCallbackExecutorService dbCallbackExecutor; |
|||
|
|||
private final Map<String, NotificationRuleTriggerType> msgTypeToTriggerType = Map.of( |
|||
DataConstants.INACTIVITY_EVENT, NotificationRuleTriggerType.DEVICE_INACTIVITY, |
|||
DataConstants.ENTITY_CREATED, NotificationRuleTriggerType.ENTITY_ACTION, |
|||
DataConstants.ENTITY_UPDATED, NotificationRuleTriggerType.ENTITY_ACTION, |
|||
DataConstants.ENTITY_DELETED, NotificationRuleTriggerType.ENTITY_ACTION |
|||
); |
|||
|
|||
@Override |
|||
public void process(TenantId tenantId, TbMsg ruleEngineMsg) { |
|||
String msgType = ruleEngineMsg.getType(); |
|||
NotificationRuleTriggerType triggerType = msgTypeToTriggerType.get(msgType); |
|||
if (triggerType == null) { |
|||
return; |
|||
} |
|||
|
|||
processTrigger(tenantId, triggerType, ruleEngineMsg.getOriginator(), ruleEngineMsg, false, () -> { |
|||
return RuleEngineOriginatedNotificationInfo.builder() |
|||
.msgOriginator(ruleEngineMsg.getOriginator()) |
|||
.msgType(ruleEngineMsg.getType()) |
|||
.msgMetadata(ruleEngineMsg.getMetaData().getData()) |
|||
.build(); |
|||
}); |
|||
} |
|||
|
|||
@Override |
|||
public void process(TenantId tenantId, Alarm alarm, boolean deleted) { |
|||
processTrigger(tenantId, NotificationRuleTriggerType.ALARM, alarm.getId(), alarm, deleted, () -> { |
|||
// TODO: add info about assignee
|
|||
return AlarmOriginatedNotificationInfo.builder() |
|||
.alarmId(alarm.getId()) |
|||
.alarmType(alarm.getType()) |
|||
.alarmOriginator(alarm.getOriginator()) |
|||
.alarmSeverity(alarm.getSeverity()) |
|||
.alarmStatus(alarm.getStatus()) |
|||
.customerId(alarm.getCustomerId()) |
|||
.build(); |
|||
}); |
|||
} |
|||
|
|||
private void processTrigger(TenantId tenantId, NotificationRuleTriggerType triggerType, EntityId originatorEntityId, |
|||
Object triggerObject, boolean triggerRemoved, |
|||
Supplier<NotificationInfo> notificationInfoProvider) { |
|||
ListenableFuture<List<NotificationRule>> rulesFuture = dbCallbackExecutor.submit(() -> { |
|||
return notificationRuleService.findNotificationRulesByTenantIdAndTriggerType(tenantId, triggerType); |
|||
}); |
|||
DonAsynchron.withCallback(rulesFuture, rules -> { |
|||
for (NotificationRule rule : rules) { |
|||
notificationExecutor.submit(() -> { |
|||
processNotificationRule(rule, originatorEntityId, triggerObject, triggerRemoved, notificationInfoProvider); |
|||
}); |
|||
} |
|||
}, e -> {}); |
|||
} |
|||
|
|||
private <T> void processNotificationRule(NotificationRule rule, EntityId originatorEntityId, |
|||
T triggerObject, boolean triggerRemoved, |
|||
Supplier<NotificationInfo> notificationInfoProvider) { |
|||
NotificationRuleTriggerConfig triggerConfig = rule.getTriggerConfig(); |
|||
log.debug("Processing notification rule '{}' for trigger type {}", rule.getName(), rule.getTriggerType()); |
|||
|
|||
if (triggerConfig.getTriggerType().isUpdatable()) { |
|||
List<NotificationRequest> notificationRequests = notificationRequestService.findNotificationRequestsByRuleIdAndOriginatorEntityId(rule.getTenantId(), rule.getId(), originatorEntityId); |
|||
if (!notificationRequests.isEmpty()) { |
|||
if (triggerRemoved || matchesClearRule(triggerObject, triggerConfig)) { |
|||
notificationRequests = notificationRequests.stream() |
|||
.filter(notificationRequest -> { |
|||
if (!notificationRequest.isSent()) { |
|||
dbCallbackExecutor.submit(() -> { |
|||
notificationCenter.deleteNotificationRequest(rule.getTenantId(), notificationRequest.getId()); |
|||
}); |
|||
return false; |
|||
} else { |
|||
return true; |
|||
} |
|||
}) |
|||
.collect(Collectors.toList()); |
|||
// not returning because we need to update notifications if any
|
|||
} |
|||
|
|||
NotificationInfo notificationInfo = notificationInfoProvider.get(); |
|||
for (NotificationRequest notificationRequest : notificationRequests) { |
|||
NotificationInfo previousNotificationInfo = notificationRequest.getInfo(); |
|||
if (!notificationInfo.equals(previousNotificationInfo)) { |
|||
notificationRequest.setInfo(notificationInfo); |
|||
// and make notifications unread ?
|
|||
dbCallbackExecutor.submit(() -> { |
|||
notificationCenter.updateNotificationRequest(rule.getTenantId(), notificationRequest); |
|||
}); |
|||
} |
|||
} |
|||
return; |
|||
} |
|||
} |
|||
|
|||
if (!matchesFilter(triggerObject, triggerConfig)) { |
|||
return; |
|||
} |
|||
|
|||
NotificationInfo notificationInfo = notificationInfoProvider.get(); |
|||
rule.getRecipientsConfig().getTargetsTable().forEach((delay, targets) -> { |
|||
notificationExecutor.submit(() -> { |
|||
try { |
|||
log.debug("Submitting notification request for rule '{}' with delay of {} ms to targets {}", rule.getName(), delay, targets); |
|||
submitNotificationRequest(targets, rule, originatorEntityId, notificationInfo, delay); |
|||
} catch (Exception e) { |
|||
log.error("Failed to submit notification request for rule {}", rule.getId(), e); |
|||
} |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
private boolean matchesFilter(Object triggerObject, NotificationRuleTriggerConfig triggerConfig) { |
|||
return triggerProcessors.get(triggerConfig.getTriggerType()).matchesFilter(triggerObject, triggerConfig); |
|||
} |
|||
|
|||
private boolean matchesClearRule(Object triggerObject, NotificationRuleTriggerConfig triggerConfig) { |
|||
return triggerProcessors.get(triggerConfig.getTriggerType()).matchesClearRule(triggerObject, triggerConfig); |
|||
} |
|||
|
|||
private void submitNotificationRequest(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()) |
|||
.targets(targets) |
|||
.templateId(rule.getTemplateId()) |
|||
.additionalConfig(config) |
|||
.info(notificationInfo) |
|||
.ruleId(rule.getId()) |
|||
.originatorEntityId(originatorEntityId) |
|||
.build(); |
|||
notificationCenter.processNotificationRequest(rule.getTenantId(), notificationRequest); |
|||
} |
|||
|
|||
@EventListener(ComponentLifecycleMsg.class) |
|||
public void onNotificationRuleDeleted(ComponentLifecycleMsg componentLifecycleMsg) { |
|||
if (componentLifecycleMsg.getEvent() != ComponentLifecycleEvent.DELETED || |
|||
componentLifecycleMsg.getEntityId().getEntityType() != EntityType.NOTIFICATION_RULE) { |
|||
return; |
|||
} |
|||
|
|||
TenantId tenantId = componentLifecycleMsg.getTenantId(); |
|||
NotificationRuleId notificationRuleId = (NotificationRuleId) componentLifecycleMsg.getEntityId(); |
|||
dbCallbackExecutor.submit(() -> { |
|||
List<NotificationRequestId> scheduledForRule = notificationRequestService.findNotificationRequestsIdsByStatusAndRuleId(tenantId, NotificationRequestStatus.SCHEDULED, notificationRuleId); |
|||
for (NotificationRequestId notificationRequestId : scheduledForRule) { |
|||
notificationCenter.deleteNotificationRequest(tenantId, notificationRequestId); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
@Autowired |
|||
public void setTriggerProcessors(Collection<NotificationRuleTriggerProcessor> processors) { |
|||
this.triggerProcessors = processors.stream() |
|||
.collect(Collectors.toMap(NotificationRuleTriggerProcessor::getTriggerType, p -> p)); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,49 @@ |
|||
/** |
|||
* Copyright © 2016-2022 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.service.notification.rule.trigger; |
|||
|
|||
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.notification.rule.trigger.AlarmNotificationRuleTriggerConfig; |
|||
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; |
|||
|
|||
@Service |
|||
public class AlarmNotificationRuleTriggerProcessor implements NotificationRuleTriggerProcessor<Alarm, AlarmNotificationRuleTriggerConfig> { |
|||
|
|||
@Override |
|||
public boolean matchesFilter(Alarm alarm, AlarmNotificationRuleTriggerConfig triggerConfig) { |
|||
return (CollectionUtils.isEmpty(triggerConfig.getAlarmTypes()) || triggerConfig.getAlarmTypes().contains(alarm.getType())) && |
|||
(CollectionUtils.isEmpty(triggerConfig.getAlarmSeverities()) || triggerConfig.getAlarmSeverities().contains(alarm.getSeverity())); |
|||
} |
|||
|
|||
@Override |
|||
public boolean matchesClearRule(Alarm alarm, AlarmNotificationRuleTriggerConfig triggerConfig) { |
|||
AlarmNotificationRuleTriggerConfig.ClearRule clearRule = triggerConfig.getClearRule(); |
|||
if (clearRule != null) { |
|||
if (clearRule.getAlarmStatus() != null) { |
|||
return clearRule.getAlarmStatus().equals(alarm.getStatus()); |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
@Override |
|||
public NotificationRuleTriggerType getTriggerType() { |
|||
return NotificationRuleTriggerType.ALARM; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,53 @@ |
|||
/** |
|||
* Copyright © 2016-2022 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.service.notification.rule.trigger; |
|||
|
|||
import lombok.RequiredArgsConstructor; |
|||
import org.apache.commons.collections.CollectionUtils; |
|||
import org.springframework.stereotype.Service; |
|||
import org.thingsboard.server.common.data.DeviceProfile; |
|||
import org.thingsboard.server.common.data.id.DeviceId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.notification.rule.trigger.DeviceInactivityNotificationRuleTriggerConfig; |
|||
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; |
|||
import org.thingsboard.server.common.msg.TbMsg; |
|||
import org.thingsboard.server.service.profile.TbDeviceProfileCache; |
|||
|
|||
@Service |
|||
@RequiredArgsConstructor |
|||
public class DeviceInactivityNotificationRuleTriggerProcessor implements NotificationRuleTriggerProcessor<TbMsg, DeviceInactivityNotificationRuleTriggerConfig> { |
|||
|
|||
private final TbDeviceProfileCache deviceProfileCache; |
|||
|
|||
@Override |
|||
public boolean matchesFilter(TbMsg ruleEngineMsg, DeviceInactivityNotificationRuleTriggerConfig triggerConfig) { |
|||
DeviceId deviceId = (DeviceId) ruleEngineMsg.getOriginator(); |
|||
if (CollectionUtils.isNotEmpty(triggerConfig.getDevices())) { |
|||
return triggerConfig.getDevices().contains(deviceId); |
|||
} else if (CollectionUtils.isNotEmpty(triggerConfig.getDeviceProfiles())) { |
|||
DeviceProfile deviceProfile = deviceProfileCache.get(TenantId.SYS_TENANT_ID, deviceId); |
|||
return deviceProfile != null && triggerConfig.getDeviceProfiles().contains(deviceProfile.getId()); |
|||
} else { |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public NotificationRuleTriggerType getTriggerType() { |
|||
return NotificationRuleTriggerType.DEVICE_INACTIVITY; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,53 @@ |
|||
/** |
|||
* Copyright © 2016-2022 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.service.notification.rule.trigger; |
|||
|
|||
import org.springframework.stereotype.Service; |
|||
import org.thingsboard.server.common.data.DataConstants; |
|||
import org.thingsboard.server.common.data.notification.rule.trigger.EntityActionNotificationRuleTriggerConfig; |
|||
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; |
|||
import org.thingsboard.server.common.msg.TbMsg; |
|||
|
|||
@Service |
|||
public class EntityActionNotificationRuleTriggerProcessor implements NotificationRuleTriggerProcessor<TbMsg, EntityActionNotificationRuleTriggerConfig> { |
|||
|
|||
@Override |
|||
public boolean matchesFilter(TbMsg ruleEngineMsg, EntityActionNotificationRuleTriggerConfig triggerConfig) { |
|||
String msgType = ruleEngineMsg.getType(); |
|||
if (msgType.equals(DataConstants.ENTITY_CREATED)) { |
|||
if (!triggerConfig.isCreated()) { |
|||
return false; |
|||
} |
|||
} else if (msgType.equals(DataConstants.ENTITY_UPDATED)) { |
|||
if (!triggerConfig.isUpdated()) { |
|||
return false; |
|||
} |
|||
} else if (msgType.equals(DataConstants.ENTITY_DELETED)){ |
|||
if (!triggerConfig.isDeleted()) { |
|||
return false; |
|||
} |
|||
} else { |
|||
return false; |
|||
} |
|||
return triggerConfig.getEntityType() == null || ruleEngineMsg.getOriginator().getEntityType() == triggerConfig.getEntityType(); |
|||
} |
|||
|
|||
@Override |
|||
public NotificationRuleTriggerType getTriggerType() { |
|||
return NotificationRuleTriggerType.ENTITY_ACTION; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
/** |
|||
* Copyright © 2016-2022 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.service.notification.rule.trigger; |
|||
|
|||
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerConfig; |
|||
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; |
|||
|
|||
public interface NotificationRuleTriggerProcessor<T, C extends NotificationRuleTriggerConfig> { |
|||
|
|||
boolean matchesFilter(T triggerObject, C triggerConfig); |
|||
|
|||
default boolean matchesClearRule(T triggerObject, C triggerConfig) { |
|||
return false; |
|||
} |
|||
|
|||
NotificationRuleTriggerType getTriggerType(); |
|||
|
|||
} |
|||
@ -0,0 +1,45 @@ |
|||
/** |
|||
* Copyright © 2016-2022 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.rule; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonIgnore; |
|||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
|||
import com.fasterxml.jackson.annotation.JsonSubTypes; |
|||
import com.fasterxml.jackson.annotation.JsonSubTypes.Type; |
|||
import com.fasterxml.jackson.annotation.JsonTypeInfo; |
|||
import lombok.Data; |
|||
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; |
|||
|
|||
import javax.validation.constraints.NotNull; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.UUID; |
|||
|
|||
@JsonIgnoreProperties |
|||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "triggerType", visible = true, include = JsonTypeInfo.As.EXISTING_PROPERTY, defaultImpl = DefaultNotificationRuleRecipientsConfig.class) |
|||
@JsonSubTypes({ |
|||
@Type(name = "ALARM", value = EscalatedNotificationRuleRecipientsConfig.class), |
|||
}) |
|||
@Data |
|||
public abstract class NotificationRuleRecipientsConfig { |
|||
|
|||
@NotNull |
|||
private NotificationRuleTriggerType triggerType; |
|||
|
|||
@JsonIgnore |
|||
public abstract Map<Integer, List<UUID>> getTargetsTable(); |
|||
|
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
/** |
|||
* Copyright © 2016-2022 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.rule.trigger; |
|||
|
|||
import lombok.Data; |
|||
import org.thingsboard.server.common.data.alarm.AlarmSeverity; |
|||
import org.thingsboard.server.common.data.alarm.AlarmStatus; |
|||
|
|||
import java.util.Set; |
|||
|
|||
@Data |
|||
public class AlarmNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { |
|||
|
|||
private Set<String> alarmTypes; |
|||
private Set<AlarmSeverity> alarmSeverities; |
|||
private ClearRule clearRule; |
|||
|
|||
@Override |
|||
public NotificationRuleTriggerType getTriggerType() { |
|||
return NotificationRuleTriggerType.ALARM; |
|||
} |
|||
|
|||
@Data |
|||
public static class ClearRule { |
|||
private AlarmStatus alarmStatus; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
/** |
|||
* Copyright © 2016-2022 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.rule.trigger; |
|||
|
|||
import lombok.Data; |
|||
import org.thingsboard.server.common.data.id.DeviceId; |
|||
import org.thingsboard.server.common.data.id.DeviceProfileId; |
|||
|
|||
import java.util.Set; |
|||
|
|||
@Data |
|||
public class DeviceInactivityNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { |
|||
|
|||
private Set<DeviceId> devices; |
|||
private Set<DeviceProfileId> deviceProfiles; |
|||
// set either devices or profiles
|
|||
|
|||
@Override |
|||
public NotificationRuleTriggerType getTriggerType() { |
|||
return NotificationRuleTriggerType.DEVICE_INACTIVITY; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
/** |
|||
* Copyright © 2016-2022 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.rule.trigger; |
|||
|
|||
import lombok.Data; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
|
|||
@Data |
|||
public class EntityActionNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { |
|||
|
|||
private EntityType entityType; // maybe add name filter ?
|
|||
private boolean created; |
|||
private boolean updated; |
|||
private boolean deleted; |
|||
|
|||
@Override |
|||
public NotificationRuleTriggerType getTriggerType() { |
|||
return NotificationRuleTriggerType.ENTITY_ACTION; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
/** |
|||
* Copyright © 2016-2022 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.rule.trigger; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
|||
import com.fasterxml.jackson.annotation.JsonSubTypes; |
|||
import com.fasterxml.jackson.annotation.JsonSubTypes.Type; |
|||
import com.fasterxml.jackson.annotation.JsonTypeInfo; |
|||
|
|||
@JsonIgnoreProperties(ignoreUnknown = true) |
|||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "triggerType") |
|||
@JsonSubTypes({ |
|||
@Type(value = AlarmNotificationRuleTriggerConfig.class, name = "ALARM"), |
|||
@Type(value = DeviceInactivityNotificationRuleTriggerConfig.class, name = "DEVICE_INACTIVITY"), |
|||
@Type(value = EntityActionNotificationRuleTriggerConfig.class, name = "ENTITY_ACTION") |
|||
}) |
|||
public interface NotificationRuleTriggerConfig { |
|||
|
|||
NotificationRuleTriggerType getTriggerType(); |
|||
|
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
/** |
|||
* Copyright © 2016-2022 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.rule.trigger; |
|||
|
|||
import lombok.Getter; |
|||
import lombok.RequiredArgsConstructor; |
|||
|
|||
@RequiredArgsConstructor |
|||
public enum NotificationRuleTriggerType { |
|||
|
|||
ALARM(true), |
|||
DEVICE_INACTIVITY(false), |
|||
ENTITY_ACTION(false); |
|||
|
|||
@Getter |
|||
private final boolean isUpdatable; |
|||
|
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
/** |
|||
* Copyright © 2016-2022 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.dao.notification.cache; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Builder; |
|||
import lombok.Data; |
|||
import lombok.NoArgsConstructor; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; |
|||
|
|||
import java.io.Serializable; |
|||
|
|||
@Data |
|||
@AllArgsConstructor |
|||
@NoArgsConstructor |
|||
@Builder |
|||
public class NotificationRuleCacheKey implements Serializable { |
|||
|
|||
private static final long serialVersionUID = 5987113265482170L; |
|||
|
|||
private TenantId tenantId; |
|||
private NotificationRuleTriggerType triggerType; |
|||
|
|||
@Override |
|||
public String toString() { |
|||
return tenantId + "_" + triggerType; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,37 @@ |
|||
/** |
|||
* Copyright © 2016-2022 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.dao.notification.cache; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Builder; |
|||
import lombok.Data; |
|||
import lombok.NoArgsConstructor; |
|||
import org.thingsboard.server.common.data.notification.rule.NotificationRule; |
|||
|
|||
import java.io.Serializable; |
|||
import java.util.List; |
|||
|
|||
@Data |
|||
@AllArgsConstructor |
|||
@NoArgsConstructor |
|||
@Builder |
|||
public class NotificationRuleCacheValue implements Serializable { |
|||
|
|||
private static final long serialVersionUID = 9503216785105415L; |
|||
|
|||
private List<NotificationRule> notificationRules; |
|||
|
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
/** |
|||
* Copyright © 2016-2022 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.dao.notification.cache; |
|||
|
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
|||
import org.springframework.cache.CacheManager; |
|||
import org.springframework.stereotype.Service; |
|||
import org.thingsboard.server.cache.CaffeineTbTransactionalCache; |
|||
import org.thingsboard.server.common.data.CacheConstants; |
|||
|
|||
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true) |
|||
@Service("notificationRuleCache") |
|||
public class NotificationRuleCaffeineCache extends CaffeineTbTransactionalCache<NotificationRuleCacheKey, NotificationRuleCacheValue> { |
|||
|
|||
public NotificationRuleCaffeineCache(CacheManager cacheManager) { |
|||
super(cacheManager, CacheConstants.NOTIFICATION_RULES_CACHE); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
/** |
|||
* Copyright © 2016-2022 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.dao.notification.cache; |
|||
|
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
|||
import org.springframework.data.redis.connection.RedisConnectionFactory; |
|||
import org.springframework.stereotype.Service; |
|||
import org.thingsboard.server.cache.CacheSpecsMap; |
|||
import org.thingsboard.server.cache.RedisTbTransactionalCache; |
|||
import org.thingsboard.server.cache.TBRedisCacheConfiguration; |
|||
import org.thingsboard.server.cache.TbFSTRedisSerializer; |
|||
import org.thingsboard.server.common.data.CacheConstants; |
|||
|
|||
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis") |
|||
@Service("notificationRuleCache") |
|||
public class NotificationRuleRedisCache extends RedisTbTransactionalCache<NotificationRuleCacheKey, NotificationRuleCacheValue> { |
|||
|
|||
public NotificationRuleRedisCache(CacheSpecsMap cacheSpecsMap, RedisConnectionFactory connectionFactory, TBRedisCacheConfiguration configuration) { |
|||
super(CacheConstants.NOTIFICATION_RULES_CACHE, cacheSpecsMap, connectionFactory, configuration, new TbFSTRedisSerializer<>()); |
|||
} |
|||
|
|||
} |
|||
Loading…
Reference in new issue