From 4a20adc62e14fc3c73083af6851cf848d4b8493e Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Thu, 16 Mar 2023 13:12:04 +0200 Subject: [PATCH] Alarm related notification rules --- .../main/data/upgrade/3.4.4/schema_update.sql | 4 +- .../apiusage/DefaultApiLimitService.java | 53 ++++++++++++ .../DefaultTbNotificationEntityService.java | 3 +- .../entitiy/TbNotificationEntityService.java | 3 +- ...aultNotificationRuleProcessingService.java | 61 ++++++++++---- .../NotificationRuleProcessingService.java | 14 +++- .../AlarmAssignmentTriggerProcessor.java | 79 ++++++++++++++++++ .../trigger/AlarmCommentTriggerProcessor.java | 24 ++++-- .../rule/trigger/AlarmTriggerProcessor.java | 81 +++++++++++++------ .../DeviceInactivityTriggerProcessor.java | 1 + .../EntitiesLimitTriggerProcessor.java | 62 ++++++++++++++ .../NewPlatformVersionTriggerProcessor.java | 45 +++++++++++ .../impl/BackupCodeTwoFaProvider.java | 2 +- .../state/DefaultDeviceStateService.java | 4 + .../DefaultSubscriptionManagerService.java | 6 -- .../DefaultGitVersionControlQueueService.java | 5 +- .../DefaultAlarmSubscriptionService.java | 4 + .../service/update/DefaultUpdateService.java | 11 ++- .../notification/NotificationRuleApiTest.java | 5 +- .../server/dao/alarm/AlarmApiCallResult.java | 13 ++- .../dao/usagerecord/ApiLimitService.java | 25 ++++++ .../NotificationProcessingContext.java | 3 +- .../data/notification/NotificationType.java | 5 +- .../info/AlarmAssignmentNotificationInfo.java | 78 ++++++++++++++++++ .../info/AlarmCommentNotificationInfo.java | 19 ++++- .../info/AlarmNotificationInfo.java | 17 ++-- .../DeviceInactivityNotificationInfo.java | 7 +- .../info/EntitiesLimitNotificationInfo.java | 45 +++++++++++ .../info/EntityActionNotificationInfo.java | 4 +- .../NewPlatformVersionNotificationInfo.java | 42 ++++++++++ ...mponentLifecycleEventNotificationInfo.java | 6 +- .../notification/rule/NotificationRule.java | 1 + ...signmentNotificationRuleTriggerConfig.java | 37 +++++++++ ...mCommentNotificationRuleTriggerConfig.java | 1 + .../AlarmNotificationRuleTriggerConfig.java | 8 ++ ...iesLimitNotificationRuleTriggerConfig.java | 36 +++++++++ ...mVersionNotificationRuleTriggerConfig.java | 28 +++++++ .../NotificationRuleTriggerConfig.java | 5 +- .../trigger/NotificationRuleTriggerType.java | 5 +- .../DefaultTenantProfileConfiguration.java | 20 +++++ .../common/data}/util/CollectionsUtil.java | 22 ++++- .../lwm2m/server/model/LwM2MModelConfig.java | 2 +- .../uplink/DefaultLwM2mUplinkMsgHandler.java | 2 +- .../DefaultClusterVersionControlService.java | 2 +- .../server/dao/alarm/BaseAlarmService.java | 2 +- .../server/dao/model/ModelConstants.java | 3 +- .../server/dao/service/DataValidator.java | 19 ++--- .../service/validator/AssetDataValidator.java | 11 +-- .../validator/CustomerDataValidator.java | 13 +-- .../validator/DashboardDataValidator.java | 12 +-- .../validator/DeviceDataValidator.java | 12 +-- .../validator/RuleChainDataValidator.java | 9 +-- .../service/validator/UserDataValidator.java | 12 +-- .../resources/sql/schema-entities-idx.sql | 15 ++-- .../main/resources/sql/schema-entities.sql | 4 +- .../TbCopyAttributesToEntityViewNode.java | 2 +- 56 files changed, 838 insertions(+), 176 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/apiusage/DefaultApiLimitService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmAssignmentTriggerProcessor.java create mode 100644 application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EntitiesLimitTriggerProcessor.java create mode 100644 application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/NewPlatformVersionTriggerProcessor.java create mode 100644 common/dao-api/src/main/java/org/thingsboard/server/dao/usagerecord/ApiLimitService.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmAssignmentNotificationInfo.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EntitiesLimitNotificationInfo.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/info/NewPlatformVersionNotificationInfo.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmAssignmentNotificationRuleTriggerConfig.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EntitiesLimitNotificationRuleTriggerConfig.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NewPlatformVersionNotificationRuleTriggerConfig.java rename common/{util/src/main/java/org/thingsboard/common => data/src/main/java/org/thingsboard/server/common/data}/util/CollectionsUtil.java (71%) diff --git a/application/src/main/data/upgrade/3.4.4/schema_update.sql b/application/src/main/data/upgrade/3.4.4/schema_update.sql index 7030ff7af1..91c49bf905 100644 --- a/application/src/main/data/upgrade/3.4.4/schema_update.sql +++ b/application/src/main/data/upgrade/3.4.4/schema_update.sql @@ -157,7 +157,7 @@ CREATE TABLE IF NOT EXISTS notification ( recipient_id UUID NOT NULL CONSTRAINT fk_notification_recipient_id REFERENCES tb_user(id) ON DELETE CASCADE, type VARCHAR(50) NOT NULL, subject VARCHAR(255), - text VARCHAR(1000) NOT NULL, + body VARCHAR(1000) NOT NULL, additional_config VARCHAR(1000), status VARCHAR(32) ) PARTITION BY RANGE (created_time); @@ -347,7 +347,7 @@ BEGIN UPDATE alarm a SET acknowledged = true, ack_ts = a_ts WHERE a.id = a_id AND a.tenant_id = t_id; END IF; SELECT * INTO result FROM alarm_info a WHERE a.id = a_id AND a.tenant_id = t_id; - RETURN json_build_object('success', true, 'modified', modified, 'alarm', row_to_json(result))::text; + RETURN json_build_object('success', true, 'modified', modified, 'alarm', row_to_json(result), 'old', row_to_json(existing))::text; END $$; diff --git a/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultApiLimitService.java b/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultApiLimitService.java new file mode 100644 index 0000000000..b12cb4bef3 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultApiLimitService.java @@ -0,0 +1,53 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.apiusage; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.query.EntityCountQuery; +import org.thingsboard.server.common.data.query.EntityTypeFilter; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; +import org.thingsboard.server.dao.entity.EntityService; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; +import org.thingsboard.server.dao.usagerecord.ApiLimitService; +import org.thingsboard.server.service.notification.rule.NotificationRuleProcessingService; + +@Service +@RequiredArgsConstructor +public class DefaultApiLimitService implements ApiLimitService { + + private final EntityService entityService; + private final TbTenantProfileCache tenantProfileCache; + private final NotificationRuleProcessingService notificationRuleProcessingService; + + @Override + public boolean checkEntitiesLimit(TenantId tenantId, EntityType entityType) { + DefaultTenantProfileConfiguration profileConfiguration = tenantProfileCache.get(tenantId).getDefaultProfileConfiguration(); + long limit = profileConfiguration.getEntitiesLimit(entityType); + if (limit > 0) { + EntityTypeFilter filter = new EntityTypeFilter(); + filter.setEntityType(entityType); + long currentCount = entityService.countEntitiesByQuery(tenantId, null, new EntityCountQuery(filter)); + notificationRuleProcessingService.process(tenantId, entityType, limit, currentCount); + return currentCount < limit; + } else { + return true; + } + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java index 03a0671162..fe4f761f81 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java @@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmComment; +import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeEventActionType; @@ -221,7 +222,7 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS } @Override - public void notifyCreateOrUpdateAlarm(Alarm alarm, ActionType actionType, User user, Object... additionalInfo) { + public void notifyCreateOrUpdateAlarm(AlarmInfo alarm, ActionType actionType, User user, Object... additionalInfo) { logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarm, alarm.getCustomerId(), actionType, user, additionalInfo); sendEntityNotificationMsg(alarm.getTenantId(), alarm.getId(), edgeTypeByActionType(actionType)); } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java index 3765358c6d..c5f8f85831 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java @@ -21,6 +21,7 @@ import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmComment; +import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeEventActionType; @@ -100,7 +101,7 @@ public interface TbNotificationEntityService { void notifyCreateOrUpdateOrDeleteEdge(TenantId tenantId, EdgeId edgeId, CustomerId customerId, Edge edge, ActionType actionType, User user, Object... additionalInfo); - void notifyCreateOrUpdateAlarm(Alarm alarm, ActionType actionType, User user, Object... additionalInfo); + void notifyCreateOrUpdateAlarm(AlarmInfo alarm, ActionType actionType, User user, Object... additionalInfo); void notifyAlarmComment(Alarm alarm, AlarmComment alarmComment, ActionType actionType, User user); diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessingService.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessingService.java index c62c03f1f5..5d89983b04 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessingService.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessingService.java @@ -23,7 +23,7 @@ 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.UpdateMessage; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.NotificationRuleId; @@ -39,11 +39,12 @@ import org.thingsboard.server.common.data.notification.rule.trigger.Notification 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.common.msg.queue.ServiceType; +import org.thingsboard.server.dao.alarm.AlarmApiCallResult; import org.thingsboard.server.dao.notification.NotificationRequestService; import org.thingsboard.server.dao.notification.NotificationRuleService; -import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.service.executors.NotificationExecutorService; -import org.thingsboard.server.service.notification.rule.trigger.AlarmTriggerProcessor.AlarmTriggerObject; +import org.thingsboard.server.service.notification.rule.trigger.EntitiesLimitTriggerProcessor.EntitiesLimitTriggerObject; import org.thingsboard.server.service.notification.rule.trigger.NotificationRuleTriggerProcessor; import org.thingsboard.server.service.notification.rule.trigger.RuleEngineComponentLifecycleEventTriggerProcessor.RuleEngineComponentLifecycleEventTriggerObject; import org.thingsboard.server.service.notification.rule.trigger.RuleEngineMsgNotificationRuleTriggerProcessor; @@ -84,12 +85,8 @@ public class DefaultNotificationRuleProcessingService implements NotificationRul } @Override - public void process(TenantId tenantId, Alarm alarm, boolean deleted) { - AlarmTriggerObject triggerObject = AlarmTriggerObject.builder() - .alarm(alarm) - .deleted(deleted) - .build(); - processTrigger(tenantId, NotificationRuleTriggerType.ALARM, alarm.getId(), triggerObject); + public void process(TenantId tenantId, AlarmApiCallResult alarmUpdate) { + processTrigger(tenantId, NotificationRuleTriggerType.ALARM, alarmUpdate.getAlarm().getId(), alarmUpdate); } @Override @@ -105,11 +102,43 @@ public class DefaultNotificationRuleProcessingService implements NotificationRul processTrigger(tenantId, NotificationRuleTriggerType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT, componentId, triggerObject); } + @Override + public void process(UpdateMessage platformUpdateMessage) { +// if (!partitionService.resolve(ServiceType.TB_CORE, TenantId.SYS_TENANT_ID, TenantId.SYS_TENANT_ID).isMyPartition()) { +// return; +// } +// // todo: don't send repetitive notification after platform restart? +// +// processTrigger(TenantId.SYS_TENANT_ID, NotificationRuleTriggerType.NEW_PLATFORM_VERSION, TenantId.SYS_TENANT_ID, platformUpdateMessage); + } + + @Override + public void process(TenantId tenantId, EntityType entityType, long limit, long currentCount) { +// EntitiesLimitTriggerObject triggerObject = EntitiesLimitTriggerObject.builder() +// .entityType(entityType) +// .limit(limit) +// .currentCount(currentCount) +// .build(); + } + + @Override + public void process(ComponentLifecycleMsg componentLifecycleMsg) { +// EntityId entityId = componentLifecycleMsg.getEntityId(); +// switch (entityId.getEntityType()) { +// case TENANT: +// +// } + } + private void processTrigger(TenantId tenantId, NotificationRuleTriggerType triggerType, EntityId originatorEntityId, Object triggerObject) { List rules = notificationRuleService.findNotificationRulesByTenantIdAndTriggerType(tenantId, triggerType); for (NotificationRule rule : rules) { notificationExecutor.submit(() -> { - processNotificationRule(rule, originatorEntityId, triggerObject); + try { + processNotificationRule(rule, originatorEntityId, triggerObject); + } catch (Throwable e) { + log.error("Failed to process notification rule {} for trigger type {} with trigger object {}", rule.getId(), rule.getTriggerType(), triggerObject, e); + } }); } } @@ -139,14 +168,12 @@ public class DefaultNotificationRuleProcessingService implements NotificationRul return; } - if (!matchesFilter(triggerObject, triggerConfig)) { - return; + if (matchesFilter(triggerObject, triggerConfig)) { + NotificationInfo notificationInfo = constructNotificationInfo(triggerObject, triggerConfig); + rule.getRecipientsConfig().getTargetsTable().forEach((delay, targets) -> { + submitNotificationRequest(targets, rule, originatorEntityId, notificationInfo, delay); + }); } - - NotificationInfo notificationInfo = constructNotificationInfo(triggerObject, triggerConfig); - rule.getRecipientsConfig().getTargetsTable().forEach((delay, targets) -> { - submitNotificationRequest(targets, rule, originatorEntityId, notificationInfo, delay); - }); } private boolean matchesFilter(Object triggerObject, NotificationRuleTriggerConfig triggerConfig) { diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/NotificationRuleProcessingService.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/NotificationRuleProcessingService.java index d769983b6a..7d9db425f1 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/NotificationRuleProcessingService.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/NotificationRuleProcessingService.java @@ -15,20 +15,30 @@ */ package org.thingsboard.server.service.notification.rule; -import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.UpdateMessage; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; 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.alarm.AlarmApiCallResult; public interface NotificationRuleProcessingService { void process(TenantId tenantId, TbMsg ruleEngineMsg); - void process(TenantId tenantId, Alarm alarm, boolean deleted); + // for handling internal component lifecycle events that are not getting to rule chain + void process(ComponentLifecycleMsg componentLifecycleMsg); + + void process(TenantId tenantId, AlarmApiCallResult alarmUpdate); void process(TenantId tenantId, RuleChainId ruleChainId, String ruleChainName, EntityId componentId, String componentName, ComponentLifecycleEvent eventType, Exception error); + void process(UpdateMessage platformUpdateMessage); + + void process(TenantId tenantId, EntityType entityType, long limit, long currentCount); + } 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 new file mode 100644 index 0000000000..6ccff5686f --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmAssignmentTriggerProcessor.java @@ -0,0 +1,79 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.notification.rule.trigger; + +import org.springframework.stereotype.Service; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.alarm.AlarmAssignee; +import org.thingsboard.server.common.data.alarm.AlarmInfo; +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.NotificationRuleTriggerType; +import org.thingsboard.server.common.msg.TbMsg; + +import java.util.Set; + +import static org.apache.commons.collections.CollectionUtils.isEmpty; + +@Service +public class AlarmAssignmentTriggerProcessor implements RuleEngineMsgNotificationRuleTriggerProcessor { + + @Override + public boolean matchesFilter(TbMsg ruleEngineMsg, AlarmAssignmentNotificationRuleTriggerConfig triggerConfig) { + if (ruleEngineMsg.getType().equals(DataConstants.ALARM_UNASSIGN) && !triggerConfig.isNotifyOnUnassign()) { + 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 + public NotificationInfo constructNotificationInfo(TbMsg ruleEngineMsg, AlarmAssignmentNotificationRuleTriggerConfig triggerConfig) { + // TODO: readable action + AlarmInfo alarmInfo = JacksonUtil.fromString(ruleEngineMsg.getData(), AlarmInfo.class); + AlarmAssignee assignee = alarmInfo.getAssignee(); + return AlarmAssignmentNotificationInfo.builder() + .assigneeFirstName(assignee != null ? assignee.getFirstName() : null) + .assigneeLastName(assignee != null ? assignee.getLastName() : null) + .assigneeEmail(assignee != null ? assignee.getEmail() : null) + .userName(ruleEngineMsg.getMetaData().getValue("userName")) + .alarmId(alarmInfo.getUuidId()) + .alarmType(alarmInfo.getType()) + .alarmOriginator(alarmInfo.getOriginator()) + .alarmOriginatorName(alarmInfo.getOriginatorName()) + .alarmSeverity(alarmInfo.getSeverity()) + .alarmStatus(alarmInfo.getStatus()) + .alarmCustomerId(alarmInfo.getCustomerId()) + .build(); + } + + @Override + public NotificationRuleTriggerType getTriggerType() { + return NotificationRuleTriggerType.ALARM_ASSIGNMENT; + } + + @Override + public Set getSupportedMsgTypes() { + return Set.of(DataConstants.ALARM_ASSIGN, DataConstants.ALARM_UNASSIGN); + } + +} 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 cd30d9b580..8be6e1622b 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 @@ -20,6 +20,8 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmComment; +import org.thingsboard.server.common.data.alarm.AlarmCommentType; +import org.thingsboard.server.common.data.alarm.AlarmInfo; 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; @@ -39,6 +41,12 @@ public class AlarmCommentTriggerProcessor implements RuleEngineMsgNotificationRu if (ruleEngineMsg.getMetaData().getValue("comment") == null) { return false; } + if (triggerConfig.isOnlyUserComments()) { + AlarmComment comment = JacksonUtil.fromString(ruleEngineMsg.getMetaData().getValue("comment"), AlarmComment.class); + if (comment.getType() == AlarmCommentType.SYSTEM) { + 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())) && @@ -47,15 +55,19 @@ public class AlarmCommentTriggerProcessor implements RuleEngineMsgNotificationRu @Override public NotificationInfo constructNotificationInfo(TbMsg ruleEngineMsg, AlarmCommentNotificationRuleTriggerConfig triggerConfig) { + // TODO: readable action AlarmComment comment = JacksonUtil.fromString(ruleEngineMsg.getMetaData().getValue("comment"), AlarmComment.class); - Alarm alarm = JacksonUtil.fromString(ruleEngineMsg.getData(), Alarm.class); + AlarmInfo alarmInfo = JacksonUtil.fromString(ruleEngineMsg.getData(), AlarmInfo.class); return AlarmCommentNotificationInfo.builder() .comment(comment.getComment().get("text").asText()) - .alarmId(alarm.getUuidId()) - .alarmType(alarm.getType()) - .alarmOriginator(alarm.getOriginator()) - .alarmSeverity(alarm.getSeverity()) - .alarmStatus(alarm.getStatus()) + .userName(ruleEngineMsg.getMetaData().getValue("userName")) + .alarmId(alarmInfo.getUuidId()) + .alarmType(alarmInfo.getType()) + .alarmOriginator(alarmInfo.getOriginator()) + .alarmOriginatorName(alarmInfo.getOriginatorName()) + .alarmSeverity(alarmInfo.getSeverity()) + .alarmStatus(alarmInfo.getStatus()) + .alarmCustomerId(alarmInfo.getCustomerId()) .build(); } diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmTriggerProcessor.java index 9f226ca352..babe177eea 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmTriggerProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmTriggerProcessor.java @@ -15,37 +15,63 @@ */ package org.thingsboard.server.service.notification.rule.trigger; -import lombok.Builder; -import lombok.Data; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.alarm.AlarmInfo; 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; +import org.thingsboard.server.common.data.notification.rule.trigger.AlarmNotificationRuleTriggerConfig.AlarmAction; import org.thingsboard.server.common.data.notification.rule.trigger.AlarmNotificationRuleTriggerConfig.ClearRule; import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; -import org.thingsboard.server.service.notification.rule.trigger.AlarmTriggerProcessor.AlarmTriggerObject; +import org.thingsboard.server.dao.alarm.AlarmApiCallResult; import static org.apache.commons.collections.CollectionUtils.isEmpty; import static org.apache.commons.collections.CollectionUtils.isNotEmpty; @Service -public class AlarmTriggerProcessor implements NotificationRuleTriggerProcessor { +public class AlarmTriggerProcessor implements NotificationRuleTriggerProcessor { @Override - public boolean matchesFilter(AlarmTriggerObject triggerObject, AlarmNotificationRuleTriggerConfig triggerConfig) { - Alarm alarm = triggerObject.getAlarm(); - return (isEmpty(triggerConfig.getAlarmTypes()) || triggerConfig.getAlarmTypes().contains(alarm.getType())) && - (isEmpty(triggerConfig.getAlarmSeverities()) || triggerConfig.getAlarmSeverities().contains(alarm.getSeverity())); + public boolean matchesFilter(AlarmApiCallResult alarmUpdate, AlarmNotificationRuleTriggerConfig triggerConfig) { + Alarm alarm = alarmUpdate.getAlarm(); + if (!typeMatches(alarm, triggerConfig)) { + return false; + } + + if (alarmUpdate.isCreated()) { + if (triggerConfig.getNotifyOn().contains(AlarmAction.CREATED)) { + return severityMatches(alarm, triggerConfig); + } + } else if (alarmUpdate.isSeverityChanged()) { + if (triggerConfig.getNotifyOn().contains(AlarmAction.SEVERITY_CHANGED)) { + return severityMatches(alarmUpdate.getOld(), triggerConfig) || severityMatches(alarm, triggerConfig); + } else { + // if we haven't yet sent notification about the alarm + return !severityMatches(alarmUpdate.getOld(), triggerConfig) && severityMatches(alarm, triggerConfig); + } + } else if (alarmUpdate.isAcknowledged()) { + if (triggerConfig.getNotifyOn().contains(AlarmAction.ACKNOWLEDGED)) { + return severityMatches(alarm, triggerConfig); + } + } else if (alarmUpdate.isCleared()) { + if (triggerConfig.getNotifyOn().contains(AlarmAction.CLEARED)) { + return severityMatches(alarm, triggerConfig); + } + } + return false; } @Override - public boolean matchesClearRule(AlarmTriggerObject triggerObject, AlarmNotificationRuleTriggerConfig triggerConfig) { - if (triggerObject.isDeleted()) { + public boolean matchesClearRule(AlarmApiCallResult alarmUpdate, AlarmNotificationRuleTriggerConfig triggerConfig) { + Alarm alarm = alarmUpdate.getAlarm(); + if (!typeMatches(alarm, triggerConfig)) { + return false; + } + if (alarmUpdate.isDeleted()) { return true; } - Alarm alarm = triggerObject.getAlarm(); ClearRule clearRule = triggerConfig.getClearRule(); if (clearRule != null) { if (isNotEmpty(clearRule.getAlarmStatuses())) { @@ -55,16 +81,26 @@ public class AlarmTriggerProcessor implements NotificationRuleTriggerProcessor { + + @Override + public boolean matchesFilter(EntitiesLimitTriggerObject triggerObject, EntitiesLimitNotificationRuleTriggerConfig triggerConfig) { + if (isNotEmpty(triggerConfig.getEntityTypes()) && !triggerConfig.getEntityTypes().contains(triggerObject.getEntityType())) { + return false; + } + return ((float) triggerObject.getCurrentCount() / triggerObject.getLimit()) >= triggerConfig.getThreshold(); + } + + @Override + public NotificationInfo constructNotificationInfo(EntitiesLimitTriggerObject triggerObject, EntitiesLimitNotificationRuleTriggerConfig triggerConfig) { + return EntitiesLimitNotificationInfo.builder() + .entityType(triggerObject.getEntityType()) + .threshold((int) (triggerConfig.getThreshold() * 100)) + .build(); + } + + @Override + public NotificationRuleTriggerType getTriggerType() { + return NotificationRuleTriggerType.ENTITIES_LIMIT; + } + + @Data + @Builder + public static class EntitiesLimitTriggerObject { + private final EntityType entityType; + private final long limit; + private final long currentCount; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/NewPlatformVersionTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/NewPlatformVersionTriggerProcessor.java new file mode 100644 index 0000000000..e26328404b --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/NewPlatformVersionTriggerProcessor.java @@ -0,0 +1,45 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.notification.rule.trigger; + +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.UpdateMessage; +import org.thingsboard.server.common.data.notification.info.NewPlatformVersionNotificationInfo; +import org.thingsboard.server.common.data.notification.info.NotificationInfo; +import org.thingsboard.server.common.data.notification.rule.trigger.NewPlatformVersionNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; + +@Service +public class NewPlatformVersionTriggerProcessor implements NotificationRuleTriggerProcessor { + + @Override + public boolean matchesFilter(UpdateMessage triggerObject, NewPlatformVersionNotificationRuleTriggerConfig triggerConfig) { + return triggerObject.isUpdateAvailable(); + } + + @Override + public NotificationInfo constructNotificationInfo(UpdateMessage updateMessage, NewPlatformVersionNotificationRuleTriggerConfig triggerConfig) { + return NewPlatformVersionNotificationInfo.builder() + .message(updateMessage.getMessage()) + .build(); + } + + @Override + public NotificationRuleTriggerType getTriggerType() { + return NotificationRuleTriggerType.NEW_PLATFORM_VERSION; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/provider/impl/BackupCodeTwoFaProvider.java b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/provider/impl/BackupCodeTwoFaProvider.java index 1369ece85e..cb718462bf 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/provider/impl/BackupCodeTwoFaProvider.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/provider/impl/BackupCodeTwoFaProvider.java @@ -18,7 +18,7 @@ package org.thingsboard.server.service.security.auth.mfa.provider.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; -import org.thingsboard.common.util.CollectionsUtil; +import org.thingsboard.server.common.data.util.CollectionsUtil; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.security.model.mfa.account.BackupCodeTwoFaAccountConfig; diff --git a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java index 80fd643253..60c159360b 100644 --- a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java @@ -139,6 +139,7 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService PERSISTENT_ENTITY_FIELDS = Arrays.asList( new EntityKey(EntityKeyType.ENTITY_FIELD, "name"), new EntityKey(EntityKeyType.ENTITY_FIELD, "type"), + new EntityKey(EntityKeyType.ENTITY_FIELD, "label"), new EntityKey(EntityKeyType.ENTITY_FIELD, "createdTime")); private final TenantService tenantService; @@ -329,6 +330,7 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService> subscriptionsByEntityId = new ConcurrentHashMap<>(); private final Map> subscriptionsByWsSessionId = new ConcurrentHashMap<>(); @@ -296,7 +292,6 @@ public class DefaultSubscriptionManagerService extends TbApplicationEventListene s -> alarm.getCreatedTime() >= s.getTs() || alarm.getAssignTs() >= s.getTs(), alarm, false ); - notificationRuleProcessingService.process(tenantId, alarm, false); callback.onSuccess(); } @@ -313,7 +308,6 @@ public class DefaultSubscriptionManagerService extends TbApplicationEventListene s -> alarm.getCreatedTime() >= s.getTs(), alarm, true ); - notificationRuleProcessingService.process(tenantId, alarm, true); callback.onSuccess(); } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitVersionControlQueueService.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitVersionControlQueueService.java index 905fa53e91..785dd50a92 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitVersionControlQueueService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitVersionControlQueueService.java @@ -26,7 +26,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; -import org.thingsboard.common.util.CollectionsUtil; +import org.thingsboard.server.common.data.util.CollectionsUtil; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.EntityType; @@ -64,7 +64,6 @@ import org.thingsboard.server.queue.util.DataDecodingEncodingService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.sync.vc.data.ClearRepositoryGitRequest; import org.thingsboard.server.service.sync.vc.data.CommitGitRequest; -import org.thingsboard.server.service.sync.vc.data.ContentsDiffGitRequest; import org.thingsboard.server.service.sync.vc.data.EntitiesContentGitRequest; import org.thingsboard.server.service.sync.vc.data.EntityContentGitRequest; import org.thingsboard.server.service.sync.vc.data.ListBranchesGitRequest; @@ -78,11 +77,9 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.TreeMap; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java index f412e76ad9..6adefb709e 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java @@ -52,6 +52,7 @@ import org.thingsboard.server.dao.alarm.AlarmOperationResult; import org.thingsboard.server.dao.alarm.AlarmService; import org.thingsboard.server.service.apiusage.TbApiUsageStateService; import org.thingsboard.server.service.entitiy.alarm.TbAlarmCommentService; +import org.thingsboard.server.service.notification.rule.NotificationRuleProcessingService; import org.thingsboard.server.service.subscription.TbSubscriptionUtils; import java.util.Collection; @@ -68,6 +69,7 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService private final TbAlarmCommentService alarmCommentService; private final TbApiUsageReportClient apiUsageClient; private final TbApiUsageStateService apiUsageStateService; + private final NotificationRuleProcessingService notificationRuleProcessingService; @Override protected String getExecutorPrefix() { @@ -232,6 +234,7 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService return TbSubscriptionUtils.toAlarmUpdateProto(tenantId, entityId, alarm); }); } + notificationRuleProcessingService.process(tenantId, result); }); } @@ -246,6 +249,7 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService return TbSubscriptionUtils.toAlarmDeletedProto(tenantId, entityId, alarm); }); } + notificationRuleProcessingService.process(tenantId, result); }); } diff --git a/application/src/main/java/org/thingsboard/server/service/update/DefaultUpdateService.java b/application/src/main/java/org/thingsboard/server/service/update/DefaultUpdateService.java index ac3bfc497a..24e6ec7d13 100644 --- a/application/src/main/java/org/thingsboard/server/service/update/DefaultUpdateService.java +++ b/application/src/main/java/org/thingsboard/server/service/update/DefaultUpdateService.java @@ -19,12 +19,14 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.common.data.UpdateMessage; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.notification.rule.NotificationRuleProcessingService; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; @@ -53,6 +55,9 @@ public class DefaultUpdateService implements UpdateService { @Value("${updates.enabled}") private boolean updatesEnabled; + @Autowired + private NotificationRuleProcessingService notificationRuleProcessingService; + private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1, ThingsBoardThreadFactory.forName("tb-update-service")); private ScheduledFuture checkUpdatesFuture = null; @@ -121,11 +126,15 @@ public class DefaultUpdateService implements UpdateService { request.put(PLATFORM_PARAM, platform); request.put(VERSION_PARAM, version); request.put(INSTANCE_ID_PARAM, instanceId.toString()); - JsonNode response = restClient.postForObject(UPDATE_SERVER_BASE_URL+"/api/thingsboard/updates", request, JsonNode.class); + JsonNode response = restClient.postForObject(UPDATE_SERVER_BASE_URL + "/api/thingsboard/updates", request, JsonNode.class); + UpdateMessage prevUpdateMessage = updateMessage; updateMessage = new UpdateMessage( response.get("message").asText(), response.get("updateAvailable").asBoolean() ); + if (updateMessage.isUpdateAvailable() && !updateMessage.equals(prevUpdateMessage)) { + notificationRuleProcessingService.process(updateMessage); + } } catch (Exception e) { log.trace(e.getMessage()); } diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java index 293eb07af9..84b22b5d14 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java @@ -56,6 +56,7 @@ import org.thingsboard.server.common.data.notification.rule.EscalatedNotificatio import org.thingsboard.server.common.data.notification.rule.NotificationRule; import org.thingsboard.server.common.data.notification.rule.NotificationRuleInfo; import org.thingsboard.server.common.data.notification.rule.trigger.AlarmNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.AlarmNotificationRuleTriggerConfig.AlarmAction; 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.data.notification.rule.trigger.RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig; @@ -180,6 +181,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { AlarmNotificationRuleTriggerConfig triggerConfig = new AlarmNotificationRuleTriggerConfig(); triggerConfig.setAlarmTypes(null); triggerConfig.setAlarmSeverities(null); + triggerConfig.setNotifyOn(Set.of(AlarmAction.CREATED, AlarmAction.SEVERITY_CHANGED, AlarmAction.ACKNOWLEDGED, AlarmAction.CLEARED)); notificationRule.setTriggerConfig(triggerConfig); EscalatedNotificationRuleRecipientsConfig recipientsConfig = new EscalatedNotificationRuleRecipientsConfig(); @@ -251,8 +253,6 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { wsClient.close(); }); - - // TODO: test severity changes } @Test @@ -273,6 +273,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { AlarmNotificationRuleTriggerConfig triggerConfig = new AlarmNotificationRuleTriggerConfig(); triggerConfig.setAlarmTypes(Set.of(alarmType)); triggerConfig.setAlarmSeverities(null); + triggerConfig.setNotifyOn(Set.of(AlarmAction.CREATED, AlarmAction.SEVERITY_CHANGED, AlarmAction.ACKNOWLEDGED)); AlarmNotificationRuleTriggerConfig.ClearRule clearRule = new AlarmNotificationRuleTriggerConfig.ClearRule(); clearRule.setAlarmStatuses(Set.of(AlarmSearchStatus.CLEARED, AlarmSearchStatus.UNACK)); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmApiCallResult.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmApiCallResult.java index a8aebf16e7..07f9b926dd 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmApiCallResult.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmApiCallResult.java @@ -32,16 +32,18 @@ public class AlarmApiCallResult { private final boolean created; private final boolean modified; private final boolean cleared; + private final boolean deleted; private final AlarmInfo alarm; private final Alarm old; private final List propagatedEntitiesList; @Builder - private AlarmApiCallResult(boolean successful, boolean created, boolean modified, boolean cleared, AlarmInfo alarm, Alarm old, List propagatedEntitiesList) { + private AlarmApiCallResult(boolean successful, boolean created, boolean modified, boolean cleared, boolean deleted, AlarmInfo alarm, Alarm old, List propagatedEntitiesList) { this.successful = successful; this.created = created; this.modified = modified; this.cleared = cleared; + this.deleted = deleted; this.alarm = alarm; this.old = old; this.propagatedEntitiesList = propagatedEntitiesList; @@ -52,6 +54,7 @@ public class AlarmApiCallResult { this.created = other.created; this.modified = other.modified; this.cleared = other.cleared; + this.deleted = other.deleted; this.alarm = other.alarm; this.old = other.old; this.propagatedEntitiesList = propagatedEntitiesList; @@ -65,6 +68,14 @@ public class AlarmApiCallResult { } } + public boolean isAcknowledged() { + if (alarm == null || old == null) { + return false; + } else { + return alarm.isAcknowledged() != old.isAcknowledged(); + } + } + public AlarmSeverity getOldSeverity() { return isSeverityChanged() ? old.getSeverity() : null; } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/usagerecord/ApiLimitService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/usagerecord/ApiLimitService.java new file mode 100644 index 0000000000..cd9524e1b1 --- /dev/null +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/usagerecord/ApiLimitService.java @@ -0,0 +1,25 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.usagerecord; + +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.TenantId; + +public interface ApiLimitService { + + boolean checkEntitiesLimit(TenantId tenantId, EntityType entityType); + +} 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 afcfa278f2..a22f6a69d2 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 @@ -129,7 +129,8 @@ public class NotificationProcessingContext { String result = template; for (Map context : contexts) { for (Map.Entry kv : context.entrySet()) { - result = result.replace("${" + kv.getKey() + '}', kv.getValue()); + 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/NotificationType.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationType.java index 79dec384e7..05df8702a3 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationType.java @@ -22,6 +22,9 @@ public enum NotificationType { DEVICE_INACTIVITY, ENTITY_ACTION, ALARM_COMMENT, - RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT + RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT, + ALARM_ASSIGNMENT, + NEW_PLATFORM_VERSION, + ENTITIES_LIMIT } 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 new file mode 100644 index 0000000000..815bf4e3db --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmAssignmentNotificationInfo.java @@ -0,0 +1,78 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.info; + +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.CustomerId; +import org.thingsboard.server.common.data.id.EntityId; + +import java.util.Map; +import java.util.UUID; + +import static org.thingsboard.server.common.data.util.CollectionsUtil.mapOf; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class AlarmAssignmentNotificationInfo implements RuleOriginatedNotificationInfo { + + private String assigneeFirstName; + private String assigneeLastName; + private String assigneeEmail; + private String userName; + + private String alarmType; + private UUID alarmId; + private EntityId alarmOriginator; + private String alarmOriginatorName; + private AlarmSeverity alarmSeverity; + private AlarmStatus alarmStatus; + private CustomerId alarmCustomerId; + + @Override + public Map getTemplateData() { + return mapOf( + "assigneeFirstName", assigneeFirstName, + "assigneeLastName", assigneeLastName, + "assigneeEmail", assigneeEmail, + "userName", userName, + "alarmType", alarmType, + "alarmId", alarmId.toString(), + "alarmSeverity", alarmSeverity.toString(), + "alarmStatus", alarmStatus.toString(), + "alarmOriginatorEntityType", alarmOriginator.getEntityType().toString(), + "alarmOriginatorId", alarmOriginator.getId().toString(), + "alarmOriginatorName", alarmOriginatorName + ); + } + + @Override + public CustomerId getOriginatorEntityCustomerId() { + return alarmCustomerId; + } + + @Override + public EntityId getStateEntityId() { + return alarmOriginator; + } + +} 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 20764f56f4..3fce737194 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 @@ -21,37 +21,50 @@ 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.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import java.util.Map; import java.util.UUID; +import static org.thingsboard.server.common.data.util.CollectionsUtil.mapOf; + @Data @AllArgsConstructor @NoArgsConstructor @Builder -public class AlarmCommentNotificationInfo implements NotificationInfo { +public class AlarmCommentNotificationInfo implements RuleOriginatedNotificationInfo { private String comment; + private String userName; private String alarmType; private UUID alarmId; private EntityId alarmOriginator; + private String alarmOriginatorName; private AlarmSeverity alarmSeverity; private AlarmStatus alarmStatus; + private CustomerId alarmCustomerId; @Override public Map getTemplateData() { - return Map.of( + return mapOf( "comment", comment, + "userName", userName, "alarmType", alarmType, "alarmId", alarmId.toString(), "alarmSeverity", alarmSeverity.toString(), "alarmStatus", alarmStatus.toString(), "alarmOriginatorEntityType", alarmOriginator.getEntityType().toString(), - "alarmOriginatorId", alarmOriginator.getId().toString() + "alarmOriginatorId", alarmOriginator.getId().toString(), + "alarmOriginatorName", alarmOriginatorName ); } + @Override + public CustomerId getOriginatorEntityCustomerId() { + return alarmCustomerId; + } + @Override public EntityId getStateEntityId() { return alarmOriginator; 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 4f387e3d86..502d6069dd 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 @@ -27,6 +27,8 @@ import org.thingsboard.server.common.data.id.EntityId; import java.util.Map; import java.util.UUID; +import static org.thingsboard.server.common.data.util.CollectionsUtil.mapOf; + @Data @NoArgsConstructor @AllArgsConstructor @@ -36,27 +38,30 @@ public class AlarmNotificationInfo implements RuleOriginatedNotificationInfo { private String alarmType; private UUID alarmId; private EntityId alarmOriginator; + private String alarmOriginatorName; private AlarmSeverity alarmSeverity; private AlarmStatus alarmStatus; private CustomerId alarmCustomerId; - @Override - public CustomerId getOriginatorEntityCustomerId() { - return alarmCustomerId; - } - @Override public Map getTemplateData() { - return Map.of( + // TODO: readable status change + return mapOf( "alarmType", alarmType, "alarmId", alarmId.toString(), "alarmSeverity", alarmSeverity.toString(), "alarmStatus", alarmStatus.toString(), "alarmOriginatorEntityType", alarmOriginator.getEntityType().toString(), + "alarmOriginatorName", alarmOriginatorName, "alarmOriginatorId", alarmOriginator.getId().toString() ); } + @Override + public CustomerId getOriginatorEntityCustomerId() { + return alarmCustomerId; + } + @Override public EntityId getStateEntityId() { return alarmOriginator; 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 8a2d6941a0..6a4647ac62 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,6 +15,7 @@ */ package org.thingsboard.server.common.data.notification.info; +import com.google.common.base.Strings; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -26,6 +27,8 @@ import org.thingsboard.server.common.data.id.EntityId; import java.util.Map; import java.util.UUID; +import static org.thingsboard.server.common.data.util.CollectionsUtil.mapOf; + @Data @NoArgsConstructor @AllArgsConstructor @@ -34,6 +37,7 @@ public class DeviceInactivityNotificationInfo implements RuleOriginatedNotificat private UUID deviceId; private String deviceName; + private String deviceLabel; private String deviceType; private CustomerId deviceCustomerId; @@ -44,9 +48,10 @@ public class DeviceInactivityNotificationInfo implements RuleOriginatedNotificat @Override public Map getTemplateData() { - return Map.of( + return mapOf( "deviceId", deviceId.toString(), "deviceName", deviceName, + "deviceLabel", deviceLabel, "deviceType", deviceType ); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EntitiesLimitNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EntitiesLimitNotificationInfo.java new file mode 100644 index 0000000000..add9f9d5d7 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EntitiesLimitNotificationInfo.java @@ -0,0 +1,45 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.info; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.EntityType; + +import java.util.Map; + +import static org.thingsboard.server.common.data.util.CollectionsUtil.mapOf; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class EntitiesLimitNotificationInfo implements NotificationInfo { + + private EntityType entityType; + private int threshold; + + @Override + public Map getTemplateData() { + // FIXME: readable entity type name, e.g. 'Devices' + return mapOf( + "entityType", entityType.name(), + "threshold", String.valueOf(threshold) + ); + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EntityActionNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EntityActionNotificationInfo.java index 60061d3a78..b7e49183ea 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EntityActionNotificationInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EntityActionNotificationInfo.java @@ -26,6 +26,8 @@ import org.thingsboard.server.common.data.id.EntityId; import java.util.Map; import java.util.UUID; +import static org.thingsboard.server.common.data.util.CollectionsUtil.mapOf; + @Data @NoArgsConstructor @AllArgsConstructor @@ -46,7 +48,7 @@ public class EntityActionNotificationInfo implements RuleOriginatedNotificationI @Override public Map getTemplateData() { - return Map.of( + return mapOf( "entityType", entityId.getEntityType().name(), "entityId", entityId.toString(), "entityName", entityName, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/NewPlatformVersionNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/NewPlatformVersionNotificationInfo.java new file mode 100644 index 0000000000..0e85d95fd0 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/NewPlatformVersionNotificationInfo.java @@ -0,0 +1,42 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.info; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Map; + +import static org.thingsboard.server.common.data.util.CollectionsUtil.mapOf; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class NewPlatformVersionNotificationInfo implements NotificationInfo { + + private String message; + + @Override + public Map getTemplateData() { + return mapOf( + "message", message + ); + } + +} 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 0dc942c588..45c1c689ac 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 @@ -15,18 +15,18 @@ */ package org.thingsboard.server.common.data.notification.info; -import com.google.common.base.Strings; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import org.apache.commons.lang3.StringUtils; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import java.util.Map; +import static org.thingsboard.server.common.data.util.CollectionsUtil.mapOf; + @Data @AllArgsConstructor @NoArgsConstructor @@ -42,7 +42,7 @@ public class RuleEngineComponentLifecycleEventNotificationInfo implements Notifi @Override public Map getTemplateData() { - return Map.of( + return mapOf( "ruleChainId", ruleChainId.toString(), "ruleChainName", ruleChainName, "componentId", componentId.toString(), 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 563f5c308b..949ab40ff7 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 @@ -47,6 +47,7 @@ public class NotificationRule extends BaseData implements Ha @NotNull private NotificationRuleTriggerType triggerType; @NotNull + @Valid private NotificationRuleTriggerConfig triggerConfig; @NotNull @Valid 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 new file mode 100644 index 0000000000..88e2c5fd76 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmAssignmentNotificationRuleTriggerConfig.java @@ -0,0 +1,37 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.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 AlarmAssignmentNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { + + private Set alarmTypes; + private Set alarmSeverities; + private Set alarmStatuses; + private boolean notifyOnUnassign; + + @Override + public NotificationRuleTriggerType getTriggerType() { + return NotificationRuleTriggerType.ALARM_ASSIGNMENT; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmCommentNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmCommentNotificationRuleTriggerConfig.java index f8cae97c0e..3cfdd7ae3c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmCommentNotificationRuleTriggerConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmCommentNotificationRuleTriggerConfig.java @@ -27,6 +27,7 @@ public class AlarmCommentNotificationRuleTriggerConfig implements NotificationRu private Set alarmTypes; private Set alarmSeverities; private Set alarmStatuses; + private boolean onlyUserComments; @Override public NotificationRuleTriggerType getTriggerType() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmNotificationRuleTriggerConfig.java index 70d986cdf7..aa692fd9fe 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmNotificationRuleTriggerConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmNotificationRuleTriggerConfig.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 @@ -26,6 +27,9 @@ public class AlarmNotificationRuleTriggerConfig implements NotificationRuleTrigg private Set alarmTypes; private Set alarmSeverities; + @NotEmpty + private Set notifyOn; + private ClearRule clearRule; @Override @@ -38,4 +42,8 @@ public class AlarmNotificationRuleTriggerConfig implements NotificationRuleTrigg private Set alarmStatuses; } + public enum AlarmAction { + CREATED, SEVERITY_CHANGED, ACKNOWLEDGED, CLEARED + } + } 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 new file mode 100644 index 0000000000..b799d4b81c --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EntitiesLimitNotificationRuleTriggerConfig.java @@ -0,0 +1,36 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.rule.trigger; + +import lombok.Data; +import org.thingsboard.server.common.data.EntityType; + +import javax.validation.constraints.Max; +import java.util.Set; + +@Data +public class EntitiesLimitNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { + + private Set entityTypes; + @Max(1) + private float threshold; // in percents, + + @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 new file mode 100644 index 0000000000..230be3b9d5 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NewPlatformVersionNotificationRuleTriggerConfig.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.rule.trigger; + +import lombok.Data; + +@Data +public class NewPlatformVersionNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { + + @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/NotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTriggerConfig.java index 99c63e525e..c60c72b8de 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTriggerConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTriggerConfig.java @@ -27,7 +27,10 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; @Type(value = DeviceInactivityNotificationRuleTriggerConfig.class, name = "DEVICE_INACTIVITY"), @Type(value = EntityActionNotificationRuleTriggerConfig.class, name = "ENTITY_ACTION"), @Type(value = AlarmCommentNotificationRuleTriggerConfig.class, name = "ALARM_COMMENT"), - @Type(value = RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig.class, name = "RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT") + @Type(value = RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig.class, name = "RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT"), + @Type(value = AlarmAssignmentNotificationRuleTriggerConfig.class, name = "ALARM_ASSIGNMENT"), + @Type(value = NewPlatformVersionNotificationRuleTriggerConfig.class, name = "NEW_PLATFORM_VERSION"), + @Type(value = EntitiesLimitNotificationRuleTriggerConfig.class, name = "ENTITIES_LIMIT") }) public interface NotificationRuleTriggerConfig { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTriggerType.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTriggerType.java index 64bba1b9df..d843347b5a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTriggerType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTriggerType.java @@ -21,6 +21,9 @@ public enum NotificationRuleTriggerType { ALARM_COMMENT, DEVICE_INACTIVITY, ENTITY_ACTION, - RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT + RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT, + ALARM_ASSIGNMENT, + NEW_PLATFORM_VERSION, + ENTITIES_LIMIT } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java index 44bff695a7..8790017b7d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java @@ -20,6 +20,7 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.thingsboard.server.common.data.ApiUsageRecordKey; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.TenantProfileType; @AllArgsConstructor @@ -107,6 +108,25 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura return (long) (getProfileThreshold(key) * (warnThreshold > 0.0 ? warnThreshold : 0.8)); } + public long getEntitiesLimit(EntityType entityType) { + switch (entityType) { + case DEVICE: + return maxDevices; + case ASSET: + return maxAssets; + case CUSTOMER: + return maxCustomers; + case USER: + return maxUsers; + case DASHBOARD: + return maxDashboards; + case RULE_CHAIN: + return maxRuleChains; + default: + return 0; + } + } + @Override public TenantProfileType getType() { return TenantProfileType.DEFAULT; diff --git a/common/util/src/main/java/org/thingsboard/common/util/CollectionsUtil.java b/common/data/src/main/java/org/thingsboard/server/common/data/util/CollectionsUtil.java similarity index 71% rename from common/util/src/main/java/org/thingsboard/common/util/CollectionsUtil.java rename to common/data/src/main/java/org/thingsboard/server/common/data/util/CollectionsUtil.java index f1f0464425..85d5621ec4 100644 --- a/common/util/src/main/java/org/thingsboard/common/util/CollectionsUtil.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/util/CollectionsUtil.java @@ -13,9 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.common.util; +package org.thingsboard.server.common.data.util; + +import com.google.common.collect.ImmutableMap; import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -47,4 +52,19 @@ public class CollectionsUtil { return count; } + @SuppressWarnings("unchecked") + public static Map mapOf(Object... kvs) { + Map map = new HashMap<>(); + for (int i = 0; i < kvs.length; i += 2) { + K key = (K) kvs[i]; + V value = (V) kvs[i + 1]; + map.put(key, value); + } + return map; + } + + public static Map unmodifiableMapOf(Object... kvs) { + return Collections.unmodifiableMap(mapOf(kvs)); + } + } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/model/LwM2MModelConfig.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/model/LwM2MModelConfig.java index 2014fece71..395cf5450b 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/model/LwM2MModelConfig.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/model/LwM2MModelConfig.java @@ -27,7 +27,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import static org.thingsboard.common.util.CollectionsUtil.diffSets; +import static org.thingsboard.server.common.data.util.CollectionsUtil.diffSets; @Data @NoArgsConstructor diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java index 25e3a6b591..02ccdce3c0 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java @@ -103,7 +103,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import static org.thingsboard.common.util.CollectionsUtil.diffSets; +import static org.thingsboard.server.common.data.util.CollectionsUtil.diffSets; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FW_3_VER_ID; import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FW_DELIVERY_METHOD; diff --git a/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultClusterVersionControlService.java b/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultClusterVersionControlService.java index e30b150d6f..c072d41d5a 100644 --- a/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultClusterVersionControlService.java +++ b/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultClusterVersionControlService.java @@ -29,7 +29,7 @@ import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.event.EventListener; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Service; -import org.thingsboard.common.util.CollectionsUtil; +import org.thingsboard.server.common.data.util.CollectionsUtil; import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.StringUtils; diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java index 0dd2eebf87..72b0eaf7f1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java @@ -185,7 +185,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ } else { deleteEntityRelations(tenantId, alarm.getId()); alarmDao.removeById(tenantId, alarm.getUuidId()); - return AlarmApiCallResult.builder().alarm(alarm).successful(true).build(); + return AlarmApiCallResult.builder().alarm(alarm).deleted(true).successful(true).build(); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index 0d4a5aa1a7..a0804e0c71 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -680,7 +680,6 @@ public class ModelConstants { * */ public static final String NOTIFICATION_TARGET_TABLE_NAME = "notification_target"; - public static final String NOTIFICATION_TARGET_TYPE_PROPERTY = "type"; public static final String NOTIFICATION_TARGET_CONFIGURATION_PROPERTY = "configuration"; public static final String NOTIFICATION_TABLE_NAME = "notification"; @@ -688,7 +687,7 @@ public class ModelConstants { public static final String NOTIFICATION_RECIPIENT_ID_PROPERTY = "recipient_id"; public static final String NOTIFICATION_TYPE_PROPERTY = "type"; public static final String NOTIFICATION_SUBJECT_PROPERTY = "subject"; - public static final String NOTIFICATION_TEXT_PROPERTY = "text"; + public static final String NOTIFICATION_TEXT_PROPERTY = "body"; public static final String NOTIFICATION_ADDITIONAL_CONFIG_PROPERTY = "additional_config"; public static final String NOTIFICATION_STATUS_PROPERTY = "status"; diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java index c2b714d81c..e95fee79ae 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java @@ -17,13 +17,15 @@ package org.thingsboard.server.dao.service; import com.fasterxml.jackson.databind.JsonNode; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.dao.TenantEntityDao; import org.thingsboard.server.dao.TenantEntityWithDataDao; import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.usagerecord.ApiLimitService; import java.util.HashSet; import java.util.Iterator; @@ -32,6 +34,8 @@ import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; +import static org.apache.commons.lang3.StringUtils.capitalize; + @Slf4j public abstract class DataValidator> { private static final Pattern EMAIL_PATTERN = @@ -42,6 +46,9 @@ public abstract class DataValidator> { private static final String NAME = "name"; private static final String TOPIC = "topic"; + @Autowired @Lazy + private ApiLimitService apiLimitService; + // Returns old instance of the same object that is fetched during validation. public D validate(D data, Function tenantIdFunction) { try { @@ -97,15 +104,9 @@ public abstract class DataValidator> { } protected void validateNumberOfEntitiesPerTenant(TenantId tenantId, - TenantEntityDao tenantEntityDao, - long maxEntities, EntityType entityType) { - if (maxEntities > 0) { - long currentEntitiesCount = tenantEntityDao.countByTenantId(tenantId); - if (currentEntitiesCount >= maxEntities) { - throw new DataValidationException(String.format("Can't create more then %d %ss!", - maxEntities, entityType.name().toLowerCase().replaceAll("_", " "))); - } + if (!apiLimitService.checkEntitiesLimit(tenantId, entityType)) { + throw new DataValidationException(String.format("%ss limit reached", capitalize(entityType.name().toLowerCase().replaceAll("_", " ")))); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/AssetDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/AssetDataValidator.java index c524aec551..57ae7d3c5e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/AssetDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/AssetDataValidator.java @@ -24,13 +24,11 @@ import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.dao.asset.AssetDao; import org.thingsboard.server.dao.asset.BaseAssetService; import org.thingsboard.server.dao.customer.CustomerDao; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; -import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantService; import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; @@ -48,17 +46,10 @@ public class AssetDataValidator extends DataValidator { @Autowired private CustomerDao customerDao; - @Autowired - @Lazy - private TbTenantProfileCache tenantProfileCache; - @Override protected void validateCreate(TenantId tenantId, Asset asset) { - DefaultTenantProfileConfiguration profileConfiguration = - (DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); if (!BaseAssetService.TB_SERVICE_QUEUE.equals(asset.getType())) { - long maxAssets = profileConfiguration.getMaxAssets(); - validateNumberOfEntitiesPerTenant(tenantId, assetDao, maxAssets, EntityType.ASSET); + validateNumberOfEntitiesPerTenant(tenantId, EntityType.ASSET); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/CustomerDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/CustomerDataValidator.java index 0e0c66550c..0f901e7ea2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/CustomerDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/CustomerDataValidator.java @@ -16,18 +16,15 @@ package org.thingsboard.server.dao.service.validator; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.dao.customer.CustomerDao; import org.thingsboard.server.dao.customer.CustomerServiceImpl; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; -import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantService; import java.util.Optional; @@ -41,17 +38,9 @@ public class CustomerDataValidator extends DataValidator { @Autowired private TenantService tenantService; - @Autowired - @Lazy - private TbTenantProfileCache tenantProfileCache; - @Override protected void validateCreate(TenantId tenantId, Customer customer) { - DefaultTenantProfileConfiguration profileConfiguration = - (DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); - long maxCustomers = profileConfiguration.getMaxCustomers(); - - validateNumberOfEntitiesPerTenant(tenantId, customerDao, maxCustomers, EntityType.CUSTOMER); + validateNumberOfEntitiesPerTenant(tenantId, EntityType.CUSTOMER); customerDao.findCustomersByTenantIdAndTitle(customer.getTenantId().getId(), customer.getTitle()).ifPresent( c -> { throw new DataValidationException("Customer with such title already exists!"); diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/DashboardDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/DashboardDataValidator.java index 8b649faf63..246944199a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/DashboardDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/DashboardDataValidator.java @@ -16,17 +16,14 @@ package org.thingsboard.server.dao.service.validator; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.dao.dashboard.DashboardDao; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; -import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantService; @Component @@ -38,16 +35,9 @@ public class DashboardDataValidator extends DataValidator { @Autowired private TenantService tenantService; - @Autowired - @Lazy - private TbTenantProfileCache tenantProfileCache; - @Override protected void validateCreate(TenantId tenantId, Dashboard data) { - DefaultTenantProfileConfiguration profileConfiguration = - (DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); - long maxDashboards = profileConfiguration.getMaxDashboards(); - validateNumberOfEntitiesPerTenant(tenantId, dashboardDao, maxDashboards, EntityType.DASHBOARD); + validateNumberOfEntitiesPerTenant(tenantId, EntityType.DASHBOARD); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceDataValidator.java index aff6b83169..51517a4dc2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceDataValidator.java @@ -16,7 +16,6 @@ package org.thingsboard.server.dao.service.validator; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Device; @@ -25,11 +24,9 @@ import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.device.data.DeviceTransportConfiguration; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.dao.customer.CustomerDao; import org.thingsboard.server.dao.device.DeviceDao; import org.thingsboard.server.dao.exception.DataValidationException; -import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantService; import java.util.Optional; @@ -48,16 +45,9 @@ public class DeviceDataValidator extends AbstractHasOtaPackageValidator @Autowired private CustomerDao customerDao; - @Autowired - @Lazy - private TbTenantProfileCache tenantProfileCache; - @Override protected void validateCreate(TenantId tenantId, Device device) { - DefaultTenantProfileConfiguration profileConfiguration = - (DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); - long maxDevices = profileConfiguration.getMaxDevices(); - validateNumberOfEntitiesPerTenant(tenantId, deviceDao, maxDevices, EntityType.DEVICE); + validateNumberOfEntitiesPerTenant(tenantId, EntityType.DEVICE); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/RuleChainDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/RuleChainDataValidator.java index 8a1f6319d1..ac145034a5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/RuleChainDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/RuleChainDataValidator.java @@ -60,16 +60,9 @@ public class RuleChainDataValidator extends DataValidator { @Autowired private TenantService tenantService; - @Autowired - @Lazy - private TbTenantProfileCache tenantProfileCache; - @Override protected void validateCreate(TenantId tenantId, RuleChain data) { - DefaultTenantProfileConfiguration profileConfiguration = - (DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); - long maxRuleChains = profileConfiguration.getMaxRuleChains(); - validateNumberOfEntitiesPerTenant(tenantId, ruleChainDao, maxRuleChains, EntityType.RULE_CHAIN); + validateNumberOfEntitiesPerTenant(tenantId, EntityType.RULE_CHAIN); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/UserDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/UserDataValidator.java index 1c09073036..dc77afcfc2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/UserDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/UserDataValidator.java @@ -19,19 +19,16 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.Customer; -import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.security.Authority; -import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.dao.customer.CustomerDao; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.service.DataValidator; -import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.user.UserDao; import org.thingsboard.server.dao.user.UserService; @@ -49,10 +46,6 @@ public class UserDataValidator extends DataValidator { @Autowired private CustomerDao customerDao; - @Autowired - @Lazy - private TbTenantProfileCache tenantProfileCache; - @Autowired @Lazy private TenantService tenantService; @@ -60,10 +53,7 @@ public class UserDataValidator extends DataValidator { @Override protected void validateCreate(TenantId tenantId, User user) { if (!user.getTenantId().getId().equals(ModelConstants.NULL_UUID)) { - DefaultTenantProfileConfiguration profileConfiguration = - (DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); - long maxUsers = profileConfiguration.getMaxUsers(); - validateNumberOfEntitiesPerTenant(tenantId, userDao, maxUsers, EntityType.USER); + validateNumberOfEntitiesPerTenant(tenantId, EntityType.USER); } } diff --git a/dao/src/main/resources/sql/schema-entities-idx.sql b/dao/src/main/resources/sql/schema-entities-idx.sql index 34359302ad..96fb740088 100644 --- a/dao/src/main/resources/sql/schema-entities-idx.sql +++ b/dao/src/main/resources/sql/schema-entities-idx.sql @@ -94,14 +94,17 @@ CREATE INDEX IF NOT EXISTS idx_notification_target_tenant_id_created_time ON not CREATE INDEX IF NOT EXISTS idx_notification_template_tenant_id_created_time ON notification_template(tenant_id, created_time DESC); -CREATE INDEX IF NOT EXISTS idx_notification_rule_tenant_id_created_time ON notification_rule(tenant_id, created_time DESC); +CREATE INDEX IF NOT EXISTS idx_notification_rule_tenant_id_trigger_type_created_time ON notification_rule(tenant_id, trigger_type, created_time DESC); -CREATE INDEX IF NOT EXISTS idx_notification_request_tenant_id_originator_type_created_time ON notification_request(tenant_id, originator_entity_type, created_time DESC); +CREATE INDEX IF NOT EXISTS idx_notification_request_tenant_id_user_created_time ON notification_request(tenant_id, created_time DESC) + WHERE originator_entity_type = 'USER'; -CREATE INDEX IF NOT EXISTS idx_notification_request_rule_id_originator_entity_id ON notification_request(rule_id, originator_entity_id); +CREATE INDEX IF NOT EXISTS idx_notification_request_rule_id_originator_entity_id ON notification_request(rule_id, originator_entity_id) + WHERE originator_entity_type = 'ALARM'; -CREATE INDEX IF NOT EXISTS idx_notification_request_status ON notification_request(status); +CREATE INDEX IF NOT EXISTS idx_notification_request_status ON notification_request(status) + WHERE status = 'SCHEDULED'; -CREATE INDEX IF NOT EXISTS idx_notification_id_recipient_id ON notification(id, recipient_id); +CREATE INDEX IF NOT EXISTS idx_notification_id ON notification(id); -CREATE INDEX IF NOT EXISTS idx_notification_recipient_id_status_created_time ON notification(recipient_id, status, created_time DESC); +CREATE INDEX IF NOT EXISTS idx_notification_recipient_id_created_time ON notification(recipient_id, created_time DESC); diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 17a989a53e..7d47138bab 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -844,7 +844,7 @@ CREATE TABLE IF NOT EXISTS notification ( recipient_id UUID NOT NULL CONSTRAINT fk_notification_recipient_id REFERENCES tb_user(id) ON DELETE CASCADE, type VARCHAR(50) NOT NULL, subject VARCHAR(255), - text VARCHAR(1000) NOT NULL, + body VARCHAR(1000) NOT NULL, additional_config VARCHAR(1000), status VARCHAR(32) ) PARTITION BY RANGE (created_time); @@ -1021,7 +1021,7 @@ BEGIN UPDATE alarm a SET acknowledged = true, ack_ts = a_ts WHERE a.id = a_id AND a.tenant_id = t_id; END IF; SELECT * INTO result FROM alarm_info a WHERE a.id = a_id AND a.tenant_id = t_id; - RETURN json_build_object('success', true, 'modified', modified, 'alarm', row_to_json(result))::text; + RETURN json_build_object('success', true, 'modified', modified, 'alarm', row_to_json(result), 'old', row_to_json(existing))::text; END $$; diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java index 2fcc1f0e39..029531494e 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java @@ -22,7 +22,7 @@ import com.google.gson.JsonParser; import com.google.gson.JsonPrimitive; import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; -import org.thingsboard.common.util.CollectionsUtil; +import org.thingsboard.server.common.data.util.CollectionsUtil; import org.thingsboard.common.util.DonAsynchron; import org.thingsboard.rule.engine.api.EmptyNodeConfiguration; import org.thingsboard.rule.engine.api.RuleNode;