added TbNodeUpgradeUtils && fixed upgrade when config is null or NullNode

This commit is contained in:
ShvaykaD 2023-11-10 15:42:10 +02:00
parent c42599c0d9
commit 92178384ea
5 changed files with 157 additions and 17 deletions

View File

@ -27,7 +27,6 @@ import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Profile; import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.common.util.JacksonUtil; 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.TbRuleChainInputNode;
import org.thingsboard.rule.engine.flow.TbRuleChainInputNodeConfiguration; import org.thingsboard.rule.engine.flow.TbRuleChainInputNodeConfiguration;
import org.thingsboard.rule.engine.profile.TbDeviceProfileNode; 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.RuleChainType;
import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.server.common.data.tenant.profile.TenantProfileQueueConfiguration; 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.DaoUtil;
import org.thingsboard.server.dao.alarm.AlarmDao; import org.thingsboard.server.dao.alarm.AlarmDao;
import org.thingsboard.server.dao.audit.AuditLogDao; 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.component.RuleNodeClassInfo;
import org.thingsboard.server.service.install.InstallScripts; import org.thingsboard.server.service.install.InstallScripts;
import org.thingsboard.server.service.install.SystemDataLoaderService; import org.thingsboard.server.service.install.SystemDataLoaderService;
import org.thingsboard.server.utils.TbNodeUpgradeUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -291,16 +290,12 @@ public class DefaultDataUpdateService implements DataUpdateService {
continue; continue;
} }
var ruleNodeId = ruleNode.getId(); var ruleNodeId = ruleNode.getId();
var oldConfiguration = ruleNode.getConfiguration();
int fromVersion = ruleNode.getConfigurationVersion(); int fromVersion = ruleNode.getConfigurationVersion();
log.debug("Going to upgrade rule node with id: {} type: {} fromVersion: {} toVersion: {}", log.debug("Going to upgrade rule node with id: {} type: {} fromVersion: {} toVersion: {}",
ruleNodeId, ruleNodeType, fromVersion, toVersion); ruleNodeId, ruleNodeType, fromVersion, toVersion);
try { try {
var tbVersionedNode = (TbNode) ruleNodeClassInfo.getClazz().getDeclaredConstructor().newInstance(); ruleNode.setConfiguration(TbNodeUpgradeUtils.upgradeRuleNodeConfiguration(ruleNode, ruleNodeClassInfo.getAnnotation(),
TbPair<Boolean, JsonNode> upgradeRuleNodeConfigurationResult = tbVersionedNode.upgrade(fromVersion, oldConfiguration); ruleNodeClassInfo.getClazz()));
if (upgradeRuleNodeConfigurationResult.getFirst()) {
ruleNode.setConfiguration(upgradeRuleNodeConfigurationResult.getSecond());
}
ruleNode.setConfigurationVersion(toVersion); ruleNode.setConfigurationVersion(toVersion);
saveFutures.add(jpaExecutorService.submit(() -> { saveFutures.add(jpaExecutorService.submit(() -> {
ruleChainService.saveRuleNode(TenantId.SYS_TENANT_ID, ruleNode); ruleChainService.saveRuleNode(TenantId.SYS_TENANT_ID, ruleNode);

View File

@ -15,12 +15,10 @@
*/ */
package org.thingsboard.server.service.rule; package org.thingsboard.server.service.rule;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.common.util.JacksonUtil; 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.TbNodeException;
import org.thingsboard.rule.engine.flow.TbRuleChainInputNode; import org.thingsboard.rule.engine.flow.TbRuleChainInputNode;
import org.thingsboard.rule.engine.flow.TbRuleChainInputNodeConfiguration; 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.RuleChainUpdateResult;
import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.server.common.data.rule.RuleNodeUpdateResult; 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.relation.RelationService;
import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.component.ComponentDiscoveryService; import org.thingsboard.server.service.component.ComponentDiscoveryService;
import org.thingsboard.server.service.entitiy.AbstractTbEntityService; import org.thingsboard.server.service.entitiy.AbstractTbEntityService;
import org.thingsboard.server.service.install.InstallScripts; import org.thingsboard.server.service.install.InstallScripts;
import org.thingsboard.server.utils.TbNodeUpgradeUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -402,17 +400,14 @@ public class DefaultTbRuleChainService extends AbstractTbEntityService implement
var ruleNodeClass = componentDiscoveryService.getRuleNodeInfo(ruleNodeType) var ruleNodeClass = componentDiscoveryService.getRuleNodeInfo(ruleNodeType)
.orElseThrow(() -> new RuntimeException("Rule node " + ruleNodeType + " is not supported!")); .orElseThrow(() -> new RuntimeException("Rule node " + ruleNodeType + " is not supported!"));
if (ruleNodeClass.isVersioned()) { if (ruleNodeClass.isVersioned()) {
TbNode tbVersionedNode = (TbNode) ruleNodeClass.getClazz().getDeclaredConstructor().newInstance();
int fromVersion = node.getConfigurationVersion(); int fromVersion = node.getConfigurationVersion();
int toVersion = ruleNodeClass.getCurrentVersion(); int toVersion = ruleNodeClass.getCurrentVersion();
if (fromVersion < toVersion) { if (fromVersion < toVersion) {
log.debug("Going to upgrade rule node with id: {} type: {} fromVersion: {} toVersion: {}", log.debug("Going to upgrade rule node with id: {} type: {} fromVersion: {} toVersion: {}",
ruleNodeId, ruleNodeType, fromVersion, toVersion); ruleNodeId, ruleNodeType, fromVersion, toVersion);
try { try {
TbPair<Boolean, JsonNode> upgradeResult = tbVersionedNode.upgrade(fromVersion, node.getConfiguration()); node.setConfiguration(TbNodeUpgradeUtils.upgradeRuleNodeConfiguration(node, ruleNodeClass.getAnnotation(),
if (upgradeResult.getFirst()) { ruleNodeClass.getClazz()));
node.setConfiguration(upgradeResult.getSecond());
}
node.setConfigurationVersion(toVersion); node.setConfigurationVersion(toVersion);
log.debug("Successfully upgrade rule node with id: {} type: {}, rule chain id: {} fromVersion: {} toVersion: {}", log.debug("Successfully upgrade rule node with id: {} type: {}, rule chain id: {} fromVersion: {} toVersion: {}",
ruleNodeId, ruleNodeType, ruleChainId, fromVersion, toVersion); ruleNodeId, ruleNodeType, ruleChainId, fromVersion, toVersion);

View File

@ -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<Boolean, JsonNode> upgradeResult = tbVersionedNode.upgrade(node.getConfigurationVersion(), oldConfiguration);
return upgradeResult.getFirst() ? upgradeResult.getSecond() : oldConfiguration;
}
}

View File

@ -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);
}
}

View File

@ -15,7 +15,6 @@
*/ */
package org.thingsboard.rule.engine.api.util; package org.thingsboard.rule.engine.api.util;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
@ -85,6 +84,7 @@ public class TbNodeUtils {
} }
} }
@Deprecated(since = "3.6.1", forRemoval = true)
public static List<String> processPatterns(List<String> patterns, TbMsgMetaData metaData) { public static List<String> processPatterns(List<String> patterns, TbMsgMetaData metaData) {
if (!CollectionUtils.isEmpty(patterns)) { if (!CollectionUtils.isEmpty(patterns)) {
return patterns.stream().map(p -> processPattern(p, metaData)).collect(Collectors.toList()); return patterns.stream().map(p -> processPattern(p, metaData)).collect(Collectors.toList());