diff --git a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java index c896e10dc3..31988394ab 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -277,6 +277,7 @@ public class DefaultDataUpdateService implements DataUpdateService { log.error("Unexpected error during rule nodes upgrade: ", e); } } + private ArrayList getTbVersionedNodes() { var ruleNodeDefinitions = beanDiscoveryService.discoverBeansByAnnotationType( org.thingsboard.rule.engine.api.RuleNode.class diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java index 4cf96dfa0d..8c25a97298 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -207,14 +207,14 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC TbPair 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, diff --git a/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesLatestDao.java b/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesLatestDao.java index 6c093509d0..605e0bb428 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesLatestDao.java +++ b/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); } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetEntityAttrNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetEntityAttrNode.java deleted file mode 100644 index 19127ac5bf..0000000000 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetEntityAttrNode.java +++ /dev/null @@ -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 extends TbAbstractNodeWithFetchTo { - - 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 findEntityAsync(TbContext ctx, EntityId originator); - - protected void checkIfMappingIsNotEmptyOrElseThrow(Map 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(); - 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> collectMappedEntityFieldsAsync(TbContext ctx, EntityId entityId, HashMap mappingsMap) { - return Futures.transform(EntitiesFieldsAsyncLoader.findAsync(ctx, entityId), - fieldsData -> { - var targetKeysToSourceValuesMap = new HashMap(); - 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> getAttributesAsync(TbContext ctx, EntityId entityId, List 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> getLatestTelemetryAsync(TbContext ctx, EntityId entityId, List 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 data, Map 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 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); - } - -} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetEntityDataNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetEntityDataNode.java new file mode 100644 index 0000000000..5615147fd0 --- /dev/null +++ b/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 extends TbAbstractGetMappedDataNode { + + 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 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 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); + } + +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetMappedDataNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetMappedDataNode.java new file mode 100644 index 0000000000..b4e4d29524 --- /dev/null +++ b/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 extends TbAbstractNodeWithFetchTo { + + protected void checkIfMappingIsNotEmptyOrElseThrow(Map 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 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 data, Map 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 processFieldsMappingPatterns(TbMsg msg) { + var mappingsMap = new HashMap(); + config.getDataMapping().forEach((sourceField, targetKey) -> { + String patternProcessedTargetKey = TbNodeUtils.processPattern(targetKey, msg); + mappingsMap.put(sourceField, patternProcessedTargetKey); + }); + return mappingsMap; + } + + private Map processKvEntryMappingPatterns(TbMsg msg) { + var mappingsMap = new HashMap(); + 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> getEntityFieldsAsync(TbContext ctx, EntityId entityId, Map mappingsMap, boolean ignoreNullStrings) { + return Futures.transform(EntitiesFieldsAsyncLoader.findAsync(ctx, entityId), + fieldsData -> { + var targetKeysToSourceValuesMap = new HashMap(); + 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> getAttributesAsync(TbContext ctx, EntityId entityId, List 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> getLatestTelemetryAsync(TbContext ctx, EntityId entityId, List 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()); + } + +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNode.java index f9cbba2ffd..e693dab684 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNode.java +++ b/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 { +public class TbGetCustomerAttributeNode extends TbAbstractGetEntityDataNode { 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; } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetEntityAttrNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetEntityDataNodeConfiguration.java similarity index 67% rename from rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetEntityAttrNodeConfiguration.java rename to rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetEntityDataNodeConfiguration.java index eec7b497e1..eb3f3b805c 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetEntityAttrNodeConfiguration.java +++ b/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 { +public class TbGetEntityDataNodeConfiguration extends TbGetMappedDataNodeConfiguration implements NodeConfiguration { - private Map attrMapping; private DataToFetch dataToFetch; @Override - public TbGetEntityAttrNodeConfiguration defaultConfiguration() { - var configuration = new TbGetEntityAttrNodeConfiguration(); - var attrMapping = new HashMap(); - attrMapping.putIfAbsent("alarmThreshold", "threshold"); - configuration.setAttrMapping(attrMapping); + public TbGetEntityDataNodeConfiguration defaultConfiguration() { + var configuration = new TbGetEntityDataNodeConfiguration(); + var dataMapping = new HashMap(); + dataMapping.putIfAbsent("alarmThreshold", "threshold"); + configuration.setDataMapping(dataMapping); configuration.setDataToFetch(DataToFetch.ATTRIBUTES); configuration.setFetchTo(FetchTo.METADATA); return configuration; diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetMappedDataNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetMappedDataNodeConfiguration.java new file mode 100644 index 0000000000..68b8f0ff31 --- /dev/null +++ b/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 dataMapping; + +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsConfiguration.java index e5d428100b..45ca8e19c0 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsConfiguration.java +++ b/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 { +public class TbGetOriginatorFieldsConfiguration extends TbGetMappedDataNodeConfiguration implements NodeConfiguration { - private Map fieldsMapping; + private Map dataMapping; private boolean ignoreNullStrings; @Override public TbGetOriginatorFieldsConfiguration defaultConfiguration() { var configuration = new TbGetOriginatorFieldsConfiguration(); - var fieldsMapping = new HashMap(); - fieldsMapping.put("name", "originatorName"); - fieldsMapping.put("type", "originatorType"); - configuration.setFieldsMapping(fieldsMapping); + var dataMapping = new HashMap(); + dataMapping.put("name", "originatorName"); + dataMapping.put("type", "originatorType"); + configuration.setDataMapping(dataMapping); configuration.setIgnoreNullStrings(false); configuration.setFetchTo(FetchTo.METADATA); return configuration; diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNode.java index 79d9a982c2..f777d98bcb 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNode.java +++ b/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 { +public class TbGetOriginatorFieldsNode extends TbAbstractGetMappedDataNode { + + 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> collectMappedEntityFieldsAsync(TbContext ctx, EntityId entityId) { - return Futures.transform(EntitiesFieldsAsyncLoader.findAsync(ctx, entityId), - fieldsData -> { - var targetKeysToSourceValuesMap = new HashMap(); - 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 upgrade(int fromVersion, JsonNode oldConfiguration) { + public TbPair 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); } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNode.java index c89d7c4b59..3c508bda14 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNode.java +++ b/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; "metadata.temperature.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeRelatedAttributesConfig") -public class TbGetRelatedAttributeNode extends TbAbstractGetEntityAttrNode { +public class TbGetRelatedAttributeNode extends TbAbstractGetEntityDataNode { 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 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), diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttrNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedDataNodeConfiguration.java similarity index 82% rename from rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttrNodeConfiguration.java rename to rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedDataNodeConfiguration.java index 3f4d95a08f..4c78915ae4 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttrNodeConfiguration.java +++ b/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(); - attrMapping.putIfAbsent("serialNumber", "sn"); - configuration.setAttrMapping(attrMapping); + public TbGetRelatedDataNodeConfiguration defaultConfiguration() { + var configuration = new TbGetRelatedDataNodeConfiguration(); + var dataMapping = new HashMap(); + dataMapping.putIfAbsent("serialNumber", "sn"); + configuration.setDataMapping(dataMapping); configuration.setDataToFetch(DataToFetch.ATTRIBUTES); configuration.setFetchTo(FetchTo.METADATA); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNode.java index 8ff44e297c..aac8d8d3bf 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNode.java +++ b/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; "metadata.temperature.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeTenantAttributesConfig") -public class TbGetTenantAttributeNode extends TbAbstractGetEntityAttrNode { +public class TbGetTenantAttributeNode extends TbAbstractGetEntityDataNode { @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; } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNodeTest.java index 201992e949..54e2f3bd4b 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNodeTest.java +++ b/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}")); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNodeTest.java index 6b162d08fb..45f42a95a7 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNodeTest.java +++ b/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")); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNodeTest.java index a9a04fb3df..b222e11d20 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNodeTest.java +++ b/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}")); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNodeTest.java index 5c56561a48..855adea614 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNodeTest.java +++ b/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}"));