Commit c991ab96f179ec71cb040d5a9dc4040288e1ef0a

Authored by Volodymyr Babak
2 parents 9c8ceaa3 82e4968d

Merge branch 'feature/edge' of github.com:volodymyr-babak/thingsboard into feature/edge

@@ -292,7 +292,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { @@ -292,7 +292,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
292 case ADDED: // used only for USER entity 292 case ADDED: // used only for USER entity
293 case UPDATED: 293 case UPDATED:
294 case CREDENTIALS_UPDATED: 294 case CREDENTIALS_UPDATED:
295 - edgeIdsFuture = findRelatedEdgeIdsByEntityId(tenantId, entityId); 295 + edgeIdsFuture = edgeService.findRelatedEdgeIdsByEntityId(tenantId, entityId);
296 Futures.addCallback(edgeIdsFuture, new FutureCallback<List<EdgeId>>() { 296 Futures.addCallback(edgeIdsFuture, new FutureCallback<List<EdgeId>>() {
297 @Override 297 @Override
298 public void onSuccess(@Nullable List<EdgeId> edgeIds) { 298 public void onSuccess(@Nullable List<EdgeId> edgeIds) {
@@ -310,7 +310,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { @@ -310,7 +310,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
310 break; 310 break;
311 case ASSIGNED_TO_CUSTOMER: 311 case ASSIGNED_TO_CUSTOMER:
312 case UNASSIGNED_FROM_CUSTOMER: 312 case UNASSIGNED_FROM_CUSTOMER:
313 - edgeIdsFuture = findRelatedEdgeIdsByEntityId(tenantId, entityId); 313 + edgeIdsFuture = edgeService.findRelatedEdgeIdsByEntityId(tenantId, entityId);
314 Futures.addCallback(edgeIdsFuture, new FutureCallback<List<EdgeId>>() { 314 Futures.addCallback(edgeIdsFuture, new FutureCallback<List<EdgeId>>() {
315 @Override 315 @Override
316 public void onSuccess(@Nullable List<EdgeId> edgeIds) { 316 public void onSuccess(@Nullable List<EdgeId> edgeIds) {
@@ -408,7 +408,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { @@ -408,7 +408,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
408 if (alarm != null) { 408 if (alarm != null) {
409 EdgeEventType edgeEventType = getEdgeQueueTypeByEntityType(alarm.getOriginator().getEntityType()); 409 EdgeEventType edgeEventType = getEdgeQueueTypeByEntityType(alarm.getOriginator().getEntityType());
410 if (edgeEventType != null) { 410 if (edgeEventType != null) {
411 - ListenableFuture<List<EdgeId>> relatedEdgeIdsByEntityIdFuture = findRelatedEdgeIdsByEntityId(tenantId, alarm.getOriginator()); 411 + ListenableFuture<List<EdgeId>> relatedEdgeIdsByEntityIdFuture = edgeService.findRelatedEdgeIdsByEntityId(tenantId, alarm.getOriginator());
412 Futures.transform(relatedEdgeIdsByEntityIdFuture, relatedEdgeIdsByEntityId -> { 412 Futures.transform(relatedEdgeIdsByEntityIdFuture, relatedEdgeIdsByEntityId -> {
413 if (relatedEdgeIdsByEntityId != null) { 413 if (relatedEdgeIdsByEntityId != null) {
414 for (EdgeId edgeId : relatedEdgeIdsByEntityId) { 414 for (EdgeId edgeId : relatedEdgeIdsByEntityId) {
@@ -433,8 +433,8 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { @@ -433,8 +433,8 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
433 if (!relation.getFrom().getEntityType().equals(EntityType.EDGE) && 433 if (!relation.getFrom().getEntityType().equals(EntityType.EDGE) &&
434 !relation.getTo().getEntityType().equals(EntityType.EDGE)) { 434 !relation.getTo().getEntityType().equals(EntityType.EDGE)) {
435 List<ListenableFuture<List<EdgeId>>> futures = new ArrayList<>(); 435 List<ListenableFuture<List<EdgeId>>> futures = new ArrayList<>();
436 - futures.add(findRelatedEdgeIdsByEntityId(tenantId, relation.getTo()));  
437 - futures.add(findRelatedEdgeIdsByEntityId(tenantId, relation.getFrom())); 436 + futures.add(edgeService.findRelatedEdgeIdsByEntityId(tenantId, relation.getTo()));
  437 + futures.add(edgeService.findRelatedEdgeIdsByEntityId(tenantId, relation.getFrom()));
438 ListenableFuture<List<List<EdgeId>>> combinedFuture = Futures.allAsList(futures); 438 ListenableFuture<List<List<EdgeId>>> combinedFuture = Futures.allAsList(futures);
439 Futures.transform(combinedFuture, listOfListsEdgeIds -> { 439 Futures.transform(combinedFuture, listOfListsEdgeIds -> {
440 Set<EdgeId> uniqueEdgeIds = new HashSet<>(); 440 Set<EdgeId> uniqueEdgeIds = new HashSet<>();
@@ -460,48 +460,6 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { @@ -460,48 +460,6 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
460 } 460 }
461 } 461 }
462 462
463 - private ListenableFuture<List<EdgeId>> findRelatedEdgeIdsByEntityId(TenantId tenantId, EntityId entityId) {  
464 - switch (entityId.getEntityType()) {  
465 - case DEVICE:  
466 - case ASSET:  
467 - case ENTITY_VIEW:  
468 - ListenableFuture<List<EntityRelation>> originatorEdgeRelationsFuture =  
469 - relationService.findByToAndTypeAsync(tenantId, entityId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE);  
470 - return Futures.transform(originatorEdgeRelationsFuture, originatorEdgeRelations -> {  
471 - if (originatorEdgeRelations != null && originatorEdgeRelations.size() > 0) {  
472 - return Collections.singletonList(new EdgeId(originatorEdgeRelations.get(0).getFrom().getId()));  
473 - } else {  
474 - return Collections.emptyList();  
475 - }  
476 - }, dbCallbackExecutorService);  
477 - case DASHBOARD:  
478 - return convertToEdgeIds(edgeService.findEdgesByTenantIdAndDashboardId(tenantId, new DashboardId(entityId.getId())));  
479 - case RULE_CHAIN:  
480 - return convertToEdgeIds(edgeService.findEdgesByTenantIdAndRuleChainId(tenantId, new RuleChainId(entityId.getId())));  
481 - case USER:  
482 - User userById = userService.findUserById(tenantId, new UserId(entityId.getId()));  
483 - TextPageData<Edge> edges;  
484 - if (userById.getCustomerId() == null || userById.getCustomerId().isNullUid()) {  
485 - edges = edgeService.findEdgesByTenantId(tenantId, new TextPageLink(Integer.MAX_VALUE));  
486 - } else {  
487 - edges = edgeService.findEdgesByTenantIdAndCustomerId(tenantId, new CustomerId(entityId.getId()), new TextPageLink(Integer.MAX_VALUE));  
488 - }  
489 - return convertToEdgeIds(Futures.immediateFuture(edges.getData()));  
490 - default:  
491 - return Futures.immediateFuture(Collections.emptyList());  
492 - }  
493 - }  
494 -  
495 - private ListenableFuture<List<EdgeId>> convertToEdgeIds(ListenableFuture<List<Edge>> future) {  
496 - return Futures.transform(future, edges -> {  
497 - if (edges != null && !edges.isEmpty()) {  
498 - return edges.stream().map(IdBased::getId).collect(Collectors.toList());  
499 - } else {  
500 - return Collections.emptyList();  
501 - }  
502 - }, dbCallbackExecutorService);  
503 - }  
504 -  
505 private EdgeEventType getEdgeQueueTypeByEntityType(EntityType entityType) { 463 private EdgeEventType getEdgeQueueTypeByEntityType(EntityType entityType) {
506 switch (entityType) { 464 switch (entityType) {
507 case DEVICE: 465 case DEVICE:
@@ -32,6 +32,7 @@ import lombok.Data; @@ -32,6 +32,7 @@ import lombok.Data;
32 import lombok.extern.slf4j.Slf4j; 32 import lombok.extern.slf4j.Slf4j;
33 import org.apache.commons.lang.RandomStringUtils; 33 import org.apache.commons.lang.RandomStringUtils;
34 import org.checkerframework.checker.nullness.qual.Nullable; 34 import org.checkerframework.checker.nullness.qual.Nullable;
  35 +import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
35 import org.thingsboard.server.common.data.AdminSettings; 36 import org.thingsboard.server.common.data.AdminSettings;
36 import org.thingsboard.server.common.data.Customer; 37 import org.thingsboard.server.common.data.Customer;
37 import org.thingsboard.server.common.data.Dashboard; 38 import org.thingsboard.server.common.data.Dashboard;
@@ -64,6 +65,7 @@ import org.thingsboard.server.common.data.id.TenantId; @@ -64,6 +65,7 @@ import org.thingsboard.server.common.data.id.TenantId;
64 import org.thingsboard.server.common.data.id.UserId; 65 import org.thingsboard.server.common.data.id.UserId;
65 import org.thingsboard.server.common.data.id.WidgetTypeId; 66 import org.thingsboard.server.common.data.id.WidgetTypeId;
66 import org.thingsboard.server.common.data.id.WidgetsBundleId; 67 import org.thingsboard.server.common.data.id.WidgetsBundleId;
  68 +import org.thingsboard.server.common.data.kv.AttributeKey;
67 import org.thingsboard.server.common.data.kv.AttributeKvEntry; 69 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
68 import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; 70 import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
69 import org.thingsboard.server.common.data.kv.LongDataEntry; 71 import org.thingsboard.server.common.data.kv.LongDataEntry;
@@ -87,6 +89,7 @@ import org.thingsboard.server.common.transport.util.JsonUtils; @@ -87,6 +89,7 @@ import org.thingsboard.server.common.transport.util.JsonUtils;
87 import org.thingsboard.server.gen.edge.AdminSettingsUpdateMsg; 89 import org.thingsboard.server.gen.edge.AdminSettingsUpdateMsg;
88 import org.thingsboard.server.gen.edge.AlarmUpdateMsg; 90 import org.thingsboard.server.gen.edge.AlarmUpdateMsg;
89 import org.thingsboard.server.gen.edge.AssetUpdateMsg; 91 import org.thingsboard.server.gen.edge.AssetUpdateMsg;
  92 +import org.thingsboard.server.gen.edge.AttributeDeleteMsg;
90 import org.thingsboard.server.gen.edge.AttributesRequestMsg; 93 import org.thingsboard.server.gen.edge.AttributesRequestMsg;
91 import org.thingsboard.server.gen.edge.ConnectRequestMsg; 94 import org.thingsboard.server.gen.edge.ConnectRequestMsg;
92 import org.thingsboard.server.gen.edge.ConnectResponseCode; 95 import org.thingsboard.server.gen.edge.ConnectResponseCode;
@@ -124,11 +127,12 @@ import org.thingsboard.server.queue.common.TbProtoQueueMsg; @@ -124,11 +127,12 @@ import org.thingsboard.server.queue.common.TbProtoQueueMsg;
124 import org.thingsboard.server.service.edge.EdgeContextComponent; 127 import org.thingsboard.server.service.edge.EdgeContextComponent;
125 128
126 import java.io.Closeable; 129 import java.io.Closeable;
127 -import java.io.IOException;  
128 import java.util.ArrayList; 130 import java.util.ArrayList;
129 import java.util.Collections; 131 import java.util.Collections;
  132 +import java.util.HashSet;
130 import java.util.List; 133 import java.util.List;
131 import java.util.Optional; 134 import java.util.Optional;
  135 +import java.util.Set;
132 import java.util.UUID; 136 import java.util.UUID;
133 import java.util.concurrent.CountDownLatch; 137 import java.util.concurrent.CountDownLatch;
134 import java.util.concurrent.ExecutionException; 138 import java.util.concurrent.ExecutionException;
@@ -387,7 +391,7 @@ public final class EdgeGrpcSession implements Closeable { @@ -387,7 +391,7 @@ public final class EdgeGrpcSession implements Closeable {
387 ctx.getAttributesService().save(edge.getTenantId(), edge.getId(), DataConstants.SERVER_SCOPE, attributes); 391 ctx.getAttributesService().save(edge.getTenantId(), edge.getId(), DataConstants.SERVER_SCOPE, attributes);
388 } 392 }
389 393
390 - private DownlinkMsg processTelemetryMessage(EdgeEvent edgeEvent) throws IOException { 394 + private DownlinkMsg processTelemetryMessage(EdgeEvent edgeEvent) {
391 log.trace("Executing processTelemetryMessage, edgeEvent [{}]", edgeEvent); 395 log.trace("Executing processTelemetryMessage, edgeEvent [{}]", edgeEvent);
392 EntityId entityId = null; 396 EntityId entityId = null;
393 switch (edgeEvent.getEdgeEventType()) { 397 switch (edgeEvent.getEdgeEventType()) {
@@ -840,6 +844,9 @@ public final class EdgeGrpcSession implements Closeable { @@ -840,6 +844,9 @@ public final class EdgeGrpcSession implements Closeable {
840 result.add(processPostTelemetry(entityId, entityData.getPostTelemetryMsg(), metaData)); 844 result.add(processPostTelemetry(entityId, entityData.getPostTelemetryMsg(), metaData));
841 } 845 }
842 } 846 }
  847 + if (entityData.hasAttributeDeleteMsg()) {
  848 + result.add(processAttributeDeleteMsg(entityId, entityData.getAttributeDeleteMsg(), entityData.getEntityType()));
  849 + }
843 } 850 }
844 } 851 }
845 852
@@ -962,6 +969,7 @@ public final class EdgeGrpcSession implements Closeable { @@ -962,6 +969,7 @@ public final class EdgeGrpcSession implements Closeable {
962 969
963 @Override 970 @Override
964 public void onFailure(Throwable t) { 971 public void onFailure(Throwable t) {
  972 + log.error("Can't process post telemetry [{}]", msg, t);
965 futureToSet.setException(t); 973 futureToSet.setException(t);
966 } 974 }
967 }); 975 });
@@ -970,11 +978,49 @@ public final class EdgeGrpcSession implements Closeable { @@ -970,11 +978,49 @@ public final class EdgeGrpcSession implements Closeable {
970 } 978 }
971 979
972 private ListenableFuture<Void> processPostAttributes(EntityId entityId, TransportProtos.PostAttributeMsg msg, TbMsgMetaData metaData) { 980 private ListenableFuture<Void> processPostAttributes(EntityId entityId, TransportProtos.PostAttributeMsg msg, TbMsgMetaData metaData) {
  981 + SettableFuture<Void> futureToSet = SettableFuture.create();
973 JsonObject json = JsonUtils.getJsonObject(msg.getKvList()); 982 JsonObject json = JsonUtils.getJsonObject(msg.getKvList());
974 TbMsg tbMsg = TbMsg.newMsg(SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), entityId, metaData, gson.toJson(json)); 983 TbMsg tbMsg = TbMsg.newMsg(SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), entityId, metaData, gson.toJson(json));
975 - // TODO: voba - verify that null callback is OK  
976 - ctx.getTbClusterService().pushMsgToRuleEngine(edge.getTenantId(), tbMsg.getOriginator(), tbMsg, null);  
977 - return Futures.immediateFuture(null); 984 + ctx.getTbClusterService().pushMsgToRuleEngine(edge.getTenantId(), tbMsg.getOriginator(), tbMsg, new TbQueueCallback() {
  985 + @Override
  986 + public void onSuccess(TbQueueMsgMetadata metadata) {
  987 + futureToSet.set(null);
  988 + }
  989 +
  990 + @Override
  991 + public void onFailure(Throwable t) {
  992 + log.error("Can't process post attributes [{}]", msg, t);
  993 + futureToSet.setException(t);
  994 + }
  995 + });
  996 + return futureToSet;
  997 + }
  998 +
  999 + private ListenableFuture<Void> processAttributeDeleteMsg(EntityId entityId, AttributeDeleteMsg attributeDeleteMsg, String entityType) {
  1000 + SettableFuture<Void> futureToSet = SettableFuture.create();
  1001 + String scope = attributeDeleteMsg.getScope();
  1002 + List<String> attributeNames = attributeDeleteMsg.getAttributeNamesList();
  1003 + ctx.getAttributesService().removeAll(edge.getTenantId(), entityId, scope, attributeNames);
  1004 + if (EntityType.DEVICE.name().equals(entityType)) {
  1005 + Set<AttributeKey> attributeKeys = new HashSet<>();
  1006 + for (String attributeName : attributeNames) {
  1007 + attributeKeys.add(new AttributeKey(scope, attributeName));
  1008 + }
  1009 + ctx.getTbClusterService().pushMsgToCore(DeviceAttributesEventNotificationMsg.onDelete(
  1010 + edge.getTenantId(), (DeviceId) entityId, attributeKeys), new TbQueueCallback() {
  1011 + @Override
  1012 + public void onSuccess(TbQueueMsgMetadata metadata) {
  1013 + futureToSet.set(null);
  1014 + }
  1015 +
  1016 + @Override
  1017 + public void onFailure(Throwable t) {
  1018 + log.error("Can't process attribute delete msg [{}]", attributeDeleteMsg, t);
  1019 + futureToSet.setException(t);
  1020 + }
  1021 + });
  1022 + }
  1023 + return futureToSet;
978 } 1024 }
979 1025
980 private ListenableFuture<Void> onDeviceUpdate(DeviceUpdateMsg deviceUpdateMsg) { 1026 private ListenableFuture<Void> onDeviceUpdate(DeviceUpdateMsg deviceUpdateMsg) {
@@ -43,9 +43,11 @@ public class EntityDataMsgConstructor { @@ -43,9 +43,11 @@ public class EntityDataMsgConstructor {
43 case TIMESERIES_UPDATED: 43 case TIMESERIES_UPDATED:
44 try { 44 try {
45 JsonObject data = entityData.getAsJsonObject(); 45 JsonObject data = entityData.getAsJsonObject();
46 - long ts = System.currentTimeMillis(); 46 + long ts;
47 if (data.get("ts") != null && !data.get("ts").isJsonNull()) { 47 if (data.get("ts") != null && !data.get("ts").isJsonNull()) {
48 - ts = data.getAsJsonObject("ts").getAsLong(); 48 + ts = data.getAsJsonPrimitive("ts").getAsLong();
  49 + } else {
  50 + ts = System.currentTimeMillis();
49 } 51 }
50 builder.setPostTelemetryMsg(JsonConverter.convertToTelemetryProto(data.getAsJsonObject("data"), ts)); 52 builder.setPostTelemetryMsg(JsonConverter.convertToTelemetryProto(data.getAsJsonObject("data"), ts));
51 } catch (Exception e) { 53 } catch (Exception e) {
@@ -68,6 +68,8 @@ public class RuleChainUpdateMsgConstructor { @@ -68,6 +68,8 @@ public class RuleChainUpdateMsgConstructor {
68 .addAllRuleChainConnections(constructRuleChainConnections(ruleChainMetaData.getRuleChainConnections())); 68 .addAllRuleChainConnections(constructRuleChainConnections(ruleChainMetaData.getRuleChainConnections()));
69 if (ruleChainMetaData.getFirstNodeIndex() != null) { 69 if (ruleChainMetaData.getFirstNodeIndex() != null) {
70 builder.setFirstNodeIndex(ruleChainMetaData.getFirstNodeIndex()); 70 builder.setFirstNodeIndex(ruleChainMetaData.getFirstNodeIndex());
  71 + } else {
  72 + builder.setFirstNodeIndex(-1);
71 } 73 }
72 builder.setMsgType(msgType); 74 builder.setMsgType(msgType);
73 return builder.build(); 75 return builder.build();
@@ -22,12 +22,11 @@ import org.thingsboard.server.common.data.edge.EdgeSearchQuery; @@ -22,12 +22,11 @@ import org.thingsboard.server.common.data.edge.EdgeSearchQuery;
22 import org.thingsboard.server.common.data.id.CustomerId; 22 import org.thingsboard.server.common.data.id.CustomerId;
23 import org.thingsboard.server.common.data.id.DashboardId; 23 import org.thingsboard.server.common.data.id.DashboardId;
24 import org.thingsboard.server.common.data.id.EdgeId; 24 import org.thingsboard.server.common.data.id.EdgeId;
  25 +import org.thingsboard.server.common.data.id.EntityId;
25 import org.thingsboard.server.common.data.id.RuleChainId; 26 import org.thingsboard.server.common.data.id.RuleChainId;
26 import org.thingsboard.server.common.data.id.TenantId; 27 import org.thingsboard.server.common.data.id.TenantId;
27 import org.thingsboard.server.common.data.page.TextPageData; 28 import org.thingsboard.server.common.data.page.TextPageData;
28 import org.thingsboard.server.common.data.page.TextPageLink; 29 import org.thingsboard.server.common.data.page.TextPageLink;
29 -import org.thingsboard.server.common.data.page.TimePageData;  
30 -import org.thingsboard.server.common.data.page.TimePageLink;  
31 30
32 import java.util.List; 31 import java.util.List;
33 import java.util.Optional; 32 import java.util.Optional;
@@ -75,6 +74,8 @@ public interface EdgeService { @@ -75,6 +74,8 @@ public interface EdgeService {
75 ListenableFuture<List<Edge>> findEdgesByTenantIdAndRuleChainId(TenantId tenantId, RuleChainId ruleChainId); 74 ListenableFuture<List<Edge>> findEdgesByTenantIdAndRuleChainId(TenantId tenantId, RuleChainId ruleChainId);
76 75
77 ListenableFuture<List<Edge>> findEdgesByTenantIdAndDashboardId(TenantId tenantId, DashboardId dashboardId); 76 ListenableFuture<List<Edge>> findEdgesByTenantIdAndDashboardId(TenantId tenantId, DashboardId dashboardId);
  77 +
  78 + ListenableFuture<List<EdgeId>> findRelatedEdgeIdsByEntityId(TenantId tenantId, EntityId entityId);
78 } 79 }
79 80
80 81
@@ -31,18 +31,22 @@ import org.thingsboard.server.common.data.Customer; @@ -31,18 +31,22 @@ import org.thingsboard.server.common.data.Customer;
31 import org.thingsboard.server.common.data.EntitySubtype; 31 import org.thingsboard.server.common.data.EntitySubtype;
32 import org.thingsboard.server.common.data.EntityType; 32 import org.thingsboard.server.common.data.EntityType;
33 import org.thingsboard.server.common.data.Tenant; 33 import org.thingsboard.server.common.data.Tenant;
  34 +import org.thingsboard.server.common.data.User;
34 import org.thingsboard.server.common.data.edge.Edge; 35 import org.thingsboard.server.common.data.edge.Edge;
35 import org.thingsboard.server.common.data.edge.EdgeSearchQuery; 36 import org.thingsboard.server.common.data.edge.EdgeSearchQuery;
36 import org.thingsboard.server.common.data.id.CustomerId; 37 import org.thingsboard.server.common.data.id.CustomerId;
37 import org.thingsboard.server.common.data.id.DashboardId; 38 import org.thingsboard.server.common.data.id.DashboardId;
38 import org.thingsboard.server.common.data.id.EdgeId; 39 import org.thingsboard.server.common.data.id.EdgeId;
39 import org.thingsboard.server.common.data.id.EntityId; 40 import org.thingsboard.server.common.data.id.EntityId;
  41 +import org.thingsboard.server.common.data.id.IdBased;
40 import org.thingsboard.server.common.data.id.RuleChainId; 42 import org.thingsboard.server.common.data.id.RuleChainId;
41 import org.thingsboard.server.common.data.id.TenantId; 43 import org.thingsboard.server.common.data.id.TenantId;
  44 +import org.thingsboard.server.common.data.id.UserId;
42 import org.thingsboard.server.common.data.page.TextPageData; 45 import org.thingsboard.server.common.data.page.TextPageData;
43 import org.thingsboard.server.common.data.page.TextPageLink; 46 import org.thingsboard.server.common.data.page.TextPageLink;
44 import org.thingsboard.server.common.data.relation.EntityRelation; 47 import org.thingsboard.server.common.data.relation.EntityRelation;
45 import org.thingsboard.server.common.data.relation.EntitySearchDirection; 48 import org.thingsboard.server.common.data.relation.EntitySearchDirection;
  49 +import org.thingsboard.server.common.data.relation.RelationTypeGroup;
46 import org.thingsboard.server.common.data.rule.RuleChain; 50 import org.thingsboard.server.common.data.rule.RuleChain;
47 import org.thingsboard.server.dao.asset.AssetService; 51 import org.thingsboard.server.dao.asset.AssetService;
48 import org.thingsboard.server.dao.customer.CustomerDao; 52 import org.thingsboard.server.dao.customer.CustomerDao;
@@ -51,12 +55,14 @@ import org.thingsboard.server.dao.device.DeviceService; @@ -51,12 +55,14 @@ import org.thingsboard.server.dao.device.DeviceService;
51 import org.thingsboard.server.dao.entity.AbstractEntityService; 55 import org.thingsboard.server.dao.entity.AbstractEntityService;
52 import org.thingsboard.server.dao.entityview.EntityViewService; 56 import org.thingsboard.server.dao.entityview.EntityViewService;
53 import org.thingsboard.server.dao.exception.DataValidationException; 57 import org.thingsboard.server.dao.exception.DataValidationException;
  58 +import org.thingsboard.server.dao.model.ModelConstants;
54 import org.thingsboard.server.dao.relation.RelationService; 59 import org.thingsboard.server.dao.relation.RelationService;
55 import org.thingsboard.server.dao.rule.RuleChainService; 60 import org.thingsboard.server.dao.rule.RuleChainService;
56 import org.thingsboard.server.dao.service.DataValidator; 61 import org.thingsboard.server.dao.service.DataValidator;
57 import org.thingsboard.server.dao.service.PaginatedRemover; 62 import org.thingsboard.server.dao.service.PaginatedRemover;
58 import org.thingsboard.server.dao.service.Validator; 63 import org.thingsboard.server.dao.service.Validator;
59 import org.thingsboard.server.dao.tenant.TenantDao; 64 import org.thingsboard.server.dao.tenant.TenantDao;
  65 +import org.thingsboard.server.dao.user.UserService;
60 66
61 import javax.annotation.Nullable; 67 import javax.annotation.Nullable;
62 import java.util.ArrayList; 68 import java.util.ArrayList;
@@ -93,6 +99,9 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic @@ -93,6 +99,9 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic
93 private CustomerDao customerDao; 99 private CustomerDao customerDao;
94 100
95 @Autowired 101 @Autowired
  102 + private UserService userService;
  103 +
  104 + @Autowired
96 private CacheManager cacheManager; 105 private CacheManager cacheManager;
97 106
98 @Autowired 107 @Autowired
@@ -430,4 +439,47 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic @@ -430,4 +439,47 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic
430 } 439 }
431 }; 440 };
432 441
  442 + @Override
  443 + public ListenableFuture<List<EdgeId>> findRelatedEdgeIdsByEntityId(TenantId tenantId, EntityId entityId) {
  444 + switch (entityId.getEntityType()) {
  445 + case DEVICE:
  446 + case ASSET:
  447 + case ENTITY_VIEW:
  448 + ListenableFuture<List<EntityRelation>> originatorEdgeRelationsFuture =
  449 + relationService.findByToAndTypeAsync(tenantId, entityId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE);
  450 + return Futures.transform(originatorEdgeRelationsFuture, originatorEdgeRelations -> {
  451 + if (originatorEdgeRelations != null && originatorEdgeRelations.size() > 0) {
  452 + return Collections.singletonList(new EdgeId(originatorEdgeRelations.get(0).getFrom().getId()));
  453 + } else {
  454 + return Collections.emptyList();
  455 + }
  456 + }, MoreExecutors.directExecutor());
  457 + case DASHBOARD:
  458 + return convertToEdgeIds(findEdgesByTenantIdAndDashboardId(tenantId, new DashboardId(entityId.getId())));
  459 + case RULE_CHAIN:
  460 + return convertToEdgeIds(findEdgesByTenantIdAndRuleChainId(tenantId, new RuleChainId(entityId.getId())));
  461 + case USER:
  462 + User userById = userService.findUserById(tenantId, new UserId(entityId.getId()));
  463 + TextPageData<Edge> edges;
  464 + if (userById.getCustomerId() == null || userById.getCustomerId().isNullUid()) {
  465 + edges = findEdgesByTenantId(tenantId, new TextPageLink(Integer.MAX_VALUE));
  466 + } else {
  467 + edges = findEdgesByTenantIdAndCustomerId(tenantId, new CustomerId(entityId.getId()), new TextPageLink(Integer.MAX_VALUE));
  468 + }
  469 + return convertToEdgeIds(Futures.immediateFuture(edges.getData()));
  470 + default:
  471 + return Futures.immediateFuture(Collections.emptyList());
  472 + }
  473 + }
  474 +
  475 + private ListenableFuture<List<EdgeId>> convertToEdgeIds(ListenableFuture<List<Edge>> future) {
  476 + return Futures.transform(future, edges -> {
  477 + if (edges != null && !edges.isEmpty()) {
  478 + return edges.stream().map(IdBased::getId).collect(Collectors.toList());
  479 + } else {
  480 + return Collections.emptyList();
  481 + }
  482 + }, MoreExecutors.directExecutor());
  483 + }
  484 +
433 } 485 }
@@ -232,14 +232,7 @@ public class TbMsgPushToEdgeNode implements TbNode { @@ -232,14 +232,7 @@ public class TbMsgPushToEdgeNode implements TbNode {
232 TextPageData<Edge> edgesByTenantId = ctx.getEdgeService().findEdgesByTenantId(tenantId, new TextPageLink(Integer.MAX_VALUE)); 232 TextPageData<Edge> edgesByTenantId = ctx.getEdgeService().findEdgesByTenantId(tenantId, new TextPageLink(Integer.MAX_VALUE));
233 return Futures.immediateFuture(edgesByTenantId.getData().stream().map(IdBased::getId).collect(Collectors.toList())); 233 return Futures.immediateFuture(edgesByTenantId.getData().stream().map(IdBased::getId).collect(Collectors.toList()));
234 } else { 234 } else {
235 - ListenableFuture<List<EntityRelation>> future = ctx.getRelationService().findByToAndTypeAsync(tenantId, originatorId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE);  
236 - return Futures.transform(future, relations -> {  
237 - List<EdgeId> result = new ArrayList<>();  
238 - if (relations != null && relations.size() > 0) {  
239 - result.add(new EdgeId(relations.get(0).getFrom().getId()));  
240 - }  
241 - return result;  
242 - }, ctx.getDbCallbackExecutor()); 235 + return ctx.getEdgeService().findRelatedEdgeIdsByEntityId(tenantId, originatorId);
243 } 236 }
244 } 237 }
245 238
@@ -284,13 +284,6 @@ function DashboardService($rootScope, $http, $q, $location, $filter) { @@ -284,13 +284,6 @@ function DashboardService($rootScope, $http, $q, $location, $filter) {
284 } 284 }
285 dashboard.assignedCustomersText = assignedCustomersTitles.join(', '); 285 dashboard.assignedCustomersText = assignedCustomersTitles.join(', ');
286 } 286 }
287 - dashboard.assignedEdgesIds = [];  
288 - if (dashboard.assignedEdges && dashboard.assignedEdges.length) {  
289 - for (var j = 0; j < dashboard.assignedEdges.length; j++) {  
290 - var assignedEdge = dashboard.assignedEdges[j];  
291 - dashboard.assignedEdgesIds.push(assignedEdge.edgeId.id);  
292 - }  
293 - }  
294 return dashboard; 287 return dashboard;
295 } 288 }
296 289
@@ -298,7 +291,6 @@ function DashboardService($rootScope, $http, $q, $location, $filter) { @@ -298,7 +291,6 @@ function DashboardService($rootScope, $http, $q, $location, $filter) {
298 delete dashboard.publicCustomerId; 291 delete dashboard.publicCustomerId;
299 delete dashboard.assignedCustomersText; 292 delete dashboard.assignedCustomersText;
300 delete dashboard.assignedCustomersIds; 293 delete dashboard.assignedCustomersIds;
301 - delete dashboard.assignedEdgesIds;  
302 return dashboard; 294 return dashboard;
303 } 295 }
304 296
@@ -19,6 +19,7 @@ @@ -19,6 +19,7 @@
19 <md-button ng-click="onManageAssets({event: $event})" ng-show="!isEdit && !isPublic" class="md-raised md-primary">{{ 'customer.manage-assets' | translate }}</md-button> 19 <md-button ng-click="onManageAssets({event: $event})" ng-show="!isEdit && !isPublic" class="md-raised md-primary">{{ 'customer.manage-assets' | translate }}</md-button>
20 <md-button ng-click="onManageDevices({event: $event})" ng-show="!isEdit" class="md-raised md-primary">{{ 'customer.manage-devices' | translate }}</md-button> 20 <md-button ng-click="onManageDevices({event: $event})" ng-show="!isEdit" class="md-raised md-primary">{{ 'customer.manage-devices' | translate }}</md-button>
21 <md-button ng-click="onManageDashboards({event: $event})" ng-show="!isEdit" class="md-raised md-primary">{{ 'customer.manage-dashboards' | translate }}</md-button> 21 <md-button ng-click="onManageDashboards({event: $event})" ng-show="!isEdit" class="md-raised md-primary">{{ 'customer.manage-dashboards' | translate }}</md-button>
  22 +<md-button ng-click="onManageEdges({event: $event})" ng-show="!isEdit && !isPublic" class="md-raised md-primary">{{ 'customer.manage-edges' | translate }}</md-button>
22 <md-button ng-click="onDeleteCustomer({event: $event})" ng-show="!isEdit && !isPublic" class="md-raised md-primary">{{ 'customer.delete' | translate }}</md-button> 23 <md-button ng-click="onDeleteCustomer({event: $event})" ng-show="!isEdit && !isPublic" class="md-raised md-primary">{{ 'customer.delete' | translate }}</md-button>
23 24
24 <div layout="row"> 25 <div layout="row">
@@ -79,6 +79,20 @@ export default function CustomerController(customerService, $state, $stateParams @@ -79,6 +79,20 @@ export default function CustomerController(customerService, $state, $stateParams
79 }, 79 },
80 { 80 {
81 onAction: function ($event, item) { 81 onAction: function ($event, item) {
  82 + openCustomerEdges($event, item);
  83 + },
  84 + name: function() { return $translate.instant('edge.edges') },
  85 + details: function(customer) {
  86 + if (customer && customer.additionalInfo && customer.additionalInfo.isPublic) {
  87 + return $translate.instant('customer.manage-public-edges')
  88 + } else {
  89 + return $translate.instant('customer.manage-customer-edges')
  90 + }
  91 + },
  92 + icon: "router"
  93 + },
  94 + {
  95 + onAction: function ($event, item) {
82 vm.grid.deleteItem($event, item); 96 vm.grid.deleteItem($event, item);
83 }, 97 },
84 name: function() { return $translate.instant('action.delete') }, 98 name: function() { return $translate.instant('action.delete') },
@@ -147,6 +161,7 @@ export default function CustomerController(customerService, $state, $stateParams @@ -147,6 +161,7 @@ export default function CustomerController(customerService, $state, $stateParams
147 vm.openCustomerAssets = openCustomerAssets; 161 vm.openCustomerAssets = openCustomerAssets;
148 vm.openCustomerDevices = openCustomerDevices; 162 vm.openCustomerDevices = openCustomerDevices;
149 vm.openCustomerDashboards = openCustomerDashboards; 163 vm.openCustomerDashboards = openCustomerDashboards;
  164 + vm.openCustomerEdges = openCustomerEdges;
150 165
151 function deleteCustomerTitle(customer) { 166 function deleteCustomerTitle(customer) {
152 return $translate.instant('customer.delete-customer-title', {customerTitle: customer.title}); 167 return $translate.instant('customer.delete-customer-title', {customerTitle: customer.title});
@@ -216,4 +231,11 @@ export default function CustomerController(customerService, $state, $stateParams @@ -216,4 +231,11 @@ export default function CustomerController(customerService, $state, $stateParams
216 $state.go('home.customers.dashboards', {customerId: customer.id.id}); 231 $state.go('home.customers.dashboards', {customerId: customer.id.id});
217 } 232 }
218 233
  234 + function openCustomerEdges($event, customer) {
  235 + if ($event) {
  236 + $event.stopPropagation();
  237 + }
  238 + $state.go('home.customers.edges', {customerId: customer.id.id});
  239 + }
  240 +
219 } 241 }
@@ -55,6 +55,7 @@ export default function CustomerDirective($compile, $templateCache, $translate, @@ -55,6 +55,7 @@ export default function CustomerDirective($compile, $templateCache, $translate,
55 onManageAssets: '&', 55 onManageAssets: '&',
56 onManageDevices: '&', 56 onManageDevices: '&',
57 onManageDashboards: '&', 57 onManageDashboards: '&',
  58 + onManageEdges: '&',
58 onDeleteCustomer: '&' 59 onDeleteCustomer: '&'
59 } 60 }
60 }; 61 };
@@ -29,6 +29,7 @@ @@ -29,6 +29,7 @@
29 on-manage-assets="vm.openCustomerAssets(event, vm.grid.detailsConfig.currentItem)" 29 on-manage-assets="vm.openCustomerAssets(event, vm.grid.detailsConfig.currentItem)"
30 on-manage-devices="vm.openCustomerDevices(event, vm.grid.detailsConfig.currentItem)" 30 on-manage-devices="vm.openCustomerDevices(event, vm.grid.detailsConfig.currentItem)"
31 on-manage-dashboards="vm.openCustomerDashboards(event, vm.grid.detailsConfig.currentItem)" 31 on-manage-dashboards="vm.openCustomerDashboards(event, vm.grid.detailsConfig.currentItem)"
  32 + on-manage-edges="vm.openCustomerEdges(event, vm.grid.detailsConfig.currentItem)"
32 on-delete-customer="vm.grid.deleteItem(event, vm.grid.detailsConfig.currentItem)"></tb-customer> 33 on-delete-customer="vm.grid.deleteItem(event, vm.grid.detailsConfig.currentItem)"></tb-customer>
33 </md-tab> 34 </md-tab>
34 <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" md-on-select="vm.grid.triggerResize()" label="{{ 'attribute.attributes' | translate }}"> 35 <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" md-on-select="vm.grid.triggerResize()" label="{{ 'attribute.attributes' | translate }}">
@@ -552,7 +552,7 @@ export function EdgeController($rootScope, userService, edgeService, customerSer @@ -552,7 +552,7 @@ export function EdgeController($rootScope, userService, edgeService, customerSer
552 $event.stopPropagation(); 552 $event.stopPropagation();
553 } 553 }
554 var pageSize = 10; 554 var pageSize = 10;
555 - ruleChainService.getRuleChains({limit: pageSize, textSearch: ''}).then( 555 + ruleChainService.getEdgesRuleChains({limit: pageSize, textSearch: ''}).then(
556 function success(_ruleChains) { 556 function success(_ruleChains) {
557 var ruleChains = { 557 var ruleChains = {
558 pageSize: pageSize, 558 pageSize: pageSize,
@@ -161,5 +161,28 @@ export default function EdgeRoutes($stateProvider, types) { @@ -161,5 +161,28 @@ export default function EdgeRoutes($stateProvider, types) {
161 ncyBreadcrumb: { 161 ncyBreadcrumb: {
162 label: '{"icon": "dashboard", "label": "{{ vm.dashboard.title }}", "translate": "false"}' 162 label: '{"icon": "dashboard", "label": "{{ vm.dashboard.title }}", "translate": "false"}'
163 } 163 }
  164 + })
  165 + .state('home.customers.edges', {
  166 + url: '/:customerId/edges',
  167 + params: {'topIndex': 0},
  168 + module: 'private',
  169 + auth: ['TENANT_ADMIN'],
  170 + views: {
  171 + "content@home": {
  172 + templateUrl: edgesTemplate,
  173 + controllerAs: 'vm',
  174 + controller: 'EdgeController'
  175 + }
  176 + },
  177 + data: {
  178 + edgesType: 'customer',
  179 + searchEnabled: true,
  180 + searchByEntitySubtype: true,
  181 + searchEntityType: types.entityType.edge,
  182 + pageTitle: 'customer.edges'
  183 + },
  184 + ncyBreadcrumb: {
  185 + label: '{"icon": "router", "label": "{{ vm.customerEdgesTitle }}", "translate": "false"}'
  186 + }
164 }); 187 });
165 } 188 }
@@ -53,7 +53,7 @@ export default function SetRootRuleChainToEdgesController(ruleChainService, edge @@ -53,7 +53,7 @@ export default function SetRootRuleChainToEdgesController(ruleChainService, edge
53 fetchMoreItems_: function () { 53 fetchMoreItems_: function () {
54 if (vm.ruleChains.hasNext && !vm.ruleChains.pending) { 54 if (vm.ruleChains.hasNext && !vm.ruleChains.pending) {
55 vm.ruleChains.pending = true; 55 vm.ruleChains.pending = true;
56 - ruleChainService.getRuleChains(vm.ruleChains.nextPageLink).then( 56 + ruleChainService.getEdgesRuleChains(vm.ruleChains.nextPageLink).then(
57 function success(ruleChains) { 57 function success(ruleChains) {
58 vm.ruleChains.data = vm.ruleChains.data.concat(ruleChains.data); 58 vm.ruleChains.data = vm.ruleChains.data.concat(ruleChains.data);
59 vm.ruleChains.nextPageLink = ruleChains.nextPageLink; 59 vm.ruleChains.nextPageLink = ruleChains.nextPageLink;
@@ -441,6 +441,7 @@ @@ -441,6 +441,7 @@
441 "manage-assets": "Manage assets", 441 "manage-assets": "Manage assets",
442 "manage-devices": "Manage devices", 442 "manage-devices": "Manage devices",
443 "manage-dashboards": "Manage dashboards", 443 "manage-dashboards": "Manage dashboards",
  444 + "manage-edges": "Manage edges",
444 "title": "Title", 445 "title": "Title",
445 "title-required": "Title is required.", 446 "title-required": "Title is required.",
446 "description": "Description", 447 "description": "Description",
@@ -812,6 +813,8 @@ @@ -812,6 +813,8 @@
812 "assign-edges-text": "Assign { count, plural, 1 {1 edge} other {# edges} } to customer", 813 "assign-edges-text": "Assign { count, plural, 1 {1 edge} other {# edges} } to customer",
813 "unassign-edge-title": "Are you sure you want to unassign the edge '{{edgeName}}'?", 814 "unassign-edge-title": "Are you sure you want to unassign the edge '{{edgeName}}'?",
814 "unassign-edge-text": "After the confirmation the edge will be unassigned and won't be accessible by the customer.", 815 "unassign-edge-text": "After the confirmation the edge will be unassigned and won't be accessible by the customer.",
  816 + "unassign-edges-title": "Are you sure you want to unassign { count, plural, 1 {1 edge} other {# edges} }?",
  817 + "unassign-edges-text": "After the confirmation all selected edges will be unassigned and won't be accessible by the customer.",
815 "make-public": "Make edge public", 818 "make-public": "Make edge public",
816 "make-public-edge-title": "Are you sure you want to make the edge '{{edgeName}}' public?", 819 "make-public-edge-title": "Are you sure you want to make the edge '{{edgeName}}' public?",
817 "make-public-edge-text": "After the confirmation the edge and all its data will be made public and accessible by others.", 820 "make-public-edge-text": "After the confirmation the edge and all its data will be made public and accessible by others.",