Refactor JS Executor.
This commit is contained in:
parent
dc41e63091
commit
024dd99cb7
@ -321,7 +321,7 @@ public class ActorSystemContext {
|
|||||||
.put("msgId", tbMsg.getId().toString())
|
.put("msgId", tbMsg.getId().toString())
|
||||||
.put("msgType", tbMsg.getType())
|
.put("msgType", tbMsg.getType())
|
||||||
.put("dataType", tbMsg.getDataType().name())
|
.put("dataType", tbMsg.getDataType().name())
|
||||||
.put("data", convertToString(tbMsg.getDataType(), tbMsg.getData()))
|
.put("data", tbMsg.getData())
|
||||||
.put("metadata", metadata);
|
.put("metadata", metadata);
|
||||||
|
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
@ -335,21 +335,6 @@ public class ActorSystemContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String convertToString(TbMsgDataType messageType, byte[] data) {
|
|
||||||
if (data == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
switch (messageType) {
|
|
||||||
case JSON:
|
|
||||||
case TEXT:
|
|
||||||
return new String(data, StandardCharsets.UTF_8);
|
|
||||||
case BINARY:
|
|
||||||
return Base64Utils.encodeToString(data);
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("Message type: " + messageType + " is not supported!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Exception toException(Throwable error) {
|
public static Exception toException(Throwable error) {
|
||||||
return Exception.class.isInstance(error) ? (Exception) error : new Exception(error);
|
return Exception.class.isInstance(error) ? (Exception) error : new Exception(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -149,7 +149,7 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule
|
|||||||
"CUSTOM",
|
"CUSTOM",
|
||||||
device.getId(),
|
device.getId(),
|
||||||
new TbMsgMetaData(),
|
new TbMsgMetaData(),
|
||||||
new byte[]{});
|
"{}");
|
||||||
actorService.onMsg(new ServiceToRuleEngineMsg(savedTenant.getId(), tbMsg));
|
actorService.onMsg(new ServiceToRuleEngineMsg(savedTenant.getId(), tbMsg));
|
||||||
|
|
||||||
Thread.sleep(3000);
|
Thread.sleep(3000);
|
||||||
|
|||||||
@ -135,7 +135,7 @@ public abstract class AbstractRuleEngineLifecycleIntegrationTest extends Abstrac
|
|||||||
"CUSTOM",
|
"CUSTOM",
|
||||||
device.getId(),
|
device.getId(),
|
||||||
new TbMsgMetaData(),
|
new TbMsgMetaData(),
|
||||||
new byte[]{});
|
"{}");
|
||||||
actorService.onMsg(new ServiceToRuleEngineMsg(savedTenant.getId(), tbMsg));
|
actorService.onMsg(new ServiceToRuleEngineMsg(savedTenant.getId(), tbMsg));
|
||||||
|
|
||||||
Thread.sleep(3000);
|
Thread.sleep(3000);
|
||||||
|
|||||||
@ -39,9 +39,9 @@ public final class TbMsg implements Serializable {
|
|||||||
private final EntityId originator;
|
private final EntityId originator;
|
||||||
private final TbMsgMetaData metaData;
|
private final TbMsgMetaData metaData;
|
||||||
private final TbMsgDataType dataType;
|
private final TbMsgDataType dataType;
|
||||||
private final byte[] data;
|
private final String data;
|
||||||
|
|
||||||
public TbMsg(UUID id, String type, EntityId originator, TbMsgMetaData metaData, byte[] data) {
|
public TbMsg(UUID id, String type, EntityId originator, TbMsgMetaData metaData, String data) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.originator = originator;
|
this.originator = originator;
|
||||||
@ -64,7 +64,7 @@ public final class TbMsg implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
builder.setDataType(msg.getDataType().ordinal());
|
builder.setDataType(msg.getDataType().ordinal());
|
||||||
builder.setData(ByteString.copyFrom(msg.getData()));
|
builder.setData(msg.getData());
|
||||||
byte[] bytes = builder.build().toByteArray();
|
byte[] bytes = builder.build().toByteArray();
|
||||||
return ByteBuffer.wrap(bytes);
|
return ByteBuffer.wrap(bytes);
|
||||||
}
|
}
|
||||||
@ -75,16 +75,13 @@ public final class TbMsg implements Serializable {
|
|||||||
TbMsgMetaData metaData = new TbMsgMetaData(proto.getMetaData().getDataMap());
|
TbMsgMetaData metaData = new TbMsgMetaData(proto.getMetaData().getDataMap());
|
||||||
EntityId entityId = EntityIdFactory.getByTypeAndId(proto.getEntityType(), proto.getEntityId());
|
EntityId entityId = EntityIdFactory.getByTypeAndId(proto.getEntityType(), proto.getEntityId());
|
||||||
TbMsgDataType dataType = TbMsgDataType.values()[proto.getDataType()];
|
TbMsgDataType dataType = TbMsgDataType.values()[proto.getDataType()];
|
||||||
return new TbMsg(UUID.fromString(proto.getId()), proto.getType(), entityId, metaData, dataType, proto.getData().toByteArray());
|
return new TbMsg(UUID.fromString(proto.getId()), proto.getType(), entityId, metaData, dataType, proto.getData());
|
||||||
} catch (InvalidProtocolBufferException e) {
|
} catch (InvalidProtocolBufferException e) {
|
||||||
throw new IllegalStateException("Could not parse protobuf for TbMsg", e);
|
throw new IllegalStateException("Could not parse protobuf for TbMsg", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public TbMsg copy() {
|
public TbMsg copy() {
|
||||||
int dataSize = data.length;
|
return new TbMsg(id, type, originator, metaData.copy(), dataType, data);
|
||||||
byte[] dataCopy = new byte[dataSize];
|
|
||||||
System.arraycopy( data, 0, dataCopy, 0, data.length );
|
|
||||||
return new TbMsg(id, type, originator, metaData.copy(), dataType, dataCopy);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,7 +33,7 @@ public final class TbMsgMetaData implements Serializable {
|
|||||||
|
|
||||||
private Map<String, String> data = new ConcurrentHashMap<>();
|
private Map<String, String> data = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
TbMsgMetaData(Map<String, String> data) {
|
public TbMsgMetaData(Map<String, String> data) {
|
||||||
this.data = data;
|
this.data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -32,5 +32,5 @@ message TbMsgProto {
|
|||||||
TbMsgMetaDataProto metaData = 5;
|
TbMsgMetaDataProto metaData = 5;
|
||||||
|
|
||||||
int32 dataType = 6;
|
int32 dataType = 6;
|
||||||
bytes data = 7;
|
string data = 7;
|
||||||
}
|
}
|
||||||
@ -126,7 +126,7 @@ public class QueueBenchmark implements CommandLineRunner {
|
|||||||
TbMsgMetaData metaData = new TbMsgMetaData();
|
TbMsgMetaData metaData = new TbMsgMetaData();
|
||||||
metaData.putValue("key", "value");
|
metaData.putValue("key", "value");
|
||||||
String dataStr = "someContent";
|
String dataStr = "someContent";
|
||||||
return new TbMsg(UUIDs.timeBased(), "type", null, metaData, TbMsgDataType.JSON, dataStr.getBytes());
|
return new TbMsg(UUIDs.timeBased(), "type", null, metaData, TbMsgDataType.JSON, dataStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
|||||||
@ -45,7 +45,7 @@ public class CassandraMsgRepositoryTest extends AbstractServiceTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void msgCanBeSavedAndRead() throws ExecutionException, InterruptedException {
|
public void msgCanBeSavedAndRead() throws ExecutionException, InterruptedException {
|
||||||
TbMsg msg = new TbMsg(UUIDs.timeBased(), "type", new DeviceId(UUIDs.timeBased()), null, TbMsgDataType.JSON, new byte[4]);
|
TbMsg msg = new TbMsg(UUIDs.timeBased(), "type", new DeviceId(UUIDs.timeBased()), null, TbMsgDataType.JSON, "0000");
|
||||||
UUID nodeId = UUIDs.timeBased();
|
UUID nodeId = UUIDs.timeBased();
|
||||||
ListenableFuture<Void> future = msgRepository.save(msg, nodeId, 1L, 1L, 1L);
|
ListenableFuture<Void> future = msgRepository.save(msg, nodeId, 1L, 1L, 1L);
|
||||||
future.get();
|
future.get();
|
||||||
@ -55,7 +55,7 @@ public class CassandraMsgRepositoryTest extends AbstractServiceTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void expiredMsgsAreNotReturned() throws ExecutionException, InterruptedException {
|
public void expiredMsgsAreNotReturned() throws ExecutionException, InterruptedException {
|
||||||
TbMsg msg = new TbMsg(UUIDs.timeBased(), "type", new DeviceId(UUIDs.timeBased()), null, TbMsgDataType.JSON, new byte[4]);
|
TbMsg msg = new TbMsg(UUIDs.timeBased(), "type", new DeviceId(UUIDs.timeBased()), null, TbMsgDataType.JSON, "0000");
|
||||||
UUID nodeId = UUIDs.timeBased();
|
UUID nodeId = UUIDs.timeBased();
|
||||||
ListenableFuture<Void> future = msgRepository.save(msg, nodeId, 2L, 2L, 2L);
|
ListenableFuture<Void> future = msgRepository.save(msg, nodeId, 2L, 2L, 2L);
|
||||||
future.get();
|
future.get();
|
||||||
@ -68,7 +68,7 @@ public class CassandraMsgRepositoryTest extends AbstractServiceTest {
|
|||||||
TbMsgMetaData metaData = new TbMsgMetaData();
|
TbMsgMetaData metaData = new TbMsgMetaData();
|
||||||
metaData.putValue("key", "value");
|
metaData.putValue("key", "value");
|
||||||
String dataStr = "someContent";
|
String dataStr = "someContent";
|
||||||
TbMsg msg = new TbMsg(UUIDs.timeBased(), "type", new DeviceId(UUIDs.timeBased()), metaData, TbMsgDataType.JSON, dataStr.getBytes());
|
TbMsg msg = new TbMsg(UUIDs.timeBased(), "type", new DeviceId(UUIDs.timeBased()), metaData, TbMsgDataType.JSON, dataStr);
|
||||||
UUID nodeId = UUIDs.timeBased();
|
UUID nodeId = UUIDs.timeBased();
|
||||||
ListenableFuture<Void> future = msgRepository.save(msg, nodeId, 1L, 1L, 1L);
|
ListenableFuture<Void> future = msgRepository.save(msg, nodeId, 1L, 1L, 1L);
|
||||||
future.get();
|
future.get();
|
||||||
|
|||||||
@ -16,6 +16,7 @@
|
|||||||
package org.thingsboard.rule.engine.debug;
|
package org.thingsboard.rule.engine.debug;
|
||||||
|
|
||||||
import com.datastax.driver.core.utils.UUIDs;
|
import com.datastax.driver.core.utils.UUIDs;
|
||||||
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
import org.thingsboard.rule.engine.TbNodeUtils;
|
import org.thingsboard.rule.engine.TbNodeUtils;
|
||||||
@ -58,9 +59,11 @@ public class TbMsgGeneratorNode implements TbNode {
|
|||||||
public static final String TB_MSG_GENERATOR_NODE_MSG = "TbMsgGeneratorNodeMsg";
|
public static final String TB_MSG_GENERATOR_NODE_MSG = "TbMsgGeneratorNodeMsg";
|
||||||
|
|
||||||
private TbMsgGeneratorNodeConfiguration config;
|
private TbMsgGeneratorNodeConfiguration config;
|
||||||
|
private NashornJsEngine jsEngine;
|
||||||
private long delay;
|
private long delay;
|
||||||
private EntityId originatorId;
|
private EntityId originatorId;
|
||||||
private UUID nextTickId;
|
private UUID nextTickId;
|
||||||
|
private TbMsg prevMsg;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
|
public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
|
||||||
@ -71,29 +74,41 @@ public class TbMsgGeneratorNode implements TbNode {
|
|||||||
} else {
|
} else {
|
||||||
originatorId = ctx.getSelfId();
|
originatorId = ctx.getSelfId();
|
||||||
}
|
}
|
||||||
|
this.jsEngine = new NashornJsEngine(config.getJsScript(), "Generate", "prevMsg", "prevMetadata", "prevMsgType");
|
||||||
sentTickMsg(ctx);
|
sentTickMsg(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMsg(TbContext ctx, TbMsg msg) {
|
public void onMsg(TbContext ctx, TbMsg msg) {
|
||||||
if (msg.getType().equals(TB_MSG_GENERATOR_NODE_MSG) && msg.getId().equals(nextTickId)) {
|
if (msg.getType().equals(TB_MSG_GENERATOR_NODE_MSG) && msg.getId().equals(nextTickId)) {
|
||||||
TbMsgMetaData metaData = new TbMsgMetaData();
|
withCallback(generate(ctx),
|
||||||
if (config.getMsgMetaData() != null) {
|
m -> {ctx.tellNext(m); sentTickMsg(ctx);},
|
||||||
config.getMsgMetaData().forEach(metaData::putValue);
|
t -> {ctx.tellError(msg, t); sentTickMsg(ctx);});
|
||||||
}
|
|
||||||
ctx.tellNext(new TbMsg(UUIDs.timeBased(), config.getMsgType(), originatorId, metaData, config.getMsgBody().getBytes(StandardCharsets.UTF_8)));
|
|
||||||
sentTickMsg(ctx);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sentTickMsg(TbContext ctx) {
|
private void sentTickMsg(TbContext ctx) {
|
||||||
TbMsg tickMsg = new TbMsg(UUIDs.timeBased(), TB_MSG_GENERATOR_NODE_MSG, ctx.getSelfId(), new TbMsgMetaData(), new byte[]{});
|
TbMsg tickMsg = new TbMsg(UUIDs.timeBased(), TB_MSG_GENERATOR_NODE_MSG, ctx.getSelfId(), new TbMsgMetaData(), "");
|
||||||
nextTickId = tickMsg.getId();
|
nextTickId = tickMsg.getId();
|
||||||
ctx.tellSelf(tickMsg, delay);
|
ctx.tellSelf(tickMsg, delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected ListenableFuture<TbMsg> generate(TbContext ctx) {
|
||||||
|
return ctx.getJsExecutor().executeAsync(() -> {
|
||||||
|
if (prevMsg == null) {
|
||||||
|
prevMsg = new TbMsg(UUIDs.timeBased(), "", originatorId, new TbMsgMetaData(), "{}");
|
||||||
|
}
|
||||||
|
TbMsg generated = jsEngine.executeGenerate(prevMsg);
|
||||||
|
prevMsg = new TbMsg(UUIDs.timeBased(), generated.getType(), originatorId, generated.getMetaData(), generated.getData());
|
||||||
|
return prevMsg;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
|
prevMsg = null;
|
||||||
|
if (jsEngine != null) {
|
||||||
|
jsEngine.destroy();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,17 +28,17 @@ public class TbMsgGeneratorNodeConfiguration implements NodeConfiguration<TbMsgG
|
|||||||
private int periodInSeconds;
|
private int periodInSeconds;
|
||||||
private String originatorId;
|
private String originatorId;
|
||||||
private EntityType originatorType;
|
private EntityType originatorType;
|
||||||
private String msgType;
|
private String jsScript;
|
||||||
private String msgBody;
|
|
||||||
private Map<String, String> msgMetaData;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TbMsgGeneratorNodeConfiguration defaultConfiguration() {
|
public TbMsgGeneratorNodeConfiguration defaultConfiguration() {
|
||||||
TbMsgGeneratorNodeConfiguration configuration = new TbMsgGeneratorNodeConfiguration();
|
TbMsgGeneratorNodeConfiguration configuration = new TbMsgGeneratorNodeConfiguration();
|
||||||
configuration.setMsgCount(0);
|
configuration.setMsgCount(0);
|
||||||
configuration.setPeriodInSeconds(1);
|
configuration.setPeriodInSeconds(1);
|
||||||
configuration.setMsgType("DebugMsg");
|
configuration.setJsScript("var msg = { temp: 42, humidity: 77 };\n" +
|
||||||
configuration.setMsgBody("{}");
|
"var metadata = { data: 40 };\n" +
|
||||||
|
"var msgType = \"DebugMsg\";\n\n" +
|
||||||
|
"return { msg: msg, metadata: metadata, msgType: msgType };");
|
||||||
return configuration;
|
return configuration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,7 +35,8 @@ import static org.thingsboard.rule.engine.DonAsynchron.withCallback;
|
|||||||
nodeDetails = "Evaluate incoming Message with configured JS condition. " +
|
nodeDetails = "Evaluate incoming Message with configured JS condition. " +
|
||||||
"If <b>True</b> - send Message via <b>True</b> chain, otherwise <b>False</b> chain is used." +
|
"If <b>True</b> - send Message via <b>True</b> chain, otherwise <b>False</b> chain is used." +
|
||||||
"Message payload can be accessed via <code>msg</code> property. For example <code>msg.temperature < 10;</code>" +
|
"Message payload can be accessed via <code>msg</code> property. For example <code>msg.temperature < 10;</code>" +
|
||||||
"Message metadata can be accessed via <code>metadata</code> property. For example <code>metadata.customerName === 'John';</code>",
|
"Message metadata can be accessed via <code>metadata</code> property. For example <code>metadata.customerName === 'John';</code>" +
|
||||||
|
"Message type can be accessed via <code>msgType</code> property.",
|
||||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||||
configDirective = "tbFilterNodeScriptConfig")
|
configDirective = "tbFilterNodeScriptConfig")
|
||||||
|
|
||||||
@ -53,15 +54,11 @@ public class TbJsFilterNode implements TbNode {
|
|||||||
@Override
|
@Override
|
||||||
public void onMsg(TbContext ctx, TbMsg msg) {
|
public void onMsg(TbContext ctx, TbMsg msg) {
|
||||||
ListeningExecutor jsExecutor = ctx.getJsExecutor();
|
ListeningExecutor jsExecutor = ctx.getJsExecutor();
|
||||||
withCallback(jsExecutor.executeAsync(() -> jsEngine.executeFilter(toBindings(msg))),
|
withCallback(jsExecutor.executeAsync(() -> jsEngine.executeFilter(msg)),
|
||||||
filterResult -> ctx.tellNext(msg, Boolean.toString(filterResult)),
|
filterResult -> ctx.tellNext(msg, Boolean.toString(filterResult)),
|
||||||
t -> ctx.tellError(msg, t));
|
t -> ctx.tellError(msg, t));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Bindings toBindings(TbMsg msg) {
|
|
||||||
return NashornJsEngine.bindMsg(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
if (jsEngine != null) {
|
if (jsEngine != null) {
|
||||||
|
|||||||
@ -36,7 +36,8 @@ import static org.thingsboard.rule.engine.DonAsynchron.withCallback;
|
|||||||
nodeDetails = "Node executes configured JS script. Script should return array of next Chain names where Message should be routed. " +
|
nodeDetails = "Node executes configured JS script. Script should return array of next Chain names where Message should be routed. " +
|
||||||
"If Array is empty - message not routed to next Node. " +
|
"If Array is empty - message not routed to next Node. " +
|
||||||
"Message payload can be accessed via <code>msg</code> property. For example <code>msg.temperature < 10;</code> " +
|
"Message payload can be accessed via <code>msg</code> property. For example <code>msg.temperature < 10;</code> " +
|
||||||
"Message metadata can be accessed via <code>metadata</code> property. For example <code>metadata.customerName === 'John';</code>",
|
"Message metadata can be accessed via <code>metadata</code> property. For example <code>metadata.customerName === 'John';</code>" +
|
||||||
|
"Message type can be accessed via <code>msgType</code> property.",
|
||||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||||
configDirective = "tbFilterNodeSwitchConfig")
|
configDirective = "tbFilterNodeSwitchConfig")
|
||||||
public class TbJsSwitchNode implements TbNode {
|
public class TbJsSwitchNode implements TbNode {
|
||||||
@ -53,7 +54,7 @@ public class TbJsSwitchNode implements TbNode {
|
|||||||
@Override
|
@Override
|
||||||
public void onMsg(TbContext ctx, TbMsg msg) {
|
public void onMsg(TbContext ctx, TbMsg msg) {
|
||||||
ListeningExecutor jsExecutor = ctx.getJsExecutor();
|
ListeningExecutor jsExecutor = ctx.getJsExecutor();
|
||||||
withCallback(jsExecutor.executeAsync(() -> jsEngine.executeSwitch(toBindings(msg))),
|
withCallback(jsExecutor.executeAsync(() -> jsEngine.executeSwitch(msg)),
|
||||||
result -> processSwitch(ctx, msg, result),
|
result -> processSwitch(ctx, msg, result),
|
||||||
t -> ctx.tellError(msg, t));
|
t -> ctx.tellError(msg, t));
|
||||||
}
|
}
|
||||||
@ -62,10 +63,6 @@ public class TbJsSwitchNode implements TbNode {
|
|||||||
ctx.tellNext(msg, nextRelations);
|
ctx.tellNext(msg, nextRelations);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Bindings toBindings(TbMsg msg) {
|
|
||||||
return NashornJsEngine.bindMsg(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
if (jsEngine != null) {
|
if (jsEngine != null) {
|
||||||
|
|||||||
@ -16,6 +16,7 @@
|
|||||||
package org.thingsboard.rule.engine.js;
|
package org.thingsboard.rule.engine.js;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
@ -23,9 +24,13 @@ import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
|
|||||||
import jdk.nashorn.api.scripting.ScriptObjectMirror;
|
import jdk.nashorn.api.scripting.ScriptObjectMirror;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.ArrayUtils;
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.thingsboard.server.common.msg.TbMsg;
|
import org.thingsboard.server.common.msg.TbMsg;
|
||||||
|
import org.thingsboard.server.common.msg.TbMsgMetaData;
|
||||||
|
|
||||||
import javax.script.*;
|
import javax.script.*;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@ -34,112 +39,158 @@ import java.util.Set;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class NashornJsEngine {
|
public class NashornJsEngine {
|
||||||
|
|
||||||
|
|
||||||
|
public static final String MSG = "msg";
|
||||||
public static final String METADATA = "metadata";
|
public static final String METADATA = "metadata";
|
||||||
public static final String DATA = "msg";
|
public static final String MSG_TYPE = "msgType";
|
||||||
|
|
||||||
private static final String JS_WRAPPER_PREFIX_TEMPLATE = "function %s(msg, metadata) { ";
|
private static final String JS_WRAPPER_PREFIX_TEMPLATE = "function %s(msgStr, metadataStr, msgType) { " +
|
||||||
private static final String JS_WRAPPER_SUFFIX_TEMPLATE = "}\n %s(msg, metadata);";
|
" var msg = JSON.parse(msgStr); " +
|
||||||
|
" var metadata = JSON.parse(metadataStr); " +
|
||||||
|
" return JSON.stringify(%s(msg, metadata, msgType));" +
|
||||||
|
" function %s(%s, %s, %s) {";
|
||||||
|
private static final String JS_WRAPPER_SUFFIX = "}" +
|
||||||
|
"\n}";
|
||||||
|
|
||||||
|
private static final ObjectMapper mapper = new ObjectMapper();
|
||||||
private static NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
|
private static NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
|
||||||
|
private static ScriptEngine engine = factory.getScriptEngine(new String[]{"--no-java"});
|
||||||
|
|
||||||
private CompiledScript engine;
|
private final String invokeFunctionName;
|
||||||
|
|
||||||
public NashornJsEngine(String script, String functionName) {
|
public NashornJsEngine(String script, String functionName, String... argNames) {
|
||||||
String jsWrapperPrefix = String.format(JS_WRAPPER_PREFIX_TEMPLATE, functionName);
|
this.invokeFunctionName = "invokeInternal" + this.hashCode();
|
||||||
String jsWrapperSuffix = String.format(JS_WRAPPER_SUFFIX_TEMPLATE, functionName);
|
String msgArg;
|
||||||
engine = compileScript(jsWrapperPrefix + script + jsWrapperSuffix);
|
String metadataArg;
|
||||||
|
String msgTypeArg;
|
||||||
|
if (argNames != null && argNames.length == 3) {
|
||||||
|
msgArg = argNames[0];
|
||||||
|
metadataArg = argNames[1];
|
||||||
|
msgTypeArg = argNames[2];
|
||||||
|
} else {
|
||||||
|
msgArg = MSG;
|
||||||
|
metadataArg = METADATA;
|
||||||
|
msgTypeArg = MSG_TYPE;
|
||||||
|
}
|
||||||
|
String jsWrapperPrefix = String.format(JS_WRAPPER_PREFIX_TEMPLATE, this.invokeFunctionName,
|
||||||
|
functionName, functionName, msgArg, metadataArg, msgTypeArg);
|
||||||
|
compileScript(jsWrapperPrefix + script + JS_WRAPPER_SUFFIX);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static CompiledScript compileScript(String script) {
|
private static void compileScript(String script) {
|
||||||
ScriptEngine engine = factory.getScriptEngine(new String[]{"--no-java"});
|
|
||||||
Compilable compEngine = (Compilable) engine;
|
|
||||||
try {
|
try {
|
||||||
return compEngine.compile(script);
|
engine.eval(script);
|
||||||
} catch (ScriptException e) {
|
} catch (ScriptException e) {
|
||||||
log.warn("Failed to compile JS script: {}", e.getMessage(), e);
|
log.warn("Failed to compile JS script: {}", e.getMessage(), e);
|
||||||
throw new IllegalArgumentException("Can't compile script: " + e.getMessage());
|
throw new IllegalArgumentException("Can't compile script: " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Bindings bindMsg(TbMsg msg) {
|
private static String[] prepareArgs(TbMsg msg) {
|
||||||
try {
|
try {
|
||||||
Bindings bindings = new SimpleBindings();
|
String[] args = new String[3];
|
||||||
if (ArrayUtils.isNotEmpty(msg.getData())) {
|
if (msg.getData() != null) {
|
||||||
ObjectMapper mapper = new ObjectMapper();
|
args[0] = msg.getData();
|
||||||
JsonNode jsonNode = mapper.readTree(msg.getData());
|
|
||||||
Map map = mapper.treeToValue(jsonNode, Map.class);
|
|
||||||
bindings.put(DATA, map);
|
|
||||||
} else {
|
} else {
|
||||||
bindings.put(DATA, Collections.emptyMap());
|
args[0] = "";
|
||||||
}
|
}
|
||||||
bindings.put(METADATA, msg.getMetaData().getData());
|
args[1] = mapper.writeValueAsString(msg.getMetaData().getData());
|
||||||
return bindings;
|
args[2] = msg.getType();
|
||||||
|
return args;
|
||||||
} catch (Throwable th) {
|
} catch (Throwable th) {
|
||||||
throw new IllegalArgumentException("Cannot bind js args", th);
|
throw new IllegalArgumentException("Cannot bind js args", th);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TbMsg unbindMsg(Bindings bindings, TbMsg msg) throws JsonProcessingException {
|
private static TbMsg unbindMsg(JsonNode msgData, TbMsg msg) {
|
||||||
for (Map.Entry<String, String> entry : msg.getMetaData().getData().entrySet()) {
|
|
||||||
Object obj = entry.getValue();
|
|
||||||
entry.setValue(obj.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
Object payload = bindings.get(DATA);
|
|
||||||
if (payload != null) {
|
|
||||||
ObjectMapper mapper = new ObjectMapper();
|
|
||||||
byte[] bytes = mapper.writeValueAsBytes(payload);
|
|
||||||
return new TbMsg(msg.getId(), msg.getType(), msg.getOriginator(), msg.getMetaData(), bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TbMsg executeUpdate(Bindings bindings, TbMsg msg) throws ScriptException {
|
|
||||||
try {
|
try {
|
||||||
engine.eval(bindings);
|
String data = null;
|
||||||
return unbindMsg(bindings, msg);
|
Map<String, String> metadata = null;
|
||||||
|
String messageType = null;
|
||||||
|
if (msgData.has(MSG)) {
|
||||||
|
JsonNode msgPayload = msgData.get(MSG);
|
||||||
|
data = mapper.writeValueAsString(msgPayload);
|
||||||
|
}
|
||||||
|
if (msgData.has(METADATA)) {
|
||||||
|
JsonNode msgMetadata = msgData.get(METADATA);
|
||||||
|
metadata = mapper.convertValue(msgMetadata, new TypeReference<Map<String, String>>() {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (msgData.has(MSG_TYPE)) {
|
||||||
|
messageType = msgData.get(MSG_TYPE).asText();
|
||||||
|
}
|
||||||
|
String newData = data != null ? data : msg.getData();
|
||||||
|
TbMsgMetaData newMetadata = metadata != null ? new TbMsgMetaData(metadata) : msg.getMetaData();
|
||||||
|
String newMessageType = !StringUtils.isEmpty(messageType) ? messageType : msg.getType();
|
||||||
|
return new TbMsg(msg.getId(), newMessageType, msg.getOriginator(), newMetadata, newData);
|
||||||
} catch (Throwable th) {
|
} catch (Throwable th) {
|
||||||
th.printStackTrace();
|
th.printStackTrace();
|
||||||
throw new IllegalArgumentException("Cannot unbind js args", th);
|
throw new RuntimeException("Failed to unbind message data from javascript result", th);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean executeFilter(Bindings bindings) throws ScriptException {
|
public TbMsg executeUpdate(TbMsg msg) throws ScriptException {
|
||||||
Object eval = engine.eval(bindings);
|
JsonNode result = executeScript(msg);
|
||||||
if (eval instanceof Boolean) {
|
if (!result.isObject()) {
|
||||||
return (boolean) eval;
|
log.warn("Wrong result type: {}", result.getNodeType());
|
||||||
} else {
|
throw new ScriptException("Wrong result type: " + result.getNodeType());
|
||||||
log.warn("Wrong result type: {}", eval);
|
|
||||||
throw new ScriptException("Wrong result type: " + eval);
|
|
||||||
}
|
}
|
||||||
|
return unbindMsg(result, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> executeSwitch(Bindings bindings) throws ScriptException, NoSuchMethodException {
|
public TbMsg executeGenerate(TbMsg prevMsg) throws ScriptException {
|
||||||
Object eval = this.engine.eval(bindings);
|
JsonNode result = executeScript(prevMsg);
|
||||||
if (eval instanceof String) {
|
if (!result.isObject()) {
|
||||||
return Collections.singleton((String) eval);
|
log.warn("Wrong result type: {}", result.getNodeType());
|
||||||
} else if (eval instanceof ScriptObjectMirror) {
|
throw new ScriptException("Wrong result type: " + result.getNodeType());
|
||||||
ScriptObjectMirror mir = (ScriptObjectMirror) eval;
|
}
|
||||||
if (mir.isArray()) {
|
return unbindMsg(result, prevMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean executeFilter(TbMsg msg) throws ScriptException {
|
||||||
|
JsonNode result = executeScript(msg);
|
||||||
|
if (!result.isBoolean()) {
|
||||||
|
log.warn("Wrong result type: {}", result.getNodeType());
|
||||||
|
throw new ScriptException("Wrong result type: " + result.getNodeType());
|
||||||
|
}
|
||||||
|
return result.asBoolean();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> executeSwitch(TbMsg msg) throws ScriptException, NoSuchMethodException {
|
||||||
|
JsonNode result = executeScript(msg);
|
||||||
|
if (result.isTextual()) {
|
||||||
|
return Collections.singleton(result.asText());
|
||||||
|
} else if (result.isArray()) {
|
||||||
Set<String> nextStates = Sets.newHashSet();
|
Set<String> nextStates = Sets.newHashSet();
|
||||||
for (Map.Entry<String, Object> entry : mir.entrySet()) {
|
for (JsonNode val : result) {
|
||||||
if (entry.getValue() instanceof String) {
|
if (!val.isTextual()) {
|
||||||
nextStates.add((String) entry.getValue());
|
log.warn("Wrong result type: {}", val.getNodeType());
|
||||||
|
throw new ScriptException("Wrong result type: " + val.getNodeType());
|
||||||
} else {
|
} else {
|
||||||
log.warn("Wrong result type: {}", eval);
|
nextStates.add(val.asText());
|
||||||
throw new ScriptException("Wrong result type: " + eval);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nextStates;
|
return nextStates;
|
||||||
|
} else {
|
||||||
|
log.warn("Wrong result type: {}", result.getNodeType());
|
||||||
|
throw new ScriptException("Wrong result type: " + result.getNodeType());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.warn("Wrong result type: {}", eval);
|
private JsonNode executeScript(TbMsg msg) throws ScriptException {
|
||||||
throw new ScriptException("Wrong result type: " + eval);
|
try {
|
||||||
|
String[] inArgs = prepareArgs(msg);
|
||||||
|
String eval = ((Invocable)engine).invokeFunction(this.invokeFunctionName, inArgs[0], inArgs[1], inArgs[2]).toString();
|
||||||
|
return mapper.readTree(eval);
|
||||||
|
} catch (ScriptException | IllegalArgumentException th) {
|
||||||
|
throw th;
|
||||||
|
} catch (Throwable th) {
|
||||||
|
th.printStackTrace();
|
||||||
|
throw new RuntimeException("Failed to execute js script", th);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
engine = null;
|
//engine = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -64,7 +64,7 @@ public class TbMsgTelemetryNode implements TbNode {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String src = new String(msg.getData(), StandardCharsets.UTF_8);
|
String src = msg.getData();
|
||||||
TelemetryUploadRequest telemetryUploadRequest = JsonConverter.convertToTelemetry(new JsonParser().parse(src));
|
TelemetryUploadRequest telemetryUploadRequest = JsonConverter.convertToTelemetry(new JsonParser().parse(src));
|
||||||
Map<Long, List<KvEntry>> tsKvMap = telemetryUploadRequest.getData();
|
Map<Long, List<KvEntry>> tsKvMap = telemetryUploadRequest.getData();
|
||||||
if (tsKvMap == null) {
|
if (tsKvMap == null) {
|
||||||
|
|||||||
@ -28,10 +28,14 @@ import javax.script.Bindings;
|
|||||||
type = ComponentType.TRANSFORMATION,
|
type = ComponentType.TRANSFORMATION,
|
||||||
name = "script",
|
name = "script",
|
||||||
configClazz = TbTransformMsgNodeConfiguration.class,
|
configClazz = TbTransformMsgNodeConfiguration.class,
|
||||||
nodeDescription = "Change Message payload and Metadata using JavaScript",
|
nodeDescription = "Change Message payload, Metadata or Message type using JavaScript",
|
||||||
nodeDetails = "JavaScript function recieve 2 input parameters that can be changed inside.<br/> " +
|
nodeDetails = "JavaScript function receive 3 input parameters.<br/> " +
|
||||||
"<code>metadata</code> - is a Message metadata.<br/>" +
|
"<code>metadata</code> - is a Message metadata.<br/>" +
|
||||||
"<code>msg</code> - is a Message payload.<br/>Any properties can be changed/removed/added in those objects.",
|
"<code>msg</code> - is a Message payload.<br/>" +
|
||||||
|
"<code>msgType</code> - is a Message type.<br/>" +
|
||||||
|
"Should return the following structure:<br/>" +
|
||||||
|
"<code>{ msg: <new payload>, metadata: <new metadata>, msgType: <new msgType> }</code>" +
|
||||||
|
"All fields in resulting object are optional and will be taken from original message if not specified.",
|
||||||
uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"},
|
uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"},
|
||||||
configDirective = "tbTransformationNodeScriptConfig")
|
configDirective = "tbTransformationNodeScriptConfig")
|
||||||
public class TbTransformMsgNode extends TbAbstractTransformNode {
|
public class TbTransformMsgNode extends TbAbstractTransformNode {
|
||||||
@ -48,11 +52,7 @@ public class TbTransformMsgNode extends TbAbstractTransformNode {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ListenableFuture<TbMsg> transform(TbContext ctx, TbMsg msg) {
|
protected ListenableFuture<TbMsg> transform(TbContext ctx, TbMsg msg) {
|
||||||
return ctx.getJsExecutor().executeAsync(() -> jsEngine.executeUpdate(toBindings(msg), msg));
|
return ctx.getJsExecutor().executeAsync(() -> jsEngine.executeUpdate(msg));
|
||||||
}
|
|
||||||
|
|
||||||
private Bindings toBindings(TbMsg msg) {
|
|
||||||
return NashornJsEngine.bindMsg(msg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -27,7 +27,7 @@ public class TbTransformMsgNodeConfiguration extends TbTransformNodeConfiguratio
|
|||||||
public TbTransformMsgNodeConfiguration defaultConfiguration() {
|
public TbTransformMsgNodeConfiguration defaultConfiguration() {
|
||||||
TbTransformMsgNodeConfiguration configuration = new TbTransformMsgNodeConfiguration();
|
TbTransformMsgNodeConfiguration configuration = new TbTransformMsgNodeConfiguration();
|
||||||
configuration.setStartNewChain(false);
|
configuration.setStartNewChain(false);
|
||||||
configuration.setJsScript("return msg.passed = msg.passed * metadata.temp; msg.bigObj.newProp = 'Ukraine' ");
|
configuration.setJsScript("return {msg: msg, metadata: metadata, msgType: msgType};");
|
||||||
return configuration;
|
return configuration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@ -52,7 +52,7 @@ public class TbJsFilterNodeTest {
|
|||||||
@Test
|
@Test
|
||||||
public void falseEvaluationDoNotSendMsg() throws TbNodeException {
|
public void falseEvaluationDoNotSendMsg() throws TbNodeException {
|
||||||
initWithScript("return 10 > 15;");
|
initWithScript("return 10 > 15;");
|
||||||
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, new TbMsgMetaData(), "{}".getBytes());
|
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, new TbMsgMetaData(), "{}");
|
||||||
|
|
||||||
mockJsExecutor();
|
mockJsExecutor();
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ public class TbJsFilterNodeTest {
|
|||||||
@Test
|
@Test
|
||||||
public void notValidMsgDataThrowsException() throws TbNodeException {
|
public void notValidMsgDataThrowsException() throws TbNodeException {
|
||||||
initWithScript("return 10 > 15;");
|
initWithScript("return 10 > 15;");
|
||||||
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, new TbMsgMetaData(), new byte[4]);
|
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, null, "{}");
|
||||||
|
|
||||||
when(ctx.getJsExecutor()).thenReturn(executor);
|
when(ctx.getJsExecutor()).thenReturn(executor);
|
||||||
|
|
||||||
@ -79,11 +79,11 @@ public class TbJsFilterNodeTest {
|
|||||||
public void exceptionInJsThrowsException() throws TbNodeException {
|
public void exceptionInJsThrowsException() throws TbNodeException {
|
||||||
initWithScript("return metadata.temp.curr < 15;");
|
initWithScript("return metadata.temp.curr < 15;");
|
||||||
TbMsgMetaData metaData = new TbMsgMetaData();
|
TbMsgMetaData metaData = new TbMsgMetaData();
|
||||||
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, "{}".getBytes());
|
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, "{}");
|
||||||
mockJsExecutor();
|
mockJsExecutor();
|
||||||
|
|
||||||
node.onMsg(ctx, msg);
|
node.onMsg(ctx, msg);
|
||||||
String expectedMessage = "TypeError: Cannot get property \"curr\" of null in <eval> at line number 1";
|
String expectedMessage = "TypeError: Cannot read property \"curr\" from undefined in <eval> at line number 1";
|
||||||
verifyError(msg, expectedMessage, ScriptException.class);
|
verifyError(msg, expectedMessage, ScriptException.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,7 +98,7 @@ public class TbJsFilterNodeTest {
|
|||||||
TbMsgMetaData metaData = new TbMsgMetaData();
|
TbMsgMetaData metaData = new TbMsgMetaData();
|
||||||
metaData.putValue("temp", "10");
|
metaData.putValue("temp", "10");
|
||||||
metaData.putValue("humidity", "99");
|
metaData.putValue("humidity", "99");
|
||||||
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, "{}".getBytes());
|
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, "{}");
|
||||||
mockJsExecutor();
|
mockJsExecutor();
|
||||||
|
|
||||||
node.onMsg(ctx, msg);
|
node.onMsg(ctx, msg);
|
||||||
@ -113,7 +113,7 @@ public class TbJsFilterNodeTest {
|
|||||||
TbMsgMetaData metaData = new TbMsgMetaData();
|
TbMsgMetaData metaData = new TbMsgMetaData();
|
||||||
metaData.putValue("temp", "10");
|
metaData.putValue("temp", "10");
|
||||||
metaData.putValue("humidity", "99");
|
metaData.putValue("humidity", "99");
|
||||||
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, "{}".getBytes());
|
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, "{}");
|
||||||
mockJsExecutor();
|
mockJsExecutor();
|
||||||
|
|
||||||
node.onMsg(ctx, msg);
|
node.onMsg(ctx, msg);
|
||||||
@ -129,7 +129,7 @@ public class TbJsFilterNodeTest {
|
|||||||
metaData.putValue("humidity", "99");
|
metaData.putValue("humidity", "99");
|
||||||
String rawJson = "{\"name\": \"Vit\", \"passed\": 5, \"bigObj\": {\"prop\":42}}";
|
String rawJson = "{\"name\": \"Vit\", \"passed\": 5, \"bigObj\": {\"prop\":42}}";
|
||||||
|
|
||||||
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson.getBytes());
|
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson);
|
||||||
mockJsExecutor();
|
mockJsExecutor();
|
||||||
|
|
||||||
node.onMsg(ctx, msg);
|
node.onMsg(ctx, msg);
|
||||||
|
|||||||
@ -68,7 +68,7 @@ public class TbJsSwitchNodeTest {
|
|||||||
metaData.putValue("humidity", "99");
|
metaData.putValue("humidity", "99");
|
||||||
String rawJson = "{\"name\": \"Vit\", \"passed\": 5}";
|
String rawJson = "{\"name\": \"Vit\", \"passed\": 5}";
|
||||||
|
|
||||||
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson.getBytes());
|
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson);
|
||||||
mockJsExecutor();
|
mockJsExecutor();
|
||||||
|
|
||||||
node.onMsg(ctx, msg);
|
node.onMsg(ctx, msg);
|
||||||
@ -92,7 +92,7 @@ public class TbJsSwitchNodeTest {
|
|||||||
metaData.putValue("humidity", "99");
|
metaData.putValue("humidity", "99");
|
||||||
String rawJson = "{\"name\": \"Vit\", \"passed\": 5}";
|
String rawJson = "{\"name\": \"Vit\", \"passed\": 5}";
|
||||||
|
|
||||||
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson.getBytes());
|
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson);
|
||||||
mockJsExecutor();
|
mockJsExecutor();
|
||||||
|
|
||||||
node.onMsg(ctx, msg);
|
node.onMsg(ctx, msg);
|
||||||
|
|||||||
@ -98,7 +98,7 @@ public class TbGetCustomerAttributeNodeTest {
|
|||||||
User user = new User();
|
User user = new User();
|
||||||
user.setCustomerId(customerId);
|
user.setCustomerId(customerId);
|
||||||
|
|
||||||
msg = new TbMsg(UUIDs.timeBased(), "USER", userId, new TbMsgMetaData(), new byte[4]);
|
msg = new TbMsg(UUIDs.timeBased(), "USER", userId, new TbMsgMetaData(), "{}");
|
||||||
|
|
||||||
when(ctx.getUserService()).thenReturn(userService);
|
when(ctx.getUserService()).thenReturn(userService);
|
||||||
when(userService.findUserByIdAsync(userId)).thenReturn(Futures.immediateFuture(user));
|
when(userService.findUserByIdAsync(userId)).thenReturn(Futures.immediateFuture(user));
|
||||||
@ -123,7 +123,7 @@ public class TbGetCustomerAttributeNodeTest {
|
|||||||
User user = new User();
|
User user = new User();
|
||||||
user.setCustomerId(customerId);
|
user.setCustomerId(customerId);
|
||||||
|
|
||||||
msg = new TbMsg(UUIDs.timeBased(), "USER", userId, new TbMsgMetaData(), new byte[4]);
|
msg = new TbMsg(UUIDs.timeBased(), "USER", userId, new TbMsgMetaData(), "{}");
|
||||||
|
|
||||||
when(ctx.getUserService()).thenReturn(userService);
|
when(ctx.getUserService()).thenReturn(userService);
|
||||||
when(userService.findUserByIdAsync(userId)).thenReturn(Futures.immediateFuture(user));
|
when(userService.findUserByIdAsync(userId)).thenReturn(Futures.immediateFuture(user));
|
||||||
@ -148,7 +148,7 @@ public class TbGetCustomerAttributeNodeTest {
|
|||||||
User user = new User();
|
User user = new User();
|
||||||
user.setCustomerId(customerId);
|
user.setCustomerId(customerId);
|
||||||
|
|
||||||
msg = new TbMsg(UUIDs.timeBased(), "USER", userId, new TbMsgMetaData(), new byte[4]);
|
msg = new TbMsg(UUIDs.timeBased(), "USER", userId, new TbMsgMetaData(), "{}");
|
||||||
|
|
||||||
when(ctx.getUserService()).thenReturn(userService);
|
when(ctx.getUserService()).thenReturn(userService);
|
||||||
when(userService.findUserByIdAsync(userId)).thenReturn(Futures.immediateFuture(null));
|
when(userService.findUserByIdAsync(userId)).thenReturn(Futures.immediateFuture(null));
|
||||||
@ -166,7 +166,7 @@ public class TbGetCustomerAttributeNodeTest {
|
|||||||
@Test
|
@Test
|
||||||
public void customerAttributeAddedInMetadata() {
|
public void customerAttributeAddedInMetadata() {
|
||||||
CustomerId customerId = new CustomerId(UUIDs.timeBased());
|
CustomerId customerId = new CustomerId(UUIDs.timeBased());
|
||||||
msg = new TbMsg(UUIDs.timeBased(), "CUSTOMER", customerId, new TbMsgMetaData(), new byte[4]);
|
msg = new TbMsg(UUIDs.timeBased(), "CUSTOMER", customerId, new TbMsgMetaData(), "{}");
|
||||||
entityAttributeFetched(customerId);
|
entityAttributeFetched(customerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,7 +177,7 @@ public class TbGetCustomerAttributeNodeTest {
|
|||||||
User user = new User();
|
User user = new User();
|
||||||
user.setCustomerId(customerId);
|
user.setCustomerId(customerId);
|
||||||
|
|
||||||
msg = new TbMsg(UUIDs.timeBased(), "USER", userId, new TbMsgMetaData(), new byte[4]);
|
msg = new TbMsg(UUIDs.timeBased(), "USER", userId, new TbMsgMetaData(), "{}");
|
||||||
|
|
||||||
when(ctx.getUserService()).thenReturn(userService);
|
when(ctx.getUserService()).thenReturn(userService);
|
||||||
when(userService.findUserByIdAsync(userId)).thenReturn(Futures.immediateFuture(user));
|
when(userService.findUserByIdAsync(userId)).thenReturn(Futures.immediateFuture(user));
|
||||||
@ -192,7 +192,7 @@ public class TbGetCustomerAttributeNodeTest {
|
|||||||
Asset asset = new Asset();
|
Asset asset = new Asset();
|
||||||
asset.setCustomerId(customerId);
|
asset.setCustomerId(customerId);
|
||||||
|
|
||||||
msg = new TbMsg(UUIDs.timeBased(), "USER", assetId, new TbMsgMetaData(), new byte[4]);
|
msg = new TbMsg(UUIDs.timeBased(), "USER", assetId, new TbMsgMetaData(), "{}");
|
||||||
|
|
||||||
when(ctx.getAssetService()).thenReturn(assetService);
|
when(ctx.getAssetService()).thenReturn(assetService);
|
||||||
when(assetService.findAssetByIdAsync(assetId)).thenReturn(Futures.immediateFuture(asset));
|
when(assetService.findAssetByIdAsync(assetId)).thenReturn(Futures.immediateFuture(asset));
|
||||||
@ -207,7 +207,7 @@ public class TbGetCustomerAttributeNodeTest {
|
|||||||
Device device = new Device();
|
Device device = new Device();
|
||||||
device.setCustomerId(customerId);
|
device.setCustomerId(customerId);
|
||||||
|
|
||||||
msg = new TbMsg(UUIDs.timeBased(), "USER", deviceId, new TbMsgMetaData(), new byte[4]);
|
msg = new TbMsg(UUIDs.timeBased(), "USER", deviceId, new TbMsgMetaData(), "{}");
|
||||||
|
|
||||||
when(ctx.getDeviceService()).thenReturn(deviceService);
|
when(ctx.getDeviceService()).thenReturn(deviceService);
|
||||||
when(deviceService.findDeviceByIdAsync(deviceId)).thenReturn(Futures.immediateFuture(device));
|
when(deviceService.findDeviceByIdAsync(deviceId)).thenReturn(Futures.immediateFuture(device));
|
||||||
@ -234,7 +234,7 @@ public class TbGetCustomerAttributeNodeTest {
|
|||||||
Device device = new Device();
|
Device device = new Device();
|
||||||
device.setCustomerId(customerId);
|
device.setCustomerId(customerId);
|
||||||
|
|
||||||
msg = new TbMsg(UUIDs.timeBased(), "USER", deviceId, new TbMsgMetaData(), new byte[4]);
|
msg = new TbMsg(UUIDs.timeBased(), "USER", deviceId, new TbMsgMetaData(), "{}");
|
||||||
|
|
||||||
when(ctx.getDeviceService()).thenReturn(deviceService);
|
when(ctx.getDeviceService()).thenReturn(deviceService);
|
||||||
when(deviceService.findDeviceByIdAsync(deviceId)).thenReturn(Futures.immediateFuture(device));
|
when(deviceService.findDeviceByIdAsync(deviceId)).thenReturn(Futures.immediateFuture(device));
|
||||||
|
|||||||
@ -57,7 +57,7 @@ public class TbChangeOriginatorNodeTest {
|
|||||||
Asset asset = new Asset();
|
Asset asset = new Asset();
|
||||||
asset.setCustomerId(customerId);
|
asset.setCustomerId(customerId);
|
||||||
|
|
||||||
TbMsg msg = new TbMsg(UUIDs.timeBased(), "ASSET", assetId, new TbMsgMetaData(), new byte[4]);
|
TbMsg msg = new TbMsg(UUIDs.timeBased(), "ASSET", assetId, new TbMsgMetaData(), "{}");
|
||||||
|
|
||||||
when(ctx.getAssetService()).thenReturn(assetService);
|
when(ctx.getAssetService()).thenReturn(assetService);
|
||||||
when(assetService.findAssetByIdAsync(assetId)).thenReturn(Futures.immediateFuture(asset));
|
when(assetService.findAssetByIdAsync(assetId)).thenReturn(Futures.immediateFuture(asset));
|
||||||
@ -78,7 +78,7 @@ public class TbChangeOriginatorNodeTest {
|
|||||||
Asset asset = new Asset();
|
Asset asset = new Asset();
|
||||||
asset.setCustomerId(customerId);
|
asset.setCustomerId(customerId);
|
||||||
|
|
||||||
TbMsg msg = new TbMsg(UUIDs.timeBased(), "ASSET", assetId, new TbMsgMetaData(), new byte[4]);
|
TbMsg msg = new TbMsg(UUIDs.timeBased(), "ASSET", assetId, new TbMsgMetaData(), "{}");
|
||||||
|
|
||||||
when(ctx.getAssetService()).thenReturn(assetService);
|
when(ctx.getAssetService()).thenReturn(assetService);
|
||||||
when(assetService.findAssetByIdAsync(assetId)).thenReturn(Futures.immediateFuture(asset));
|
when(assetService.findAssetByIdAsync(assetId)).thenReturn(Futures.immediateFuture(asset));
|
||||||
@ -99,7 +99,7 @@ public class TbChangeOriginatorNodeTest {
|
|||||||
Asset asset = new Asset();
|
Asset asset = new Asset();
|
||||||
asset.setCustomerId(customerId);
|
asset.setCustomerId(customerId);
|
||||||
|
|
||||||
TbMsg msg = new TbMsg(UUIDs.timeBased(), "ASSET", assetId, new TbMsgMetaData(), new byte[4]);
|
TbMsg msg = new TbMsg(UUIDs.timeBased(), "ASSET", assetId, new TbMsgMetaData(), "{}");
|
||||||
|
|
||||||
when(ctx.getAssetService()).thenReturn(assetService);
|
when(ctx.getAssetService()).thenReturn(assetService);
|
||||||
when(assetService.findAssetByIdAsync(assetId)).thenReturn(Futures.immediateFailedFuture(new IllegalStateException("wrong")));
|
when(assetService.findAssetByIdAsync(assetId)).thenReturn(Futures.immediateFailedFuture(new IllegalStateException("wrong")));
|
||||||
|
|||||||
@ -51,13 +51,13 @@ public class TbTransformMsgNodeTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void metadataCanBeUpdated() throws TbNodeException {
|
public void metadataCanBeUpdated() throws TbNodeException {
|
||||||
initWithScript("return metadata.temp = metadata.temp * 10;");
|
initWithScript("metadata.temp = metadata.temp * 10; return {metadata: metadata};");
|
||||||
TbMsgMetaData metaData = new TbMsgMetaData();
|
TbMsgMetaData metaData = new TbMsgMetaData();
|
||||||
metaData.putValue("temp", "7");
|
metaData.putValue("temp", "7");
|
||||||
metaData.putValue("humidity", "99");
|
metaData.putValue("humidity", "99");
|
||||||
String rawJson = "{\"name\": \"Vit\", \"passed\": 5, \"bigObj\": {\"prop\":42}}";
|
String rawJson = "{\"name\": \"Vit\", \"passed\": 5, \"bigObj\": {\"prop\":42}}";
|
||||||
|
|
||||||
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson.getBytes());
|
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson);
|
||||||
mockJsExecutor();
|
mockJsExecutor();
|
||||||
|
|
||||||
node.onMsg(ctx, msg);
|
node.onMsg(ctx, msg);
|
||||||
@ -65,18 +65,18 @@ public class TbTransformMsgNodeTest {
|
|||||||
ArgumentCaptor<TbMsg> captor = ArgumentCaptor.forClass(TbMsg.class);
|
ArgumentCaptor<TbMsg> captor = ArgumentCaptor.forClass(TbMsg.class);
|
||||||
verify(ctx).tellNext(captor.capture());
|
verify(ctx).tellNext(captor.capture());
|
||||||
TbMsg actualMsg = captor.getValue();
|
TbMsg actualMsg = captor.getValue();
|
||||||
assertEquals("70.0", actualMsg.getMetaData().getValue("temp"));
|
assertEquals("70", actualMsg.getMetaData().getValue("temp"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void metadataCanBeAdded() throws TbNodeException {
|
public void metadataCanBeAdded() throws TbNodeException {
|
||||||
initWithScript("return metadata.newAttr = metadata.humidity - msg.passed;");
|
initWithScript("metadata.newAttr = metadata.humidity - msg.passed; return {metadata: metadata};");
|
||||||
TbMsgMetaData metaData = new TbMsgMetaData();
|
TbMsgMetaData metaData = new TbMsgMetaData();
|
||||||
metaData.putValue("temp", "7");
|
metaData.putValue("temp", "7");
|
||||||
metaData.putValue("humidity", "99");
|
metaData.putValue("humidity", "99");
|
||||||
String rawJson = "{\"name\": \"Vit\", \"passed\": 5, \"bigObj\": {\"prop\":42}}";
|
String rawJson = "{\"name\": \"Vit\", \"passed\": 5, \"bigObj\": {\"prop\":42}}";
|
||||||
|
|
||||||
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson.getBytes());
|
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson);
|
||||||
mockJsExecutor();
|
mockJsExecutor();
|
||||||
|
|
||||||
node.onMsg(ctx, msg);
|
node.onMsg(ctx, msg);
|
||||||
@ -84,18 +84,18 @@ public class TbTransformMsgNodeTest {
|
|||||||
ArgumentCaptor<TbMsg> captor = ArgumentCaptor.forClass(TbMsg.class);
|
ArgumentCaptor<TbMsg> captor = ArgumentCaptor.forClass(TbMsg.class);
|
||||||
verify(ctx).tellNext(captor.capture());
|
verify(ctx).tellNext(captor.capture());
|
||||||
TbMsg actualMsg = captor.getValue();
|
TbMsg actualMsg = captor.getValue();
|
||||||
assertEquals("94.0", actualMsg.getMetaData().getValue("newAttr"));
|
assertEquals("94", actualMsg.getMetaData().getValue("newAttr"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void payloadCanBeUpdated() throws TbNodeException {
|
public void payloadCanBeUpdated() throws TbNodeException {
|
||||||
initWithScript("msg.passed = msg.passed * metadata.temp; return msg.bigObj.newProp = 'Ukraine' ");
|
initWithScript("msg.passed = msg.passed * metadata.temp; msg.bigObj.newProp = 'Ukraine'; return {msg: msg};");
|
||||||
TbMsgMetaData metaData = new TbMsgMetaData();
|
TbMsgMetaData metaData = new TbMsgMetaData();
|
||||||
metaData.putValue("temp", "7");
|
metaData.putValue("temp", "7");
|
||||||
metaData.putValue("humidity", "99");
|
metaData.putValue("humidity", "99");
|
||||||
String rawJson = "{\"name\":\"Vit\",\"passed\": 5,\"bigObj\":{\"prop\":42}}";
|
String rawJson = "{\"name\":\"Vit\",\"passed\": 5,\"bigObj\":{\"prop\":42}}";
|
||||||
|
|
||||||
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson.getBytes());
|
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson);
|
||||||
mockJsExecutor();
|
mockJsExecutor();
|
||||||
|
|
||||||
node.onMsg(ctx, msg);
|
node.onMsg(ctx, msg);
|
||||||
@ -103,7 +103,7 @@ public class TbTransformMsgNodeTest {
|
|||||||
ArgumentCaptor<TbMsg> captor = ArgumentCaptor.forClass(TbMsg.class);
|
ArgumentCaptor<TbMsg> captor = ArgumentCaptor.forClass(TbMsg.class);
|
||||||
verify(ctx).tellNext(captor.capture());
|
verify(ctx).tellNext(captor.capture());
|
||||||
TbMsg actualMsg = captor.getValue();
|
TbMsg actualMsg = captor.getValue();
|
||||||
String expectedJson = "{\"name\":\"Vit\",\"passed\":35.0,\"bigObj\":{\"prop\":42,\"newProp\":\"Ukraine\"}}";
|
String expectedJson = "{\"name\":\"Vit\",\"passed\":35,\"bigObj\":{\"prop\":42,\"newProp\":\"Ukraine\"}}";
|
||||||
assertEquals(expectedJson, new String(actualMsg.getData()));
|
assertEquals(expectedJson, new String(actualMsg.getData()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user