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 70c150b5f2..22cdcdcd18 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 @@ -19,7 +19,6 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.flow.TbRuleChainInputNode; import org.thingsboard.rule.engine.flow.TbRuleChainInputNodeConfiguration; import org.thingsboard.rule.engine.flow.TbRuleChainOutputNode; @@ -405,21 +404,16 @@ public class DefaultTbRuleChainService extends AbstractTbEntityService implement if (fromVersion < toVersion) { log.debug("Going to upgrade rule node with id: {} type: {} fromVersion: {} toVersion: {}", ruleNodeId, ruleNodeType, fromVersion, toVersion); - try { - TbNodeUpgradeUtils.upgradeConfigurationAndVersion(node, ruleNodeClass); - log.debug("Successfully upgrade rule node with id: {} type: {}, rule chain id: {} fromVersion: {} toVersion: {}", - ruleNodeId, ruleNodeType, ruleChainId, fromVersion, 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); - } + TbNodeUpgradeUtils.upgradeConfigurationAndVersion(node, ruleNodeClass); + log.debug("Successfully upgrade rule node with id: {} type: {}, rule chain id: {} fromVersion: {} toVersion: {}", + ruleNodeId, ruleNodeType, ruleChainId, fromVersion, toVersion); } else { log.debug("Rule node with id: {} type: {} ruleChainId: {} already set to latest version!", ruleNodeId, ruleChainId, ruleNodeType); } } } catch (Exception e) { - log.error("Failed to update the rule node with id: {} type: {}, rule chain id: {}", + log.error("Failed to upgrade rule node with id: {} type: {}, rule chain id: {}", ruleNodeId, ruleNodeType, ruleChainId, e); } return node; diff --git a/application/src/main/java/org/thingsboard/server/utils/TbNodeUpgradeUtils.java b/application/src/main/java/org/thingsboard/server/utils/TbNodeUpgradeUtils.java index 5aa4176b1f..530679683d 100644 --- a/application/src/main/java/org/thingsboard/server/utils/TbNodeUpgradeUtils.java +++ b/application/src/main/java/org/thingsboard/server/utils/TbNodeUpgradeUtils.java @@ -16,27 +16,60 @@ package org.thingsboard.server.utils; import com.fasterxml.jackson.databind.JsonNode; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.NodeConfiguration; import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.common.data.util.TbPair; import org.thingsboard.server.service.component.RuleNodeClassInfo; +@Slf4j public class TbNodeUpgradeUtils { - public static void upgradeConfigurationAndVersion(RuleNode node, RuleNodeClassInfo nodeInfo) throws Exception { + public static void upgradeConfigurationAndVersion(RuleNode node, RuleNodeClassInfo nodeInfo) { JsonNode oldConfiguration = node.getConfiguration(); + int configurationVersion = node.getConfigurationVersion(); + + int currentVersion = nodeInfo.getCurrentVersion(); + var configClass = nodeInfo.getAnnotation().configClazz(); + if (oldConfiguration == null || !oldConfiguration.isObject()) { - var configClass = nodeInfo.getAnnotation().configClazz(); - node.setConfiguration(JacksonUtil.valueToTree(configClass.getDeclaredConstructor().newInstance().defaultConfiguration())); + log.warn("Failed to upgrade rule node with id: {} type: {} fromVersion: {} toVersion: {}. " + + "Current configuration is null or not a json object. " + + "Going to set default configuration ... ", + node.getId(), node.getType(), configurationVersion, currentVersion); + node.setConfiguration(getDefaultConfig(configClass)); } else { - var tbVersionedNode = (TbNode) nodeInfo.getClazz().getDeclaredConstructor().newInstance(); - TbPair upgradeResult = tbVersionedNode.upgrade(node.getConfigurationVersion(), oldConfiguration); - if (upgradeResult.getFirst()) { - node.setConfiguration(upgradeResult.getSecond()); + var tbVersionedNode = getTbVersionedNode(nodeInfo); + try { + TbPair upgradeResult = tbVersionedNode.upgrade(configurationVersion, oldConfiguration); + if (upgradeResult.getFirst()) { + node.setConfiguration(upgradeResult.getSecond()); + } + } catch (Exception e) { + try { + JacksonUtil.treeToValue(oldConfiguration, configClass); + } catch (Exception ex) { + log.warn("Failed to upgrade rule node with id: {} type: {} fromVersion: {} toVersion: {}. " + + "Going to set default configuration ... ", + node.getId(), node.getType(), configurationVersion, currentVersion, e); + node.setConfiguration(getDefaultConfig(configClass)); + } } } - node.setConfigurationVersion(nodeInfo.getCurrentVersion()); + node.setConfigurationVersion(currentVersion); + } + + @SneakyThrows + private static TbNode getTbVersionedNode(RuleNodeClassInfo nodeInfo) { + return (TbNode) nodeInfo.getClazz().getDeclaredConstructor().newInstance(); + } + + @SneakyThrows + private static JsonNode getDefaultConfig(Class configClass) { + return JacksonUtil.valueToTree(configClass.getDeclaredConstructor().newInstance().defaultConfiguration()); } } diff --git a/application/src/test/java/org/thingsboard/server/utils/TbNodeUpgradeUtilsTest.java b/application/src/test/java/org/thingsboard/server/utils/TbNodeUpgradeUtilsTest.java index 483772301b..5acf00f0c0 100644 --- a/application/src/test/java/org/thingsboard/server/utils/TbNodeUpgradeUtilsTest.java +++ b/application/src/test/java/org/thingsboard/server/utils/TbNodeUpgradeUtilsTest.java @@ -21,6 +21,8 @@ import org.junit.Test; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.metadata.TbGetAttributesNode; import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration; +import org.thingsboard.rule.engine.metadata.TbGetCustomerAttributeNode; +import org.thingsboard.rule.engine.metadata.TbGetEntityDataNodeConfiguration; import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.service.component.RuleNodeClassInfo; @@ -38,7 +40,7 @@ public class TbNodeUpgradeUtilsTest { var annotation = mock(org.thingsboard.rule.engine.api.RuleNode.class); var defaultConfig = JacksonUtil.valueToTree(nodeConfigClazz.getDeclaredConstructor().newInstance().defaultConfiguration()); - when(nodeInfo.getClazz()).thenReturn((Class)TbGetAttributesNode.class); + when(nodeInfo.getClazz()).thenReturn((Class) TbGetAttributesNode.class); when(nodeInfo.getCurrentVersion()).thenReturn(1); when(nodeInfo.getAnnotation()).thenReturn(annotation); when(annotation.configClazz()).thenReturn((Class) nodeConfigClazz); @@ -60,7 +62,7 @@ public class TbNodeUpgradeUtilsTest { var annotation = mock(org.thingsboard.rule.engine.api.RuleNode.class); var defaultConfig = JacksonUtil.valueToTree(nodeConfigClazz.getDeclaredConstructor().newInstance().defaultConfiguration()); - when(nodeInfo.getClazz()).thenReturn((Class)TbGetAttributesNode.class); + when(nodeInfo.getClazz()).thenReturn((Class) TbGetAttributesNode.class); when(nodeInfo.getCurrentVersion()).thenReturn(1); when(nodeInfo.getAnnotation()).thenReturn(annotation); when(annotation.configClazz()).thenReturn((Class) nodeConfigClazz); @@ -81,7 +83,7 @@ public class TbNodeUpgradeUtilsTest { var annotation = mock(org.thingsboard.rule.engine.api.RuleNode.class); var defaultConfig = JacksonUtil.valueToTree(nodeConfigClazz.getDeclaredConstructor().newInstance().defaultConfiguration()); - when(nodeInfo.getClazz()).thenReturn((Class)TbGetAttributesNode.class); + when(nodeInfo.getClazz()).thenReturn((Class) TbGetAttributesNode.class); when(nodeInfo.getCurrentVersion()).thenReturn(1); when(nodeInfo.getAnnotation()).thenReturn(annotation); when(annotation.configClazz()).thenReturn((Class) nodeConfigClazz); @@ -102,4 +104,55 @@ public class TbNodeUpgradeUtilsTest { } + @Test + public void testUpgradeRuleNodeConfigurationWithNewConfigAndOldConfigVersion() throws Exception { + // GIVEN + var node = new RuleNode(); + var nodeInfo = mock(RuleNodeClassInfo.class); + var nodeConfigClazz = TbGetEntityDataNodeConfiguration.class; + var annotation = mock(org.thingsboard.rule.engine.api.RuleNode.class); + var defaultConfig = JacksonUtil.valueToTree(nodeConfigClazz.getDeclaredConstructor().newInstance().defaultConfiguration()); + + when(nodeInfo.getClazz()).thenReturn((Class) TbGetCustomerAttributeNode.class); + when(nodeInfo.getCurrentVersion()).thenReturn(1); + when(nodeInfo.getAnnotation()).thenReturn(annotation); + when(annotation.configClazz()).thenReturn((Class) nodeConfigClazz); + + String versionOneDefaultConfig = "{\"fetchTo\":\"METADATA\"," + + "\"dataMapping\":{\"alarmThreshold\":\"threshold\"}," + + "\"dataToFetch\":\"ATTRIBUTES\"}"; + node.setConfiguration(JacksonUtil.toJsonNode(versionOneDefaultConfig)); + // WHEN + TbNodeUpgradeUtils.upgradeConfigurationAndVersion(node, nodeInfo); + // THEN + Assertions.assertThat(node.getConfiguration()).isEqualTo(defaultConfig); + Assertions.assertThat(node.getConfigurationVersion()).isEqualTo(1); + + } + + @Test + public void testUpgradeRuleNodeConfigurationWithInvalidConfigAndOldConfigVersion() throws Exception { + // GIVEN + var node = new RuleNode(); + var nodeInfo = mock(RuleNodeClassInfo.class); + var nodeConfigClazz = TbGetEntityDataNodeConfiguration.class; + var annotation = mock(org.thingsboard.rule.engine.api.RuleNode.class); + var defaultConfig = JacksonUtil.valueToTree(nodeConfigClazz.getDeclaredConstructor().newInstance().defaultConfiguration()); + + when(nodeInfo.getClazz()).thenReturn((Class) TbGetCustomerAttributeNode.class); + when(nodeInfo.getCurrentVersion()).thenReturn(1); + when(nodeInfo.getAnnotation()).thenReturn(annotation); + when(annotation.configClazz()).thenReturn((Class) nodeConfigClazz); + + // missing telemetry field + String oldConfig = "{\"attrMapping\":{\"alarmThreshold\":\"threshold\"}}";; + node.setConfiguration(JacksonUtil.toJsonNode(oldConfig)); + // WHEN + TbNodeUpgradeUtils.upgradeConfigurationAndVersion(node, nodeInfo); + // THEN + Assertions.assertThat(node.getConfiguration()).isEqualTo(defaultConfig); + Assertions.assertThat(node.getConfigurationVersion()).isEqualTo(1); + + } + }