From 40d92b612f8bbb5710c956de5792de8be173ddb1 Mon Sep 17 00:00:00 2001 From: ShvaykaD Date: Mon, 20 Nov 2023 14:03:53 +0200 Subject: [PATCH 1/4] added logic to update config version for nodes with valid config --- .../server/utils/TbNodeUpgradeUtils.java | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) 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..90e9c07707 100644 --- a/application/src/main/java/org/thingsboard/server/utils/TbNodeUpgradeUtils.java +++ b/application/src/main/java/org/thingsboard/server/utils/TbNodeUpgradeUtils.java @@ -17,7 +17,9 @@ package org.thingsboard.server.utils; import com.fasterxml.jackson.databind.JsonNode; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.NodeConfiguration; import org.thingsboard.rule.engine.api.TbNode; +import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.common.data.util.TbPair; import org.thingsboard.server.service.component.RuleNodeClassInfo; @@ -26,17 +28,33 @@ public class TbNodeUpgradeUtils { public static void upgradeConfigurationAndVersion(RuleNode node, RuleNodeClassInfo nodeInfo) throws Exception { JsonNode oldConfiguration = node.getConfiguration(); + var configClass = nodeInfo.getAnnotation().configClazz(); + if (oldConfiguration == null || !oldConfiguration.isObject()) { - var configClass = nodeInfo.getAnnotation().configClazz(); node.setConfiguration(JacksonUtil.valueToTree(configClass.getDeclaredConstructor().newInstance().defaultConfiguration())); } else { var tbVersionedNode = (TbNode) nodeInfo.getClazz().getDeclaredConstructor().newInstance(); - TbPair upgradeResult = tbVersionedNode.upgrade(node.getConfigurationVersion(), oldConfiguration); - if (upgradeResult.getFirst()) { - node.setConfiguration(upgradeResult.getSecond()); + try { + TbPair upgradeResult = tbVersionedNode.upgrade(node.getConfigurationVersion(), oldConfiguration); + if (upgradeResult.getFirst()) { + node.setConfiguration(upgradeResult.getSecond()); + } + } catch (TbNodeException e) { + if (!isValidConfig(oldConfiguration, configClass)) { + throw e; + } } } node.setConfigurationVersion(nodeInfo.getCurrentVersion()); } + private static boolean isValidConfig(JsonNode oldConfiguration, Class configClass) { + try { + JacksonUtil.treeToValue(oldConfiguration, configClass); + return true; + } catch (Exception e) { + return false; + } + } + } From 5dbb13aba0b04f5ac814db485dfece5b212fc1c7 Mon Sep 17 00:00:00 2001 From: ShvaykaD Date: Mon, 20 Nov 2023 14:39:17 +0200 Subject: [PATCH 2/4] added test testUpgradeRuleNodeConfigurationWithNewConfigAndOldConfigVersion --- .../server/utils/TbNodeUpgradeUtilsTest.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) 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..c7f987f2fe 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; @@ -102,4 +104,30 @@ 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); + + } + } From ebcbda119f798b6ee462ee5a8e90a7b963aaed80 Mon Sep 17 00:00:00 2001 From: ShvaykaD Date: Mon, 20 Nov 2023 15:51:19 +0200 Subject: [PATCH 3/4] logic change after review --- .../rule/DefaultTbRuleChainService.java | 14 ++---- .../server/utils/TbNodeUpgradeUtils.java | 44 +++++++++++++------ .../server/utils/TbNodeUpgradeUtilsTest.java | 31 +++++++++++-- 3 files changed, 62 insertions(+), 27 deletions(-) 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 90e9c07707..fca47585fa 100644 --- a/application/src/main/java/org/thingsboard/server/utils/TbNodeUpgradeUtils.java +++ b/application/src/main/java/org/thingsboard/server/utils/TbNodeUpgradeUtils.java @@ -16,6 +16,8 @@ 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; @@ -24,37 +26,51 @@ 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()) { - 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(); + var tbVersionedNode = getTbVersionedNode(nodeInfo); try { - TbPair upgradeResult = tbVersionedNode.upgrade(node.getConfigurationVersion(), oldConfiguration); + TbPair upgradeResult = tbVersionedNode.upgrade(configurationVersion, oldConfiguration); if (upgradeResult.getFirst()) { node.setConfiguration(upgradeResult.getSecond()); } } catch (TbNodeException e) { - if (!isValidConfig(oldConfiguration, configClass)) { - throw 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); } - private static boolean isValidConfig(JsonNode oldConfiguration, Class configClass) { - try { - JacksonUtil.treeToValue(oldConfiguration, configClass); - return true; - } catch (Exception e) { - return false; - } + @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 c7f987f2fe..5acf00f0c0 100644 --- a/application/src/test/java/org/thingsboard/server/utils/TbNodeUpgradeUtilsTest.java +++ b/application/src/test/java/org/thingsboard/server/utils/TbNodeUpgradeUtilsTest.java @@ -40,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); @@ -62,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); @@ -83,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); @@ -130,4 +130,29 @@ public class TbNodeUpgradeUtilsTest { } + @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); + + } + } From 4d5f9e0c48ab95ad90737476c485f1b46006c9f7 Mon Sep 17 00:00:00 2001 From: ShvaykaD Date: Thu, 23 Nov 2023 12:06:11 +0200 Subject: [PATCH 4/4] replaced catch of TbNodeException with Exception --- .../java/org/thingsboard/server/utils/TbNodeUpgradeUtils.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 fca47585fa..530679683d 100644 --- a/application/src/main/java/org/thingsboard/server/utils/TbNodeUpgradeUtils.java +++ b/application/src/main/java/org/thingsboard/server/utils/TbNodeUpgradeUtils.java @@ -21,7 +21,6 @@ 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.rule.engine.api.TbNodeException; import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.common.data.util.TbPair; import org.thingsboard.server.service.component.RuleNodeClassInfo; @@ -49,7 +48,7 @@ public class TbNodeUpgradeUtils { if (upgradeResult.getFirst()) { node.setConfiguration(upgradeResult.getSecond()); } - } catch (TbNodeException e) { + } catch (Exception e) { try { JacksonUtil.treeToValue(oldConfiguration, configClass); } catch (Exception ex) {