Added edge license key and cloud endpoint
This commit is contained in:
		
							parent
							
								
									4bc7f5288f
								
							
						
					
					
						commit
						b67e21604b
					
				@ -57,6 +57,8 @@ CREATE TABLE IF NOT EXISTS thingsboard.edge (
 | 
			
		||||
    search_text text,
 | 
			
		||||
    routing_key text,
 | 
			
		||||
    secret text,
 | 
			
		||||
    edge_license_key text,
 | 
			
		||||
    cloud_endpoint text,
 | 
			
		||||
    configuration text,
 | 
			
		||||
    additional_info text,
 | 
			
		||||
    PRIMARY KEY (id, tenant_id, customer_id, type)
 | 
			
		||||
 | 
			
		||||
@ -25,6 +25,8 @@ CREATE TABLE IF NOT EXISTS edge (
 | 
			
		||||
    label varchar(255),
 | 
			
		||||
    routing_key varchar(255),
 | 
			
		||||
    secret varchar(255),
 | 
			
		||||
    edge_license_key varchar(30),
 | 
			
		||||
    cloud_endpoint varchar(255),
 | 
			
		||||
    search_text varchar(255),
 | 
			
		||||
    tenant_id varchar(31),
 | 
			
		||||
    CONSTRAINT edge_name_unq_key UNIQUE (tenant_id, name),
 | 
			
		||||
 | 
			
		||||
@ -69,7 +69,7 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt
 | 
			
		||||
    public static final String FORM_BASED_LOGIN_ENTRY_POINT = "/api/auth/login";
 | 
			
		||||
    public static final String PUBLIC_LOGIN_ENTRY_POINT = "/api/auth/login/public";
 | 
			
		||||
    public static final String TOKEN_REFRESH_ENTRY_POINT = "/api/auth/token";
 | 
			
		||||
    protected static final String[] NON_TOKEN_BASED_AUTH_ENTRY_POINTS = new String[] {"/index.html", "/static/**", "/api/noauth/**", "/webjars/**"};
 | 
			
		||||
    protected static final String[] NON_TOKEN_BASED_AUTH_ENTRY_POINTS = new String[] {"/index.html", "/static/**", "/api/noauth/**", "/webjars/**",  "/api/license/**"};
 | 
			
		||||
    public static final String TOKEN_BASED_AUTH_ENTRY_POINT = "/api/**";
 | 
			
		||||
    public static final String WS_TOKEN_BASED_AUTH_ENTRY_POINT = "/api/ws/**";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -32,6 +32,7 @@ import org.thingsboard.server.common.data.EntityType;
 | 
			
		||||
import org.thingsboard.server.common.data.audit.ActionType;
 | 
			
		||||
import org.thingsboard.server.common.data.edge.Edge;
 | 
			
		||||
import org.thingsboard.server.common.data.edge.EdgeSearchQuery;
 | 
			
		||||
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
 | 
			
		||||
import org.thingsboard.server.common.data.exception.ThingsboardException;
 | 
			
		||||
import org.thingsboard.server.common.data.id.CustomerId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.EdgeId;
 | 
			
		||||
@ -408,4 +409,24 @@ public class EdgeController extends BaseController {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @RequestMapping(value = "/license/checkInstance", method = RequestMethod.POST)
 | 
			
		||||
    @ResponseBody
 | 
			
		||||
    public Object checkInstance(@RequestBody Object request) throws ThingsboardException {
 | 
			
		||||
        try {
 | 
			
		||||
            return edgeService.checkInstance(request);
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            throw new ThingsboardException(e, ThingsboardErrorCode.SUBSCRIPTION_VIOLATION);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @RequestMapping(value = "/license/activateInstance", params = {"licenseSecret", "releaseDate"}, method = RequestMethod.POST)
 | 
			
		||||
    @ResponseBody
 | 
			
		||||
    public Object activateInstance(@RequestParam String licenseSecret,
 | 
			
		||||
                                   @RequestParam String releaseDate) throws ThingsboardException {
 | 
			
		||||
        try {
 | 
			
		||||
            return edgeService.activateInstance(licenseSecret, releaseDate);
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            throw new ThingsboardException(e, ThingsboardErrorCode.SUBSCRIPTION_VIOLATION);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -27,6 +27,7 @@ import org.springframework.security.authentication.LockedException;
 | 
			
		||||
import org.springframework.security.core.AuthenticationException;
 | 
			
		||||
import org.springframework.security.web.access.AccessDeniedHandler;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
import org.springframework.web.client.HttpClientErrorException;
 | 
			
		||||
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
 | 
			
		||||
import org.thingsboard.server.common.data.exception.ThingsboardException;
 | 
			
		||||
import org.thingsboard.server.common.msg.tools.TbRateLimitsException;
 | 
			
		||||
@ -66,7 +67,12 @@ public class ThingsboardErrorResponseHandler implements AccessDeniedHandler {
 | 
			
		||||
                response.setContentType(MediaType.APPLICATION_JSON_VALUE);
 | 
			
		||||
 | 
			
		||||
                if (exception instanceof ThingsboardException) {
 | 
			
		||||
                    handleThingsboardException((ThingsboardException) exception, response);
 | 
			
		||||
                    ThingsboardException thingsboardException = (ThingsboardException) exception;
 | 
			
		||||
                    if (thingsboardException.getErrorCode() == ThingsboardErrorCode.SUBSCRIPTION_VIOLATION) {
 | 
			
		||||
                        handleSubscriptionException((ThingsboardException) exception, response);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        handleThingsboardException((ThingsboardException) exception, response);
 | 
			
		||||
                    }
 | 
			
		||||
                } else if (exception instanceof TbRateLimitsException) {
 | 
			
		||||
                    handleRateLimitException(response, (TbRateLimitsException) exception);
 | 
			
		||||
                } else if (exception instanceof AccessDeniedException) {
 | 
			
		||||
@ -126,6 +132,11 @@ public class ThingsboardErrorResponseHandler implements AccessDeniedHandler {
 | 
			
		||||
                        ThingsboardErrorCode.TOO_MANY_REQUESTS, HttpStatus.TOO_MANY_REQUESTS));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void handleSubscriptionException(ThingsboardException subscriptionException, HttpServletResponse response) throws IOException {
 | 
			
		||||
        response.setStatus(HttpStatus.FORBIDDEN.value());
 | 
			
		||||
        mapper.writeValue(response.getWriter(),
 | 
			
		||||
                (new ObjectMapper()).readValue(((HttpClientErrorException) subscriptionException.getCause()).getResponseBodyAsByteArray(), Object.class));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void handleAccessDeniedException(HttpServletResponse response) throws IOException {
 | 
			
		||||
        response.setStatus(HttpStatus.FORBIDDEN.value());
 | 
			
		||||
 | 
			
		||||
@ -145,8 +145,15 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
 | 
			
		||||
        executor.submit(() -> {
 | 
			
		||||
            while (!Thread.interrupted()) {
 | 
			
		||||
                try {
 | 
			
		||||
                    for (EdgeGrpcSession session : sessions.values()) {
 | 
			
		||||
                        session.processHandleMessages();
 | 
			
		||||
                    if (sessions.size() > 0) {
 | 
			
		||||
                        for (EdgeGrpcSession session : sessions.values()) {
 | 
			
		||||
                            session.processHandleMessages();
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
                        log.trace("No sessions available, sleep for the next run");
 | 
			
		||||
                        try {
 | 
			
		||||
                            Thread.sleep(1000);
 | 
			
		||||
                        } catch (InterruptedException ignore) {}
 | 
			
		||||
                    }
 | 
			
		||||
                } catch (Exception e) {
 | 
			
		||||
                    log.warn("Failed to process messages handling!", e);
 | 
			
		||||
 | 
			
		||||
@ -159,10 +159,10 @@ public final class EdgeGrpcSession implements Closeable {
 | 
			
		||||
                    if (ConnectResponseCode.ACCEPTED != responseMsg.getResponseCode()) {
 | 
			
		||||
                        outputStream.onError(new RuntimeException(responseMsg.getErrorMsg()));
 | 
			
		||||
                    }
 | 
			
		||||
                    if (ConnectResponseCode.ACCEPTED == responseMsg.getResponseCode()) {
 | 
			
		||||
                        connected = true;
 | 
			
		||||
                        ctx.getSyncEdgeService().sync(edge);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if (!connected && requestMsg.getMsgType().equals(RequestMsgType.SYNC_REQUEST_RPC_MESSAGE)) {
 | 
			
		||||
                    connected = true;
 | 
			
		||||
                    ctx.getSyncEdgeService().sync(edge);
 | 
			
		||||
                }
 | 
			
		||||
                if (connected) {
 | 
			
		||||
                    if (requestMsg.getMsgType().equals(RequestMsgType.UPLINK_RPC_MESSAGE) && requestMsg.hasUplinkMsg()) {
 | 
			
		||||
@ -182,8 +182,12 @@ public final class EdgeGrpcSession implements Closeable {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void onCompleted() {
 | 
			
		||||
                connected = false;
 | 
			
		||||
                sessionCloseListener.accept(edge.getId());
 | 
			
		||||
                outputStream.onCompleted();
 | 
			
		||||
                if (edge != null) {
 | 
			
		||||
                    sessionCloseListener.accept(edge.getId());
 | 
			
		||||
                }
 | 
			
		||||
                try {
 | 
			
		||||
                    outputStream.onCompleted();
 | 
			
		||||
                } catch (Exception ignored) {}
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
@ -948,6 +952,8 @@ public final class EdgeGrpcSession implements Closeable {
 | 
			
		||||
                .setName(edge.getName())
 | 
			
		||||
                .setRoutingKey(edge.getRoutingKey())
 | 
			
		||||
                .setType(edge.getType())
 | 
			
		||||
                .setEdgeLicenseKey(edge.getEdgeLicenseKey())
 | 
			
		||||
                .setCloudEndpoint(edge.getCloudEndpoint())
 | 
			
		||||
                .setCloudType("CE")
 | 
			
		||||
                .build();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -17,11 +17,10 @@ package org.thingsboard.server.service.edge.rpc.constructor;
 | 
			
		||||
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
import org.thingsboard.server.common.data.Customer;
 | 
			
		||||
import org.thingsboard.server.common.data.asset.Asset;
 | 
			
		||||
import org.thingsboard.server.common.data.id.AssetId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.CustomerId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.EntityId;
 | 
			
		||||
import org.thingsboard.server.dao.util.mapping.JacksonUtil;
 | 
			
		||||
import org.thingsboard.server.gen.edge.AssetUpdateMsg;
 | 
			
		||||
import org.thingsboard.server.gen.edge.UpdateMsgType;
 | 
			
		||||
 | 
			
		||||
@ -43,6 +42,9 @@ public class AssetMsgConstructor {
 | 
			
		||||
            builder.setCustomerIdMSB(customerId.getId().getMostSignificantBits());
 | 
			
		||||
            builder.setCustomerIdLSB(customerId.getId().getLeastSignificantBits());
 | 
			
		||||
        }
 | 
			
		||||
        if (asset.getAdditionalInfo() != null) {
 | 
			
		||||
            builder.setAdditionalInfo(JacksonUtil.toString(asset.getAdditionalInfo()));
 | 
			
		||||
        }
 | 
			
		||||
        return builder.build();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -24,6 +24,7 @@ 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.dao.util.mapping.JacksonUtil;
 | 
			
		||||
import org.thingsboard.server.gen.edge.DeviceCredentialsUpdateMsg;
 | 
			
		||||
import org.thingsboard.server.gen.edge.DeviceRpcCallMsg;
 | 
			
		||||
import org.thingsboard.server.gen.edge.DeviceUpdateMsg;
 | 
			
		||||
@ -50,6 +51,9 @@ public class DeviceMsgConstructor {
 | 
			
		||||
            builder.setCustomerIdMSB(customerId.getId().getMostSignificantBits());
 | 
			
		||||
            builder.setCustomerIdLSB(customerId.getId().getLeastSignificantBits());
 | 
			
		||||
        }
 | 
			
		||||
        if (device.getAdditionalInfo() != null) {
 | 
			
		||||
            builder.setAdditionalInfo(JacksonUtil.toString(device.getAdditionalInfo()));
 | 
			
		||||
        }
 | 
			
		||||
        return builder.build();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -19,8 +19,8 @@ import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
import org.thingsboard.server.common.data.EntityView;
 | 
			
		||||
import org.thingsboard.server.common.data.id.CustomerId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.EntityId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.EntityViewId;
 | 
			
		||||
import org.thingsboard.server.dao.util.mapping.JacksonUtil;
 | 
			
		||||
import org.thingsboard.server.gen.edge.EdgeEntityType;
 | 
			
		||||
import org.thingsboard.server.gen.edge.EntityViewUpdateMsg;
 | 
			
		||||
import org.thingsboard.server.gen.edge.UpdateMsgType;
 | 
			
		||||
@ -54,6 +54,9 @@ public class EntityViewMsgConstructor {
 | 
			
		||||
            builder.setCustomerIdMSB(customerId.getId().getMostSignificantBits());
 | 
			
		||||
            builder.setCustomerIdLSB(customerId.getId().getLeastSignificantBits());
 | 
			
		||||
        }
 | 
			
		||||
        if (entityView.getAdditionalInfo() != null) {
 | 
			
		||||
            builder.setAdditionalInfo(JacksonUtil.toString(entityView.getAdditionalInfo()));
 | 
			
		||||
        }
 | 
			
		||||
        return builder.build();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -510,6 +510,8 @@ public abstract class AbstractControllerTest {
 | 
			
		||||
        edge.setType(type);
 | 
			
		||||
        edge.setSecret(RandomStringUtils.randomAlphanumeric(20));
 | 
			
		||||
        edge.setRoutingKey(RandomStringUtils.randomAlphanumeric(20));
 | 
			
		||||
        edge.setEdgeLicenseKey(RandomStringUtils.randomAlphanumeric(20));
 | 
			
		||||
        edge.setCloudEndpoint("http://localhost:8080");
 | 
			
		||||
        return edge;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -18,6 +18,7 @@ package org.thingsboard.server.controller;
 | 
			
		||||
import com.datastax.driver.core.utils.UUIDs;
 | 
			
		||||
import com.fasterxml.jackson.core.type.TypeReference;
 | 
			
		||||
import org.apache.commons.lang3.RandomStringUtils;
 | 
			
		||||
import org.apache.commons.lang3.StringUtils;
 | 
			
		||||
import org.junit.After;
 | 
			
		||||
import org.junit.Assert;
 | 
			
		||||
import org.junit.Before;
 | 
			
		||||
@ -90,6 +91,8 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
 | 
			
		||||
        Assert.assertNotNull(savedEdge.getCustomerId());
 | 
			
		||||
        Assert.assertEquals(NULL_UUID, savedEdge.getCustomerId().getId());
 | 
			
		||||
        Assert.assertEquals(edge.getName(), savedEdge.getName());
 | 
			
		||||
        Assert.assertTrue(StringUtils.isNoneBlank(savedEdge.getEdgeLicenseKey()));
 | 
			
		||||
        Assert.assertTrue(StringUtils.isNoneBlank(savedEdge.getCloudEndpoint()));
 | 
			
		||||
 | 
			
		||||
        savedEdge.setName("My new edge");
 | 
			
		||||
        doPost("/api/edge", savedEdge, Edge.class);
 | 
			
		||||
 | 
			
		||||
@ -76,4 +76,8 @@ public interface EdgeService {
 | 
			
		||||
    ListenableFuture<List<Edge>> findEdgesByTenantIdAndDashboardId(TenantId tenantId, DashboardId dashboardId);
 | 
			
		||||
 | 
			
		||||
    ListenableFuture<List<EdgeId>> findRelatedEdgeIdsByEntityId(TenantId tenantId, EntityId entityId);
 | 
			
		||||
 | 
			
		||||
    Object checkInstance(Object request);
 | 
			
		||||
 | 
			
		||||
    Object activateInstance(String licenseSecret, String releaseDate);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -45,6 +45,8 @@ public class Edge extends SearchTextBasedWithAdditionalInfo<EdgeId> implements H
 | 
			
		||||
    private String label;
 | 
			
		||||
    private String routingKey;
 | 
			
		||||
    private String secret;
 | 
			
		||||
    private String edgeLicenseKey;
 | 
			
		||||
    private String cloudEndpoint;
 | 
			
		||||
    private transient JsonNode configuration;
 | 
			
		||||
 | 
			
		||||
    public Edge() {
 | 
			
		||||
@ -64,6 +66,8 @@ public class Edge extends SearchTextBasedWithAdditionalInfo<EdgeId> implements H
 | 
			
		||||
        this.name = edge.getName();
 | 
			
		||||
        this.routingKey = edge.getRoutingKey();
 | 
			
		||||
        this.secret = edge.getSecret();
 | 
			
		||||
        this.edgeLicenseKey = edge.getEdgeLicenseKey();
 | 
			
		||||
        this.cloudEndpoint = edge.getCloudEndpoint();
 | 
			
		||||
        this.configuration = edge.getConfiguration();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -28,7 +28,8 @@ public enum ThingsboardErrorCode {
 | 
			
		||||
    BAD_REQUEST_PARAMS(31),
 | 
			
		||||
    ITEM_NOT_FOUND(32),
 | 
			
		||||
    TOO_MANY_REQUESTS(33),
 | 
			
		||||
    TOO_MANY_UPDATES(34);
 | 
			
		||||
    TOO_MANY_UPDATES(34),
 | 
			
		||||
    SUBSCRIPTION_VIOLATION(40);
 | 
			
		||||
 | 
			
		||||
    private int errorCode;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -81,7 +81,6 @@ public class EdgeGrpcClient implements EdgeRpcClient {
 | 
			
		||||
                throw new RuntimeException(e);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        gracefulShutdown();
 | 
			
		||||
        channel = builder.build();
 | 
			
		||||
        EdgeRpcServiceGrpc.EdgeRpcServiceStub stub = EdgeRpcServiceGrpc.newStub(channel);
 | 
			
		||||
        log.info("[{}] Sending a connect request to the TB!", edgeKey);
 | 
			
		||||
@ -92,53 +91,6 @@ public class EdgeGrpcClient implements EdgeRpcClient {
 | 
			
		||||
                .build());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void gracefulShutdown() {
 | 
			
		||||
        try {
 | 
			
		||||
            if (channel != null) {
 | 
			
		||||
                channel.shutdown().awaitTermination(timeoutSecs, TimeUnit.SECONDS);
 | 
			
		||||
            }
 | 
			
		||||
        } catch (InterruptedException e) {
 | 
			
		||||
            log.debug("Error during shutdown of the previous channel", e);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void disconnect() throws InterruptedException {
 | 
			
		||||
        try {
 | 
			
		||||
            inputStream.onCompleted();
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
        }
 | 
			
		||||
        if (channel != null) {
 | 
			
		||||
            channel.shutdown().awaitTermination(timeoutSecs, TimeUnit.SECONDS);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void sendUplinkMsg(UplinkMsg msg) {
 | 
			
		||||
        try {
 | 
			
		||||
            uplinkMsgLock.lock();
 | 
			
		||||
            this.inputStream.onNext(RequestMsg.newBuilder()
 | 
			
		||||
                    .setMsgType(RequestMsgType.UPLINK_RPC_MESSAGE)
 | 
			
		||||
                    .setUplinkMsg(msg)
 | 
			
		||||
                    .build());
 | 
			
		||||
        } finally {
 | 
			
		||||
            uplinkMsgLock.unlock();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void sendDownlinkResponseMsg(DownlinkResponseMsg downlinkResponseMsg) {
 | 
			
		||||
        try {
 | 
			
		||||
            uplinkMsgLock.lock();
 | 
			
		||||
            this.inputStream.onNext(RequestMsg.newBuilder()
 | 
			
		||||
                    .setMsgType(RequestMsgType.UPLINK_RPC_MESSAGE)
 | 
			
		||||
                    .setDownlinkResponseMsg(downlinkResponseMsg)
 | 
			
		||||
                    .build());
 | 
			
		||||
        } finally {
 | 
			
		||||
            uplinkMsgLock.unlock();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private StreamObserver<ResponseMsg> initOutputStream(String edgeKey,
 | 
			
		||||
                                                         Consumer<UplinkResponseMsg> onUplinkResponse,
 | 
			
		||||
                                                         Consumer<EdgeConfiguration> onEdgeUpdate,
 | 
			
		||||
@ -179,8 +131,52 @@ public class EdgeGrpcClient implements EdgeRpcClient {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void onCompleted() {
 | 
			
		||||
                log.debug("[{}] The rpc session was closed!", edgeKey);
 | 
			
		||||
                onError.accept(new EdgeConnectionException("[" + edgeKey + "] The rpc session was closed!"));
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void disconnect() throws InterruptedException {
 | 
			
		||||
        if (channel != null) {
 | 
			
		||||
            channel.shutdown().awaitTermination(timeoutSecs, TimeUnit.SECONDS);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void sendUplinkMsg(UplinkMsg msg) {
 | 
			
		||||
        try {
 | 
			
		||||
            uplinkMsgLock.lock();
 | 
			
		||||
            this.inputStream.onNext(RequestMsg.newBuilder()
 | 
			
		||||
                    .setMsgType(RequestMsgType.UPLINK_RPC_MESSAGE)
 | 
			
		||||
                    .setUplinkMsg(msg)
 | 
			
		||||
                    .build());
 | 
			
		||||
        } finally {
 | 
			
		||||
            uplinkMsgLock.unlock();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void sendSyncRequestMsg() {
 | 
			
		||||
        try {
 | 
			
		||||
            uplinkMsgLock.lock();
 | 
			
		||||
            this.inputStream.onNext(RequestMsg.newBuilder()
 | 
			
		||||
                    .setMsgType(RequestMsgType.SYNC_REQUEST_RPC_MESSAGE)
 | 
			
		||||
                    .build());
 | 
			
		||||
        } finally {
 | 
			
		||||
            uplinkMsgLock.unlock();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void sendDownlinkResponseMsg(DownlinkResponseMsg downlinkResponseMsg) {
 | 
			
		||||
        try {
 | 
			
		||||
            uplinkMsgLock.lock();
 | 
			
		||||
            this.inputStream.onNext(RequestMsg.newBuilder()
 | 
			
		||||
                    .setMsgType(RequestMsgType.UPLINK_RPC_MESSAGE)
 | 
			
		||||
                    .setDownlinkResponseMsg(downlinkResponseMsg)
 | 
			
		||||
                    .build());
 | 
			
		||||
        } finally {
 | 
			
		||||
            uplinkMsgLock.unlock();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -34,6 +34,8 @@ public interface EdgeRpcClient {
 | 
			
		||||
 | 
			
		||||
    void disconnect() throws InterruptedException;
 | 
			
		||||
 | 
			
		||||
    void sendSyncRequestMsg();
 | 
			
		||||
 | 
			
		||||
    void sendUplinkMsg(UplinkMsg uplinkMsg);
 | 
			
		||||
 | 
			
		||||
    void sendDownlinkResponseMsg(DownlinkResponseMsg downlinkResponseMsg);
 | 
			
		||||
 | 
			
		||||
@ -49,6 +49,7 @@ message ResponseMsg {
 | 
			
		||||
enum RequestMsgType {
 | 
			
		||||
  CONNECT_RPC_MESSAGE = 0;
 | 
			
		||||
  UPLINK_RPC_MESSAGE = 1;
 | 
			
		||||
  SYNC_REQUEST_RPC_MESSAGE = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message ConnectRequestMsg {
 | 
			
		||||
@ -76,7 +77,9 @@ message EdgeConfiguration {
 | 
			
		||||
  string name = 5;
 | 
			
		||||
  string routingKey = 6;
 | 
			
		||||
  string type = 7;
 | 
			
		||||
  string cloudType = 8;
 | 
			
		||||
  string edgeLicenseKey = 8;
 | 
			
		||||
  string cloudEndpoint = 9;
 | 
			
		||||
  string cloudType = 10;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum UpdateMsgType {
 | 
			
		||||
@ -169,6 +172,7 @@ message DeviceUpdateMsg {
 | 
			
		||||
  string name = 6;
 | 
			
		||||
  string type = 7;
 | 
			
		||||
  string label = 8;
 | 
			
		||||
  string additionalInfo = 9;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message DeviceCredentialsUpdateMsg {
 | 
			
		||||
@ -188,6 +192,7 @@ message AssetUpdateMsg {
 | 
			
		||||
  string name = 6;
 | 
			
		||||
  string type = 7;
 | 
			
		||||
  string label = 8;
 | 
			
		||||
  string additionalInfo = 9;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message EntityViewUpdateMsg {
 | 
			
		||||
@ -201,6 +206,7 @@ message EntityViewUpdateMsg {
 | 
			
		||||
  int64 entityIdMSB = 8;
 | 
			
		||||
  int64 entityIdLSB = 9;
 | 
			
		||||
  EdgeEntityType entityType = 10;
 | 
			
		||||
  string additionalInfo = 11;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message AlarmUpdateMsg {
 | 
			
		||||
 | 
			
		||||
@ -20,13 +20,21 @@ import com.google.common.util.concurrent.Futures;
 | 
			
		||||
import com.google.common.util.concurrent.ListenableFuture;
 | 
			
		||||
import com.google.common.util.concurrent.MoreExecutors;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.apache.http.HttpHost;
 | 
			
		||||
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
 | 
			
		||||
import org.apache.http.impl.client.CloseableHttpClient;
 | 
			
		||||
import org.apache.http.impl.client.HttpClients;
 | 
			
		||||
import org.springframework.beans.factory.annotation.Autowired;
 | 
			
		||||
import org.springframework.beans.factory.annotation.Value;
 | 
			
		||||
import org.springframework.cache.Cache;
 | 
			
		||||
import org.springframework.cache.CacheManager;
 | 
			
		||||
import org.springframework.cache.annotation.CacheEvict;
 | 
			
		||||
import org.springframework.cache.annotation.Cacheable;
 | 
			
		||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
 | 
			
		||||
import org.springframework.http.client.SimpleClientHttpRequestFactory;
 | 
			
		||||
import org.springframework.stereotype.Service;
 | 
			
		||||
import org.springframework.util.StringUtils;
 | 
			
		||||
import org.springframework.web.client.RestTemplate;
 | 
			
		||||
import org.thingsboard.server.common.data.Customer;
 | 
			
		||||
import org.thingsboard.server.common.data.EntitySubtype;
 | 
			
		||||
import org.thingsboard.server.common.data.EntityType;
 | 
			
		||||
@ -48,14 +56,9 @@ import org.thingsboard.server.common.data.relation.EntityRelation;
 | 
			
		||||
import org.thingsboard.server.common.data.relation.EntitySearchDirection;
 | 
			
		||||
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
 | 
			
		||||
import org.thingsboard.server.common.data.rule.RuleChain;
 | 
			
		||||
import org.thingsboard.server.dao.asset.AssetService;
 | 
			
		||||
import org.thingsboard.server.dao.customer.CustomerDao;
 | 
			
		||||
import org.thingsboard.server.dao.dashboard.DashboardService;
 | 
			
		||||
import org.thingsboard.server.dao.device.DeviceService;
 | 
			
		||||
import org.thingsboard.server.dao.entity.AbstractEntityService;
 | 
			
		||||
import org.thingsboard.server.dao.entityview.EntityViewService;
 | 
			
		||||
import org.thingsboard.server.dao.exception.DataValidationException;
 | 
			
		||||
import org.thingsboard.server.dao.model.ModelConstants;
 | 
			
		||||
import org.thingsboard.server.dao.relation.RelationService;
 | 
			
		||||
import org.thingsboard.server.dao.rule.RuleChainService;
 | 
			
		||||
import org.thingsboard.server.dao.service.DataValidator;
 | 
			
		||||
@ -65,10 +68,15 @@ import org.thingsboard.server.dao.tenant.TenantDao;
 | 
			
		||||
import org.thingsboard.server.dao.user.UserService;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
import javax.annotation.PostConstruct;
 | 
			
		||||
import java.net.InetSocketAddress;
 | 
			
		||||
import java.net.Proxy;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.Comparator;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Optional;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
 | 
			
		||||
@ -89,6 +97,10 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic
 | 
			
		||||
    public static final String INCORRECT_CUSTOMER_ID = "Incorrect customerId ";
 | 
			
		||||
    public static final String INCORRECT_EDGE_ID = "Incorrect edgeId ";
 | 
			
		||||
 | 
			
		||||
    private RestTemplate restTemplate;
 | 
			
		||||
 | 
			
		||||
    private static final String EDGE_LICENSE_SERVER_ENDPOINT = "https://license.thingsboard.io";
 | 
			
		||||
 | 
			
		||||
    @Autowired
 | 
			
		||||
    private EdgeDao edgeDao;
 | 
			
		||||
 | 
			
		||||
@ -110,6 +122,17 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic
 | 
			
		||||
    @Autowired
 | 
			
		||||
    private RelationService relationService;
 | 
			
		||||
 | 
			
		||||
    @Value("${edges.rpc.enabled:false}")
 | 
			
		||||
    private boolean edgesRpcEnabled;
 | 
			
		||||
 | 
			
		||||
    @PostConstruct
 | 
			
		||||
    public void init() {
 | 
			
		||||
        super.init();
 | 
			
		||||
        if (edgesRpcEnabled) {
 | 
			
		||||
            initRestTemplate();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Edge findEdgeById(TenantId tenantId, EdgeId edgeId) {
 | 
			
		||||
        log.trace("Executing findEdgeById [{}]", edgeId);
 | 
			
		||||
@ -374,6 +397,12 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic
 | 
			
		||||
                    if (StringUtils.isEmpty(edge.getRoutingKey())) {
 | 
			
		||||
                        throw new DataValidationException("Edge routing key should be specified!");
 | 
			
		||||
                    }
 | 
			
		||||
                    if (StringUtils.isEmpty(edge.getEdgeLicenseKey())) {
 | 
			
		||||
                        throw new DataValidationException("Edge license key should be specified!");
 | 
			
		||||
                    }
 | 
			
		||||
                    if (StringUtils.isEmpty(edge.getCloudEndpoint())) {
 | 
			
		||||
                        throw new DataValidationException("Cloud endpoint should be specified!");
 | 
			
		||||
                    }
 | 
			
		||||
                    if (edge.getTenantId() == null) {
 | 
			
		||||
                        throw new DataValidationException("Edge should be assigned to tenant!");
 | 
			
		||||
                    } else {
 | 
			
		||||
@ -475,4 +504,55 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic
 | 
			
		||||
        }, MoreExecutors.directExecutor());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Object checkInstance(Object request) {
 | 
			
		||||
        return this.restTemplate.postForEntity(EDGE_LICENSE_SERVER_ENDPOINT + "/api/license/checkInstance", request, Object.class, new Object[0]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Object activateInstance(String edgeLicenseSecret, String releaseDate) {
 | 
			
		||||
        Map<String, String> params = new HashMap();
 | 
			
		||||
        params.put("licenseSecret", edgeLicenseSecret);
 | 
			
		||||
        params.put("releaseDate", releaseDate);
 | 
			
		||||
        return this.restTemplate.postForEntity(EDGE_LICENSE_SERVER_ENDPOINT + "/api/license/activateInstance?licenseSecret={licenseSecret}&releaseDate={releaseDate}", (Object) null, Object.class, params);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void initRestTemplate() {
 | 
			
		||||
        boolean jdkHttpClientEnabled = org.apache.commons.lang3.StringUtils.isNotEmpty(System.getProperty("tb.proxy.jdk")) && System.getProperty("tb.proxy.jdk").equalsIgnoreCase("true");
 | 
			
		||||
        boolean systemProxyEnabled = org.apache.commons.lang3.StringUtils.isNotEmpty(System.getProperty("tb.proxy.system")) && System.getProperty("tb.proxy.system").equalsIgnoreCase("true");
 | 
			
		||||
        boolean proxyEnabled = org.apache.commons.lang3.StringUtils.isNotEmpty(System.getProperty("tb.proxy.host")) && org.apache.commons.lang3.StringUtils.isNotEmpty(System.getProperty("tb.proxy.port"));
 | 
			
		||||
        if (jdkHttpClientEnabled) {
 | 
			
		||||
            log.warn("Going to use plain JDK Http Client!");
 | 
			
		||||
            SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
 | 
			
		||||
            if (proxyEnabled) {
 | 
			
		||||
                log.warn("Going to use Proxy Server: [{}:{}]", System.getProperty("tb.proxy.host"), System.getProperty("tb.proxy.port"));
 | 
			
		||||
                factory.setProxy(new Proxy(Proxy.Type.HTTP, InetSocketAddress.createUnresolved(System.getProperty("tb.proxy.host"), Integer.parseInt(System.getProperty("tb.proxy.port")))));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.restTemplate = new RestTemplate(new SimpleClientHttpRequestFactory());
 | 
			
		||||
        } else {
 | 
			
		||||
            CloseableHttpClient httpClient;
 | 
			
		||||
            HttpComponentsClientHttpRequestFactory requestFactory;
 | 
			
		||||
            if (systemProxyEnabled) {
 | 
			
		||||
                log.warn("Going to use System Proxy Server!");
 | 
			
		||||
                httpClient = HttpClients.createSystem();
 | 
			
		||||
                requestFactory = new HttpComponentsClientHttpRequestFactory();
 | 
			
		||||
                requestFactory.setHttpClient(httpClient);
 | 
			
		||||
                this.restTemplate = new RestTemplate(requestFactory);
 | 
			
		||||
            } else if (proxyEnabled) {
 | 
			
		||||
                log.warn("Going to use Proxy Server: [{}:{}]", System.getProperty("tb.proxy.host"), System.getProperty("tb.proxy.port"));
 | 
			
		||||
                httpClient = HttpClients.custom().setSSLHostnameVerifier(new DefaultHostnameVerifier()).setProxy(new HttpHost(System.getProperty("tb.proxy.host"), Integer.parseInt(System.getProperty("tb.proxy.port")), "https")).build();
 | 
			
		||||
                requestFactory = new HttpComponentsClientHttpRequestFactory();
 | 
			
		||||
                requestFactory.setHttpClient(httpClient);
 | 
			
		||||
                this.restTemplate = new RestTemplate(requestFactory);
 | 
			
		||||
            } else {
 | 
			
		||||
                httpClient = HttpClients.custom().setSSLHostnameVerifier(new DefaultHostnameVerifier()).build();
 | 
			
		||||
                requestFactory = new HttpComponentsClientHttpRequestFactory();
 | 
			
		||||
                requestFactory.setHttpClient(httpClient);
 | 
			
		||||
                this.restTemplate = new RestTemplate(requestFactory);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -377,6 +377,8 @@ public class ModelConstants {
 | 
			
		||||
 | 
			
		||||
    public static final String EDGE_ROUTING_KEY_PROPERTY = "routing_key";
 | 
			
		||||
    public static final String EDGE_SECRET_PROPERTY = "secret";
 | 
			
		||||
    public static final String EDGE_LICENSE_KEY_PROPERTY = "edge_license_key";
 | 
			
		||||
    public static final String EDGE_CLOUD_ENDPOINT_KEY_PROPERTY = "cloud_endpoint";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Cassandra edge queue constants.
 | 
			
		||||
 | 
			
		||||
@ -16,7 +16,6 @@
 | 
			
		||||
package org.thingsboard.server.dao.model.nosql;
 | 
			
		||||
 | 
			
		||||
import com.datastax.driver.core.utils.UUIDs;
 | 
			
		||||
import com.datastax.driver.mapping.annotations.ClusteringColumn;
 | 
			
		||||
import com.datastax.driver.mapping.annotations.Column;
 | 
			
		||||
import com.datastax.driver.mapping.annotations.PartitionKey;
 | 
			
		||||
import com.datastax.driver.mapping.annotations.Table;
 | 
			
		||||
@ -32,14 +31,13 @@ import org.thingsboard.server.dao.model.type.JsonCodec;
 | 
			
		||||
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
 | 
			
		||||
import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_CUSTOMER_ID_PROPERTY;
 | 
			
		||||
import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_TENANT_ID_PROPERTY;
 | 
			
		||||
import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_TYPE_PROPERTY;
 | 
			
		||||
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_ADDITIONAL_INFO_PROPERTY;
 | 
			
		||||
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_CLOUD_ENDPOINT_KEY_PROPERTY;
 | 
			
		||||
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_COLUMN_FAMILY_NAME;
 | 
			
		||||
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_CONFIGURATION_PROPERTY;
 | 
			
		||||
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_CUSTOMER_ID_PROPERTY;
 | 
			
		||||
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_LABEL_PROPERTY;
 | 
			
		||||
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_LICENSE_KEY_PROPERTY;
 | 
			
		||||
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_NAME_PROPERTY;
 | 
			
		||||
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_ROOT_RULE_CHAIN_ID_PROPERTY;
 | 
			
		||||
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_ROUTING_KEY_PROPERTY;
 | 
			
		||||
@ -84,6 +82,12 @@ public class EdgeEntity implements SearchTextEntity<Edge> {
 | 
			
		||||
    @Column(name = EDGE_ROUTING_KEY_PROPERTY)
 | 
			
		||||
    private String routingKey;
 | 
			
		||||
 | 
			
		||||
    @Column(name = EDGE_LICENSE_KEY_PROPERTY)
 | 
			
		||||
    private String edgeLicenseKey;
 | 
			
		||||
 | 
			
		||||
    @Column(name = EDGE_CLOUD_ENDPOINT_KEY_PROPERTY)
 | 
			
		||||
    private String cloudEndpoint;
 | 
			
		||||
 | 
			
		||||
    @Column(name = EDGE_SECRET_PROPERTY)
 | 
			
		||||
    private String secret;
 | 
			
		||||
 | 
			
		||||
@ -125,6 +129,8 @@ public class EdgeEntity implements SearchTextEntity<Edge> {
 | 
			
		||||
        this.label = edge.getLabel();
 | 
			
		||||
        this.routingKey = edge.getRoutingKey();
 | 
			
		||||
        this.secret = edge.getSecret();
 | 
			
		||||
        this.edgeLicenseKey = edge.getEdgeLicenseKey();
 | 
			
		||||
        this.cloudEndpoint = edge.getCloudEndpoint();
 | 
			
		||||
        this.configuration = edge.getConfiguration();
 | 
			
		||||
        this.additionalInfo = edge.getAdditionalInfo();
 | 
			
		||||
    }
 | 
			
		||||
@ -152,6 +158,8 @@ public class EdgeEntity implements SearchTextEntity<Edge> {
 | 
			
		||||
        edge.setLabel(label);
 | 
			
		||||
        edge.setRoutingKey(routingKey);
 | 
			
		||||
        edge.setSecret(secret);
 | 
			
		||||
        edge.setEdgeLicenseKey(edgeLicenseKey);
 | 
			
		||||
        edge.setCloudEndpoint(cloudEndpoint);
 | 
			
		||||
        edge.setConfiguration(configuration);
 | 
			
		||||
        edge.setAdditionalInfo(additionalInfo);
 | 
			
		||||
        return edge;
 | 
			
		||||
 | 
			
		||||
@ -36,9 +36,11 @@ import javax.persistence.Column;
 | 
			
		||||
import javax.persistence.Entity;
 | 
			
		||||
import javax.persistence.Table;
 | 
			
		||||
 | 
			
		||||
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_CLOUD_ENDPOINT_KEY_PROPERTY;
 | 
			
		||||
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_COLUMN_FAMILY_NAME;
 | 
			
		||||
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_CUSTOMER_ID_PROPERTY;
 | 
			
		||||
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_LABEL_PROPERTY;
 | 
			
		||||
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_LICENSE_KEY_PROPERTY;
 | 
			
		||||
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_NAME_PROPERTY;
 | 
			
		||||
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_ROOT_RULE_CHAIN_ID_PROPERTY;
 | 
			
		||||
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_ROUTING_KEY_PROPERTY;
 | 
			
		||||
@ -81,6 +83,12 @@ public class EdgeEntity extends BaseSqlEntity<Edge> implements SearchTextEntity<
 | 
			
		||||
    @Column(name = EDGE_SECRET_PROPERTY)
 | 
			
		||||
    private String secret;
 | 
			
		||||
 | 
			
		||||
    @Column(name = EDGE_LICENSE_KEY_PROPERTY)
 | 
			
		||||
    private String edgeLicenseKey;
 | 
			
		||||
 | 
			
		||||
    @Column(name = EDGE_CLOUD_ENDPOINT_KEY_PROPERTY)
 | 
			
		||||
    private String cloudEndpoint;
 | 
			
		||||
 | 
			
		||||
    @Type(type = "json")
 | 
			
		||||
    @Column(name = ModelConstants.EDGE_CONFIGURATION_PROPERTY)
 | 
			
		||||
    private JsonNode configuration;
 | 
			
		||||
@ -111,6 +119,8 @@ public class EdgeEntity extends BaseSqlEntity<Edge> implements SearchTextEntity<
 | 
			
		||||
        this.label = edge.getLabel();
 | 
			
		||||
        this.routingKey = edge.getRoutingKey();
 | 
			
		||||
        this.secret = edge.getSecret();
 | 
			
		||||
        this.edgeLicenseKey = edge.getEdgeLicenseKey();
 | 
			
		||||
        this.cloudEndpoint = edge.getCloudEndpoint();
 | 
			
		||||
        this.configuration = edge.getConfiguration();
 | 
			
		||||
        this.additionalInfo = edge.getAdditionalInfo();
 | 
			
		||||
    }
 | 
			
		||||
@ -147,6 +157,8 @@ public class EdgeEntity extends BaseSqlEntity<Edge> implements SearchTextEntity<
 | 
			
		||||
        edge.setLabel(label);
 | 
			
		||||
        edge.setRoutingKey(routingKey);
 | 
			
		||||
        edge.setSecret(secret);
 | 
			
		||||
        edge.setEdgeLicenseKey(edgeLicenseKey);
 | 
			
		||||
        edge.setCloudEndpoint(cloudEndpoint);
 | 
			
		||||
        edge.setConfiguration(configuration);
 | 
			
		||||
        edge.setAdditionalInfo(additionalInfo);
 | 
			
		||||
        return edge;
 | 
			
		||||
 | 
			
		||||
@ -737,6 +737,8 @@ CREATE TABLE IF NOT EXISTS thingsboard.edge (
 | 
			
		||||
    search_text text,
 | 
			
		||||
    routing_key text,
 | 
			
		||||
    secret text,
 | 
			
		||||
    edge_license_key text,
 | 
			
		||||
    cloud_endpoint text,
 | 
			
		||||
    configuration text,
 | 
			
		||||
    additional_info text,
 | 
			
		||||
    PRIMARY KEY (id, tenant_id, customer_id, type)
 | 
			
		||||
 | 
			
		||||
@ -265,6 +265,8 @@ CREATE TABLE IF NOT EXISTS edge (
 | 
			
		||||
    label varchar(255),
 | 
			
		||||
    routing_key varchar(255),
 | 
			
		||||
    secret varchar(255),
 | 
			
		||||
    edge_license_key varchar(30),
 | 
			
		||||
    cloud_endpoint varchar(255),
 | 
			
		||||
    search_text varchar(255),
 | 
			
		||||
    tenant_id varchar(31),
 | 
			
		||||
    CONSTRAINT edge_name_unq_key UNIQUE (tenant_id, name),
 | 
			
		||||
 | 
			
		||||
@ -265,6 +265,8 @@ CREATE TABLE IF NOT EXISTS edge (
 | 
			
		||||
    label varchar(255),
 | 
			
		||||
    routing_key varchar(255),
 | 
			
		||||
    secret varchar(255),
 | 
			
		||||
    edge_license_key varchar(30),
 | 
			
		||||
    cloud_endpoint varchar(255),
 | 
			
		||||
    search_text varchar(255),
 | 
			
		||||
    tenant_id varchar(31),
 | 
			
		||||
    CONSTRAINT edge_name_unq_key UNIQUE (tenant_id, name),
 | 
			
		||||
 | 
			
		||||
@ -358,6 +358,8 @@ public abstract class BaseDashboardServiceTest extends AbstractServiceTest {
 | 
			
		||||
        edge.setType("default");
 | 
			
		||||
        edge.setSecret(RandomStringUtils.randomAlphanumeric(20));
 | 
			
		||||
        edge.setRoutingKey(RandomStringUtils.randomAlphanumeric(20));
 | 
			
		||||
        edge.setEdgeLicenseKey(RandomStringUtils.randomAlphanumeric(20));
 | 
			
		||||
        edge.setCloudEndpoint("http://localhost:8080");
 | 
			
		||||
        edge = edgeService.saveEdge(edge);
 | 
			
		||||
        try {
 | 
			
		||||
            dashboardService.assignDashboardToEdge(tenantId, dashboard.getId(), edge.getId());
 | 
			
		||||
 | 
			
		||||
@ -590,6 +590,8 @@ public abstract class BaseEdgeServiceTest extends AbstractServiceTest {
 | 
			
		||||
        edge.setType(type);
 | 
			
		||||
        edge.setSecret(RandomStringUtils.randomAlphanumeric(20));
 | 
			
		||||
        edge.setRoutingKey(RandomStringUtils.randomAlphanumeric(20));
 | 
			
		||||
        edge.setEdgeLicenseKey(RandomStringUtils.randomAlphanumeric(20));
 | 
			
		||||
        edge.setCloudEndpoint("http://localhost:8080");
 | 
			
		||||
        return edge;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -33,7 +33,7 @@ export default angular.module('thingsboard.utils', [thingsboardTypes])
 | 
			
		||||
const varsRegex = /\$\{([^}]*)\}/g;
 | 
			
		||||
 | 
			
		||||
/*@ngInject*/
 | 
			
		||||
function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, types) {
 | 
			
		||||
function Utils($mdColorPalette, $rootScope, $window, $location, $translate, $q, $timeout, types) {
 | 
			
		||||
 | 
			
		||||
    var predefinedFunctions = {},
 | 
			
		||||
        predefinedFunctionsList = [],
 | 
			
		||||
@ -142,6 +142,7 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t
 | 
			
		||||
        guid: guid,
 | 
			
		||||
        cleanCopy: cleanCopy,
 | 
			
		||||
        isLocalUrl: isLocalUrl,
 | 
			
		||||
        baseUrl: baseUrl,
 | 
			
		||||
        validateDatasources: validateDatasources,
 | 
			
		||||
        createKey: createKey,
 | 
			
		||||
        createAdditionalDataKey: createAdditionalDataKey,
 | 
			
		||||
@ -435,6 +436,15 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function baseUrl() {
 | 
			
		||||
        var url = $location.protocol() + '://' + $location.host();
 | 
			
		||||
        var port = $location.port();
 | 
			
		||||
        if (port != 80 && port != 443) {
 | 
			
		||||
            url += ":" + port;
 | 
			
		||||
        }
 | 
			
		||||
        return url;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function validateDatasources(datasources) {
 | 
			
		||||
        datasources.forEach(function (datasource) {
 | 
			
		||||
            if (datasource.type === 'device') {
 | 
			
		||||
 | 
			
		||||
@ -83,6 +83,20 @@
 | 
			
		||||
            <label translate>edge.description</label>
 | 
			
		||||
            <textarea ng-model="edge.additionalInfo.description" rows="2"></textarea>
 | 
			
		||||
        </md-input-container>
 | 
			
		||||
        <md-input-container class="md-block">
 | 
			
		||||
            <label translate>edge.edge-license-key</label>
 | 
			
		||||
            <input required name="edgeLicenseKey" ng-model="edge.edgeLicenseKey">
 | 
			
		||||
            <div ng-messages="theForm.edgeLicenseKey.$error">
 | 
			
		||||
                <div translate ng-message="required">edge.edge-license-key-required</div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </md-input-container>
 | 
			
		||||
        <md-input-container class="md-block">
 | 
			
		||||
            <label translate>edge.cloud-endpoint</label>
 | 
			
		||||
            <input required name="cloudEndpoint" ng-model="edge.cloudEndpoint">
 | 
			
		||||
            <div ng-messages="theForm.cloudEndpoint.$error">
 | 
			
		||||
                <div translate ng-message="required">edge.cloud-endpoint-required</div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </md-input-container>
 | 
			
		||||
    </fieldset>
 | 
			
		||||
    <div layout="row">
 | 
			
		||||
        <md-input-container class="md-block" flex>
 | 
			
		||||
 | 
			
		||||
@ -35,6 +35,7 @@ export default function EdgeDirective($compile, $templateCache, $translate, $mdD
 | 
			
		||||
                if (!scope.edge.id) {
 | 
			
		||||
                    scope.edge.routingKey = utils.guid('');
 | 
			
		||||
                    scope.edge.secret = generateSecret(20);
 | 
			
		||||
                    scope.edge.cloudEndpoint = utils.baseUrl();
 | 
			
		||||
                }
 | 
			
		||||
                if (scope.edge.customerId && scope.edge.customerId.id !== types.id.nullUid) {
 | 
			
		||||
                    scope.isAssignedToCustomer = true;
 | 
			
		||||
 | 
			
		||||
@ -747,6 +747,10 @@
 | 
			
		||||
    "delete-edges-text": "Vorsicht, nach Bestätigung werden alle ausgewählten Rand entfernt und alle zugehörigen Daten werden nicht wiederhergestellt.",
 | 
			
		||||
    "name": "Name",
 | 
			
		||||
    "name-required": "Name ist erforderlich.",
 | 
			
		||||
    "edge-license-key": "Edge Lizenzschlüssel",
 | 
			
		||||
    "edge-license-key-required": "Edge Lizenzschlüssel ist erforderlich.",
 | 
			
		||||
    "cloud-endpoint": "Cloud-Endpunkt",
 | 
			
		||||
    "cloud-endpoint-required": "Cloud-Endpunkt ist erforderlich.",
 | 
			
		||||
    "description": "Beschreibung",
 | 
			
		||||
    "events": "Ereignisse",
 | 
			
		||||
    "details": "Details",
 | 
			
		||||
 | 
			
		||||
@ -778,6 +778,10 @@
 | 
			
		||||
        "delete-edges-text": "Be careful, after the confirmation all selected edges will be removed and all related data will become unrecoverable.",
 | 
			
		||||
        "name": "Name",
 | 
			
		||||
        "name-required": "Name is required.",
 | 
			
		||||
        "edge-license-key": "Edge License Key",
 | 
			
		||||
        "edge-license-key-required": "Edge License Key is required.",
 | 
			
		||||
        "cloud-endpoint": "Cloud Endpoint",
 | 
			
		||||
        "cloud-endpoint-required": "Cloud Endpoint is required.",
 | 
			
		||||
        "description": "Description",
 | 
			
		||||
        "entity-info": "Entity info",
 | 
			
		||||
        "details": "Details",
 | 
			
		||||
 | 
			
		||||
@ -760,6 +760,10 @@
 | 
			
		||||
        "delete-edges-text": "Tenga cuidado, después de la confirmación se eliminarán todos los bordes seleccionados y todos los datos relacionados se volverán irrecuperables",
 | 
			
		||||
        "name": "Nombre",
 | 
			
		||||
        "name-required": "Se requiere nombre",
 | 
			
		||||
        "edge-license-key": "Edge Clave de licencia",
 | 
			
		||||
        "edge-license-key-required": "Se requiere edge clave de licencia",
 | 
			
		||||
        "cloud-endpoint": "Punto final de la nube",
 | 
			
		||||
        "cloud-endpoint-required": "Se requiere punto final de la nube",
 | 
			
		||||
        "description": "Descripción",
 | 
			
		||||
        "events": "Eventos",
 | 
			
		||||
        "details": "Detalles",
 | 
			
		||||
 | 
			
		||||
@ -765,6 +765,10 @@
 | 
			
		||||
        "delete-edges-text": "Faites attention, après la confirmation, tous les bordures sélectionnés seront supprimés et toutes les données associées deviendront irrécupérables.",
 | 
			
		||||
        "name": "Nom",
 | 
			
		||||
        "name-required": "Le nom de la bordure est requis",
 | 
			
		||||
        "edge-license-key": "Edge Clé de licence",
 | 
			
		||||
        "edge-license-key-required": "La edge clé de licence est requise",
 | 
			
		||||
        "cloud-endpoint": "Clé de licence",
 | 
			
		||||
        "cloud-endpoint-required": "La clé de licence est requise",
 | 
			
		||||
        "description": "Dispositifs",
 | 
			
		||||
        "events": "Événements",
 | 
			
		||||
        "details": "Détails de l'entité",
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user