Browse Source

moved queueName from rule-node config to rule-node

pull/9828/head
YevhenBondarenko 3 years ago
parent
commit
3fea0f88f3
  1. 19
      application/src/main/data/upgrade/3.6.2/schema_update.sql
  2. 4
      application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
  3. 5
      application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java
  4. 1
      application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java
  5. 3
      application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java
  6. 49
      application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java
  7. 21
      application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/RuleChainImportService.java
  8. 7
      common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentDescriptor.java
  9. 6
      common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java
  10. 2
      dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
  11. 5
      dao/src/main/java/org/thingsboard/server/dao/model/sql/ComponentDescriptorEntity.java
  12. 5
      dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleNodeEntity.java
  13. 3
      dao/src/main/java/org/thingsboard/server/dao/sql/component/AbstractComponentDescriptorInsertRepository.java
  14. 4
      dao/src/main/java/org/thingsboard/server/dao/sql/component/SqlComponentDescriptorInsertRepository.java
  15. 4
      dao/src/main/resources/sql/schema-entities.sql
  16. 2
      rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleNode.java
  17. 9
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java
  18. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNodeConfiguration.java
  19. 5
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/deduplication/TbMsgDeduplicationNode.java
  20. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/deduplication/TbMsgDeduplicationNodeConfiguration.java
  21. 9
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbCheckpointNode.java
  22. 30
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbCheckpointNodeConfiguration.java
  23. 17
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbMsgDeduplicationNodeTest.java

19
application/src/main/data/upgrade/3.6.2/schema_update.sql

@ -0,0 +1,19 @@
--
-- 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.
--
ALTER TABLE rule_node ADD COLUMN IF NOT EXISTS queue_name varchar(255);
ALTER TABLE component_descriptor ADD COLUMN IF NOT EXISTS has_queue_name boolean DEFAULT false;

4
application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java

@ -185,7 +185,7 @@ class DefaultTbContext implements TbContext {
@Override @Override
public void enqueue(TbMsg tbMsg, Runnable onSuccess, Consumer<Throwable> onFailure) { public void enqueue(TbMsg tbMsg, Runnable onSuccess, Consumer<Throwable> onFailure) {
TopicPartitionInfo tpi = mainCtx.resolve(ServiceType.TB_RULE_ENGINE, getTenantId(), tbMsg.getOriginator()); TopicPartitionInfo tpi = mainCtx.resolve(ServiceType.TB_RULE_ENGINE, nodeCtx.getSelf().getQueueName(), getTenantId(), tbMsg.getOriginator());
enqueue(tpi, tbMsg, onFailure, onSuccess); enqueue(tpi, tbMsg, onFailure, onSuccess);
} }
@ -310,7 +310,7 @@ class DefaultTbContext implements TbContext {
@Override @Override
public boolean isLocalEntity(EntityId entityId) { public boolean isLocalEntity(EntityId entityId) {
return mainCtx.resolve(ServiceType.TB_RULE_ENGINE, getTenantId(), entityId).isMyPartition(); return mainCtx.resolve(ServiceType.TB_RULE_ENGINE, nodeCtx.getSelf().getQueueName(), getTenantId(), entityId).isMyPartition();
} }
private void scheduleMsgWithDelay(TbActorMsg msg, long delayInMs, TbActorRef target) { private void scheduleMsgWithDelay(TbActorMsg msg, long delayInMs, TbActorRef target) {

5
application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java

@ -277,6 +277,11 @@ public class ThingsboardInstallService {
} else { } else {
log.info("Skipping images migration. Run the upgrade with fromVersion as '3.6.2-images' to migrate"); log.info("Skipping images migration. Run the upgrade with fromVersion as '3.6.2-images' to migrate");
} }
break;
case "3.6.2":
log.info("Upgrading ThingsBoard from version 3.6.2 to 3.6.3 ...");
databaseEntitiesUpgradeService.upgradeDatabase("3.6.2");
dataUpdateService.updateData("3.6.2");
//TODO DON'T FORGET to update switch statement in the CacheCleanupService if you need to clear the cache //TODO DON'T FORGET to update switch statement in the CacheCleanupService if you need to clear the cache
break; break;
default: default:

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

@ -189,6 +189,7 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
scannedComponent.setName(ruleNodeAnnotation.name()); scannedComponent.setName(ruleNodeAnnotation.name());
scannedComponent.setScope(ruleNodeAnnotation.scope()); scannedComponent.setScope(ruleNodeAnnotation.scope());
scannedComponent.setClusteringMode(ruleNodeAnnotation.clusteringMode()); scannedComponent.setClusteringMode(ruleNodeAnnotation.clusteringMode());
scannedComponent.setHasQueueName(ruleNodeAnnotation.hasQueueName());
NodeDefinition nodeDefinition = prepareNodeDefinition(clazz, ruleNodeAnnotation); NodeDefinition nodeDefinition = prepareNodeDefinition(clazz, ruleNodeAnnotation);
ObjectNode configurationDescriptor = JacksonUtil.newObjectNode(); ObjectNode configurationDescriptor = JacksonUtil.newObjectNode();
JsonNode node = JacksonUtil.valueToTree(nodeDefinition); JsonNode node = JacksonUtil.valueToTree(nodeDefinition);

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

@ -772,6 +772,9 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
} }
}); });
break; break;
case "3.6.2":
updateSchema("3.6.2", 3006002, "3.6.3", 3006003, null);
break;
default: default:
throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion);
} }

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

@ -227,11 +227,25 @@ public class DefaultDataUpdateService implements DataUpdateService {
log.info("Updating data from version 3.6.0 to 3.6.1 ..."); log.info("Updating data from version 3.6.0 to 3.6.1 ...");
migrateDeviceConnectivity(); migrateDeviceConnectivity();
break; break;
case "3.6.2":
updateRuleNodesQueueName();
break;
default: default:
throw new RuntimeException("Unable to update data, unsupported fromVersion: " + fromVersion); throw new RuntimeException("Unable to update data, unsupported fromVersion: " + fromVersion);
} }
} }
private void updateRuleNodesQueueName() {
String[] ruleNodeTypes = {
"org.thingsboard.rule.engine.debug.TbMsgGeneratorNode",
"org.thingsboard.rule.engine.flow.TbCheckpointNode",
"org.thingsboard.rule.engine.deduplication.TbMsgDeduplicationNode"
};
for (String ruleNodeType : ruleNodeTypes) {
ruleNodeQueueNameUpdater.updateEntities(ruleNodeType);
}
}
private void migrateEdgeEvents(String logPrefix) { private void migrateEdgeEvents(String logPrefix) {
boolean skipEdgeEventsMigration = getEnv("TB_SKIP_EDGE_EVENTS_MIGRATION", false); boolean skipEdgeEventsMigration = getEnv("TB_SKIP_EDGE_EVENTS_MIGRATION", false);
if (!skipEdgeEventsMigration) { if (!skipEdgeEventsMigration) {
@ -792,4 +806,39 @@ public class DefaultDataUpdateService implements DataUpdateService {
} }
} }
private final PaginatedUpdater<String, RuleNode> ruleNodeQueueNameUpdater =
new PaginatedUpdater<>() {
@Override
protected String getName() {
return "RuleNode queue name updater";
}
@Override
protected boolean forceReportTotal() {
return true;
}
@Override
protected PageData<RuleNode> findEntities(String type, PageLink pageLink) {
return ruleChainService.findAllRuleNodesByType(type, pageLink);
}
@Override
protected void updateEntity(RuleNode ruleNode) {
try {
ObjectNode configuration = (ObjectNode) ruleNode.getConfiguration();
JsonNode queueName = configuration.remove("queueName");
if (queueName != null) {
if (!queueName.isNull()) {
ruleNode.setQueueName(queueName.asText());
}
ruleChainService.saveRuleNode(null, ruleNode);
}
} catch (Exception e) {
log.error("Unable to update RuleNode", e);
}
}
};
} }

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

@ -15,13 +15,14 @@
*/ */
package org.thingsboard.server.service.sync.ie.importing.impl; package org.thingsboard.server.service.sync.ie.importing.impl;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
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.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
@ -84,12 +85,14 @@ public class RuleChainImportService extends BaseEntityImportService<RuleChainId,
node.setRuleChainId(old.getId()); node.setRuleChainId(old.getId());
node.setExternalId(node.getId()); node.setExternalId(node.getId());
node.setId((RuleNodeId) ctx.getInternalId(node.getId())); node.setId((RuleNodeId) ctx.getInternalId(node.getId()));
setQueueName(node);
}); });
} else { } else {
ruleNodes.forEach(node -> { ruleNodes.forEach(node -> {
node.setRuleChainId(null); node.setRuleChainId(null);
node.setExternalId(node.getId()); node.setExternalId(node.getId());
node.setId(null); node.setId(null);
setQueueName(node);
}); });
} }
@ -105,6 +108,22 @@ public class RuleChainImportService extends BaseEntityImportService<RuleChainId,
return ruleChain; return ruleChain;
} }
private void setQueueName(RuleNode ruleNode) {
try {
Class<?> clazz = Class.forName(ruleNode.getType());
org.thingsboard.rule.engine.api.RuleNode ruleNodeAnnotation = clazz.getAnnotation(org.thingsboard.rule.engine.api.RuleNode.class);
if (ruleNodeAnnotation.hasQueueName()) {
ObjectNode configuration = (ObjectNode) ruleNode.getConfiguration();
JsonNode queueName = configuration.remove("queueName");
if (queueName != null && !queueName.isNull()) {
ruleNode.setQueueName(queueName.asText());
}
}
} catch (ClassNotFoundException e) {
log.warn("[{}] RuleNode class not found [{}]", ruleNode.getName(), ruleNode.getType());
}
}
@Override @Override
protected RuleChain saveOrUpdate(EntitiesImportCtx ctx, RuleChain ruleChain, RuleChainExportData exportData, IdProvider idProvider) { protected RuleChain saveOrUpdate(EntitiesImportCtx ctx, RuleChain ruleChain, RuleChainExportData exportData, IdProvider idProvider) {
ruleChain = ruleChainService.saveRuleChain(ruleChain); ruleChain = ruleChainService.saveRuleChain(ruleChain);

7
common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentDescriptor.java

@ -55,6 +55,8 @@ public class ComponentDescriptor extends BaseData<ComponentDescriptorId> {
@Length(fieldName = "actions") @Length(fieldName = "actions")
@ApiModelProperty(position = 10, value = "Rule Node Actions. Deprecated. Always null.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @ApiModelProperty(position = 10, value = "Rule Node Actions. Deprecated. Always null.", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
@Getter @Setter private String actions; @Getter @Setter private String actions;
@ApiModelProperty(position = 11, value = "Indicates that the RuleNode is support queue name.", accessMode = ApiModelProperty.AccessMode.READ_ONLY, example = "true")
@Getter @Setter private boolean hasQueueName;
public ComponentDescriptor() { public ComponentDescriptor() {
super(); super();
@ -74,6 +76,7 @@ public class ComponentDescriptor extends BaseData<ComponentDescriptorId> {
this.configurationDescriptor = plugin.getConfigurationDescriptor(); this.configurationDescriptor = plugin.getConfigurationDescriptor();
this.configurationVersion = plugin.getConfigurationVersion(); this.configurationVersion = plugin.getConfigurationVersion();
this.actions = plugin.getActions(); this.actions = plugin.getActions();
this.hasQueueName = plugin.isHasQueueName();
} }
@ApiModelProperty(position = 1, value = "JSON object with the descriptor Id. " + @ApiModelProperty(position = 1, value = "JSON object with the descriptor Id. " +
@ -104,6 +107,8 @@ public class ComponentDescriptor extends BaseData<ComponentDescriptorId> {
if (!Objects.equals(actions, that.actions)) return false; if (!Objects.equals(actions, that.actions)) return false;
if (!Objects.equals(configurationDescriptor, that.configurationDescriptor)) return false; if (!Objects.equals(configurationDescriptor, that.configurationDescriptor)) return false;
if (configurationVersion != that.configurationVersion) return false; if (configurationVersion != that.configurationVersion) return false;
if (clusteringMode != that.clusteringMode) return false;
if (hasQueueName != that.isHasQueueName()) return false;
return Objects.equals(clazz, that.clazz); return Objects.equals(clazz, that.clazz);
} }
@ -115,6 +120,8 @@ public class ComponentDescriptor extends BaseData<ComponentDescriptorId> {
result = 31 * result + (name != null ? name.hashCode() : 0); result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + (clazz != null ? clazz.hashCode() : 0); result = 31 * result + (clazz != null ? clazz.hashCode() : 0);
result = 31 * result + (actions != null ? actions.hashCode() : 0); result = 31 * result + (actions != null ? actions.hashCode() : 0);
result = 31 * result + (clusteringMode != null ? clusteringMode.hashCode() : 0);
result = 31 * result + (hasQueueName ? 1 : 0);
return result; return result;
} }

6
common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java

@ -50,9 +50,11 @@ public class RuleNode extends BaseDataWithAdditionalInfo<RuleNodeId> implements
private boolean debugMode; private boolean debugMode;
@ApiModelProperty(position = 7, value = "Enable/disable singleton mode. ", example = "false") @ApiModelProperty(position = 7, value = "Enable/disable singleton mode. ", example = "false")
private boolean singletonMode; private boolean singletonMode;
@ApiModelProperty(position = 8, value = "Version of rule node configuration. ", example = "0") @ApiModelProperty(position = 8, value = "Queue name. ", example = "Main")
private String queueName;
@ApiModelProperty(position = 9, value = "Version of rule node configuration. ", example = "0")
private int configurationVersion; private int configurationVersion;
@ApiModelProperty(position = 9, value = "JSON with the rule node configuration. Structure depends on the rule node implementation.", dataType = "com.fasterxml.jackson.databind.JsonNode") @ApiModelProperty(position = 10, value = "JSON with the rule node configuration. Structure depends on the rule node implementation.", dataType = "com.fasterxml.jackson.databind.JsonNode")
private transient JsonNode configuration; private transient JsonNode configuration;
@JsonIgnore @JsonIgnore
private byte[] configurationBytes; private byte[] configurationBytes;

2
dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java

@ -354,6 +354,7 @@ public class ModelConstants {
public static final String COMPONENT_DESCRIPTOR_CONFIGURATION_DESCRIPTOR_PROPERTY = "configuration_descriptor"; public static final String COMPONENT_DESCRIPTOR_CONFIGURATION_DESCRIPTOR_PROPERTY = "configuration_descriptor";
public static final String COMPONENT_DESCRIPTOR_CONFIGURATION_VERSION_PROPERTY = "configuration_version"; public static final String COMPONENT_DESCRIPTOR_CONFIGURATION_VERSION_PROPERTY = "configuration_version";
public static final String COMPONENT_DESCRIPTOR_ACTIONS_PROPERTY = "actions"; public static final String COMPONENT_DESCRIPTOR_ACTIONS_PROPERTY = "actions";
public static final String COMPONENT_DESCRIPTOR_HAS_QUEUE_NAME_PROPERTY = "has_queue_name";
/** /**
* Event constants. * Event constants.
@ -389,6 +390,7 @@ public class ModelConstants {
public static final String DEBUG_MODE = "debug_mode"; public static final String DEBUG_MODE = "debug_mode";
public static final String SINGLETON_MODE = "singleton_mode"; public static final String SINGLETON_MODE = "singleton_mode";
public static final String QUEUE_NAME = "queue_name";
/** /**
* Rule chain constants. * Rule chain constants.

5
dao/src/main/java/org/thingsboard/server/dao/model/sql/ComponentDescriptorEntity.java

@ -70,6 +70,9 @@ public class ComponentDescriptorEntity extends BaseSqlEntity<ComponentDescriptor
@Column(name = ModelConstants.COMPONENT_DESCRIPTOR_ACTIONS_PROPERTY) @Column(name = ModelConstants.COMPONENT_DESCRIPTOR_ACTIONS_PROPERTY)
private String actions; private String actions;
@Column(name = ModelConstants.COMPONENT_DESCRIPTOR_HAS_QUEUE_NAME_PROPERTY)
private boolean hasQueueName;
public ComponentDescriptorEntity() { public ComponentDescriptorEntity() {
} }
@ -86,6 +89,7 @@ public class ComponentDescriptorEntity extends BaseSqlEntity<ComponentDescriptor
this.clazz = component.getClazz(); this.clazz = component.getClazz();
this.configurationDescriptor = component.getConfigurationDescriptor(); this.configurationDescriptor = component.getConfigurationDescriptor();
this.configurationVersion = component.getConfigurationVersion(); this.configurationVersion = component.getConfigurationVersion();
this.hasQueueName = component.isHasQueueName();
} }
@Override @Override
@ -100,6 +104,7 @@ public class ComponentDescriptorEntity extends BaseSqlEntity<ComponentDescriptor
data.setActions(this.getActions()); data.setActions(this.getActions());
data.setConfigurationDescriptor(configurationDescriptor); data.setConfigurationDescriptor(configurationDescriptor);
data.setConfigurationVersion(configurationVersion); data.setConfigurationVersion(configurationVersion);
data.setHasQueueName(hasQueueName);
return data; return data;
} }
} }

5
dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleNodeEntity.java

@ -66,6 +66,9 @@ public class RuleNodeEntity extends BaseSqlEntity<RuleNode> {
@Column(name = ModelConstants.SINGLETON_MODE) @Column(name = ModelConstants.SINGLETON_MODE)
private boolean singletonMode; private boolean singletonMode;
@Column(name = ModelConstants.QUEUE_NAME)
private String queueName;
@Column(name = ModelConstants.EXTERNAL_ID_PROPERTY) @Column(name = ModelConstants.EXTERNAL_ID_PROPERTY)
private UUID externalId; private UUID externalId;
@ -84,6 +87,7 @@ public class RuleNodeEntity extends BaseSqlEntity<RuleNode> {
this.name = ruleNode.getName(); this.name = ruleNode.getName();
this.debugMode = ruleNode.isDebugMode(); this.debugMode = ruleNode.isDebugMode();
this.singletonMode = ruleNode.isSingletonMode(); this.singletonMode = ruleNode.isSingletonMode();
this.queueName = ruleNode.getQueueName();
this.configurationVersion = ruleNode.getConfigurationVersion(); this.configurationVersion = ruleNode.getConfigurationVersion();
this.configuration = ruleNode.getConfiguration(); this.configuration = ruleNode.getConfiguration();
this.additionalInfo = ruleNode.getAdditionalInfo(); this.additionalInfo = ruleNode.getAdditionalInfo();
@ -103,6 +107,7 @@ public class RuleNodeEntity extends BaseSqlEntity<RuleNode> {
ruleNode.setName(name); ruleNode.setName(name);
ruleNode.setDebugMode(debugMode); ruleNode.setDebugMode(debugMode);
ruleNode.setSingletonMode(singletonMode); ruleNode.setSingletonMode(singletonMode);
ruleNode.setQueueName(queueName);
ruleNode.setConfigurationVersion(configurationVersion); ruleNode.setConfigurationVersion(configurationVersion);
ruleNode.setConfiguration(configuration); ruleNode.setConfiguration(configuration);
ruleNode.setAdditionalInfo(additionalInfo); ruleNode.setAdditionalInfo(additionalInfo);

3
dao/src/main/java/org/thingsboard/server/dao/sql/component/AbstractComponentDescriptorInsertRepository.java

@ -77,7 +77,8 @@ public abstract class AbstractComponentDescriptorInsertRepository implements Com
.setParameter("name", entity.getName()) .setParameter("name", entity.getName())
.setParameter("scope", entity.getScope().name()) .setParameter("scope", entity.getScope().name())
.setParameter("type", entity.getType().name()) .setParameter("type", entity.getType().name())
.setParameter("clustering_mode", entity.getClusteringMode().name()); .setParameter("clustering_mode", entity.getClusteringMode().name())
.setParameter("has_queue_name", entity.isHasQueueName());
} }
private ComponentDescriptorEntity processSaveOrUpdate(ComponentDescriptorEntity entity, String query) { private ComponentDescriptorEntity processSaveOrUpdate(ComponentDescriptorEntity entity, String query) {

4
dao/src/main/java/org/thingsboard/server/dao/sql/component/SqlComponentDescriptorInsertRepository.java

@ -44,10 +44,10 @@ public class SqlComponentDescriptorInsertRepository extends AbstractComponentDes
} }
private static String getInsertOrUpdateStatement(String conflictKeyStatement, String updateKeyStatement) { private static String getInsertOrUpdateStatement(String conflictKeyStatement, String updateKeyStatement) {
return "INSERT INTO component_descriptor (id, created_time, actions, clazz, configuration_descriptor, configuration_version, name, scope, type, clustering_mode) VALUES (:id, :created_time, :actions, :clazz, :configuration_descriptor, :configuration_version, :name, :scope, :type, :clustering_mode) ON CONFLICT " + conflictKeyStatement + " DO UPDATE SET " + updateKeyStatement + " returning *"; return "INSERT INTO component_descriptor (id, created_time, actions, clazz, configuration_descriptor, configuration_version, name, scope, type, clustering_mode, has_queue_name) VALUES (:id, :created_time, :actions, :clazz, :configuration_descriptor, :configuration_version, :name, :scope, :type, :clustering_mode, :has_queue_name) ON CONFLICT " + conflictKeyStatement + " DO UPDATE SET " + updateKeyStatement + " returning *";
} }
private static String getUpdateStatement(String id) { private static String getUpdateStatement(String id) {
return "actions = :actions, " + id + ",created_time = :created_time, configuration_descriptor = :configuration_descriptor, configuration_version = :configuration_version, name = :name, scope = :scope, type = :type, clustering_mode = :clustering_mode"; return "actions = :actions, " + id + ",created_time = :created_time, configuration_descriptor = :configuration_descriptor, configuration_version = :configuration_version, name = :name, scope = :scope, type = :type, clustering_mode = :clustering_mode, has_queue_name = :has_queue_name";
} }
} }

4
dao/src/main/resources/sql/schema-entities.sql

@ -126,7 +126,8 @@ CREATE TABLE IF NOT EXISTS component_descriptor (
name varchar(255), name varchar(255),
scope varchar(255), scope varchar(255),
type varchar(255), type varchar(255),
clustering_mode varchar(255) clustering_mode varchar(255),
has_queue_name boolean DEFAULT false
); );
CREATE TABLE IF NOT EXISTS customer ( CREATE TABLE IF NOT EXISTS customer (
@ -187,6 +188,7 @@ CREATE TABLE IF NOT EXISTS rule_node (
name varchar(255), name varchar(255),
debug_mode boolean, debug_mode boolean,
singleton_mode boolean, singleton_mode boolean,
queue_name varchar(255),
external_id uuid external_id uuid
); );

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

@ -42,6 +42,8 @@ public @interface RuleNode {
ComponentClusteringMode clusteringMode() default ComponentClusteringMode.ENABLED; ComponentClusteringMode clusteringMode() default ComponentClusteringMode.ENABLED;
boolean hasQueueName() default false;
boolean inEnabled() default true; boolean inEnabled() default true;
boolean outEnabled() default true; boolean outEnabled() default true;

9
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java

@ -50,6 +50,7 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback;
type = ComponentType.ACTION, type = ComponentType.ACTION,
name = "generator", name = "generator",
configClazz = TbMsgGeneratorNodeConfiguration.class, configClazz = TbMsgGeneratorNodeConfiguration.class,
hasQueueName = true,
nodeDescription = "Periodically generates messages", nodeDescription = "Periodically generates messages",
nodeDetails = "Generates messages with configurable period. Javascript function used for message generation.", nodeDetails = "Generates messages with configurable period. Javascript function used for message generation.",
inEnabled = false, inEnabled = false,
@ -69,6 +70,7 @@ public class TbMsgGeneratorNode implements TbNode {
private UUID nextTickId; private UUID nextTickId;
private TbMsg prevMsg; private TbMsg prevMsg;
private final AtomicBoolean initialized = new AtomicBoolean(false); private final AtomicBoolean initialized = new AtomicBoolean(false);
private String queueName;
@Override @Override
public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
@ -76,6 +78,7 @@ public class TbMsgGeneratorNode implements TbNode {
this.config = TbNodeUtils.convert(configuration, TbMsgGeneratorNodeConfiguration.class); this.config = TbNodeUtils.convert(configuration, TbMsgGeneratorNodeConfiguration.class);
this.delay = TimeUnit.SECONDS.toMillis(config.getPeriodInSeconds()); this.delay = TimeUnit.SECONDS.toMillis(config.getPeriodInSeconds());
this.currentMsgCount = 0; this.currentMsgCount = 0;
this.queueName = ctx.getSelf().getQueueName();
if (!StringUtils.isEmpty(config.getOriginatorId())) { if (!StringUtils.isEmpty(config.getOriginatorId())) {
originatorId = EntityIdFactory.getByTypeAndUuid(config.getOriginatorType(), config.getOriginatorId()); originatorId = EntityIdFactory.getByTypeAndUuid(config.getOriginatorType(), config.getOriginatorId());
ctx.checkTenantEntity(originatorId); ctx.checkTenantEntity(originatorId);
@ -137,7 +140,7 @@ public class TbMsgGeneratorNode implements TbNode {
} }
lastScheduledTs = lastScheduledTs + delay; lastScheduledTs = lastScheduledTs + delay;
long curDelay = Math.max(0L, (lastScheduledTs - curTs)); long curDelay = Math.max(0L, (lastScheduledTs - curTs));
TbMsg tickMsg = ctx.newMsg(config.getQueueName(), TbMsgType.GENERATOR_NODE_SELF_MSG, ctx.getSelfId(), TbMsg tickMsg = ctx.newMsg(queueName, TbMsgType.GENERATOR_NODE_SELF_MSG, ctx.getSelfId(),
getCustomerIdFromMsg(msg), TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); getCustomerIdFromMsg(msg), TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING);
nextTickId = tickMsg.getId(); nextTickId = tickMsg.getId();
ctx.tellSelf(tickMsg, curDelay); ctx.tellSelf(tickMsg, curDelay);
@ -146,14 +149,14 @@ public class TbMsgGeneratorNode implements TbNode {
private ListenableFuture<TbMsg> generate(TbContext ctx, TbMsg msg) { private ListenableFuture<TbMsg> generate(TbContext ctx, TbMsg msg) {
log.trace("generate, config {}", config); log.trace("generate, config {}", config);
if (prevMsg == null) { if (prevMsg == null) {
prevMsg = ctx.newMsg(config.getQueueName(), TbMsg.EMPTY_STRING, originatorId, msg.getCustomerId(), TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); prevMsg = ctx.newMsg(queueName, TbMsg.EMPTY_STRING, originatorId, msg.getCustomerId(), TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT);
} }
if (initialized.get()) { if (initialized.get()) {
ctx.logJsEvalRequest(); ctx.logJsEvalRequest();
return Futures.transformAsync(scriptEngine.executeGenerateAsync(prevMsg), generated -> { return Futures.transformAsync(scriptEngine.executeGenerateAsync(prevMsg), generated -> {
log.trace("generate process response, generated {}, config {}", generated, config); log.trace("generate process response, generated {}, config {}", generated, config);
ctx.logJsEvalResponse(); ctx.logJsEvalResponse();
prevMsg = ctx.newMsg(config.getQueueName(), generated.getType(), originatorId, msg.getCustomerId(), generated.getMetaData(), generated.getData()); prevMsg = ctx.newMsg(queueName, generated.getType(), originatorId, msg.getCustomerId(), generated.getMetaData(), generated.getData());
return Futures.immediateFuture(prevMsg); return Futures.immediateFuture(prevMsg);
}, MoreExecutors.directExecutor()); //usually it runs on js-executor-remote-callback thread pool }, MoreExecutors.directExecutor()); //usually it runs on js-executor-remote-callback thread pool
} }

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNodeConfiguration.java

@ -36,7 +36,6 @@ public class TbMsgGeneratorNodeConfiguration implements NodeConfiguration<TbMsgG
private ScriptLanguage scriptLang; private ScriptLanguage scriptLang;
private String jsScript; private String jsScript;
private String tbelScript; private String tbelScript;
private String queueName;
@Override @Override
public TbMsgGeneratorNodeConfiguration defaultConfiguration() { public TbMsgGeneratorNodeConfiguration defaultConfiguration() {

5
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/deduplication/TbMsgDeduplicationNode.java

@ -47,6 +47,7 @@ import java.util.concurrent.TimeUnit;
type = ComponentType.TRANSFORMATION, type = ComponentType.TRANSFORMATION,
name = "deduplication", name = "deduplication",
configClazz = TbMsgDeduplicationNodeConfiguration.class, configClazz = TbMsgDeduplicationNodeConfiguration.class,
hasQueueName = true,
nodeDescription = "Deduplicate messages within the same originator entity for a configurable period " + nodeDescription = "Deduplicate messages within the same originator entity for a configurable period " +
"based on a specified deduplication strategy.", "based on a specified deduplication strategy.",
nodeDetails = "Deduplication strategies: <ul><li><strong>FIRST</strong> - return first message that arrived during deduplication period.</li>" + nodeDetails = "Deduplication strategies: <ul><li><strong>FIRST</strong> - return first message that arrived during deduplication period.</li>" +
@ -66,6 +67,7 @@ public class TbMsgDeduplicationNode implements TbNode {
private final Map<EntityId, DeduplicationData> deduplicationMap; private final Map<EntityId, DeduplicationData> deduplicationMap;
private long deduplicationInterval; private long deduplicationInterval;
private String queueName;
public TbMsgDeduplicationNode() { public TbMsgDeduplicationNode() {
this.deduplicationMap = new HashMap<>(); this.deduplicationMap = new HashMap<>();
@ -75,6 +77,7 @@ public class TbMsgDeduplicationNode implements TbNode {
public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
this.config = TbNodeUtils.convert(configuration, TbMsgDeduplicationNodeConfiguration.class); this.config = TbNodeUtils.convert(configuration, TbMsgDeduplicationNodeConfiguration.class);
this.deduplicationInterval = TimeUnit.SECONDS.toMillis(config.getInterval()); this.deduplicationInterval = TimeUnit.SECONDS.toMillis(config.getInterval());
this.queueName = ctx.getSelf().getQueueName();
} }
@Override @Override
@ -132,7 +135,7 @@ public class TbMsgDeduplicationNode implements TbNode {
} }
} }
deduplicationResults.add(TbMsg.newMsg( deduplicationResults.add(TbMsg.newMsg(
config.getQueueName(), queueName,
config.getOutMsgType(), config.getOutMsgType(),
deduplicationId, deduplicationId,
getMetadata(), getMetadata(),

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/deduplication/TbMsgDeduplicationNodeConfiguration.java

@ -26,7 +26,6 @@ public class TbMsgDeduplicationNodeConfiguration implements NodeConfiguration<Tb
// only for DeduplicationStrategy.ALL: // only for DeduplicationStrategy.ALL:
private String outMsgType; private String outMsgType;
private String queueName;
// Advanced settings: // Advanced settings:
private int maxPendingMsgs; private int maxPendingMsgs;

9
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbCheckpointNode.java

@ -16,6 +16,7 @@
package org.thingsboard.rule.engine.flow; package org.thingsboard.rule.engine.flow;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.thingsboard.rule.engine.api.EmptyNodeConfiguration;
import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.RuleNode;
import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNode;
@ -30,11 +31,12 @@ import org.thingsboard.server.common.msg.TbMsg;
@RuleNode( @RuleNode(
type = ComponentType.FLOW, type = ComponentType.FLOW,
name = "checkpoint", name = "checkpoint",
configClazz = TbCheckpointNodeConfiguration.class, configClazz = EmptyNodeConfiguration.class,
hasQueueName = true,
nodeDescription = "transfers the message to another queue", nodeDescription = "transfers the message to another queue",
nodeDetails = "After successful transfer incoming message is automatically acknowledged. Queue name is configurable.", nodeDetails = "After successful transfer incoming message is automatically acknowledged. Queue name is configurable.",
uiResources = {"static/rulenode/rulenode-core-config.js"}, uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbActionNodeCheckPointConfig" configDirective = "tbNodeEmptyConfig"
) )
public class TbCheckpointNode implements TbNode { public class TbCheckpointNode implements TbNode {
@ -42,8 +44,7 @@ public class TbCheckpointNode implements TbNode {
@Override @Override
public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
TbCheckpointNodeConfiguration config = TbNodeUtils.convert(configuration, TbCheckpointNodeConfiguration.class); this.queueName = ctx.getSelf().getQueueName();
this.queueName = config.getQueueName();
} }
@Override @Override

30
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbCheckpointNodeConfiguration.java

@ -1,30 +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.rule.engine.flow;
import lombok.Data;
import org.thingsboard.rule.engine.api.NodeConfiguration;
@Data
public class TbCheckpointNodeConfiguration implements NodeConfiguration<TbCheckpointNodeConfiguration> {
private String queueName;
@Override
public TbCheckpointNodeConfiguration defaultConfiguration() {
return new TbCheckpointNodeConfiguration();
}
}

17
rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbMsgDeduplicationNodeTest.java

@ -40,6 +40,7 @@ import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.msg.TbMsgType;
import org.thingsboard.server.common.data.msg.TbNodeConnectionType; import org.thingsboard.server.common.data.msg.TbNodeConnectionType;
import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.TbMsgMetaData;
@ -241,7 +242,7 @@ public class TbMsgDeduplicationNodeTest {
config.setInterval(deduplicationInterval); config.setInterval(deduplicationInterval);
config.setStrategy(DeduplicationStrategy.ALL); config.setStrategy(DeduplicationStrategy.ALL);
config.setOutMsgType(TbMsgType.POST_ATTRIBUTES_REQUEST.name()); config.setOutMsgType(TbMsgType.POST_ATTRIBUTES_REQUEST.name());
config.setQueueName(DataConstants.HP_QUEUE_NAME); setQueueName(DataConstants.HP_QUEUE_NAME);
nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config));
node.init(ctx, nodeConfiguration); node.init(ctx, nodeConfiguration);
@ -268,7 +269,7 @@ public class TbMsgDeduplicationNodeTest {
Assertions.assertEquals(getMergedData(inputMsgs), outMessage.getData()); Assertions.assertEquals(getMergedData(inputMsgs), outMessage.getData());
Assertions.assertEquals(deviceId, outMessage.getOriginator()); Assertions.assertEquals(deviceId, outMessage.getOriginator());
Assertions.assertEquals(config.getOutMsgType(), outMessage.getType()); Assertions.assertEquals(config.getOutMsgType(), outMessage.getType());
Assertions.assertEquals(config.getQueueName(), outMessage.getQueueName()); Assertions.assertEquals(DataConstants.HP_QUEUE_NAME, outMessage.getQueueName());
} }
@Test @Test
@ -281,7 +282,7 @@ public class TbMsgDeduplicationNodeTest {
config.setInterval(deduplicationInterval); config.setInterval(deduplicationInterval);
config.setStrategy(DeduplicationStrategy.ALL); config.setStrategy(DeduplicationStrategy.ALL);
config.setOutMsgType(TbMsgType.POST_ATTRIBUTES_REQUEST.name()); config.setOutMsgType(TbMsgType.POST_ATTRIBUTES_REQUEST.name());
config.setQueueName(DataConstants.HP_QUEUE_NAME); setQueueName(DataConstants.HP_QUEUE_NAME);
nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config));
node.init(ctx, nodeConfiguration); node.init(ctx, nodeConfiguration);
@ -316,13 +317,13 @@ public class TbMsgDeduplicationNodeTest {
Assertions.assertEquals(getMergedData(firstMsgPack), firstMsg.getData()); Assertions.assertEquals(getMergedData(firstMsgPack), firstMsg.getData());
Assertions.assertEquals(deviceId, firstMsg.getOriginator()); Assertions.assertEquals(deviceId, firstMsg.getOriginator());
Assertions.assertEquals(config.getOutMsgType(), firstMsg.getType()); Assertions.assertEquals(config.getOutMsgType(), firstMsg.getType());
Assertions.assertEquals(config.getQueueName(), firstMsg.getQueueName()); Assertions.assertEquals(DataConstants.HP_QUEUE_NAME, firstMsg.getQueueName());
TbMsg secondMsg = resultMsgs.get(1); TbMsg secondMsg = resultMsgs.get(1);
Assertions.assertEquals(getMergedData(secondMsgPack), secondMsg.getData()); Assertions.assertEquals(getMergedData(secondMsgPack), secondMsg.getData());
Assertions.assertEquals(deviceId, secondMsg.getOriginator()); Assertions.assertEquals(deviceId, secondMsg.getOriginator());
Assertions.assertEquals(config.getOutMsgType(), secondMsg.getType()); Assertions.assertEquals(config.getOutMsgType(), secondMsg.getType());
Assertions.assertEquals(config.getQueueName(), secondMsg.getQueueName()); Assertions.assertEquals(DataConstants.HP_QUEUE_NAME, secondMsg.getQueueName());
} }
@Test @Test
@ -429,4 +430,10 @@ public class TbMsgDeduplicationNodeTest {
return JacksonUtil.toString(mergedData); return JacksonUtil.toString(mergedData);
} }
private void setQueueName(String queueName) {
RuleNode ruleNode = new RuleNode();
ruleNode.setQueueName(queueName);
when(ctx.getSelf()).thenReturn(ruleNode);
}
} }

Loading…
Cancel
Save