Refactoring for input node relations

This commit is contained in:
ViacheslavKlimov 2025-05-07 12:17:24 +03:00
parent ac41220a6d
commit 381e976d87
3 changed files with 117 additions and 125 deletions

View File

@ -24,21 +24,18 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile; import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageDataIterable; import org.thingsboard.server.common.data.page.PageDataIterable;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.query.DynamicValue; import org.thingsboard.server.common.data.query.DynamicValue;
import org.thingsboard.server.common.data.query.FilterPredicateValue; import org.thingsboard.server.common.data.query.FilterPredicateValue;
import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.service.component.ComponentDiscoveryService; import org.thingsboard.server.service.component.ComponentDiscoveryService;
import org.thingsboard.server.service.component.RuleNodeClassInfo; import org.thingsboard.server.service.component.RuleNodeClassInfo;
import org.thingsboard.server.service.install.DbUpgradeExecutorService; import org.thingsboard.server.service.install.DbUpgradeExecutorService;
@ -46,6 +43,7 @@ import org.thingsboard.server.utils.TbNodeUpgradeUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
@ -65,9 +63,6 @@ public class DefaultDataUpdateService implements DataUpdateService {
@Autowired @Autowired
private RelationService relationService; private RelationService relationService;
@Autowired
private TenantService tenantService;
@Autowired @Autowired
private ComponentDiscoveryService componentDiscoveryService; private ComponentDiscoveryService componentDiscoveryService;
@ -78,52 +73,36 @@ public class DefaultDataUpdateService implements DataUpdateService {
public void updateData() throws Exception { public void updateData() throws Exception {
log.info("Updating data ..."); log.info("Updating data ...");
//TODO: should be cleaned after each release //TODO: should be cleaned after each release
inputNodesUpdater.updateEntities(); updateInputNodes();
log.info("Data updated."); log.info("Data updated.");
} }
//TODO: should be removed after release private void updateInputNodes() {
private final PaginatedUpdater<String, Tenant> inputNodesUpdater = new PaginatedUpdater<>() { log.info("Creating relations for input nodes...");
@Override int n = 0;
protected String getName() { var inputNodes = new PageDataIterable<>(pageLink -> ruleChainService.findAllRuleNodesByType(TB_RULE_CHAIN_INPUT_NODE, pageLink), 1024);
return "Input nodes updater"; for (RuleNode inputNode : inputNodes) {
try {
RuleChainId targetRuleChainId = Optional.ofNullable(inputNode.getConfiguration().get("ruleChainId"))
.filter(JsonNode::isTextual).map(JsonNode::asText).map(id -> new RuleChainId(UUID.fromString(id)))
.orElse(null);
if (targetRuleChainId == null) {
continue;
} }
@Override
protected PageData<Tenant> findEntities(String type, PageLink pageLink) {
return tenantService.findTenants(pageLink);
}
@Override
protected void updateEntity(Tenant tenant) {
TenantId tenantId = tenant.getId();
try {
var inputNodes = ruleChainService.findRuleNodesByTenantIdAndType(tenantId, TB_RULE_CHAIN_INPUT_NODE);
var resultFutures = inputNodes.stream().map(ruleNode -> {
try {
JsonNode id = ruleNode.getConfiguration().get("ruleChainId");
if (id != null) {
RuleChainId toRuleChainId = new RuleChainId(UUID.fromString(id.asText()));
RuleChainId fromRuleChainId = ruleNode.getRuleChainId();
EntityRelation relation = new EntityRelation(); EntityRelation relation = new EntityRelation();
relation.setFrom(fromRuleChainId); relation.setFrom(inputNode.getRuleChainId());
relation.setTo(toRuleChainId); relation.setTo(targetRuleChainId);
relation.setType(EntityRelation.USES_TYPE); relation.setType(EntityRelation.USES_TYPE);
relation.setTypeGroup(RelationTypeGroup.COMMON); relation.setTypeGroup(RelationTypeGroup.COMMON);
return relationService.saveRelationAsync(tenantId, relation); relationService.saveRelation(TenantId.SYS_TENANT_ID, relation);
} n++;
} catch (Exception e) { } catch (Exception e) {
log.error("[{}] Failed to save relation for input node: [{}]", tenantId, ruleNode, e); log.error("Failed to save relation for input node: {}", inputNode, e);
}
return Futures.immediateFuture(null);
}).toList();
Futures.allAsList(resultFutures).get();
} catch (Exception e) {
log.error("[{}] Unable to update Tenant input nodes", tenantId, e);
} }
} }
}; log.info("Created {} relations for input nodes", n);
}
@Override @Override
public void upgradeRuleNodes() { public void upgradeRuleNodes() {

View File

@ -211,9 +211,8 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
for (RuleNode existingNode : existingRuleNodes) { for (RuleNode existingNode : existingRuleNodes) {
relationService.deleteEntityRelations(tenantId, existingNode.getId()); relationService.deleteEntityRelations(tenantId, existingNode.getId());
if (existingNode.getType().equals(TB_RULE_CHAIN_INPUT_NODE)) { if (existingNode.getType().equals(TB_RULE_CHAIN_INPUT_NODE)) {
if (existingNode.getConfiguration().has("ruleChainId")) { EntityRelation relation = getRuleChainInputRelation(ruleChainId, existingNode);
RuleChainId targetRuleChainId = extractRuleChainIdFromInputNode(existingNode); if (relation != null) {
var relation = createRuleChainInputRelation(ruleChainId, targetRuleChainId);
relationService.deleteRelation(tenantId, relation); relationService.deleteRelation(tenantId, relation);
} }
} }
@ -242,9 +241,8 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
relations.add(new EntityRelation(ruleChainMetaData.getRuleChainId(), savedNode.getId(), relations.add(new EntityRelation(ruleChainMetaData.getRuleChainId(), savedNode.getId(),
EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN)); EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN));
if (node.getType().equals(TB_RULE_CHAIN_INPUT_NODE)) { if (node.getType().equals(TB_RULE_CHAIN_INPUT_NODE)) {
if (node.getConfiguration().has("ruleChainId")) { EntityRelation relation = getRuleChainInputRelation(ruleChainId, node);
RuleChainId targetRuleChainId = extractRuleChainIdFromInputNode(node); if (relation != null) {
var relation = createRuleChainInputRelation(ruleChainId, targetRuleChainId);
relations.add(relation); relations.add(relation);
} }
} }
@ -317,19 +315,20 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
return RuleChainUpdateResult.successful(updatedRuleNodes); return RuleChainUpdateResult.successful(updatedRuleNodes);
} }
private EntityRelation createRuleChainInputRelation(RuleChainId ruleChainId, RuleChainId targetRuleChainId) { private EntityRelation getRuleChainInputRelation(RuleChainId ruleChainId, RuleNode inputNode) {
RuleChainId targetRuleChainId = Optional.ofNullable(inputNode.getConfiguration().get("ruleChainId"))
.filter(JsonNode::isTextual).map(JsonNode::asText).map(id -> new RuleChainId(UUID.fromString(id)))
.orElse(null);
if (targetRuleChainId != null) {
EntityRelation relation = new EntityRelation(); EntityRelation relation = new EntityRelation();
relation.setFrom(ruleChainId); relation.setFrom(ruleChainId);
relation.setTo(targetRuleChainId); relation.setTo(targetRuleChainId);
relation.setType(EntityRelation.USES_TYPE); relation.setType(EntityRelation.USES_TYPE);
relation.setTypeGroup(RelationTypeGroup.COMMON); relation.setTypeGroup(RelationTypeGroup.COMMON);
return relation; return relation;
} else {
return null;
} }
private RuleChainId extractRuleChainIdFromInputNode(RuleNode node) {
JsonNode configuration = node.getConfiguration();
UUID targetUuid = UUID.fromString(configuration.get("ruleChainId").asText());
return new RuleChainId(targetUuid);
} }
@Override @Override

View File

@ -16,7 +16,6 @@
package org.thingsboard.server.dao.service; package org.thingsboard.server.dao.service;
import com.datastax.oss.driver.api.core.uuid.Uuids; import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
@ -46,6 +45,7 @@ import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.function.Function; import java.util.function.Function;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.thingsboard.server.common.data.relation.EntityRelation.USES_TYPE; import static org.thingsboard.server.common.data.relation.EntityRelation.USES_TYPE;
import static org.thingsboard.server.dao.rule.BaseRuleChainService.TB_RULE_CHAIN_INPUT_NODE; import static org.thingsboard.server.dao.rule.BaseRuleChainService.TB_RULE_CHAIN_INPUT_NODE;
@ -277,7 +277,7 @@ public class RuleChainServiceTest extends AbstractServiceTest {
List<RuleNode> ruleNodes = savedRuleChainMetaData.getNodes(); List<RuleNode> ruleNodes = savedRuleChainMetaData.getNodes();
int name3Index = -1; int name3Index = -1;
for (int i=0;i<ruleNodes.size();i++) { for (int i = 0; i < ruleNodes.size(); i++) {
if ("name3".equals(ruleNodes.get(i).getName())) { if ("name3".equals(ruleNodes.get(i).getName())) {
name3Index = i; name3Index = i;
break; break;
@ -363,62 +363,76 @@ public class RuleChainServiceTest extends AbstractServiceTest {
@Test @Test
public void testSaveRuleChainWithInputNode() { public void testSaveRuleChainWithInputNode() {
RuleChain toRuleChain = new RuleChain();
toRuleChain.setName("To Rule Chain");
toRuleChain.setTenantId(tenantId);
RuleChain savedToRuleChain = ruleChainService.saveRuleChain(toRuleChain);
RuleChain fromRuleChain = new RuleChain(); RuleChain fromRuleChain = new RuleChain();
fromRuleChain.setName("From RuleChain"); fromRuleChain.setName("From RuleChain");
fromRuleChain.setTenantId(tenantId); fromRuleChain.setTenantId(tenantId);
RuleChain savedFromRuleChain = ruleChainService.saveRuleChain(fromRuleChain); fromRuleChain = ruleChainService.saveRuleChain(fromRuleChain);
RuleChainId fromRuleChainId = fromRuleChain.getId();
RuleChain toRuleChain1 = new RuleChain();
toRuleChain1.setName("To Rule Chain 1");
toRuleChain1.setTenantId(tenantId);
toRuleChain1 = ruleChainService.saveRuleChain(toRuleChain1);
RuleChainId toRuleChain1Id = toRuleChain1.getId();
RuleChainMetaData ruleChainMetaData = new RuleChainMetaData(); RuleChainMetaData ruleChainMetaData = new RuleChainMetaData();
ruleChainMetaData.setRuleChainId(savedFromRuleChain.getId()); ruleChainMetaData.setRuleChainId(fromRuleChainId);
RuleNode ruleNode = new RuleNode(); RuleNode toRuleChain1Node = new RuleNode();
ruleNode.setName("Input node"); toRuleChain1Node.setName("To Rule Chain 1");
ruleNode.setType(TB_RULE_CHAIN_INPUT_NODE); toRuleChain1Node.setType(TB_RULE_CHAIN_INPUT_NODE);
ObjectNode configuration = JacksonUtil.newObjectNode(); toRuleChain1Node.setConfiguration(JacksonUtil.newObjectNode()
configuration.put("ruleChainId", savedToRuleChain.getId().toString()); .put("ruleChainId", toRuleChain1Id.toString()));
ruleNode.setConfiguration(configuration);
RuleNode toRuleChain1Node2 = new RuleNode();
toRuleChain1Node2.setName("To Rule Chain 1");
toRuleChain1Node2.setType(TB_RULE_CHAIN_INPUT_NODE);
toRuleChain1Node2.setConfiguration(JacksonUtil.newObjectNode()
.put("ruleChainId", toRuleChain1Id.toString()));
List<RuleNode> ruleNodes = new ArrayList<>(); List<RuleNode> ruleNodes = new ArrayList<>();
ruleNodes.add(ruleNode); ruleNodes.add(toRuleChain1Node);
ruleNodes.add(toRuleChain1Node2);
ruleChainMetaData.setFirstNodeIndex(0); ruleChainMetaData.setFirstNodeIndex(0);
ruleChainMetaData.setNodes(ruleNodes); ruleChainMetaData.setNodes(ruleNodes);
ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData, Function.identity()); ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData, Function.identity());
List<EntityRelation> relations = relationService.findByFromAndType(tenantId, savedFromRuleChain.getId(), USES_TYPE, RelationTypeGroup.COMMON); List<EntityRelation> relations = relationService.findByFromAndType(tenantId, fromRuleChainId, USES_TYPE, RelationTypeGroup.COMMON);
Assert.assertEquals(1, relations.size()); assertThat(relations).singleElement().satisfies(relationToRuleChain1 -> {
EntityRelation usesRelation = relations.get(0); assertThat(relationToRuleChain1.getFrom()).isEqualTo(fromRuleChainId);
Assert.assertEquals(savedFromRuleChain.getId(), usesRelation.getFrom()); assertThat(relationToRuleChain1.getTo()).isEqualTo(toRuleChain1Id);
Assert.assertEquals(savedToRuleChain.getId(), usesRelation.getTo()); });
RuleChain newToRuleChain = new RuleChain(); RuleChain toRuleChain2 = new RuleChain();
newToRuleChain.setName("New To Rule Chain"); toRuleChain2.setName("To Rule Chain 2");
newToRuleChain.setTenantId(tenantId); toRuleChain2.setTenantId(tenantId);
RuleChain savedNewToRuleChain = ruleChainService.saveRuleChain(newToRuleChain); toRuleChain2 = ruleChainService.saveRuleChain(toRuleChain2);
RuleChainId toRuleChain2Id = toRuleChain2.getId();
RuleNode newRuleNode = new RuleNode(); RuleNode toRuleChain2Node = new RuleNode();
newRuleNode.setName("Input node"); toRuleChain2Node.setName("To Rule Chain 2");
newRuleNode.setType(TB_RULE_CHAIN_INPUT_NODE); toRuleChain2Node.setType(TB_RULE_CHAIN_INPUT_NODE);
ObjectNode newConfiguration = JacksonUtil.newObjectNode(); toRuleChain2Node.setConfiguration(JacksonUtil.newObjectNode()
configuration.put("ruleChainId", savedNewToRuleChain.getId().toString()); .put("ruleChainId", toRuleChain2Id.toString()));
newRuleNode.setConfiguration(newConfiguration);
List<RuleNode> newRuleNodes = new ArrayList<>(); List<RuleNode> newRuleNodes = new ArrayList<>();
newRuleNodes.add(newRuleNode); newRuleNodes.add(toRuleChain2Node);
RuleChainMetaData foundRuleChainMetaData = ruleChainService.loadRuleChainMetaData(tenantId, ruleChainMetaData.getRuleChainId()); newRuleNodes.add(toRuleChain1Node);
foundRuleChainMetaData.setNodes(newRuleNodes); ruleChainMetaData = ruleChainService.loadRuleChainMetaData(tenantId, ruleChainMetaData.getRuleChainId());
ruleChainMetaData.setNodes(newRuleNodes);
ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData, Function.identity()); ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData, Function.identity());
List<EntityRelation> newRelations = relationService.findByFromAndType(tenantId, savedFromRuleChain.getId(), USES_TYPE, RelationTypeGroup.COMMON); List<EntityRelation> newRelations = relationService.findByFromAndType(tenantId, fromRuleChainId, USES_TYPE, RelationTypeGroup.COMMON);
Assert.assertEquals(1, relations.size()); assertThat(newRelations).hasSize(2);
EntityRelation newUsesRelation = newRelations.get(0); assertThat(newRelations).anySatisfy(relationToRuleChain1 -> {
Assert.assertEquals(savedFromRuleChain.getId(), newUsesRelation.getFrom()); assertThat(relationToRuleChain1.getFrom()).isEqualTo(fromRuleChainId);
Assert.assertEquals(savedNewToRuleChain.getId(), newUsesRelation.getTo()); assertThat(relationToRuleChain1.getTo()).isEqualTo(toRuleChain1Id);
});
assertThat(newRelations).anySatisfy(relationToRuleChain2 -> {
assertThat(relationToRuleChain2.getFrom()).isEqualTo(fromRuleChainId);
assertThat(relationToRuleChain2.getTo()).isEqualTo(toRuleChain2Id);
});
} }
private RuleChainId saveRuleChainAndSetAutoAssignToEdge(String name) { private RuleChainId saveRuleChainAndSetAutoAssignToEdge(String name) {
@ -462,9 +476,9 @@ public class RuleChainServiceTest extends AbstractServiceTest {
ruleChainMetaData.setFirstNodeIndex(0); ruleChainMetaData.setFirstNodeIndex(0);
ruleChainMetaData.setNodes(ruleNodes); ruleChainMetaData.setNodes(ruleNodes);
ruleChainMetaData.addConnectionInfo(0,1,"success"); ruleChainMetaData.addConnectionInfo(0, 1, "success");
ruleChainMetaData.addConnectionInfo(0,2,"fail"); ruleChainMetaData.addConnectionInfo(0, 2, "fail");
ruleChainMetaData.addConnectionInfo(1,2,"success"); ruleChainMetaData.addConnectionInfo(1, 2, "success");
Assert.assertTrue(ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData, Function.identity()).isSuccess()); Assert.assertTrue(ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData, Function.identity()).isSuccess());
return ruleChainService.loadRuleChainMetaData(tenantId, ruleChainMetaData.getRuleChainId()); return ruleChainService.loadRuleChainMetaData(tenantId, ruleChainMetaData.getRuleChainId());
@ -501,10 +515,10 @@ public class RuleChainServiceTest extends AbstractServiceTest {
ruleChainMetaData.setFirstNodeIndex(0); ruleChainMetaData.setFirstNodeIndex(0);
ruleChainMetaData.setNodes(ruleNodes); ruleChainMetaData.setNodes(ruleNodes);
ruleChainMetaData.addConnectionInfo(0,1,"success"); ruleChainMetaData.addConnectionInfo(0, 1, "success");
ruleChainMetaData.addConnectionInfo(0,2,"fail"); ruleChainMetaData.addConnectionInfo(0, 2, "fail");
ruleChainMetaData.addConnectionInfo(1,2,"success"); ruleChainMetaData.addConnectionInfo(1, 2, "success");
ruleChainMetaData.addConnectionInfo(2,2,"success"); ruleChainMetaData.addConnectionInfo(2, 2, "success");
return ruleChainMetaData; return ruleChainMetaData;
} }
@ -540,10 +554,10 @@ public class RuleChainServiceTest extends AbstractServiceTest {
ruleChainMetaData.setFirstNodeIndex(0); ruleChainMetaData.setFirstNodeIndex(0);
ruleChainMetaData.setNodes(ruleNodes); ruleChainMetaData.setNodes(ruleNodes);
ruleChainMetaData.addConnectionInfo(0,1,"success"); ruleChainMetaData.addConnectionInfo(0, 1, "success");
ruleChainMetaData.addConnectionInfo(0,2,"fail"); ruleChainMetaData.addConnectionInfo(0, 2, "fail");
ruleChainMetaData.addConnectionInfo(1,2,"success"); ruleChainMetaData.addConnectionInfo(1, 2, "success");
ruleChainMetaData.addConnectionInfo(2,0,"success"); ruleChainMetaData.addConnectionInfo(2, 0, "success");
return ruleChainMetaData; return ruleChainMetaData;
} }