From 92178384eaaee61d726649b75af5c8746e391845 Mon Sep 17 00:00:00 2001 From: ShvaykaD Date: Fri, 10 Nov 2023 15:42:10 +0200 Subject: [PATCH] added TbNodeUpgradeUtils && fixed upgrade when config is null or NullNode --- .../update/DefaultDataUpdateService.java | 11 +- .../rule/DefaultTbRuleChainService.java | 11 +- .../server/utils/TbNodeUpgradeUtils.java | 39 ++++++ .../server/utils/TbNodeUpgradeUtilsTest.java | 111 ++++++++++++++++++ .../rule/engine/api/util/TbNodeUtils.java | 2 +- 5 files changed, 157 insertions(+), 17 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/utils/TbNodeUpgradeUtils.java create mode 100644 application/src/test/java/org/thingsboard/server/utils/TbNodeUpgradeUtilsTest.java diff --git a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java index c49578c912..5375590084 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java @@ -27,7 +27,6 @@ import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.flow.TbRuleChainInputNode; import org.thingsboard.rule.engine.flow.TbRuleChainInputNodeConfiguration; import org.thingsboard.rule.engine.profile.TbDeviceProfileNode; @@ -67,7 +66,6 @@ import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.common.data.tenant.profile.TenantProfileQueueConfiguration; -import org.thingsboard.server.common.data.util.TbPair; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.alarm.AlarmDao; import org.thingsboard.server.dao.audit.AuditLogDao; @@ -90,6 +88,7 @@ import org.thingsboard.server.service.component.ComponentDiscoveryService; import org.thingsboard.server.service.component.RuleNodeClassInfo; import org.thingsboard.server.service.install.InstallScripts; import org.thingsboard.server.service.install.SystemDataLoaderService; +import org.thingsboard.server.utils.TbNodeUpgradeUtils; import java.util.ArrayList; import java.util.Collections; @@ -291,16 +290,12 @@ public class DefaultDataUpdateService implements DataUpdateService { continue; } var ruleNodeId = ruleNode.getId(); - var oldConfiguration = ruleNode.getConfiguration(); int fromVersion = ruleNode.getConfigurationVersion(); log.debug("Going to upgrade rule node with id: {} type: {} fromVersion: {} toVersion: {}", ruleNodeId, ruleNodeType, fromVersion, toVersion); try { - var tbVersionedNode = (TbNode) ruleNodeClassInfo.getClazz().getDeclaredConstructor().newInstance(); - TbPair upgradeRuleNodeConfigurationResult = tbVersionedNode.upgrade(fromVersion, oldConfiguration); - if (upgradeRuleNodeConfigurationResult.getFirst()) { - ruleNode.setConfiguration(upgradeRuleNodeConfigurationResult.getSecond()); - } + ruleNode.setConfiguration(TbNodeUpgradeUtils.upgradeRuleNodeConfiguration(ruleNode, ruleNodeClassInfo.getAnnotation(), + ruleNodeClassInfo.getClazz())); ruleNode.setConfigurationVersion(toVersion); saveFutures.add(jpaExecutorService.submit(() -> { ruleChainService.saveRuleNode(TenantId.SYS_TENANT_ID, ruleNode); 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 56df2caa71..50f370fbb9 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 @@ -15,12 +15,10 @@ */ package org.thingsboard.server.service.rule; -import com.fasterxml.jackson.databind.JsonNode; 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.TbNode; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.flow.TbRuleChainInputNode; import org.thingsboard.rule.engine.flow.TbRuleChainInputNodeConfiguration; @@ -44,13 +42,13 @@ import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.data.rule.RuleChainUpdateResult; import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.common.data.rule.RuleNodeUpdateResult; -import org.thingsboard.server.common.data.util.TbPair; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.component.ComponentDiscoveryService; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; import org.thingsboard.server.service.install.InstallScripts; +import org.thingsboard.server.utils.TbNodeUpgradeUtils; import java.util.ArrayList; import java.util.Collections; @@ -402,17 +400,14 @@ public class DefaultTbRuleChainService extends AbstractTbEntityService implement var ruleNodeClass = componentDiscoveryService.getRuleNodeInfo(ruleNodeType) .orElseThrow(() -> new RuntimeException("Rule node " + ruleNodeType + " is not supported!")); if (ruleNodeClass.isVersioned()) { - TbNode tbVersionedNode = (TbNode) ruleNodeClass.getClazz().getDeclaredConstructor().newInstance(); int fromVersion = node.getConfigurationVersion(); int toVersion = ruleNodeClass.getCurrentVersion(); if (fromVersion < toVersion) { log.debug("Going to upgrade rule node with id: {} type: {} fromVersion: {} toVersion: {}", ruleNodeId, ruleNodeType, fromVersion, toVersion); try { - TbPair upgradeResult = tbVersionedNode.upgrade(fromVersion, node.getConfiguration()); - if (upgradeResult.getFirst()) { - node.setConfiguration(upgradeResult.getSecond()); - } + node.setConfiguration(TbNodeUpgradeUtils.upgradeRuleNodeConfiguration(node, ruleNodeClass.getAnnotation(), + ruleNodeClass.getClazz())); node.setConfigurationVersion(toVersion); log.debug("Successfully upgrade rule node with id: {} type: {}, rule chain id: {} fromVersion: {} toVersion: {}", ruleNodeId, ruleNodeType, ruleChainId, fromVersion, toVersion); diff --git a/application/src/main/java/org/thingsboard/server/utils/TbNodeUpgradeUtils.java b/application/src/main/java/org/thingsboard/server/utils/TbNodeUpgradeUtils.java new file mode 100644 index 0000000000..98f5e713df --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/utils/TbNodeUpgradeUtils.java @@ -0,0 +1,39 @@ +/** + * 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.utils; + +import com.fasterxml.jackson.databind.JsonNode; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.TbNode; +import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.common.data.util.TbPair; + +public class TbNodeUpgradeUtils { + + public static JsonNode upgradeRuleNodeConfiguration(RuleNode node, + org.thingsboard.rule.engine.api.RuleNode annotation, + Class nodeClass) throws Exception { + JsonNode oldConfiguration = node.getConfiguration(); + if (oldConfiguration == null || !oldConfiguration.isObject()) { + var configClass = annotation.configClazz(); + return JacksonUtil.valueToTree(configClass.getDeclaredConstructor().newInstance().defaultConfiguration()); + } + var tbVersionedNode = (TbNode) nodeClass.getDeclaredConstructor().newInstance(); + TbPair upgradeResult = tbVersionedNode.upgrade(node.getConfigurationVersion(), oldConfiguration); + return upgradeResult.getFirst() ? upgradeResult.getSecond() : oldConfiguration; + } + +} diff --git a/application/src/test/java/org/thingsboard/server/utils/TbNodeUpgradeUtilsTest.java b/application/src/test/java/org/thingsboard/server/utils/TbNodeUpgradeUtilsTest.java new file mode 100644 index 0000000000..1fb2ddcc25 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/utils/TbNodeUpgradeUtilsTest.java @@ -0,0 +1,111 @@ +/** + * 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.utils; + +import com.fasterxml.jackson.databind.node.NullNode; +import org.assertj.core.api.Assertions; +import org.junit.Test; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.TbNode; +import org.thingsboard.rule.engine.metadata.TbGetAttributesNode; +import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration; +import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.common.data.util.TbPair; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class TbNodeUpgradeUtilsTest { + + @Test + public void testUpgradeRuleNodeConfigurationWithNullConfig() throws Exception { + // GIVEN + var node = mock(RuleNode.class); + var nodeClass = TbGetAttributesNode.class; + var nodeConfigClazz = TbGetAttributesNodeConfiguration.class; + + var annotation = mock(org.thingsboard.rule.engine.api.RuleNode.class); + + var defaultConfig = JacksonUtil.valueToTree(nodeConfigClazz.getDeclaredConstructor().newInstance().defaultConfiguration()); + + when(node.getConfiguration()).thenReturn(null); + when(node.getConfigurationVersion()).thenReturn(0); + when(annotation.configClazz()).thenReturn((Class) nodeConfigClazz); + // WHEN + var upgradedConfig = TbNodeUpgradeUtils.upgradeRuleNodeConfiguration(node, annotation, nodeClass); + + // THEN + Assertions.assertThat(upgradedConfig).isEqualTo(defaultConfig); + } + + @Test + public void testUpgradeRuleNodeConfigurationWithNullNodeConfig() throws Exception { + // GIVEN + var node = mock(RuleNode.class); + var nodeClass = TbGetAttributesNode.class; + var nodeConfigClazz = TbGetAttributesNodeConfiguration.class; + + var annotation = mock(org.thingsboard.rule.engine.api.RuleNode.class); + + var defaultConfig = JacksonUtil.valueToTree(nodeConfigClazz.getDeclaredConstructor().newInstance().defaultConfiguration()); + + when(node.getConfiguration()).thenReturn(NullNode.instance); + when(node.getConfigurationVersion()).thenReturn(0); + when(annotation.configClazz()).thenReturn((Class) nodeConfigClazz); + // WHEN + var upgradedConfig = TbNodeUpgradeUtils.upgradeRuleNodeConfiguration(node, annotation, nodeClass); + + // THEN + Assertions.assertThat(upgradedConfig).isEqualTo(defaultConfig); + } + + @Test + public void testUpgradeRuleNodeConfigurationWithNonNullConfig() throws Exception { + // GIVEN + var node = mock(RuleNode.class); + var nodeClass = TbGetAttributesNode.class; + var nodeConfigClazz = TbGetAttributesNodeConfiguration.class; + + var annotation = mock(org.thingsboard.rule.engine.api.RuleNode.class); + + String versionZeroDefaultConfigStr = "{\"fetchToData\":false," + + "\"clientAttributeNames\":[]," + + "\"sharedAttributeNames\":[]," + + "\"serverAttributeNames\":[]," + + "\"latestTsKeyNames\":[]," + + "\"tellFailureIfAbsent\":true," + + "\"getLatestValueWithTs\":false}"; + + var existingConfig = JacksonUtil.toJsonNode(versionZeroDefaultConfigStr); + int fromVersion = 0; + var currentDefaultConfig = JacksonUtil.valueToTree(nodeConfigClazz.getDeclaredConstructor().newInstance().defaultConfiguration()); + + when(node.getConfiguration()).thenReturn(existingConfig); + when(node.getConfigurationVersion()).thenReturn(fromVersion); + when(annotation.configClazz()).thenReturn((Class) nodeConfigClazz); + + TbNode tbVersionedNodeMock = mock(nodeClass); + + when(tbVersionedNodeMock.upgrade(fromVersion, existingConfig)).thenReturn(new TbPair<>(true, currentDefaultConfig)); + + // WHEN + var upgradedConfig = TbNodeUpgradeUtils.upgradeRuleNodeConfiguration(node, annotation, nodeClass); + + // THEN + Assertions.assertThat(upgradedConfig).isEqualTo(currentDefaultConfig); + } + +} diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/util/TbNodeUtils.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/util/TbNodeUtils.java index bbba1855c2..396d810b00 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/util/TbNodeUtils.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/util/TbNodeUtils.java @@ -15,7 +15,6 @@ */ package org.thingsboard.rule.engine.api.util; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import org.springframework.util.CollectionUtils; import org.thingsboard.common.util.JacksonUtil; @@ -85,6 +84,7 @@ public class TbNodeUtils { } } + @Deprecated(since = "3.6.1", forRemoval = true) public static List processPatterns(List patterns, TbMsgMetaData metaData) { if (!CollectionUtils.isEmpty(patterns)) { return patterns.stream().map(p -> processPattern(p, metaData)).collect(Collectors.toList());