diff --git a/application/pom.xml b/application/pom.xml index 24c3b6a950..21e9b566ce 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -88,6 +88,10 @@ org.thingsboard.common queue + + org.thingsboard.common + edge-api + org.thingsboard dao diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java new file mode 100644 index 0000000000..0844b2117b --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java @@ -0,0 +1,104 @@ +/** + * Copyright © 2016-2019 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc; + +import com.google.common.io.Resources; +import io.grpc.Server; +import io.grpc.ServerBuilder; +import io.grpc.stub.StreamObserver; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.edge.gen.EdgeProtos; +import org.thingsboard.server.common.edge.gen.EdgeRpcServiceGrpc; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.io.File; +import java.io.IOException; + +@Service +@Slf4j +@ConditionalOnProperty(prefix = "edges.rpc", value = "enabled", havingValue = "true") +public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase { + + @Value("${edges.rpc.port}") + private int rpcPort; + @Value("${edges.rpc.ssl.enabled}") + private boolean sslEnabled; + @Value("${edges.rpc.ssl.cert}") + private String certFileResource; + @Value("${edges.rpc.ssl.privateKey}") + private String privateKeyResource; + + private Server server; + + @PostConstruct + public void init() { + log.info("Initializing Edge RPC service!"); + ServerBuilder builder = ServerBuilder.forPort(rpcPort).addService(this); + if (sslEnabled) { + try { + File certFile = new File(Resources.getResource(certFileResource).toURI()); + File privateKeyFile = new File(Resources.getResource(privateKeyResource).toURI()); + builder.useTransportSecurity(certFile, privateKeyFile); + } catch (Exception e) { + log.error("Unable to set up SSL context. Reason: " + e.getMessage(), e); + throw new RuntimeException("Unable to set up SSL context!", e); + } + } + server = builder.build(); + log.info("Going to start Edge RPC server using port: {}", rpcPort); + try { + server.start(); + } catch (IOException e) { + log.error("Failed to start Edge RPC server!", e); + throw new RuntimeException("Failed to start Edge RPC server!"); + } + log.info("Edge RPC service initialized!"); + } + + + @PreDestroy + public void destroy() { + if (server != null) { + server.shutdownNow(); + } + } + + @Override + public StreamObserver sendUplink(StreamObserver responseObserver) { + log.info("sendUplink [{}]", responseObserver); + return new StreamObserver() { + + @Override + public void onNext(EdgeProtos.UplinkMsg uplinkMsg) { + log.info("onNext [{}]", uplinkMsg); + } + + @Override + public void onError(Throwable throwable) { + log.info("onError", throwable); + } + + @Override + public void onCompleted() { + log.info("onCompleted"); + } + }; + } +} diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 58fe7bfa9c..86e4548117 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -495,6 +495,17 @@ transport: bind_port: "${COAP_BIND_PORT:5683}" timeout: "${COAP_TIMEOUT:10000}" +# Edges parameters +edges: + rpc: + enabled: "${EDGES_RPC_ENABLED:true}" + port: "${EDGES_RPC_PORT:60061}" + ssl: + # Enable/disable SSL support + enabled: "${EDGES_RPC_SSL_ENABLED:false}" + cert: "${EDGES_RPC_SSL_CERT:certChainFile.pem}" + privateKey: "${EDGES_RPC_SSL_PRIVATE_KEY:privateKeyFile.pem}" + swagger: api_path_regex: "${SWAGGER_API_PATH_REGEX:/api.*}" security_path_regex: "${SWAGGER_SECURITY_PATH_REGEX:/api.*}" diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java index 60ea2919ee..5cc093fcad 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java @@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageLink; import java.util.List; +import java.util.Optional; public interface EdgeService { @@ -35,6 +36,8 @@ public interface EdgeService { Edge findEdgeByTenantIdAndName(TenantId tenantId, String name); + Optional findEdgeByRoutingKey(TenantId tenantId, String routingKey); + Edge saveEdge(Edge edge); Edge assignEdgeToCustomer(TenantId tenantId, EdgeId edgeId, CustomerId customerId); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/edge/Edge.java b/common/data/src/main/java/org/thingsboard/server/common/data/edge/Edge.java index 8d95ace9dc..93198dcb50 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/edge/Edge.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/edge/Edge.java @@ -44,6 +44,8 @@ public class Edge extends SearchTextBasedWithAdditionalInfo implements H private String name; private String type; private String label; + private String routingKey; + private String secret; private transient JsonNode configuration; public Edge() { @@ -60,6 +62,8 @@ public class Edge extends SearchTextBasedWithAdditionalInfo implements H this.customerId = edge.getCustomerId(); this.type = edge.getType(); this.name = edge.getName(); + this.routingKey = edge.getRoutingKey(); + this.secret = edge.getSecret(); this.configuration = edge.getConfiguration(); } diff --git a/common/edge-api/pom.xml b/common/edge-api/pom.xml new file mode 100644 index 0000000000..b5380fb87c --- /dev/null +++ b/common/edge-api/pom.xml @@ -0,0 +1,129 @@ + + + 4.0.0 + + org.thingsboard + 2.4.1-SNAPSHOT + common + + org.thingsboard.common + edge-api + jar + + Thingsboard Server Remote Edge wrapper + https://thingsboard.io + + + UTF-8 + ${basedir}/../.. + + + + + org.thingsboard.common + data + + + org.thingsboard.common + message + + + com.google.code.gson + gson + + + org.slf4j + slf4j-api + + + org.slf4j + log4j-over-slf4j + + + ch.qos.logback + logback-core + + + ch.qos.logback + logback-classic + + + org.springframework + spring-context + + + org.springframework.boot + spring-boot-starter-web + + + io.netty + netty-all + provided + + + com.google.guava + guava + + + io.grpc + grpc-netty + + + netty-transport + io.netty + + + netty-common + io.netty + + + + + io.grpc + grpc-protobuf + + + io.grpc + grpc-stub + + + com.google.protobuf + protobuf-java + + + + + + + org.xolstice.maven.plugins + protobuf-maven-plugin + + + + + + + thingsboard-repo-deploy + ThingsBoard Repo Deployment + https://repo.thingsboard.io/artifactory/libs-release-public + + + + diff --git a/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java b/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java new file mode 100644 index 0000000000..999a8a0e0f --- /dev/null +++ b/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java @@ -0,0 +1,84 @@ +/** + * Copyright © 2016-2019 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.edge.rpc; + +import com.google.common.io.Resources; +import io.grpc.ManagedChannel; +import io.grpc.netty.GrpcSslContexts; +import io.grpc.netty.NettyChannelBuilder; +import io.grpc.stub.StreamObserver; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.edge.gen.EdgeProtos; +import org.thingsboard.server.common.edge.gen.EdgeRpcServiceGrpc; + +import javax.net.ssl.SSLException; +import java.io.File; +import java.net.URISyntaxException; + +@Service +@Slf4j +public class EdgeGrpcClient implements EdgeRpcClient { + + @Value("${cloud.rpc.host}") + private String rpcHost; + @Value("${cloud.rpc.port}") + private int rpcPort; + @Value("${cloud.rpc.timeout}") + private int timeoutSecs; + @Value("${cloud.rpc.ssl.enabled}") + private boolean sslEnabled; + @Value("${cloud.rpc.ssl.cert}") + private String certResource; + + private ManagedChannel channel; + + private StreamObserver inputStream; + + @Override + public void connect() { + NettyChannelBuilder builder = NettyChannelBuilder.forAddress(rpcHost, rpcPort).usePlaintext(); + if (sslEnabled) { + try { + builder.sslContext(GrpcSslContexts.forClient().trustManager(new File(Resources.getResource(certResource).toURI())).build()); + } catch (URISyntaxException | SSLException e) { + log.error("Failed to initialize channel!", e); + throw new RuntimeException(e); + } + } + channel = builder.build(); + EdgeRpcServiceGrpc.EdgeRpcServiceStub stub = EdgeRpcServiceGrpc.newStub(channel); + StreamObserver responseObserver = new StreamObserver() { + @Override + public void onNext(EdgeProtos.DownlinkMsg downlinkMsg) { + log.info("onNext [{}]", downlinkMsg); + } + + @Override + public void onError(Throwable throwable) { + + } + + @Override + public void onCompleted() { + + } + }; + inputStream = stub.sendUplink(responseObserver); + inputStream.onNext(EdgeProtos.UplinkMsg.newBuilder().setMsgType(EdgeProtos.UplinkMsgType.DELETE_DEVICE_MESSAGE).build()); + } +} diff --git a/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeRpcClient.java b/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeRpcClient.java new file mode 100644 index 0000000000..f627330e80 --- /dev/null +++ b/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeRpcClient.java @@ -0,0 +1,21 @@ +/** + * Copyright © 2016-2019 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.edge.rpc; + +public interface EdgeRpcClient { + + void connect(); +} diff --git a/common/edge-api/src/main/proto/edge.proto b/common/edge-api/src/main/proto/edge.proto new file mode 100644 index 0000000000..f86fa96b4a --- /dev/null +++ b/common/edge-api/src/main/proto/edge.proto @@ -0,0 +1,49 @@ +/** + * Copyright © 2016-2019 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +syntax = "proto3"; + +option java_package = "org.thingsboard.server.common.edge.gen"; +option java_outer_classname = "EdgeProtos"; + +package edge; + +// Interface exported by the ThingsBoard PRC Edge. +service EdgeRpcService { + + rpc sendUplink(stream UplinkMsg) returns (stream DownlinkMsg) {} + +} + +/** + * Data Structures; + */ +message UplinkMsg { + UplinkMsgType msgType = 1; +} + +message DownlinkMsg { + DownlinkMsgType msgType = 1; +} + +enum UplinkMsgType { + SAVE_DEVICE_MESSAGE = 0; + DELETE_DEVICE_MESSAGE = 1; +} + +enum DownlinkMsgType { + SAVE_ENTITY_MESSAGE = 0; + DELETE_ENTITY_MESSAGE = 1; +} diff --git a/common/pom.xml b/common/pom.xml index 7492cb3199..3c2d5e3740 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -40,6 +40,7 @@ queue transport dao-api + edge-api diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeService.java b/dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeService.java index 52c63b92bc..7deb6844b0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeService.java @@ -46,6 +46,7 @@ import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; +import org.thingsboard.server.dao.service.Validator; import org.thingsboard.server.dao.tenant.TenantDao; import javax.annotation.Nullable; @@ -111,6 +112,13 @@ public class BaseEdgeService extends AbstractEntityService implements EdgeServic return edgeOpt.orElse(null); } + @Override + public Optional findEdgeByRoutingKey(TenantId tenantId, String routingKey) { + log.trace("Executing findEdgeByRoutingKey [{}]", routingKey); + Validator.validateString(routingKey, "Incorrect edge routingKey for search request."); + return edgeDao.findByRoutingKey(tenantId.getId(), routingKey); + } + @CacheEvict(cacheNames = EDGE_CACHE, key = "{#edge.tenantId, #edge.name}") @Override public Edge saveEdge(Edge edge) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/CassandraEdgeDao.java b/dao/src/main/java/org/thingsboard/server/dao/edge/CassandraEdgeDao.java index 90446cf4f8..4dee08d46b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/CassandraEdgeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/CassandraEdgeDao.java @@ -86,4 +86,9 @@ public class CassandraEdgeDao extends CassandraAbstractSearchTextDao> findTenantEdgeTypesAsync(UUID tenantId) { return null; } + + @Override + public Optional findByRoutingKey(UUID tenantId, String routingKey) { + return Optional.empty(); + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java index 4f2d3f85c5..ecf7d0d612 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java @@ -116,5 +116,12 @@ public interface EdgeDao extends Dao { */ ListenableFuture> findTenantEdgeTypesAsync(UUID tenantId); + /** + * Find edge by routing Key. + * + * @param routingKey the edge routingKey + * @return the optional edge object + */ + Optional findByRoutingKey(UUID tenantId, String routingKey); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index a91cf0b1eb..516ad3f690 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -361,7 +361,8 @@ public class ModelConstants { public static final String EDGE_CONFIGURATION_PROPERTY = "configuration"; public static final String EDGE_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY; - public static final String EDGE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "edge_by_tenant_and_search_text"; + public static final String EDGE_ROUTING_KEY_PROPERTY = "routing_key"; + public static final String EDGE_SECRET_PROPERTY = "secret"; /** diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/EdgeEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/EdgeEntity.java index 413961b406..de8d81f659 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/EdgeEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/EdgeEntity.java @@ -5,7 +5,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -37,6 +37,8 @@ import static org.thingsboard.server.dao.model.ModelConstants.EDGE_CONFIGURATION 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_NAME_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_ROUTING_KEY_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_SECRET_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.EDGE_TENANT_ID_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.EDGE_TYPE_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; @@ -70,6 +72,12 @@ public class EdgeEntity implements SearchTextEntity { @Column(name = SEARCH_TEXT_PROPERTY) private String searchText; + @Column(name = EDGE_ROUTING_KEY_PROPERTY) + private String routingKey; + + @Column(name = EDGE_SECRET_PROPERTY) + private String secret; + @Column(name = EDGE_CONFIGURATION_PROPERTY, codec = JsonCodec.class) private JsonNode configuration; @@ -90,6 +98,8 @@ public class EdgeEntity implements SearchTextEntity { this.type = edge.getType(); this.name = edge.getName(); this.label = edge.getLabel(); + this.routingKey = edge.getRoutingKey(); + this.secret = edge.getSecret(); this.configuration = edge.getConfiguration(); this.additionalInfo = edge.getAdditionalInfo(); } @@ -112,6 +122,8 @@ public class EdgeEntity implements SearchTextEntity { edge.setType(type); edge.setName(name); edge.setLabel(label); + edge.setRoutingKey(routingKey); + edge.setSecret(secret); edge.setConfiguration(configuration); edge.setAdditionalInfo(additionalInfo); return edge; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/EdgeEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/EdgeEntity.java index 6c0ee3eb6e..6fce28077f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/EdgeEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/EdgeEntity.java @@ -5,7 +5,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -35,11 +35,12 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; -import static org.thingsboard.server.dao.model.ModelConstants.ASSET_CUSTOMER_ID_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_NAME_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_ROUTING_KEY_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_SECRET_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.EDGE_TENANT_ID_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.EDGE_TYPE_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; @@ -69,6 +70,12 @@ public class EdgeEntity extends BaseSqlEntity implements SearchTextEntity< @Column(name = SEARCH_TEXT_PROPERTY) private String searchText; + @Column(name = EDGE_ROUTING_KEY_PROPERTY) + private String routingKey; + + @Column(name = EDGE_SECRET_PROPERTY) + private String secret; + @Type(type = "json") @Column(name = ModelConstants.EDGE_CONFIGURATION_PROPERTY) private JsonNode configuration; @@ -94,6 +101,8 @@ public class EdgeEntity extends BaseSqlEntity implements SearchTextEntity< this.type = edge.getType(); this.name = edge.getName(); this.label = edge.getLabel(); + this.routingKey = edge.getRoutingKey(); + this.secret = edge.getSecret(); this.configuration = edge.getConfiguration(); this.additionalInfo = edge.getAdditionalInfo(); } @@ -125,6 +134,8 @@ public class EdgeEntity extends BaseSqlEntity implements SearchTextEntity< edge.setType(type); edge.setName(name); edge.setLabel(label); + edge.setRoutingKey(routingKey); + edge.setSecret(secret); edge.setConfiguration(configuration); edge.setAdditionalInfo(additionalInfo); return edge; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeRepository.java index 553e4b6879..56cc38d82f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeRepository.java @@ -76,4 +76,5 @@ public interface EdgeRepository extends CrudRepository { List findEdgesByTenantIdAndIdIn(String tenantId, List edgeIds); + EdgeEntity findByRoutingKey(String routingKey); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java index db0b305819..64d5412169 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java @@ -5,7 +5,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -126,6 +126,12 @@ public class JpaEdgeDao extends JpaAbstractSearchTextDao imple return service.submit(() -> convertTenantEdgeTypesToDto(tenantId, edgeRepository.findTenantEdgeTypes(fromTimeUUID(tenantId)))); } + @Override + public Optional findByRoutingKey(UUID tenantId, String routingKey) { + Edge edge = DaoUtil.getData(edgeRepository.findByRoutingKey(routingKey)); + return Optional.ofNullable(edge); + } + private List convertTenantEdgeTypesToDto(UUID tenantId, List types) { List list = Collections.emptyList(); if (types != null && !types.isEmpty()) { diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 2c20110478..7b881c9ea1 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -257,6 +257,8 @@ CREATE TABLE IF NOT EXISTS edge ( type varchar(255), name varchar(255), label varchar(255), + routing_key varchar(255), + secret varchar(255), search_text varchar(255), tenant_id varchar(31) ); \ No newline at end of file diff --git a/pom.xml b/pom.xml index 6c3b298391..d22524f951 100755 --- a/pom.xml +++ b/pom.xml @@ -406,6 +406,11 @@ coap ${project.version} + + org.thingsboard.common + edge-api + ${project.version} + org.thingsboard dao diff --git a/ui/src/app/edge/edge-fieldset.tpl.html b/ui/src/app/edge/edge-fieldset.tpl.html index 759e53e164..5d9e63c68e 100644 --- a/ui/src/app/edge/edge-fieldset.tpl.html +++ b/ui/src/app/edge/edge-fieldset.tpl.html @@ -27,7 +27,7 @@
@@ -69,4 +69,34 @@ +
+ + + + + + + + {{ 'edge.copy-edge-key' | translate }} + + +
+
+ + + + + + + + {{ 'edge.copy-edge-secret' | translate }} + + +
diff --git a/ui/src/app/edge/edge.directive.js b/ui/src/app/edge/edge.directive.js index cbc4d6d10f..5eed24e823 100644 --- a/ui/src/app/edge/edge.directive.js +++ b/ui/src/app/edge/edge.directive.js @@ -20,7 +20,7 @@ import edgeFieldsetTemplate from './edge-fieldset.tpl.html'; /* eslint-enable import/no-unresolved, import/default */ /*@ngInject*/ -export default function EdgeDirective($compile, $templateCache, $translate, $mdDialog, $document, toast, types, customerService) { +export default function EdgeDirective($compile, $templateCache, $translate, $mdDialog, $document, utils, toast, types, customerService) { var linker = function (scope, element) { var template = $templateCache.get(edgeFieldsetTemplate); element.html(template); @@ -32,6 +32,10 @@ export default function EdgeDirective($compile, $templateCache, $translate, $mdD scope.$watch('edge', function(newVal) { if (newVal) { + if (!scope.edge.id) { + scope.edge.routingKey = utils.guid(''); + scope.edge.secret = generateSecret(20); + } if (scope.edge.customerId && scope.edge.customerId.id !== types.id.nullUid) { scope.isAssignedToCustomer = true; customerService.getShortCustomerInfo(scope.edge.customerId.id).then( @@ -48,12 +52,38 @@ export default function EdgeDirective($compile, $templateCache, $translate, $mdD } }); + function generateSecret(length) { + if (angular.isUndefined(length) || length == null) { + length = 1; + } + var l = length > 10 ? 10 : length; + var str = Math.random().toString(36).substr(2, l); + if(str.length >= length){ + return str; + } + return str.concat(generateSecret(length - str.length)); + } + scope.onEdgeIdCopied = function() { toast.showSuccess($translate.instant('edge.id-copied-message'), 750, angular.element(element).parent().parent(), 'bottom left'); }; $compile(element.contents())(scope); + scope.onEdgeInfoCopied = function(type) { + let translateInstant = ""; + switch (type) { + case 'key': + translateInstant = "edge.edge-key-copied-message"; + break; + case 'secret': + translateInstant = "edge.edge-secret-copied-message"; + break; + } + toast.showSuccess($translate.instant(translateInstant), 750, angular.element(element).parent().parent(), 'top left'); + }; + + }; return { restrict: "E", diff --git a/ui/src/app/import-export/import-export.service.js b/ui/src/app/import-export/import-export.service.js index 610a5afdea..1a130a71ef 100644 --- a/ui/src/app/import-export/import-export.service.js +++ b/ui/src/app/import-export/import-export.service.js @@ -602,6 +602,16 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, } ); return deferred.promise; + case types.entityType.edge: + openImportDialogCSV($event, entityType, 'edge.import', 'edge.edge-file').then( + function success() { + deferred.resolve(); + }, + function fail() { + deferred.reject(); + } + ); + return deferred.promise; } } diff --git a/ui/src/app/locale/locale.constant-en_US.json b/ui/src/app/locale/locale.constant-en_US.json index 8add2421dd..f20a324129 100644 --- a/ui/src/app/locale/locale.constant-en_US.json +++ b/ui/src/app/locale/locale.constant-en_US.json @@ -777,7 +777,13 @@ "unassign-from-edge": "Unassign from edge", "dashboards": "Edge Dashboards", "manage-edge-rulechains": "Manage edge rule chains", - "rulechains": "Edge Rule Chains" + "rulechains": "Edge Rule Chains", + "edge-key": "Edge key", + "copy-edge-key": "Copy edge key", + "edge-key-copied-message": "Edge key has been copied to clipboard", + "edge-secret": "Edge secret", + "copy-edge-secret": "Copy edge secret", + "edge-secret-copied-message": "Edge secret has been copied to clipboard" }, "error": { "unable-to-connect": "Unable to connect to the server! Please check your internet connection.",