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 292 case ADDED: // used only for USER entity
293 293 case UPDATED:
294 294 case CREDENTIALS_UPDATED:
295   - edgeIdsFuture = findRelatedEdgeIdsByEntityId(tenantId, entityId);
  295 + edgeIdsFuture = edgeService.findRelatedEdgeIdsByEntityId(tenantId, entityId);
296 296 Futures.addCallback(edgeIdsFuture, new FutureCallback<List<EdgeId>>() {
297 297 @Override
298 298 public void onSuccess(@Nullable List<EdgeId> edgeIds) {
... ... @@ -310,7 +310,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
310 310 break;
311 311 case ASSIGNED_TO_CUSTOMER:
312 312 case UNASSIGNED_FROM_CUSTOMER:
313   - edgeIdsFuture = findRelatedEdgeIdsByEntityId(tenantId, entityId);
  313 + edgeIdsFuture = edgeService.findRelatedEdgeIdsByEntityId(tenantId, entityId);
314 314 Futures.addCallback(edgeIdsFuture, new FutureCallback<List<EdgeId>>() {
315 315 @Override
316 316 public void onSuccess(@Nullable List<EdgeId> edgeIds) {
... ... @@ -408,7 +408,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
408 408 if (alarm != null) {
409 409 EdgeEventType edgeEventType = getEdgeQueueTypeByEntityType(alarm.getOriginator().getEntityType());
410 410 if (edgeEventType != null) {
411   - ListenableFuture<List<EdgeId>> relatedEdgeIdsByEntityIdFuture = findRelatedEdgeIdsByEntityId(tenantId, alarm.getOriginator());
  411 + ListenableFuture<List<EdgeId>> relatedEdgeIdsByEntityIdFuture = edgeService.findRelatedEdgeIdsByEntityId(tenantId, alarm.getOriginator());
412 412 Futures.transform(relatedEdgeIdsByEntityIdFuture, relatedEdgeIdsByEntityId -> {
413 413 if (relatedEdgeIdsByEntityId != null) {
414 414 for (EdgeId edgeId : relatedEdgeIdsByEntityId) {
... ... @@ -433,8 +433,8 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
433 433 if (!relation.getFrom().getEntityType().equals(EntityType.EDGE) &&
434 434 !relation.getTo().getEntityType().equals(EntityType.EDGE)) {
435 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 438 ListenableFuture<List<List<EdgeId>>> combinedFuture = Futures.allAsList(futures);
439 439 Futures.transform(combinedFuture, listOfListsEdgeIds -> {
440 440 Set<EdgeId> uniqueEdgeIds = new HashSet<>();
... ... @@ -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 463 private EdgeEventType getEdgeQueueTypeByEntityType(EntityType entityType) {
506 464 switch (entityType) {
507 465 case DEVICE:
... ...
... ... @@ -32,6 +32,7 @@ import lombok.Data;
32 32 import lombok.extern.slf4j.Slf4j;
33 33 import org.apache.commons.lang.RandomStringUtils;
34 34 import org.checkerframework.checker.nullness.qual.Nullable;
  35 +import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
35 36 import org.thingsboard.server.common.data.AdminSettings;
36 37 import org.thingsboard.server.common.data.Customer;
37 38 import org.thingsboard.server.common.data.Dashboard;
... ... @@ -64,6 +65,7 @@ import org.thingsboard.server.common.data.id.TenantId;
64 65 import org.thingsboard.server.common.data.id.UserId;
65 66 import org.thingsboard.server.common.data.id.WidgetTypeId;
66 67 import org.thingsboard.server.common.data.id.WidgetsBundleId;
  68 +import org.thingsboard.server.common.data.kv.AttributeKey;
67 69 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
68 70 import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
69 71 import org.thingsboard.server.common.data.kv.LongDataEntry;
... ... @@ -87,6 +89,7 @@ import org.thingsboard.server.common.transport.util.JsonUtils;
87 89 import org.thingsboard.server.gen.edge.AdminSettingsUpdateMsg;
88 90 import org.thingsboard.server.gen.edge.AlarmUpdateMsg;
89 91 import org.thingsboard.server.gen.edge.AssetUpdateMsg;
  92 +import org.thingsboard.server.gen.edge.AttributeDeleteMsg;
90 93 import org.thingsboard.server.gen.edge.AttributesRequestMsg;
91 94 import org.thingsboard.server.gen.edge.ConnectRequestMsg;
92 95 import org.thingsboard.server.gen.edge.ConnectResponseCode;
... ... @@ -124,11 +127,12 @@ import org.thingsboard.server.queue.common.TbProtoQueueMsg;
124 127 import org.thingsboard.server.service.edge.EdgeContextComponent;
125 128
126 129 import java.io.Closeable;
127   -import java.io.IOException;
128 130 import java.util.ArrayList;
129 131 import java.util.Collections;
  132 +import java.util.HashSet;
130 133 import java.util.List;
131 134 import java.util.Optional;
  135 +import java.util.Set;
132 136 import java.util.UUID;
133 137 import java.util.concurrent.CountDownLatch;
134 138 import java.util.concurrent.ExecutionException;
... ... @@ -387,7 +391,7 @@ public final class EdgeGrpcSession implements Closeable {
387 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 395 log.trace("Executing processTelemetryMessage, edgeEvent [{}]", edgeEvent);
392 396 EntityId entityId = null;
393 397 switch (edgeEvent.getEdgeEventType()) {
... ... @@ -840,6 +844,9 @@ public final class EdgeGrpcSession implements Closeable {
840 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 969
963 970 @Override
964 971 public void onFailure(Throwable t) {
  972 + log.error("Can't process post telemetry [{}]", msg, t);
965 973 futureToSet.setException(t);
966 974 }
967 975 });
... ... @@ -970,11 +978,49 @@ public final class EdgeGrpcSession implements Closeable {
970 978 }
971 979
972 980 private ListenableFuture<Void> processPostAttributes(EntityId entityId, TransportProtos.PostAttributeMsg msg, TbMsgMetaData metaData) {
  981 + SettableFuture<Void> futureToSet = SettableFuture.create();
973 982 JsonObject json = JsonUtils.getJsonObject(msg.getKvList());
974 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 1026 private ListenableFuture<Void> onDeviceUpdate(DeviceUpdateMsg deviceUpdateMsg) {
... ...
... ... @@ -43,9 +43,11 @@ public class EntityDataMsgConstructor {
43 43 case TIMESERIES_UPDATED:
44 44 try {
45 45 JsonObject data = entityData.getAsJsonObject();
46   - long ts = System.currentTimeMillis();
  46 + long ts;
47 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 52 builder.setPostTelemetryMsg(JsonConverter.convertToTelemetryProto(data.getAsJsonObject("data"), ts));
51 53 } catch (Exception e) {
... ...
... ... @@ -68,6 +68,8 @@ public class RuleChainUpdateMsgConstructor {
68 68 .addAllRuleChainConnections(constructRuleChainConnections(ruleChainMetaData.getRuleChainConnections()));
69 69 if (ruleChainMetaData.getFirstNodeIndex() != null) {
70 70 builder.setFirstNodeIndex(ruleChainMetaData.getFirstNodeIndex());
  71 + } else {
  72 + builder.setFirstNodeIndex(-1);
71 73 }
72 74 builder.setMsgType(msgType);
73 75 return builder.build();
... ...
... ... @@ -22,12 +22,11 @@ import org.thingsboard.server.common.data.edge.EdgeSearchQuery;
22 22 import org.thingsboard.server.common.data.id.CustomerId;
23 23 import org.thingsboard.server.common.data.id.DashboardId;
24 24 import org.thingsboard.server.common.data.id.EdgeId;
  25 +import org.thingsboard.server.common.data.id.EntityId;
25 26 import org.thingsboard.server.common.data.id.RuleChainId;
26 27 import org.thingsboard.server.common.data.id.TenantId;
27 28 import org.thingsboard.server.common.data.page.TextPageData;
28 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 31 import java.util.List;
33 32 import java.util.Optional;
... ... @@ -75,6 +74,8 @@ public interface EdgeService {
75 74 ListenableFuture<List<Edge>> findEdgesByTenantIdAndRuleChainId(TenantId tenantId, RuleChainId ruleChainId);
76 75
77 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 31 import org.thingsboard.server.common.data.EntitySubtype;
32 32 import org.thingsboard.server.common.data.EntityType;
33 33 import org.thingsboard.server.common.data.Tenant;
  34 +import org.thingsboard.server.common.data.User;
34 35 import org.thingsboard.server.common.data.edge.Edge;
35 36 import org.thingsboard.server.common.data.edge.EdgeSearchQuery;
36 37 import org.thingsboard.server.common.data.id.CustomerId;
37 38 import org.thingsboard.server.common.data.id.DashboardId;
38 39 import org.thingsboard.server.common.data.id.EdgeId;
39 40 import org.thingsboard.server.common.data.id.EntityId;
  41 +import org.thingsboard.server.common.data.id.IdBased;
40 42 import org.thingsboard.server.common.data.id.RuleChainId;
41 43 import org.thingsboard.server.common.data.id.TenantId;
  44 +import org.thingsboard.server.common.data.id.UserId;
42 45 import org.thingsboard.server.common.data.page.TextPageData;
43 46 import org.thingsboard.server.common.data.page.TextPageLink;
44 47 import org.thingsboard.server.common.data.relation.EntityRelation;
45 48 import org.thingsboard.server.common.data.relation.EntitySearchDirection;
  49 +import org.thingsboard.server.common.data.relation.RelationTypeGroup;
46 50 import org.thingsboard.server.common.data.rule.RuleChain;
47 51 import org.thingsboard.server.dao.asset.AssetService;
48 52 import org.thingsboard.server.dao.customer.CustomerDao;
... ... @@ -51,12 +55,14 @@ import org.thingsboard.server.dao.device.DeviceService;
51 55 import org.thingsboard.server.dao.entity.AbstractEntityService;
52 56 import org.thingsboard.server.dao.entityview.EntityViewService;
53 57 import org.thingsboard.server.dao.exception.DataValidationException;
  58 +import org.thingsboard.server.dao.model.ModelConstants;
54 59 import org.thingsboard.server.dao.relation.RelationService;
55 60 import org.thingsboard.server.dao.rule.RuleChainService;
56 61 import org.thingsboard.server.dao.service.DataValidator;
57 62 import org.thingsboard.server.dao.service.PaginatedRemover;
58 63 import org.thingsboard.server.dao.service.Validator;
59 64 import org.thingsboard.server.dao.tenant.TenantDao;
  65 +import org.thingsboard.server.dao.user.UserService;
60 66
61 67 import javax.annotation.Nullable;
62 68 import java.util.ArrayList;
... ... @@ -93,6 +99,9 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic
93 99 private CustomerDao customerDao;
94 100
95 101 @Autowired
  102 + private UserService userService;
  103 +
  104 + @Autowired
96 105 private CacheManager cacheManager;
97 106
98 107 @Autowired
... ... @@ -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 232 TextPageData<Edge> edgesByTenantId = ctx.getEdgeService().findEdgesByTenantId(tenantId, new TextPageLink(Integer.MAX_VALUE));
233 233 return Futures.immediateFuture(edgesByTenantId.getData().stream().map(IdBased::getId).collect(Collectors.toList()));
234 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 284 }
285 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 287 return dashboard;
295 288 }
296 289
... ... @@ -298,7 +291,6 @@ function DashboardService($rootScope, $http, $q, $location, $filter) {
298 291 delete dashboard.publicCustomerId;
299 292 delete dashboard.assignedCustomersText;
300 293 delete dashboard.assignedCustomersIds;
301   - delete dashboard.assignedEdgesIds;
302 294 return dashboard;
303 295 }
304 296
... ...
... ... @@ -19,6 +19,7 @@
19 19 <md-button ng-click="onManageAssets({event: $event})" ng-show="!isEdit && !isPublic" class="md-raised md-primary">{{ 'customer.manage-assets' | translate }}</md-button>
20 20 <md-button ng-click="onManageDevices({event: $event})" ng-show="!isEdit" class="md-raised md-primary">{{ 'customer.manage-devices' | translate }}</md-button>
21 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 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 25 <div layout="row">
... ...
... ... @@ -79,6 +79,20 @@ export default function CustomerController(customerService, $state, $stateParams
79 79 },
80 80 {
81 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 96 vm.grid.deleteItem($event, item);
83 97 },
84 98 name: function() { return $translate.instant('action.delete') },
... ... @@ -147,6 +161,7 @@ export default function CustomerController(customerService, $state, $stateParams
147 161 vm.openCustomerAssets = openCustomerAssets;
148 162 vm.openCustomerDevices = openCustomerDevices;
149 163 vm.openCustomerDashboards = openCustomerDashboards;
  164 + vm.openCustomerEdges = openCustomerEdges;
150 165
151 166 function deleteCustomerTitle(customer) {
152 167 return $translate.instant('customer.delete-customer-title', {customerTitle: customer.title});
... ... @@ -216,4 +231,11 @@ export default function CustomerController(customerService, $state, $stateParams
216 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 55 onManageAssets: '&',
56 56 onManageDevices: '&',
57 57 onManageDashboards: '&',
  58 + onManageEdges: '&',
58 59 onDeleteCustomer: '&'
59 60 }
60 61 };
... ...
... ... @@ -29,6 +29,7 @@
29 29 on-manage-assets="vm.openCustomerAssets(event, vm.grid.detailsConfig.currentItem)"
30 30 on-manage-devices="vm.openCustomerDevices(event, vm.grid.detailsConfig.currentItem)"
31 31 on-manage-dashboards="vm.openCustomerDashboards(event, vm.grid.detailsConfig.currentItem)"
  32 + on-manage-edges="vm.openCustomerEdges(event, vm.grid.detailsConfig.currentItem)"
32 33 on-delete-customer="vm.grid.deleteItem(event, vm.grid.detailsConfig.currentItem)"></tb-customer>
33 34 </md-tab>
34 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 552 $event.stopPropagation();
553 553 }
554 554 var pageSize = 10;
555   - ruleChainService.getRuleChains({limit: pageSize, textSearch: ''}).then(
  555 + ruleChainService.getEdgesRuleChains({limit: pageSize, textSearch: ''}).then(
556 556 function success(_ruleChains) {
557 557 var ruleChains = {
558 558 pageSize: pageSize,
... ...
... ... @@ -161,5 +161,28 @@ export default function EdgeRoutes($stateProvider, types) {
161 161 ncyBreadcrumb: {
162 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 53 fetchMoreItems_: function () {
54 54 if (vm.ruleChains.hasNext && !vm.ruleChains.pending) {
55 55 vm.ruleChains.pending = true;
56   - ruleChainService.getRuleChains(vm.ruleChains.nextPageLink).then(
  56 + ruleChainService.getEdgesRuleChains(vm.ruleChains.nextPageLink).then(
57 57 function success(ruleChains) {
58 58 vm.ruleChains.data = vm.ruleChains.data.concat(ruleChains.data);
59 59 vm.ruleChains.nextPageLink = ruleChains.nextPageLink;
... ...
... ... @@ -441,6 +441,7 @@
441 441 "manage-assets": "Manage assets",
442 442 "manage-devices": "Manage devices",
443 443 "manage-dashboards": "Manage dashboards",
  444 + "manage-edges": "Manage edges",
444 445 "title": "Title",
445 446 "title-required": "Title is required.",
446 447 "description": "Description",
... ... @@ -812,6 +813,8 @@
812 813 "assign-edges-text": "Assign { count, plural, 1 {1 edge} other {# edges} } to customer",
813 814 "unassign-edge-title": "Are you sure you want to unassign the edge '{{edgeName}}'?",
814 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 818 "make-public": "Make edge public",
816 819 "make-public-edge-title": "Are you sure you want to make the edge '{{edgeName}}' public?",
817 820 "make-public-edge-text": "After the confirmation the edge and all its data will be made public and accessible by others.",
... ...