Browse Source

Improved the procedure of rule node update

pull/8753/head
Andrii Shvaika 3 years ago
parent
commit
adcf23cabb
  1. 1
      application/src/main/java/org/thingsboard/server/ThingsboardInstallApplication.java
  2. 2
      application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
  3. 45
      application/src/main/java/org/thingsboard/server/service/bean/AnnotationBeanDiscoveryService.java
  4. 87
      application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java
  5. 6
      application/src/main/java/org/thingsboard/server/service/component/ComponentDiscoveryService.java
  6. 25
      application/src/main/java/org/thingsboard/server/service/component/RuleNodeClassInfo.java
  7. 3
      application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java
  8. 36
      application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java
  9. 50
      application/src/main/java/org/thingsboard/server/service/rule/DefaultTbRuleChainService.java
  10. 3
      application/src/main/java/org/thingsboard/server/service/rule/TbRuleChainService.java
  11. 4
      application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/RuleChainImportService.java
  12. 12
      application/src/test/java/org/thingsboard/server/controller/RuleChainControllerTest.java
  13. 5
      application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java
  14. 2
      application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java
  15. 9
      application/src/test/java/org/thingsboard/server/service/sync/ie/BaseExportImportServiceTest.java
  16. 5
      common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java
  17. 40
      dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java
  18. 3
      dao/src/test/java/org/thingsboard/server/dao/service/EdgeServiceTest.java
  19. 9
      dao/src/test/java/org/thingsboard/server/dao/service/RuleChainServiceTest.java
  20. 2
      rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleNode.java
  21. 2
      rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbVersionedNode.java
  22. 5
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractNodeWithFetchTo.java
  23. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbFetchDeviceCredentialsNode.java
  24. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNode.java
  25. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNode.java
  26. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNode.java
  27. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNode.java
  28. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNode.java
  29. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNode.java
  30. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNode.java
  31. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNode.java

1
application/src/main/java/org/thingsboard/server/ThingsboardInstallApplication.java

@ -27,7 +27,6 @@ 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",

2
application/src/main/java/org/thingsboard/server/controller/RuleChainController.java

@ -460,7 +460,7 @@ public class RuleChainController extends BaseController {
@ApiParam(value = "Enables overwrite for existing rule chains with the same name.")
@RequestParam(required = false, defaultValue = "false") boolean overwrite) throws ThingsboardException {
TenantId tenantId = getCurrentUser().getTenantId();
List<RuleChainImportResult> importResults = ruleChainService.importTenantRuleChains(tenantId, ruleChainData, overwrite);
List<RuleChainImportResult> importResults = ruleChainService.importTenantRuleChains(tenantId, ruleChainData, overwrite, tbRuleChainService::updateRuleNodeConfiguration);
for (RuleChainImportResult importResult : importResults) {
if (importResult.getError() == null) {
tbClusterService.broadcastEntityStateChangeEvent(importResult.getTenantId(), importResult.getRuleChainId(),

45
application/src/main/java/org/thingsboard/server/service/bean/AnnotationBeanDiscoveryService.java

@ -1,45 +0,0 @@
/**
* 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;
}
}

87
application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java

@ -19,9 +19,12 @@ import com.fasterxml.jackson.databind.JsonNode;
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.common.util.JacksonUtil;
import org.thingsboard.rule.engine.api.NodeConfiguration;
@ -34,17 +37,19 @@ 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;
import java.util.Set;
import java.util.stream.Collectors;
@Service
@Slf4j
@ -52,20 +57,22 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
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;
private Map<String, ComponentDescriptor> components = new HashMap<>();
private final Map<String, RuleNodeClassInfo> ruleNodeClasses = new HashMap<>();
private final Map<String, ComponentDescriptor> components = new HashMap<>();
private Map<ComponentType, List<ComponentDescriptor>> coreComponentsMap = new HashMap<>();
private final Map<ComponentType, List<ComponentDescriptor>> coreComponentsMap = new HashMap<>();
private Map<ComponentType, List<ComponentDescriptor>> edgeComponentsMap = new HashMap<>();
private final Map<ComponentType, List<ComponentDescriptor>> edgeComponentsMap = new HashMap<>();
private boolean isInstall() {
return environment.acceptsProfiles(Profiles.of("install"));
@ -73,28 +80,62 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
@PostConstruct
public void init() {
for (var def : discoverBeansByAnnotationType(RuleNode.class)) {
String clazzName = def.getBeanClassName();
try {
var clazz = Class.forName(clazzName);
RuleNode annotation = clazz.getAnnotation(RuleNode.class);
boolean versioned = false;
if (annotation.version() > 0) { // No need to process nodes that has version = 0;
if (TbVersionedNode.class.isAssignableFrom(clazz)) {
versioned = true;
} else {
log.error("RuleNode [{}] has version {} but does not implement TbVersionedNode interface! Any update procedures for this rule node will be skipped!", clazzName, annotation.version());
}
}
ruleNodeClasses.put(clazzName, new RuleNodeClassInfo(clazz, annotation, versioned));
} catch (Exception e) {
log.warn("Failed to create instance of rule node type: {} due to: ", clazzName, e);
}
}
if (!isInstall()) {
discoverComponents();
}
}
private 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;
}
@Override
public Optional<RuleNodeClassInfo> getRuleNodeInfo(String clazz) {
return Optional.ofNullable(ruleNodeClasses.get(clazz));
}
@Override
public List<RuleNodeClassInfo> getVersionedNodes() {
return ruleNodeClasses.values().stream().filter(RuleNodeClassInfo::isVersioned).collect(Collectors.toList());
}
private void registerRuleNodeComponents() {
Set<BeanDefinition> ruleNodeBeanDefinitions = beanDiscoveryService.discoverBeansByAnnotationType(RuleNode.class);
for (BeanDefinition def : ruleNodeBeanDefinitions) {
for (RuleNodeClassInfo def : ruleNodeClasses.values()) {
int retryCount = 0;
Exception cause = null;
while (retryCount < MAX_OPTIMISITC_RETRIES) {
try {
String clazzName = def.getBeanClassName();
Class<?> clazz = Class.forName(clazzName);
RuleNode ruleNodeAnnotation = clazz.getAnnotation(RuleNode.class);
ComponentType type = ruleNodeAnnotation.type();
ComponentType type = def.getAnnotation().type();
ComponentDescriptor component = scanAndPersistComponent(def, type);
components.put(component.getClazz(), component);
putComponentIntoMaps(type, ruleNodeAnnotation, component);
putComponentIntoMaps(type, def.getAnnotation(), component);
break;
} catch (Exception e) {
log.trace("Can't initialize component {}, due to {}", def.getBeanClassName(), e.getMessage(), e);
log.trace("Can't initialize component {}, due to {}", def.getClassName(), e.getMessage(), e);
cause = e;
retryCount++;
try {
@ -105,7 +146,7 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
}
}
if (cause != null && retryCount == MAX_OPTIMISITC_RETRIES) {
log.error("Can't initialize component {}, due to {}", def.getBeanClassName(), cause.getMessage(), cause);
log.error("Can't initialize component {}, due to {}", def.getClassName(), cause.getMessage(), cause);
throw new RuntimeException(cause);
}
}
@ -142,18 +183,14 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
return false;
}
private ComponentDescriptor scanAndPersistComponent(BeanDefinition def, ComponentType type) {
private ComponentDescriptor scanAndPersistComponent(RuleNodeClassInfo def, ComponentType type) {
ComponentDescriptor scannedComponent = new ComponentDescriptor();
String clazzName = def.getBeanClassName();
String clazzName = def.getClassName();
try {
scannedComponent.setType(type);
Class<?> clazz = Class.forName(clazzName);
Class<?> clazz = def.getClazz();
RuleNode ruleNodeAnnotation = clazz.getAnnotation(RuleNode.class);
if (TbVersionedNode.class.isAssignableFrom(clazz)) {
TbVersionedNode tbVersionNode = (TbVersionedNode) clazz.getDeclaredConstructor().newInstance();
int currentVersion = tbVersionNode.getCurrentVersion();
scannedComponent.setConfigurationVersion(currentVersion);
}
scannedComponent.setConfigurationVersion(def.isVersioned() ? def.getCurrentVersion() : 0);
scannedComponent.setName(ruleNodeAnnotation.name());
scannedComponent.setScope(ruleNodeAnnotation.scope());
scannedComponent.setClusteringMode(ruleNodeAnnotation.clusteringMode());
@ -165,7 +202,7 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
scannedComponent.setClazz(clazzName);
log.debug("Processing scanned component: {}", scannedComponent);
} catch (Exception e) {
log.error("Can't initialize component {}, due to {}", def.getBeanClassName(), e.getMessage(), e);
log.error("Can't initialize component {}, due to {}", clazzName, e.getMessage(), e);
throw new RuntimeException(e);
}
ComponentDescriptor persistedComponent = componentDescriptorService.findByClazz(TenantId.SYS_TENANT_ID, clazzName);

6
application/src/main/java/org/thingsboard/server/service/component/ComponentDiscoveryService.java

@ -19,7 +19,9 @@ 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 java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
@ -30,6 +32,10 @@ public interface ComponentDiscoveryService {
void discoverComponents();
Optional<RuleNodeClassInfo> getRuleNodeInfo(String clazz);
List<RuleNodeClassInfo> getVersionedNodes();
List<ComponentDescriptor> getComponents(ComponentType type, RuleChainType ruleChainType);
List<ComponentDescriptor> getComponents(Set<ComponentType> types, RuleChainType ruleChainType);

25
application/src/main/java/org/thingsboard/server/service/bean/BeanDiscoveryService.java → application/src/main/java/org/thingsboard/server/service/component/RuleNodeClassInfo.java

@ -13,15 +13,28 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.bean;
package org.thingsboard.server.service.component;
import org.springframework.beans.factory.config.BeanDefinition;
import lombok.Data;
import org.thingsboard.rule.engine.api.RuleNode;
import java.lang.annotation.Annotation;
import java.util.Set;
@Data
public class RuleNodeClassInfo {
public interface BeanDiscoveryService {
private final Class<?> clazz;
private final RuleNode annotation;
private final boolean versioned;
Set<BeanDefinition> discoverBeansByAnnotationType(Class<? extends Annotation> annotationType);
public String getClassName(){
return clazz.getName();
}
public String getSimpleName() {
return clazz.getSimpleName();
}
public int getCurrentVersion() {
return annotation.version();
}
}

3
application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java

@ -48,6 +48,7 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.Optional;
import java.util.function.Function;
import static org.thingsboard.server.utils.LwM2mObjectModelUtils.toLwm2mResource;
@ -173,7 +174,7 @@ public class InstallScripts {
ruleChain = ruleChainService.saveRuleChain(ruleChain);
ruleChainMetaData.setRuleChainId(ruleChain.getId());
ruleChainService.saveRuleChainMetaData(TenantId.SYS_TENANT_ID, ruleChainMetaData);
ruleChainService.saveRuleChainMetaData(TenantId.SYS_TENANT_ID, ruleChainMetaData, Function.identity());
return ruleChain;
}

36
application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java

@ -81,7 +81,7 @@ 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.component.ComponentDiscoveryService;
import org.thingsboard.server.service.install.InstallScripts;
import org.thingsboard.server.service.install.SystemDataLoaderService;
@ -92,6 +92,7 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
import java.util.stream.Collectors;
import static org.thingsboard.server.common.data.StringUtils.isBlank;
@ -139,7 +140,7 @@ public class DefaultDataUpdateService implements DataUpdateService {
private QueueService queueService;
@Autowired
private BeanDiscoveryService beanDiscoveryService;
private ComponentDiscoveryService componentDiscoveryService;
@Autowired
private SystemDataLoaderService systemDataLoaderService;
@ -221,11 +222,12 @@ public class DefaultDataUpdateService implements DataUpdateService {
public void upgradeRuleNodes() {
try {
log.info("Lookup rule nodes to upgrade ...");
var nodeClassToVersionMap = getNodeClassToVersionMap();
var nodeClassToVersionMap = componentDiscoveryService.getVersionedNodes();
log.info("Found {} versioned nodes to check for upgrade!", nodeClassToVersionMap.size());
nodeClassToVersionMap.forEach((clazz, toVersion) -> {
var ruleNodeType = clazz.getName();
nodeClassToVersionMap.forEach(clazz -> {
var ruleNodeType = clazz.getClassName();
var ruleNodeTypeForLogs = clazz.getSimpleName();
var toVersion = clazz.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
@ -240,7 +242,7 @@ public class DefaultDataUpdateService implements DataUpdateService {
log.info("Going to upgrade rule node with id: {} type: {} fromVersion: {} toVersion: {}",
ruleNodeId, ruleNodeTypeForLogs, fromVersion, toVersion);
try {
var tbVersionedNode = (TbVersionedNode) clazz.getDeclaredConstructor().newInstance();
var tbVersionedNode = (TbVersionedNode) clazz.getClazz().getDeclaredConstructor().newInstance();
TbPair<Boolean, JsonNode> upgradeRuleNodeConfigurationResult = tbVersionedNode.upgrade(fromVersion, oldConfiguration);
if (upgradeRuleNodeConfigurationResult.getFirst()) {
ruleNode.setConfiguration(upgradeRuleNodeConfigurationResult.getSecond());
@ -262,26 +264,6 @@ public class DefaultDataUpdateService implements DataUpdateService {
}
}
private Map<Class<?>, Integer> getNodeClassToVersionMap() {
var ruleNodeDefinitions = beanDiscoveryService.discoverBeansByAnnotationType(
org.thingsboard.rule.engine.api.RuleNode.class
);
var tbVersionedNodes = new HashMap<Class<?>, Integer>();
for (var def : ruleNodeDefinitions) {
String clazzName = def.getBeanClassName();
try {
var clazz = Class.forName(clazzName);
if (TbVersionedNode.class.isAssignableFrom(clazz)) {
TbVersionedNode tbVersionedNode = (TbVersionedNode) clazz.getDeclaredConstructor().newInstance();
tbVersionedNodes.put(clazz, tbVersionedNode.getCurrentVersion());
}
} catch (Exception e) {
log.warn("Failed to create instance of rule node type: {} due to: ", clazzName, e);
}
}
return tbVersionedNodes;
}
private final PaginatedUpdater<String, DeviceProfileEntity> deviceProfileEntityDynamicConditionsUpdater =
new PaginatedUpdater<>() {
@ -516,7 +498,7 @@ public class DefaultDataUpdateService implements DataUpdateService {
md.getNodes().add(ruleNode);
md.setFirstNodeIndex(newIdx);
md.addConnectionInfo(newIdx, oldIdx, "Success");
ruleChainService.saveRuleChainMetaData(tenant.getId(), md);
ruleChainService.saveRuleChainMetaData(tenant.getId(), md, Function.identity());
}
} catch (Exception e) {
log.error("[{}] Unable to update Tenant: {}", tenant.getId(), tenant.getName(), e);

50
application/src/main/java/org/thingsboard/server/service/rule/DefaultTbRuleChainService.java

@ -15,10 +15,13 @@
*/
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.TbNodeException;
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.flow.TbRuleChainOutputNode;
@ -42,12 +45,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.service.sync.vc.EntitiesVersionControlService;
import java.util.ArrayList;
import java.util.Collections;
@ -70,8 +74,7 @@ public class DefaultTbRuleChainService extends AbstractTbEntityService implement
private final RuleChainService ruleChainService;
private final RelationService relationService;
private final InstallScripts installScripts;
private final EntitiesVersionControlService vcService;
private final ComponentDiscoveryService componentDiscoveryService;
@Override
public Set<String> getRuleChainOutputLabels(TenantId tenantId, RuleChainId ruleChainId) {
@ -277,7 +280,7 @@ public class DefaultTbRuleChainService extends AbstractTbEntityService implement
RuleChainId ruleChainId = ruleChain.getId();
RuleChainId ruleChainMetaDataId = ruleChainMetaData.getRuleChainId();
try {
RuleChainUpdateResult result = ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData);
RuleChainUpdateResult result = ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData, this::updateRuleNodeConfiguration);
checkNotNull(result.isSuccess() ? true : null);
List<RuleChain> updatedRuleChains;
@ -404,6 +407,45 @@ public class DefaultTbRuleChainService extends AbstractTbEntityService implement
}
}
@Override
public RuleNode updateRuleNodeConfiguration(RuleNode node) {
var ruleChainId = node.getRuleChainId();
var ruleNodeId = node.getId();
var ruleNodeType = node.getType();
try {
var ruleNodeClass = componentDiscoveryService.getRuleNodeInfo(ruleNodeType)
.orElseThrow(() -> new RuntimeException("Rule node " + ruleNodeType + " is not supported!"));
if (ruleNodeClass.isVersioned()) {
TbVersionedNode tbVersionedNode = (TbVersionedNode) 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<Boolean, JsonNode> upgradeResult = tbVersionedNode.upgrade(fromVersion, node.getConfiguration());
if (upgradeResult.getFirst()) {
node.setConfiguration(upgradeResult.getSecond());
}
node.setConfigurationVersion(toVersion);
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);
}
} 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: {}",
ruleNodeId, ruleNodeType, ruleChainId, e);
}
return node;
}
private Set<RuleChainId> updateRelatedRuleChains(TenantId tenantId, RuleChainId ruleChainId, Map<String, String> labelsMap) {
Set<RuleChainId> updatedRuleChains = new HashSet<>();
List<RuleChainOutputLabelsUsage> usageList = getOutputLabelUsage(tenantId, ruleChainId);

3
application/src/main/java/org/thingsboard/server/service/rule/TbRuleChainService.java

@ -25,6 +25,7 @@ import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.rule.RuleChainOutputLabelsUsage;
import org.thingsboard.server.common.data.rule.RuleChainUpdateResult;
import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.server.service.entitiy.SimpleTbEntityService;
import java.util.List;
@ -54,4 +55,6 @@ public interface TbRuleChainService extends SimpleTbEntityService<RuleChain> {
RuleChain setAutoAssignToEdgeRuleChain(TenantId tenantId, RuleChain ruleChain, User user) throws ThingsboardException;
RuleChain unsetAutoAssignToEdgeRuleChain(TenantId tenantId, RuleChain ruleChain, User user) throws ThingsboardException;
RuleNode updateRuleNodeConfiguration(RuleNode ruleNode);
}

4
application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/RuleChainImportService.java

@ -35,6 +35,7 @@ import org.thingsboard.server.common.data.sync.ie.RuleChainExportData;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.rule.RuleNodeDao;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.rule.TbRuleChainService;
import org.thingsboard.server.service.sync.vc.data.EntitiesImportCtx;
import java.util.Arrays;
@ -52,6 +53,7 @@ public class RuleChainImportService extends BaseEntityImportService<RuleChainId,
private static final LinkedHashSet<EntityType> HINTS = new LinkedHashSet<>(Arrays.asList(EntityType.RULE_CHAIN, EntityType.DEVICE, EntityType.ASSET));
private final TbRuleChainService tbRuleChainService;
private final RuleChainService ruleChainService;
private final RuleNodeDao ruleNodeDao;
@ -106,7 +108,7 @@ public class RuleChainImportService extends BaseEntityImportService<RuleChainId,
ruleChain = ruleChainService.saveRuleChain(ruleChain);
if (ctx.isFinalImportAttempt() || ctx.getCurrentImportResult().isUpdatedAllExternalIds()) {
exportData.getMetaData().setRuleChainId(ruleChain.getId());
ruleChainService.saveRuleChainMetaData(ctx.getTenantId(), exportData.getMetaData());
ruleChainService.saveRuleChainMetaData(ctx.getTenantId(), exportData.getMetaData(), tbRuleChainService::updateRuleNodeConfiguration);
return ruleChainService.findRuleChainById(ctx.getTenantId(), ruleChain.getId());
} else {
return ruleChain;

12
application/src/test/java/org/thingsboard/server/controller/RuleChainControllerTest.java

@ -21,6 +21,7 @@ import org.junit.Assert;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.jupiter.api.Assertions;
import org.mockito.AdditionalAnswers;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
@ -144,16 +145,17 @@ public class RuleChainControllerTest extends AbstractControllerTest {
Assert.assertTrue(savedRuleChain.getCreatedTime() > 0);
Assert.assertEquals(ruleChain.getName(), savedRuleChain.getName());
TbVersionedNode tbVersionedNode = new TbGetRelatedAttributeNode();
String ruleNodeType = tbVersionedNode.getClass().getName();
int currentVersion = tbVersionedNode.getCurrentVersion();
var annotation = TbGetRelatedAttributeNode.class.getAnnotation(org.thingsboard.rule.engine.api.RuleNode.class);
String ruleNodeType = TbGetRelatedAttributeNode.class.getName();
int currentVersion = annotation.version();
String oldConfig = "{\"attrMapping\":{\"serialNumber\":\"sn\"}," +
"\"relationsQuery\":{\"direction\":\"FROM\",\"maxLevel\":1," +
"\"filters\":[{\"relationType\":\"Contains\",\"entityTypes\":[]}]," +
"\"fetchLastLevelOnly\":false},\"telemetry\":false}";
String newConfig = JacksonUtil.toString(new TbGetRelatedDataNodeConfiguration().defaultConfiguration());
TbGetRelatedDataNodeConfiguration defaultConfiguration = new TbGetRelatedDataNodeConfiguration().defaultConfiguration();
String newConfig = JacksonUtil.toString(defaultConfiguration);
var ruleChainMetaData = createRuleChainMetadataWithTbVersionedNodes(
ruleChainId,
@ -170,7 +172,7 @@ public class RuleChainControllerTest extends AbstractControllerTest {
for (RuleNode ruleNode : savedRuleChainMetaData.getNodes()) {
Assert.assertNotNull(ruleNode.getId());
Assert.assertEquals(currentVersion, ruleNode.getConfigurationVersion());
Assert.assertEquals(JacksonUtil.toJsonNode(newConfig), ruleNode.getConfiguration());
Assert.assertEquals(defaultConfiguration, JacksonUtil.treeToValue(ruleNode.getConfiguration(), defaultConfiguration.getClass()));
}
}

5
application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java

@ -28,6 +28,7 @@ import org.springframework.test.util.ReflectionTestUtils;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.flow.TbRuleChainInputNodeConfiguration;
import org.thingsboard.rule.engine.metadata.FetchTo;
import org.thingsboard.rule.engine.metadata.TbGetAttributesNode;
import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.common.data.DataConstants;
@ -138,6 +139,7 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule
RuleNode ruleNode1 = new RuleNode();
ruleNode1.setName("Simple Rule Node 1");
ruleNode1.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName());
ruleNode1.setConfigurationVersion(TbGetAttributesNode.class.getAnnotation(org.thingsboard.rule.engine.api.RuleNode.class).version());
ruleNode1.setDebugMode(true);
TbGetAttributesNodeConfiguration configuration1 = new TbGetAttributesNodeConfiguration();
configuration1.setFetchTo(FetchTo.METADATA);
@ -147,6 +149,7 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule
RuleNode ruleNode2 = new RuleNode();
ruleNode2.setName("Simple Rule Node 2");
ruleNode2.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName());
ruleNode2.setConfigurationVersion(TbGetAttributesNode.class.getAnnotation(org.thingsboard.rule.engine.api.RuleNode.class).version());
ruleNode2.setDebugMode(true);
TbGetAttributesNodeConfiguration configuration2 = new TbGetAttributesNodeConfiguration();
configuration2.setFetchTo(FetchTo.METADATA);
@ -242,6 +245,7 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule
RuleNode ruleNode1 = new RuleNode();
ruleNode1.setName("Simple Rule Node 1");
ruleNode1.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName());
ruleNode1.setConfigurationVersion(TbGetAttributesNode.class.getAnnotation(org.thingsboard.rule.engine.api.RuleNode.class).version());
ruleNode1.setDebugMode(true);
TbGetAttributesNodeConfiguration configuration1 = new TbGetAttributesNodeConfiguration();
configuration1.setFetchTo(FetchTo.METADATA);
@ -275,6 +279,7 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule
RuleNode ruleNode2 = new RuleNode();
ruleNode2.setName("Simple Rule Node 2");
ruleNode2.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName());
ruleNode2.setConfigurationVersion(TbGetAttributesNode.class.getAnnotation(org.thingsboard.rule.engine.api.RuleNode.class).version());
ruleNode2.setDebugMode(true);
TbGetAttributesNodeConfiguration configuration2 = new TbGetAttributesNodeConfiguration();
configuration2.setFetchTo(FetchTo.METADATA);

2
application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java

@ -25,6 +25,7 @@ import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.metadata.FetchTo;
import org.thingsboard.rule.engine.metadata.TbGetAttributesNode;
import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.common.data.DataConstants;
@ -93,6 +94,7 @@ public abstract class AbstractRuleEngineLifecycleIntegrationTest extends Abstrac
RuleNode ruleNode = new RuleNode();
ruleNode.setName("Simple Rule Node");
ruleNode.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName());
ruleNode.setConfigurationVersion(TbGetAttributesNode.class.getAnnotation(org.thingsboard.rule.engine.api.RuleNode.class).version());
ruleNode.setDebugMode(true);
TbGetAttributesNodeConfiguration configuration = new TbGetAttributesNodeConfiguration();
configuration.setFetchTo(FetchTo.METADATA);

9
application/src/test/java/org/thingsboard/server/service/sync/ie/BaseExportImportServiceTest.java

@ -23,6 +23,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.debug.TbMsgGeneratorNode;
import org.thingsboard.rule.engine.debug.TbMsgGeneratorNodeConfiguration;
import org.thingsboard.rule.engine.metadata.TbGetAttributesNode;
import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Dashboard;
@ -86,6 +87,7 @@ import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Collections;
import java.util.UUID;
import java.util.function.Function;
import static org.assertj.core.api.Assertions.assertThat;
@ -333,6 +335,7 @@ public abstract class BaseExportImportServiceTest extends AbstractControllerTest
RuleNode ruleNode2 = new RuleNode();
ruleNode2.setName("Simple Rule Node 2");
ruleNode2.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName());
ruleNode2.setConfigurationVersion(TbGetAttributesNode.class.getAnnotation(org.thingsboard.rule.engine.api.RuleNode.class).version());
ruleNode2.setDebugMode(true);
TbGetAttributesNodeConfiguration configuration2 = new TbGetAttributesNodeConfiguration();
configuration2.setServerAttributeNames(Collections.singletonList("serverAttributeKey2"));
@ -341,7 +344,7 @@ public abstract class BaseExportImportServiceTest extends AbstractControllerTest
metaData.setNodes(Arrays.asList(ruleNode1, ruleNode2));
metaData.setFirstNodeIndex(0);
metaData.addConnectionInfo(0, 1, "Success");
ruleChainService.saveRuleChainMetaData(tenantId, metaData);
ruleChainService.saveRuleChainMetaData(tenantId, metaData, Function.identity());
return ruleChainService.findRuleChainById(tenantId, ruleChain.getId());
}
@ -361,6 +364,7 @@ public abstract class BaseExportImportServiceTest extends AbstractControllerTest
RuleNode ruleNode1 = new RuleNode();
ruleNode1.setName("Simple Rule Node 1");
ruleNode1.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName());
ruleNode1.setConfigurationVersion(TbGetAttributesNode.class.getAnnotation(org.thingsboard.rule.engine.api.RuleNode.class).version());
ruleNode1.setDebugMode(true);
TbGetAttributesNodeConfiguration configuration1 = new TbGetAttributesNodeConfiguration();
configuration1.setServerAttributeNames(Collections.singletonList("serverAttributeKey1"));
@ -369,6 +373,7 @@ public abstract class BaseExportImportServiceTest extends AbstractControllerTest
RuleNode ruleNode2 = new RuleNode();
ruleNode2.setName("Simple Rule Node 2");
ruleNode2.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName());
ruleNode2.setConfigurationVersion(TbGetAttributesNode.class.getAnnotation(org.thingsboard.rule.engine.api.RuleNode.class).version());
ruleNode2.setDebugMode(true);
TbGetAttributesNodeConfiguration configuration2 = new TbGetAttributesNodeConfiguration();
configuration2.setServerAttributeNames(Collections.singletonList("serverAttributeKey2"));
@ -377,7 +382,7 @@ public abstract class BaseExportImportServiceTest extends AbstractControllerTest
metaData.setNodes(Arrays.asList(ruleNode1, ruleNode2));
metaData.setFirstNodeIndex(0);
metaData.addConnectionInfo(0, 1, "Success");
ruleChainService.saveRuleChainMetaData(tenantId, metaData);
ruleChainService.saveRuleChainMetaData(tenantId, metaData, Function.identity());
return ruleChainService.findRuleChainById(tenantId, ruleChain.getId());
}

5
common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java

@ -35,6 +35,7 @@ import org.thingsboard.server.dao.entity.EntityDaoService;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
/**
* Created by igor on 3/12/18.
@ -45,7 +46,7 @@ public interface RuleChainService extends EntityDaoService {
boolean setRootRuleChain(TenantId tenantId, RuleChainId ruleChainId);
RuleChainUpdateResult saveRuleChainMetaData(TenantId tenantId, RuleChainMetaData ruleChainMetaData);
RuleChainUpdateResult saveRuleChainMetaData(TenantId tenantId, RuleChainMetaData ruleChainMetaData, Function<RuleNode, RuleNode> ruleNodeUpdater);
RuleChainMetaData loadRuleChainMetaData(TenantId tenantId, RuleChainId ruleChainId);
@ -75,7 +76,7 @@ public interface RuleChainService extends EntityDaoService {
RuleChainData exportTenantRuleChains(TenantId tenantId, PageLink pageLink) throws ThingsboardException;
List<RuleChainImportResult> importTenantRuleChains(TenantId tenantId, RuleChainData ruleChainData, boolean overwrite);
List<RuleChainImportResult> importTenantRuleChains(TenantId tenantId, RuleChainData ruleChainData, boolean overwrite, Function<RuleNode, RuleNode> ruleNodeUpdater);
RuleChain assignRuleChainToEdge(TenantId tenantId, RuleChainId ruleChainId, EdgeId edgeId);

40
dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java

@ -73,6 +73,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
import static org.thingsboard.server.common.data.DataConstants.TENANT;
@ -145,7 +146,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
}
@Override
public RuleChainUpdateResult saveRuleChainMetaData(TenantId tenantId, RuleChainMetaData ruleChainMetaData) {
public RuleChainUpdateResult saveRuleChainMetaData(TenantId tenantId, RuleChainMetaData ruleChainMetaData, Function<RuleNode, RuleNode> ruleNodeUpdater) {
Validator.validateId(ruleChainMetaData.getRuleChainId(), "Incorrect rule chain id.");
RuleChain ruleChain = findRuleChainById(tenantId, ruleChainMetaData.getRuleChainId());
if (ruleChain == null) {
@ -189,38 +190,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
if (nodes != null) {
for (RuleNode node : toAddOrUpdate) {
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());
}
node.setConfigurationVersion(toVersion);
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);
}
} else {
log.debug("Rule node with id: {} type: {} ruleChainId: {} already set to latest version!",
ruleNodeId, ruleChainId, ruleNodeType);
}
}
} catch (Exception e) {
log.error("Failed to create instance of rule node with id: {} type: {}, rule chain id: {}",
ruleNodeId, ruleNodeType, ruleChainId);
}
node = ruleNodeUpdater.apply(node);
RuleNode savedNode = ruleNodeDao.save(tenantId, node);
relations.add(new EntityRelation(ruleChainMetaData.getRuleChainId(), savedNode.getId(),
EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN));
@ -484,7 +454,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
}
@Override
public List<RuleChainImportResult> importTenantRuleChains(TenantId tenantId, RuleChainData ruleChainData, boolean overwrite) {
public List<RuleChainImportResult> importTenantRuleChains(TenantId tenantId, RuleChainData ruleChainData, boolean overwrite, Function<RuleNode, RuleNode> ruleNodeUpdater) {
List<RuleChainImportResult> importResults = new ArrayList<>();
setRandomRuleChainIds(ruleChainData);
@ -521,7 +491,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
}
if (CollectionUtils.isNotEmpty(ruleChainData.getMetadata())) {
ruleChainData.getMetadata().forEach(md -> saveRuleChainMetaData(tenantId, md));
ruleChainData.getMetadata().forEach(md -> saveRuleChainMetaData(tenantId, md, ruleNodeUpdater));
}
return importResults;

3
dao/src/test/java/org/thingsboard/server/dao/service/EdgeServiceTest.java

@ -44,6 +44,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
@ -639,7 +640,7 @@ public class EdgeServiceTest extends AbstractServiceTest {
ruleChainMetaData3.setNodes(Arrays.asList(ruleNode1, ruleNode2));
ruleChainMetaData3.setFirstNodeIndex(0);
ruleChainMetaData3.setRuleChainId(ruleChain3.getId());
ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData3);
ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData3, Function.identity());
ruleChainService.assignRuleChainToEdge(tenantId, ruleChain3.getId(), savedEdge.getId());

9
dao/src/test/java/org/thingsboard/server/dao/service/RuleChainServiceTest.java

@ -40,6 +40,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
/**
* Created by igor on 3/13/18.
@ -280,7 +281,7 @@ public class RuleChainServiceTest extends AbstractServiceTest {
ruleNodes.set(name3Index, ruleNode4);
Assert.assertTrue(ruleChainService.saveRuleChainMetaData(tenantId, savedRuleChainMetaData).isSuccess());
Assert.assertTrue(ruleChainService.saveRuleChainMetaData(tenantId, savedRuleChainMetaData, Function.identity()).isSuccess());
RuleChainMetaData updatedRuleChainMetaData = ruleChainService.loadRuleChainMetaData(tenantId, savedRuleChainMetaData.getRuleChainId());
Assert.assertEquals(3, updatedRuleChainMetaData.getNodes().size());
@ -311,14 +312,14 @@ public class RuleChainServiceTest extends AbstractServiceTest {
@Test
public void testUpdateRuleChainMetaDataWithCirclingRelation() {
Assertions.assertThrows(DataValidationException.class, () -> {
ruleChainService.saveRuleChainMetaData(tenantId, createRuleChainMetadataWithCirclingRelation());
ruleChainService.saveRuleChainMetaData(tenantId, createRuleChainMetadataWithCirclingRelation(), Function.identity());
});
}
@Test
public void testUpdateRuleChainMetaDataWithCirclingRelation2() {
Assertions.assertThrows(DataValidationException.class, () -> {
ruleChainService.saveRuleChainMetaData(tenantId, createRuleChainMetadataWithCirclingRelation2());
ruleChainService.saveRuleChainMetaData(tenantId, createRuleChainMetadataWithCirclingRelation2(), Function.identity());
});
}
@ -395,7 +396,7 @@ public class RuleChainServiceTest extends AbstractServiceTest {
ruleChainMetaData.addConnectionInfo(0,2,"fail");
ruleChainMetaData.addConnectionInfo(1,2,"success");
Assert.assertTrue(ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData).isSuccess());
Assert.assertTrue(ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData, Function.identity()).isSuccess());
return ruleChainService.loadRuleChainMetaData(tenantId, ruleChainMetaData.getRuleChainId());
}

2
rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleNode.java

@ -65,4 +65,6 @@ public @interface RuleNode {
RuleChainType[] ruleChainTypes() default {RuleChainType.CORE, RuleChainType.EDGE};
int version() default 0;
}

2
rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbVersionedNode.java

@ -22,6 +22,4 @@ public interface TbVersionedNode extends TbNode {
TbPair<Boolean, JsonNode> upgrade(int fromVersion, JsonNode oldConfiguration) throws TbNodeException;
int getCurrentVersion();
}

5
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractNodeWithFetchTo.java

@ -41,11 +41,6 @@ public abstract class TbAbstractNodeWithFetchTo<C extends TbAbstractFetchToNodeC
protected C config;
protected FetchTo fetchTo;
@Override
public int getCurrentVersion() {
return 1;
}
@Override
public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
config = loadNodeConfiguration(configuration);

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbFetchDeviceCredentialsNode.java

@ -36,6 +36,7 @@ import java.util.concurrent.ExecutionException;
@RuleNode(
type = ComponentType.ENRICHMENT,
name = "fetch device credentials",
version = 1,
configClazz = TbFetchDeviceCredentialsNodeConfiguration.class,
nodeDescription = "Adds device credentials to the message or message metadata",
nodeDetails = "if message originator type is Device and device credentials was successfully fetched, " +

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNode.java

@ -36,6 +36,7 @@ import org.thingsboard.server.common.msg.TbMsg;
@RuleNode(type = ComponentType.ENRICHMENT,
name = "originator attributes",
configClazz = TbGetAttributesNodeConfiguration.class,
version = 1,
nodeDescription = "Adds attributes and/or latest timeseries data for the message originator to the message or message metadata",
nodeDetails = "Useful when you need to retrieve some attributes or the latest telemetry readings from the message originator " +
"that are not included in the incoming message to use them for further message processing. " +

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNode.java

@ -35,6 +35,7 @@ import org.thingsboard.server.common.data.util.TbPair;
type = ComponentType.ENRICHMENT,
name = "customer attributes",
configClazz = TbGetEntityDataNodeConfiguration.class,
version = 1,
nodeDescription = "Adds message originator customer attributes or latest telemetry into message or message metadata",
nodeDetails = "Useful in multi-customer solutions where each customer has a different configuration or threshold set " +
"that is stored as customer attributes or telemetry data and used for dynamic message filtering, transformation, " +

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNode.java

@ -44,6 +44,7 @@ import java.util.NoSuchElementException;
@RuleNode(type = ComponentType.ENRICHMENT,
name = "customer details",
configClazz = TbGetCustomerDetailsNodeConfiguration.class,
version = 1,
nodeDescription = "Adds message originator customer details into message or message metadata",
nodeDetails = "Useful in multi-customer solutions where we need dynamically use customer contact information " +
"such as email, phone, address, etc., for notifications via email, SMS, and other notification providers.",

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNode.java

@ -34,6 +34,7 @@ import org.thingsboard.server.common.msg.TbMsg;
@RuleNode(type = ComponentType.ENRICHMENT,
name = "related device attributes",
configClazz = TbGetDeviceAttrNodeConfiguration.class,
version = 1,
nodeDescription = "Add originators related device attributes and/or latest telemetry values into message or message metadata",
nodeDetails = "Related device lookup based on the configured relation query. " +
"If multiple related devices are found, only first device is used for message enrichment, other entities are discarded. " +

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNode.java

@ -37,6 +37,7 @@ import java.util.concurrent.ExecutionException;
@RuleNode(type = ComponentType.ENRICHMENT,
name = "originator fields",
configClazz = TbGetOriginatorFieldsConfiguration.class,
version = 1,
nodeDescription = "Adds message originator fields values into message or message metadata",
nodeDetails = "Fetches fields values specified in the mapping. If specified field is not part of originator fields it will be ignored. " +
"Useful when you need to retrieve originator fields and use them for further message processing.",

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNode.java

@ -36,6 +36,7 @@ import java.util.Arrays;
type = ComponentType.ENRICHMENT,
name = "related entity data",
configClazz = TbGetRelatedDataNodeConfiguration.class,
version = 1,
nodeDescription = "Adds originators related entity attributes or latest telemetry or fields into message or message metadata",
nodeDetails = "Related entity lookup based on the configured relation query. " +
"If multiple related entities are found, only first entity is used for message enrichment, other entities are discarded. " +

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNode.java

@ -34,6 +34,7 @@ import org.thingsboard.server.common.data.util.TbPair;
type = ComponentType.ENRICHMENT,
name = "tenant attributes",
configClazz = TbGetEntityDataNodeConfiguration.class,
version = 1,
nodeDescription = "Adds message originator tenant attributes or latest telemetry into message or message metadata",
nodeDetails = "Useful when you need to retrieve some common configuration or threshold set " +
"that is stored as tenant attributes or telemetry data and use it for further message processing.",

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNode.java

@ -33,6 +33,7 @@ import org.thingsboard.server.common.msg.TbMsg;
@RuleNode(type = ComponentType.ENRICHMENT,
name = "tenant details",
configClazz = TbGetTenantDetailsNodeConfiguration.class,
version = 1,
nodeDescription = "Adds message originator tenant details into message or message metadata",
nodeDetails = "Useful when we need to retrieve contact information from your tenant " +
"such as email, phone, address, etc., for notifications via email, SMS, and other notification providers.",

Loading…
Cancel
Save