AI rule node: add support for all data/metadata patterns in rule nodes
This commit is contained in:
parent
9567dd3090
commit
d2d22a44c2
@ -23,9 +23,6 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Created by ashvayka on 13.01.18.
|
||||
*/
|
||||
@Data
|
||||
public final class TbMsgMetaData implements Serializable {
|
||||
|
||||
@ -34,7 +31,7 @@ public final class TbMsgMetaData implements Serializable {
|
||||
private final Map<String, String> data;
|
||||
|
||||
public TbMsgMetaData() {
|
||||
this.data = new ConcurrentHashMap<>();
|
||||
data = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
public TbMsgMetaData(Map<String, String> data) {
|
||||
@ -46,24 +43,29 @@ public final class TbMsgMetaData implements Serializable {
|
||||
* Internal constructor to create immutable TbMsgMetaData.EMPTY
|
||||
* */
|
||||
private TbMsgMetaData(int ignored) {
|
||||
this.data = Collections.emptyMap();
|
||||
data = Collections.emptyMap();
|
||||
}
|
||||
|
||||
public String getValue(String key) {
|
||||
return this.data.get(key);
|
||||
return data.get(key);
|
||||
}
|
||||
|
||||
public void putValue(String key, String value) {
|
||||
if (key != null && value != null) {
|
||||
this.data.put(key, value);
|
||||
data.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, String> values() {
|
||||
return new HashMap<>(this.data);
|
||||
return new HashMap<>(data);
|
||||
}
|
||||
|
||||
public TbMsgMetaData copy() {
|
||||
return new TbMsgMetaData(this.data);
|
||||
return new TbMsgMetaData(data);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return data == null || data.isEmpty();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -16,11 +16,11 @@
|
||||
package org.thingsboard.rule.engine.api.util;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.thingsboard.common.util.JacksonUtil;
|
||||
import org.thingsboard.rule.engine.api.TbNodeConfiguration;
|
||||
import org.thingsboard.rule.engine.api.TbNodeException;
|
||||
import org.thingsboard.server.common.data.StringUtils;
|
||||
import org.thingsboard.server.common.data.util.CollectionsUtil;
|
||||
import org.thingsboard.server.common.msg.TbMsg;
|
||||
import org.thingsboard.server.common.msg.TbMsgMetaData;
|
||||
|
||||
@ -29,15 +29,18 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Created by ashvayka on 19.01.18.
|
||||
*/
|
||||
public class TbNodeUtils {
|
||||
public final class TbNodeUtils {
|
||||
|
||||
private TbNodeUtils() {
|
||||
throw new IllegalStateException("Utility class");
|
||||
}
|
||||
|
||||
private static final Pattern DATA_PATTERN = Pattern.compile("(\\$\\[)(.*?)(])");
|
||||
|
||||
private static final String ALL_DATA_TEMPLATE = "$[*]";
|
||||
private static final String ALL_METADATA_TEMPLATE = "${*}";
|
||||
|
||||
public static <T> T convert(TbNodeConfiguration configuration, Class<T> clazz) throws TbNodeException {
|
||||
try {
|
||||
return JacksonUtil.treeToValue(configuration.getData(), clazz);
|
||||
@ -47,16 +50,19 @@ public class TbNodeUtils {
|
||||
}
|
||||
|
||||
public static List<String> processPatterns(List<String> patterns, TbMsg tbMsg) {
|
||||
if (!CollectionUtils.isEmpty(patterns)) {
|
||||
return patterns.stream().map(p -> processPattern(p, tbMsg)).collect(Collectors.toList());
|
||||
if (CollectionsUtil.isEmpty(patterns)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Collections.emptyList();
|
||||
return patterns.stream().map(p -> processPattern(p, tbMsg)).toList();
|
||||
}
|
||||
|
||||
public static String processPattern(String pattern, TbMsg tbMsg) {
|
||||
try {
|
||||
String result = processPattern(pattern, tbMsg.getMetaData());
|
||||
JsonNode json = JacksonUtil.toJsonNode(tbMsg.getData());
|
||||
|
||||
result = result.replace(ALL_DATA_TEMPLATE, JacksonUtil.toString(json));
|
||||
|
||||
if (json.isObject()) {
|
||||
Matcher matcher = DATA_PATTERN.matcher(result);
|
||||
while (matcher.find()) {
|
||||
@ -64,7 +70,7 @@ public class TbNodeUtils {
|
||||
String[] keys = group.split("\\.");
|
||||
JsonNode jsonNode = json;
|
||||
for (String key : keys) {
|
||||
if (!StringUtils.isEmpty(key) && jsonNode != null) {
|
||||
if (StringUtils.isNotEmpty(key) && jsonNode != null) {
|
||||
jsonNode = jsonNode.get(key);
|
||||
} else {
|
||||
jsonNode = null;
|
||||
@ -83,15 +89,9 @@ public class TbNodeUtils {
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated(since = "3.6.1", forRemoval = true)
|
||||
public static List<String> processPatterns(List<String> patterns, TbMsgMetaData metaData) {
|
||||
if (!CollectionUtils.isEmpty(patterns)) {
|
||||
return patterns.stream().map(p -> processPattern(p, metaData)).collect(Collectors.toList());
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public static String processPattern(String pattern, TbMsgMetaData metaData) {
|
||||
private static String processPattern(String pattern, TbMsgMetaData metaData) {
|
||||
String replacement = metaData.isEmpty() ? "{}" : JacksonUtil.toString(metaData.getData());
|
||||
pattern = pattern.replace(ALL_METADATA_TEMPLATE, replacement);
|
||||
return processTemplate(pattern, metaData.values());
|
||||
}
|
||||
|
||||
@ -108,10 +108,11 @@ public class TbNodeUtils {
|
||||
}
|
||||
|
||||
static String formatDataVarTemplate(String key) {
|
||||
return "$[" + key + ']';
|
||||
return "$[" + key + "]";
|
||||
}
|
||||
|
||||
static String formatMetadataVarTemplate(String key) {
|
||||
return "${" + key + '}';
|
||||
return "${" + key + "}";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -26,6 +26,8 @@ import org.thingsboard.server.common.data.msg.TbMsgType;
|
||||
import org.thingsboard.server.common.msg.TbMsg;
|
||||
import org.thingsboard.server.common.msg.TbMsgMetaData;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
@ -167,4 +169,160 @@ public class TbNodeUtilsTest {
|
||||
assertThat(TbNodeUtils.formatMetadataVarTemplate(null), is("${null}"));
|
||||
assertThat(TbNodeUtils.formatMetadataVarTemplate(null), is(String.format(METADATA_VARIABLE_TEMPLATE, (String) null)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAllMetadataTemplateReplacement() {
|
||||
// GIVEN
|
||||
String pattern = "META ${*}";
|
||||
var metadata = new TbMsgMetaData();
|
||||
metadata.putValue("meta_key", "meta_value");
|
||||
|
||||
var msg = TbMsg.newMsg()
|
||||
.data(TbMsg.EMPTY_JSON_OBJECT)
|
||||
.metaData(metadata)
|
||||
.build();
|
||||
|
||||
// WHEN
|
||||
String actual = TbNodeUtils.processPattern(pattern, msg);
|
||||
|
||||
// THEN
|
||||
String expected = "META {\"meta_key\":\"meta_value\"}";
|
||||
assertThat(actual, is(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleAllMetadataTemplatesReplacement() {
|
||||
// GIVEN
|
||||
String pattern = "${*} then again ${*}";
|
||||
var metadata = new TbMsgMetaData();
|
||||
metadata.putValue("meta_key", "meta_value");
|
||||
|
||||
var msg = TbMsg.newMsg()
|
||||
.data(TbMsg.EMPTY_JSON_OBJECT)
|
||||
.metaData(metadata)
|
||||
.build();
|
||||
|
||||
// WHEN
|
||||
String actual = TbNodeUtils.processPattern(pattern, msg);
|
||||
|
||||
// THEN
|
||||
String expected = "{\"meta_key\":\"meta_value\"} then again {\"meta_key\":\"meta_value\"}";
|
||||
assertThat(actual, is(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAllDataTemplateReplacement() {
|
||||
// GIVEN
|
||||
String pattern = "DATA $[*]";
|
||||
var dataJson = JacksonUtil.newObjectNode().put("data_key", "data_value");
|
||||
|
||||
var msg = TbMsg.newMsg()
|
||||
.data(JacksonUtil.toString(dataJson))
|
||||
.metaData(TbMsgMetaData.EMPTY)
|
||||
.build();
|
||||
|
||||
// WHEN
|
||||
String actual = TbNodeUtils.processPattern(pattern, msg);
|
||||
|
||||
// THEN
|
||||
String expected = "DATA {\"data_key\":\"data_value\"}";
|
||||
assertThat(actual, is(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleAllDataTemplatesReplacement() {
|
||||
// GIVEN
|
||||
String pattern = "$[*] then again $[*]";
|
||||
var dataJson = JacksonUtil.newObjectNode().put("data_key", "data_value");
|
||||
|
||||
var msg = TbMsg.newMsg()
|
||||
.data(JacksonUtil.toString(dataJson))
|
||||
.metaData(TbMsgMetaData.EMPTY)
|
||||
.build();
|
||||
|
||||
// WHEN
|
||||
String actual = TbNodeUtils.processPattern(pattern, msg);
|
||||
|
||||
// THEN
|
||||
String expected = "{\"data_key\":\"data_value\"} then again {\"data_key\":\"data_value\"}";
|
||||
assertThat(actual, is(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAllDataAndAllMetadataTemplatesSimultaneously() {
|
||||
// GIVEN
|
||||
String pattern = "META ${*} DATA $[*]";
|
||||
|
||||
var metadata = new TbMsgMetaData(Map.of("meta_key", "meta_value"));
|
||||
var dataJson = JacksonUtil.newObjectNode().put("data_key", "data_value");
|
||||
|
||||
var msg = TbMsg.newMsg()
|
||||
.data(JacksonUtil.toString(dataJson))
|
||||
.metaData(metadata)
|
||||
.build();
|
||||
|
||||
// WHEN
|
||||
String actual = TbNodeUtils.processPattern(pattern, msg);
|
||||
|
||||
// THEN
|
||||
String expected = "META {\"meta_key\":\"meta_value\"} DATA {\"data_key\":\"data_value\"}";
|
||||
assertThat(actual, is(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAllDataAndAllMetadataTemplatesSimultaneouslyEmpty() {
|
||||
// GIVEN
|
||||
String pattern = "META ${*} DATA $[*]";
|
||||
|
||||
var msg = TbMsg.newMsg()
|
||||
.data(TbMsg.EMPTY_JSON_OBJECT)
|
||||
.metaData(TbMsgMetaData.EMPTY)
|
||||
.build();
|
||||
|
||||
// WHEN
|
||||
String actual = TbNodeUtils.processPattern(pattern, msg);
|
||||
|
||||
// THEN
|
||||
String expected = "META {} DATA {}";
|
||||
assertThat(actual, is(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAllDataTemplateArray() {
|
||||
// GIVEN
|
||||
String pattern = "DATA $[*]";
|
||||
|
||||
var msg = TbMsg.newMsg()
|
||||
.data("[1, \"two\", true]")
|
||||
.metaData(TbMsgMetaData.EMPTY)
|
||||
.build();
|
||||
|
||||
// WHEN
|
||||
String actual = TbNodeUtils.processPattern(pattern, msg);
|
||||
|
||||
// THEN
|
||||
String expected = "DATA [1,\"two\",true]";
|
||||
assertThat(actual, is(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMixedAllDataMetadataAndNormalTemplates() {
|
||||
// GIVEN
|
||||
String pattern = "fullMeta=${*}, singleMeta=${meta_key}, fullData=$[*], singleData=$[data_key]";
|
||||
var metadata = new TbMsgMetaData(Map.of("meta_key", "meta_value"));
|
||||
var dataJson = JacksonUtil.newObjectNode().put("data_key", "data_value");
|
||||
|
||||
var msg = TbMsg.newMsg()
|
||||
.data(JacksonUtil.toString(dataJson))
|
||||
.metaData(metadata)
|
||||
.build();
|
||||
|
||||
// WHEN
|
||||
String actual = TbNodeUtils.processPattern(pattern, msg);
|
||||
|
||||
// THEN
|
||||
String expected = "fullMeta={\"meta_key\":\"meta_value\"}, singleMeta=meta_value, fullData={\"data_key\":\"data_value\"}, singleData=data_value";
|
||||
assertThat(actual, is(expected));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user