diff --git a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java index ccdac8b363..702a73decc 100644 --- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java +++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java @@ -210,6 +210,9 @@ public class ThingsboardInstallService { log.info("Upgrading ThingsBoard from version 3.3.0 to 3.3.1 ..."); case "3.3.1": log.info("Upgrading ThingsBoard from version 3.3.1 to 3.3.2 ..."); + case "3.3.2": + log.info("Upgrading ThingsBoard from version 3.3.2 to 3.3.3 ..."); + dataUpdateService.updateData("3.3.2"); log.info("Updating system data..."); systemDataLoaderService.updateSystemWidgets(); break; diff --git a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java index de483dfda8..e244a0f0b0 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java @@ -25,6 +25,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.flow.TbRuleChainInputNode; +import org.thingsboard.rule.engine.flow.TbRuleChainInputNodeConfiguration; import org.thingsboard.rule.engine.profile.TbDeviceProfileNode; import org.thingsboard.rule.engine.profile.TbDeviceProfileNodeConfiguration; import org.thingsboard.server.common.data.EntityView; @@ -34,6 +36,8 @@ import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmQuery; import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.id.EntityViewId; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; import org.thingsboard.server.common.data.kv.ReadTsKvQuery; @@ -43,8 +47,11 @@ import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.query.DynamicValue; import org.thingsboard.server.common.data.query.FilterPredicateValue; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; +import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.alarm.AlarmDao; @@ -52,7 +59,9 @@ import org.thingsboard.server.dao.alarm.AlarmService; import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.model.sql.DeviceProfileEntity; +import org.thingsboard.server.dao.model.sql.RelationEntity; import org.thingsboard.server.dao.oauth2.OAuth2Service; +import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.sql.device.DeviceProfileRepository; import org.thingsboard.server.dao.tenant.TenantService; @@ -76,6 +85,9 @@ public class DefaultDataUpdateService implements DataUpdateService { @Autowired private TenantService tenantService; + @Autowired + private RelationService relationService; + @Autowired private RuleChainService ruleChainService; @@ -125,6 +137,10 @@ public class DefaultDataUpdateService implements DataUpdateService { deviceProfileEntityDynamicConditionsUpdater.updateEntities(null); updateOAuth2Params(); break; + case "3.3.2": + log.info("Updating data from version 3.3.2 to 3.3.3 ..."); + nestedRuleNodeUpdater.updateEntities(null); + break; default: throw new RuntimeException("Unable to update data, unsupported fromVersion: " + fromVersion); } @@ -209,6 +225,74 @@ public class DefaultDataUpdateService implements DataUpdateService { } }; + private final PaginatedUpdater nestedRuleNodeUpdater = + new PaginatedUpdater<>() { + + @Override + protected String getName() { + return "Tenants nested rule chain updater"; + } + + @Override + protected boolean forceReportTotal() { + return true; + } + + @Override + protected PageData findEntities(String region, PageLink pageLink) { + return tenantService.findTenants(pageLink); + } + + @Override + protected void updateEntity(Tenant tenant) { + try { + var tenantId = tenant.getId(); + var packSize = 1024; + boolean hasNext = true; + while (hasNext) { + List relations = relationService.findRuleNodeToRuleChainRelations(tenantId, RuleChainType.CORE, packSize); + hasNext = relations.size() == packSize; + for (EntityRelation relation : relations) { + + RuleNode sourceNode = ruleChainService.findRuleNodeById(tenantId, new RuleNodeId(relation.getFrom().getId())); + RuleChainId sourceRuleChainId = sourceNode.getRuleChainId(); + RuleChain targetRuleChain = ruleChainService.findRuleChainById(tenantId, new RuleChainId(relation.getTo().getId())); + RuleNode targetNode = new RuleNode(); + targetNode.setName(targetRuleChain.getName()); + targetNode.setRuleChainId(sourceRuleChainId); + targetNode.setType(TbRuleChainInputNode.class.getName()); + TbRuleChainInputNodeConfiguration configuration = new TbRuleChainInputNodeConfiguration(); + configuration.setRuleChainId(targetRuleChain.getId().toString()); + targetNode.setConfiguration(JacksonUtil.valueToTree(configuration)); + targetNode.setAdditionalInfo(relation.getAdditionalInfo()); + targetNode.setDebugMode(false); + targetNode = ruleChainService.saveRuleNode(tenantId, targetNode); + + EntityRelation sourceRuleChainToRuleNode = new EntityRelation(); + sourceRuleChainToRuleNode.setFrom(sourceRuleChainId); + sourceRuleChainToRuleNode.setTo(targetNode.getId()); + sourceRuleChainToRuleNode.setType(EntityRelation.CONTAINS_TYPE); + sourceRuleChainToRuleNode.setTypeGroup(RelationTypeGroup.RULE_CHAIN); + relationService.saveRelation(tenantId, sourceRuleChainToRuleNode); + + EntityRelation sourceRuleNodeToTargetRuleNode = new EntityRelation(); + sourceRuleNodeToTargetRuleNode.setFrom(sourceNode.getId()); + sourceRuleNodeToTargetRuleNode.setTo(targetNode.getId()); + sourceRuleNodeToTargetRuleNode.setType(relation.getType()); + sourceRuleNodeToTargetRuleNode.setTypeGroup(RelationTypeGroup.RULE_NODE); + sourceRuleNodeToTargetRuleNode.setAdditionalInfo(relation.getAdditionalInfo()); + relationService.saveRelation(tenantId, sourceRuleNodeToTargetRuleNode); + + //Delete old relation + relationService.deleteRelation(tenantId, relation); + } + } + } catch (Exception e) { + log.error("Unable to update Tenant", e); + } + } + }; + private final PaginatedUpdater tenantsDefaultEdgeRuleChainUpdater = new PaginatedUpdater<>() { diff --git a/application/src/main/java/org/thingsboard/server/service/rule/DefaultTbRuleChainService.java b/application/src/main/java/org/thingsboard/server/service/rule/DefaultTbRuleChainService.java index 414abcb0ef..e6d1ba1d8a 100644 --- a/application/src/main/java/org/thingsboard/server/service/rule/DefaultTbRuleChainService.java +++ b/application/src/main/java/org/thingsboard/server/service/rule/DefaultTbRuleChainService.java @@ -193,6 +193,6 @@ public class DefaultTbRuleChainService implements TbRuleChainService { } private boolean isRuleNode(RuleNode ruleNode, Class clazz) { - return ruleNode.getType().equals(clazz.getName()); + return ruleNode != null && ruleNode.getType().equals(clazz.getName()); } } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/relation/RelationService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/relation/RelationService.java index c8f2a38005..87ac26b41e 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/relation/RelationService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/relation/RelationService.java @@ -16,12 +16,16 @@ package org.thingsboard.server.dao.relation; import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelationInfo; import org.thingsboard.server.common.data.relation.EntityRelationsQuery; import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.common.data.rule.RuleChainType; import java.util.List; @@ -78,6 +82,8 @@ public interface RelationService { void removeRelations(TenantId tenantId, EntityId entityId); + List findRuleNodeToRuleChainRelations(TenantId tenantId, RuleChainType ruleChainType, int limit); + // TODO: This method may be useful for some validations in the future // ListenableFuture checkRecursiveRelation(EntityId from, EntityId to); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java index f46393b5d1..48f16215bb 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java @@ -91,4 +91,6 @@ public interface RuleChainService { PageData findAutoAssignToEdgeRuleChainsByTenantId(TenantId tenantId, PageLink pageLink); List findRuleNodesByTenantIdAndType(TenantId tenantId, String name, String toString); + + RuleNode saveRuleNode(TenantId tenantId, RuleNode ruleNode); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java b/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java index 2be2331a99..1e1d0eee18 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java @@ -30,8 +30,11 @@ import org.springframework.cache.annotation.Caching; import org.springframework.dao.ConcurrencyFailureException; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelationInfo; import org.thingsboard.server.common.data.relation.EntityRelationsQuery; @@ -39,6 +42,7 @@ import org.thingsboard.server.common.data.relation.EntitySearchDirection; import org.thingsboard.server.common.data.relation.RelationEntityTypeFilter; import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.relation.RelationsSearchParameters; +import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.ConstraintValidator; @@ -54,6 +58,7 @@ import java.util.concurrent.ExecutionException; import java.util.function.BiConsumer; import static org.thingsboard.server.common.data.CacheConstants.RELATIONS_CACHE; +import static org.thingsboard.server.dao.service.Validator.validateId; /** * Created by ashvayka on 28.04.17. @@ -194,11 +199,11 @@ public class BaseRelationService implements RelationService { outboundRelations.addAll(relationDao.findAllByFrom(tenantId, entityId, typeGroup)); } - for (EntityRelation relation : inboundRelations){ + for (EntityRelation relation : inboundRelations) { delete(tenantId, cache, relation, true); } - for (EntityRelation relation : outboundRelations){ + for (EntityRelation relation : outboundRelations) { delete(tenantId, cache, relation, false); } @@ -556,6 +561,14 @@ public class BaseRelationService implements RelationService { } } + @Override + public List findRuleNodeToRuleChainRelations(TenantId tenantId, RuleChainType ruleChainType, int limit) { + log.trace("Executing findRuleNodeToRuleChainRelations, tenantId [{}] and limit {}", + tenantId, limit); + validateId(tenantId, "Invalid tenant id: " + tenantId); + return relationDao.findRuleNodeToRuleChainRelations(tenantId, ruleChainType, limit); + } + protected void validate(EntityRelation relation) { if (relation == null) { throw new DataValidationException("Relation type should be specified!"); diff --git a/dao/src/main/java/org/thingsboard/server/dao/relation/RelationDao.java b/dao/src/main/java/org/thingsboard/server/dao/relation/RelationDao.java index d60f176832..efb516af3e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/relation/RelationDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/relation/RelationDao.java @@ -16,13 +16,11 @@ package org.thingsboard.server.dao.relation; import com.google.common.util.concurrent.ListenableFuture; -import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.page.PageData; -import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.common.data.rule.RuleChainType; import java.util.List; @@ -63,4 +61,5 @@ public interface RelationDao { ListenableFuture deleteOutboundRelationsAsync(TenantId tenantId, EntityId entity); + List findRuleNodeToRuleChainRelations(TenantId tenantId, RuleChainType ruleChainType, int limit); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java index b747fd4851..e1aaa31d3c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java @@ -28,6 +28,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.Tenant; @@ -670,6 +671,11 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC return ruleNodeDao.findRuleNodesByTenantIdAndType(tenantId, type, search); } + @Override + public RuleNode saveRuleNode(TenantId tenantId, RuleNode ruleNode) { + return ruleNodeDao.save(tenantId, ruleNode); + } + private void checkRuleNodesAndDelete(TenantId tenantId, RuleChainId ruleChainId) { try { ruleChainDao.removeById(tenantId, ruleChainId.getId()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/relation/JpaRelationDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/relation/JpaRelationDao.java index 2eaa5c28a2..5291da6669 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/relation/JpaRelationDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/relation/JpaRelationDao.java @@ -19,6 +19,7 @@ import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.ConcurrencyFailureException; +import org.springframework.data.domain.PageRequest; import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.EntityType; @@ -26,6 +27,7 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.model.sql.RelationCompositeKey; import org.thingsboard.server.dao.model.sql.RelationEntity; @@ -196,11 +198,16 @@ public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService imple }); } + @Override + public List findRuleNodeToRuleChainRelations(TenantId tenantId, RuleChainType ruleChainType, int limit) { + return DaoUtil.convertDataList(relationRepository.findRuleNodeToRuleChainRelations(tenantId.getId(), ruleChainType, PageRequest.of(0, limit))); + } + private Specification getEntityFieldsSpec(EntityId from, String relationType, RelationTypeGroup typeGroup, EntityType childType) { return (root, criteriaQuery, criteriaBuilder) -> { List predicates = new ArrayList<>(); if (from != null) { - Predicate fromIdPredicate = criteriaBuilder.equal(root.get("fromId"), from.getId()); + Predicate fromIdPredicate = criteriaBuilder.equal(root.get("fromId"), from.getId()); predicates.add(fromIdPredicate); Predicate fromEntityTypePredicate = criteriaBuilder.equal(root.get("fromType"), from.getEntityType().name()); predicates.add(fromEntityTypePredicate); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/relation/RelationRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/relation/RelationRepository.java index c5a1774ae6..28b125ac3c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/relation/RelationRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/relation/RelationRepository.java @@ -15,11 +15,20 @@ */ package org.thingsboard.server.dao.sql.relation; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.query.Param; import org.springframework.transaction.annotation.Transactional; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.dao.model.sql.RelationCompositeKey; import org.thingsboard.server.dao.model.sql.RelationEntity; +import org.thingsboard.server.dao.model.sql.RuleChainEntity; import java.util.List; import java.util.UUID; @@ -48,6 +57,17 @@ public interface RelationRepository List findAllByFromIdAndFromType(UUID fromId, String fromType); + @Query("SELECT r FROM RelationEntity r WHERE " + + "r.fromId in (SELECT id from RuleNodeEntity where ruleChainId in " + + "(SELECT id from RuleChainEntity where tenantId = :tenantId and type = :ruleChainType ))" + + "AND r.fromType = 'RULE_NODE' " + + "AND r.toType = 'RULE_CHAIN' " + + "AND r.relationTypeGroup = 'RULE_NODE'") + List findRuleNodeToRuleChainRelations( + @Param("tenantId") UUID tenantId, + @Param("ruleChainType") RuleChainType ruleChainType, + Pageable page); + @Transactional S save(S entity); @@ -56,4 +76,5 @@ public interface RelationRepository @Transactional void deleteByFromIdAndFromType(UUID fromId, String fromType); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleChainRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleChainRepository.java index 750b59e363..9b80dad34f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleChainRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleChainRepository.java @@ -20,7 +20,11 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.query.Param; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.rule.RuleChainType; +import org.thingsboard.server.dao.model.sql.RelationEntity; import org.thingsboard.server.dao.model.sql.RuleChainEntity; import java.util.List; @@ -59,6 +63,7 @@ public interface RuleChainRepository extends PagingAndSortingRepository