Merge remote-tracking branch 'ce/master' into jwt-random
This commit is contained in:
		
						commit
						4fc13b7938
					
				@ -823,6 +823,9 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
 | 
			
		||||
        body.put("expirationTime", msg.getExpirationTime());
 | 
			
		||||
        body.put("method", msg.getBody().getMethod());
 | 
			
		||||
        body.put("params", msg.getBody().getParams());
 | 
			
		||||
        body.put("persisted", msg.isPersisted());
 | 
			
		||||
        body.put("retries", msg.getRetries());
 | 
			
		||||
        body.put("additionalInfo", msg.getAdditionalInfo());
 | 
			
		||||
 | 
			
		||||
        EdgeEvent edgeEvent = EdgeUtils.constructEdgeEvent(tenantId, edgeId, EdgeEventType.DEVICE, EdgeEventActionType.RPC_CALL, deviceId, body);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -611,7 +611,7 @@ public final class EdgeGrpcSession implements Closeable {
 | 
			
		||||
            }
 | 
			
		||||
            if (uplinkMsg.getDeviceRpcCallMsgCount() > 0) {
 | 
			
		||||
                for (DeviceRpcCallMsg deviceRpcCallMsg : uplinkMsg.getDeviceRpcCallMsgList()) {
 | 
			
		||||
                    result.add(ctx.getDeviceProcessor().processDeviceRpcCallResponseFromEdge(edge.getTenantId(), deviceRpcCallMsg));
 | 
			
		||||
                    result.add(ctx.getDeviceProcessor().processDeviceRpcCallFromEdge(edge.getTenantId(), edge, deviceRpcCallMsg));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (uplinkMsg.getWidgetBundleTypesRequestMsgCount() > 0) {
 | 
			
		||||
 | 
			
		||||
@ -21,13 +21,13 @@ import org.springframework.beans.factory.annotation.Autowired;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
import org.thingsboard.common.util.JacksonUtil;
 | 
			
		||||
import org.thingsboard.server.common.data.Device;
 | 
			
		||||
import org.thingsboard.server.common.data.id.CustomerId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.DeviceId;
 | 
			
		||||
import org.thingsboard.server.common.data.security.DeviceCredentials;
 | 
			
		||||
import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg;
 | 
			
		||||
import org.thingsboard.server.gen.edge.v1.DeviceRpcCallMsg;
 | 
			
		||||
import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg;
 | 
			
		||||
import org.thingsboard.server.gen.edge.v1.RpcRequestMsg;
 | 
			
		||||
import org.thingsboard.server.gen.edge.v1.RpcResponseMsg;
 | 
			
		||||
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
 | 
			
		||||
import org.thingsboard.server.queue.util.DataDecodingEncodingService;
 | 
			
		||||
import org.thingsboard.server.queue.util.TbCoreComponent;
 | 
			
		||||
@ -97,25 +97,55 @@ public class DeviceMsgConstructor {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public DeviceRpcCallMsg constructDeviceRpcCallMsg(UUID deviceId, JsonNode body) {
 | 
			
		||||
        int requestId = body.get("requestId").asInt();
 | 
			
		||||
        boolean oneway = body.get("oneway").asBoolean();
 | 
			
		||||
        UUID requestUUID = UUID.fromString(body.get("requestUUID").asText());
 | 
			
		||||
        long expirationTime = body.get("expirationTime").asLong();
 | 
			
		||||
        String method = body.get("method").asText();
 | 
			
		||||
        String params = body.get("params").asText();
 | 
			
		||||
        DeviceRpcCallMsg.Builder builder = constructDeviceRpcMsg(deviceId, body);
 | 
			
		||||
        if (body.has("error") || body.has("response")) {
 | 
			
		||||
            RpcResponseMsg.Builder responseBuilder = RpcResponseMsg.newBuilder();
 | 
			
		||||
            if (body.has("error")) {
 | 
			
		||||
                responseBuilder.setError(body.get("error").asText());
 | 
			
		||||
            } else {
 | 
			
		||||
                responseBuilder.setResponse(body.get("response").asText());
 | 
			
		||||
            }
 | 
			
		||||
            builder.setResponseMsg(responseBuilder.build());
 | 
			
		||||
        } else {
 | 
			
		||||
            RpcRequestMsg.Builder requestBuilder = RpcRequestMsg.newBuilder();
 | 
			
		||||
            requestBuilder.setMethod(body.get("method").asText());
 | 
			
		||||
            requestBuilder.setParams(body.get("params").asText());
 | 
			
		||||
            builder.setRequestMsg(requestBuilder.build());
 | 
			
		||||
        }
 | 
			
		||||
        return builder.build();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        RpcRequestMsg.Builder requestBuilder = RpcRequestMsg.newBuilder();
 | 
			
		||||
        requestBuilder.setMethod(method);
 | 
			
		||||
        requestBuilder.setParams(params);
 | 
			
		||||
    private DeviceRpcCallMsg.Builder constructDeviceRpcMsg(UUID deviceId, JsonNode body) {
 | 
			
		||||
        DeviceRpcCallMsg.Builder builder = DeviceRpcCallMsg.newBuilder()
 | 
			
		||||
                .setDeviceIdMSB(deviceId.getMostSignificantBits())
 | 
			
		||||
                .setDeviceIdLSB(deviceId.getLeastSignificantBits())
 | 
			
		||||
                .setRequestUuidMSB(requestUUID.getMostSignificantBits())
 | 
			
		||||
                .setRequestUuidLSB(requestUUID.getLeastSignificantBits())
 | 
			
		||||
                .setRequestId(requestId)
 | 
			
		||||
                .setExpirationTime(expirationTime)
 | 
			
		||||
                .setOneway(oneway)
 | 
			
		||||
                .setRequestMsg(requestBuilder.build());
 | 
			
		||||
        return builder.build();
 | 
			
		||||
                .setRequestId(body.get("requestId").asInt());
 | 
			
		||||
        if (body.get("oneway") != null) {
 | 
			
		||||
            builder.setOneway(body.get("oneway").asBoolean());
 | 
			
		||||
        }
 | 
			
		||||
        if (body.get("requestUUID") != null) {
 | 
			
		||||
            UUID requestUUID = UUID.fromString(body.get("requestUUID").asText());
 | 
			
		||||
            builder.setRequestUuidMSB(requestUUID.getMostSignificantBits())
 | 
			
		||||
                    .setRequestUuidLSB(requestUUID.getLeastSignificantBits());
 | 
			
		||||
        }
 | 
			
		||||
        if (body.get("expirationTime") != null) {
 | 
			
		||||
            builder.setExpirationTime(body.get("expirationTime").asLong());
 | 
			
		||||
        }
 | 
			
		||||
        if (body.get("persisted") != null) {
 | 
			
		||||
            builder.setPersisted(body.get("persisted").asBoolean());
 | 
			
		||||
        }
 | 
			
		||||
        if (body.get("retries") != null) {
 | 
			
		||||
            builder.setRetries(body.get("retries").asInt());
 | 
			
		||||
        }
 | 
			
		||||
        if (body.get("additionalInfo") != null) {
 | 
			
		||||
            builder.setAdditionalInfo(JacksonUtil.toString(body.get("additionalInfo")));
 | 
			
		||||
        }
 | 
			
		||||
        if (body.get("serviceId") != null) {
 | 
			
		||||
            builder.setServiceId(body.get("serviceId").asText());
 | 
			
		||||
        }
 | 
			
		||||
        if (body.get("sessionId") != null) {
 | 
			
		||||
            builder.setSessionId(body.get("sessionId").asText());
 | 
			
		||||
        }
 | 
			
		||||
        return builder;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -52,6 +52,7 @@ import org.thingsboard.server.common.msg.TbMsg;
 | 
			
		||||
import org.thingsboard.server.common.msg.TbMsgDataType;
 | 
			
		||||
import org.thingsboard.server.common.msg.TbMsgMetaData;
 | 
			
		||||
import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse;
 | 
			
		||||
import org.thingsboard.server.common.msg.session.SessionMsgType;
 | 
			
		||||
import org.thingsboard.server.dao.exception.DataValidationException;
 | 
			
		||||
import org.thingsboard.server.gen.edge.v1.DeviceCredentialsRequestMsg;
 | 
			
		||||
import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg;
 | 
			
		||||
@ -325,8 +326,17 @@ public class DeviceEdgeProcessor extends BaseEdgeProcessor {
 | 
			
		||||
        return metaData;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ListenableFuture<Void> processDeviceRpcCallResponseFromEdge(TenantId tenantId, DeviceRpcCallMsg deviceRpcCallMsg) {
 | 
			
		||||
        log.trace("[{}] processDeviceRpcCallResponseMsg [{}]", tenantId, deviceRpcCallMsg);
 | 
			
		||||
    public ListenableFuture<Void> processDeviceRpcCallFromEdge(TenantId tenantId, Edge edge, DeviceRpcCallMsg deviceRpcCallMsg) {
 | 
			
		||||
        log.trace("[{}] processDeviceRpcCallFromEdge [{}]", tenantId, deviceRpcCallMsg);
 | 
			
		||||
        if (deviceRpcCallMsg.hasResponseMsg()) {
 | 
			
		||||
            return processDeviceRpcResponseFromEdge(tenantId, deviceRpcCallMsg);
 | 
			
		||||
        } else if (deviceRpcCallMsg.hasRequestMsg()) {
 | 
			
		||||
            return processDeviceRpcRequestFromEdge(tenantId, edge, deviceRpcCallMsg);
 | 
			
		||||
        }
 | 
			
		||||
        return Futures.immediateFuture(null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private ListenableFuture<Void> processDeviceRpcResponseFromEdge(TenantId tenantId, DeviceRpcCallMsg deviceRpcCallMsg) {
 | 
			
		||||
        SettableFuture<Void> futureToSet = SettableFuture.create();
 | 
			
		||||
        UUID requestUuid = new UUID(deviceRpcCallMsg.getRequestUuidMSB(), deviceRpcCallMsg.getRequestUuidLSB());
 | 
			
		||||
        DeviceId deviceId = new DeviceId(new UUID(deviceRpcCallMsg.getDeviceIdMSB(), deviceRpcCallMsg.getDeviceIdLSB()));
 | 
			
		||||
@ -357,6 +367,46 @@ public class DeviceEdgeProcessor extends BaseEdgeProcessor {
 | 
			
		||||
        return futureToSet;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private ListenableFuture<Void> processDeviceRpcRequestFromEdge(TenantId tenantId, Edge edge, DeviceRpcCallMsg deviceRpcCallMsg) {
 | 
			
		||||
        DeviceId deviceId = new DeviceId(new UUID(deviceRpcCallMsg.getDeviceIdMSB(), deviceRpcCallMsg.getDeviceIdLSB()));
 | 
			
		||||
        try {
 | 
			
		||||
            TbMsgMetaData metaData = new TbMsgMetaData();
 | 
			
		||||
            String requestId = Integer.toString(deviceRpcCallMsg.getRequestId());
 | 
			
		||||
            metaData.putValue("requestId", requestId);
 | 
			
		||||
            metaData.putValue("serviceId", deviceRpcCallMsg.getServiceId());
 | 
			
		||||
            metaData.putValue("sessionId", deviceRpcCallMsg.getSessionId());
 | 
			
		||||
            metaData.putValue(DataConstants.EDGE_ID, edge.getId().toString());
 | 
			
		||||
            Device device = deviceService.findDeviceById(tenantId, deviceId);
 | 
			
		||||
            if (device != null) {
 | 
			
		||||
                metaData.putValue("deviceName", device.getName());
 | 
			
		||||
                metaData.putValue("deviceType", device.getType());
 | 
			
		||||
                metaData.putValue(DataConstants.DEVICE_ID, deviceId.getId().toString());
 | 
			
		||||
            }
 | 
			
		||||
            ObjectNode data = JacksonUtil.OBJECT_MAPPER.createObjectNode();
 | 
			
		||||
            data.put("method", deviceRpcCallMsg.getRequestMsg().getMethod());
 | 
			
		||||
            data.put("params", deviceRpcCallMsg.getRequestMsg().getParams());
 | 
			
		||||
            TbMsg tbMsg = TbMsg.newMsg(SessionMsgType.TO_SERVER_RPC_REQUEST.name(), deviceId, null, metaData,
 | 
			
		||||
                    TbMsgDataType.JSON, JacksonUtil.OBJECT_MAPPER.writeValueAsString(data));
 | 
			
		||||
            tbClusterService.pushMsgToRuleEngine(tenantId, deviceId, tbMsg, new TbQueueCallback() {
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onSuccess(TbQueueMsgMetadata metadata) {
 | 
			
		||||
                    log.debug("Successfully send TO_SERVER_RPC_REQUEST to rule engine [{}], deviceRpcCallMsg {}",
 | 
			
		||||
                            device, deviceRpcCallMsg);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onFailure(Throwable t) {
 | 
			
		||||
                    log.debug("Failed to send TO_SERVER_RPC_REQUEST to rule engine [{}], deviceRpcCallMsg {}",
 | 
			
		||||
                            device, deviceRpcCallMsg, t);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        } catch (JsonProcessingException | IllegalArgumentException e) {
 | 
			
		||||
            log.warn("[{}] Failed to push TO_SERVER_RPC_REQUEST to rule engine. deviceRpcCallMsg {}", deviceId, deviceRpcCallMsg, e);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return Futures.immediateFuture(null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public DownlinkMsg convertDeviceEventToDownlink(EdgeEvent edgeEvent) {
 | 
			
		||||
        DeviceId deviceId = new DeviceId(edgeEvent.getEntityId());
 | 
			
		||||
        DownlinkMsg downlinkMsg = null;
 | 
			
		||||
@ -413,11 +463,9 @@ public class DeviceEdgeProcessor extends BaseEdgeProcessor {
 | 
			
		||||
 | 
			
		||||
    private DownlinkMsg convertRpcCallEventToDownlink(EdgeEvent edgeEvent) {
 | 
			
		||||
        log.trace("Executing convertRpcCallEventToDownlink, edgeEvent [{}]", edgeEvent);
 | 
			
		||||
        DeviceRpcCallMsg deviceRpcCallMsg =
 | 
			
		||||
                deviceMsgConstructor.constructDeviceRpcCallMsg(edgeEvent.getEntityId(), edgeEvent.getBody());
 | 
			
		||||
        return DownlinkMsg.newBuilder()
 | 
			
		||||
                .setDownlinkMsgId(EdgeUtils.nextPositiveInt())
 | 
			
		||||
                .addDeviceRpcCallMsg(deviceRpcCallMsg)
 | 
			
		||||
                .addDeviceRpcCallMsg(deviceMsgConstructor.constructDeviceRpcCallMsg(edgeEvent.getEntityId(), edgeEvent.getBody()))
 | 
			
		||||
                .build();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -511,8 +511,11 @@ abstract public class BaseDeviceEdgeTest extends AbstractEdgeTest {
 | 
			
		||||
        body.put("expirationTime", System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(10));
 | 
			
		||||
        body.put("method", "test_method");
 | 
			
		||||
        body.put("params", "{\"param1\":\"value1\"}");
 | 
			
		||||
        body.put("persisted", true);
 | 
			
		||||
        body.put("retries", 2);
 | 
			
		||||
 | 
			
		||||
        EdgeEvent edgeEvent = constructEdgeEvent(tenantId, edge.getId(), EdgeEventActionType.RPC_CALL, device.getId().getId(), EdgeEventType.DEVICE, body);
 | 
			
		||||
        EdgeEvent edgeEvent = constructEdgeEvent(tenantId, edge.getId(), EdgeEventActionType.RPC_CALL,
 | 
			
		||||
                device.getId().getId(), EdgeEventType.DEVICE, body);
 | 
			
		||||
        edgeImitator.expectMessageAmount(1);
 | 
			
		||||
        edgeEventService.saveAsync(edgeEvent).get();
 | 
			
		||||
        clusterService.onEdgeEventUpdate(tenantId, edge.getId());
 | 
			
		||||
@ -522,6 +525,8 @@ abstract public class BaseDeviceEdgeTest extends AbstractEdgeTest {
 | 
			
		||||
        Assert.assertTrue(latestMessage instanceof DeviceRpcCallMsg);
 | 
			
		||||
        DeviceRpcCallMsg latestDeviceRpcCallMsg = (DeviceRpcCallMsg) latestMessage;
 | 
			
		||||
        Assert.assertEquals("test_method", latestDeviceRpcCallMsg.getRequestMsg().getMethod());
 | 
			
		||||
        Assert.assertTrue(latestDeviceRpcCallMsg.getPersisted());
 | 
			
		||||
        Assert.assertEquals(2, latestDeviceRpcCallMsg.getRetries());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void sendAttributesRequestAndVerify(Device device, String scope, String attributesDataStr, String expectedKey,
 | 
			
		||||
 | 
			
		||||
@ -40,6 +40,8 @@ public class DataConstants {
 | 
			
		||||
    public static final String EXPIRATION_TIME = "expirationTime";
 | 
			
		||||
    public static final String ADDITIONAL_INFO = "additionalInfo";
 | 
			
		||||
    public static final String RETRIES = "retries";
 | 
			
		||||
    public static final String EDGE_ID = "edgeId";
 | 
			
		||||
    public static final String DEVICE_ID = "deviceId";
 | 
			
		||||
    public static final String COAP_TRANSPORT_NAME = "COAP";
 | 
			
		||||
    public static final String LWM2M_TRANSPORT_NAME = "LWM2M";
 | 
			
		||||
    public static final String MQTT_TRANSPORT_NAME = "MQTT";
 | 
			
		||||
 | 
			
		||||
@ -430,6 +430,11 @@ message DeviceRpcCallMsg {
 | 
			
		||||
  bool oneway = 7;
 | 
			
		||||
  RpcRequestMsg requestMsg = 8;
 | 
			
		||||
  RpcResponseMsg responseMsg = 9;
 | 
			
		||||
  optional bool persisted = 10;
 | 
			
		||||
  optional int32 retries = 11;
 | 
			
		||||
  optional string additionalInfo = 12;
 | 
			
		||||
  optional string serviceId = 13;
 | 
			
		||||
  optional string sessionId = 14;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message RpcRequestMsg {
 | 
			
		||||
 | 
			
		||||
@ -731,6 +731,8 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
 | 
			
		||||
            ConstraintViolationException e = extractConstraintViolationException(t).orElse(null);
 | 
			
		||||
            if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_default_rule_chain_device_profile")) {
 | 
			
		||||
                throw new DataValidationException("The rule chain referenced by the device profiles cannot be deleted!");
 | 
			
		||||
            } else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_default_rule_chain_asset_profile")) {
 | 
			
		||||
                throw new DataValidationException("The rule chain referenced by the asset profiles cannot be deleted!");
 | 
			
		||||
            } else {
 | 
			
		||||
                throw t;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -3,4 +3,5 @@ LOGGER_LEVEL=info
 | 
			
		||||
LOG_FOLDER=logs
 | 
			
		||||
LOGGER_FILENAME=tb-js-executor-%DATE%.log
 | 
			
		||||
DOCKER_MODE=true
 | 
			
		||||
SCRIPT_BODY_TRACE_FREQUENCY=1000
 | 
			
		||||
SCRIPT_BODY_TRACE_FREQUENCY=1000
 | 
			
		||||
NODE_OPTIONS="--max-old-space-size=200"
 | 
			
		||||
 | 
			
		||||
@ -39,6 +39,7 @@ const TIMEOUT_ERROR = 2;
 | 
			
		||||
const NOT_FOUND_ERROR = 3;
 | 
			
		||||
 | 
			
		||||
const statFrequency = Number(config.get('script.stat_print_frequency'));
 | 
			
		||||
const memoryUsageTraceFrequency = Number(config.get('script.memory_usage_trace_frequency'));
 | 
			
		||||
const scriptBodyTraceFrequency = Number(config.get('script.script_body_trace_frequency'));
 | 
			
		||||
const useSandbox = config.get('script.use_sandbox') === 'true';
 | 
			
		||||
const maxActiveScripts = Number(config.get('script.max_active_scripts'));
 | 
			
		||||
@ -167,6 +168,10 @@ export class JsInvokeMessageProcessor {
 | 
			
		||||
        if (this.executedScriptsCounter % scriptBodyTraceFrequency == 0) {
 | 
			
		||||
            this.logger.info('[%s] Executing script body: [%s]', scriptId, invokeRequest.scriptBody);
 | 
			
		||||
        }
 | 
			
		||||
        if (this.executedScriptsCounter % memoryUsageTraceFrequency == 0) {
 | 
			
		||||
            this.logger.info('Current memory usage: [%s]', process.memoryUsage());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.getOrCompileScript(scriptId, invokeRequest.scriptBody).then(
 | 
			
		||||
            (script) => {
 | 
			
		||||
                this.executor.executeScript(script, invokeRequest.args, invokeRequest.timeout).then(
 | 
			
		||||
 | 
			
		||||
@ -75,6 +75,7 @@ logger:
 | 
			
		||||
 | 
			
		||||
script:
 | 
			
		||||
  use_sandbox: "SCRIPT_USE_SANDBOX"
 | 
			
		||||
  memory_usage_trace_frequency: "MEMORY_USAGE_TRACE_FREQUENCY"
 | 
			
		||||
  stat_print_frequency: "SCRIPT_STAT_PRINT_FREQUENCY"
 | 
			
		||||
  script_body_trace_frequency: "SCRIPT_BODY_TRACE_FREQUENCY"
 | 
			
		||||
  max_active_scripts: "MAX_ACTIVE_SCRIPTS"
 | 
			
		||||
 | 
			
		||||
@ -64,6 +64,7 @@ logger:
 | 
			
		||||
 | 
			
		||||
script:
 | 
			
		||||
  use_sandbox: "true"
 | 
			
		||||
  memory_usage_trace_frequency: "1000"
 | 
			
		||||
  script_body_trace_frequency: "10000"
 | 
			
		||||
  stat_print_frequency: "10000"
 | 
			
		||||
  max_active_scripts: "1000"
 | 
			
		||||
 | 
			
		||||
@ -27,4 +27,4 @@ source "${CONF_FOLDER}/${configfile}"
 | 
			
		||||
cd ${pkg.installFolder}
 | 
			
		||||
 | 
			
		||||
# This will forward this PID 1 to the node.js and forward SIGTERM for graceful shutdown as well
 | 
			
		||||
exec node server.js
 | 
			
		||||
exec node --no-compilation-cache server.js
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										2
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								pom.xml
									
									
									
									
									
								
							@ -77,7 +77,7 @@
 | 
			
		||||
        <zookeeper.version>3.5.5</zookeeper.version>
 | 
			
		||||
        <protobuf.version>3.21.9</protobuf.version>
 | 
			
		||||
        <grpc.version>1.42.1</grpc.version>
 | 
			
		||||
        <mvel.version>2.4.23TB</mvel.version>
 | 
			
		||||
        <mvel.version>2.4.25TB</mvel.version>
 | 
			
		||||
        <lombok.version>1.18.18</lombok.version>
 | 
			
		||||
        <paho.client.version>1.2.4</paho.client.version>
 | 
			
		||||
        <netty.version>4.1.75.Final</netty.version>
 | 
			
		||||
 | 
			
		||||
@ -142,8 +142,11 @@ public abstract class AbstractTbMsgPushNode<T extends BaseTbMsgPushNodeConfigura
 | 
			
		||||
            actionType = EdgeEventActionType.ATTRIBUTES_UPDATED;
 | 
			
		||||
        } else if (SessionMsgType.POST_ATTRIBUTES_REQUEST.name().equals(msgType)) {
 | 
			
		||||
            actionType = EdgeEventActionType.POST_ATTRIBUTES;
 | 
			
		||||
        } else {
 | 
			
		||||
        } else if (DataConstants.ATTRIBUTES_DELETED.equals(msgType)) {
 | 
			
		||||
            actionType = EdgeEventActionType.ATTRIBUTES_DELETED;
 | 
			
		||||
        } else {
 | 
			
		||||
            log.warn("Unsupported msg type [{}]", msgType);
 | 
			
		||||
            throw new IllegalArgumentException("Unsupported msg type: " + msgType);
 | 
			
		||||
        }
 | 
			
		||||
        return actionType;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -15,15 +15,27 @@
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.rule.engine.rpc;
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.databind.node.ObjectNode;
 | 
			
		||||
import com.google.common.util.concurrent.FutureCallback;
 | 
			
		||||
import com.google.common.util.concurrent.Futures;
 | 
			
		||||
import com.google.common.util.concurrent.ListenableFuture;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.thingsboard.server.common.data.StringUtils;
 | 
			
		||||
import org.thingsboard.common.util.JacksonUtil;
 | 
			
		||||
import org.thingsboard.rule.engine.api.RuleNode;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbContext;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbNode;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbNodeConfiguration;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbNodeException;
 | 
			
		||||
import org.thingsboard.rule.engine.api.util.TbNodeUtils;
 | 
			
		||||
import org.thingsboard.server.common.data.DataConstants;
 | 
			
		||||
import org.thingsboard.server.common.data.EdgeUtils;
 | 
			
		||||
import org.thingsboard.server.common.data.EntityType;
 | 
			
		||||
import org.thingsboard.server.common.data.StringUtils;
 | 
			
		||||
import org.thingsboard.server.common.data.edge.EdgeEvent;
 | 
			
		||||
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
 | 
			
		||||
import org.thingsboard.server.common.data.edge.EdgeEventType;
 | 
			
		||||
import org.thingsboard.server.common.data.id.DeviceId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.EdgeId;
 | 
			
		||||
import org.thingsboard.server.common.data.plugin.ComponentType;
 | 
			
		||||
import org.thingsboard.server.common.msg.TbMsg;
 | 
			
		||||
 | 
			
		||||
@ -65,9 +77,46 @@ public class TbSendRPCReplyNode implements TbNode {
 | 
			
		||||
        } else if (StringUtils.isEmpty(msg.getData())) {
 | 
			
		||||
            ctx.tellFailure(msg, new RuntimeException("Request body is empty!"));
 | 
			
		||||
        } else {
 | 
			
		||||
            ctx.getRpcService().sendRpcReplyToDevice(serviceIdStr, UUID.fromString(sessionIdStr), Integer.parseInt(requestIdStr), msg.getData());
 | 
			
		||||
            ctx.tellSuccess(msg);
 | 
			
		||||
            if (StringUtils.isNotBlank(msg.getMetaData().getValue(DataConstants.EDGE_ID))) {
 | 
			
		||||
                saveRpcResponseToEdgeQueue(ctx, msg, serviceIdStr, sessionIdStr, requestIdStr);
 | 
			
		||||
            } else {
 | 
			
		||||
                ctx.getRpcService().sendRpcReplyToDevice(serviceIdStr, UUID.fromString(sessionIdStr), Integer.parseInt(requestIdStr), msg.getData());
 | 
			
		||||
                ctx.tellSuccess(msg);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void saveRpcResponseToEdgeQueue(TbContext ctx, TbMsg msg, String serviceIdStr, String sessionIdStr, String requestIdStr) {
 | 
			
		||||
        EdgeId edgeId;
 | 
			
		||||
        DeviceId deviceId;
 | 
			
		||||
        try {
 | 
			
		||||
            edgeId = new EdgeId(UUID.fromString(msg.getMetaData().getValue(DataConstants.EDGE_ID)));
 | 
			
		||||
            deviceId = new DeviceId(UUID.fromString(msg.getMetaData().getValue(DataConstants.DEVICE_ID)));
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            String errMsg = String.format("[%s] Failed to parse edgeId or deviceId from metadata %s!", ctx.getTenantId(), msg.getMetaData());
 | 
			
		||||
            ctx.tellFailure(msg, new RuntimeException(errMsg));
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ObjectNode body = JacksonUtil.OBJECT_MAPPER.createObjectNode();
 | 
			
		||||
        body.put("serviceId", serviceIdStr);
 | 
			
		||||
        body.put("sessionId", sessionIdStr);
 | 
			
		||||
        body.put("requestId", requestIdStr);
 | 
			
		||||
        body.put("response", msg.getData());
 | 
			
		||||
        EdgeEvent edgeEvent = EdgeUtils.constructEdgeEvent(ctx.getTenantId(), edgeId, EdgeEventType.DEVICE,
 | 
			
		||||
                        EdgeEventActionType.RPC_CALL, deviceId, JacksonUtil.OBJECT_MAPPER.valueToTree(body));
 | 
			
		||||
        ListenableFuture<Void> future = ctx.getEdgeEventService().saveAsync(edgeEvent);
 | 
			
		||||
        Futures.addCallback(future, new FutureCallback<>() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void onSuccess(Void result) {
 | 
			
		||||
                ctx.onEdgeEventUpdate(ctx.getTenantId(), edgeId);
 | 
			
		||||
                ctx.tellSuccess(msg);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            public void onFailure(Throwable t) {
 | 
			
		||||
                ctx.tellFailure(msg, t);
 | 
			
		||||
            }
 | 
			
		||||
        }, ctx.getDbCallbackExecutor());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,119 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright © 2016-2022 The Thingsboard Authors
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.rule.engine.rpc;
 | 
			
		||||
 | 
			
		||||
import com.google.common.util.concurrent.SettableFuture;
 | 
			
		||||
import org.junit.Before;
 | 
			
		||||
import org.junit.Test;
 | 
			
		||||
import org.junit.runner.RunWith;
 | 
			
		||||
import org.mockito.Mock;
 | 
			
		||||
import org.mockito.Mockito;
 | 
			
		||||
import org.mockito.junit.MockitoJUnitRunner;
 | 
			
		||||
import org.thingsboard.common.util.JacksonUtil;
 | 
			
		||||
import org.thingsboard.common.util.ListeningExecutor;
 | 
			
		||||
import org.thingsboard.rule.engine.api.RuleEngineRpcService;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbContext;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbNodeConfiguration;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbNodeException;
 | 
			
		||||
import org.thingsboard.server.common.data.DataConstants;
 | 
			
		||||
import org.thingsboard.server.common.data.id.DeviceId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.TenantId;
 | 
			
		||||
import org.thingsboard.server.common.msg.TbMsg;
 | 
			
		||||
import org.thingsboard.server.common.msg.TbMsgDataType;
 | 
			
		||||
import org.thingsboard.server.common.msg.TbMsgMetaData;
 | 
			
		||||
import org.thingsboard.server.common.msg.session.SessionMsgType;
 | 
			
		||||
import org.thingsboard.server.dao.edge.EdgeEventService;
 | 
			
		||||
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
 | 
			
		||||
import static org.mockito.ArgumentMatchers.any;
 | 
			
		||||
import static org.mockito.Mockito.never;
 | 
			
		||||
import static org.mockito.Mockito.verify;
 | 
			
		||||
 | 
			
		||||
@RunWith(MockitoJUnitRunner.class)
 | 
			
		||||
public class TbSendRPCReplyNodeTest {
 | 
			
		||||
 | 
			
		||||
    private static final String DUMMY_SERVICE_ID = "testServiceId";
 | 
			
		||||
    private static final int DUMMY_REQUEST_ID = 0;
 | 
			
		||||
    private static final UUID DUMMY_SESSION_ID = UUID.randomUUID();
 | 
			
		||||
    private static final String DUMMY_DATA = "{\"key\":\"value\"}";
 | 
			
		||||
 | 
			
		||||
    TbSendRPCReplyNode node;
 | 
			
		||||
 | 
			
		||||
    private final TenantId tenantId = TenantId.fromUUID(UUID.randomUUID());
 | 
			
		||||
    private final DeviceId deviceId = new DeviceId(UUID.randomUUID());
 | 
			
		||||
 | 
			
		||||
    @Mock
 | 
			
		||||
    private TbContext ctx;
 | 
			
		||||
 | 
			
		||||
    @Mock
 | 
			
		||||
    private RuleEngineRpcService rpcService;
 | 
			
		||||
 | 
			
		||||
    @Mock
 | 
			
		||||
    private EdgeEventService edgeEventService;
 | 
			
		||||
 | 
			
		||||
    @Mock
 | 
			
		||||
    private ListeningExecutor listeningExecutor;
 | 
			
		||||
 | 
			
		||||
    @Before
 | 
			
		||||
    public void setUp() throws TbNodeException {
 | 
			
		||||
        node = new TbSendRPCReplyNode();
 | 
			
		||||
        TbSendRpcReplyNodeConfiguration config = new TbSendRpcReplyNodeConfiguration().defaultConfiguration();
 | 
			
		||||
        node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void sendReplyToTransport() {
 | 
			
		||||
        Mockito.when(ctx.getRpcService()).thenReturn(rpcService);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        TbMsg msg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, getDefaultMetadata(),
 | 
			
		||||
                TbMsgDataType.JSON, DUMMY_DATA, null, null);
 | 
			
		||||
 | 
			
		||||
        node.onMsg(ctx, msg);
 | 
			
		||||
 | 
			
		||||
        verify(rpcService).sendRpcReplyToDevice(DUMMY_SERVICE_ID, DUMMY_SESSION_ID, DUMMY_REQUEST_ID, DUMMY_DATA);
 | 
			
		||||
        verify(edgeEventService, never()).saveAsync(any());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void sendReplyToEdgeQueue() {
 | 
			
		||||
        Mockito.when(ctx.getTenantId()).thenReturn(tenantId);
 | 
			
		||||
        Mockito.when(ctx.getEdgeEventService()).thenReturn(edgeEventService);
 | 
			
		||||
        Mockito.when(edgeEventService.saveAsync(any())).thenReturn(SettableFuture.create());
 | 
			
		||||
        Mockito.when(ctx.getDbCallbackExecutor()).thenReturn(listeningExecutor);
 | 
			
		||||
 | 
			
		||||
        TbMsgMetaData defaultMetadata = getDefaultMetadata();
 | 
			
		||||
        defaultMetadata.putValue(DataConstants.EDGE_ID, UUID.randomUUID().toString());
 | 
			
		||||
        defaultMetadata.putValue(DataConstants.DEVICE_ID, UUID.randomUUID().toString());
 | 
			
		||||
        TbMsg msg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, defaultMetadata,
 | 
			
		||||
                TbMsgDataType.JSON, DUMMY_DATA, null, null);
 | 
			
		||||
 | 
			
		||||
        node.onMsg(ctx, msg);
 | 
			
		||||
 | 
			
		||||
        verify(edgeEventService).saveAsync(any());
 | 
			
		||||
        verify(rpcService, never()).sendRpcReplyToDevice(DUMMY_SERVICE_ID, DUMMY_SESSION_ID, DUMMY_REQUEST_ID, DUMMY_DATA);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private TbMsgMetaData getDefaultMetadata() {
 | 
			
		||||
        TbSendRpcReplyNodeConfiguration config = new TbSendRpcReplyNodeConfiguration().defaultConfiguration();
 | 
			
		||||
        TbMsgMetaData metadata = new TbMsgMetaData();
 | 
			
		||||
        metadata.putValue(config.getServiceIdMetaDataAttribute(), DUMMY_SERVICE_ID);
 | 
			
		||||
        metadata.putValue(config.getSessionIdMetaDataAttribute(), DUMMY_SESSION_ID.toString());
 | 
			
		||||
        metadata.putValue(config.getRequestIdMetaDataAttribute(), Integer.toString(DUMMY_REQUEST_ID));
 | 
			
		||||
        return metadata;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user