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 @Slf4j
@SpringBootConfiguration @SpringBootConfiguration
@ComponentScan({"org.thingsboard.server.install", @ComponentScan({"org.thingsboard.server.install",
"org.thingsboard.server.service.bean",
"org.thingsboard.server.service.component", "org.thingsboard.server.service.component",
"org.thingsboard.server.service.install", "org.thingsboard.server.service.install",
"org.thingsboard.server.service.security.auth.jwt.settings", "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.") @ApiParam(value = "Enables overwrite for existing rule chains with the same name.")
@RequestParam(required = false, defaultValue = "false") boolean overwrite) throws ThingsboardException { @RequestParam(required = false, defaultValue = "false") boolean overwrite) throws ThingsboardException {
TenantId tenantId = getCurrentUser().getTenantId(); 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) { for (RuleChainImportResult importResult : importResults) {
if (importResult.getError() == null) { if (importResult.getError() == null) {
tbClusterService.broadcastEntityStateChangeEvent(importResult.getTenantId(), importResult.getRuleChainId(), 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 com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.core.env.Profiles; import org.springframework.core.env.Profiles;
import org.springframework.core.type.filter.AnnotationTypeFilter;
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.NodeConfiguration; 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.plugin.ComponentType;
import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.dao.component.ComponentDescriptorService; import org.thingsboard.server.dao.component.ComponentDescriptorService;
import org.thingsboard.server.service.bean.BeanDiscoveryService;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import java.lang.annotation.Annotation;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
@Service @Service
@Slf4j @Slf4j
@ -52,20 +57,22 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
public static final int MAX_OPTIMISITC_RETRIES = 3; public static final int MAX_OPTIMISITC_RETRIES = 3;
@Value("${plugins.scan_packages}")
private String[] scanPackages;
@Autowired @Autowired
private Environment environment; private Environment environment;
@Autowired(required = false)
private BeanDiscoveryService beanDiscoveryService;
@Autowired @Autowired
private ComponentDescriptorService componentDescriptorService; 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() { private boolean isInstall() {
return environment.acceptsProfiles(Profiles.of("install")); return environment.acceptsProfiles(Profiles.of("install"));
@ -73,28 +80,62 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
@PostConstruct @PostConstruct
public void init() { 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()) { if (!isInstall()) {
discoverComponents(); 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() { private void registerRuleNodeComponents() {
Set<BeanDefinition> ruleNodeBeanDefinitions = beanDiscoveryService.discoverBeansByAnnotationType(RuleNode.class); for (RuleNodeClassInfo def : ruleNodeClasses.values()) {
for (BeanDefinition def : ruleNodeBeanDefinitions) {
int retryCount = 0; int retryCount = 0;
Exception cause = null; Exception cause = null;
while (retryCount < MAX_OPTIMISITC_RETRIES) { while (retryCount < MAX_OPTIMISITC_RETRIES) {
try { try {
String clazzName = def.getBeanClassName(); ComponentType type = def.getAnnotation().type();
Class<?> clazz = Class.forName(clazzName);
RuleNode ruleNodeAnnotation = clazz.getAnnotation(RuleNode.class);
ComponentType type = ruleNodeAnnotation.type();
ComponentDescriptor component = scanAndPersistComponent(def, type); ComponentDescriptor component = scanAndPersistComponent(def, type);
components.put(component.getClazz(), component); components.put(component.getClazz(), component);
putComponentIntoMaps(type, ruleNodeAnnotation, component); putComponentIntoMaps(type, def.getAnnotation(), component);
break; break;
} catch (Exception e) { } 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; cause = e;
retryCount++; retryCount++;
try { try {
@ -105,7 +146,7 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
} }
} }
if (cause != null && retryCount == MAX_OPTIMISITC_RETRIES) { 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); throw new RuntimeException(cause);
} }
} }
@ -142,18 +183,14 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
return false; return false;
} }
private ComponentDescriptor scanAndPersistComponent(BeanDefinition def, ComponentType type) { private ComponentDescriptor scanAndPersistComponent(RuleNodeClassInfo def, ComponentType type) {
ComponentDescriptor scannedComponent = new ComponentDescriptor(); ComponentDescriptor scannedComponent = new ComponentDescriptor();
String clazzName = def.getBeanClassName(); String clazzName = def.getClassName();
try { try {
scannedComponent.setType(type); scannedComponent.setType(type);
Class<?> clazz = Class.forName(clazzName); Class<?> clazz = def.getClazz();
RuleNode ruleNodeAnnotation = clazz.getAnnotation(RuleNode.class); RuleNode ruleNodeAnnotation = clazz.getAnnotation(RuleNode.class);
if (TbVersionedNode.class.isAssignableFrom(clazz)) { scannedComponent.setConfigurationVersion(def.isVersioned() ? def.getCurrentVersion() : 0);
TbVersionedNode tbVersionNode = (TbVersionedNode) clazz.getDeclaredConstructor().newInstance();
int currentVersion = tbVersionNode.getCurrentVersion();
scannedComponent.setConfigurationVersion(currentVersion);
}
scannedComponent.setName(ruleNodeAnnotation.name()); scannedComponent.setName(ruleNodeAnnotation.name());
scannedComponent.setScope(ruleNodeAnnotation.scope()); scannedComponent.setScope(ruleNodeAnnotation.scope());
scannedComponent.setClusteringMode(ruleNodeAnnotation.clusteringMode()); scannedComponent.setClusteringMode(ruleNodeAnnotation.clusteringMode());
@ -165,7 +202,7 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
scannedComponent.setClazz(clazzName); scannedComponent.setClazz(clazzName);
log.debug("Processing scanned component: {}", scannedComponent); log.debug("Processing scanned component: {}", scannedComponent);
} catch (Exception e) { } 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); throw new RuntimeException(e);
} }
ComponentDescriptor persistedComponent = componentDescriptorService.findByClazz(TenantId.SYS_TENANT_ID, clazzName); 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.plugin.ComponentType;
import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.data.rule.RuleChainType;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
@ -30,6 +32,10 @@ public interface ComponentDiscoveryService {
void discoverComponents(); void discoverComponents();
Optional<RuleNodeClassInfo> getRuleNodeInfo(String clazz);
List<RuleNodeClassInfo> getVersionedNodes();
List<ComponentDescriptor> getComponents(ComponentType type, RuleChainType ruleChainType); List<ComponentDescriptor> getComponents(ComponentType type, RuleChainType ruleChainType);
List<ComponentDescriptor> getComponents(Set<ComponentType> types, 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 * See the License for the specific language governing permissions and
* limitations under the License. * 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; @Data
import java.util.Set; 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.nio.file.Paths;
import java.util.Base64; import java.util.Base64;
import java.util.Optional; import java.util.Optional;
import java.util.function.Function;
import static org.thingsboard.server.utils.LwM2mObjectModelUtils.toLwm2mResource; import static org.thingsboard.server.utils.LwM2mObjectModelUtils.toLwm2mResource;
@ -173,7 +174,7 @@ public class InstallScripts {
ruleChain = ruleChainService.saveRuleChain(ruleChain); ruleChain = ruleChainService.saveRuleChain(ruleChain);
ruleChainMetaData.setRuleChainId(ruleChain.getId()); ruleChainMetaData.setRuleChainId(ruleChain.getId());
ruleChainService.saveRuleChainMetaData(TenantId.SYS_TENANT_ID, ruleChainMetaData); ruleChainService.saveRuleChainMetaData(TenantId.SYS_TENANT_ID, ruleChainMetaData, Function.identity());
return ruleChain; 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.TenantProfileService;
import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.timeseries.TimeseriesService; 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.InstallScripts;
import org.thingsboard.server.service.install.SystemDataLoaderService; import org.thingsboard.server.service.install.SystemDataLoaderService;
@ -92,6 +92,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.thingsboard.server.common.data.StringUtils.isBlank; import static org.thingsboard.server.common.data.StringUtils.isBlank;
@ -139,7 +140,7 @@ public class DefaultDataUpdateService implements DataUpdateService {
private QueueService queueService; private QueueService queueService;
@Autowired @Autowired
private BeanDiscoveryService beanDiscoveryService; private ComponentDiscoveryService componentDiscoveryService;
@Autowired @Autowired
private SystemDataLoaderService systemDataLoaderService; private SystemDataLoaderService systemDataLoaderService;
@ -221,11 +222,12 @@ public class DefaultDataUpdateService implements DataUpdateService {
public void upgradeRuleNodes() { public void upgradeRuleNodes() {
try { try {
log.info("Lookup rule nodes to upgrade ..."); 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()); log.info("Found {} versioned nodes to check for upgrade!", nodeClassToVersionMap.size());
nodeClassToVersionMap.forEach((clazz, toVersion) -> { nodeClassToVersionMap.forEach(clazz -> {
var ruleNodeType = clazz.getName(); var ruleNodeType = clazz.getClassName();
var ruleNodeTypeForLogs = clazz.getSimpleName(); var ruleNodeTypeForLogs = clazz.getSimpleName();
var toVersion = clazz.getCurrentVersion();
log.info("Going to check for nodes with type: {} to upgrade to version: {}.", ruleNodeTypeForLogs, toVersion); log.info("Going to check for nodes with type: {} to upgrade to version: {}.", ruleNodeTypeForLogs, toVersion);
var ruleNodesToUpdate = new PageDataIterable<>( var ruleNodesToUpdate = new PageDataIterable<>(
pageLink -> ruleChainService.findAllRuleNodesByTypeAndVersionLessThan(ruleNodeType, toVersion, pageLink), 1024 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: {}", log.info("Going to upgrade rule node with id: {} type: {} fromVersion: {} toVersion: {}",
ruleNodeId, ruleNodeTypeForLogs, fromVersion, toVersion); ruleNodeId, ruleNodeTypeForLogs, fromVersion, toVersion);
try { try {
var tbVersionedNode = (TbVersionedNode) clazz.getDeclaredConstructor().newInstance(); var tbVersionedNode = (TbVersionedNode) clazz.getClazz().getDeclaredConstructor().newInstance();
TbPair<Boolean, JsonNode> upgradeRuleNodeConfigurationResult = tbVersionedNode.upgrade(fromVersion, oldConfiguration); TbPair<Boolean, JsonNode> upgradeRuleNodeConfigurationResult = tbVersionedNode.upgrade(fromVersion, oldConfiguration);
if (upgradeRuleNodeConfigurationResult.getFirst()) { if (upgradeRuleNodeConfigurationResult.getFirst()) {
ruleNode.setConfiguration(upgradeRuleNodeConfigurationResult.getSecond()); 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 = private final PaginatedUpdater<String, DeviceProfileEntity> deviceProfileEntityDynamicConditionsUpdater =
new PaginatedUpdater<>() { new PaginatedUpdater<>() {
@ -516,7 +498,7 @@ public class DefaultDataUpdateService implements DataUpdateService {
md.getNodes().add(ruleNode); md.getNodes().add(ruleNode);
md.setFirstNodeIndex(newIdx); md.setFirstNodeIndex(newIdx);
md.addConnectionInfo(newIdx, oldIdx, "Success"); md.addConnectionInfo(newIdx, oldIdx, "Success");
ruleChainService.saveRuleChainMetaData(tenant.getId(), md); ruleChainService.saveRuleChainMetaData(tenant.getId(), md, Function.identity());
} }
} catch (Exception e) { } catch (Exception e) {
log.error("[{}] Unable to update Tenant: {}", tenant.getId(), tenant.getName(), 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; 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.TbNodeException;
import org.thingsboard.rule.engine.api.TbVersionedNode;
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.flow.TbRuleChainOutputNode; 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.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.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.service.sync.vc.EntitiesVersionControlService;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -70,8 +74,7 @@ public class DefaultTbRuleChainService extends AbstractTbEntityService implement
private final RuleChainService ruleChainService; private final RuleChainService ruleChainService;
private final RelationService relationService; private final RelationService relationService;
private final InstallScripts installScripts; private final InstallScripts installScripts;
private final ComponentDiscoveryService componentDiscoveryService;
private final EntitiesVersionControlService vcService;
@Override @Override
public Set<String> getRuleChainOutputLabels(TenantId tenantId, RuleChainId ruleChainId) { public Set<String> getRuleChainOutputLabels(TenantId tenantId, RuleChainId ruleChainId) {
@ -277,7 +280,7 @@ public class DefaultTbRuleChainService extends AbstractTbEntityService implement
RuleChainId ruleChainId = ruleChain.getId(); RuleChainId ruleChainId = ruleChain.getId();
RuleChainId ruleChainMetaDataId = ruleChainMetaData.getRuleChainId(); RuleChainId ruleChainMetaDataId = ruleChainMetaData.getRuleChainId();
try { try {
RuleChainUpdateResult result = ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData); RuleChainUpdateResult result = ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData, this::updateRuleNodeConfiguration);
checkNotNull(result.isSuccess() ? true : null); checkNotNull(result.isSuccess() ? true : null);
List<RuleChain> updatedRuleChains; 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) { private Set<RuleChainId> updateRelatedRuleChains(TenantId tenantId, RuleChainId ruleChainId, Map<String, String> labelsMap) {
Set<RuleChainId> updatedRuleChains = new HashSet<>(); Set<RuleChainId> updatedRuleChains = new HashSet<>();
List<RuleChainOutputLabelsUsage> usageList = getOutputLabelUsage(tenantId, ruleChainId); 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.RuleChainMetaData;
import org.thingsboard.server.common.data.rule.RuleChainOutputLabelsUsage; import org.thingsboard.server.common.data.rule.RuleChainOutputLabelsUsage;
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.service.entitiy.SimpleTbEntityService; import org.thingsboard.server.service.entitiy.SimpleTbEntityService;
import java.util.List; 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 setAutoAssignToEdgeRuleChain(TenantId tenantId, RuleChain ruleChain, User user) throws ThingsboardException;
RuleChain unsetAutoAssignToEdgeRuleChain(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.RuleChainService;
import org.thingsboard.server.dao.rule.RuleNodeDao; import org.thingsboard.server.dao.rule.RuleNodeDao;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.rule.TbRuleChainService;
import org.thingsboard.server.service.sync.vc.data.EntitiesImportCtx; import org.thingsboard.server.service.sync.vc.data.EntitiesImportCtx;
import java.util.Arrays; 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 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 RuleChainService ruleChainService;
private final RuleNodeDao ruleNodeDao; private final RuleNodeDao ruleNodeDao;
@ -106,7 +108,7 @@ public class RuleChainImportService extends BaseEntityImportService<RuleChainId,
ruleChain = ruleChainService.saveRuleChain(ruleChain); ruleChain = ruleChainService.saveRuleChain(ruleChain);
if (ctx.isFinalImportAttempt() || ctx.getCurrentImportResult().isUpdatedAllExternalIds()) { if (ctx.isFinalImportAttempt() || ctx.getCurrentImportResult().isUpdatedAllExternalIds()) {
exportData.getMetaData().setRuleChainId(ruleChain.getId()); 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()); return ruleChainService.findRuleChainById(ctx.getTenantId(), ruleChain.getId());
} else { } else {
return ruleChain; 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.Before;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.junit.jupiter.api.Assertions;
import org.mockito.AdditionalAnswers; import org.mockito.AdditionalAnswers;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -144,16 +145,17 @@ public class RuleChainControllerTest extends AbstractControllerTest {
Assert.assertTrue(savedRuleChain.getCreatedTime() > 0); Assert.assertTrue(savedRuleChain.getCreatedTime() > 0);
Assert.assertEquals(ruleChain.getName(), savedRuleChain.getName()); Assert.assertEquals(ruleChain.getName(), savedRuleChain.getName());
TbVersionedNode tbVersionedNode = new TbGetRelatedAttributeNode(); var annotation = TbGetRelatedAttributeNode.class.getAnnotation(org.thingsboard.rule.engine.api.RuleNode.class);
String ruleNodeType = tbVersionedNode.getClass().getName(); String ruleNodeType = TbGetRelatedAttributeNode.class.getName();
int currentVersion = tbVersionedNode.getCurrentVersion(); int currentVersion = annotation.version();
String oldConfig = "{\"attrMapping\":{\"serialNumber\":\"sn\"}," + String oldConfig = "{\"attrMapping\":{\"serialNumber\":\"sn\"}," +
"\"relationsQuery\":{\"direction\":\"FROM\",\"maxLevel\":1," + "\"relationsQuery\":{\"direction\":\"FROM\",\"maxLevel\":1," +
"\"filters\":[{\"relationType\":\"Contains\",\"entityTypes\":[]}]," + "\"filters\":[{\"relationType\":\"Contains\",\"entityTypes\":[]}]," +
"\"fetchLastLevelOnly\":false},\"telemetry\":false}"; "\"fetchLastLevelOnly\":false},\"telemetry\":false}";
String newConfig = JacksonUtil.toString(new TbGetRelatedDataNodeConfiguration().defaultConfiguration()); TbGetRelatedDataNodeConfiguration defaultConfiguration = new TbGetRelatedDataNodeConfiguration().defaultConfiguration();
String newConfig = JacksonUtil.toString(defaultConfiguration);
var ruleChainMetaData = createRuleChainMetadataWithTbVersionedNodes( var ruleChainMetaData = createRuleChainMetadataWithTbVersionedNodes(
ruleChainId, ruleChainId,
@ -170,7 +172,7 @@ public class RuleChainControllerTest extends AbstractControllerTest {
for (RuleNode ruleNode : savedRuleChainMetaData.getNodes()) { for (RuleNode ruleNode : savedRuleChainMetaData.getNodes()) {
Assert.assertNotNull(ruleNode.getId()); Assert.assertNotNull(ruleNode.getId());
Assert.assertEquals(currentVersion, ruleNode.getConfigurationVersion()); 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.common.util.JacksonUtil;
import org.thingsboard.rule.engine.flow.TbRuleChainInputNodeConfiguration; import org.thingsboard.rule.engine.flow.TbRuleChainInputNodeConfiguration;
import org.thingsboard.rule.engine.metadata.FetchTo; import org.thingsboard.rule.engine.metadata.FetchTo;
import org.thingsboard.rule.engine.metadata.TbGetAttributesNode;
import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration; import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration;
import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.DataConstants;
@ -138,6 +139,7 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule
RuleNode ruleNode1 = new RuleNode(); RuleNode ruleNode1 = new RuleNode();
ruleNode1.setName("Simple Rule Node 1"); ruleNode1.setName("Simple Rule Node 1");
ruleNode1.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); 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); ruleNode1.setDebugMode(true);
TbGetAttributesNodeConfiguration configuration1 = new TbGetAttributesNodeConfiguration(); TbGetAttributesNodeConfiguration configuration1 = new TbGetAttributesNodeConfiguration();
configuration1.setFetchTo(FetchTo.METADATA); configuration1.setFetchTo(FetchTo.METADATA);
@ -147,6 +149,7 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule
RuleNode ruleNode2 = new RuleNode(); RuleNode ruleNode2 = new RuleNode();
ruleNode2.setName("Simple Rule Node 2"); ruleNode2.setName("Simple Rule Node 2");
ruleNode2.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); 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); ruleNode2.setDebugMode(true);
TbGetAttributesNodeConfiguration configuration2 = new TbGetAttributesNodeConfiguration(); TbGetAttributesNodeConfiguration configuration2 = new TbGetAttributesNodeConfiguration();
configuration2.setFetchTo(FetchTo.METADATA); configuration2.setFetchTo(FetchTo.METADATA);
@ -242,6 +245,7 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule
RuleNode ruleNode1 = new RuleNode(); RuleNode ruleNode1 = new RuleNode();
ruleNode1.setName("Simple Rule Node 1"); ruleNode1.setName("Simple Rule Node 1");
ruleNode1.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); 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); ruleNode1.setDebugMode(true);
TbGetAttributesNodeConfiguration configuration1 = new TbGetAttributesNodeConfiguration(); TbGetAttributesNodeConfiguration configuration1 = new TbGetAttributesNodeConfiguration();
configuration1.setFetchTo(FetchTo.METADATA); configuration1.setFetchTo(FetchTo.METADATA);
@ -275,6 +279,7 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule
RuleNode ruleNode2 = new RuleNode(); RuleNode ruleNode2 = new RuleNode();
ruleNode2.setName("Simple Rule Node 2"); ruleNode2.setName("Simple Rule Node 2");
ruleNode2.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); 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); ruleNode2.setDebugMode(true);
TbGetAttributesNodeConfiguration configuration2 = new TbGetAttributesNodeConfiguration(); TbGetAttributesNodeConfiguration configuration2 = new TbGetAttributesNodeConfiguration();
configuration2.setFetchTo(FetchTo.METADATA); 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.springframework.beans.factory.annotation.Autowired;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.metadata.FetchTo; import org.thingsboard.rule.engine.metadata.FetchTo;
import org.thingsboard.rule.engine.metadata.TbGetAttributesNode;
import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration; import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration;
import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.DataConstants;
@ -93,6 +94,7 @@ public abstract class AbstractRuleEngineLifecycleIntegrationTest extends Abstrac
RuleNode ruleNode = new RuleNode(); RuleNode ruleNode = new RuleNode();
ruleNode.setName("Simple Rule Node"); ruleNode.setName("Simple Rule Node");
ruleNode.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); 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); ruleNode.setDebugMode(true);
TbGetAttributesNodeConfiguration configuration = new TbGetAttributesNodeConfiguration(); TbGetAttributesNodeConfiguration configuration = new TbGetAttributesNodeConfiguration();
configuration.setFetchTo(FetchTo.METADATA); 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.common.util.JacksonUtil;
import org.thingsboard.rule.engine.debug.TbMsgGeneratorNode; import org.thingsboard.rule.engine.debug.TbMsgGeneratorNode;
import org.thingsboard.rule.engine.debug.TbMsgGeneratorNodeConfiguration; import org.thingsboard.rule.engine.debug.TbMsgGeneratorNodeConfiguration;
import org.thingsboard.rule.engine.metadata.TbGetAttributesNode;
import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration; import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration;
import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.Dashboard;
@ -86,6 +87,7 @@ import java.nio.ByteBuffer;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.UUID; import java.util.UUID;
import java.util.function.Function;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -333,6 +335,7 @@ public abstract class BaseExportImportServiceTest extends AbstractControllerTest
RuleNode ruleNode2 = new RuleNode(); RuleNode ruleNode2 = new RuleNode();
ruleNode2.setName("Simple Rule Node 2"); ruleNode2.setName("Simple Rule Node 2");
ruleNode2.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); 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); ruleNode2.setDebugMode(true);
TbGetAttributesNodeConfiguration configuration2 = new TbGetAttributesNodeConfiguration(); TbGetAttributesNodeConfiguration configuration2 = new TbGetAttributesNodeConfiguration();
configuration2.setServerAttributeNames(Collections.singletonList("serverAttributeKey2")); configuration2.setServerAttributeNames(Collections.singletonList("serverAttributeKey2"));
@ -341,7 +344,7 @@ public abstract class BaseExportImportServiceTest extends AbstractControllerTest
metaData.setNodes(Arrays.asList(ruleNode1, ruleNode2)); metaData.setNodes(Arrays.asList(ruleNode1, ruleNode2));
metaData.setFirstNodeIndex(0); metaData.setFirstNodeIndex(0);
metaData.addConnectionInfo(0, 1, "Success"); metaData.addConnectionInfo(0, 1, "Success");
ruleChainService.saveRuleChainMetaData(tenantId, metaData); ruleChainService.saveRuleChainMetaData(tenantId, metaData, Function.identity());
return ruleChainService.findRuleChainById(tenantId, ruleChain.getId()); return ruleChainService.findRuleChainById(tenantId, ruleChain.getId());
} }
@ -361,6 +364,7 @@ public abstract class BaseExportImportServiceTest extends AbstractControllerTest
RuleNode ruleNode1 = new RuleNode(); RuleNode ruleNode1 = new RuleNode();
ruleNode1.setName("Simple Rule Node 1"); ruleNode1.setName("Simple Rule Node 1");
ruleNode1.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); 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); ruleNode1.setDebugMode(true);
TbGetAttributesNodeConfiguration configuration1 = new TbGetAttributesNodeConfiguration(); TbGetAttributesNodeConfiguration configuration1 = new TbGetAttributesNodeConfiguration();
configuration1.setServerAttributeNames(Collections.singletonList("serverAttributeKey1")); configuration1.setServerAttributeNames(Collections.singletonList("serverAttributeKey1"));
@ -369,6 +373,7 @@ public abstract class BaseExportImportServiceTest extends AbstractControllerTest
RuleNode ruleNode2 = new RuleNode(); RuleNode ruleNode2 = new RuleNode();
ruleNode2.setName("Simple Rule Node 2"); ruleNode2.setName("Simple Rule Node 2");
ruleNode2.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); 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); ruleNode2.setDebugMode(true);
TbGetAttributesNodeConfiguration configuration2 = new TbGetAttributesNodeConfiguration(); TbGetAttributesNodeConfiguration configuration2 = new TbGetAttributesNodeConfiguration();
configuration2.setServerAttributeNames(Collections.singletonList("serverAttributeKey2")); configuration2.setServerAttributeNames(Collections.singletonList("serverAttributeKey2"));
@ -377,7 +382,7 @@ public abstract class BaseExportImportServiceTest extends AbstractControllerTest
metaData.setNodes(Arrays.asList(ruleNode1, ruleNode2)); metaData.setNodes(Arrays.asList(ruleNode1, ruleNode2));
metaData.setFirstNodeIndex(0); metaData.setFirstNodeIndex(0);
metaData.addConnectionInfo(0, 1, "Success"); metaData.addConnectionInfo(0, 1, "Success");
ruleChainService.saveRuleChainMetaData(tenantId, metaData); ruleChainService.saveRuleChainMetaData(tenantId, metaData, Function.identity());
return ruleChainService.findRuleChainById(tenantId, ruleChain.getId()); 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.Collection;
import java.util.List; import java.util.List;
import java.util.function.Function;
/** /**
* Created by igor on 3/12/18. * Created by igor on 3/12/18.
@ -45,7 +46,7 @@ public interface RuleChainService extends EntityDaoService {
boolean setRootRuleChain(TenantId tenantId, RuleChainId ruleChainId); 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); RuleChainMetaData loadRuleChainMetaData(TenantId tenantId, RuleChainId ruleChainId);
@ -75,7 +76,7 @@ public interface RuleChainService extends EntityDaoService {
RuleChainData exportTenantRuleChains(TenantId tenantId, PageLink pageLink) throws ThingsboardException; 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); 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.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.thingsboard.server.common.data.DataConstants.TENANT; import static org.thingsboard.server.common.data.DataConstants.TENANT;
@ -145,7 +146,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
} }
@Override @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."); Validator.validateId(ruleChainMetaData.getRuleChainId(), "Incorrect rule chain id.");
RuleChain ruleChain = findRuleChainById(tenantId, ruleChainMetaData.getRuleChainId()); RuleChain ruleChain = findRuleChainById(tenantId, ruleChainMetaData.getRuleChainId());
if (ruleChain == null) { if (ruleChain == null) {
@ -189,38 +190,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
if (nodes != null) { if (nodes != null) {
for (RuleNode node : toAddOrUpdate) { for (RuleNode node : toAddOrUpdate) {
node.setRuleChainId(ruleChainId); node.setRuleChainId(ruleChainId);
String ruleNodeType = node.getType(); node = ruleNodeUpdater.apply(node);
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);
}
RuleNode savedNode = ruleNodeDao.save(tenantId, node); RuleNode savedNode = ruleNodeDao.save(tenantId, node);
relations.add(new EntityRelation(ruleChainMetaData.getRuleChainId(), savedNode.getId(), relations.add(new EntityRelation(ruleChainMetaData.getRuleChainId(), savedNode.getId(),
EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN)); EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN));
@ -484,7 +454,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
} }
@Override @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<>(); List<RuleChainImportResult> importResults = new ArrayList<>();
setRandomRuleChainIds(ruleChainData); setRandomRuleChainIds(ruleChainData);
@ -521,7 +491,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
} }
if (CollectionUtils.isNotEmpty(ruleChainData.getMetadata())) { if (CollectionUtils.isNotEmpty(ruleChainData.getMetadata())) {
ruleChainData.getMetadata().forEach(md -> saveRuleChainMetaData(tenantId, md)); ruleChainData.getMetadata().forEach(md -> saveRuleChainMetaData(tenantId, md, ruleNodeUpdater));
} }
return importResults; 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.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.function.Function;
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; 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.setNodes(Arrays.asList(ruleNode1, ruleNode2));
ruleChainMetaData3.setFirstNodeIndex(0); ruleChainMetaData3.setFirstNodeIndex(0);
ruleChainMetaData3.setRuleChainId(ruleChain3.getId()); ruleChainMetaData3.setRuleChainId(ruleChain3.getId());
ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData3); ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData3, Function.identity());
ruleChainService.assignRuleChainToEdge(tenantId, ruleChain3.getId(), savedEdge.getId()); 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.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.function.Function;
/** /**
* Created by igor on 3/13/18. * Created by igor on 3/13/18.
@ -280,7 +281,7 @@ public class RuleChainServiceTest extends AbstractServiceTest {
ruleNodes.set(name3Index, ruleNode4); 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()); RuleChainMetaData updatedRuleChainMetaData = ruleChainService.loadRuleChainMetaData(tenantId, savedRuleChainMetaData.getRuleChainId());
Assert.assertEquals(3, updatedRuleChainMetaData.getNodes().size()); Assert.assertEquals(3, updatedRuleChainMetaData.getNodes().size());
@ -311,14 +312,14 @@ public class RuleChainServiceTest extends AbstractServiceTest {
@Test @Test
public void testUpdateRuleChainMetaDataWithCirclingRelation() { public void testUpdateRuleChainMetaDataWithCirclingRelation() {
Assertions.assertThrows(DataValidationException.class, () -> { Assertions.assertThrows(DataValidationException.class, () -> {
ruleChainService.saveRuleChainMetaData(tenantId, createRuleChainMetadataWithCirclingRelation()); ruleChainService.saveRuleChainMetaData(tenantId, createRuleChainMetadataWithCirclingRelation(), Function.identity());
}); });
} }
@Test @Test
public void testUpdateRuleChainMetaDataWithCirclingRelation2() { public void testUpdateRuleChainMetaDataWithCirclingRelation2() {
Assertions.assertThrows(DataValidationException.class, () -> { 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(0,2,"fail");
ruleChainMetaData.addConnectionInfo(1,2,"success"); 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()); 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}; 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; 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 C config;
protected FetchTo fetchTo; protected FetchTo fetchTo;
@Override
public int getCurrentVersion() {
return 1;
}
@Override @Override
public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
config = loadNodeConfiguration(configuration); 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( @RuleNode(
type = ComponentType.ENRICHMENT, type = ComponentType.ENRICHMENT,
name = "fetch device credentials", name = "fetch device credentials",
version = 1,
configClazz = TbFetchDeviceCredentialsNodeConfiguration.class, configClazz = TbFetchDeviceCredentialsNodeConfiguration.class,
nodeDescription = "Adds device credentials to the message or message metadata", nodeDescription = "Adds device credentials to the message or message metadata",
nodeDetails = "if message originator type is Device and device credentials was successfully fetched, " + 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, @RuleNode(type = ComponentType.ENRICHMENT,
name = "originator attributes", name = "originator attributes",
configClazz = TbGetAttributesNodeConfiguration.class, configClazz = TbGetAttributesNodeConfiguration.class,
version = 1,
nodeDescription = "Adds attributes and/or latest timeseries data for the message originator to the message or message metadata", 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 " + 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. " + "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, type = ComponentType.ENRICHMENT,
name = "customer attributes", name = "customer attributes",
configClazz = TbGetEntityDataNodeConfiguration.class, configClazz = TbGetEntityDataNodeConfiguration.class,
version = 1,
nodeDescription = "Adds message originator customer attributes or latest telemetry into message or message metadata", 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 " + 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, " + "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, @RuleNode(type = ComponentType.ENRICHMENT,
name = "customer details", name = "customer details",
configClazz = TbGetCustomerDetailsNodeConfiguration.class, configClazz = TbGetCustomerDetailsNodeConfiguration.class,
version = 1,
nodeDescription = "Adds message originator customer details into message or message metadata", 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 " + 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.", "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, @RuleNode(type = ComponentType.ENRICHMENT,
name = "related device attributes", name = "related device attributes",
configClazz = TbGetDeviceAttrNodeConfiguration.class, configClazz = TbGetDeviceAttrNodeConfiguration.class,
version = 1,
nodeDescription = "Add originators related device attributes and/or latest telemetry values into message or message metadata", 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. " + 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. " + "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, @RuleNode(type = ComponentType.ENRICHMENT,
name = "originator fields", name = "originator fields",
configClazz = TbGetOriginatorFieldsConfiguration.class, configClazz = TbGetOriginatorFieldsConfiguration.class,
version = 1,
nodeDescription = "Adds message originator fields values into message or message metadata", 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. " + 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.", "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, type = ComponentType.ENRICHMENT,
name = "related entity data", name = "related entity data",
configClazz = TbGetRelatedDataNodeConfiguration.class, configClazz = TbGetRelatedDataNodeConfiguration.class,
version = 1,
nodeDescription = "Adds originators related entity attributes or latest telemetry or fields into message or message metadata", 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. " + 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. " + "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, type = ComponentType.ENRICHMENT,
name = "tenant attributes", name = "tenant attributes",
configClazz = TbGetEntityDataNodeConfiguration.class, configClazz = TbGetEntityDataNodeConfiguration.class,
version = 1,
nodeDescription = "Adds message originator tenant attributes or latest telemetry into message or message metadata", 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 " + 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.", "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, @RuleNode(type = ComponentType.ENRICHMENT,
name = "tenant details", name = "tenant details",
configClazz = TbGetTenantDetailsNodeConfiguration.class, configClazz = TbGetTenantDetailsNodeConfiguration.class,
version = 1,
nodeDescription = "Adds message originator tenant details into message or message metadata", nodeDescription = "Adds message originator tenant details into message or message metadata",
nodeDetails = "Useful when we need to retrieve contact information from your tenant " + 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.", "such as email, phone, address, etc., for notifications via email, SMS, and other notification providers.",

Loading…
Cancel
Save