Commit 024dd99cb70d663c483460b11e818a6da64279aa

Authored by Igor Kulikov
1 parent dc41e630

Refactor JS Executor.

Showing 22 changed files with 207 additions and 165 deletions
... ... @@ -321,7 +321,7 @@ public class ActorSystemContext {
321 321 .put("msgId", tbMsg.getId().toString())
322 322 .put("msgType", tbMsg.getType())
323 323 .put("dataType", tbMsg.getDataType().name())
324   - .put("data", convertToString(tbMsg.getDataType(), tbMsg.getData()))
  324 + .put("data", tbMsg.getData())
325 325 .put("metadata", metadata);
326 326
327 327 if (error != null) {
... ... @@ -335,21 +335,6 @@ public class ActorSystemContext {
335 335 }
336 336 }
337 337
338   - private String convertToString(TbMsgDataType messageType, byte[] data) {
339   - if (data == null) {
340   - return null;
341   - }
342   - switch (messageType) {
343   - case JSON:
344   - case TEXT:
345   - return new String(data, StandardCharsets.UTF_8);
346   - case BINARY:
347   - return Base64Utils.encodeToString(data);
348   - default:
349   - throw new RuntimeException("Message type: " + messageType + " is not supported!");
350   - }
351   - }
352   -
353 338 public static Exception toException(Throwable error) {
354 339 return Exception.class.isInstance(error) ? (Exception) error : new Exception(error);
355 340 }
... ...
... ... @@ -149,7 +149,7 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule
149 149 "CUSTOM",
150 150 device.getId(),
151 151 new TbMsgMetaData(),
152   - new byte[]{});
  152 + "{}");
153 153 actorService.onMsg(new ServiceToRuleEngineMsg(savedTenant.getId(), tbMsg));
154 154
155 155 Thread.sleep(3000);
... ...
... ... @@ -135,7 +135,7 @@ public abstract class AbstractRuleEngineLifecycleIntegrationTest extends Abstrac
135 135 "CUSTOM",
136 136 device.getId(),
137 137 new TbMsgMetaData(),
138   - new byte[]{});
  138 + "{}");
139 139 actorService.onMsg(new ServiceToRuleEngineMsg(savedTenant.getId(), tbMsg));
140 140
141 141 Thread.sleep(3000);
... ...
... ... @@ -39,9 +39,9 @@ public final class TbMsg implements Serializable {
39 39 private final EntityId originator;
40 40 private final TbMsgMetaData metaData;
41 41 private final TbMsgDataType dataType;
42   - private final byte[] data;
  42 + private final String data;
43 43
44   - public TbMsg(UUID id, String type, EntityId originator, TbMsgMetaData metaData, byte[] data) {
  44 + public TbMsg(UUID id, String type, EntityId originator, TbMsgMetaData metaData, String data) {
45 45 this.id = id;
46 46 this.type = type;
47 47 this.originator = originator;
... ... @@ -64,7 +64,7 @@ public final class TbMsg implements Serializable {
64 64 }
65 65
66 66 builder.setDataType(msg.getDataType().ordinal());
67   - builder.setData(ByteString.copyFrom(msg.getData()));
  67 + builder.setData(msg.getData());
68 68 byte[] bytes = builder.build().toByteArray();
69 69 return ByteBuffer.wrap(bytes);
70 70 }
... ... @@ -75,16 +75,13 @@ public final class TbMsg implements Serializable {
75 75 TbMsgMetaData metaData = new TbMsgMetaData(proto.getMetaData().getDataMap());
76 76 EntityId entityId = EntityIdFactory.getByTypeAndId(proto.getEntityType(), proto.getEntityId());
77 77 TbMsgDataType dataType = TbMsgDataType.values()[proto.getDataType()];
78   - return new TbMsg(UUID.fromString(proto.getId()), proto.getType(), entityId, metaData, dataType, proto.getData().toByteArray());
  78 + return new TbMsg(UUID.fromString(proto.getId()), proto.getType(), entityId, metaData, dataType, proto.getData());
79 79 } catch (InvalidProtocolBufferException e) {
80 80 throw new IllegalStateException("Could not parse protobuf for TbMsg", e);
81 81 }
82 82 }
83 83
84 84 public TbMsg copy() {
85   - int dataSize = data.length;
86   - byte[] dataCopy = new byte[dataSize];
87   - System.arraycopy( data, 0, dataCopy, 0, data.length );
88   - return new TbMsg(id, type, originator, metaData.copy(), dataType, dataCopy);
  85 + return new TbMsg(id, type, originator, metaData.copy(), dataType, data);
89 86 }
90 87 }
... ...
... ... @@ -33,7 +33,7 @@ public final class TbMsgMetaData implements Serializable {
33 33
34 34 private Map<String, String> data = new ConcurrentHashMap<>();
35 35
36   - TbMsgMetaData(Map<String, String> data) {
  36 + public TbMsgMetaData(Map<String, String> data) {
37 37 this.data = data;
38 38 }
39 39
... ...
... ... @@ -32,5 +32,5 @@ message TbMsgProto {
32 32 TbMsgMetaDataProto metaData = 5;
33 33
34 34 int32 dataType = 6;
35   - bytes data = 7;
  35 + string data = 7;
36 36 }
\ No newline at end of file
... ...
... ... @@ -126,7 +126,7 @@ public class QueueBenchmark implements CommandLineRunner {
126 126 TbMsgMetaData metaData = new TbMsgMetaData();
127 127 metaData.putValue("key", "value");
128 128 String dataStr = "someContent";
129   - return new TbMsg(UUIDs.timeBased(), "type", null, metaData, TbMsgDataType.JSON, dataStr.getBytes());
  129 + return new TbMsg(UUIDs.timeBased(), "type", null, metaData, TbMsgDataType.JSON, dataStr);
130 130 }
131 131
132 132 @Bean
... ...
... ... @@ -45,7 +45,7 @@ public class CassandraMsgRepositoryTest extends AbstractServiceTest {
45 45
46 46 @Test
47 47 public void msgCanBeSavedAndRead() throws ExecutionException, InterruptedException {
48   - TbMsg msg = new TbMsg(UUIDs.timeBased(), "type", new DeviceId(UUIDs.timeBased()), null, TbMsgDataType.JSON, new byte[4]);
  48 + TbMsg msg = new TbMsg(UUIDs.timeBased(), "type", new DeviceId(UUIDs.timeBased()), null, TbMsgDataType.JSON, "0000");
49 49 UUID nodeId = UUIDs.timeBased();
50 50 ListenableFuture<Void> future = msgRepository.save(msg, nodeId, 1L, 1L, 1L);
51 51 future.get();
... ... @@ -55,7 +55,7 @@ public class CassandraMsgRepositoryTest extends AbstractServiceTest {
55 55
56 56 @Test
57 57 public void expiredMsgsAreNotReturned() throws ExecutionException, InterruptedException {
58   - TbMsg msg = new TbMsg(UUIDs.timeBased(), "type", new DeviceId(UUIDs.timeBased()), null, TbMsgDataType.JSON, new byte[4]);
  58 + TbMsg msg = new TbMsg(UUIDs.timeBased(), "type", new DeviceId(UUIDs.timeBased()), null, TbMsgDataType.JSON, "0000");
59 59 UUID nodeId = UUIDs.timeBased();
60 60 ListenableFuture<Void> future = msgRepository.save(msg, nodeId, 2L, 2L, 2L);
61 61 future.get();
... ... @@ -68,7 +68,7 @@ public class CassandraMsgRepositoryTest extends AbstractServiceTest {
68 68 TbMsgMetaData metaData = new TbMsgMetaData();
69 69 metaData.putValue("key", "value");
70 70 String dataStr = "someContent";
71   - TbMsg msg = new TbMsg(UUIDs.timeBased(), "type", new DeviceId(UUIDs.timeBased()), metaData, TbMsgDataType.JSON, dataStr.getBytes());
  71 + TbMsg msg = new TbMsg(UUIDs.timeBased(), "type", new DeviceId(UUIDs.timeBased()), metaData, TbMsgDataType.JSON, dataStr);
72 72 UUID nodeId = UUIDs.timeBased();
73 73 ListenableFuture<Void> future = msgRepository.save(msg, nodeId, 1L, 1L, 1L);
74 74 future.get();
... ...
... ... @@ -16,6 +16,7 @@
16 16 package org.thingsboard.rule.engine.debug;
17 17
18 18 import com.datastax.driver.core.utils.UUIDs;
  19 +import com.google.common.util.concurrent.ListenableFuture;
19 20 import lombok.extern.slf4j.Slf4j;
20 21 import org.springframework.util.StringUtils;
21 22 import org.thingsboard.rule.engine.TbNodeUtils;
... ... @@ -58,9 +59,11 @@ public class TbMsgGeneratorNode implements TbNode {
58 59 public static final String TB_MSG_GENERATOR_NODE_MSG = "TbMsgGeneratorNodeMsg";
59 60
60 61 private TbMsgGeneratorNodeConfiguration config;
  62 + private NashornJsEngine jsEngine;
61 63 private long delay;
62 64 private EntityId originatorId;
63 65 private UUID nextTickId;
  66 + private TbMsg prevMsg;
64 67
65 68 @Override
66 69 public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
... ... @@ -71,29 +74,41 @@ public class TbMsgGeneratorNode implements TbNode {
71 74 } else {
72 75 originatorId = ctx.getSelfId();
73 76 }
  77 + this.jsEngine = new NashornJsEngine(config.getJsScript(), "Generate", "prevMsg", "prevMetadata", "prevMsgType");
74 78 sentTickMsg(ctx);
75 79 }
76 80
77 81 @Override
78 82 public void onMsg(TbContext ctx, TbMsg msg) {
79 83 if (msg.getType().equals(TB_MSG_GENERATOR_NODE_MSG) && msg.getId().equals(nextTickId)) {
80   - TbMsgMetaData metaData = new TbMsgMetaData();
81   - if (config.getMsgMetaData() != null) {
82   - config.getMsgMetaData().forEach(metaData::putValue);
83   - }
84   - ctx.tellNext(new TbMsg(UUIDs.timeBased(), config.getMsgType(), originatorId, metaData, config.getMsgBody().getBytes(StandardCharsets.UTF_8)));
85   - sentTickMsg(ctx);
  84 + withCallback(generate(ctx),
  85 + m -> {ctx.tellNext(m); sentTickMsg(ctx);},
  86 + t -> {ctx.tellError(msg, t); sentTickMsg(ctx);});
86 87 }
87 88 }
88 89
89 90 private void sentTickMsg(TbContext ctx) {
90   - TbMsg tickMsg = new TbMsg(UUIDs.timeBased(), TB_MSG_GENERATOR_NODE_MSG, ctx.getSelfId(), new TbMsgMetaData(), new byte[]{});
  91 + TbMsg tickMsg = new TbMsg(UUIDs.timeBased(), TB_MSG_GENERATOR_NODE_MSG, ctx.getSelfId(), new TbMsgMetaData(), "");
91 92 nextTickId = tickMsg.getId();
92 93 ctx.tellSelf(tickMsg, delay);
93 94 }
94 95
  96 + protected ListenableFuture<TbMsg> generate(TbContext ctx) {
  97 + return ctx.getJsExecutor().executeAsync(() -> {
  98 + if (prevMsg == null) {
  99 + prevMsg = new TbMsg(UUIDs.timeBased(), "", originatorId, new TbMsgMetaData(), "{}");
  100 + }
  101 + TbMsg generated = jsEngine.executeGenerate(prevMsg);
  102 + prevMsg = new TbMsg(UUIDs.timeBased(), generated.getType(), originatorId, generated.getMetaData(), generated.getData());
  103 + return prevMsg;
  104 + });
  105 + }
95 106
96 107 @Override
97 108 public void destroy() {
  109 + prevMsg = null;
  110 + if (jsEngine != null) {
  111 + jsEngine.destroy();
  112 + }
98 113 }
99 114 }
... ...
... ... @@ -28,17 +28,17 @@ public class TbMsgGeneratorNodeConfiguration implements NodeConfiguration<TbMsgG
28 28 private int periodInSeconds;
29 29 private String originatorId;
30 30 private EntityType originatorType;
31   - private String msgType;
32   - private String msgBody;
33   - private Map<String, String> msgMetaData;
  31 + private String jsScript;
34 32
35 33 @Override
36 34 public TbMsgGeneratorNodeConfiguration defaultConfiguration() {
37 35 TbMsgGeneratorNodeConfiguration configuration = new TbMsgGeneratorNodeConfiguration();
38 36 configuration.setMsgCount(0);
39 37 configuration.setPeriodInSeconds(1);
40   - configuration.setMsgType("DebugMsg");
41   - configuration.setMsgBody("{}");
  38 + configuration.setJsScript("var msg = { temp: 42, humidity: 77 };\n" +
  39 + "var metadata = { data: 40 };\n" +
  40 + "var msgType = \"DebugMsg\";\n\n" +
  41 + "return { msg: msg, metadata: metadata, msgType: msgType };");
42 42 return configuration;
43 43 }
44 44 }
... ...
... ... @@ -35,7 +35,8 @@ import static org.thingsboard.rule.engine.DonAsynchron.withCallback;
35 35 nodeDetails = "Evaluate incoming Message with configured JS condition. " +
36 36 "If <b>True</b> - send Message via <b>True</b> chain, otherwise <b>False</b> chain is used." +
37 37 "Message payload can be accessed via <code>msg</code> property. For example <code>msg.temperature < 10;</code>" +
38   - "Message metadata can be accessed via <code>metadata</code> property. For example <code>metadata.customerName === 'John';</code>",
  38 + "Message metadata can be accessed via <code>metadata</code> property. For example <code>metadata.customerName === 'John';</code>" +
  39 + "Message type can be accessed via <code>msgType</code> property.",
39 40 uiResources = {"static/rulenode/rulenode-core-config.js"},
40 41 configDirective = "tbFilterNodeScriptConfig")
41 42
... ... @@ -53,15 +54,11 @@ public class TbJsFilterNode implements TbNode {
53 54 @Override
54 55 public void onMsg(TbContext ctx, TbMsg msg) {
55 56 ListeningExecutor jsExecutor = ctx.getJsExecutor();
56   - withCallback(jsExecutor.executeAsync(() -> jsEngine.executeFilter(toBindings(msg))),
  57 + withCallback(jsExecutor.executeAsync(() -> jsEngine.executeFilter(msg)),
57 58 filterResult -> ctx.tellNext(msg, Boolean.toString(filterResult)),
58 59 t -> ctx.tellError(msg, t));
59 60 }
60 61
61   - private Bindings toBindings(TbMsg msg) {
62   - return NashornJsEngine.bindMsg(msg);
63   - }
64   -
65 62 @Override
66 63 public void destroy() {
67 64 if (jsEngine != null) {
... ...
... ... @@ -36,7 +36,8 @@ import static org.thingsboard.rule.engine.DonAsynchron.withCallback;
36 36 nodeDetails = "Node executes configured JS script. Script should return array of next Chain names where Message should be routed. " +
37 37 "If Array is empty - message not routed to next Node. " +
38 38 "Message payload can be accessed via <code>msg</code> property. For example <code>msg.temperature < 10;</code> " +
39   - "Message metadata can be accessed via <code>metadata</code> property. For example <code>metadata.customerName === 'John';</code>",
  39 + "Message metadata can be accessed via <code>metadata</code> property. For example <code>metadata.customerName === 'John';</code>" +
  40 + "Message type can be accessed via <code>msgType</code> property.",
40 41 uiResources = {"static/rulenode/rulenode-core-config.js"},
41 42 configDirective = "tbFilterNodeSwitchConfig")
42 43 public class TbJsSwitchNode implements TbNode {
... ... @@ -53,7 +54,7 @@ public class TbJsSwitchNode implements TbNode {
53 54 @Override
54 55 public void onMsg(TbContext ctx, TbMsg msg) {
55 56 ListeningExecutor jsExecutor = ctx.getJsExecutor();
56   - withCallback(jsExecutor.executeAsync(() -> jsEngine.executeSwitch(toBindings(msg))),
  57 + withCallback(jsExecutor.executeAsync(() -> jsEngine.executeSwitch(msg)),
57 58 result -> processSwitch(ctx, msg, result),
58 59 t -> ctx.tellError(msg, t));
59 60 }
... ... @@ -62,11 +63,7 @@ public class TbJsSwitchNode implements TbNode {
62 63 ctx.tellNext(msg, nextRelations);
63 64 }
64 65
65   - private Bindings toBindings(TbMsg msg) {
66   - return NashornJsEngine.bindMsg(msg);
67   - }
68   -
69   - @Override
  66 + @Override
70 67 public void destroy() {
71 68 if (jsEngine != null) {
72 69 jsEngine.destroy();
... ...
... ... @@ -16,6 +16,7 @@
16 16 package org.thingsboard.rule.engine.js;
17 17
18 18 import com.fasterxml.jackson.core.JsonProcessingException;
  19 +import com.fasterxml.jackson.core.type.TypeReference;
19 20 import com.fasterxml.jackson.databind.JsonNode;
20 21 import com.fasterxml.jackson.databind.ObjectMapper;
21 22 import com.google.common.collect.Sets;
... ... @@ -23,9 +24,13 @@ import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
23 24 import jdk.nashorn.api.scripting.ScriptObjectMirror;
24 25 import lombok.extern.slf4j.Slf4j;
25 26 import org.apache.commons.lang3.ArrayUtils;
  27 +import org.apache.commons.lang3.StringUtils;
26 28 import org.thingsboard.server.common.msg.TbMsg;
  29 +import org.thingsboard.server.common.msg.TbMsgMetaData;
27 30
28 31 import javax.script.*;
  32 +import java.io.IOException;
  33 +import java.nio.charset.StandardCharsets;
29 34 import java.util.Collections;
30 35 import java.util.Map;
31 36 import java.util.Set;
... ... @@ -34,112 +39,158 @@ import java.util.Set;
34 39 @Slf4j
35 40 public class NashornJsEngine {
36 41
  42 +
  43 + public static final String MSG = "msg";
37 44 public static final String METADATA = "metadata";
38   - public static final String DATA = "msg";
  45 + public static final String MSG_TYPE = "msgType";
39 46
40   - private static final String JS_WRAPPER_PREFIX_TEMPLATE = "function %s(msg, metadata) { ";
41   - private static final String JS_WRAPPER_SUFFIX_TEMPLATE = "}\n %s(msg, metadata);";
  47 + private static final String JS_WRAPPER_PREFIX_TEMPLATE = "function %s(msgStr, metadataStr, msgType) { " +
  48 + " var msg = JSON.parse(msgStr); " +
  49 + " var metadata = JSON.parse(metadataStr); " +
  50 + " return JSON.stringify(%s(msg, metadata, msgType));" +
  51 + " function %s(%s, %s, %s) {";
  52 + private static final String JS_WRAPPER_SUFFIX = "}" +
  53 + "\n}";
42 54
  55 + private static final ObjectMapper mapper = new ObjectMapper();
43 56 private static NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
44   -
45   - private CompiledScript engine;
46   -
47   - public NashornJsEngine(String script, String functionName) {
48   - String jsWrapperPrefix = String.format(JS_WRAPPER_PREFIX_TEMPLATE, functionName);
49   - String jsWrapperSuffix = String.format(JS_WRAPPER_SUFFIX_TEMPLATE, functionName);
50   - engine = compileScript(jsWrapperPrefix + script + jsWrapperSuffix);
  57 + private static ScriptEngine engine = factory.getScriptEngine(new String[]{"--no-java"});
  58 +
  59 + private final String invokeFunctionName;
  60 +
  61 + public NashornJsEngine(String script, String functionName, String... argNames) {
  62 + this.invokeFunctionName = "invokeInternal" + this.hashCode();
  63 + String msgArg;
  64 + String metadataArg;
  65 + String msgTypeArg;
  66 + if (argNames != null && argNames.length == 3) {
  67 + msgArg = argNames[0];
  68 + metadataArg = argNames[1];
  69 + msgTypeArg = argNames[2];
  70 + } else {
  71 + msgArg = MSG;
  72 + metadataArg = METADATA;
  73 + msgTypeArg = MSG_TYPE;
  74 + }
  75 + String jsWrapperPrefix = String.format(JS_WRAPPER_PREFIX_TEMPLATE, this.invokeFunctionName,
  76 + functionName, functionName, msgArg, metadataArg, msgTypeArg);
  77 + compileScript(jsWrapperPrefix + script + JS_WRAPPER_SUFFIX);
51 78 }
52 79
53   - private static CompiledScript compileScript(String script) {
54   - ScriptEngine engine = factory.getScriptEngine(new String[]{"--no-java"});
55   - Compilable compEngine = (Compilable) engine;
  80 + private static void compileScript(String script) {
56 81 try {
57   - return compEngine.compile(script);
  82 + engine.eval(script);
58 83 } catch (ScriptException e) {
59 84 log.warn("Failed to compile JS script: {}", e.getMessage(), e);
60 85 throw new IllegalArgumentException("Can't compile script: " + e.getMessage());
61 86 }
62 87 }
63 88
64   - public static Bindings bindMsg(TbMsg msg) {
  89 + private static String[] prepareArgs(TbMsg msg) {
65 90 try {
66   - Bindings bindings = new SimpleBindings();
67   - if (ArrayUtils.isNotEmpty(msg.getData())) {
68   - ObjectMapper mapper = new ObjectMapper();
69   - JsonNode jsonNode = mapper.readTree(msg.getData());
70   - Map map = mapper.treeToValue(jsonNode, Map.class);
71   - bindings.put(DATA, map);
  91 + String[] args = new String[3];
  92 + if (msg.getData() != null) {
  93 + args[0] = msg.getData();
72 94 } else {
73   - bindings.put(DATA, Collections.emptyMap());
  95 + args[0] = "";
74 96 }
75   - bindings.put(METADATA, msg.getMetaData().getData());
76   - return bindings;
  97 + args[1] = mapper.writeValueAsString(msg.getMetaData().getData());
  98 + args[2] = msg.getType();
  99 + return args;
77 100 } catch (Throwable th) {
78 101 throw new IllegalArgumentException("Cannot bind js args", th);
79 102 }
80 103 }
81 104
82   - private static TbMsg unbindMsg(Bindings bindings, TbMsg msg) throws JsonProcessingException {
83   - for (Map.Entry<String, String> entry : msg.getMetaData().getData().entrySet()) {
84   - Object obj = entry.getValue();
85   - entry.setValue(obj.toString());
  105 + private static TbMsg unbindMsg(JsonNode msgData, TbMsg msg) {
  106 + try {
  107 + String data = null;
  108 + Map<String, String> metadata = null;
  109 + String messageType = null;
  110 + if (msgData.has(MSG)) {
  111 + JsonNode msgPayload = msgData.get(MSG);
  112 + data = mapper.writeValueAsString(msgPayload);
  113 + }
  114 + if (msgData.has(METADATA)) {
  115 + JsonNode msgMetadata = msgData.get(METADATA);
  116 + metadata = mapper.convertValue(msgMetadata, new TypeReference<Map<String, String>>() {
  117 + });
  118 + }
  119 + if (msgData.has(MSG_TYPE)) {
  120 + messageType = msgData.get(MSG_TYPE).asText();
  121 + }
  122 + String newData = data != null ? data : msg.getData();
  123 + TbMsgMetaData newMetadata = metadata != null ? new TbMsgMetaData(metadata) : msg.getMetaData();
  124 + String newMessageType = !StringUtils.isEmpty(messageType) ? messageType : msg.getType();
  125 + return new TbMsg(msg.getId(), newMessageType, msg.getOriginator(), newMetadata, newData);
  126 + } catch (Throwable th) {
  127 + th.printStackTrace();
  128 + throw new RuntimeException("Failed to unbind message data from javascript result", th);
86 129 }
  130 + }
87 131
88   - Object payload = bindings.get(DATA);
89   - if (payload != null) {
90   - ObjectMapper mapper = new ObjectMapper();
91   - byte[] bytes = mapper.writeValueAsBytes(payload);
92   - return new TbMsg(msg.getId(), msg.getType(), msg.getOriginator(), msg.getMetaData(), bytes);
  132 + public TbMsg executeUpdate(TbMsg msg) throws ScriptException {
  133 + JsonNode result = executeScript(msg);
  134 + if (!result.isObject()) {
  135 + log.warn("Wrong result type: {}", result.getNodeType());
  136 + throw new ScriptException("Wrong result type: " + result.getNodeType());
93 137 }
94   -
95   - return msg;
  138 + return unbindMsg(result, msg);
96 139 }
97 140
98   - public TbMsg executeUpdate(Bindings bindings, TbMsg msg) throws ScriptException {
99   - try {
100   - engine.eval(bindings);
101   - return unbindMsg(bindings, msg);
102   - } catch (Throwable th) {
103   - th.printStackTrace();
104   - throw new IllegalArgumentException("Cannot unbind js args", th);
  141 + public TbMsg executeGenerate(TbMsg prevMsg) throws ScriptException {
  142 + JsonNode result = executeScript(prevMsg);
  143 + if (!result.isObject()) {
  144 + log.warn("Wrong result type: {}", result.getNodeType());
  145 + throw new ScriptException("Wrong result type: " + result.getNodeType());
105 146 }
  147 + return unbindMsg(result, prevMsg);
106 148 }
107 149
108   - public boolean executeFilter(Bindings bindings) throws ScriptException {
109   - Object eval = engine.eval(bindings);
110   - if (eval instanceof Boolean) {
111   - return (boolean) eval;
112   - } else {
113   - log.warn("Wrong result type: {}", eval);
114   - throw new ScriptException("Wrong result type: " + eval);
  150 + public boolean executeFilter(TbMsg msg) throws ScriptException {
  151 + JsonNode result = executeScript(msg);
  152 + if (!result.isBoolean()) {
  153 + log.warn("Wrong result type: {}", result.getNodeType());
  154 + throw new ScriptException("Wrong result type: " + result.getNodeType());
115 155 }
  156 + return result.asBoolean();
116 157 }
117 158
118   - public Set<String> executeSwitch(Bindings bindings) throws ScriptException, NoSuchMethodException {
119   - Object eval = this.engine.eval(bindings);
120   - if (eval instanceof String) {
121   - return Collections.singleton((String) eval);
122   - } else if (eval instanceof ScriptObjectMirror) {
123   - ScriptObjectMirror mir = (ScriptObjectMirror) eval;
124   - if (mir.isArray()) {
125   - Set<String> nextStates = Sets.newHashSet();
126   - for (Map.Entry<String, Object> entry : mir.entrySet()) {
127   - if (entry.getValue() instanceof String) {
128   - nextStates.add((String) entry.getValue());
129   - } else {
130   - log.warn("Wrong result type: {}", eval);
131   - throw new ScriptException("Wrong result type: " + eval);
132   - }
  159 + public Set<String> executeSwitch(TbMsg msg) throws ScriptException, NoSuchMethodException {
  160 + JsonNode result = executeScript(msg);
  161 + if (result.isTextual()) {
  162 + return Collections.singleton(result.asText());
  163 + } else if (result.isArray()) {
  164 + Set<String> nextStates = Sets.newHashSet();
  165 + for (JsonNode val : result) {
  166 + if (!val.isTextual()) {
  167 + log.warn("Wrong result type: {}", val.getNodeType());
  168 + throw new ScriptException("Wrong result type: " + val.getNodeType());
  169 + } else {
  170 + nextStates.add(val.asText());
133 171 }
134   - return nextStates;
135 172 }
  173 + return nextStates;
  174 + } else {
  175 + log.warn("Wrong result type: {}", result.getNodeType());
  176 + throw new ScriptException("Wrong result type: " + result.getNodeType());
136 177 }
  178 + }
137 179
138   - log.warn("Wrong result type: {}", eval);
139   - throw new ScriptException("Wrong result type: " + eval);
  180 + private JsonNode executeScript(TbMsg msg) throws ScriptException {
  181 + try {
  182 + String[] inArgs = prepareArgs(msg);
  183 + String eval = ((Invocable)engine).invokeFunction(this.invokeFunctionName, inArgs[0], inArgs[1], inArgs[2]).toString();
  184 + return mapper.readTree(eval);
  185 + } catch (ScriptException | IllegalArgumentException th) {
  186 + throw th;
  187 + } catch (Throwable th) {
  188 + th.printStackTrace();
  189 + throw new RuntimeException("Failed to execute js script", th);
  190 + }
140 191 }
141 192
142 193 public void destroy() {
143   - engine = null;
  194 + //engine = null;
144 195 }
145 196 }
... ...
... ... @@ -64,7 +64,7 @@ public class TbMsgTelemetryNode implements TbNode {
64 64 return;
65 65 }
66 66
67   - String src = new String(msg.getData(), StandardCharsets.UTF_8);
  67 + String src = msg.getData();
68 68 TelemetryUploadRequest telemetryUploadRequest = JsonConverter.convertToTelemetry(new JsonParser().parse(src));
69 69 Map<Long, List<KvEntry>> tsKvMap = telemetryUploadRequest.getData();
70 70 if (tsKvMap == null) {
... ...
... ... @@ -28,10 +28,14 @@ import javax.script.Bindings;
28 28 type = ComponentType.TRANSFORMATION,
29 29 name = "script",
30 30 configClazz = TbTransformMsgNodeConfiguration.class,
31   - nodeDescription = "Change Message payload and Metadata using JavaScript",
32   - nodeDetails = "JavaScript function recieve 2 input parameters that can be changed inside.<br/> " +
  31 + nodeDescription = "Change Message payload, Metadata or Message type using JavaScript",
  32 + nodeDetails = "JavaScript function receive 3 input parameters.<br/> " +
33 33 "<code>metadata</code> - is a Message metadata.<br/>" +
34   - "<code>msg</code> - is a Message payload.<br/>Any properties can be changed/removed/added in those objects.",
  34 + "<code>msg</code> - is a Message payload.<br/>" +
  35 + "<code>msgType</code> - is a Message type.<br/>" +
  36 + "Should return the following structure:<br/>" +
  37 + "<code>{ msg: <new payload>, metadata: <new metadata>, msgType: <new msgType> }</code>" +
  38 + "All fields in resulting object are optional and will be taken from original message if not specified.",
35 39 uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"},
36 40 configDirective = "tbTransformationNodeScriptConfig")
37 41 public class TbTransformMsgNode extends TbAbstractTransformNode {
... ... @@ -48,11 +52,7 @@ public class TbTransformMsgNode extends TbAbstractTransformNode {
48 52
49 53 @Override
50 54 protected ListenableFuture<TbMsg> transform(TbContext ctx, TbMsg msg) {
51   - return ctx.getJsExecutor().executeAsync(() -> jsEngine.executeUpdate(toBindings(msg), msg));
52   - }
53   -
54   - private Bindings toBindings(TbMsg msg) {
55   - return NashornJsEngine.bindMsg(msg);
  55 + return ctx.getJsExecutor().executeAsync(() -> jsEngine.executeUpdate(msg));
56 56 }
57 57
58 58 @Override
... ...
... ... @@ -27,7 +27,7 @@ public class TbTransformMsgNodeConfiguration extends TbTransformNodeConfiguratio
27 27 public TbTransformMsgNodeConfiguration defaultConfiguration() {
28 28 TbTransformMsgNodeConfiguration configuration = new TbTransformMsgNodeConfiguration();
29 29 configuration.setStartNewChain(false);
30   - configuration.setJsScript("return msg.passed = msg.passed * metadata.temp; msg.bigObj.newProp = 'Ukraine' ");
  30 + configuration.setJsScript("return {msg: msg, metadata: metadata, msgType: msgType};");
31 31 return configuration;
32 32 }
33 33 }
... ...
1   -!function(e){function t(a){if(n[a])return n[a].exports;var r=n[a]={exports:{},id:a,loaded:!1};return e[a].call(r.exports,r,r.exports,t),r.loaded=!0,r.exports}var n={};return t.m=e,t.c=n,t.p="/static/",t(0)}(function(e){for(var t in e)if(Object.prototype.hasOwnProperty.call(e,t))switch(typeof e[t]){case"function":break;case"object":e[t]=function(t){var n=t.slice(1),a=e[t[0]];return function(e,t,r){a.apply(this,[e,t,r].concat(n))}}(e[t]);break;default:e[t]=e[e[t]]}return e}([function(e,t,n){e.exports=n(34)},function(e,t){},1,1,function(e,t){e.exports=" <section class=tb-generator-config ng-form name=generatorConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.message-count</label> <input ng-required=true type=number step=1 name=messageCount ng-model=configuration.msgCount min=0> <div ng-messages=generatorConfigForm.messageCount.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.message-count-required</div> <div ng-message=min translate>tb.rulenode.min-message-count-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.period-seconds</label> <input ng-required=true type=number step=1 name=periodInSeconds ng-model=configuration.periodInSeconds min=1> <div ng-messages=generatorConfigForm.periodInSeconds.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.period-seconds-required</div> <div ng-message=min translate>tb.rulenode.min-period-seconds-message</div> </div> </md-input-container> <div flex layout=column> <label class=tb-small>{{ 'tb.rulenode.originator' | translate }}</label> <tb-entity-select flex the-form=generatorConfigForm tb-required=false ng-model=originator> </tb-entity-select> </div> <md-input-container class=md-block> <label translate>tb.rulenode.message-type</label> <input ng-required=true name=messageType ng-model=configuration.msgType> <div ng-messages=generatorConfigForm.messageType.$error> <div ng-message=required translate>tb.rulenode.message-type-required</div> </div> </md-input-container> <tb-json-content class=tb-message-body ng-model=configuration.msgBody label=\"{{ 'tb.rulenode.message-body' | translate }}\" content-type=types.contentType.JSON.value validate-content=false fill-height=true> </tb-json-content> <tb-json-object-edit class=tb-metadata-json ng-model=configuration.msgMetaData label=\"{{ 'tb.rulenode.message-metadata' | translate }}\" fill-height=true> </tb-json-object-edit> </section> "},function(e,t){e.exports=" <section ng-form name=telemetryConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.default-ttl</label> <input ng-required=true type=number step=1 name=defaultTTL ng-model=configuration.defaultTTL min=0> <div ng-messages=telemetryConfigForm.defaultTTL.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.default-ttl-required</div> <div ng-message=min translate>tb.rulenode.min-default-ttl-message</div> </div> </md-input-container> </section> "},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title tb-required\">tb.rulenode.attr-mapping</label> <md-checkbox aria-label=\"{{ 'tb.rulenode.latest-telemetry' | translate }}\" ng-model=configuration.telemetry>{{ 'tb.rulenode.latest-telemetry' | translate }} </md-checkbox> <tb-kv-map-config ng-model=configuration.attrMapping ng-required=true required-text=\"'tb.rulenode.attr-mapping-required'\" key-text=\"configuration.telemetry ? 'tb.rulenode.source-telemetry' : 'tb.rulenode.source-attribute'\" key-required-text=\"configuration.telemetry ? 'tb.rulenode.source-telemetry-required' : 'tb.rulenode.source-attribute-required'\" val-text=\"'tb.rulenode.target-attribute'\" val-required-text=\"'tb.rulenode.target-attribute-required'\"> </tb-kv-map-config> </section> "},function(e,t){e.exports=' <section layout=column> <label translate class="tb-title no-padding">tb.rulenode.client-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.clientAttributeNames placeholder="{{\'tb.rulenode.client-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.shared-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.sharedAttributeNames placeholder="{{\'tb.rulenode.shared-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.server-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.serverAttributeNames placeholder="{{\'tb.rulenode.server-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.latest-timeseries</label> <md-chips ng-required=false readonly=readonly ng-model=configuration.latestTsKeyNames placeholder="{{\'tb.rulenode.latest-timeseries\' | translate}}" md-separator-keys=separatorKeys> </md-chips> </section> '},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title tb-required\">tb.rulenode.relations-query</label> <tb-relations-query-config style=padding-bottom:15px ng-model=configuration.relationsQuery> </tb-relations-query-config> <label translate class=\"tb-title tb-required\">tb.rulenode.attr-mapping</label> <md-checkbox aria-label=\"{{ 'tb.rulenode.latest-telemetry' | translate }}\" ng-model=configuration.telemetry>{{ 'tb.rulenode.latest-telemetry' | translate }} </md-checkbox> <tb-kv-map-config ng-model=configuration.attrMapping ng-required=true required-text=\"'tb.rulenode.attr-mapping-required'\" key-text=\"configuration.telemetry ? 'tb.rulenode.source-telemetry' : 'tb.rulenode.source-attribute'\" key-required-text=\"configuration.telemetry ? 'tb.rulenode.source-telemetry-required' : 'tb.rulenode.source-attribute-required'\" val-text=\"'tb.rulenode.target-attribute'\" val-required-text=\"'tb.rulenode.target-attribute-required'\"> </tb-kv-map-config> </section> "},6,function(e,t){e.exports=' <section layout=column> <label translate class="tb-title no-padding" ng-class="{\'tb-required\': required}">tb.rulenode.message-types-filter</label> <md-chips id=message_type_chips ng-required=required readonly=readonly ng-model=messageTypes md-autocomplete-snap md-transform-chip=transformMessageTypeChip($chip) md-require-match=false> <md-autocomplete id=message_type md-no-cache=true md-selected-item=selectedMessageType md-search-text=messageTypeSearchText md-items="item in messageTypesSearch(messageTypeSearchText)" md-item-text=item.name md-min-length=0 placeholder="{{\'tb.rulenode.message-type\' | translate }}" md-menu-class=tb-message-type-autocomplete> <span md-highlight-text=messageTypeSearchText md-highlight-flags=^i>{{item}}</span> <md-not-found> <div class=tb-not-found> <div class=tb-no-entries ng-if="!messageTypeSearchText || !messageTypeSearchText.length"> <span translate>tb.rulenode.no-message-types-found</span> </div> <div ng-if="messageTypeSearchText && messageTypeSearchText.length"> <span translate translate-values=\'{ messageType: "{{messageTypeSearchText | truncate:true:6:&apos;...&apos;}}" }\'>tb.rulenode.no-message-type-matching</span> <span> <a translate ng-click="createMessageType($event, \'#message_type_chips\')">tb.rulenode.create-new-message-type</a> </span> </div> </div> </md-not-found> </md-autocomplete> <md-chip-template> <span>{{$chip.name}}</span> </md-chip-template> </md-chips> <div class=tb-error-messages ng-messages=ngModelCtrl.$error role=alert> <div translate ng-message=messageTypes class=tb-error-message>tb.rulenode.message-types-required</div> </div> </section>'},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.filter</label> <tb-js-func ng-model=configuration.jsScript function-name=Filter function-args=\"{{ ['msg', 'metadata'] }}\" no-validate=true> </tb-js-func> </section> "},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.switch</label> <tb-js-func ng-model=configuration.jsScript function-name=Switch function-args=\"{{ ['msg', 'metadata'] }}\" no-validate=true> </tb-js-func> </section> "},function(e,t){e.exports=' <section class=tb-kv-map-config layout=column> <div class=header flex layout=row> <span class=cell flex translate>{{ keyText }}</span> <span class=cell flex translate>{{ valText }}</span> <span ng-show=!disabled style=width:52px>&nbsp</span> </div> <div class=body> <div class=row ng-form name=kvForm flex layout=row layout-align="start center" ng-repeat="keyVal in kvList track by $index"> <md-input-container class="cell md-block" flex md-no-float> <input placeholder="{{ keyText | translate }}" ng-required=true name=key ng-model=keyVal.key> <div ng-messages=kvForm.key.$error> <div translate ng-message=required>{{keyRequiredText}}</div> </div> </md-input-container> <md-input-container class="cell md-block" flex md-no-float> <input placeholder="{{ valText | translate }}" ng-required=true name=value ng-model=keyVal.value> <div ng-messages=kvForm.value.$error> <div translate ng-message=required>{{valRequiredText}}</div> </div> </md-input-container> <md-button ng-show=!disabled ng-disabled=loading class="md-icon-button md-primary" ng-click=removeKeyVal($index) aria-label="{{ \'action.remove\' | translate }}"> <md-tooltip md-direction=top> {{ \'tb.key-val.remove-entry\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.delete\' | translate }}" class=material-icons> close </md-icon> </md-button> </div> </div> <div class=tb-error-messages ng-messages=ngModelCtrl.$error role=alert> <div translate ng-message=kvMap class=tb-error-message>{{requiredText}}</div> </div> <div> <md-button ng-show=!disabled ng-disabled=loading class="md-primary md-raised" ng-click=addKeyVal() aria-label="{{ \'action.add\' | translate }}"> <md-tooltip md-direction=top> {{ \'tb.key-val.add-entry\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.add\' | translate }}" class=material-icons> add </md-icon> {{ \'action.add\' | translate }} </md-button> </div> </section> '},function(e,t){e.exports=" <section layout=column> <div flex layout=row> <md-input-container class=md-block style=min-width:100px> <label translate>relation.direction</label> <md-select required ng-model=query.direction> <md-option ng-repeat=\"direction in types.entitySearchDirection\" ng-value=direction> {{ ('relation.search-direction.' + direction) | translate}} </md-option> </md-select> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.max-relation-level</label> <input name=maxRelationLevel type=number min=1 step=1 placeholder=\"{{ 'tb.rulenode.unlimited-level' | translate }}\" ng-model=query.maxLevel aria-label=\"{{ 'tb.rulenode.max-relation-level' | translate }}\"> </md-input-container> </div> <div class=md-caption style=padding-bottom:10px;color:rgba(0,0,0,.57) translate>relation.relation-filters</div> <tb-relation-filters ng-model=query.filters> </tb-relation-filters> </section> "},function(e,t){e.exports=' <section layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.originator-source</label> <md-select required ng-model=configuration.originatorSource> <md-option ng-repeat="source in ruleNodeTypes.originatorSource" ng-value=source.value> {{ source.name | translate}} </md-option> </md-select> </md-input-container> <section layout=column ng-if="configuration.originatorSource == ruleNodeTypes.originatorSource.RELATED.value"> <label translate class="tb-title tb-required">tb.rulenode.relations-query</label> <tb-relations-query-config style=padding-bottom:15px ng-model=configuration.relationsQuery> </tb-relations-query-config> </section> <md-checkbox aria-label="{{ \'tb.rulenode.clone-message\' | translate }}" ng-model=configuration.startNewChain>{{ \'tb.rulenode.clone-message\' | translate }} </md-checkbox> </section> '},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.transform</label> <tb-js-func ng-model=configuration.jsScript function-name=Transform function-args=\"{{ ['msg', 'metadata'] }}\" no-validate=true> </tb-js-func> <md-checkbox aria-label=\"{{ 'tb.rulenode.clone-message' | translate }}\" ng-model=configuration.startNewChain>{{ 'tb.rulenode.clone-message' | translate }} </md-checkbox> </section> "},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.originator=null,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue,n.configuration.originatorId&&n.configuration.originatorType?n.originator={id:n.configuration.originatorId,entityType:n.configuration.originatorType}:n.originator=null,n.$watch("originator",function(e,t){angular.equals(e,t)||(n.originator?(i.$viewValue.originatorId=n.originator.id,i.$viewValue.originatorType=n.originator.entityType):(i.$viewValue.originatorId=null,i.$viewValue.originatorType=null))},!0)},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r,n(1);var i=n(4),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(19),i=a(r),o=n(17),l=a(o);t.default=angular.module("thingsboard.ruleChain.config.action",[]).directive("tbActionNodeTelemetryConfig",i.default).directive("tbActionNodeGeneratorConfig",l.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(5),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(6),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(22),i=a(r),o=n(23),l=a(o),s=n(20),u=a(s),d=n(24),c=a(d);t.default=angular.module("thingsboard.ruleChain.config.enrichment",[]).directive("tbEnrichmentNodeOriginatorAttributesConfig",i.default).directive("tbEnrichmentNodeRelatedAttributesConfig",l.default).directive("tbEnrichmentNodeCustomerAttributesConfig",u.default).directive("tbEnrichmentNodeTenantAttributesConfig",c.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(7),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(8),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(9),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(27),i=a(r),o=n(26),l=a(o),s=n(28),u=a(s);t.default=angular.module("thingsboard.ruleChain.config.filter",[]).directive("tbFilterNodeScriptConfig",i.default).directive("tbFilterNodeMessageTypeConfig",l.default).directive("tbFilterNodeSwitchConfig",u.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){function s(){if(l.$viewValue){for(var e=[],t=0;t<a.messageTypes.length;t++)e.push(a.messageTypes[t].value);l.$viewValue.messageTypes=e,u()}}function u(){if(a.required){var e=!(!l.$viewValue.messageTypes||!l.$viewValue.messageTypes.length);l.$setValidity("messageTypes",e)}else l.$setValidity("messageTypes",!0)}var d=o.default;r.html(d),a.selectedMessageType=null,a.messageTypeSearchText=null,a.ngModelCtrl=l;var c=[];for(var m in n.messageType){var g={name:n.messageType[m].name,value:n.messageType[m].value};c.push(g)}a.transformMessageTypeChip=function(e){var n,a=t("filter")(c,{name:e},!0);return n=a&&a.length?angular.copy(a[0]):{name:e,value:e}},a.messageTypesSearch=function(e){var n=e?t("filter")(c,{name:e}):c;return n.map(function(e){return e.name})},a.createMessageType=function(e,t){var n=angular.element(t,r)[0].firstElementChild,a=angular.element(n),i=a.scope().$mdChipsCtrl.getChipBuffer();e.preventDefault(),e.stopPropagation(),a.scope().$mdChipsCtrl.appendChip(i.trim()),a.scope().$mdChipsCtrl.resetChipBuffer()},l.$render=function(){var e=l.$viewValue,t=[];if(e&&e.messageTypes)for(var r=0;r<e.messageTypes.length;r++){var i=e.messageTypes[r];n.messageType[i]?t.push(angular.copy(n.messageType[i])):t.push({name:i,value:i})}a.messageTypes=t,a.$watch("messageTypes",function(e,t){angular.equals(e,t)||s()},!0)},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",readonly:"=ngReadonly"},link:a}}r.$inject=["$compile","$filter","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r,n(2);var i=n(10),o=a(i)},[38,11],function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(12),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){function i(e){e>-1&&t.kvList.splice(e,1)}function l(){t.kvList||(t.kvList=[]),t.kvList.push({key:"",value:""})}function s(){var e={};t.kvList.forEach(function(t){t.key&&(e[t.key]=t.value)}),r.$setViewValue(e),u()}function u(){var e=!0;t.required&&!t.kvList.length&&(e=!1),r.$setValidity("kvMap",e)}var d=o.default;n.html(d),t.ngModelCtrl=r,t.removeKeyVal=i,t.addKeyVal=l,t.kvList=[],t.$watch("query",function(e,n){angular.equals(e,n)||r.$setViewValue(t.query)}),r.$render=function(){if(r.$viewValue){var e=r.$viewValue;t.kvList.length=0;for(var n in e)t.kvList.push({key:n,value:e[n]})}t.$watch("kvList",function(e,t){angular.equals(e,t)||s()},!0),u()},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",disabled:"=ngDisabled",requiredText:"=",keyText:"=",keyRequiredText:"=",valText:"=",valRequiredText:"="},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(13),o=a(i);n(3)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||i.$setViewValue(n.query)}),i.$render=function(){n.query=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(14),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(15),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(31),i=a(r),o=n(33),l=a(o);t.default=angular.module("thingsboard.ruleChain.config.transform",[]).directive("tbTransformationNodeChangeOriginatorConfig",i.default).directive("tbTransformationNodeScriptConfig",l.default).name},[38,16],function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(37),i=a(r),o=n(25),l=a(o),s=n(21),u=a(s),d=n(32),c=a(d),m=n(18),g=a(m),f=n(30),p=a(f),b=n(29),y=a(b),v=n(36),h=a(v);t.default=angular.module("thingsboard.ruleChain.config",[i.default,l.default,u.default,c.default,g.default]).directive("tbRelationsQueryConfig",p.default).directive("tbKvMapConfig",y.default).config(h.default).name},function(e,t){"use strict";function n(e){var t={tb:{rulenode:{filter:"Filter",switch:"Switch","message-type":"Message type","message-type-required":"Message type is required.","message-types-filter":"Message types filter","no-message-types-found":"No message types found","no-message-type-matching":"'{{messageType}}' not found.","create-new-message-type":"Create a new one!","message-types-required":"Message types are required.","client-attributes":"Client attributes","shared-attributes":"Shared attributes","server-attributes":"Server attributes","latest-timeseries":"Latest timeseries","relations-query":"Relations query","max-relation-level":"Max relation level","unlimited-level":"Unlimited level","latest-telemetry":"Latest telemetry","attr-mapping":"Attributes mapping","source-attribute":"Source attribute","source-attribute-required":"Source attribute is required.","source-telemetry":"Source telemetry","source-telemetry-required":"Source telemetry is required.","target-attribute":"Target attribute","target-attribute-required":"Target attribute is required.","attr-mapping-required":"At least one attribute mapping should be specified.","originator-source":"Originator source","originator-customer":"Customer","originator-tenant":"Tenant","originator-related":"Related","clone-message":"Clone message",transform:"Transform","default-ttl":"Default TTL in seconds","default-ttl-required":"Default TTL is required.","min-default-ttl-message":"Only 0 minimum TTL is allowed.","message-count":"Message count (0 - unlimited)","message-count-required":"Message count is required.","min-message-count-message":"Only 0 minimum message count is allowed.","period-seconds":"Period in seconds","period-seconds-required":"Period is required.","min-period-seconds-message":"Only 1 second minimum period is allowed.",originator:"Originator","message-body":"Message body","message-metadata":"Message metadata"},"key-val":{key:"Key",value:"Value","remove-entry":"Remove entry","add-entry":"Add entry"}}};angular.merge(e.en_US,t)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){(0,o.default)(t);for(var n in t){var a=t[n];e.translations(n,a)}}r.$inject=["$translateProvider","locales"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(35),o=a(i)},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=angular.module("thingsboard.ruleChain.config.types",[]).constant("ruleNodeTypes",{messageType:{POST_ATTRIBUTES:{name:"Post attributes",value:"POST_ATTRIBUTES"},POST_TELEMETRY:{name:"Post telemetry",value:"POST_TELEMETRY"},RPC_REQUEST:{name:"RPC Request",value:"RPC_REQUEST"}},originatorSource:{CUSTOMER:{name:"tb.rulenode.originator-customer",value:"CUSTOMER"},TENANT:{name:"tb.rulenode.originator-tenant",value:"TENANT"},RELATED:{name:"tb.rulenode.originator-related",value:"RELATED"}}}).name},function(e,t,n,a){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,r){var i=l.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var o=n(a),l=r(o)}]));
  1 +!function(e){function t(r){if(n[r])return n[r].exports;var a=n[r]={exports:{},id:r,loaded:!1};return e[r].call(a.exports,a,a.exports,t),a.loaded=!0,a.exports}var n={};return t.m=e,t.c=n,t.p="/static/",t(0)}(function(e){for(var t in e)if(Object.prototype.hasOwnProperty.call(e,t))switch(typeof e[t]){case"function":break;case"object":e[t]=function(t){var n=t.slice(1),r=e[t[0]];return function(e,t,a){r.apply(this,[e,t,a].concat(n))}}(e[t]);break;default:e[t]=e[e[t]]}return e}([function(e,t,n){e.exports=n(34)},function(e,t){},1,1,function(e,t){e.exports=" <section class=tb-generator-config ng-form name=generatorConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.message-count</label> <input ng-required=true type=number step=1 name=messageCount ng-model=configuration.msgCount min=0> <div ng-messages=generatorConfigForm.messageCount.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.message-count-required</div> <div ng-message=min translate>tb.rulenode.min-message-count-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.period-seconds</label> <input ng-required=true type=number step=1 name=periodInSeconds ng-model=configuration.periodInSeconds min=1> <div ng-messages=generatorConfigForm.periodInSeconds.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.period-seconds-required</div> <div ng-message=min translate>tb.rulenode.min-period-seconds-message</div> </div> </md-input-container> <div flex layout=column> <label class=tb-small>{{ 'tb.rulenode.originator' | translate }}</label> <tb-entity-select flex the-form=generatorConfigForm tb-required=false ng-model=originator> </tb-entity-select> </div> <label translate class=\"tb-title no-padding\">tb.rulenode.generate</label> <tb-js-func ng-model=configuration.jsScript function-name=Generate function-args=\"{{ ['prevMsg', 'prevMetadata', 'prevMsgType'] }}\" no-validate=true> </tb-js-func> </section> "},function(e,t){e.exports=" <section ng-form name=telemetryConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.default-ttl</label> <input ng-required=true type=number step=1 name=defaultTTL ng-model=configuration.defaultTTL min=0> <div ng-messages=telemetryConfigForm.defaultTTL.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.default-ttl-required</div> <div ng-message=min translate>tb.rulenode.min-default-ttl-message</div> </div> </md-input-container> </section> "},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title tb-required\">tb.rulenode.attr-mapping</label> <md-checkbox aria-label=\"{{ 'tb.rulenode.latest-telemetry' | translate }}\" ng-model=configuration.telemetry>{{ 'tb.rulenode.latest-telemetry' | translate }} </md-checkbox> <tb-kv-map-config ng-model=configuration.attrMapping ng-required=true required-text=\"'tb.rulenode.attr-mapping-required'\" key-text=\"configuration.telemetry ? 'tb.rulenode.source-telemetry' : 'tb.rulenode.source-attribute'\" key-required-text=\"configuration.telemetry ? 'tb.rulenode.source-telemetry-required' : 'tb.rulenode.source-attribute-required'\" val-text=\"'tb.rulenode.target-attribute'\" val-required-text=\"'tb.rulenode.target-attribute-required'\"> </tb-kv-map-config> </section> "},function(e,t){e.exports=' <section layout=column> <label translate class="tb-title no-padding">tb.rulenode.client-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.clientAttributeNames placeholder="{{\'tb.rulenode.client-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.shared-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.sharedAttributeNames placeholder="{{\'tb.rulenode.shared-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.server-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.serverAttributeNames placeholder="{{\'tb.rulenode.server-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.latest-timeseries</label> <md-chips ng-required=false readonly=readonly ng-model=configuration.latestTsKeyNames placeholder="{{\'tb.rulenode.latest-timeseries\' | translate}}" md-separator-keys=separatorKeys> </md-chips> </section> '},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title tb-required\">tb.rulenode.relations-query</label> <tb-relations-query-config style=padding-bottom:15px ng-model=configuration.relationsQuery> </tb-relations-query-config> <label translate class=\"tb-title tb-required\">tb.rulenode.attr-mapping</label> <md-checkbox aria-label=\"{{ 'tb.rulenode.latest-telemetry' | translate }}\" ng-model=configuration.telemetry>{{ 'tb.rulenode.latest-telemetry' | translate }} </md-checkbox> <tb-kv-map-config ng-model=configuration.attrMapping ng-required=true required-text=\"'tb.rulenode.attr-mapping-required'\" key-text=\"configuration.telemetry ? 'tb.rulenode.source-telemetry' : 'tb.rulenode.source-attribute'\" key-required-text=\"configuration.telemetry ? 'tb.rulenode.source-telemetry-required' : 'tb.rulenode.source-attribute-required'\" val-text=\"'tb.rulenode.target-attribute'\" val-required-text=\"'tb.rulenode.target-attribute-required'\"> </tb-kv-map-config> </section> "},6,function(e,t){e.exports=' <section layout=column> <label translate class="tb-title no-padding" ng-class="{\'tb-required\': required}">tb.rulenode.message-types-filter</label> <md-chips id=message_type_chips ng-required=required readonly=readonly ng-model=messageTypes md-autocomplete-snap md-transform-chip=transformMessageTypeChip($chip) md-require-match=false> <md-autocomplete id=message_type md-no-cache=true md-selected-item=selectedMessageType md-search-text=messageTypeSearchText md-items="item in messageTypesSearch(messageTypeSearchText)" md-item-text=item.name md-min-length=0 placeholder="{{\'tb.rulenode.message-type\' | translate }}" md-menu-class=tb-message-type-autocomplete> <span md-highlight-text=messageTypeSearchText md-highlight-flags=^i>{{item}}</span> <md-not-found> <div class=tb-not-found> <div class=tb-no-entries ng-if="!messageTypeSearchText || !messageTypeSearchText.length"> <span translate>tb.rulenode.no-message-types-found</span> </div> <div ng-if="messageTypeSearchText && messageTypeSearchText.length"> <span translate translate-values=\'{ messageType: "{{messageTypeSearchText | truncate:true:6:&apos;...&apos;}}" }\'>tb.rulenode.no-message-type-matching</span> <span> <a translate ng-click="createMessageType($event, \'#message_type_chips\')">tb.rulenode.create-new-message-type</a> </span> </div> </div> </md-not-found> </md-autocomplete> <md-chip-template> <span>{{$chip.name}}</span> </md-chip-template> </md-chips> <div class=tb-error-messages ng-messages=ngModelCtrl.$error role=alert> <div translate ng-message=messageTypes class=tb-error-message>tb.rulenode.message-types-required</div> </div> </section>'},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.filter</label> <tb-js-func ng-model=configuration.jsScript function-name=Filter function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> </section> "},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.switch</label> <tb-js-func ng-model=configuration.jsScript function-name=Switch function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> </section> "},function(e,t){e.exports=' <section class=tb-kv-map-config layout=column> <div class=header flex layout=row> <span class=cell flex translate>{{ keyText }}</span> <span class=cell flex translate>{{ valText }}</span> <span ng-show=!disabled style=width:52px>&nbsp</span> </div> <div class=body> <div class=row ng-form name=kvForm flex layout=row layout-align="start center" ng-repeat="keyVal in kvList track by $index"> <md-input-container class="cell md-block" flex md-no-float> <input placeholder="{{ keyText | translate }}" ng-required=true name=key ng-model=keyVal.key> <div ng-messages=kvForm.key.$error> <div translate ng-message=required>{{keyRequiredText}}</div> </div> </md-input-container> <md-input-container class="cell md-block" flex md-no-float> <input placeholder="{{ valText | translate }}" ng-required=true name=value ng-model=keyVal.value> <div ng-messages=kvForm.value.$error> <div translate ng-message=required>{{valRequiredText}}</div> </div> </md-input-container> <md-button ng-show=!disabled ng-disabled=loading class="md-icon-button md-primary" ng-click=removeKeyVal($index) aria-label="{{ \'action.remove\' | translate }}"> <md-tooltip md-direction=top> {{ \'tb.key-val.remove-entry\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.delete\' | translate }}" class=material-icons> close </md-icon> </md-button> </div> </div> <div class=tb-error-messages ng-messages=ngModelCtrl.$error role=alert> <div translate ng-message=kvMap class=tb-error-message>{{requiredText}}</div> </div> <div> <md-button ng-show=!disabled ng-disabled=loading class="md-primary md-raised" ng-click=addKeyVal() aria-label="{{ \'action.add\' | translate }}"> <md-tooltip md-direction=top> {{ \'tb.key-val.add-entry\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.add\' | translate }}" class=material-icons> add </md-icon> {{ \'action.add\' | translate }} </md-button> </div> </section> '},function(e,t){e.exports=" <section layout=column> <div flex layout=row> <md-input-container class=md-block style=min-width:100px> <label translate>relation.direction</label> <md-select required ng-model=query.direction> <md-option ng-repeat=\"direction in types.entitySearchDirection\" ng-value=direction> {{ ('relation.search-direction.' + direction) | translate}} </md-option> </md-select> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.max-relation-level</label> <input name=maxRelationLevel type=number min=1 step=1 placeholder=\"{{ 'tb.rulenode.unlimited-level' | translate }}\" ng-model=query.maxLevel aria-label=\"{{ 'tb.rulenode.max-relation-level' | translate }}\"> </md-input-container> </div> <div class=md-caption style=padding-bottom:10px;color:rgba(0,0,0,.57) translate>relation.relation-filters</div> <tb-relation-filters ng-model=query.filters> </tb-relation-filters> </section> "},function(e,t){e.exports=' <section layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.originator-source</label> <md-select required ng-model=configuration.originatorSource> <md-option ng-repeat="source in ruleNodeTypes.originatorSource" ng-value=source.value> {{ source.name | translate}} </md-option> </md-select> </md-input-container> <section layout=column ng-if="configuration.originatorSource == ruleNodeTypes.originatorSource.RELATED.value"> <label translate class="tb-title tb-required">tb.rulenode.relations-query</label> <tb-relations-query-config style=padding-bottom:15px ng-model=configuration.relationsQuery> </tb-relations-query-config> </section> <md-checkbox aria-label="{{ \'tb.rulenode.clone-message\' | translate }}" ng-model=configuration.startNewChain>{{ \'tb.rulenode.clone-message\' | translate }} </md-checkbox> </section> '},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.transform</label> <tb-js-func ng-model=configuration.jsScript function-name=Transform function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <md-checkbox aria-label=\"{{ 'tb.rulenode.clone-message' | translate }}\" ng-model=configuration.startNewChain>{{ 'tb.rulenode.clone-message' | translate }} </md-checkbox> </section> "},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,r,a,i){var l=o.default;r.html(l),n.types=t,n.originator=null,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue,n.configuration.originatorId&&n.configuration.originatorType?n.originator={id:n.configuration.originatorId,entityType:n.configuration.originatorType}:n.originator=null,n.$watch("originator",function(e,t){angular.equals(e,t)||(n.originator?(i.$viewValue.originatorId=n.originator.id,i.$viewValue.originatorType=n.originator.entityType):(i.$viewValue.originatorId=null,i.$viewValue.originatorType=null))},!0)},e(r.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a,n(1);var i=n(4),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(19),i=r(a),o=n(17),l=r(o);t.default=angular.module("thingsboard.ruleChain.config.action",[]).directive("tbActionNodeTelemetryConfig",i.default).directive("tbActionNodeGeneratorConfig",l.default).name},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,r,a){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(5),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,r,a){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(6),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(22),i=r(a),o=n(23),l=r(o),s=n(20),u=r(s),d=n(24),c=r(d);t.default=angular.module("thingsboard.ruleChain.config.enrichment",[]).directive("tbEnrichmentNodeOriginatorAttributesConfig",i.default).directive("tbEnrichmentNodeRelatedAttributesConfig",l.default).directive("tbEnrichmentNodeCustomerAttributesConfig",u.default).directive("tbEnrichmentNodeTenantAttributesConfig",c.default).name},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,r,a,i){var l=o.default;r.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(r.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(7),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,r,a){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(8),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,r,a){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(9),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(27),i=r(a),o=n(26),l=r(o),s=n(28),u=r(s);t.default=angular.module("thingsboard.ruleChain.config.filter",[]).directive("tbFilterNodeScriptConfig",i.default).directive("tbFilterNodeMessageTypeConfig",l.default).directive("tbFilterNodeSwitchConfig",u.default).name},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var r=function(r,a,i,l){function s(){if(l.$viewValue){for(var e=[],t=0;t<r.messageTypes.length;t++)e.push(r.messageTypes[t].value);l.$viewValue.messageTypes=e,u()}}function u(){if(r.required){var e=!(!l.$viewValue.messageTypes||!l.$viewValue.messageTypes.length);l.$setValidity("messageTypes",e)}else l.$setValidity("messageTypes",!0)}var d=o.default;a.html(d),r.selectedMessageType=null,r.messageTypeSearchText=null,r.ngModelCtrl=l;var c=[];for(var m in n.messageType){var g={name:n.messageType[m].name,value:n.messageType[m].value};c.push(g)}r.transformMessageTypeChip=function(e){var n,r=t("filter")(c,{name:e},!0);return n=r&&r.length?angular.copy(r[0]):{name:e,value:e}},r.messageTypesSearch=function(e){var n=e?t("filter")(c,{name:e}):c;return n.map(function(e){return e.name})},r.createMessageType=function(e,t){var n=angular.element(t,a)[0].firstElementChild,r=angular.element(n),i=r.scope().$mdChipsCtrl.getChipBuffer();e.preventDefault(),e.stopPropagation(),r.scope().$mdChipsCtrl.appendChip(i.trim()),r.scope().$mdChipsCtrl.resetChipBuffer()},l.$render=function(){var e=l.$viewValue,t=[];if(e&&e.messageTypes)for(var a=0;a<e.messageTypes.length;a++){var i=e.messageTypes[a];n.messageType[i]?t.push(angular.copy(n.messageType[i])):t.push({name:i,value:i})}r.messageTypes=t,r.$watch("messageTypes",function(e,t){angular.equals(e,t)||s()},!0)},e(a.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",readonly:"=ngReadonly"},link:r}}a.$inject=["$compile","$filter","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a,n(2);var i=n(10),o=r(i)},[38,11],function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,r,a){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(12),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,r,a){function i(e){e>-1&&t.kvList.splice(e,1)}function l(){t.kvList||(t.kvList=[]),t.kvList.push({key:"",value:""})}function s(){var e={};t.kvList.forEach(function(t){t.key&&(e[t.key]=t.value)}),a.$setViewValue(e),u()}function u(){var e=!0;t.required&&!t.kvList.length&&(e=!1),a.$setValidity("kvMap",e)}var d=o.default;n.html(d),t.ngModelCtrl=a,t.removeKeyVal=i,t.addKeyVal=l,t.kvList=[],t.$watch("query",function(e,n){angular.equals(e,n)||a.$setViewValue(t.query)}),a.$render=function(){if(a.$viewValue){var e=a.$viewValue;t.kvList.length=0;for(var n in e)t.kvList.push({key:n,value:e[n]})}t.$watch("kvList",function(e,t){angular.equals(e,t)||s()},!0),u()},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",disabled:"=ngDisabled",requiredText:"=",keyText:"=",keyRequiredText:"=",valText:"=",valRequiredText:"="},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(13),o=r(i);n(3)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,r,a,i){var l=o.default;r.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||i.$setViewValue(n.query)}),i.$render=function(){n.query=i.$viewValue},e(r.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(14),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,r,a,i){var l=o.default;r.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(r.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(15),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(31),i=r(a),o=n(33),l=r(o);t.default=angular.module("thingsboard.ruleChain.config.transform",[]).directive("tbTransformationNodeChangeOriginatorConfig",i.default).directive("tbTransformationNodeScriptConfig",l.default).name},[38,16],function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(37),i=r(a),o=n(25),l=r(o),s=n(21),u=r(s),d=n(32),c=r(d),m=n(18),g=r(m),f=n(30),p=r(f),b=n(29),v=r(b),y=n(36),h=r(y);t.default=angular.module("thingsboard.ruleChain.config",[i.default,l.default,u.default,c.default,g.default]).directive("tbRelationsQueryConfig",p.default).directive("tbKvMapConfig",v.default).config(h.default).name},function(e,t){"use strict";function n(e){var t={tb:{rulenode:{filter:"Filter",switch:"Switch","message-type":"Message type","message-type-required":"Message type is required.","message-types-filter":"Message types filter","no-message-types-found":"No message types found","no-message-type-matching":"'{{messageType}}' not found.","create-new-message-type":"Create a new one!","message-types-required":"Message types are required.","client-attributes":"Client attributes","shared-attributes":"Shared attributes","server-attributes":"Server attributes","latest-timeseries":"Latest timeseries","relations-query":"Relations query","max-relation-level":"Max relation level","unlimited-level":"Unlimited level","latest-telemetry":"Latest telemetry","attr-mapping":"Attributes mapping","source-attribute":"Source attribute","source-attribute-required":"Source attribute is required.","source-telemetry":"Source telemetry","source-telemetry-required":"Source telemetry is required.","target-attribute":"Target attribute","target-attribute-required":"Target attribute is required.","attr-mapping-required":"At least one attribute mapping should be specified.","originator-source":"Originator source","originator-customer":"Customer","originator-tenant":"Tenant","originator-related":"Related","clone-message":"Clone message",transform:"Transform","default-ttl":"Default TTL in seconds","default-ttl-required":"Default TTL is required.","min-default-ttl-message":"Only 0 minimum TTL is allowed.","message-count":"Message count (0 - unlimited)","message-count-required":"Message count is required.","min-message-count-message":"Only 0 minimum message count is allowed.","period-seconds":"Period in seconds","period-seconds-required":"Period is required.","min-period-seconds-message":"Only 1 second minimum period is allowed.",originator:"Originator","message-body":"Message body","message-metadata":"Message metadata",generate:"Generate"},"key-val":{key:"Key",value:"Value","remove-entry":"Remove entry","add-entry":"Add entry"}}};angular.merge(e.en_US,t)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t){(0,o.default)(t);for(var n in t){var r=t[n];e.translations(n,r)}}a.$inject=["$translateProvider","locales"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(35),o=r(i)},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=angular.module("thingsboard.ruleChain.config.types",[]).constant("ruleNodeTypes",{messageType:{POST_ATTRIBUTES:{name:"Post attributes",value:"POST_ATTRIBUTES"},POST_TELEMETRY:{name:"Post telemetry",value:"POST_TELEMETRY"},RPC_REQUEST:{name:"RPC Request",value:"RPC_REQUEST"}},originatorSource:{CUSTOMER:{name:"tb.rulenode.originator-customer",value:"CUSTOMER"},TENANT:{name:"tb.rulenode.originator-tenant",value:"TENANT"},RELATED:{name:"tb.rulenode.originator-related",value:"RELATED"}}}).name},function(e,t,n,r){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,r,a){var i=l.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var o=n(r),l=a(o)}]));
2 2 //# sourceMappingURL=rulenode-core-config.js.map
\ No newline at end of file
... ...
... ... @@ -52,7 +52,7 @@ public class TbJsFilterNodeTest {
52 52 @Test
53 53 public void falseEvaluationDoNotSendMsg() throws TbNodeException {
54 54 initWithScript("return 10 > 15;");
55   - TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, new TbMsgMetaData(), "{}".getBytes());
  55 + TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, new TbMsgMetaData(), "{}");
56 56
57 57 mockJsExecutor();
58 58
... ... @@ -65,7 +65,7 @@ public class TbJsFilterNodeTest {
65 65 @Test
66 66 public void notValidMsgDataThrowsException() throws TbNodeException {
67 67 initWithScript("return 10 > 15;");
68   - TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, new TbMsgMetaData(), new byte[4]);
  68 + TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, null, "{}");
69 69
70 70 when(ctx.getJsExecutor()).thenReturn(executor);
71 71
... ... @@ -79,11 +79,11 @@ public class TbJsFilterNodeTest {
79 79 public void exceptionInJsThrowsException() throws TbNodeException {
80 80 initWithScript("return metadata.temp.curr < 15;");
81 81 TbMsgMetaData metaData = new TbMsgMetaData();
82   - TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, "{}".getBytes());
  82 + TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, "{}");
83 83 mockJsExecutor();
84 84
85 85 node.onMsg(ctx, msg);
86   - String expectedMessage = "TypeError: Cannot get property \"curr\" of null in <eval> at line number 1";
  86 + String expectedMessage = "TypeError: Cannot read property \"curr\" from undefined in <eval> at line number 1";
87 87 verifyError(msg, expectedMessage, ScriptException.class);
88 88 }
89 89
... ... @@ -98,7 +98,7 @@ public class TbJsFilterNodeTest {
98 98 TbMsgMetaData metaData = new TbMsgMetaData();
99 99 metaData.putValue("temp", "10");
100 100 metaData.putValue("humidity", "99");
101   - TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, "{}".getBytes());
  101 + TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, "{}");
102 102 mockJsExecutor();
103 103
104 104 node.onMsg(ctx, msg);
... ... @@ -113,7 +113,7 @@ public class TbJsFilterNodeTest {
113 113 TbMsgMetaData metaData = new TbMsgMetaData();
114 114 metaData.putValue("temp", "10");
115 115 metaData.putValue("humidity", "99");
116   - TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, "{}".getBytes());
  116 + TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, "{}");
117 117 mockJsExecutor();
118 118
119 119 node.onMsg(ctx, msg);
... ... @@ -129,7 +129,7 @@ public class TbJsFilterNodeTest {
129 129 metaData.putValue("humidity", "99");
130 130 String rawJson = "{\"name\": \"Vit\", \"passed\": 5, \"bigObj\": {\"prop\":42}}";
131 131
132   - TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson.getBytes());
  132 + TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson);
133 133 mockJsExecutor();
134 134
135 135 node.onMsg(ctx, msg);
... ...
... ... @@ -68,7 +68,7 @@ public class TbJsSwitchNodeTest {
68 68 metaData.putValue("humidity", "99");
69 69 String rawJson = "{\"name\": \"Vit\", \"passed\": 5}";
70 70
71   - TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson.getBytes());
  71 + TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson);
72 72 mockJsExecutor();
73 73
74 74 node.onMsg(ctx, msg);
... ... @@ -92,7 +92,7 @@ public class TbJsSwitchNodeTest {
92 92 metaData.putValue("humidity", "99");
93 93 String rawJson = "{\"name\": \"Vit\", \"passed\": 5}";
94 94
95   - TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson.getBytes());
  95 + TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson);
96 96 mockJsExecutor();
97 97
98 98 node.onMsg(ctx, msg);
... ...
... ... @@ -98,7 +98,7 @@ public class TbGetCustomerAttributeNodeTest {
98 98 User user = new User();
99 99 user.setCustomerId(customerId);
100 100
101   - msg = new TbMsg(UUIDs.timeBased(), "USER", userId, new TbMsgMetaData(), new byte[4]);
  101 + msg = new TbMsg(UUIDs.timeBased(), "USER", userId, new TbMsgMetaData(), "{}");
102 102
103 103 when(ctx.getUserService()).thenReturn(userService);
104 104 when(userService.findUserByIdAsync(userId)).thenReturn(Futures.immediateFuture(user));
... ... @@ -123,7 +123,7 @@ public class TbGetCustomerAttributeNodeTest {
123 123 User user = new User();
124 124 user.setCustomerId(customerId);
125 125
126   - msg = new TbMsg(UUIDs.timeBased(), "USER", userId, new TbMsgMetaData(), new byte[4]);
  126 + msg = new TbMsg(UUIDs.timeBased(), "USER", userId, new TbMsgMetaData(), "{}");
127 127
128 128 when(ctx.getUserService()).thenReturn(userService);
129 129 when(userService.findUserByIdAsync(userId)).thenReturn(Futures.immediateFuture(user));
... ... @@ -148,7 +148,7 @@ public class TbGetCustomerAttributeNodeTest {
148 148 User user = new User();
149 149 user.setCustomerId(customerId);
150 150
151   - msg = new TbMsg(UUIDs.timeBased(), "USER", userId, new TbMsgMetaData(), new byte[4]);
  151 + msg = new TbMsg(UUIDs.timeBased(), "USER", userId, new TbMsgMetaData(), "{}");
152 152
153 153 when(ctx.getUserService()).thenReturn(userService);
154 154 when(userService.findUserByIdAsync(userId)).thenReturn(Futures.immediateFuture(null));
... ... @@ -166,7 +166,7 @@ public class TbGetCustomerAttributeNodeTest {
166 166 @Test
167 167 public void customerAttributeAddedInMetadata() {
168 168 CustomerId customerId = new CustomerId(UUIDs.timeBased());
169   - msg = new TbMsg(UUIDs.timeBased(), "CUSTOMER", customerId, new TbMsgMetaData(), new byte[4]);
  169 + msg = new TbMsg(UUIDs.timeBased(), "CUSTOMER", customerId, new TbMsgMetaData(), "{}");
170 170 entityAttributeFetched(customerId);
171 171 }
172 172
... ... @@ -177,7 +177,7 @@ public class TbGetCustomerAttributeNodeTest {
177 177 User user = new User();
178 178 user.setCustomerId(customerId);
179 179
180   - msg = new TbMsg(UUIDs.timeBased(), "USER", userId, new TbMsgMetaData(), new byte[4]);
  180 + msg = new TbMsg(UUIDs.timeBased(), "USER", userId, new TbMsgMetaData(), "{}");
181 181
182 182 when(ctx.getUserService()).thenReturn(userService);
183 183 when(userService.findUserByIdAsync(userId)).thenReturn(Futures.immediateFuture(user));
... ... @@ -192,7 +192,7 @@ public class TbGetCustomerAttributeNodeTest {
192 192 Asset asset = new Asset();
193 193 asset.setCustomerId(customerId);
194 194
195   - msg = new TbMsg(UUIDs.timeBased(), "USER", assetId, new TbMsgMetaData(), new byte[4]);
  195 + msg = new TbMsg(UUIDs.timeBased(), "USER", assetId, new TbMsgMetaData(), "{}");
196 196
197 197 when(ctx.getAssetService()).thenReturn(assetService);
198 198 when(assetService.findAssetByIdAsync(assetId)).thenReturn(Futures.immediateFuture(asset));
... ... @@ -207,7 +207,7 @@ public class TbGetCustomerAttributeNodeTest {
207 207 Device device = new Device();
208 208 device.setCustomerId(customerId);
209 209
210   - msg = new TbMsg(UUIDs.timeBased(), "USER", deviceId, new TbMsgMetaData(), new byte[4]);
  210 + msg = new TbMsg(UUIDs.timeBased(), "USER", deviceId, new TbMsgMetaData(), "{}");
211 211
212 212 when(ctx.getDeviceService()).thenReturn(deviceService);
213 213 when(deviceService.findDeviceByIdAsync(deviceId)).thenReturn(Futures.immediateFuture(device));
... ... @@ -234,7 +234,7 @@ public class TbGetCustomerAttributeNodeTest {
234 234 Device device = new Device();
235 235 device.setCustomerId(customerId);
236 236
237   - msg = new TbMsg(UUIDs.timeBased(), "USER", deviceId, new TbMsgMetaData(), new byte[4]);
  237 + msg = new TbMsg(UUIDs.timeBased(), "USER", deviceId, new TbMsgMetaData(), "{}");
238 238
239 239 when(ctx.getDeviceService()).thenReturn(deviceService);
240 240 when(deviceService.findDeviceByIdAsync(deviceId)).thenReturn(Futures.immediateFuture(device));
... ...
... ... @@ -57,7 +57,7 @@ public class TbChangeOriginatorNodeTest {
57 57 Asset asset = new Asset();
58 58 asset.setCustomerId(customerId);
59 59
60   - TbMsg msg = new TbMsg(UUIDs.timeBased(), "ASSET", assetId, new TbMsgMetaData(), new byte[4]);
  60 + TbMsg msg = new TbMsg(UUIDs.timeBased(), "ASSET", assetId, new TbMsgMetaData(), "{}");
61 61
62 62 when(ctx.getAssetService()).thenReturn(assetService);
63 63 when(assetService.findAssetByIdAsync(assetId)).thenReturn(Futures.immediateFuture(asset));
... ... @@ -78,7 +78,7 @@ public class TbChangeOriginatorNodeTest {
78 78 Asset asset = new Asset();
79 79 asset.setCustomerId(customerId);
80 80
81   - TbMsg msg = new TbMsg(UUIDs.timeBased(), "ASSET", assetId, new TbMsgMetaData(), new byte[4]);
  81 + TbMsg msg = new TbMsg(UUIDs.timeBased(), "ASSET", assetId, new TbMsgMetaData(), "{}");
82 82
83 83 when(ctx.getAssetService()).thenReturn(assetService);
84 84 when(assetService.findAssetByIdAsync(assetId)).thenReturn(Futures.immediateFuture(asset));
... ... @@ -99,7 +99,7 @@ public class TbChangeOriginatorNodeTest {
99 99 Asset asset = new Asset();
100 100 asset.setCustomerId(customerId);
101 101
102   - TbMsg msg = new TbMsg(UUIDs.timeBased(), "ASSET", assetId, new TbMsgMetaData(), new byte[4]);
  102 + TbMsg msg = new TbMsg(UUIDs.timeBased(), "ASSET", assetId, new TbMsgMetaData(), "{}");
103 103
104 104 when(ctx.getAssetService()).thenReturn(assetService);
105 105 when(assetService.findAssetByIdAsync(assetId)).thenReturn(Futures.immediateFailedFuture(new IllegalStateException("wrong")));
... ...
... ... @@ -51,13 +51,13 @@ public class TbTransformMsgNodeTest {
51 51
52 52 @Test
53 53 public void metadataCanBeUpdated() throws TbNodeException {
54   - initWithScript("return metadata.temp = metadata.temp * 10;");
  54 + initWithScript("metadata.temp = metadata.temp * 10; return {metadata: metadata};");
55 55 TbMsgMetaData metaData = new TbMsgMetaData();
56 56 metaData.putValue("temp", "7");
57 57 metaData.putValue("humidity", "99");
58 58 String rawJson = "{\"name\": \"Vit\", \"passed\": 5, \"bigObj\": {\"prop\":42}}";
59 59
60   - TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson.getBytes());
  60 + TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson);
61 61 mockJsExecutor();
62 62
63 63 node.onMsg(ctx, msg);
... ... @@ -65,18 +65,18 @@ public class TbTransformMsgNodeTest {
65 65 ArgumentCaptor<TbMsg> captor = ArgumentCaptor.forClass(TbMsg.class);
66 66 verify(ctx).tellNext(captor.capture());
67 67 TbMsg actualMsg = captor.getValue();
68   - assertEquals("70.0", actualMsg.getMetaData().getValue("temp"));
  68 + assertEquals("70", actualMsg.getMetaData().getValue("temp"));
69 69 }
70 70
71 71 @Test
72 72 public void metadataCanBeAdded() throws TbNodeException {
73   - initWithScript("return metadata.newAttr = metadata.humidity - msg.passed;");
  73 + initWithScript("metadata.newAttr = metadata.humidity - msg.passed; return {metadata: metadata};");
74 74 TbMsgMetaData metaData = new TbMsgMetaData();
75 75 metaData.putValue("temp", "7");
76 76 metaData.putValue("humidity", "99");
77 77 String rawJson = "{\"name\": \"Vit\", \"passed\": 5, \"bigObj\": {\"prop\":42}}";
78 78
79   - TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson.getBytes());
  79 + TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson);
80 80 mockJsExecutor();
81 81
82 82 node.onMsg(ctx, msg);
... ... @@ -84,18 +84,18 @@ public class TbTransformMsgNodeTest {
84 84 ArgumentCaptor<TbMsg> captor = ArgumentCaptor.forClass(TbMsg.class);
85 85 verify(ctx).tellNext(captor.capture());
86 86 TbMsg actualMsg = captor.getValue();
87   - assertEquals("94.0", actualMsg.getMetaData().getValue("newAttr"));
  87 + assertEquals("94", actualMsg.getMetaData().getValue("newAttr"));
88 88 }
89 89
90 90 @Test
91 91 public void payloadCanBeUpdated() throws TbNodeException {
92   - initWithScript("msg.passed = msg.passed * metadata.temp; return msg.bigObj.newProp = 'Ukraine' ");
  92 + initWithScript("msg.passed = msg.passed * metadata.temp; msg.bigObj.newProp = 'Ukraine'; return {msg: msg};");
93 93 TbMsgMetaData metaData = new TbMsgMetaData();
94 94 metaData.putValue("temp", "7");
95 95 metaData.putValue("humidity", "99");
96 96 String rawJson = "{\"name\":\"Vit\",\"passed\": 5,\"bigObj\":{\"prop\":42}}";
97 97
98   - TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson.getBytes());
  98 + TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson);
99 99 mockJsExecutor();
100 100
101 101 node.onMsg(ctx, msg);
... ... @@ -103,7 +103,7 @@ public class TbTransformMsgNodeTest {
103 103 ArgumentCaptor<TbMsg> captor = ArgumentCaptor.forClass(TbMsg.class);
104 104 verify(ctx).tellNext(captor.capture());
105 105 TbMsg actualMsg = captor.getValue();
106   - String expectedJson = "{\"name\":\"Vit\",\"passed\":35.0,\"bigObj\":{\"prop\":42,\"newProp\":\"Ukraine\"}}";
  106 + String expectedJson = "{\"name\":\"Vit\",\"passed\":35,\"bigObj\":{\"prop\":42,\"newProp\":\"Ukraine\"}}";
107 107 assertEquals(expectedJson, new String(actualMsg.getData()));
108 108 }
109 109
... ...