diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbRenameMsgKeysNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbRenameKeysNode.java similarity index 53% rename from rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbRenameMsgKeysNode.java rename to rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbRenameKeysNode.java index 09543083e3..8704874594 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbRenameMsgKeysNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbRenameKeysNode.java @@ -28,6 +28,7 @@ import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; import java.util.Map; import java.util.concurrent.ExecutionException; @@ -36,22 +37,22 @@ import java.util.concurrent.ExecutionException; @RuleNode( type = ComponentType.TRANSFORMATION, name = "rename keys", - configClazz = TbRenameMsgKeysNodeConfiguration.class, - nodeDescription = "Renames msg data keys to the new key names selected in the key mapping.", - nodeDetails = "If the key that is selected in the key mapping is missed in the msg data, it will be ignored." + - "If the msg is not a JSON object returns the incoming message as outbound message with Failure chain," + + configClazz = TbRenameKeysNodeConfiguration.class, + nodeDescription = "Renames msg data or metadata keys to the new key names selected in the key mapping.", + nodeDetails = "If the key that is selected in the key mapping is missed in the msg data or metadata, it will be ignored." + + "If the msg data is not a JSON object returns the incoming message as outbound message with Failure chain," + " otherwise returns transformed messages via Success chain", uiResources = {"static/rulenode/rulenode-core-config.js"}, - configDirective = "tbTransformationNodeRenameMsgKeysConfig", - icon = "functions" + configDirective = "tbTransformationNodeRenameKeysConfig", + icon = "find_replace" ) -public class TbRenameMsgKeysNode implements TbNode { +public class TbRenameKeysNode implements TbNode { - TbRenameMsgKeysNodeConfiguration config; + TbRenameKeysNodeConfiguration config; @Override public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { - this.config = TbNodeUtils.convert(configuration, TbRenameMsgKeysNodeConfiguration.class); + this.config = TbNodeUtils.convert(configuration, TbRenameKeysNodeConfiguration.class); } @Override @@ -60,19 +61,38 @@ public class TbRenameMsgKeysNode implements TbNode { if (CollectionUtils.isEmpty(renameKeysMapping)) { ctx.tellSuccess(msg); } else { - JsonNode dataNode = JacksonUtil.toJsonNode(msg.getData()); - if (dataNode.isObject()) { - ObjectNode msgData = (ObjectNode) dataNode; + TbMsgMetaData metaData = msg.getMetaData(); + String data = msg.getData(); + if (config.isFromMetadata()) { + Map metaDataMap = metaData.getData(); renameKeysMapping.forEach((nameKey, newNameKey) -> { - if (msgData.has(nameKey)) { - msgData.set(newNameKey, msgData.get(nameKey)); - msgData.remove(nameKey); + if (!newNameKey.equals(nameKey)) { + if (metaDataMap.containsKey(nameKey)) { + metaDataMap.put(newNameKey, metaDataMap.get(nameKey)); + metaDataMap.remove(nameKey); + } } }); - ctx.tellSuccess(TbMsg.transformMsg(msg, msg.getType(), msg.getOriginator(), msg.getMetaData(), JacksonUtil.toString(msgData))); + metaData = new TbMsgMetaData(metaDataMap); } else { - ctx.tellFailure(msg, new RuntimeException("Msg data is not a JSON Object!")); + JsonNode dataNode = JacksonUtil.toJsonNode(msg.getData()); + if (dataNode.isObject()) { + ObjectNode msgData = (ObjectNode) dataNode; + renameKeysMapping.forEach((nameKey, newNameKey) -> { + if (!newNameKey.equals(nameKey)) { + if (msgData.has(nameKey)) { + msgData.set(newNameKey, msgData.get(nameKey)); + msgData.remove(nameKey); + } + } + }); + data = JacksonUtil.toString(msgData); + } else { + ctx.tellFailure(msg, new RuntimeException("Msg data is not a JSON Object!")); + return; + } } + ctx.tellSuccess(TbMsg.transformMsg(msg, msg.getType(), msg.getOriginator(), metaData, data)); } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbRenameMsgKeysNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbRenameKeysNodeConfiguration.java similarity index 74% rename from rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbRenameMsgKeysNodeConfiguration.java rename to rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbRenameKeysNodeConfiguration.java index 0efcb38ce2..8d9c4d2f94 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbRenameMsgKeysNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbRenameKeysNodeConfiguration.java @@ -22,14 +22,16 @@ import java.util.Collections; import java.util.Map; @Data -public class TbRenameMsgKeysNodeConfiguration implements NodeConfiguration { +public class TbRenameKeysNodeConfiguration implements NodeConfiguration { + private boolean fromMetadata; private Map renameKeysMapping; @Override - public TbRenameMsgKeysNodeConfiguration defaultConfiguration() { - TbRenameMsgKeysNodeConfiguration configuration = new TbRenameMsgKeysNodeConfiguration(); + public TbRenameKeysNodeConfiguration defaultConfiguration() { + TbRenameKeysNodeConfiguration configuration = new TbRenameKeysNodeConfiguration(); configuration.setRenameKeysMapping(Collections.emptyMap()); + configuration.setFromMetadata(false); return configuration; } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbRenameMsgKeysNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbRenameMsgKeysNodeTest.java index 0f06fdc520..e42128157d 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbRenameMsgKeysNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbRenameMsgKeysNodeTest.java @@ -44,8 +44,8 @@ import static org.mockito.Mockito.verify; public class TbRenameMsgKeysNodeTest { DeviceId deviceId; - TbRenameMsgKeysNode node; - TbRenameMsgKeysNodeConfiguration config; + TbRenameKeysNode node; + TbRenameKeysNodeConfiguration config; TbNodeConfiguration nodeConfiguration; TbContext ctx; TbMsgCallback callback; @@ -55,10 +55,10 @@ public class TbRenameMsgKeysNodeTest { deviceId = new DeviceId(UUID.randomUUID()); callback = mock(TbMsgCallback.class); ctx = mock(TbContext.class); - config = new TbRenameMsgKeysNodeConfiguration().defaultConfiguration(); + config = new TbRenameKeysNodeConfiguration().defaultConfiguration(); config.setRenameKeysMapping(Map.of("TestKey_1", "Attribute_1", "TestKey_2", "Attribute_2")); nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); - node = spy(new TbRenameMsgKeysNode()); + node = spy(new TbRenameKeysNode()); node.init(ctx, nodeConfiguration); } @@ -74,8 +74,9 @@ public class TbRenameMsgKeysNodeTest { @Test void givenDefaultConfig_whenVerify_thenOK() { - TbRenameMsgKeysNodeConfiguration defaultConfig = new TbRenameMsgKeysNodeConfiguration().defaultConfiguration(); + TbRenameKeysNodeConfiguration defaultConfig = new TbRenameKeysNodeConfiguration().defaultConfiguration(); assertThat(defaultConfig.getRenameKeysMapping()).isEqualTo(Collections.emptyMap()); + assertThat(defaultConfig.isFromMetadata()).isEqualTo(false); } @Test @@ -95,9 +96,31 @@ public class TbRenameMsgKeysNodeTest { assertThat(dataNode.has("Temperature_1")).isEqualTo(true); } + @Test + void givenMetadata_whenOnMsg_thenVerifyOutput() throws Exception { + config = new TbRenameKeysNodeConfiguration().defaultConfiguration(); + config.setRenameKeysMapping(Map.of("TestKey_1", "Attribute_1", "TestKey_2", "Attribute_2")); + config.setFromMetadata(true); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); + node.init(ctx, nodeConfiguration); + + String data = "{\"Temperature_1\":22.5,\"TestKey_2\":10.3}"; + node.onMsg(ctx, getTbMsg(deviceId, data)); + + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctx, times(1)).tellSuccess(newMsgCaptor.capture()); + verify(ctx, never()).tellFailure(any(), any()); + + TbMsg newMsg = newMsgCaptor.getValue(); + assertThat(newMsg).isNotNull(); + + Map mdDataMap = newMsg.getMetaData().getData(); + assertThat(mdDataMap.containsKey("Attribute_1")).isEqualTo(true); + } + @Test void givenEmptyKeys_whenOnMsg_thenVerifyOutput() throws Exception { - TbRenameMsgKeysNodeConfiguration defaultConfig = new TbRenameMsgKeysNodeConfiguration().defaultConfiguration(); + TbRenameKeysNodeConfiguration defaultConfig = new TbRenameKeysNodeConfiguration().defaultConfiguration(); nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(defaultConfig)); node.init(ctx, nodeConfiguration);