added upgrade logic tests && added fixes for review comments

This commit is contained in:
ShvaykaD 2023-05-18 18:55:00 +03:00
parent 21e5a1a130
commit cfcf539c50
29 changed files with 451 additions and 157 deletions

View File

@ -27,6 +27,7 @@ import java.util.Arrays;
@Slf4j
@SpringBootConfiguration
@ComponentScan({"org.thingsboard.server.install",
"org.thingsboard.server.service.bean",
"org.thingsboard.server.service.component",
"org.thingsboard.server.service.install",
"org.thingsboard.server.service.security.auth.jwt.settings",

View File

@ -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.bean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.stereotype.Service;
import java.lang.annotation.Annotation;
import java.util.HashSet;
import java.util.Set;
@Service
public class AnnotationBeanDiscoveryService implements BeanDiscoveryService {
@Value("${plugins.scan_packages}")
private String[] scanPackages;
@Override
public Set<BeanDefinition> discoverBeansByAnnotationType(Class<? extends Annotation> annotationType) {
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType));
Set<BeanDefinition> defs = new HashSet<>();
for (String scanPackage : scanPackages) {
defs.addAll(scanner.findCandidateComponents(scanPackage));
}
return defs;
}
}

View File

@ -0,0 +1,27 @@
/**
* 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.bean;
import org.springframework.beans.factory.config.BeanDefinition;
import java.lang.annotation.Annotation;
import java.util.Set;
public interface BeanDiscoveryService {
Set<BeanDefinition> discoverBeansByAnnotationType(Class<? extends Annotation> annotationType);
}

View File

@ -20,12 +20,9 @@ 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.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.env.Environment;
import org.springframework.core.env.Profiles;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.stereotype.Service;
import org.thingsboard.rule.engine.api.NodeConfiguration;
import org.thingsboard.rule.engine.api.NodeDefinition;
@ -36,14 +33,13 @@ import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.dao.component.ComponentDescriptorService;
import org.thingsboard.server.service.bean.BeanDiscoveryService;
import javax.annotation.PostConstruct;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@ -54,12 +50,13 @@ import java.util.Set;
public class AnnotationComponentDiscoveryService implements ComponentDiscoveryService {
public static final int MAX_OPTIMISITC_RETRIES = 3;
@Value("${plugins.scan_packages}")
private String[] scanPackages;
@Autowired
private Environment environment;
@Autowired(required = false)
private BeanDiscoveryService beanDiscoveryService;
@Autowired
private ComponentDescriptorService componentDescriptorService;
@ -83,7 +80,7 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
}
private void registerRuleNodeComponents() {
Set<BeanDefinition> ruleNodeBeanDefinitions = getBeanDefinitions(RuleNode.class);
Set<BeanDefinition> ruleNodeBeanDefinitions = beanDiscoveryService.discoverBeansByAnnotationType(RuleNode.class);
for (BeanDefinition def : ruleNodeBeanDefinitions) {
int retryCount = 0;
Exception cause = null;
@ -212,16 +209,6 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
return relationTypes.toArray(new String[relationTypes.size()]);
}
private Set<BeanDefinition> getBeanDefinitions(Class<? extends Annotation> componentType) {
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
scanner.addIncludeFilter(new AnnotationTypeFilter(componentType));
Set<BeanDefinition> defs = new HashSet<>();
for (String scanPackage : scanPackages) {
defs.addAll(scanner.findCandidateComponents(scanPackage));
}
return defs;
}
@Override
public void discoverComponents() {
registerRuleNodeComponents();

View File

@ -17,22 +17,18 @@ package org.thingsboard.server.service.install.update;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.Iterables;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Profile;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.api.TbNode;
import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.rule.engine.api.VersionedNode;
import org.thingsboard.rule.engine.api.TbVersionedNode;
import org.thingsboard.rule.engine.flow.TbRuleChainInputNode;
import org.thingsboard.rule.engine.flow.TbRuleChainInputNodeConfiguration;
import org.thingsboard.rule.engine.profile.TbDeviceProfileNode;
@ -86,17 +82,14 @@ import org.thingsboard.server.dao.sql.device.DeviceProfileRepository;
import org.thingsboard.server.dao.tenant.TenantProfileService;
import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.timeseries.TimeseriesService;
import org.thingsboard.server.service.bean.BeanDiscoveryService;
import org.thingsboard.server.service.install.InstallScripts;
import org.thingsboard.server.service.install.SystemDataLoaderService;
import org.thingsboard.server.service.install.TbRuleEngineQueueConfigService;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
@ -108,9 +101,6 @@ import static org.thingsboard.server.common.data.StringUtils.isBlank;
@Slf4j
public class DefaultDataUpdateService implements DataUpdateService {
@Value("${plugins.scan_packages}")
private String[] scanPackages;
@Autowired
private TenantService tenantService;
@ -149,7 +139,7 @@ public class DefaultDataUpdateService implements DataUpdateService {
private QueueService queueService;
@Autowired
private TbRuleEngineQueueConfigService queueConfig;
private BeanDiscoveryService beanDiscoveryService;
@Autowired
private SystemDataLoaderService systemDataLoaderService;
@ -229,57 +219,54 @@ public class DefaultDataUpdateService implements DataUpdateService {
}
private void upgradeRuleNodes() {
var ruleChainIdToTenantIdMap = new HashMap<RuleChainId, TenantId>();
try {
log.info("Starting rule nodes upgrade ...");
var ruleNodeDefinitions = getBeanDefinitions(
org.thingsboard.rule.engine.api.RuleNode.class
);
for (BeanDefinition def : ruleNodeDefinitions) {
String clazzName = def.getBeanClassName();
Class<?> clazz = Class.forName(clazzName);
TbNode tbNode = (TbNode) clazz.getDeclaredConstructor().newInstance();
if (tbNode instanceof VersionedNode) {
var versionedNode = (VersionedNode) tbNode;
var ruleNodeName = versionedNode.getClass().getName();
int currentVersion = versionedNode.getCurrentVersion();
var ruleNodesToUpdate = new PageDataIterable<>(
pageLink ->
ruleChainService.findAllRuleNodesByTypeAndVersionLessThan(ruleNodeName, currentVersion, pageLink),
1024
);
log.info("Lookup rule nodes to upgrade ...");
ArrayList<TbVersionedNode> tbVersionedNodes = getTbVersionedNodes();
log.info("Found {} versioned nodes to check for upgrade!", tbVersionedNodes.size());
for (TbVersionedNode tbVersionedNode : tbVersionedNodes) {
String ruleNodeType = tbVersionedNode.getClass().getName();
String ruleNodeTypeForLogs = tbVersionedNode.getClass().getSimpleName();
int toVersion = tbVersionedNode.getCurrentVersion();
log.info("Going to check for nodes with type: {} to upgrade to version: {}.", ruleNodeTypeForLogs, toVersion);
var ruleNodesToUpdate = new PageDataIterable<>(
pageLink ->
ruleChainService.findAllRuleNodesByTypeAndVersionLessThan(
ruleNodeType,
toVersion,
pageLink
),
1024
);
if (Iterables.isEmpty(ruleNodesToUpdate)) {
log.info("There are no active nodes with type: {}, or all nodes with this type already set to latest version!", ruleNodeTypeForLogs);
} else {
for (RuleNode ruleNode : ruleNodesToUpdate) {
RuleNodeId ruleNodeId = ruleNode.getId();
var oldConfiguration = ruleNode.getConfiguration();
int fromVersion = ruleNode.getConfigurationVersion();
log.info("Going to upgrade rule node with id: {} type: {} fromVersion: {} toVersion: {}",
ruleNodeId,
clazzName,
ruleNodeTypeForLogs,
fromVersion,
currentVersion);
toVersion);
try {
TbPair<Boolean, JsonNode> upgradeRuleNodeConfigurationResult = versionedNode.upgrade(ruleNodeId, fromVersion, oldConfiguration);
TbPair<Boolean, JsonNode> upgradeRuleNodeConfigurationResult = tbVersionedNode.upgrade(fromVersion, oldConfiguration);
if (upgradeRuleNodeConfigurationResult.getFirst()) {
ruleNode.setConfiguration(upgradeRuleNodeConfigurationResult.getSecond());
var ruleChainId = ruleNode.getRuleChainId();
var tenantId = getTenantId(ruleChainIdToTenantIdMap, ruleNodeId, ruleChainId);
if (tenantId == null) {
log.warn("Failed to find tenant id for rule chain with id: {}", ruleChainId);
continue;
}
ruleChainService.saveRuleNode(tenantId, ruleNode);
log.info("Successfully upgrade rule node with id: {} type: {} fromVersion: {} toVersion: {}",
ruleNodeId,
clazzName,
fromVersion,
currentVersion);
}
ruleNode.setConfigurationVersion(toVersion);
ruleChainService.saveRuleNode(TenantId.SYS_TENANT_ID, ruleNode);
log.info("Successfully upgrade rule node with id: {} type: {} fromVersion: {} toVersion: {}",
ruleNodeId,
ruleNodeTypeForLogs,
fromVersion,
toVersion);
} catch (TbNodeException e) {
log.warn("Failed to upgrade rule node with id: {} type: {} fromVersion: {} toVersion: {} due to: ",
ruleNodeId,
clazzName,
ruleNodeTypeForLogs,
fromVersion,
currentVersion,
toVersion,
e);
}
}
@ -290,27 +277,28 @@ public class DefaultDataUpdateService implements DataUpdateService {
log.error("Unexpected error during rule nodes upgrade: ", e);
}
}
private TenantId getTenantId(HashMap<RuleChainId, TenantId> ruleChainIdToTenantId, RuleNodeId ruleNodeId, RuleChainId ruleChainId) {
return ruleChainIdToTenantId.computeIfAbsent(ruleChainId,
id -> {
RuleChain ruleChain = ruleChainService.findRuleChainById(TenantId.SYS_TENANT_ID, id);
if (ruleChain == null) {
log.warn("Failed to find rule chain by id: {} ruleNodeId: {}", ruleChainId, ruleNodeId);
return null;
}
return ruleChain.getTenantId();
});
}
private Set<BeanDefinition> getBeanDefinitions(Class<? extends Annotation> componentType) {
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
scanner.addIncludeFilter(new AnnotationTypeFilter(componentType));
Set<BeanDefinition> defs = new HashSet<>();
for (String scanPackage : scanPackages) {
defs.addAll(scanner.findCandidateComponents(scanPackage));
private ArrayList<TbVersionedNode> getTbVersionedNodes() {
var ruleNodeDefinitions = beanDiscoveryService.discoverBeansByAnnotationType(
org.thingsboard.rule.engine.api.RuleNode.class
);
var tbVersionedNodes = new ArrayList<TbVersionedNode>();
for (var def : ruleNodeDefinitions) {
String clazzName = def.getBeanClassName();
try {
var clazz = Class.forName(clazzName);
if (TbVersionedNode.class.isAssignableFrom(clazz)) {
tbVersionedNodes.add((TbVersionedNode) clazz.getDeclaredConstructor().newInstance());
}
} catch (NoSuchMethodException |
InstantiationException |
IllegalAccessException |
InvocationTargetException |
ClassNotFoundException e
) {
log.warn("Failed to create instance of rule node type: {} due to: ", clazzName, e);
}
}
return defs;
return tbVersionedNodes;
}
private final PaginatedUpdater<String, DeviceProfileEntity> deviceProfileEntityDynamicConditionsUpdater =

View File

@ -27,8 +27,11 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.test.context.ContextConfiguration;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.action.TbCreateAlarmNode;
import org.thingsboard.rule.engine.action.TbCreateAlarmNodeConfiguration;
import org.thingsboard.rule.engine.api.TbVersionedNode;
import org.thingsboard.rule.engine.metadata.TbGetRelatedAttributeNode;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
@ -128,6 +131,82 @@ public class RuleChainControllerTest extends AbstractControllerTest {
ActionType.UPDATED);
}
@Test
public void testSaveRuleChainMetadataWithVersionedNodes() throws Exception {
RuleChain ruleChain = new RuleChain();
ruleChain.setName("RuleChain");
RuleChain savedRuleChain = doPost("/api/ruleChain", ruleChain, RuleChain.class);
Assert.assertNotNull(savedRuleChain);
RuleChainId ruleChainId = savedRuleChain.getId();
Assert.assertNotNull(ruleChainId);
Assert.assertTrue(savedRuleChain.getCreatedTime() > 0);
Assert.assertEquals(ruleChain.getName(), savedRuleChain.getName());
TbVersionedNode tbVersionedNode = new TbGetRelatedAttributeNode();
String ruleNodeType = tbVersionedNode.getClass().getName();
int currentVersion = tbVersionedNode.getCurrentVersion();
String oldConfig = "{\"attrMapping\":{\"serialNumber\":\"sn\"}," +
"\"relationsQuery\":{\"direction\":\"FROM\",\"maxLevel\":1," +
"\"filters\":[{\"relationType\":\"Contains\",\"entityTypes\":[]}]," +
"\"fetchLastLevelOnly\":false},\"telemetry\":false}";
String newConfig = "{\"fetchTo\":\"METADATA\"," +
"\"attrMapping\":{\"serialNumber\":\"sn\"}," +
"\"dataToFetch\":\"ATTRIBUTES\"," +
"\"relationsQuery\":{\"direction\":\"FROM\",\"maxLevel\":1," +
"\"filters\":[{\"relationType\":\"Contains\",\"entityTypes\":[]}]," +
"\"fetchLastLevelOnly\":false}}";
var ruleChainMetaData = createRuleChainMetadataWithTbVersionedNodes(
ruleChainId,
ruleNodeType,
currentVersion,
oldConfig,
newConfig
);
var savedRuleChainMetaData = doPost("/api/ruleChain/metadata", ruleChainMetaData, RuleChainMetaData.class);
Assert.assertEquals(ruleChainId, savedRuleChainMetaData.getRuleChainId());
Assert.assertEquals(2, savedRuleChainMetaData.getNodes().size());
for (RuleNode ruleNode : savedRuleChainMetaData.getNodes()) {
Assert.assertNotNull(ruleNode.getId());
Assert.assertEquals(currentVersion, ruleNode.getConfigurationVersion());
Assert.assertEquals(JacksonUtil.toJsonNode(newConfig), ruleNode.getConfiguration());
}
}
private RuleChainMetaData createRuleChainMetadataWithTbVersionedNodes(
RuleChainId ruleChainId,
String ruleNodeType,
int currentVersion,
String oldConfig,
String newConfig
) {
RuleChainMetaData ruleChainMetaData = new RuleChainMetaData();
ruleChainMetaData.setRuleChainId(ruleChainId);
var ruleNodeWithOldConfig = new RuleNode();
ruleNodeWithOldConfig.setName("Old Rule Node");
ruleNodeWithOldConfig.setType(ruleNodeType);
ruleNodeWithOldConfig.setConfiguration(JacksonUtil.toJsonNode(oldConfig));
var ruleNodeWithNewConfig = new RuleNode();
ruleNodeWithNewConfig.setName("New Rule Node");
ruleNodeWithNewConfig.setType(ruleNodeType);
ruleNodeWithNewConfig.setConfigurationVersion(currentVersion);
ruleNodeWithNewConfig.setConfiguration(JacksonUtil.toJsonNode(newConfig));
List<RuleNode> ruleNodes = new ArrayList<>();
ruleNodes.add(ruleNodeWithOldConfig);
ruleNodes.add(ruleNodeWithNewConfig);
ruleChainMetaData.setFirstNodeIndex(0);
ruleChainMetaData.setNodes(ruleNodes);
return ruleChainMetaData;
}
@Test
public void testSaveRuleChainWithViolationOfLengthValidation() throws Exception {

View File

@ -28,7 +28,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.rule.engine.api.VersionedNode;
import org.thingsboard.rule.engine.api.TbVersionedNode;
import org.thingsboard.server.common.data.BaseData;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.edge.Edge;
@ -185,17 +185,62 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
}
updatedRuleNodes.add(new RuleNodeUpdateResult(existingNode, newRuleNode));
}
RuleChainId ruleChainId = ruleChain.getId();
if (nodes != null) {
for (RuleNode node : toAddOrUpdate) {
node.setRuleChainId(ruleChain.getId());
node.setRuleChainId(ruleChainId);
String ruleNodeType = node.getType();
RuleNodeId ruleNodeId = node.getId();
try {
var ruleNodeClazz = Class.forName(ruleNodeType);
if (TbVersionedNode.class.isAssignableFrom(ruleNodeClazz)) {
TbVersionedNode tbVersionedNode = (TbVersionedNode) ruleNodeClazz.getDeclaredConstructor().newInstance();
int fromVersion = node.getConfigurationVersion();
int toVersion = tbVersionedNode.getCurrentVersion();
if (fromVersion < toVersion) {
log.debug("Going to upgrade rule node with id: {} type: {} fromVersion: {} toVersion: {}",
ruleNodeId,
ruleNodeType,
fromVersion,
toVersion);
try {
TbPair<Boolean, JsonNode> upgradeResult = tbVersionedNode.upgrade(fromVersion, node.getConfiguration());
if (upgradeResult.getFirst()) {
node.setConfiguration(upgradeResult.getSecond());
log.info("Successfully upgrade rule node with id: {} type: {}, rule chain id: {} fromVersion: {} toVersion: {}",
ruleNodeId,
ruleNodeType,
ruleChainId,
fromVersion,
toVersion);
}
node.setConfigurationVersion(toVersion);
} catch (TbNodeException e) {
log.warn("Failed to upgrade rule node with id: {} type: {} rule chain id: {} fromVersion: {} toVersion: {} due to: ",
ruleNodeId,
ruleNodeType,
ruleChainId,
fromVersion,
toVersion,
e);
}
} else {
log.debug("Rule node with id: {} type: {} ruleChainId: {} already set to latest version!",
ruleNodeId,
ruleChainId,
ruleNodeType);
}
}
} catch (ClassNotFoundException |
InvocationTargetException |
InstantiationException |
IllegalAccessException |
NoSuchMethodException e) {
log.error("Failed to create instance of rule node with id: {} type: {}, rule chain id: {}", ruleNodeId, ruleNodeType, ruleChainId);
}
RuleNode savedNode = ruleNodeDao.save(tenantId, node);
relations.add(new EntityRelation(ruleChainMetaData.getRuleChainId(), savedNode.getId(),
EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN));
// TbPair<Boolean, JsonNode> upgradeResult = upgradeRuleNode(savedNode);
// if (upgradeResult.getFirst()) {
// savedNode.setConfiguration(upgradeResult.getSecond());
// savedNode = ruleNodeDao.save(tenantId, savedNode);
// }
int index = nodes.indexOf(node);
nodes.set(index, savedNode);
ruleNodeIndexMap.put(savedNode.getId(), index);
@ -228,7 +273,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
RuleChain targetRuleChain = findRuleChainById(TenantId.SYS_TENANT_ID, targetRuleChainId);
RuleNode targetNode = new RuleNode();
targetNode.setName(targetRuleChain != null ? targetRuleChain.getName() : "Rule Chain Input");
targetNode.setRuleChainId(ruleChain.getId());
targetNode.setRuleChainId(ruleChainId);
targetNode.setType("org.thingsboard.rule.engine.flow.TbRuleChainInputNode");
var configuration = JacksonUtil.newObjectNode();
configuration.put("ruleChainId", targetRuleChainId.getId().toString());
@ -241,7 +286,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
targetNode = ruleNodeDao.save(tenantId, targetNode);
EntityRelation sourceRuleChainToRuleNode = new EntityRelation();
sourceRuleChainToRuleNode.setFrom(ruleChain.getId());
sourceRuleChainToRuleNode.setFrom(ruleChainId);
sourceRuleChainToRuleNode.setTo(targetNode.getId());
sourceRuleChainToRuleNode.setType(EntityRelation.CONTAINS_TYPE);
sourceRuleChainToRuleNode.setTypeGroup(RelationTypeGroup.RULE_CHAIN);
@ -265,40 +310,6 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
return RuleChainUpdateResult.successful(updatedRuleNodes);
}
private TbPair<Boolean, JsonNode> upgradeRuleNode(RuleNode ruleNode) {
RuleNodeId ruleNodeId = ruleNode.getId();
String ruleNodeType = ruleNode.getType();
int fromVersion = ruleNode.getConfigurationVersion();
try {
var ruleNodeClass = Class.forName(ruleNodeType).getDeclaredConstructor().newInstance();
if (ruleNodeClass instanceof VersionedNode) {
VersionedNode versionedNode = (VersionedNode) ruleNodeClass;
int toVersion = versionedNode.getCurrentVersion();
log.info("Going to upgrade rule node with id: {} type: {} fromVersion: {} toVersion: {}",
ruleNodeId,
ruleNodeType,
fromVersion,
toVersion);
try {
return versionedNode.upgrade(ruleNodeId, fromVersion, ruleNode.getConfiguration());
} catch (TbNodeException e) {
log.warn("Failed to upgrade rule node with id: {} fromVersion: {} toVersion: {} due to: ",
ruleNodeId,
fromVersion,
toVersion,
e);
}
}
} catch (ClassNotFoundException |
InvocationTargetException |
InstantiationException |
IllegalAccessException |
NoSuchMethodException e) {
log.warn("Failed to create instance of rule node with id: {} and type: {}", ruleNodeId, ruleNodeType);
}
return new TbPair<>(false, ruleNode.getConfiguration());
}
@Override
public RuleChainMetaData loadRuleChainMetaData(TenantId tenantId, RuleChainId ruleChainId) {
Validator.validateId(ruleChainId, "Incorrect rule chain id.");

View File

@ -16,12 +16,11 @@
package org.thingsboard.rule.engine.api;
import com.fasterxml.jackson.databind.JsonNode;
import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.util.TbPair;
public interface VersionedNode extends TbNode {
public interface TbVersionedNode extends TbNode {
TbPair<Boolean, JsonNode> upgrade(RuleNodeId ruleNodeId, int fromVersion, JsonNode oldConfiguration) throws TbNodeException;
TbPair<Boolean, JsonNode> upgrade(int fromVersion, JsonNode oldConfiguration) throws TbNodeException;
int getCurrentVersion();

View File

@ -22,12 +22,10 @@ import com.google.common.util.concurrent.Futures;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.rule.engine.api.TbNode;
import org.thingsboard.rule.engine.api.TbNodeConfiguration;
import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.rule.engine.api.VersionedNode;
import org.thingsboard.rule.engine.api.TbVersionedNode;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.kv.KvEntry;
import org.thingsboard.server.common.data.util.TbPair;
import org.thingsboard.server.common.msg.TbMsg;
@ -36,7 +34,7 @@ import org.thingsboard.server.common.msg.TbMsgMetaData;
import java.util.NoSuchElementException;
@Slf4j
public abstract class TbAbstractNodeWithFetchTo<C extends TbAbstractFetchToNodeConfiguration> implements VersionedNode {
public abstract class TbAbstractNodeWithFetchTo<C extends TbAbstractFetchToNodeConfiguration> implements TbVersionedNode {
protected final static String FETCH_TO_PROPERTY_NAME = "fetchTo";

View File

@ -25,7 +25,6 @@ import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.rule.engine.api.util.TbNodeUtils;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.common.data.util.TbPair;
@ -89,7 +88,7 @@ public class TbFetchDeviceCredentialsNode extends TbAbstractNodeWithFetchTo<TbFe
}
@Override
public TbPair<Boolean, JsonNode> upgrade(RuleNodeId ruleNodeId, int fromVersion, JsonNode oldConfiguration) throws TbNodeException {
public TbPair<Boolean, JsonNode> upgrade(int fromVersion, JsonNode oldConfiguration) throws TbNodeException {
return fromVersion == 0 ?
upgradeRuleNodesWithOldPropertyToUseFetchTo(
oldConfiguration,

View File

@ -25,7 +25,6 @@ import org.thingsboard.rule.engine.api.TbNodeConfiguration;
import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.rule.engine.api.util.TbNodeUtils;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.data.util.TbPair;
import org.thingsboard.server.common.msg.TbMsg;
@ -57,7 +56,7 @@ public class TbGetAttributesNode extends TbAbstractGetAttributesNode<TbGetAttrib
}
@Override
public TbPair<Boolean, JsonNode> upgrade(RuleNodeId ruleNodeId, int fromVersion, JsonNode oldConfiguration) throws TbNodeException {
public TbPair<Boolean, JsonNode> upgrade(int fromVersion, JsonNode oldConfiguration) throws TbNodeException {
return fromVersion == 0 ?
upgradeRuleNodesWithOldPropertyToUseFetchTo(
oldConfiguration,

View File

@ -27,7 +27,6 @@ import org.thingsboard.rule.engine.api.util.TbNodeUtils;
import org.thingsboard.rule.engine.util.EntitiesCustomerIdAsyncLoader;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.data.util.TbPair;
@ -72,7 +71,7 @@ public class TbGetCustomerAttributeNode extends TbAbstractGetEntityAttrNode<Cust
}
@Override
public TbPair<Boolean, JsonNode> upgrade(RuleNodeId ruleNodeId, int fromVersion, JsonNode oldConfiguration) throws TbNodeException {
public TbPair<Boolean, JsonNode> upgrade(int fromVersion, JsonNode oldConfiguration) throws TbNodeException {
return fromVersion == 0 ?
upgradeToUseFetchToAndDataToFetch(oldConfiguration) :
new TbPair<>(false, oldConfiguration);

View File

@ -33,7 +33,6 @@ import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.data.util.TbPair;
@ -107,7 +106,7 @@ public class TbGetCustomerDetailsNode extends TbAbstractGetEntityDetailsNode<TbG
}
@Override
public TbPair<Boolean, JsonNode> upgrade(RuleNodeId ruleNodeId, int fromVersion, JsonNode oldConfiguration) throws TbNodeException {
public TbPair<Boolean, JsonNode> upgrade(int fromVersion, JsonNode oldConfiguration) throws TbNodeException {
return fromVersion == 0 ?
upgradeRuleNodesWithOldPropertyToUseFetchTo(
oldConfiguration,

View File

@ -26,7 +26,6 @@ import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.rule.engine.api.util.TbNodeUtils;
import org.thingsboard.rule.engine.util.EntitiesRelatedDeviceIdAsyncLoader;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.data.util.TbPair;
import org.thingsboard.server.common.msg.TbMsg;
@ -60,7 +59,7 @@ public class TbGetDeviceAttrNode extends TbAbstractGetAttributesNode<TbGetDevice
}
@Override
public TbPair<Boolean, JsonNode> upgrade(RuleNodeId ruleNodeId, int fromVersion, JsonNode oldConfiguration) throws TbNodeException {
public TbPair<Boolean, JsonNode> upgrade(int fromVersion, JsonNode oldConfiguration) throws TbNodeException {
return fromVersion == 0 ?
upgradeRuleNodesWithOldPropertyToUseFetchTo(
oldConfiguration,

View File

@ -28,7 +28,6 @@ import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.rule.engine.api.util.TbNodeUtils;
import org.thingsboard.rule.engine.util.EntitiesFieldsAsyncLoader;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.data.util.TbPair;
import org.thingsboard.server.common.msg.TbMsg;
@ -102,7 +101,7 @@ public class TbGetOriginatorFieldsNode extends TbAbstractNodeWithFetchTo<TbGetOr
}
@Override
public TbPair<Boolean, JsonNode> upgrade(RuleNodeId ruleNodeId, int fromVersion, JsonNode oldConfiguration) {
public TbPair<Boolean, JsonNode> upgrade(int fromVersion, JsonNode oldConfiguration) {
if (fromVersion == 0) {
var newConfigObjectNode = (ObjectNode) oldConfiguration;
newConfigObjectNode.put(FETCH_TO_PROPERTY_NAME, FetchTo.METADATA.name());

View File

@ -26,7 +26,6 @@ import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.rule.engine.api.util.TbNodeUtils;
import org.thingsboard.rule.engine.util.EntitiesRelatedEntityIdAsyncLoader;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.data.util.TbPair;
@ -75,7 +74,7 @@ public class TbGetRelatedAttributeNode extends TbAbstractGetEntityAttrNode<Entit
}
@Override
public TbPair<Boolean, JsonNode> upgrade(RuleNodeId ruleNodeId, int fromVersion, JsonNode oldConfiguration) throws TbNodeException {
public TbPair<Boolean, JsonNode> upgrade(int fromVersion, JsonNode oldConfiguration) throws TbNodeException {
return fromVersion == 0 ? upgradeToUseFetchToAndDataToFetch(oldConfiguration) : new TbPair<>(false, oldConfiguration);
}

View File

@ -25,7 +25,6 @@ import org.thingsboard.rule.engine.api.TbNodeConfiguration;
import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.rule.engine.api.util.TbNodeUtils;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.data.util.TbPair;
@ -66,7 +65,7 @@ public class TbGetTenantAttributeNode extends TbAbstractGetEntityAttrNode<Tenant
}
@Override
public TbPair<Boolean, JsonNode> upgrade(RuleNodeId ruleNodeId, int fromVersion, JsonNode oldConfiguration) throws TbNodeException {
public TbPair<Boolean, JsonNode> upgrade(int fromVersion, JsonNode oldConfiguration) throws TbNodeException {
return fromVersion == 0 ? upgradeToUseFetchToAndDataToFetch(oldConfiguration) : new TbPair<>(false, oldConfiguration);
}

View File

@ -24,7 +24,6 @@ import org.thingsboard.rule.engine.api.TbNodeConfiguration;
import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.rule.engine.api.util.TbNodeUtils;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.data.util.TbPair;
@ -62,7 +61,7 @@ public class TbGetTenantDetailsNode extends TbAbstractGetEntityDetailsNode<TbGet
}
@Override
public TbPair<Boolean, JsonNode> upgrade(RuleNodeId ruleNodeId, int fromVersion, JsonNode oldConfiguration) throws TbNodeException {
public TbPair<Boolean, JsonNode> upgrade(int fromVersion, JsonNode oldConfiguration) throws TbNodeException {
return fromVersion == 0 ?
upgradeRuleNodesWithOldPropertyToUseFetchTo(
oldConfiguration,

View File

@ -15,6 +15,7 @@
*/
package org.thingsboard.rule.engine.metadata;
import com.fasterxml.jackson.databind.JsonNode;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -31,6 +32,7 @@ import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.util.TbPair;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.common.msg.queue.TbMsgCallback;
@ -40,6 +42,8 @@ import java.util.Map;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.willAnswer;
import static org.mockito.Mockito.doAnswer;
@ -67,7 +71,6 @@ public class TbFetchDeviceCredentialsNodeTest {
void setUp() throws TbNodeException {
deviceId = new DeviceId(UUID.randomUUID());
config = new TbFetchDeviceCredentialsNodeConfiguration().defaultConfiguration();
config.setFetchTo(FetchTo.METADATA);
node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config)));
}
@ -150,6 +153,15 @@ public class TbFetchDeviceCredentialsNodeTest {
assertThat(exceptionCaptor.getValue()).isInstanceOf(RuntimeException.class);
}
@Test
void givenOldConfig_whenUpgrade_thenShouldReturnSuccessResult() throws Exception {
String oldConfig = "{\"fetchToMetadata\":true}";
JsonNode configJson = JacksonUtil.toJsonNode(oldConfig);
TbPair<Boolean, JsonNode> upgrade = node.upgrade(0, configJson);
assertTrue(upgrade.getFirst());
assertEquals(JacksonUtil.valueToTree(config), upgrade.getSecond());
}
private TbMsg getTbMsg(EntityId entityId) {
final Map<String, String> mdMap = Map.of(
"country", "US",

View File

@ -16,10 +16,12 @@
package org.thingsboard.rule.engine.metadata;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.util.concurrent.Futures;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.jupiter.api.Assertions;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
@ -39,6 +41,7 @@ import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
import org.thingsboard.server.common.data.kv.JsonDataEntry;
import org.thingsboard.server.common.data.kv.StringDataEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.util.TbPair;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.dao.attributes.AttributesService;
@ -247,6 +250,23 @@ public class TbGetAttributesNodeTest {
assertThat(exception.getMessage()).isEqualTo("Message body is not an object!");
}
@Test
public void givenOldConfig_whenUpgrade_thenShouldReturnSuccessResult() throws Exception {
var defaultConfig = new TbGetAttributesNodeConfiguration().defaultConfiguration();
var node = new TbGetAttributesNode();
String oldConfig = "{\"fetchToData\":false," +
"\"clientAttributeNames\":[]," +
"\"sharedAttributeNames\":[]," +
"\"serverAttributeNames\":[]," +
"\"latestTsKeyNames\":[]," +
"\"tellFailureIfAbsent\":true," +
"\"getLatestValueWithTs\":false}";
JsonNode configJson = JacksonUtil.toJsonNode(oldConfig);
TbPair<Boolean, JsonNode> upgrade = node.upgrade(0, configJson);
Assertions.assertTrue(upgrade.getFirst());
Assertions.assertEquals(JacksonUtil.valueToTree(defaultConfig), upgrade.getSecond());
}
private TbMsg checkMsg(boolean checkSuccess) {
var msgCaptor = ArgumentCaptor.forClass(TbMsg.class);
if (checkSuccess) {

View File

@ -15,10 +15,12 @@
*/
package org.thingsboard.rule.engine.metadata;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@ -46,6 +48,7 @@ import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
import org.thingsboard.server.common.data.kv.StringDataEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.util.TbPair;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.dao.asset.AssetService;
@ -451,6 +454,17 @@ public class TbGetCustomerAttributeNodeTest {
assertThat(actualMessageCaptor.getValue().getMetaData()).isEqualTo(expectedMsgMetaData);
}
@Test
public void givenOldConfig_whenUpgrade_thenShouldReturnSuccessResult() throws Exception {
var defaultConfig = new TbGetEntityAttrNodeConfiguration().defaultConfiguration();
var node = new TbGetCustomerAttributeNode();
String oldConfig = "{\"attrMapping\":{\"alarmThreshold\":\"threshold\"},\"telemetry\":false}";
JsonNode configJson = JacksonUtil.toJsonNode(oldConfig);
TbPair<Boolean, JsonNode> upgrade = node.upgrade(0, configJson);
Assertions.assertTrue(upgrade.getFirst());
Assertions.assertEquals(JacksonUtil.valueToTree(defaultConfig), upgrade.getSecond());
}
private void prepareMsgAndConfig(FetchTo fetchTo, DataToFetch dataToFetch, EntityId originator) {
config.setAttrMapping(Map.of(
"sourceKey1", "targetKey1",

View File

@ -15,9 +15,11 @@
*/
package org.thingsboard.rule.engine.metadata;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@ -44,6 +46,7 @@ import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.util.TbPair;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.dao.asset.AssetService;
@ -444,6 +447,17 @@ public class TbGetCustomerDetailsNodeTest {
assertThat(actualException.getMessage()).isEqualTo("Entity with entityType 'DASHBOARD' is not supported.");
}
@Test
public void givenOldConfig_whenUpgrade_thenShouldReturnSuccessResult() throws Exception {
var defaultConfig = new TbGetCustomerDetailsNodeConfiguration().defaultConfiguration();
var node = new TbGetCustomerDetailsNode();
String oldConfig = "{\"detailsList\":[],\"addToMetadata\":false}";
JsonNode configJson = JacksonUtil.toJsonNode(oldConfig);
TbPair<Boolean, JsonNode> upgrade = node.upgrade(0, configJson);
Assertions.assertTrue(upgrade.getFirst());
Assertions.assertEquals(JacksonUtil.valueToTree(defaultConfig), upgrade.getSecond());
}
private void prepareMsgAndConfig(FetchTo fetchTo, List<ContactBasedEntityDetails> detailsList, EntityId originator) {
config.setDetailsList(detailsList);
config.setFetchTo(fetchTo);

View File

@ -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.rule.engine.metadata;
import com.fasterxml.jackson.databind.JsonNode;
import org.junit.Test;
import org.junit.jupiter.api.Assertions;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.util.TbPair;
public class TbGetDeviceAttrNodeTest {
@Test
public void givenOldConfig_whenUpgrade_thenShouldReturnSuccessResult() throws Exception {
var defaultConfig = new TbGetDeviceAttrNodeConfiguration().defaultConfiguration();
var node = new TbGetDeviceAttrNode();
String oldConfig = "{\"fetchToData\":false," +
"\"clientAttributeNames\":[]," +
"\"sharedAttributeNames\":[]," +
"\"serverAttributeNames\":[]," +
"\"latestTsKeyNames\":[]," +
"\"tellFailureIfAbsent\":true," +
"\"getLatestValueWithTs\":false," +
"\"deviceRelationsQuery\":{\"direction\":\"FROM\",\"maxLevel\":1,\"relationType\":\"Contains\",\"deviceTypes\":[\"default\"]," +
"\"fetchLastLevelOnly\":false}}";
JsonNode configJson = JacksonUtil.toJsonNode(oldConfig);
TbPair<Boolean, JsonNode> upgrade = node.upgrade(0, configJson);
Assertions.assertTrue(upgrade.getFirst());
Assertions.assertEquals(JacksonUtil.valueToTree(defaultConfig), upgrade.getSecond());
}
}

View File

@ -15,9 +15,11 @@
*/
package org.thingsboard.rule.engine.metadata;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@ -33,6 +35,7 @@ import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.util.TbPair;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.dao.device.DeviceService;
@ -339,4 +342,15 @@ public class TbGetOriginatorFieldsNodeTest {
assertThat(actualMessageCaptor.getValue().getMetaData()).isEqualTo(msgMetaData);
}
@Test
public void givenOldConfig_whenUpgrade_thenShouldReturnSuccessResult() throws Exception {
var defaultConfig = new TbGetOriginatorFieldsConfiguration().defaultConfiguration();
var node = new TbGetOriginatorFieldsNode();
String oldConfig = "{\"fieldsMapping\":{\"name\":\"originatorName\",\"type\":\"originatorType\"},\"ignoreNullStrings\":false}";
JsonNode configJson = JacksonUtil.toJsonNode(oldConfig);
TbPair<Boolean, JsonNode> upgrade = node.upgrade(0, configJson);
Assertions.assertTrue(upgrade.getFirst());
Assertions.assertEquals(JacksonUtil.valueToTree(defaultConfig), upgrade.getSecond());
}
}

View File

@ -15,10 +15,12 @@
*/
package org.thingsboard.rule.engine.metadata;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@ -43,6 +45,7 @@ import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.EntitySearchDirection;
import org.thingsboard.server.common.data.relation.RelationEntityTypeFilter;
import org.thingsboard.server.common.data.util.TbPair;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.common.msg.session.SessionMsgType;
@ -548,6 +551,21 @@ public class TbGetRelatedAttributeNodeTest {
assertThat(actualMessageCaptor.getValue().getMetaData()).isEqualTo(expectedMsgMetadata);
}
@Test
public void givenOldConfig_whenUpgrade_thenShouldReturnSuccessResult() throws Exception {
var defaultConfig = new TbGetRelatedAttrNodeConfiguration().defaultConfiguration();
var node = new TbGetRelatedAttributeNode();
String oldConfig = "{\"attrMapping\":{\"serialNumber\":\"sn\"}," +
"\"relationsQuery\":{\"direction\":\"FROM\",\"maxLevel\":1," +
"\"filters\":[{\"relationType\":\"Contains\",\"entityTypes\":[]}]," +
"\"fetchLastLevelOnly\":false}," +
"\"telemetry\":false}";
JsonNode configJson = JacksonUtil.toJsonNode(oldConfig);
TbPair<Boolean, JsonNode> upgrade = node.upgrade(0, configJson);
Assertions.assertTrue(upgrade.getFirst());
Assertions.assertEquals(JacksonUtil.valueToTree(defaultConfig), upgrade.getSecond());
}
private void prepareMsgAndConfig(FetchTo fetchTo, DataToFetch dataToFetch, EntityId originator) {
config.setDataToFetch(dataToFetch);

View File

@ -15,10 +15,12 @@
*/
package org.thingsboard.rule.engine.metadata;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@ -41,6 +43,7 @@ import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
import org.thingsboard.server.common.data.kv.StringDataEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.util.TbPair;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.dao.attributes.AttributesService;
@ -381,6 +384,17 @@ public class TbGetTenantAttributeNodeTest {
assertThat(actualMessageCaptor.getValue().getMetaData()).isEqualTo(expectedMsgMetaData);
}
@Test
public void givenOldConfig_whenUpgrade_thenShouldReturnSuccessResult() throws Exception {
var defaultConfig = new TbGetEntityAttrNodeConfiguration().defaultConfiguration();
var node = new TbGetTenantAttributeNode();
String oldConfig = "{\"attrMapping\":{\"alarmThreshold\":\"threshold\"},\"telemetry\":false}";
JsonNode configJson = JacksonUtil.toJsonNode(oldConfig);
TbPair<Boolean, JsonNode> upgrade = node.upgrade(0, configJson);
Assertions.assertTrue(upgrade.getFirst());
Assertions.assertEquals(JacksonUtil.valueToTree(defaultConfig), upgrade.getSecond());
}
private void prepareMsgAndConfig(FetchTo fetchTo, DataToFetch dataToFetch, EntityId originator) {
config.setAttrMapping(Map.of(
"sourceKey1", "targetKey1",

View File

@ -15,7 +15,9 @@
*/
package org.thingsboard.rule.engine.metadata;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.util.concurrent.Futures;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@ -30,6 +32,7 @@ import org.thingsboard.rule.engine.util.ContactBasedEntityDetails;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.util.TbPair;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.dao.tenant.TenantService;
@ -259,6 +262,17 @@ public class TbGetTenantDetailsNodeTest {
assertThat(actualMessageCaptor.getValue().getMetaData()).isEqualTo(msg.getMetaData());
}
@Test
public void givenOldConfig_whenUpgrade_thenShouldReturnSuccessResult() throws Exception {
var defaultConfig = new TbGetTenantDetailsNodeConfiguration().defaultConfiguration();
var node = new TbGetTenantDetailsNode();
String oldConfig = "{\"detailsList\":[],\"addToMetadata\":false}";
JsonNode configJson = JacksonUtil.toJsonNode(oldConfig);
TbPair<Boolean, JsonNode> upgrade = node.upgrade(0, configJson);
Assertions.assertTrue(upgrade.getFirst());
Assertions.assertEquals(JacksonUtil.valueToTree(defaultConfig), upgrade.getSecond());
}
private void prepareMsgAndConfig(FetchTo fetchTo, List<ContactBasedEntityDetails> detailsList) {
config.setDetailsList(detailsList);
config.setFetchTo(fetchTo);

View File

@ -555,6 +555,7 @@ export class RuleChainPageComponent extends PageComponent
ruleNodeId: ruleNode.id,
additionalInfo: ruleNode.additionalInfo,
configuration: ruleNode.configuration,
configurationVersion: ruleNode.configurationVersion,
debugMode: ruleNode.debugMode,
singletonMode: ruleNode.singletonMode,
x: Math.round(ruleNode.additionalInfo.layoutX),
@ -1424,6 +1425,7 @@ export class RuleChainPageComponent extends PageComponent
id: node.ruleNodeId,
type: node.component.clazz,
name: node.name,
configurationVersion: node.configurationVersion,
configuration: node.configuration,
additionalInfo: node.additionalInfo ? node.additionalInfo : {},
debugMode: node.debugMode,

View File

@ -37,6 +37,7 @@ export interface RuleNode extends BaseData<RuleNodeId> {
name: string;
debugMode: boolean;
singletonMode: boolean;
configurationVersion?: number;
configuration: RuleNodeConfiguration;
additionalInfo?: any;
}