Commit d3bda85a9333e02cc463cad8e8a1535cd5b3caf1

Authored by Volodymyr Babak
1 parent 4327f4d3

Added routing key and secret

@@ -89,6 +89,10 @@ @@ -89,6 +89,10 @@
89 <artifactId>queue</artifactId> 89 <artifactId>queue</artifactId>
90 </dependency> 90 </dependency>
91 <dependency> 91 <dependency>
  92 + <groupId>org.thingsboard.common</groupId>
  93 + <artifactId>edge-api</artifactId>
  94 + </dependency>
  95 + <dependency>
92 <groupId>org.thingsboard</groupId> 96 <groupId>org.thingsboard</groupId>
93 <artifactId>dao</artifactId> 97 <artifactId>dao</artifactId>
94 <type>test-jar</type> 98 <type>test-jar</type>
  1 +/**
  2 + * Copyright © 2016-2019 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.edge.rpc;
  17 +
  18 +import com.google.common.io.Resources;
  19 +import io.grpc.Server;
  20 +import io.grpc.ServerBuilder;
  21 +import io.grpc.stub.StreamObserver;
  22 +import lombok.extern.slf4j.Slf4j;
  23 +import org.springframework.beans.factory.annotation.Value;
  24 +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
  25 +import org.springframework.stereotype.Service;
  26 +import org.thingsboard.server.common.edge.gen.EdgeProtos;
  27 +import org.thingsboard.server.common.edge.gen.EdgeRpcServiceGrpc;
  28 +
  29 +import javax.annotation.PostConstruct;
  30 +import javax.annotation.PreDestroy;
  31 +import java.io.File;
  32 +import java.io.IOException;
  33 +
  34 +@Service
  35 +@Slf4j
  36 +@ConditionalOnProperty(prefix = "edges.rpc", value = "enabled", havingValue = "true")
  37 +public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase {
  38 +
  39 + @Value("${edges.rpc.port}")
  40 + private int rpcPort;
  41 + @Value("${edges.rpc.ssl.enabled}")
  42 + private boolean sslEnabled;
  43 + @Value("${edges.rpc.ssl.cert}")
  44 + private String certFileResource;
  45 + @Value("${edges.rpc.ssl.privateKey}")
  46 + private String privateKeyResource;
  47 +
  48 + private Server server;
  49 +
  50 + @PostConstruct
  51 + public void init() {
  52 + log.info("Initializing Edge RPC service!");
  53 + ServerBuilder builder = ServerBuilder.forPort(rpcPort).addService(this);
  54 + if (sslEnabled) {
  55 + try {
  56 + File certFile = new File(Resources.getResource(certFileResource).toURI());
  57 + File privateKeyFile = new File(Resources.getResource(privateKeyResource).toURI());
  58 + builder.useTransportSecurity(certFile, privateKeyFile);
  59 + } catch (Exception e) {
  60 + log.error("Unable to set up SSL context. Reason: " + e.getMessage(), e);
  61 + throw new RuntimeException("Unable to set up SSL context!", e);
  62 + }
  63 + }
  64 + server = builder.build();
  65 + log.info("Going to start Edge RPC server using port: {}", rpcPort);
  66 + try {
  67 + server.start();
  68 + } catch (IOException e) {
  69 + log.error("Failed to start Edge RPC server!", e);
  70 + throw new RuntimeException("Failed to start Edge RPC server!");
  71 + }
  72 + log.info("Edge RPC service initialized!");
  73 + }
  74 +
  75 +
  76 + @PreDestroy
  77 + public void destroy() {
  78 + if (server != null) {
  79 + server.shutdownNow();
  80 + }
  81 + }
  82 +
  83 + @Override
  84 + public StreamObserver<EdgeProtos.UplinkMsg> sendUplink(StreamObserver<EdgeProtos.DownlinkMsg> responseObserver) {
  85 + log.info("sendUplink [{}]", responseObserver);
  86 + return new StreamObserver<EdgeProtos.UplinkMsg>() {
  87 +
  88 + @Override
  89 + public void onNext(EdgeProtos.UplinkMsg uplinkMsg) {
  90 + log.info("onNext [{}]", uplinkMsg);
  91 + }
  92 +
  93 + @Override
  94 + public void onError(Throwable throwable) {
  95 + log.info("onError", throwable);
  96 + }
  97 +
  98 + @Override
  99 + public void onCompleted() {
  100 + log.info("onCompleted");
  101 + }
  102 + };
  103 + }
  104 +}
@@ -495,6 +495,17 @@ transport: @@ -495,6 +495,17 @@ transport:
495 bind_port: "${COAP_BIND_PORT:5683}" 495 bind_port: "${COAP_BIND_PORT:5683}"
496 timeout: "${COAP_TIMEOUT:10000}" 496 timeout: "${COAP_TIMEOUT:10000}"
497 497
  498 +# Edges parameters
  499 +edges:
  500 + rpc:
  501 + enabled: "${EDGES_RPC_ENABLED:true}"
  502 + port: "${EDGES_RPC_PORT:60061}"
  503 + ssl:
  504 + # Enable/disable SSL support
  505 + enabled: "${EDGES_RPC_SSL_ENABLED:false}"
  506 + cert: "${EDGES_RPC_SSL_CERT:certChainFile.pem}"
  507 + privateKey: "${EDGES_RPC_SSL_PRIVATE_KEY:privateKeyFile.pem}"
  508 +
498 swagger: 509 swagger:
499 api_path_regex: "${SWAGGER_API_PATH_REGEX:/api.*}" 510 api_path_regex: "${SWAGGER_API_PATH_REGEX:/api.*}"
500 security_path_regex: "${SWAGGER_SECURITY_PATH_REGEX:/api.*}" 511 security_path_regex: "${SWAGGER_SECURITY_PATH_REGEX:/api.*}"
@@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.page.TextPageData; @@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.page.TextPageData;
26 import org.thingsboard.server.common.data.page.TextPageLink; 26 import org.thingsboard.server.common.data.page.TextPageLink;
27 27
28 import java.util.List; 28 import java.util.List;
  29 +import java.util.Optional;
29 30
30 public interface EdgeService { 31 public interface EdgeService {
31 32
@@ -35,6 +36,8 @@ public interface EdgeService { @@ -35,6 +36,8 @@ public interface EdgeService {
35 36
36 Edge findEdgeByTenantIdAndName(TenantId tenantId, String name); 37 Edge findEdgeByTenantIdAndName(TenantId tenantId, String name);
37 38
  39 + Optional<Edge> findEdgeByRoutingKey(TenantId tenantId, String routingKey);
  40 +
38 Edge saveEdge(Edge edge); 41 Edge saveEdge(Edge edge);
39 42
40 Edge assignEdgeToCustomer(TenantId tenantId, EdgeId edgeId, CustomerId customerId); 43 Edge assignEdgeToCustomer(TenantId tenantId, EdgeId edgeId, CustomerId customerId);
@@ -44,6 +44,8 @@ public class Edge extends SearchTextBasedWithAdditionalInfo<EdgeId> implements H @@ -44,6 +44,8 @@ public class Edge extends SearchTextBasedWithAdditionalInfo<EdgeId> implements H
44 private String name; 44 private String name;
45 private String type; 45 private String type;
46 private String label; 46 private String label;
  47 + private String routingKey;
  48 + private String secret;
47 private transient JsonNode configuration; 49 private transient JsonNode configuration;
48 50
49 public Edge() { 51 public Edge() {
@@ -60,6 +62,8 @@ public class Edge extends SearchTextBasedWithAdditionalInfo<EdgeId> implements H @@ -60,6 +62,8 @@ public class Edge extends SearchTextBasedWithAdditionalInfo<EdgeId> implements H
60 this.customerId = edge.getCustomerId(); 62 this.customerId = edge.getCustomerId();
61 this.type = edge.getType(); 63 this.type = edge.getType();
62 this.name = edge.getName(); 64 this.name = edge.getName();
  65 + this.routingKey = edge.getRoutingKey();
  66 + this.secret = edge.getSecret();
63 this.configuration = edge.getConfiguration(); 67 this.configuration = edge.getConfiguration();
64 } 68 }
65 69
  1 +<!--
  2 +
  3 + Copyright © 2016-2019 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  19 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  20 + <modelVersion>4.0.0</modelVersion>
  21 + <parent>
  22 + <groupId>org.thingsboard</groupId>
  23 + <version>2.4.1-SNAPSHOT</version>
  24 + <artifactId>common</artifactId>
  25 + </parent>
  26 + <groupId>org.thingsboard.common</groupId>
  27 + <artifactId>edge-api</artifactId>
  28 + <packaging>jar</packaging>
  29 +
  30 + <name>Thingsboard Server Remote Edge wrapper</name>
  31 + <url>https://thingsboard.io</url>
  32 +
  33 + <properties>
  34 + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  35 + <main.dir>${basedir}/../..</main.dir>
  36 + </properties>
  37 +
  38 + <dependencies>
  39 + <dependency>
  40 + <groupId>org.thingsboard.common</groupId>
  41 + <artifactId>data</artifactId>
  42 + </dependency>
  43 + <dependency>
  44 + <groupId>org.thingsboard.common</groupId>
  45 + <artifactId>message</artifactId>
  46 + </dependency>
  47 + <dependency>
  48 + <groupId>com.google.code.gson</groupId>
  49 + <artifactId>gson</artifactId>
  50 + </dependency>
  51 + <dependency>
  52 + <groupId>org.slf4j</groupId>
  53 + <artifactId>slf4j-api</artifactId>
  54 + </dependency>
  55 + <dependency>
  56 + <groupId>org.slf4j</groupId>
  57 + <artifactId>log4j-over-slf4j</artifactId>
  58 + </dependency>
  59 + <dependency>
  60 + <groupId>ch.qos.logback</groupId>
  61 + <artifactId>logback-core</artifactId>
  62 + </dependency>
  63 + <dependency>
  64 + <groupId>ch.qos.logback</groupId>
  65 + <artifactId>logback-classic</artifactId>
  66 + </dependency>
  67 + <dependency>
  68 + <groupId>org.springframework</groupId>
  69 + <artifactId>spring-context</artifactId>
  70 + </dependency>
  71 + <dependency>
  72 + <groupId>org.springframework.boot</groupId>
  73 + <artifactId>spring-boot-starter-web</artifactId>
  74 + </dependency>
  75 + <dependency>
  76 + <groupId>io.netty</groupId>
  77 + <artifactId>netty-all</artifactId>
  78 + <scope>provided</scope>
  79 + </dependency>
  80 + <dependency>
  81 + <groupId>com.google.guava</groupId>
  82 + <artifactId>guava</artifactId>
  83 + </dependency>
  84 + <dependency>
  85 + <groupId>io.grpc</groupId>
  86 + <artifactId>grpc-netty</artifactId>
  87 + <exclusions>
  88 + <exclusion>
  89 + <artifactId>netty-transport</artifactId>
  90 + <groupId>io.netty</groupId>
  91 + </exclusion>
  92 + <exclusion>
  93 + <artifactId>netty-common</artifactId>
  94 + <groupId>io.netty</groupId>
  95 + </exclusion>
  96 + </exclusions>
  97 + </dependency>
  98 + <dependency>
  99 + <groupId>io.grpc</groupId>
  100 + <artifactId>grpc-protobuf</artifactId>
  101 + </dependency>
  102 + <dependency>
  103 + <groupId>io.grpc</groupId>
  104 + <artifactId>grpc-stub</artifactId>
  105 + </dependency>
  106 + <dependency>
  107 + <groupId>com.google.protobuf</groupId>
  108 + <artifactId>protobuf-java</artifactId>
  109 + </dependency>
  110 + </dependencies>
  111 +
  112 + <build>
  113 + <plugins>
  114 + <plugin>
  115 + <groupId>org.xolstice.maven.plugins</groupId>
  116 + <artifactId>protobuf-maven-plugin</artifactId>
  117 + </plugin>
  118 + </plugins>
  119 + </build>
  120 +
  121 + <distributionManagement>
  122 + <repository>
  123 + <id>thingsboard-repo-deploy</id>
  124 + <name>ThingsBoard Repo Deployment</name>
  125 + <url>https://repo.thingsboard.io/artifactory/libs-release-public</url>
  126 + </repository>
  127 + </distributionManagement>
  128 +
  129 +</project>
  1 +/**
  2 + * Copyright © 2016-2019 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.edge.rpc;
  17 +
  18 +import com.google.common.io.Resources;
  19 +import io.grpc.ManagedChannel;
  20 +import io.grpc.netty.GrpcSslContexts;
  21 +import io.grpc.netty.NettyChannelBuilder;
  22 +import io.grpc.stub.StreamObserver;
  23 +import lombok.extern.slf4j.Slf4j;
  24 +import org.springframework.beans.factory.annotation.Value;
  25 +import org.springframework.stereotype.Service;
  26 +import org.thingsboard.server.common.edge.gen.EdgeProtos;
  27 +import org.thingsboard.server.common.edge.gen.EdgeRpcServiceGrpc;
  28 +
  29 +import javax.net.ssl.SSLException;
  30 +import java.io.File;
  31 +import java.net.URISyntaxException;
  32 +
  33 +@Service
  34 +@Slf4j
  35 +public class EdgeGrpcClient implements EdgeRpcClient {
  36 +
  37 + @Value("${cloud.rpc.host}")
  38 + private String rpcHost;
  39 + @Value("${cloud.rpc.port}")
  40 + private int rpcPort;
  41 + @Value("${cloud.rpc.timeout}")
  42 + private int timeoutSecs;
  43 + @Value("${cloud.rpc.ssl.enabled}")
  44 + private boolean sslEnabled;
  45 + @Value("${cloud.rpc.ssl.cert}")
  46 + private String certResource;
  47 +
  48 + private ManagedChannel channel;
  49 +
  50 + private StreamObserver<EdgeProtos.UplinkMsg> inputStream;
  51 +
  52 + @Override
  53 + public void connect() {
  54 + NettyChannelBuilder builder = NettyChannelBuilder.forAddress(rpcHost, rpcPort).usePlaintext();
  55 + if (sslEnabled) {
  56 + try {
  57 + builder.sslContext(GrpcSslContexts.forClient().trustManager(new File(Resources.getResource(certResource).toURI())).build());
  58 + } catch (URISyntaxException | SSLException e) {
  59 + log.error("Failed to initialize channel!", e);
  60 + throw new RuntimeException(e);
  61 + }
  62 + }
  63 + channel = builder.build();
  64 + EdgeRpcServiceGrpc.EdgeRpcServiceStub stub = EdgeRpcServiceGrpc.newStub(channel);
  65 + StreamObserver<EdgeProtos.DownlinkMsg> responseObserver = new StreamObserver<EdgeProtos.DownlinkMsg>() {
  66 + @Override
  67 + public void onNext(EdgeProtos.DownlinkMsg downlinkMsg) {
  68 + log.info("onNext [{}]", downlinkMsg);
  69 + }
  70 +
  71 + @Override
  72 + public void onError(Throwable throwable) {
  73 +
  74 + }
  75 +
  76 + @Override
  77 + public void onCompleted() {
  78 +
  79 + }
  80 + };
  81 + inputStream = stub.sendUplink(responseObserver);
  82 + inputStream.onNext(EdgeProtos.UplinkMsg.newBuilder().setMsgType(EdgeProtos.UplinkMsgType.DELETE_DEVICE_MESSAGE).build());
  83 + }
  84 +}
  1 +/**
  2 + * Copyright © 2016-2019 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.edge.rpc;
  17 +
  18 +public interface EdgeRpcClient {
  19 +
  20 + void connect();
  21 +}
  1 +/**
  2 + * Copyright © 2016-2019 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +syntax = "proto3";
  17 +
  18 +option java_package = "org.thingsboard.server.common.edge.gen";
  19 +option java_outer_classname = "EdgeProtos";
  20 +
  21 +package edge;
  22 +
  23 +// Interface exported by the ThingsBoard PRC Edge.
  24 +service EdgeRpcService {
  25 +
  26 + rpc sendUplink(stream UplinkMsg) returns (stream DownlinkMsg) {}
  27 +
  28 +}
  29 +
  30 +/**
  31 + * Data Structures;
  32 + */
  33 +message UplinkMsg {
  34 + UplinkMsgType msgType = 1;
  35 +}
  36 +
  37 +message DownlinkMsg {
  38 + DownlinkMsgType msgType = 1;
  39 +}
  40 +
  41 +enum UplinkMsgType {
  42 + SAVE_DEVICE_MESSAGE = 0;
  43 + DELETE_DEVICE_MESSAGE = 1;
  44 +}
  45 +
  46 +enum DownlinkMsgType {
  47 + SAVE_ENTITY_MESSAGE = 0;
  48 + DELETE_ENTITY_MESSAGE = 1;
  49 +}
@@ -40,6 +40,7 @@ @@ -40,6 +40,7 @@
40 <module>queue</module> 40 <module>queue</module>
41 <module>transport</module> 41 <module>transport</module>
42 <module>dao-api</module> 42 <module>dao-api</module>
  43 + <module>edge-api</module>
43 </modules> 44 </modules>
44 45
45 </project> 46 </project>
@@ -46,6 +46,7 @@ import org.thingsboard.server.dao.entity.AbstractEntityService; @@ -46,6 +46,7 @@ import org.thingsboard.server.dao.entity.AbstractEntityService;
46 import org.thingsboard.server.dao.exception.DataValidationException; 46 import org.thingsboard.server.dao.exception.DataValidationException;
47 import org.thingsboard.server.dao.service.DataValidator; 47 import org.thingsboard.server.dao.service.DataValidator;
48 import org.thingsboard.server.dao.service.PaginatedRemover; 48 import org.thingsboard.server.dao.service.PaginatedRemover;
  49 +import org.thingsboard.server.dao.service.Validator;
49 import org.thingsboard.server.dao.tenant.TenantDao; 50 import org.thingsboard.server.dao.tenant.TenantDao;
50 51
51 import javax.annotation.Nullable; 52 import javax.annotation.Nullable;
@@ -111,6 +112,13 @@ public class BaseEdgeService extends AbstractEntityService implements EdgeServic @@ -111,6 +112,13 @@ public class BaseEdgeService extends AbstractEntityService implements EdgeServic
111 return edgeOpt.orElse(null); 112 return edgeOpt.orElse(null);
112 } 113 }
113 114
  115 + @Override
  116 + public Optional<Edge> findEdgeByRoutingKey(TenantId tenantId, String routingKey) {
  117 + log.trace("Executing findEdgeByRoutingKey [{}]", routingKey);
  118 + Validator.validateString(routingKey, "Incorrect edge routingKey for search request.");
  119 + return edgeDao.findByRoutingKey(tenantId.getId(), routingKey);
  120 + }
  121 +
114 @CacheEvict(cacheNames = EDGE_CACHE, key = "{#edge.tenantId, #edge.name}") 122 @CacheEvict(cacheNames = EDGE_CACHE, key = "{#edge.tenantId, #edge.name}")
115 @Override 123 @Override
116 public Edge saveEdge(Edge edge) { 124 public Edge saveEdge(Edge edge) {
@@ -86,4 +86,9 @@ public class CassandraEdgeDao extends CassandraAbstractSearchTextDao<EdgeEntity, @@ -86,4 +86,9 @@ public class CassandraEdgeDao extends CassandraAbstractSearchTextDao<EdgeEntity,
86 public ListenableFuture<List<EntitySubtype>> findTenantEdgeTypesAsync(UUID tenantId) { 86 public ListenableFuture<List<EntitySubtype>> findTenantEdgeTypesAsync(UUID tenantId) {
87 return null; 87 return null;
88 } 88 }
  89 +
  90 + @Override
  91 + public Optional<Edge> findByRoutingKey(UUID tenantId, String routingKey) {
  92 + return Optional.empty();
  93 + }
89 } 94 }
@@ -116,5 +116,12 @@ public interface EdgeDao extends Dao<Edge> { @@ -116,5 +116,12 @@ public interface EdgeDao extends Dao<Edge> {
116 */ 116 */
117 ListenableFuture<List<EntitySubtype>> findTenantEdgeTypesAsync(UUID tenantId); 117 ListenableFuture<List<EntitySubtype>> findTenantEdgeTypesAsync(UUID tenantId);
118 118
  119 + /**
  120 + * Find edge by routing Key.
  121 + *
  122 + * @param routingKey the edge routingKey
  123 + * @return the optional edge object
  124 + */
  125 + Optional<Edge> findByRoutingKey(UUID tenantId, String routingKey);
119 126
120 } 127 }
@@ -361,7 +361,8 @@ public class ModelConstants { @@ -361,7 +361,8 @@ public class ModelConstants {
361 public static final String EDGE_CONFIGURATION_PROPERTY = "configuration"; 361 public static final String EDGE_CONFIGURATION_PROPERTY = "configuration";
362 public static final String EDGE_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY; 362 public static final String EDGE_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY;
363 363
364 - public static final String EDGE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "edge_by_tenant_and_search_text"; 364 + public static final String EDGE_ROUTING_KEY_PROPERTY = "routing_key";
  365 + public static final String EDGE_SECRET_PROPERTY = "secret";
365 366
366 367
367 /** 368 /**
@@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at 6 * You may obtain a copy of the License at
7 * 7 *
8 - * http://www.apache.org/licenses/LICENSE-2.0 8 + * http://www.apache.org/licenses/LICENSE-2.0
9 * 9 *
10 * Unless required by applicable law or agreed to in writing, software 10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, 11 * distributed under the License is distributed on an "AS IS" BASIS,
@@ -37,6 +37,8 @@ import static org.thingsboard.server.dao.model.ModelConstants.EDGE_CONFIGURATION @@ -37,6 +37,8 @@ import static org.thingsboard.server.dao.model.ModelConstants.EDGE_CONFIGURATION
37 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_CUSTOMER_ID_PROPERTY; 37 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_CUSTOMER_ID_PROPERTY;
38 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_LABEL_PROPERTY; 38 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_LABEL_PROPERTY;
39 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_NAME_PROPERTY; 39 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_NAME_PROPERTY;
  40 +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_ROUTING_KEY_PROPERTY;
  41 +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_SECRET_PROPERTY;
40 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_TENANT_ID_PROPERTY; 42 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_TENANT_ID_PROPERTY;
41 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_TYPE_PROPERTY; 43 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_TYPE_PROPERTY;
42 import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; 44 import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY;
@@ -70,6 +72,12 @@ public class EdgeEntity implements SearchTextEntity<Edge> { @@ -70,6 +72,12 @@ public class EdgeEntity implements SearchTextEntity<Edge> {
70 @Column(name = SEARCH_TEXT_PROPERTY) 72 @Column(name = SEARCH_TEXT_PROPERTY)
71 private String searchText; 73 private String searchText;
72 74
  75 + @Column(name = EDGE_ROUTING_KEY_PROPERTY)
  76 + private String routingKey;
  77 +
  78 + @Column(name = EDGE_SECRET_PROPERTY)
  79 + private String secret;
  80 +
73 @Column(name = EDGE_CONFIGURATION_PROPERTY, codec = JsonCodec.class) 81 @Column(name = EDGE_CONFIGURATION_PROPERTY, codec = JsonCodec.class)
74 private JsonNode configuration; 82 private JsonNode configuration;
75 83
@@ -90,6 +98,8 @@ public class EdgeEntity implements SearchTextEntity<Edge> { @@ -90,6 +98,8 @@ public class EdgeEntity implements SearchTextEntity<Edge> {
90 this.type = edge.getType(); 98 this.type = edge.getType();
91 this.name = edge.getName(); 99 this.name = edge.getName();
92 this.label = edge.getLabel(); 100 this.label = edge.getLabel();
  101 + this.routingKey = edge.getRoutingKey();
  102 + this.secret = edge.getSecret();
93 this.configuration = edge.getConfiguration(); 103 this.configuration = edge.getConfiguration();
94 this.additionalInfo = edge.getAdditionalInfo(); 104 this.additionalInfo = edge.getAdditionalInfo();
95 } 105 }
@@ -112,6 +122,8 @@ public class EdgeEntity implements SearchTextEntity<Edge> { @@ -112,6 +122,8 @@ public class EdgeEntity implements SearchTextEntity<Edge> {
112 edge.setType(type); 122 edge.setType(type);
113 edge.setName(name); 123 edge.setName(name);
114 edge.setLabel(label); 124 edge.setLabel(label);
  125 + edge.setRoutingKey(routingKey);
  126 + edge.setSecret(secret);
115 edge.setConfiguration(configuration); 127 edge.setConfiguration(configuration);
116 edge.setAdditionalInfo(additionalInfo); 128 edge.setAdditionalInfo(additionalInfo);
117 return edge; 129 return edge;
@@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at 6 * You may obtain a copy of the License at
7 * 7 *
8 - * http://www.apache.org/licenses/LICENSE-2.0 8 + * http://www.apache.org/licenses/LICENSE-2.0
9 * 9 *
10 * Unless required by applicable law or agreed to in writing, software 10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, 11 * distributed under the License is distributed on an "AS IS" BASIS,
@@ -35,11 +35,12 @@ import javax.persistence.Column; @@ -35,11 +35,12 @@ import javax.persistence.Column;
35 import javax.persistence.Entity; 35 import javax.persistence.Entity;
36 import javax.persistence.Table; 36 import javax.persistence.Table;
37 37
38 -import static org.thingsboard.server.dao.model.ModelConstants.ASSET_CUSTOMER_ID_PROPERTY;  
39 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_COLUMN_FAMILY_NAME; 38 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_COLUMN_FAMILY_NAME;
40 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_CUSTOMER_ID_PROPERTY; 39 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_CUSTOMER_ID_PROPERTY;
41 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_LABEL_PROPERTY; 40 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_LABEL_PROPERTY;
42 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_NAME_PROPERTY; 41 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_NAME_PROPERTY;
  42 +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_ROUTING_KEY_PROPERTY;
  43 +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_SECRET_PROPERTY;
43 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_TENANT_ID_PROPERTY; 44 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_TENANT_ID_PROPERTY;
44 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_TYPE_PROPERTY; 45 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_TYPE_PROPERTY;
45 import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; 46 import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY;
@@ -69,6 +70,12 @@ public class EdgeEntity extends BaseSqlEntity<Edge> implements SearchTextEntity< @@ -69,6 +70,12 @@ public class EdgeEntity extends BaseSqlEntity<Edge> implements SearchTextEntity<
69 @Column(name = SEARCH_TEXT_PROPERTY) 70 @Column(name = SEARCH_TEXT_PROPERTY)
70 private String searchText; 71 private String searchText;
71 72
  73 + @Column(name = EDGE_ROUTING_KEY_PROPERTY)
  74 + private String routingKey;
  75 +
  76 + @Column(name = EDGE_SECRET_PROPERTY)
  77 + private String secret;
  78 +
72 @Type(type = "json") 79 @Type(type = "json")
73 @Column(name = ModelConstants.EDGE_CONFIGURATION_PROPERTY) 80 @Column(name = ModelConstants.EDGE_CONFIGURATION_PROPERTY)
74 private JsonNode configuration; 81 private JsonNode configuration;
@@ -94,6 +101,8 @@ public class EdgeEntity extends BaseSqlEntity<Edge> implements SearchTextEntity< @@ -94,6 +101,8 @@ public class EdgeEntity extends BaseSqlEntity<Edge> implements SearchTextEntity<
94 this.type = edge.getType(); 101 this.type = edge.getType();
95 this.name = edge.getName(); 102 this.name = edge.getName();
96 this.label = edge.getLabel(); 103 this.label = edge.getLabel();
  104 + this.routingKey = edge.getRoutingKey();
  105 + this.secret = edge.getSecret();
97 this.configuration = edge.getConfiguration(); 106 this.configuration = edge.getConfiguration();
98 this.additionalInfo = edge.getAdditionalInfo(); 107 this.additionalInfo = edge.getAdditionalInfo();
99 } 108 }
@@ -125,6 +134,8 @@ public class EdgeEntity extends BaseSqlEntity<Edge> implements SearchTextEntity< @@ -125,6 +134,8 @@ public class EdgeEntity extends BaseSqlEntity<Edge> implements SearchTextEntity<
125 edge.setType(type); 134 edge.setType(type);
126 edge.setName(name); 135 edge.setName(name);
127 edge.setLabel(label); 136 edge.setLabel(label);
  137 + edge.setRoutingKey(routingKey);
  138 + edge.setSecret(secret);
128 edge.setConfiguration(configuration); 139 edge.setConfiguration(configuration);
129 edge.setAdditionalInfo(additionalInfo); 140 edge.setAdditionalInfo(additionalInfo);
130 return edge; 141 return edge;
@@ -76,4 +76,5 @@ public interface EdgeRepository extends CrudRepository<EdgeEntity, String> { @@ -76,4 +76,5 @@ public interface EdgeRepository extends CrudRepository<EdgeEntity, String> {
76 76
77 List<EdgeEntity> findEdgesByTenantIdAndIdIn(String tenantId, List<String> edgeIds); 77 List<EdgeEntity> findEdgesByTenantIdAndIdIn(String tenantId, List<String> edgeIds);
78 78
  79 + EdgeEntity findByRoutingKey(String routingKey);
79 } 80 }
@@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at 6 * You may obtain a copy of the License at
7 * 7 *
8 - * http://www.apache.org/licenses/LICENSE-2.0 8 + * http://www.apache.org/licenses/LICENSE-2.0
9 * 9 *
10 * Unless required by applicable law or agreed to in writing, software 10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, 11 * distributed under the License is distributed on an "AS IS" BASIS,
@@ -126,6 +126,12 @@ public class JpaEdgeDao extends JpaAbstractSearchTextDao<EdgeEntity, Edge> imple @@ -126,6 +126,12 @@ public class JpaEdgeDao extends JpaAbstractSearchTextDao<EdgeEntity, Edge> imple
126 return service.submit(() -> convertTenantEdgeTypesToDto(tenantId, edgeRepository.findTenantEdgeTypes(fromTimeUUID(tenantId)))); 126 return service.submit(() -> convertTenantEdgeTypesToDto(tenantId, edgeRepository.findTenantEdgeTypes(fromTimeUUID(tenantId))));
127 } 127 }
128 128
  129 + @Override
  130 + public Optional<Edge> findByRoutingKey(UUID tenantId, String routingKey) {
  131 + Edge edge = DaoUtil.getData(edgeRepository.findByRoutingKey(routingKey));
  132 + return Optional.ofNullable(edge);
  133 + }
  134 +
129 private List<EntitySubtype> convertTenantEdgeTypesToDto(UUID tenantId, List<String> types) { 135 private List<EntitySubtype> convertTenantEdgeTypesToDto(UUID tenantId, List<String> types) {
130 List<EntitySubtype> list = Collections.emptyList(); 136 List<EntitySubtype> list = Collections.emptyList();
131 if (types != null && !types.isEmpty()) { 137 if (types != null && !types.isEmpty()) {
@@ -257,6 +257,8 @@ CREATE TABLE IF NOT EXISTS edge ( @@ -257,6 +257,8 @@ CREATE TABLE IF NOT EXISTS edge (
257 type varchar(255), 257 type varchar(255),
258 name varchar(255), 258 name varchar(255),
259 label varchar(255), 259 label varchar(255),
  260 + routing_key varchar(255),
  261 + secret varchar(255),
260 search_text varchar(255), 262 search_text varchar(255),
261 tenant_id varchar(31) 263 tenant_id varchar(31)
262 ); 264 );
@@ -407,6 +407,11 @@ @@ -407,6 +407,11 @@
407 <version>${project.version}</version> 407 <version>${project.version}</version>
408 </dependency> 408 </dependency>
409 <dependency> 409 <dependency>
  410 + <groupId>org.thingsboard.common</groupId>
  411 + <artifactId>edge-api</artifactId>
  412 + <version>${project.version}</version>
  413 + </dependency>
  414 + <dependency>
410 <groupId>org.thingsboard</groupId> 415 <groupId>org.thingsboard</groupId>
411 <artifactId>dao</artifactId> 416 <artifactId>dao</artifactId>
412 <version>${project.version}</version> 417 <version>${project.version}</version>
@@ -27,7 +27,7 @@ @@ -27,7 +27,7 @@
27 27
28 <div layout="row"> 28 <div layout="row">
29 <md-button ngclipboard data-clipboard-action="copy" 29 <md-button ngclipboard data-clipboard-action="copy"
30 - ngclipboard-success="onEdgeIdCopied(e)" 30 + ngclipboard-success="onEdgeIdCopied()"
31 data-clipboard-text="{{edge.id.id}}" ng-show="!isEdit" 31 data-clipboard-text="{{edge.id.id}}" ng-show="!isEdit"
32 class="md-raised"> 32 class="md-raised">
33 <md-icon md-svg-icon="mdi:clipboard-arrow-left"></md-icon> 33 <md-icon md-svg-icon="mdi:clipboard-arrow-left"></md-icon>
@@ -69,4 +69,34 @@ @@ -69,4 +69,34 @@
69 <textarea ng-model="edge.additionalInfo.description" rows="2"></textarea> 69 <textarea ng-model="edge.additionalInfo.description" rows="2"></textarea>
70 </md-input-container> 70 </md-input-container>
71 </fieldset> 71 </fieldset>
  72 + <div layout="row">
  73 + <md-input-container class="md-block" flex>
  74 + <label translate>edge.edge-key</label>
  75 + <input ng-model="edge.routingKey" disabled>
  76 + </md-input-container>
  77 + <md-button class="md-icon-button" style="margin-top: 14px;"
  78 + ngclipboard data-clipboard-action="copy"
  79 + ngclipboard-success="onEdgeInfoCopied('key')"
  80 + data-clipboard-text="{{edge.routingKey}}">
  81 + <md-icon md-svg-icon="mdi:clipboard-arrow-left"></md-icon>
  82 + <md-tooltip md-direction="top">
  83 + {{ 'edge.copy-edge-key' | translate }}
  84 + </md-tooltip>
  85 + </md-button>
  86 + </div>
  87 + <div layout="row">
  88 + <md-input-container class="md-block" flex>
  89 + <label translate>edge.edge-secret</label>
  90 + <input ng-model="edge.secret" disabled>
  91 + </md-input-container>
  92 + <md-button class="md-icon-button" style="margin-top: 14px;"
  93 + ngclipboard data-clipboard-action="copy"
  94 + data-clipboard-text="{{edge.secret}}"
  95 + ngclipboard-success="onEdgeInfoCopied('secret')">
  96 + <md-icon md-svg-icon="mdi:clipboard-arrow-left"></md-icon>
  97 + <md-tooltip md-direction="top">
  98 + {{ 'edge.copy-edge-secret' | translate }}
  99 + </md-tooltip>
  100 + </md-button>
  101 + </div>
72 </md-content> 102 </md-content>
@@ -20,7 +20,7 @@ import edgeFieldsetTemplate from './edge-fieldset.tpl.html'; @@ -20,7 +20,7 @@ import edgeFieldsetTemplate from './edge-fieldset.tpl.html';
20 /* eslint-enable import/no-unresolved, import/default */ 20 /* eslint-enable import/no-unresolved, import/default */
21 21
22 /*@ngInject*/ 22 /*@ngInject*/
23 -export default function EdgeDirective($compile, $templateCache, $translate, $mdDialog, $document, toast, types, customerService) { 23 +export default function EdgeDirective($compile, $templateCache, $translate, $mdDialog, $document, utils, toast, types, customerService) {
24 var linker = function (scope, element) { 24 var linker = function (scope, element) {
25 var template = $templateCache.get(edgeFieldsetTemplate); 25 var template = $templateCache.get(edgeFieldsetTemplate);
26 element.html(template); 26 element.html(template);
@@ -32,6 +32,10 @@ export default function EdgeDirective($compile, $templateCache, $translate, $mdD @@ -32,6 +32,10 @@ export default function EdgeDirective($compile, $templateCache, $translate, $mdD
32 32
33 scope.$watch('edge', function(newVal) { 33 scope.$watch('edge', function(newVal) {
34 if (newVal) { 34 if (newVal) {
  35 + if (!scope.edge.id) {
  36 + scope.edge.routingKey = utils.guid('');
  37 + scope.edge.secret = generateSecret(20);
  38 + }
35 if (scope.edge.customerId && scope.edge.customerId.id !== types.id.nullUid) { 39 if (scope.edge.customerId && scope.edge.customerId.id !== types.id.nullUid) {
36 scope.isAssignedToCustomer = true; 40 scope.isAssignedToCustomer = true;
37 customerService.getShortCustomerInfo(scope.edge.customerId.id).then( 41 customerService.getShortCustomerInfo(scope.edge.customerId.id).then(
@@ -48,12 +52,38 @@ export default function EdgeDirective($compile, $templateCache, $translate, $mdD @@ -48,12 +52,38 @@ export default function EdgeDirective($compile, $templateCache, $translate, $mdD
48 } 52 }
49 }); 53 });
50 54
  55 + function generateSecret(length) {
  56 + if (angular.isUndefined(length) || length == null) {
  57 + length = 1;
  58 + }
  59 + var l = length > 10 ? 10 : length;
  60 + var str = Math.random().toString(36).substr(2, l);
  61 + if(str.length >= length){
  62 + return str;
  63 + }
  64 + return str.concat(generateSecret(length - str.length));
  65 + }
  66 +
51 scope.onEdgeIdCopied = function() { 67 scope.onEdgeIdCopied = function() {
52 toast.showSuccess($translate.instant('edge.id-copied-message'), 750, angular.element(element).parent().parent(), 'bottom left'); 68 toast.showSuccess($translate.instant('edge.id-copied-message'), 750, angular.element(element).parent().parent(), 'bottom left');
53 }; 69 };
54 70
55 $compile(element.contents())(scope); 71 $compile(element.contents())(scope);
56 72
  73 + scope.onEdgeInfoCopied = function(type) {
  74 + let translateInstant = "";
  75 + switch (type) {
  76 + case 'key':
  77 + translateInstant = "edge.edge-key-copied-message";
  78 + break;
  79 + case 'secret':
  80 + translateInstant = "edge.edge-secret-copied-message";
  81 + break;
  82 + }
  83 + toast.showSuccess($translate.instant(translateInstant), 750, angular.element(element).parent().parent(), 'top left');
  84 + };
  85 +
  86 +
57 }; 87 };
58 return { 88 return {
59 restrict: "E", 89 restrict: "E",
@@ -602,6 +602,16 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, @@ -602,6 +602,16 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
602 } 602 }
603 ); 603 );
604 return deferred.promise; 604 return deferred.promise;
  605 + case types.entityType.edge:
  606 + openImportDialogCSV($event, entityType, 'edge.import', 'edge.edge-file').then(
  607 + function success() {
  608 + deferred.resolve();
  609 + },
  610 + function fail() {
  611 + deferred.reject();
  612 + }
  613 + );
  614 + return deferred.promise;
605 } 615 }
606 616
607 } 617 }
@@ -777,7 +777,13 @@ @@ -777,7 +777,13 @@
777 "unassign-from-edge": "Unassign from edge", 777 "unassign-from-edge": "Unassign from edge",
778 "dashboards": "Edge Dashboards", 778 "dashboards": "Edge Dashboards",
779 "manage-edge-rulechains": "Manage edge rule chains", 779 "manage-edge-rulechains": "Manage edge rule chains",
780 - "rulechains": "Edge Rule Chains" 780 + "rulechains": "Edge Rule Chains",
  781 + "edge-key": "Edge key",
  782 + "copy-edge-key": "Copy edge key",
  783 + "edge-key-copied-message": "Edge key has been copied to clipboard",
  784 + "edge-secret": "Edge secret",
  785 + "copy-edge-secret": "Copy edge secret",
  786 + "edge-secret-copied-message": "Edge secret has been copied to clipboard"
781 }, 787 },
782 "error": { 788 "error": {
783 "unable-to-connect": "Unable to connect to the server! Please check your internet connection.", 789 "unable-to-connect": "Unable to connect to the server! Please check your internet connection.",