Browse Source

remove duplicate logic for TbGetOriginatorFieldsNode and TbRelatedAttributesNode && fix typos

pull/8661/head
ShvaykaD 3 years ago
parent
commit
2c251e2f5c
  1. 9
      application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java
  2. 20
      dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java
  3. 2
      dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesLatestDao.java
  4. 171
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetEntityAttrNode.java
  5. 89
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetEntityDataNode.java
  6. 153
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetMappedDataNode.java
  7. 10
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNode.java
  8. 14
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetEntityDataNodeConfiguration.java
  9. 29
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetMappedDataNodeConfiguration.java
  10. 12
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsConfiguration.java
  11. 65
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNode.java
  12. 12
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNode.java
  13. 12
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedDataNodeConfiguration.java
  14. 10
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNode.java
  15. 16
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNodeTest.java
  16. 25
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNodeTest.java
  17. 45
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNodeTest.java
  18. 16
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNodeTest.java

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

@ -1,12 +1,12 @@
/**
* Copyright © 2016-2023 The Thingsboard Authors
*
* <p>
* 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
*
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.
@ -277,6 +277,7 @@ public class DefaultDataUpdateService implements DataUpdateService {
log.error("Unexpected error during rule nodes upgrade: ", e);
}
}
private ArrayList<TbVersionedNode> getTbVersionedNodes() {
var ruleNodeDefinitions = beanDiscoveryService.discoverBeansByAnnotationType(
org.thingsboard.rule.engine.api.RuleNode.class

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

@ -1,12 +1,12 @@
/**
* Copyright © 2016-2023 The Thingsboard Authors
*
* <p>
* 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
*
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.
@ -207,14 +207,14 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
TbPair<Boolean, JsonNode> upgradeResult = tbVersionedNode.upgrade(fromVersion, node.getConfiguration());
if (upgradeResult.getFirst()) {
node.setConfiguration(upgradeResult.getSecond());
log.info("Successfully upgrade rule node with id: {} type: {}, rule chain id: {} fromVersion: {} toVersion: {}",
ruleNodeId,
ruleNodeType,
ruleChainId,
fromVersion,
toVersion);
}
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,

2
dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesLatestDao.java

@ -75,7 +75,7 @@ public class CassandraBaseTimeseriesLatestDao extends AbstractCassandraBaseTimes
try {
return findLatest(tenantId, entityId, key, rs -> convertResultToTsKvEntry(key, rs.one())).get();
} catch (InterruptedException | ExecutionException e) {
log.error("[{}][{}] Failed to get latest entry for key: {}", tenantId, entityId, key, e);
log.error("[{}][{}] Failed to get latest entry for key: {} due to: ", tenantId, entityId, key, e);
throw new RuntimeException(e);
}
}

171
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetEntityAttrNode.java

@ -1,171 +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.metadata;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.rule.engine.api.util.TbNodeUtils;
import org.thingsboard.rule.engine.util.EntitiesFieldsAsyncLoader;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.kv.KvEntry;
import org.thingsboard.server.common.data.util.TbPair;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static org.thingsboard.common.util.DonAsynchron.withCallback;
import static org.thingsboard.server.common.data.DataConstants.SERVER_SCOPE;
@Slf4j
public abstract class TbAbstractGetEntityAttrNode<T extends EntityId> extends TbAbstractNodeWithFetchTo<TbGetEntityAttrNodeConfiguration> {
protected final static String DATA_TO_FETCH_PROPERTY_NAME = "dataToFetch";
protected static final String OLD_PROPERTY_NAME = "telemetry";
@Override
public void onMsg(TbContext ctx, TbMsg msg) {
var msgDataAsObjectNode = FetchTo.DATA.equals(fetchTo) ? getMsgDataAsObjectNode(msg) : null;
withCallback(findEntityAsync(ctx, msg.getOriginator()),
entityId -> getData(ctx, msg, entityId, msgDataAsObjectNode),
t -> ctx.tellFailure(msg, t), ctx.getDbCallbackExecutor());
}
protected abstract ListenableFuture<T> findEntityAsync(TbContext ctx, EntityId originator);
protected void checkIfMappingIsNotEmptyOrElseThrow(Map<String, String> attrMapping) throws TbNodeException {
if (attrMapping == null || attrMapping.isEmpty()) {
throw new TbNodeException("At least one mapping entry should be specified!");
}
}
protected abstract void checkDataToFetchSupportedOrElseThrow(DataToFetch dataToFetch) throws TbNodeException;
private void getData(TbContext ctx, TbMsg msg, T entityId, ObjectNode msgDataAsJsonNode) {
var mappingsMap = new HashMap<String, String>();
if (DataToFetch.FIELDS.equals(config.getDataToFetch())) {
config.getAttrMapping().forEach((sourceField, targetKey) -> {
String patternProcessedTargetKey = TbNodeUtils.processPattern(targetKey, msg);
mappingsMap.put(sourceField, patternProcessedTargetKey);
});
withCallback(collectMappedEntityFieldsAsync(ctx, entityId, mappingsMap),
targetKeysToSourceValuesMap -> {
TbMsgMetaData msgMetaData = msg.getMetaData().copy();
for (var entry : targetKeysToSourceValuesMap.entrySet()) {
var targetKeyName = entry.getKey();
var sourceFieldValue = entry.getValue();
if (FetchTo.DATA.equals(fetchTo)) {
msgDataAsJsonNode.put(targetKeyName, sourceFieldValue);
} else if (FetchTo.METADATA.equals(fetchTo)) {
msgMetaData.putValue(targetKeyName, sourceFieldValue);
}
}
TbMsg outMsg = transformMessage(msg, msgDataAsJsonNode, msgMetaData);
ctx.tellSuccess(outMsg);
},
t -> ctx.tellFailure(msg, t),
MoreExecutors.directExecutor());
} else {
config.getAttrMapping().forEach((sourceKey, targetKey) -> {
String patternProcessedSourceKey = TbNodeUtils.processPattern(sourceKey, msg);
String patternProcessedTargetKey = TbNodeUtils.processPattern(targetKey, msg);
mappingsMap.put(patternProcessedSourceKey, patternProcessedTargetKey);
});
var sourceKeys = List.copyOf(mappingsMap.keySet());
withCallback(DataToFetch.LATEST_TELEMETRY.equals(config.getDataToFetch()) ?
getLatestTelemetryAsync(ctx, entityId, sourceKeys) :
getAttributesAsync(ctx, entityId, sourceKeys),
data -> putDataAndTell(ctx, msg, data, mappingsMap, msgDataAsJsonNode),
t -> ctx.tellFailure(msg, t),
MoreExecutors.directExecutor());
}
}
private ListenableFuture<Map<String, String>> collectMappedEntityFieldsAsync(TbContext ctx, EntityId entityId, HashMap<String, String> mappingsMap) {
return Futures.transform(EntitiesFieldsAsyncLoader.findAsync(ctx, entityId),
fieldsData -> {
var targetKeysToSourceValuesMap = new HashMap<String, String>();
for (var mappingEntry : mappingsMap.entrySet()) {
var sourceFieldName = mappingEntry.getKey();
var targetKeyName = mappingEntry.getValue();
var sourceFieldValue = fieldsData.getFieldValue(sourceFieldName, true);
if (sourceFieldValue != null) {
targetKeysToSourceValuesMap.put(targetKeyName, sourceFieldValue);
}
}
return targetKeysToSourceValuesMap;
}, ctx.getDbCallbackExecutor()
);
}
private ListenableFuture<List<KvEntry>> getAttributesAsync(TbContext ctx, EntityId entityId, List<String> attrKeys) {
var latest = ctx.getAttributesService().find(ctx.getTenantId(), entityId, SERVER_SCOPE, attrKeys);
return Futures.transform(latest, l ->
l.stream()
.map(i -> (KvEntry) i)
.collect(Collectors.toList()),
ctx.getDbCallbackExecutor());
}
private ListenableFuture<List<KvEntry>> getLatestTelemetryAsync(TbContext ctx, EntityId entityId, List<String> timeseriesKeys) {
var latest = ctx.getTimeseriesService().findLatest(ctx.getTenantId(), entityId, timeseriesKeys);
return Futures.transform(latest, l ->
l.stream()
.map(i -> (KvEntry) i)
.collect(Collectors.toList()),
ctx.getDbCallbackExecutor());
}
private void putDataAndTell(TbContext ctx, TbMsg msg, List<? extends KvEntry> data, Map<String, String> map, ObjectNode msgData) {
var msgMetaData = msg.getMetaData().copy();
for (KvEntry entry : data) {
String targetKey = map.get(entry.getKey());
enrichMessage(msgData, msgMetaData, entry, targetKey);
}
ctx.tellSuccess(transformMessage(msg, msgData, msgMetaData));
}
protected TbPair<Boolean, JsonNode> upgradeToUseFetchToAndDataToFetch(JsonNode oldConfiguration) throws TbNodeException {
var newConfigObjectNode = (ObjectNode) oldConfiguration;
if (!newConfigObjectNode.has(OLD_PROPERTY_NAME)) {
throw new TbNodeException("property to update: '" + OLD_PROPERTY_NAME + "' doesn't exists in configuration!");
}
var value = newConfigObjectNode.get(OLD_PROPERTY_NAME).asText();
if ("true".equals(value)) {
newConfigObjectNode.remove(OLD_PROPERTY_NAME);
newConfigObjectNode.put(DATA_TO_FETCH_PROPERTY_NAME, DataToFetch.LATEST_TELEMETRY.name());
} else if ("false".equals(value)) {
newConfigObjectNode.remove(OLD_PROPERTY_NAME);
newConfigObjectNode.put(DATA_TO_FETCH_PROPERTY_NAME, DataToFetch.ATTRIBUTES.name());
} else {
throw new TbNodeException("property to update: '" + OLD_PROPERTY_NAME + "' has unexpected value: " + value + ". Allowed values: true or false!");
}
newConfigObjectNode.put(FETCH_TO_PROPERTY_NAME, FetchTo.METADATA.name());
return new TbPair<>(true, newConfigObjectNode);
}
}

89
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetEntityDataNode.java

@ -0,0 +1,89 @@
/**
* 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.metadata;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.util.TbPair;
import org.thingsboard.server.common.msg.TbMsg;
import static org.thingsboard.common.util.DonAsynchron.withCallback;
@Slf4j
public abstract class TbAbstractGetEntityDataNode<T extends EntityId> extends TbAbstractGetMappedDataNode<T, TbGetEntityDataNodeConfiguration> {
protected final static String DATA_TO_FETCH_PROPERTY_NAME = "dataToFetch";
protected static final String OLD_DATA_TO_FETCH_PROPERTY_NAME = "telemetry";
protected final static String DATA_MAPPING_PROPERTY_NAME = "dataMapping";
protected static final String OLD_DATA_MAPPING_PROPERTY_NAME = "attrMapping";
@Override
public void onMsg(TbContext ctx, TbMsg msg) {
var msgDataAsObjectNode = FetchTo.DATA.equals(fetchTo) ? getMsgDataAsObjectNode(msg) : null;
withCallback(findEntityAsync(ctx, msg.getOriginator()),
entityId -> processDataAndTell(ctx, msg, entityId, msgDataAsObjectNode),
t -> ctx.tellFailure(msg, t), ctx.getDbCallbackExecutor());
}
protected abstract ListenableFuture<T> findEntityAsync(TbContext ctx, EntityId originator);
protected abstract void checkDataToFetchSupportedOrElseThrow(DataToFetch dataToFetch) throws TbNodeException;
protected void processDataAndTell(TbContext ctx, TbMsg msg, T entityId, ObjectNode msgDataAsJsonNode) {
DataToFetch dataToFetch = config.getDataToFetch();
switch (dataToFetch) {
case ATTRIBUTES:
processAttributesKvEntryData(ctx, msg, entityId, msgDataAsJsonNode);
break;
case LATEST_TELEMETRY:
processTsKvEntryData(ctx, msg, entityId, msgDataAsJsonNode);
break;
case FIELDS:
processFieldsData(ctx, msg, entityId, msgDataAsJsonNode, true);
break;
}
}
protected TbPair<Boolean, JsonNode> upgradeToUseFetchToAndDataToFetch(JsonNode oldConfiguration) throws TbNodeException {
var newConfigObjectNode = (ObjectNode) oldConfiguration;
if (!newConfigObjectNode.has(OLD_DATA_TO_FETCH_PROPERTY_NAME)) {
throw new TbNodeException("property to update: '" + OLD_DATA_TO_FETCH_PROPERTY_NAME + "' doesn't exists in configuration!");
}
if (!newConfigObjectNode.has(OLD_DATA_MAPPING_PROPERTY_NAME)) {
throw new TbNodeException("property to update: '" + OLD_DATA_MAPPING_PROPERTY_NAME + "' doesn't exists in configuration!");
}
newConfigObjectNode.set(DATA_MAPPING_PROPERTY_NAME, newConfigObjectNode.get(OLD_DATA_MAPPING_PROPERTY_NAME));
newConfigObjectNode.remove(OLD_DATA_MAPPING_PROPERTY_NAME);
var value = newConfigObjectNode.get(OLD_DATA_TO_FETCH_PROPERTY_NAME).asText();
if ("true".equals(value)) {
newConfigObjectNode.remove(OLD_DATA_TO_FETCH_PROPERTY_NAME);
newConfigObjectNode.put(DATA_TO_FETCH_PROPERTY_NAME, DataToFetch.LATEST_TELEMETRY.name());
} else if ("false".equals(value)) {
newConfigObjectNode.remove(OLD_DATA_TO_FETCH_PROPERTY_NAME);
newConfigObjectNode.put(DATA_TO_FETCH_PROPERTY_NAME, DataToFetch.ATTRIBUTES.name());
} else {
throw new TbNodeException("property to update: '" + OLD_DATA_TO_FETCH_PROPERTY_NAME + "' has unexpected value: " + value + ". Allowed values: true or false!");
}
newConfigObjectNode.put(FETCH_TO_PROPERTY_NAME, FetchTo.METADATA.name());
return new TbPair<>(true, newConfigObjectNode);
}
}

153
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetMappedDataNode.java

@ -0,0 +1,153 @@
/**
* 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.metadata;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.rule.engine.api.util.TbNodeUtils;
import org.thingsboard.rule.engine.util.EntitiesFieldsAsyncLoader;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.kv.KvEntry;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static org.thingsboard.common.util.DonAsynchron.withCallback;
import static org.thingsboard.server.common.data.DataConstants.SERVER_SCOPE;
@Slf4j
public abstract class TbAbstractGetMappedDataNode<T extends EntityId, C extends TbGetMappedDataNodeConfiguration> extends TbAbstractNodeWithFetchTo<C> {
protected void checkIfMappingIsNotEmptyOrElseThrow(Map<String, String> dataMapping) throws TbNodeException {
if (dataMapping == null || dataMapping.isEmpty()) {
throw new TbNodeException("At least one mapping entry should be specified!");
}
}
protected void processFieldsData(TbContext ctx, TbMsg msg, T entityId, ObjectNode msgDataAsJsonNode, boolean ignoreNullStrings) {
var mappingsMap = processFieldsMappingPatterns(msg);
withCallback(getEntityFieldsAsync(ctx, entityId, mappingsMap, ignoreNullStrings),
data -> putFieldsDataAndTell(ctx, msg, msgDataAsJsonNode, data),
t -> ctx.tellFailure(msg, t),
MoreExecutors.directExecutor());
}
protected void processAttributesKvEntryData(TbContext ctx, TbMsg msg, T entityId, ObjectNode msgDataAsJsonNode) {
var mappingsMap = processKvEntryMappingPatterns(msg);
var sourceKeys = List.copyOf(mappingsMap.keySet());
withCallback(getAttributesAsync(ctx, entityId, sourceKeys),
data -> putKvEntryDataAndTell(ctx, msg, data, mappingsMap, msgDataAsJsonNode),
t -> ctx.tellFailure(msg, t),
MoreExecutors.directExecutor());
}
protected void processTsKvEntryData(TbContext ctx, TbMsg msg, T entityId, ObjectNode msgDataAsJsonNode) {
var mappingsMap = processKvEntryMappingPatterns(msg);
var sourceKeys = List.copyOf(mappingsMap.keySet());
withCallback(getLatestTelemetryAsync(ctx, entityId, sourceKeys),
data -> putKvEntryDataAndTell(ctx, msg, data, mappingsMap, msgDataAsJsonNode),
t -> ctx.tellFailure(msg, t),
MoreExecutors.directExecutor());
}
private void putFieldsDataAndTell(TbContext ctx, TbMsg msg, ObjectNode msgDataAsJsonNode, Map<String, String> targetKeysToSourceValuesMap) {
TbMsgMetaData msgMetaData = msg.getMetaData().copy();
for (var entry : targetKeysToSourceValuesMap.entrySet()) {
var targetKeyName = entry.getKey();
var sourceFieldValue = entry.getValue();
if (FetchTo.DATA.equals(fetchTo)) {
msgDataAsJsonNode.put(targetKeyName, sourceFieldValue);
} else if (FetchTo.METADATA.equals(fetchTo)) {
msgMetaData.putValue(targetKeyName, sourceFieldValue);
}
}
TbMsg outMsg = transformMessage(msg, msgDataAsJsonNode, msgMetaData);
ctx.tellSuccess(outMsg);
}
private void putKvEntryDataAndTell(TbContext ctx, TbMsg msg, List<? extends KvEntry> data, Map<String, String> map, ObjectNode msgData) {
var msgMetaData = msg.getMetaData().copy();
for (KvEntry entry : data) {
String targetKey = map.get(entry.getKey());
enrichMessage(msgData, msgMetaData, entry, targetKey);
}
ctx.tellSuccess(transformMessage(msg, msgData, msgMetaData));
}
private Map<String, String> processFieldsMappingPatterns(TbMsg msg) {
var mappingsMap = new HashMap<String, String>();
config.getDataMapping().forEach((sourceField, targetKey) -> {
String patternProcessedTargetKey = TbNodeUtils.processPattern(targetKey, msg);
mappingsMap.put(sourceField, patternProcessedTargetKey);
});
return mappingsMap;
}
private Map<String, String> processKvEntryMappingPatterns(TbMsg msg) {
var mappingsMap = new HashMap<String, String>();
config.getDataMapping().forEach((sourceKey, targetKey) -> {
String patternProcessedSourceKey = TbNodeUtils.processPattern(sourceKey, msg);
String patternProcessedTargetKey = TbNodeUtils.processPattern(targetKey, msg);
mappingsMap.put(patternProcessedSourceKey, patternProcessedTargetKey);
});
return mappingsMap;
}
private ListenableFuture<Map<String, String>> getEntityFieldsAsync(TbContext ctx, EntityId entityId, Map<String, String> mappingsMap, boolean ignoreNullStrings) {
return Futures.transform(EntitiesFieldsAsyncLoader.findAsync(ctx, entityId),
fieldsData -> {
var targetKeysToSourceValuesMap = new HashMap<String, String>();
for (var mappingEntry : mappingsMap.entrySet()) {
var sourceFieldName = mappingEntry.getKey();
var targetKeyName = mappingEntry.getValue();
var sourceFieldValue = fieldsData.getFieldValue(sourceFieldName, ignoreNullStrings);
if (sourceFieldValue != null) {
targetKeysToSourceValuesMap.put(targetKeyName, sourceFieldValue);
}
}
return targetKeysToSourceValuesMap;
}, ctx.getDbCallbackExecutor()
);
}
private ListenableFuture<List<KvEntry>> getAttributesAsync(TbContext ctx, EntityId entityId, List<String> attrKeys) {
var latest = ctx.getAttributesService().find(ctx.getTenantId(), entityId, SERVER_SCOPE, attrKeys);
return Futures.transform(latest, l ->
l.stream()
.map(i -> (KvEntry) i)
.collect(Collectors.toList()),
ctx.getDbCallbackExecutor());
}
private ListenableFuture<List<KvEntry>> getLatestTelemetryAsync(TbContext ctx, EntityId entityId, List<String> timeseriesKeys) {
var latest = ctx.getTimeseriesService().findLatest(ctx.getTenantId(), entityId, timeseriesKeys);
return Futures.transform(latest, l ->
l.stream()
.map(i -> (KvEntry) i)
.collect(Collectors.toList()),
ctx.getDbCallbackExecutor());
}
}

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

@ -34,7 +34,7 @@ import org.thingsboard.server.common.data.util.TbPair;
@RuleNode(
type = ComponentType.ENRICHMENT,
name = "customer attributes",
configClazz = TbGetEntityAttrNodeConfiguration.class,
configClazz = TbGetEntityDataNodeConfiguration.class,
nodeDescription = "Add Originators Customer Attributes or Latest Telemetry into Message or Metadata",
nodeDetails = "Enrich the Message or Metadata with the corresponding customer's latest attributes or telemetry value. " +
"The customer is selected based on the originator of the message: device, asset, etc. " +
@ -42,14 +42,14 @@ import org.thingsboard.server.common.data.util.TbPair;
"Useful when you store some parameters on the customer level and would like to use them for message processing.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbEnrichmentNodeCustomerAttributesConfig")
public class TbGetCustomerAttributeNode extends TbAbstractGetEntityAttrNode<CustomerId> {
public class TbGetCustomerAttributeNode extends TbAbstractGetEntityDataNode<CustomerId> {
private static final String CUSTOMER_NOT_FOUND_MESSAGE = "Failed to find customer for entity with id %s and type %s";
@Override
protected TbGetEntityAttrNodeConfiguration loadNodeConfiguration(TbNodeConfiguration configuration) throws TbNodeException {
var config = TbNodeUtils.convert(configuration, TbGetEntityAttrNodeConfiguration.class);
checkIfMappingIsNotEmptyOrElseThrow(config.getAttrMapping());
protected TbGetEntityDataNodeConfiguration loadNodeConfiguration(TbNodeConfiguration configuration) throws TbNodeException {
var config = TbNodeUtils.convert(configuration, TbGetEntityDataNodeConfiguration.class);
checkIfMappingIsNotEmptyOrElseThrow(config.getDataMapping());
checkDataToFetchSupportedOrElseThrow(config.getDataToFetch());
return config;
}

14
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetEntityAttrNodeConfiguration.java → rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetEntityDataNodeConfiguration.java

@ -20,21 +20,19 @@ import lombok.EqualsAndHashCode;
import org.thingsboard.rule.engine.api.NodeConfiguration;
import java.util.HashMap;
import java.util.Map;
@Data
@EqualsAndHashCode(callSuper = true)
public class TbGetEntityAttrNodeConfiguration extends TbAbstractFetchToNodeConfiguration implements NodeConfiguration<TbGetEntityAttrNodeConfiguration> {
public class TbGetEntityDataNodeConfiguration extends TbGetMappedDataNodeConfiguration implements NodeConfiguration<TbGetEntityDataNodeConfiguration> {
private Map<String, String> attrMapping;
private DataToFetch dataToFetch;
@Override
public TbGetEntityAttrNodeConfiguration defaultConfiguration() {
var configuration = new TbGetEntityAttrNodeConfiguration();
var attrMapping = new HashMap<String, String>();
attrMapping.putIfAbsent("alarmThreshold", "threshold");
configuration.setAttrMapping(attrMapping);
public TbGetEntityDataNodeConfiguration defaultConfiguration() {
var configuration = new TbGetEntityDataNodeConfiguration();
var dataMapping = new HashMap<String, String>();
dataMapping.putIfAbsent("alarmThreshold", "threshold");
configuration.setDataMapping(dataMapping);
configuration.setDataToFetch(DataToFetch.ATTRIBUTES);
configuration.setFetchTo(FetchTo.METADATA);
return configuration;

29
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetMappedDataNodeConfiguration.java

@ -0,0 +1,29 @@
/**
* 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.metadata;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Map;
@Data
@EqualsAndHashCode(callSuper = true)
public abstract class TbGetMappedDataNodeConfiguration extends TbAbstractFetchToNodeConfiguration {
private Map<String, String> dataMapping;
}

12
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsConfiguration.java

@ -24,18 +24,18 @@ import java.util.Map;
@Data
@EqualsAndHashCode(callSuper = true)
public class TbGetOriginatorFieldsConfiguration extends TbAbstractFetchToNodeConfiguration implements NodeConfiguration<TbGetOriginatorFieldsConfiguration> {
public class TbGetOriginatorFieldsConfiguration extends TbGetMappedDataNodeConfiguration implements NodeConfiguration<TbGetOriginatorFieldsConfiguration> {
private Map<String, String> fieldsMapping;
private Map<String, String> dataMapping;
private boolean ignoreNullStrings;
@Override
public TbGetOriginatorFieldsConfiguration defaultConfiguration() {
var configuration = new TbGetOriginatorFieldsConfiguration();
var fieldsMapping = new HashMap<String, String>();
fieldsMapping.put("name", "originatorName");
fieldsMapping.put("type", "originatorType");
configuration.setFieldsMapping(fieldsMapping);
var dataMapping = new HashMap<String, String>();
dataMapping.put("name", "originatorName");
dataMapping.put("type", "originatorType");
configuration.setDataMapping(dataMapping);
configuration.setIgnoreNullStrings(false);
configuration.setFetchTo(FetchTo.METADATA);
return configuration;

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

@ -17,26 +17,18 @@ package org.thingsboard.rule.engine.metadata;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.rule.engine.api.RuleNode;
import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.rule.engine.api.TbNodeConfiguration;
import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.rule.engine.api.util.TbNodeUtils;
import org.thingsboard.rule.engine.util.EntitiesFieldsAsyncLoader;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.data.util.TbPair;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import java.util.HashMap;
import java.util.Map;
import static org.thingsboard.common.util.DonAsynchron.withCallback;
import java.util.concurrent.ExecutionException;
/**
* Created by ashvayka on 19.01.18.
@ -50,60 +42,33 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback;
"This node supports only following originator types: TENANT, CUSTOMER, USER, ASSET, DEVICE, ALARM, RULE_CHAIN, ENTITY_VIEW.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbEnrichmentNodeOriginatorFieldsConfig")
public class TbGetOriginatorFieldsNode extends TbAbstractNodeWithFetchTo<TbGetOriginatorFieldsConfiguration> {
public class TbGetOriginatorFieldsNode extends TbAbstractGetMappedDataNode<EntityId, TbGetOriginatorFieldsConfiguration> {
protected final static String DATA_MAPPING_PROPERTY_NAME = "dataMapping";
protected static final String OLD_DATA_MAPPING_PROPERTY_NAME = "fieldsMapping";
@Override
protected TbGetOriginatorFieldsConfiguration loadNodeConfiguration(TbNodeConfiguration configuration) throws TbNodeException {
var config = TbNodeUtils.convert(configuration, TbGetOriginatorFieldsConfiguration.class);
if (config.getFieldsMapping() == null || config.getFieldsMapping().isEmpty()) {
throw new TbNodeException("At least one mapping entry should be specified!");
}
checkIfMappingIsNotEmptyOrElseThrow(config.getDataMapping());
return config;
}
@Override
public void onMsg(TbContext ctx, TbMsg msg) {
var msgDataAsObjectNode = FetchTo.DATA.equals(fetchTo) ? getMsgDataAsObjectNode(msg) : null;
withCallback(collectMappedEntityFieldsAsync(ctx, msg.getOriginator()),
targetKeysToSourceValuesMap -> {
TbMsgMetaData msgMetaData = msg.getMetaData().copy();
for (var entry : targetKeysToSourceValuesMap.entrySet()) {
var targetKeyName = entry.getKey();
var sourceFieldValue = entry.getValue();
if (FetchTo.DATA.equals(fetchTo)) {
msgDataAsObjectNode.put(targetKeyName, sourceFieldValue);
} else if (FetchTo.METADATA.equals(fetchTo)) {
msgMetaData.putValue(targetKeyName, sourceFieldValue);
}
}
TbMsg outMsg = transformMessage(msg, msgDataAsObjectNode, msgMetaData);
ctx.tellSuccess(outMsg);
},
t -> ctx.tellFailure(msg, t),
MoreExecutors.directExecutor());
}
private ListenableFuture<Map<String, String>> collectMappedEntityFieldsAsync(TbContext ctx, EntityId entityId) {
return Futures.transform(EntitiesFieldsAsyncLoader.findAsync(ctx, entityId),
fieldsData -> {
var targetKeysToSourceValuesMap = new HashMap<String, String>();
for (var mappingEntry : config.getFieldsMapping().entrySet()) {
var sourceFieldName = mappingEntry.getKey();
var targetKeyName = mappingEntry.getValue();
var sourceFieldValue = fieldsData.getFieldValue(sourceFieldName, config.isIgnoreNullStrings());
if (sourceFieldValue != null) {
targetKeysToSourceValuesMap.put(targetKeyName, sourceFieldValue);
}
}
return targetKeysToSourceValuesMap;
}, ctx.getDbCallbackExecutor()
);
public void onMsg(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException, TbNodeException {
var msgDataAsJsonNode = FetchTo.DATA.equals(fetchTo) ? getMsgDataAsObjectNode(msg) : null;
processFieldsData(ctx, msg, msg.getOriginator(), msgDataAsJsonNode, config.isIgnoreNullStrings());
}
@Override
public TbPair<Boolean, JsonNode> upgrade(int fromVersion, JsonNode oldConfiguration) {
public TbPair<Boolean, JsonNode> upgrade(int fromVersion, JsonNode oldConfiguration) throws TbNodeException {
if (fromVersion == 0) {
var newConfigObjectNode = (ObjectNode) oldConfiguration;
if (!newConfigObjectNode.has(OLD_DATA_MAPPING_PROPERTY_NAME)) {
throw new TbNodeException("property to update: '" + OLD_DATA_MAPPING_PROPERTY_NAME + "' doesn't exists in configuration!");
}
newConfigObjectNode.set(DATA_MAPPING_PROPERTY_NAME, newConfigObjectNode.get(OLD_DATA_MAPPING_PROPERTY_NAME));
newConfigObjectNode.remove(OLD_DATA_MAPPING_PROPERTY_NAME);
newConfigObjectNode.put(FETCH_TO_PROPERTY_NAME, FetchTo.METADATA.name());
return new TbPair<>(true, newConfigObjectNode);
}

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

@ -35,7 +35,7 @@ import java.util.Arrays;
@RuleNode(
type = ComponentType.ENRICHMENT,
name = "related attributes",
configClazz = TbGetRelatedAttrNodeConfiguration.class,
configClazz = TbGetRelatedDataNodeConfiguration.class,
nodeDescription = "Add Originators Related Entity Attributes or Latest Telemetry into Message Metadata/Data",
nodeDetails = "Related Entity found using configured relation direction and Relation Type. " +
"If multiple Related Entities are found, only first Entity is used for attributes enrichment, other entities are discarded. " +
@ -45,21 +45,21 @@ import java.util.Arrays;
"<code>metadata.temperature</code>.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbEnrichmentNodeRelatedAttributesConfig")
public class TbGetRelatedAttributeNode extends TbAbstractGetEntityAttrNode<EntityId> {
public class TbGetRelatedAttributeNode extends TbAbstractGetEntityDataNode<EntityId> {
private static final String RELATED_ENTITY_NOT_FOUND_MESSAGE = "Failed to find related entity to message originator using relation query specified in the configuration!";
@Override
public TbGetRelatedAttrNodeConfiguration loadNodeConfiguration(TbNodeConfiguration configuration) throws TbNodeException {
var config = TbNodeUtils.convert(configuration, TbGetRelatedAttrNodeConfiguration.class);
checkIfMappingIsNotEmptyOrElseThrow(config.getAttrMapping());
public TbGetRelatedDataNodeConfiguration loadNodeConfiguration(TbNodeConfiguration configuration) throws TbNodeException {
var config = TbNodeUtils.convert(configuration, TbGetRelatedDataNodeConfiguration.class);
checkIfMappingIsNotEmptyOrElseThrow(config.getDataMapping());
checkDataToFetchSupportedOrElseThrow(config.getDataToFetch());
return config;
}
@Override
public ListenableFuture<EntityId> findEntityAsync(TbContext ctx, EntityId originator) {
var relatedAttrConfig = (TbGetRelatedAttrNodeConfiguration) config;
var relatedAttrConfig = (TbGetRelatedDataNodeConfiguration) config;
return Futures.transformAsync(
EntitiesRelatedEntityIdAsyncLoader.findEntityAsync(ctx, originator, relatedAttrConfig.getRelationsQuery()),
checkIfEntityIsPresentOrThrow(RELATED_ENTITY_NOT_FOUND_MESSAGE),

12
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttrNodeConfiguration.java → rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedDataNodeConfiguration.java

@ -27,16 +27,16 @@ import java.util.HashMap;
@Data
@EqualsAndHashCode(callSuper = true)
public class TbGetRelatedAttrNodeConfiguration extends TbGetEntityAttrNodeConfiguration {
public class TbGetRelatedDataNodeConfiguration extends TbGetEntityDataNodeConfiguration {
private RelationsQuery relationsQuery;
@Override
public TbGetRelatedAttrNodeConfiguration defaultConfiguration() {
var configuration = new TbGetRelatedAttrNodeConfiguration();
var attrMapping = new HashMap<String, String>();
attrMapping.putIfAbsent("serialNumber", "sn");
configuration.setAttrMapping(attrMapping);
public TbGetRelatedDataNodeConfiguration defaultConfiguration() {
var configuration = new TbGetRelatedDataNodeConfiguration();
var dataMapping = new HashMap<String, String>();
dataMapping.putIfAbsent("serialNumber", "sn");
configuration.setDataMapping(dataMapping);
configuration.setDataToFetch(DataToFetch.ATTRIBUTES);
configuration.setFetchTo(FetchTo.METADATA);

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

@ -33,7 +33,7 @@ import org.thingsboard.server.common.data.util.TbPair;
@RuleNode(
type = ComponentType.ENRICHMENT,
name = "tenant attributes",
configClazz = TbGetEntityAttrNodeConfiguration.class,
configClazz = TbGetEntityDataNodeConfiguration.class,
nodeDescription = "Add Originators Tenant Attributes or Latest Telemetry into Message Metadata/Data",
nodeDetails = "If Attributes enrichment configured, server scope attributes are added into Message Metadata/Data. " +
"If Latest Telemetry enrichment configured, latest telemetry added into Metadata/Data. " +
@ -41,12 +41,12 @@ import org.thingsboard.server.common.data.util.TbPair;
"<code>metadata.temperature</code>.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbEnrichmentNodeTenantAttributesConfig")
public class TbGetTenantAttributeNode extends TbAbstractGetEntityAttrNode<TenantId> {
public class TbGetTenantAttributeNode extends TbAbstractGetEntityDataNode<TenantId> {
@Override
public TbGetEntityAttrNodeConfiguration loadNodeConfiguration(TbNodeConfiguration configuration) throws TbNodeException {
var config = TbNodeUtils.convert(configuration, TbGetEntityAttrNodeConfiguration.class);
checkIfMappingIsNotEmptyOrElseThrow(config.getAttrMapping());
public TbGetEntityDataNodeConfiguration loadNodeConfiguration(TbNodeConfiguration configuration) throws TbNodeException {
var config = TbNodeUtils.convert(configuration, TbGetEntityDataNodeConfiguration.class);
checkIfMappingIsNotEmptyOrElseThrow(config.getDataMapping());
checkDataToFetchSupportedOrElseThrow(config.getDataToFetch());
return config;
}

16
rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNodeTest.java

@ -112,14 +112,14 @@ public class TbGetCustomerAttributeNodeTest {
@Mock
private DeviceService deviceServiceMock;
private TbGetCustomerAttributeNode node;
private TbGetEntityAttrNodeConfiguration config;
private TbGetEntityDataNodeConfiguration config;
private TbNodeConfiguration nodeConfiguration;
private TbMsg msg;
@BeforeEach
public void setUp() {
node = new TbGetCustomerAttributeNode();
config = new TbGetEntityAttrNodeConfiguration().defaultConfiguration();
config = new TbGetEntityDataNodeConfiguration().defaultConfiguration();
nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config));
}
@ -174,7 +174,7 @@ public class TbGetCustomerAttributeNodeTest {
// THEN
assertThat(node.config).isEqualTo(config);
assertThat(config.getAttrMapping()).isEqualTo(Map.of("alarmThreshold", "threshold"));
assertThat(config.getDataMapping()).isEqualTo(Map.of("alarmThreshold", "threshold"));
assertThat(config.getDataToFetch()).isEqualTo(DataToFetch.ATTRIBUTES);
assertThat(node.fetchTo).isEqualTo(FetchTo.METADATA);
}
@ -182,7 +182,7 @@ public class TbGetCustomerAttributeNodeTest {
@Test
public void givenCustomConfig_whenInit_thenOK() throws TbNodeException {
// GIVEN
config.setAttrMapping(Map.of(
config.setDataMapping(Map.of(
"sourceAttr1", "targetKey1",
"sourceAttr2", "targetKey2",
"sourceAttr3", "targetKey3"));
@ -195,7 +195,7 @@ public class TbGetCustomerAttributeNodeTest {
// THEN
assertThat(node.config).isEqualTo(config);
assertThat(config.getAttrMapping()).isEqualTo(Map.of(
assertThat(config.getDataMapping()).isEqualTo(Map.of(
"sourceAttr1", "targetKey1",
"sourceAttr2", "targetKey2",
"sourceAttr3", "targetKey3"));
@ -208,7 +208,7 @@ public class TbGetCustomerAttributeNodeTest {
// GIVEN
var expectedExceptionMessage = "At least one mapping entry should be specified!";
config.setAttrMapping(Collections.emptyMap());
config.setDataMapping(Collections.emptyMap());
nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config));
// WHEN
@ -456,7 +456,7 @@ public class TbGetCustomerAttributeNodeTest {
@Test
public void givenOldConfig_whenUpgrade_thenShouldReturnSuccessResult() throws Exception {
var defaultConfig = new TbGetEntityAttrNodeConfiguration().defaultConfiguration();
var defaultConfig = new TbGetEntityDataNodeConfiguration().defaultConfiguration();
var node = new TbGetCustomerAttributeNode();
String oldConfig = "{\"attrMapping\":{\"alarmThreshold\":\"threshold\"},\"telemetry\":false}";
JsonNode configJson = JacksonUtil.toJsonNode(oldConfig);
@ -466,7 +466,7 @@ public class TbGetCustomerAttributeNodeTest {
}
private void prepareMsgAndConfig(FetchTo fetchTo, DataToFetch dataToFetch, EntityId originator) {
config.setAttrMapping(Map.of(
config.setDataMapping(Map.of(
"sourceKey1", "targetKey1",
"${metaDataPattern1}", "$[messageBodyPattern1]",
"$[messageBodyPattern2]", "${metaDataPattern2}"));

25
rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNodeTest.java

@ -44,6 +44,7 @@ import java.util.Collections;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
@ -111,7 +112,7 @@ public class TbGetOriginatorFieldsNodeTest {
// THEN
assertThat(node.config).isEqualTo(config);
assertThat(config.getFieldsMapping()).isEqualTo(Map.of(
assertThat(config.getDataMapping()).isEqualTo(Map.of(
"name", "originatorName",
"type", "originatorType"));
assertThat(config.isIgnoreNullStrings()).isEqualTo(false);
@ -122,7 +123,7 @@ public class TbGetOriginatorFieldsNodeTest {
@Test
public void givenCustomConfig_whenInit_thenOK() throws TbNodeException {
// GIVEN
config.setFieldsMapping(Map.of(
config.setDataMapping(Map.of(
"email", "originatorEmail",
"title", "originatorTitle",
"country", "originatorCountry"));
@ -135,7 +136,7 @@ public class TbGetOriginatorFieldsNodeTest {
// THEN
assertThat(node.config).isEqualTo(config);
assertThat(config.getFieldsMapping()).isEqualTo(Map.of(
assertThat(config.getDataMapping()).isEqualTo(Map.of(
"email", "originatorEmail",
"title", "originatorTitle",
"country", "originatorCountry"));
@ -159,14 +160,14 @@ public class TbGetOriginatorFieldsNodeTest {
}
@Test
public void givenValidMsgAndFetchToData_whenOnMsg_thenShouldTellSuccessAndFetchToData() {
public void givenValidMsgAndFetchToData_whenOnMsg_thenShouldTellSuccessAndFetchToData() throws TbNodeException, ExecutionException, InterruptedException {
// GIVEN
var device = new Device();
device.setId(DUMMY_DEVICE_ORIGINATOR);
device.setName("Test device");
device.setType("Test device type");
config.setFieldsMapping(Map.of(
config.setDataMapping(Map.of(
"name", "originatorName",
"type", "originatorType",
"label", "originatorLabel"));
@ -200,14 +201,14 @@ public class TbGetOriginatorFieldsNodeTest {
}
@Test
public void givenValidMsgAndFetchToMetaData_whenOnMsg_thenShouldTellSuccessAndFetchToMetaData() {
public void givenValidMsgAndFetchToMetaData_whenOnMsg_thenShouldTellSuccessAndFetchToMetaData() throws TbNodeException, ExecutionException, InterruptedException {
// GIVEN
var device = new Device();
device.setId(DUMMY_DEVICE_ORIGINATOR);
device.setName("Test device");
device.setType("Test device type");
config.setFieldsMapping(Map.of(
config.setDataMapping(Map.of(
"name", "originatorName",
"type", "originatorType",
"label", "originatorLabel"));
@ -248,14 +249,14 @@ public class TbGetOriginatorFieldsNodeTest {
}
@Test
public void givenNullEntityFieldsAndIgnoreNullStringsFalse_whenOnMsg_thenShouldTellSuccessAndFetchNullField() {
public void givenNullEntityFieldsAndIgnoreNullStringsFalse_whenOnMsg_thenShouldTellSuccessAndFetchNullField() throws TbNodeException, ExecutionException, InterruptedException {
// GIVEN
var device = new Device();
device.setId(DUMMY_DEVICE_ORIGINATOR);
device.setName("Test device");
device.setType("Test device type");
config.setFieldsMapping(Map.of(
config.setDataMapping(Map.of(
"name", "originatorName",
"type", "originatorType",
"label", "originatorLabel"));
@ -299,7 +300,7 @@ public class TbGetOriginatorFieldsNodeTest {
@Test
public void givenEmptyFieldsMapping_whenInit_thenException() {
// GIVEN
config.setFieldsMapping(Collections.emptyMap());
config.setDataMapping(Collections.emptyMap());
nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config));
// WHEN
@ -311,9 +312,9 @@ public class TbGetOriginatorFieldsNodeTest {
}
@Test
public void givenUnsupportedEntityType_whenOnMsg_thenShouldTellFailureWithSameMsg() {
public void givenUnsupportedEntityType_whenOnMsg_thenShouldTellFailureWithSameMsg() throws TbNodeException, ExecutionException, InterruptedException {
// GIVEN
config.setFieldsMapping(Map.of(
config.setDataMapping(Map.of(
"name", "originatorName",
"type", "originatorType",
"label", "originatorLabel"));

45
rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNodeTest.java

@ -34,9 +34,21 @@ import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.rule.engine.api.TbNodeConfiguration;
import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.rule.engine.data.RelationsQuery;
import org.thingsboard.server.common.data.*;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.id.*;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
@ -54,7 +66,12 @@ import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.dao.timeseries.TimeseriesService;
import java.util.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.UUID;
import java.util.concurrent.Callable;
import static org.assertj.core.api.Assertions.assertThat;
@ -102,7 +119,7 @@ public class TbGetRelatedAttributeNodeTest {
@Mock
private DeviceService deviceServiceMock;
private TbGetRelatedAttributeNode node;
private TbGetRelatedAttrNodeConfiguration config;
private TbGetRelatedDataNodeConfiguration config;
private TbNodeConfiguration nodeConfiguration;
private EntityRelation entityRelation;
private TbMsg msg;
@ -110,7 +127,7 @@ public class TbGetRelatedAttributeNodeTest {
@BeforeEach
public void setUp() {
node = new TbGetRelatedAttributeNode();
config = new TbGetRelatedAttrNodeConfiguration().defaultConfiguration();
config = new TbGetRelatedDataNodeConfiguration().defaultConfiguration();
nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config));
entityRelation = new EntityRelation();
}
@ -151,9 +168,9 @@ public class TbGetRelatedAttributeNodeTest {
node.init(ctxMock, nodeConfiguration);
// THEN
var nodeConfig = (TbGetRelatedAttrNodeConfiguration) node.config;
var nodeConfig = (TbGetRelatedDataNodeConfiguration) node.config;
assertThat(nodeConfig).isEqualTo(config);
assertThat(nodeConfig.getAttrMapping()).isEqualTo(Map.of("serialNumber", "sn"));
assertThat(nodeConfig.getDataMapping()).isEqualTo(Map.of("serialNumber", "sn"));
assertThat(nodeConfig.getDataToFetch()).isEqualTo(DataToFetch.ATTRIBUTES);
assertThat(node.fetchTo).isEqualTo(FetchTo.METADATA);
@ -169,7 +186,7 @@ public class TbGetRelatedAttributeNodeTest {
@Test
public void givenCustomConfig_whenInit_thenOK() throws TbNodeException {
// GIVEN
config.setAttrMapping(Map.of(
config.setDataMapping(Map.of(
"sourceAttr1", "targetKey1",
"sourceAttr2", "targetKey2",
"sourceAttr3", "targetKey3"));
@ -189,9 +206,9 @@ public class TbGetRelatedAttributeNodeTest {
node.init(ctxMock, nodeConfiguration);
// THEN
var nodeConfig = (TbGetRelatedAttrNodeConfiguration) node.config;
var nodeConfig = (TbGetRelatedDataNodeConfiguration) node.config;
assertThat(nodeConfig).isEqualTo(config);
assertThat(nodeConfig.getAttrMapping()).isEqualTo(Map.of(
assertThat(nodeConfig.getDataMapping()).isEqualTo(Map.of(
"sourceAttr1", "targetKey1",
"sourceAttr2", "targetKey2",
"sourceAttr3", "targetKey3"
@ -206,7 +223,7 @@ public class TbGetRelatedAttributeNodeTest {
// GIVEN
var expectedExceptionMessage = "At least one mapping entry should be specified!";
config.setAttrMapping(Collections.emptyMap());
config.setDataMapping(Collections.emptyMap());
nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config));
// WHEN
@ -553,7 +570,7 @@ public class TbGetRelatedAttributeNodeTest {
@Test
public void givenOldConfig_whenUpgrade_thenShouldReturnSuccessResult() throws Exception {
var defaultConfig = new TbGetRelatedAttrNodeConfiguration().defaultConfiguration();
var defaultConfig = new TbGetRelatedDataNodeConfiguration().defaultConfiguration();
var node = new TbGetRelatedAttributeNode();
String oldConfig = "{\"attrMapping\":{\"serialNumber\":\"sn\"}," +
"\"relationsQuery\":{\"direction\":\"FROM\",\"maxLevel\":1," +
@ -575,13 +592,13 @@ public class TbGetRelatedAttributeNodeTest {
var msgMetaData = new TbMsgMetaData();
String msgData;
if (dataToFetch.equals(DataToFetch.FIELDS)) {
config.setAttrMapping(Map.of(
config.setDataMapping(Map.of(
"id", "$[messageBodyPattern]",
"name", "${metaDataPattern}"));
msgMetaData.putValue("metaDataPattern", "relatedEntityName");
msgData = "{\"temp\":42,\"humidity\":77,\"messageBodyPattern\":\"relatedEntityId\"}";
} else {
config.setAttrMapping(Map.of(
config.setDataMapping(Map.of(
"sourceKey1", "targetKey1",
"${metaDataPattern1}", "$[messageBodyPattern1]",
"$[messageBodyPattern2]", "${metaDataPattern2}"));

16
rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNodeTest.java

@ -93,14 +93,14 @@ public class TbGetTenantAttributeNodeTest {
@Mock
private TimeseriesService timeseriesServiceMock;
private TbGetTenantAttributeNode node;
private TbGetEntityAttrNodeConfiguration config;
private TbGetEntityDataNodeConfiguration config;
private TbNodeConfiguration nodeConfiguration;
private TbMsg msg;
@BeforeEach
public void setUp() {
node = new TbGetTenantAttributeNode();
config = new TbGetEntityAttrNodeConfiguration().defaultConfiguration();
config = new TbGetEntityDataNodeConfiguration().defaultConfiguration();
nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config));
}
@ -155,7 +155,7 @@ public class TbGetTenantAttributeNodeTest {
// THEN
assertThat(node.config).isEqualTo(config);
assertThat(config.getAttrMapping()).isEqualTo(Map.of("alarmThreshold", "threshold"));
assertThat(config.getDataMapping()).isEqualTo(Map.of("alarmThreshold", "threshold"));
assertThat(config.getDataToFetch()).isEqualTo(DataToFetch.ATTRIBUTES);
assertThat(node.fetchTo).isEqualTo(FetchTo.METADATA);
}
@ -163,7 +163,7 @@ public class TbGetTenantAttributeNodeTest {
@Test
public void givenCustomConfig_whenInit_thenOK() throws TbNodeException {
// GIVEN
config.setAttrMapping(Map.of(
config.setDataMapping(Map.of(
"sourceAttr1", "targetKey1",
"sourceAttr2", "targetKey2",
"sourceAttr3", "targetKey3"));
@ -176,7 +176,7 @@ public class TbGetTenantAttributeNodeTest {
// THEN
assertThat(node.config).isEqualTo(config);
assertThat(config.getAttrMapping()).isEqualTo(Map.of(
assertThat(config.getDataMapping()).isEqualTo(Map.of(
"sourceAttr1", "targetKey1",
"sourceAttr2", "targetKey2",
"sourceAttr3", "targetKey3"));
@ -189,7 +189,7 @@ public class TbGetTenantAttributeNodeTest {
// GIVEN
var expectedExceptionMessage = "At least one mapping entry should be specified!";
config.setAttrMapping(Collections.emptyMap());
config.setDataMapping(Collections.emptyMap());
nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config));
// WHEN
@ -386,7 +386,7 @@ public class TbGetTenantAttributeNodeTest {
@Test
public void givenOldConfig_whenUpgrade_thenShouldReturnSuccessResult() throws Exception {
var defaultConfig = new TbGetEntityAttrNodeConfiguration().defaultConfiguration();
var defaultConfig = new TbGetEntityDataNodeConfiguration().defaultConfiguration();
var node = new TbGetTenantAttributeNode();
String oldConfig = "{\"attrMapping\":{\"alarmThreshold\":\"threshold\"},\"telemetry\":false}";
JsonNode configJson = JacksonUtil.toJsonNode(oldConfig);
@ -396,7 +396,7 @@ public class TbGetTenantAttributeNodeTest {
}
private void prepareMsgAndConfig(FetchTo fetchTo, DataToFetch dataToFetch, EntityId originator) {
config.setAttrMapping(Map.of(
config.setDataMapping(Map.of(
"sourceKey1", "targetKey1",
"${metaDataPattern1}", "$[messageBodyPattern1]",
"$[messageBodyPattern2]", "${metaDataPattern2}"));

Loading…
Cancel
Save