Showing
22 changed files
with
207 additions
and
165 deletions
@@ -321,7 +321,7 @@ public class ActorSystemContext { | @@ -321,7 +321,7 @@ public class ActorSystemContext { | ||
321 | .put("msgId", tbMsg.getId().toString()) | 321 | .put("msgId", tbMsg.getId().toString()) |
322 | .put("msgType", tbMsg.getType()) | 322 | .put("msgType", tbMsg.getType()) |
323 | .put("dataType", tbMsg.getDataType().name()) | 323 | .put("dataType", tbMsg.getDataType().name()) |
324 | - .put("data", convertToString(tbMsg.getDataType(), tbMsg.getData())) | 324 | + .put("data", tbMsg.getData()) |
325 | .put("metadata", metadata); | 325 | .put("metadata", metadata); |
326 | 326 | ||
327 | if (error != null) { | 327 | if (error != null) { |
@@ -335,21 +335,6 @@ public class ActorSystemContext { | @@ -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 | public static Exception toException(Throwable error) { | 338 | public static Exception toException(Throwable error) { |
354 | return Exception.class.isInstance(error) ? (Exception) error : new Exception(error); | 339 | return Exception.class.isInstance(error) ? (Exception) error : new Exception(error); |
355 | } | 340 | } |
@@ -149,7 +149,7 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule | @@ -149,7 +149,7 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule | ||
149 | "CUSTOM", | 149 | "CUSTOM", |
150 | device.getId(), | 150 | device.getId(), |
151 | new TbMsgMetaData(), | 151 | new TbMsgMetaData(), |
152 | - new byte[]{}); | 152 | + "{}"); |
153 | actorService.onMsg(new ServiceToRuleEngineMsg(savedTenant.getId(), tbMsg)); | 153 | actorService.onMsg(new ServiceToRuleEngineMsg(savedTenant.getId(), tbMsg)); |
154 | 154 | ||
155 | Thread.sleep(3000); | 155 | Thread.sleep(3000); |
@@ -135,7 +135,7 @@ public abstract class AbstractRuleEngineLifecycleIntegrationTest extends Abstrac | @@ -135,7 +135,7 @@ public abstract class AbstractRuleEngineLifecycleIntegrationTest extends Abstrac | ||
135 | "CUSTOM", | 135 | "CUSTOM", |
136 | device.getId(), | 136 | device.getId(), |
137 | new TbMsgMetaData(), | 137 | new TbMsgMetaData(), |
138 | - new byte[]{}); | 138 | + "{}"); |
139 | actorService.onMsg(new ServiceToRuleEngineMsg(savedTenant.getId(), tbMsg)); | 139 | actorService.onMsg(new ServiceToRuleEngineMsg(savedTenant.getId(), tbMsg)); |
140 | 140 | ||
141 | Thread.sleep(3000); | 141 | Thread.sleep(3000); |
@@ -39,9 +39,9 @@ public final class TbMsg implements Serializable { | @@ -39,9 +39,9 @@ public final class TbMsg implements Serializable { | ||
39 | private final EntityId originator; | 39 | private final EntityId originator; |
40 | private final TbMsgMetaData metaData; | 40 | private final TbMsgMetaData metaData; |
41 | private final TbMsgDataType dataType; | 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 | this.id = id; | 45 | this.id = id; |
46 | this.type = type; | 46 | this.type = type; |
47 | this.originator = originator; | 47 | this.originator = originator; |
@@ -64,7 +64,7 @@ public final class TbMsg implements Serializable { | @@ -64,7 +64,7 @@ public final class TbMsg implements Serializable { | ||
64 | } | 64 | } |
65 | 65 | ||
66 | builder.setDataType(msg.getDataType().ordinal()); | 66 | builder.setDataType(msg.getDataType().ordinal()); |
67 | - builder.setData(ByteString.copyFrom(msg.getData())); | 67 | + builder.setData(msg.getData()); |
68 | byte[] bytes = builder.build().toByteArray(); | 68 | byte[] bytes = builder.build().toByteArray(); |
69 | return ByteBuffer.wrap(bytes); | 69 | return ByteBuffer.wrap(bytes); |
70 | } | 70 | } |
@@ -75,16 +75,13 @@ public final class TbMsg implements Serializable { | @@ -75,16 +75,13 @@ public final class TbMsg implements Serializable { | ||
75 | TbMsgMetaData metaData = new TbMsgMetaData(proto.getMetaData().getDataMap()); | 75 | TbMsgMetaData metaData = new TbMsgMetaData(proto.getMetaData().getDataMap()); |
76 | EntityId entityId = EntityIdFactory.getByTypeAndId(proto.getEntityType(), proto.getEntityId()); | 76 | EntityId entityId = EntityIdFactory.getByTypeAndId(proto.getEntityType(), proto.getEntityId()); |
77 | TbMsgDataType dataType = TbMsgDataType.values()[proto.getDataType()]; | 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 | } catch (InvalidProtocolBufferException e) { | 79 | } catch (InvalidProtocolBufferException e) { |
80 | throw new IllegalStateException("Could not parse protobuf for TbMsg", e); | 80 | throw new IllegalStateException("Could not parse protobuf for TbMsg", e); |
81 | } | 81 | } |
82 | } | 82 | } |
83 | 83 | ||
84 | public TbMsg copy() { | 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,7 +33,7 @@ public final class TbMsgMetaData implements Serializable { | ||
33 | 33 | ||
34 | private Map<String, String> data = new ConcurrentHashMap<>(); | 34 | private Map<String, String> data = new ConcurrentHashMap<>(); |
35 | 35 | ||
36 | - TbMsgMetaData(Map<String, String> data) { | 36 | + public TbMsgMetaData(Map<String, String> data) { |
37 | this.data = data; | 37 | this.data = data; |
38 | } | 38 | } |
39 | 39 |
@@ -126,7 +126,7 @@ public class QueueBenchmark implements CommandLineRunner { | @@ -126,7 +126,7 @@ public class QueueBenchmark implements CommandLineRunner { | ||
126 | TbMsgMetaData metaData = new TbMsgMetaData(); | 126 | TbMsgMetaData metaData = new TbMsgMetaData(); |
127 | metaData.putValue("key", "value"); | 127 | metaData.putValue("key", "value"); |
128 | String dataStr = "someContent"; | 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 | @Bean | 132 | @Bean |
@@ -45,7 +45,7 @@ public class CassandraMsgRepositoryTest extends AbstractServiceTest { | @@ -45,7 +45,7 @@ public class CassandraMsgRepositoryTest extends AbstractServiceTest { | ||
45 | 45 | ||
46 | @Test | 46 | @Test |
47 | public void msgCanBeSavedAndRead() throws ExecutionException, InterruptedException { | 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 | UUID nodeId = UUIDs.timeBased(); | 49 | UUID nodeId = UUIDs.timeBased(); |
50 | ListenableFuture<Void> future = msgRepository.save(msg, nodeId, 1L, 1L, 1L); | 50 | ListenableFuture<Void> future = msgRepository.save(msg, nodeId, 1L, 1L, 1L); |
51 | future.get(); | 51 | future.get(); |
@@ -55,7 +55,7 @@ public class CassandraMsgRepositoryTest extends AbstractServiceTest { | @@ -55,7 +55,7 @@ public class CassandraMsgRepositoryTest extends AbstractServiceTest { | ||
55 | 55 | ||
56 | @Test | 56 | @Test |
57 | public void expiredMsgsAreNotReturned() throws ExecutionException, InterruptedException { | 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 | UUID nodeId = UUIDs.timeBased(); | 59 | UUID nodeId = UUIDs.timeBased(); |
60 | ListenableFuture<Void> future = msgRepository.save(msg, nodeId, 2L, 2L, 2L); | 60 | ListenableFuture<Void> future = msgRepository.save(msg, nodeId, 2L, 2L, 2L); |
61 | future.get(); | 61 | future.get(); |
@@ -68,7 +68,7 @@ public class CassandraMsgRepositoryTest extends AbstractServiceTest { | @@ -68,7 +68,7 @@ public class CassandraMsgRepositoryTest extends AbstractServiceTest { | ||
68 | TbMsgMetaData metaData = new TbMsgMetaData(); | 68 | TbMsgMetaData metaData = new TbMsgMetaData(); |
69 | metaData.putValue("key", "value"); | 69 | metaData.putValue("key", "value"); |
70 | String dataStr = "someContent"; | 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 | UUID nodeId = UUIDs.timeBased(); | 72 | UUID nodeId = UUIDs.timeBased(); |
73 | ListenableFuture<Void> future = msgRepository.save(msg, nodeId, 1L, 1L, 1L); | 73 | ListenableFuture<Void> future = msgRepository.save(msg, nodeId, 1L, 1L, 1L); |
74 | future.get(); | 74 | future.get(); |
@@ -16,6 +16,7 @@ | @@ -16,6 +16,7 @@ | ||
16 | package org.thingsboard.rule.engine.debug; | 16 | package org.thingsboard.rule.engine.debug; |
17 | 17 | ||
18 | import com.datastax.driver.core.utils.UUIDs; | 18 | import com.datastax.driver.core.utils.UUIDs; |
19 | +import com.google.common.util.concurrent.ListenableFuture; | ||
19 | import lombok.extern.slf4j.Slf4j; | 20 | import lombok.extern.slf4j.Slf4j; |
20 | import org.springframework.util.StringUtils; | 21 | import org.springframework.util.StringUtils; |
21 | import org.thingsboard.rule.engine.TbNodeUtils; | 22 | import org.thingsboard.rule.engine.TbNodeUtils; |
@@ -58,9 +59,11 @@ public class TbMsgGeneratorNode implements TbNode { | @@ -58,9 +59,11 @@ public class TbMsgGeneratorNode implements TbNode { | ||
58 | public static final String TB_MSG_GENERATOR_NODE_MSG = "TbMsgGeneratorNodeMsg"; | 59 | public static final String TB_MSG_GENERATOR_NODE_MSG = "TbMsgGeneratorNodeMsg"; |
59 | 60 | ||
60 | private TbMsgGeneratorNodeConfiguration config; | 61 | private TbMsgGeneratorNodeConfiguration config; |
62 | + private NashornJsEngine jsEngine; | ||
61 | private long delay; | 63 | private long delay; |
62 | private EntityId originatorId; | 64 | private EntityId originatorId; |
63 | private UUID nextTickId; | 65 | private UUID nextTickId; |
66 | + private TbMsg prevMsg; | ||
64 | 67 | ||
65 | @Override | 68 | @Override |
66 | public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { | 69 | public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { |
@@ -71,29 +74,41 @@ public class TbMsgGeneratorNode implements TbNode { | @@ -71,29 +74,41 @@ public class TbMsgGeneratorNode implements TbNode { | ||
71 | } else { | 74 | } else { |
72 | originatorId = ctx.getSelfId(); | 75 | originatorId = ctx.getSelfId(); |
73 | } | 76 | } |
77 | + this.jsEngine = new NashornJsEngine(config.getJsScript(), "Generate", "prevMsg", "prevMetadata", "prevMsgType"); | ||
74 | sentTickMsg(ctx); | 78 | sentTickMsg(ctx); |
75 | } | 79 | } |
76 | 80 | ||
77 | @Override | 81 | @Override |
78 | public void onMsg(TbContext ctx, TbMsg msg) { | 82 | public void onMsg(TbContext ctx, TbMsg msg) { |
79 | if (msg.getType().equals(TB_MSG_GENERATOR_NODE_MSG) && msg.getId().equals(nextTickId)) { | 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 | private void sentTickMsg(TbContext ctx) { | 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 | nextTickId = tickMsg.getId(); | 92 | nextTickId = tickMsg.getId(); |
92 | ctx.tellSelf(tickMsg, delay); | 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 | @Override | 107 | @Override |
97 | public void destroy() { | 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,17 +28,17 @@ public class TbMsgGeneratorNodeConfiguration implements NodeConfiguration<TbMsgG | ||
28 | private int periodInSeconds; | 28 | private int periodInSeconds; |
29 | private String originatorId; | 29 | private String originatorId; |
30 | private EntityType originatorType; | 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 | @Override | 33 | @Override |
36 | public TbMsgGeneratorNodeConfiguration defaultConfiguration() { | 34 | public TbMsgGeneratorNodeConfiguration defaultConfiguration() { |
37 | TbMsgGeneratorNodeConfiguration configuration = new TbMsgGeneratorNodeConfiguration(); | 35 | TbMsgGeneratorNodeConfiguration configuration = new TbMsgGeneratorNodeConfiguration(); |
38 | configuration.setMsgCount(0); | 36 | configuration.setMsgCount(0); |
39 | configuration.setPeriodInSeconds(1); | 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 | return configuration; | 42 | return configuration; |
43 | } | 43 | } |
44 | } | 44 | } |
@@ -35,7 +35,8 @@ import static org.thingsboard.rule.engine.DonAsynchron.withCallback; | @@ -35,7 +35,8 @@ import static org.thingsboard.rule.engine.DonAsynchron.withCallback; | ||
35 | nodeDetails = "Evaluate incoming Message with configured JS condition. " + | 35 | nodeDetails = "Evaluate incoming Message with configured JS condition. " + |
36 | "If <b>True</b> - send Message via <b>True</b> chain, otherwise <b>False</b> chain is used." + | 36 | "If <b>True</b> - send Message via <b>True</b> chain, otherwise <b>False</b> chain is used." + |
37 | "Message payload can be accessed via <code>msg</code> property. For example <code>msg.temperature < 10;</code>" + | 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 | uiResources = {"static/rulenode/rulenode-core-config.js"}, | 40 | uiResources = {"static/rulenode/rulenode-core-config.js"}, |
40 | configDirective = "tbFilterNodeScriptConfig") | 41 | configDirective = "tbFilterNodeScriptConfig") |
41 | 42 | ||
@@ -53,15 +54,11 @@ public class TbJsFilterNode implements TbNode { | @@ -53,15 +54,11 @@ public class TbJsFilterNode implements TbNode { | ||
53 | @Override | 54 | @Override |
54 | public void onMsg(TbContext ctx, TbMsg msg) { | 55 | public void onMsg(TbContext ctx, TbMsg msg) { |
55 | ListeningExecutor jsExecutor = ctx.getJsExecutor(); | 56 | ListeningExecutor jsExecutor = ctx.getJsExecutor(); |
56 | - withCallback(jsExecutor.executeAsync(() -> jsEngine.executeFilter(toBindings(msg))), | 57 | + withCallback(jsExecutor.executeAsync(() -> jsEngine.executeFilter(msg)), |
57 | filterResult -> ctx.tellNext(msg, Boolean.toString(filterResult)), | 58 | filterResult -> ctx.tellNext(msg, Boolean.toString(filterResult)), |
58 | t -> ctx.tellError(msg, t)); | 59 | t -> ctx.tellError(msg, t)); |
59 | } | 60 | } |
60 | 61 | ||
61 | - private Bindings toBindings(TbMsg msg) { | ||
62 | - return NashornJsEngine.bindMsg(msg); | ||
63 | - } | ||
64 | - | ||
65 | @Override | 62 | @Override |
66 | public void destroy() { | 63 | public void destroy() { |
67 | if (jsEngine != null) { | 64 | if (jsEngine != null) { |
@@ -36,7 +36,8 @@ import static org.thingsboard.rule.engine.DonAsynchron.withCallback; | @@ -36,7 +36,8 @@ import static org.thingsboard.rule.engine.DonAsynchron.withCallback; | ||
36 | nodeDetails = "Node executes configured JS script. Script should return array of next Chain names where Message should be routed. " + | 36 | nodeDetails = "Node executes configured JS script. Script should return array of next Chain names where Message should be routed. " + |
37 | "If Array is empty - message not routed to next Node. " + | 37 | "If Array is empty - message not routed to next Node. " + |
38 | "Message payload can be accessed via <code>msg</code> property. For example <code>msg.temperature < 10;</code> " + | 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 | uiResources = {"static/rulenode/rulenode-core-config.js"}, | 41 | uiResources = {"static/rulenode/rulenode-core-config.js"}, |
41 | configDirective = "tbFilterNodeSwitchConfig") | 42 | configDirective = "tbFilterNodeSwitchConfig") |
42 | public class TbJsSwitchNode implements TbNode { | 43 | public class TbJsSwitchNode implements TbNode { |
@@ -53,7 +54,7 @@ public class TbJsSwitchNode implements TbNode { | @@ -53,7 +54,7 @@ public class TbJsSwitchNode implements TbNode { | ||
53 | @Override | 54 | @Override |
54 | public void onMsg(TbContext ctx, TbMsg msg) { | 55 | public void onMsg(TbContext ctx, TbMsg msg) { |
55 | ListeningExecutor jsExecutor = ctx.getJsExecutor(); | 56 | ListeningExecutor jsExecutor = ctx.getJsExecutor(); |
56 | - withCallback(jsExecutor.executeAsync(() -> jsEngine.executeSwitch(toBindings(msg))), | 57 | + withCallback(jsExecutor.executeAsync(() -> jsEngine.executeSwitch(msg)), |
57 | result -> processSwitch(ctx, msg, result), | 58 | result -> processSwitch(ctx, msg, result), |
58 | t -> ctx.tellError(msg, t)); | 59 | t -> ctx.tellError(msg, t)); |
59 | } | 60 | } |
@@ -62,11 +63,7 @@ public class TbJsSwitchNode implements TbNode { | @@ -62,11 +63,7 @@ public class TbJsSwitchNode implements TbNode { | ||
62 | ctx.tellNext(msg, nextRelations); | 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 | public void destroy() { | 67 | public void destroy() { |
71 | if (jsEngine != null) { | 68 | if (jsEngine != null) { |
72 | jsEngine.destroy(); | 69 | jsEngine.destroy(); |
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/js/NashornJsEngine.java
@@ -16,6 +16,7 @@ | @@ -16,6 +16,7 @@ | ||
16 | package org.thingsboard.rule.engine.js; | 16 | package org.thingsboard.rule.engine.js; |
17 | 17 | ||
18 | import com.fasterxml.jackson.core.JsonProcessingException; | 18 | import com.fasterxml.jackson.core.JsonProcessingException; |
19 | +import com.fasterxml.jackson.core.type.TypeReference; | ||
19 | import com.fasterxml.jackson.databind.JsonNode; | 20 | import com.fasterxml.jackson.databind.JsonNode; |
20 | import com.fasterxml.jackson.databind.ObjectMapper; | 21 | import com.fasterxml.jackson.databind.ObjectMapper; |
21 | import com.google.common.collect.Sets; | 22 | import com.google.common.collect.Sets; |
@@ -23,9 +24,13 @@ import jdk.nashorn.api.scripting.NashornScriptEngineFactory; | @@ -23,9 +24,13 @@ import jdk.nashorn.api.scripting.NashornScriptEngineFactory; | ||
23 | import jdk.nashorn.api.scripting.ScriptObjectMirror; | 24 | import jdk.nashorn.api.scripting.ScriptObjectMirror; |
24 | import lombok.extern.slf4j.Slf4j; | 25 | import lombok.extern.slf4j.Slf4j; |
25 | import org.apache.commons.lang3.ArrayUtils; | 26 | import org.apache.commons.lang3.ArrayUtils; |
27 | +import org.apache.commons.lang3.StringUtils; | ||
26 | import org.thingsboard.server.common.msg.TbMsg; | 28 | import org.thingsboard.server.common.msg.TbMsg; |
29 | +import org.thingsboard.server.common.msg.TbMsgMetaData; | ||
27 | 30 | ||
28 | import javax.script.*; | 31 | import javax.script.*; |
32 | +import java.io.IOException; | ||
33 | +import java.nio.charset.StandardCharsets; | ||
29 | import java.util.Collections; | 34 | import java.util.Collections; |
30 | import java.util.Map; | 35 | import java.util.Map; |
31 | import java.util.Set; | 36 | import java.util.Set; |
@@ -34,112 +39,158 @@ import java.util.Set; | @@ -34,112 +39,158 @@ import java.util.Set; | ||
34 | @Slf4j | 39 | @Slf4j |
35 | public class NashornJsEngine { | 40 | public class NashornJsEngine { |
36 | 41 | ||
42 | + | ||
43 | + public static final String MSG = "msg"; | ||
37 | public static final String METADATA = "metadata"; | 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 | private static NashornScriptEngineFactory factory = new NashornScriptEngineFactory(); | 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 | try { | 81 | try { |
57 | - return compEngine.compile(script); | 82 | + engine.eval(script); |
58 | } catch (ScriptException e) { | 83 | } catch (ScriptException e) { |
59 | log.warn("Failed to compile JS script: {}", e.getMessage(), e); | 84 | log.warn("Failed to compile JS script: {}", e.getMessage(), e); |
60 | throw new IllegalArgumentException("Can't compile script: " + e.getMessage()); | 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 | try { | 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 | } else { | 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 | } catch (Throwable th) { | 100 | } catch (Throwable th) { |
78 | throw new IllegalArgumentException("Cannot bind js args", th); | 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 | public void destroy() { | 193 | public void destroy() { |
143 | - engine = null; | 194 | + //engine = null; |
144 | } | 195 | } |
145 | } | 196 | } |
@@ -64,7 +64,7 @@ public class TbMsgTelemetryNode implements TbNode { | @@ -64,7 +64,7 @@ public class TbMsgTelemetryNode implements TbNode { | ||
64 | return; | 64 | return; |
65 | } | 65 | } |
66 | 66 | ||
67 | - String src = new String(msg.getData(), StandardCharsets.UTF_8); | 67 | + String src = msg.getData(); |
68 | TelemetryUploadRequest telemetryUploadRequest = JsonConverter.convertToTelemetry(new JsonParser().parse(src)); | 68 | TelemetryUploadRequest telemetryUploadRequest = JsonConverter.convertToTelemetry(new JsonParser().parse(src)); |
69 | Map<Long, List<KvEntry>> tsKvMap = telemetryUploadRequest.getData(); | 69 | Map<Long, List<KvEntry>> tsKvMap = telemetryUploadRequest.getData(); |
70 | if (tsKvMap == null) { | 70 | if (tsKvMap == null) { |
@@ -28,10 +28,14 @@ import javax.script.Bindings; | @@ -28,10 +28,14 @@ import javax.script.Bindings; | ||
28 | type = ComponentType.TRANSFORMATION, | 28 | type = ComponentType.TRANSFORMATION, |
29 | name = "script", | 29 | name = "script", |
30 | configClazz = TbTransformMsgNodeConfiguration.class, | 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 | "<code>metadata</code> - is a Message metadata.<br/>" + | 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 | uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"}, | 39 | uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"}, |
36 | configDirective = "tbTransformationNodeScriptConfig") | 40 | configDirective = "tbTransformationNodeScriptConfig") |
37 | public class TbTransformMsgNode extends TbAbstractTransformNode { | 41 | public class TbTransformMsgNode extends TbAbstractTransformNode { |
@@ -48,11 +52,7 @@ public class TbTransformMsgNode extends TbAbstractTransformNode { | @@ -48,11 +52,7 @@ public class TbTransformMsgNode extends TbAbstractTransformNode { | ||
48 | 52 | ||
49 | @Override | 53 | @Override |
50 | protected ListenableFuture<TbMsg> transform(TbContext ctx, TbMsg msg) { | 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 | @Override | 58 | @Override |
@@ -27,7 +27,7 @@ public class TbTransformMsgNodeConfiguration extends TbTransformNodeConfiguratio | @@ -27,7 +27,7 @@ public class TbTransformMsgNodeConfiguration extends TbTransformNodeConfiguratio | ||
27 | public TbTransformMsgNodeConfiguration defaultConfiguration() { | 27 | public TbTransformMsgNodeConfiguration defaultConfiguration() { |
28 | TbTransformMsgNodeConfiguration configuration = new TbTransformMsgNodeConfiguration(); | 28 | TbTransformMsgNodeConfiguration configuration = new TbTransformMsgNodeConfiguration(); |
29 | configuration.setStartNewChain(false); | 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 | return configuration; | 31 | return configuration; |
32 | } | 32 | } |
33 | } | 33 | } |
rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js
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:'...'}}" }\'>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> </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:'...'}}" }\'>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> </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 | //# sourceMappingURL=rulenode-core-config.js.map | 2 | //# sourceMappingURL=rulenode-core-config.js.map |
@@ -52,7 +52,7 @@ public class TbJsFilterNodeTest { | @@ -52,7 +52,7 @@ public class TbJsFilterNodeTest { | ||
52 | @Test | 52 | @Test |
53 | public void falseEvaluationDoNotSendMsg() throws TbNodeException { | 53 | public void falseEvaluationDoNotSendMsg() throws TbNodeException { |
54 | initWithScript("return 10 > 15;"); | 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 | mockJsExecutor(); | 57 | mockJsExecutor(); |
58 | 58 | ||
@@ -65,7 +65,7 @@ public class TbJsFilterNodeTest { | @@ -65,7 +65,7 @@ public class TbJsFilterNodeTest { | ||
65 | @Test | 65 | @Test |
66 | public void notValidMsgDataThrowsException() throws TbNodeException { | 66 | public void notValidMsgDataThrowsException() throws TbNodeException { |
67 | initWithScript("return 10 > 15;"); | 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 | when(ctx.getJsExecutor()).thenReturn(executor); | 70 | when(ctx.getJsExecutor()).thenReturn(executor); |
71 | 71 | ||
@@ -79,11 +79,11 @@ public class TbJsFilterNodeTest { | @@ -79,11 +79,11 @@ public class TbJsFilterNodeTest { | ||
79 | public void exceptionInJsThrowsException() throws TbNodeException { | 79 | public void exceptionInJsThrowsException() throws TbNodeException { |
80 | initWithScript("return metadata.temp.curr < 15;"); | 80 | initWithScript("return metadata.temp.curr < 15;"); |
81 | TbMsgMetaData metaData = new TbMsgMetaData(); | 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 | mockJsExecutor(); | 83 | mockJsExecutor(); |
84 | 84 | ||
85 | node.onMsg(ctx, msg); | 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 | verifyError(msg, expectedMessage, ScriptException.class); | 87 | verifyError(msg, expectedMessage, ScriptException.class); |
88 | } | 88 | } |
89 | 89 | ||
@@ -98,7 +98,7 @@ public class TbJsFilterNodeTest { | @@ -98,7 +98,7 @@ public class TbJsFilterNodeTest { | ||
98 | TbMsgMetaData metaData = new TbMsgMetaData(); | 98 | TbMsgMetaData metaData = new TbMsgMetaData(); |
99 | metaData.putValue("temp", "10"); | 99 | metaData.putValue("temp", "10"); |
100 | metaData.putValue("humidity", "99"); | 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 | mockJsExecutor(); | 102 | mockJsExecutor(); |
103 | 103 | ||
104 | node.onMsg(ctx, msg); | 104 | node.onMsg(ctx, msg); |
@@ -113,7 +113,7 @@ public class TbJsFilterNodeTest { | @@ -113,7 +113,7 @@ public class TbJsFilterNodeTest { | ||
113 | TbMsgMetaData metaData = new TbMsgMetaData(); | 113 | TbMsgMetaData metaData = new TbMsgMetaData(); |
114 | metaData.putValue("temp", "10"); | 114 | metaData.putValue("temp", "10"); |
115 | metaData.putValue("humidity", "99"); | 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 | mockJsExecutor(); | 117 | mockJsExecutor(); |
118 | 118 | ||
119 | node.onMsg(ctx, msg); | 119 | node.onMsg(ctx, msg); |
@@ -129,7 +129,7 @@ public class TbJsFilterNodeTest { | @@ -129,7 +129,7 @@ public class TbJsFilterNodeTest { | ||
129 | metaData.putValue("humidity", "99"); | 129 | metaData.putValue("humidity", "99"); |
130 | String rawJson = "{\"name\": \"Vit\", \"passed\": 5, \"bigObj\": {\"prop\":42}}"; | 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 | mockJsExecutor(); | 133 | mockJsExecutor(); |
134 | 134 | ||
135 | node.onMsg(ctx, msg); | 135 | node.onMsg(ctx, msg); |
@@ -68,7 +68,7 @@ public class TbJsSwitchNodeTest { | @@ -68,7 +68,7 @@ public class TbJsSwitchNodeTest { | ||
68 | metaData.putValue("humidity", "99"); | 68 | metaData.putValue("humidity", "99"); |
69 | String rawJson = "{\"name\": \"Vit\", \"passed\": 5}"; | 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 | mockJsExecutor(); | 72 | mockJsExecutor(); |
73 | 73 | ||
74 | node.onMsg(ctx, msg); | 74 | node.onMsg(ctx, msg); |
@@ -92,7 +92,7 @@ public class TbJsSwitchNodeTest { | @@ -92,7 +92,7 @@ public class TbJsSwitchNodeTest { | ||
92 | metaData.putValue("humidity", "99"); | 92 | metaData.putValue("humidity", "99"); |
93 | String rawJson = "{\"name\": \"Vit\", \"passed\": 5}"; | 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 | mockJsExecutor(); | 96 | mockJsExecutor(); |
97 | 97 | ||
98 | node.onMsg(ctx, msg); | 98 | node.onMsg(ctx, msg); |
@@ -98,7 +98,7 @@ public class TbGetCustomerAttributeNodeTest { | @@ -98,7 +98,7 @@ public class TbGetCustomerAttributeNodeTest { | ||
98 | User user = new User(); | 98 | User user = new User(); |
99 | user.setCustomerId(customerId); | 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 | when(ctx.getUserService()).thenReturn(userService); | 103 | when(ctx.getUserService()).thenReturn(userService); |
104 | when(userService.findUserByIdAsync(userId)).thenReturn(Futures.immediateFuture(user)); | 104 | when(userService.findUserByIdAsync(userId)).thenReturn(Futures.immediateFuture(user)); |
@@ -123,7 +123,7 @@ public class TbGetCustomerAttributeNodeTest { | @@ -123,7 +123,7 @@ public class TbGetCustomerAttributeNodeTest { | ||
123 | User user = new User(); | 123 | User user = new User(); |
124 | user.setCustomerId(customerId); | 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 | when(ctx.getUserService()).thenReturn(userService); | 128 | when(ctx.getUserService()).thenReturn(userService); |
129 | when(userService.findUserByIdAsync(userId)).thenReturn(Futures.immediateFuture(user)); | 129 | when(userService.findUserByIdAsync(userId)).thenReturn(Futures.immediateFuture(user)); |
@@ -148,7 +148,7 @@ public class TbGetCustomerAttributeNodeTest { | @@ -148,7 +148,7 @@ public class TbGetCustomerAttributeNodeTest { | ||
148 | User user = new User(); | 148 | User user = new User(); |
149 | user.setCustomerId(customerId); | 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 | when(ctx.getUserService()).thenReturn(userService); | 153 | when(ctx.getUserService()).thenReturn(userService); |
154 | when(userService.findUserByIdAsync(userId)).thenReturn(Futures.immediateFuture(null)); | 154 | when(userService.findUserByIdAsync(userId)).thenReturn(Futures.immediateFuture(null)); |
@@ -166,7 +166,7 @@ public class TbGetCustomerAttributeNodeTest { | @@ -166,7 +166,7 @@ public class TbGetCustomerAttributeNodeTest { | ||
166 | @Test | 166 | @Test |
167 | public void customerAttributeAddedInMetadata() { | 167 | public void customerAttributeAddedInMetadata() { |
168 | CustomerId customerId = new CustomerId(UUIDs.timeBased()); | 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 | entityAttributeFetched(customerId); | 170 | entityAttributeFetched(customerId); |
171 | } | 171 | } |
172 | 172 | ||
@@ -177,7 +177,7 @@ public class TbGetCustomerAttributeNodeTest { | @@ -177,7 +177,7 @@ public class TbGetCustomerAttributeNodeTest { | ||
177 | User user = new User(); | 177 | User user = new User(); |
178 | user.setCustomerId(customerId); | 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 | when(ctx.getUserService()).thenReturn(userService); | 182 | when(ctx.getUserService()).thenReturn(userService); |
183 | when(userService.findUserByIdAsync(userId)).thenReturn(Futures.immediateFuture(user)); | 183 | when(userService.findUserByIdAsync(userId)).thenReturn(Futures.immediateFuture(user)); |
@@ -192,7 +192,7 @@ public class TbGetCustomerAttributeNodeTest { | @@ -192,7 +192,7 @@ public class TbGetCustomerAttributeNodeTest { | ||
192 | Asset asset = new Asset(); | 192 | Asset asset = new Asset(); |
193 | asset.setCustomerId(customerId); | 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 | when(ctx.getAssetService()).thenReturn(assetService); | 197 | when(ctx.getAssetService()).thenReturn(assetService); |
198 | when(assetService.findAssetByIdAsync(assetId)).thenReturn(Futures.immediateFuture(asset)); | 198 | when(assetService.findAssetByIdAsync(assetId)).thenReturn(Futures.immediateFuture(asset)); |
@@ -207,7 +207,7 @@ public class TbGetCustomerAttributeNodeTest { | @@ -207,7 +207,7 @@ public class TbGetCustomerAttributeNodeTest { | ||
207 | Device device = new Device(); | 207 | Device device = new Device(); |
208 | device.setCustomerId(customerId); | 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 | when(ctx.getDeviceService()).thenReturn(deviceService); | 212 | when(ctx.getDeviceService()).thenReturn(deviceService); |
213 | when(deviceService.findDeviceByIdAsync(deviceId)).thenReturn(Futures.immediateFuture(device)); | 213 | when(deviceService.findDeviceByIdAsync(deviceId)).thenReturn(Futures.immediateFuture(device)); |
@@ -234,7 +234,7 @@ public class TbGetCustomerAttributeNodeTest { | @@ -234,7 +234,7 @@ public class TbGetCustomerAttributeNodeTest { | ||
234 | Device device = new Device(); | 234 | Device device = new Device(); |
235 | device.setCustomerId(customerId); | 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 | when(ctx.getDeviceService()).thenReturn(deviceService); | 239 | when(ctx.getDeviceService()).thenReturn(deviceService); |
240 | when(deviceService.findDeviceByIdAsync(deviceId)).thenReturn(Futures.immediateFuture(device)); | 240 | when(deviceService.findDeviceByIdAsync(deviceId)).thenReturn(Futures.immediateFuture(device)); |
@@ -57,7 +57,7 @@ public class TbChangeOriginatorNodeTest { | @@ -57,7 +57,7 @@ public class TbChangeOriginatorNodeTest { | ||
57 | Asset asset = new Asset(); | 57 | Asset asset = new Asset(); |
58 | asset.setCustomerId(customerId); | 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 | when(ctx.getAssetService()).thenReturn(assetService); | 62 | when(ctx.getAssetService()).thenReturn(assetService); |
63 | when(assetService.findAssetByIdAsync(assetId)).thenReturn(Futures.immediateFuture(asset)); | 63 | when(assetService.findAssetByIdAsync(assetId)).thenReturn(Futures.immediateFuture(asset)); |
@@ -78,7 +78,7 @@ public class TbChangeOriginatorNodeTest { | @@ -78,7 +78,7 @@ public class TbChangeOriginatorNodeTest { | ||
78 | Asset asset = new Asset(); | 78 | Asset asset = new Asset(); |
79 | asset.setCustomerId(customerId); | 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 | when(ctx.getAssetService()).thenReturn(assetService); | 83 | when(ctx.getAssetService()).thenReturn(assetService); |
84 | when(assetService.findAssetByIdAsync(assetId)).thenReturn(Futures.immediateFuture(asset)); | 84 | when(assetService.findAssetByIdAsync(assetId)).thenReturn(Futures.immediateFuture(asset)); |
@@ -99,7 +99,7 @@ public class TbChangeOriginatorNodeTest { | @@ -99,7 +99,7 @@ public class TbChangeOriginatorNodeTest { | ||
99 | Asset asset = new Asset(); | 99 | Asset asset = new Asset(); |
100 | asset.setCustomerId(customerId); | 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 | when(ctx.getAssetService()).thenReturn(assetService); | 104 | when(ctx.getAssetService()).thenReturn(assetService); |
105 | when(assetService.findAssetByIdAsync(assetId)).thenReturn(Futures.immediateFailedFuture(new IllegalStateException("wrong"))); | 105 | when(assetService.findAssetByIdAsync(assetId)).thenReturn(Futures.immediateFailedFuture(new IllegalStateException("wrong"))); |
@@ -51,13 +51,13 @@ public class TbTransformMsgNodeTest { | @@ -51,13 +51,13 @@ public class TbTransformMsgNodeTest { | ||
51 | 51 | ||
52 | @Test | 52 | @Test |
53 | public void metadataCanBeUpdated() throws TbNodeException { | 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 | TbMsgMetaData metaData = new TbMsgMetaData(); | 55 | TbMsgMetaData metaData = new TbMsgMetaData(); |
56 | metaData.putValue("temp", "7"); | 56 | metaData.putValue("temp", "7"); |
57 | metaData.putValue("humidity", "99"); | 57 | metaData.putValue("humidity", "99"); |
58 | String rawJson = "{\"name\": \"Vit\", \"passed\": 5, \"bigObj\": {\"prop\":42}}"; | 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 | mockJsExecutor(); | 61 | mockJsExecutor(); |
62 | 62 | ||
63 | node.onMsg(ctx, msg); | 63 | node.onMsg(ctx, msg); |
@@ -65,18 +65,18 @@ public class TbTransformMsgNodeTest { | @@ -65,18 +65,18 @@ public class TbTransformMsgNodeTest { | ||
65 | ArgumentCaptor<TbMsg> captor = ArgumentCaptor.forClass(TbMsg.class); | 65 | ArgumentCaptor<TbMsg> captor = ArgumentCaptor.forClass(TbMsg.class); |
66 | verify(ctx).tellNext(captor.capture()); | 66 | verify(ctx).tellNext(captor.capture()); |
67 | TbMsg actualMsg = captor.getValue(); | 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 | @Test | 71 | @Test |
72 | public void metadataCanBeAdded() throws TbNodeException { | 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 | TbMsgMetaData metaData = new TbMsgMetaData(); | 74 | TbMsgMetaData metaData = new TbMsgMetaData(); |
75 | metaData.putValue("temp", "7"); | 75 | metaData.putValue("temp", "7"); |
76 | metaData.putValue("humidity", "99"); | 76 | metaData.putValue("humidity", "99"); |
77 | String rawJson = "{\"name\": \"Vit\", \"passed\": 5, \"bigObj\": {\"prop\":42}}"; | 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 | mockJsExecutor(); | 80 | mockJsExecutor(); |
81 | 81 | ||
82 | node.onMsg(ctx, msg); | 82 | node.onMsg(ctx, msg); |
@@ -84,18 +84,18 @@ public class TbTransformMsgNodeTest { | @@ -84,18 +84,18 @@ public class TbTransformMsgNodeTest { | ||
84 | ArgumentCaptor<TbMsg> captor = ArgumentCaptor.forClass(TbMsg.class); | 84 | ArgumentCaptor<TbMsg> captor = ArgumentCaptor.forClass(TbMsg.class); |
85 | verify(ctx).tellNext(captor.capture()); | 85 | verify(ctx).tellNext(captor.capture()); |
86 | TbMsg actualMsg = captor.getValue(); | 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 | @Test | 90 | @Test |
91 | public void payloadCanBeUpdated() throws TbNodeException { | 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 | TbMsgMetaData metaData = new TbMsgMetaData(); | 93 | TbMsgMetaData metaData = new TbMsgMetaData(); |
94 | metaData.putValue("temp", "7"); | 94 | metaData.putValue("temp", "7"); |
95 | metaData.putValue("humidity", "99"); | 95 | metaData.putValue("humidity", "99"); |
96 | String rawJson = "{\"name\":\"Vit\",\"passed\": 5,\"bigObj\":{\"prop\":42}}"; | 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 | mockJsExecutor(); | 99 | mockJsExecutor(); |
100 | 100 | ||
101 | node.onMsg(ctx, msg); | 101 | node.onMsg(ctx, msg); |
@@ -103,7 +103,7 @@ public class TbTransformMsgNodeTest { | @@ -103,7 +103,7 @@ public class TbTransformMsgNodeTest { | ||
103 | ArgumentCaptor<TbMsg> captor = ArgumentCaptor.forClass(TbMsg.class); | 103 | ArgumentCaptor<TbMsg> captor = ArgumentCaptor.forClass(TbMsg.class); |
104 | verify(ctx).tellNext(captor.capture()); | 104 | verify(ctx).tellNext(captor.capture()); |
105 | TbMsg actualMsg = captor.getValue(); | 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 | assertEquals(expectedJson, new String(actualMsg.getData())); | 107 | assertEquals(expectedJson, new String(actualMsg.getData())); |
108 | } | 108 | } |
109 | 109 |