Browse Source

Added Edge UI support and assignedEdges to Dashboard

pull/2818/head
Volodymyr Babak 7 years ago
parent
commit
00ce0707d7
  1. 3
      application/src/main/java/org/thingsboard/server/controller/BaseController.java
  2. 218
      application/src/main/java/org/thingsboard/server/controller/DashboardController.java
  3. 3
      application/src/main/java/org/thingsboard/server/service/security/permission/Operation.java
  4. 2
      application/src/test/java/org/thingsboard/server/controller/BaseEdgeControllerTest.java
  5. 8
      common/dao-api/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java
  6. 2
      common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java
  7. 56
      common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java
  8. 15
      common/data/src/main/java/org/thingsboard/server/common/data/ShortEdgeInfo.java
  9. 4
      common/data/src/main/java/org/thingsboard/server/common/data/audit/ActionType.java
  10. 11
      common/data/src/main/java/org/thingsboard/server/common/data/edge/Edge.java
  11. 3
      common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationTypeGroup.java
  12. 16
      dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java
  13. 16
      dao/src/main/java/org/thingsboard/server/dao/dashboard/CassandraDashboardInfoDao.java
  14. 11
      dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardInfoDao.java
  15. 129
      dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java
  16. 10
      dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeService.java
  17. 3
      dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
  18. 6
      dao/src/main/java/org/thingsboard/server/dao/model/nosql/EdgeEntity.java
  19. 20
      dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardEntity.java
  20. 6
      dao/src/main/java/org/thingsboard/server/dao/model/sql/EdgeEntity.java
  21. 16
      dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDao.java
  22. 2
      dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeRepository.java
  23. 2
      dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java
  24. 1
      dao/src/main/resources/sql/schema-entities.sql
  25. 36
      dao/src/test/java/org/thingsboard/server/dao/service/BaseDashboardServiceTest.java
  26. 2
      dao/src/test/java/org/thingsboard/server/dao/service/BaseEdgeServiceTest.java
  27. 2
      dao/src/test/java/org/thingsboard/server/dao/service/nosql/EdgeServiceNoSqlTest.java
  28. 2
      dao/src/test/java/org/thingsboard/server/dao/service/sql/EdgeServiceSqlTest.java
  29. 88
      ui/src/app/api/edge.service.js
  30. 35
      ui/src/app/api/entity.service.js
  31. 14
      ui/src/app/common/types.constant.js
  32. 4
      ui/src/app/edge/edge-card.tpl.html
  33. 33
      ui/src/app/edge/edge-fieldset.tpl.html
  34. 29
      ui/src/app/edge/edge.directive.js
  35. 33
      ui/src/app/edge/edge.routes.js
  36. 20
      ui/src/app/edge/edges.tpl.html
  37. 18
      ui/src/app/edge/index.js
  38. 11
      ui/src/app/entity/entity-subtype-autocomplete.directive.js
  39. 10
      ui/src/app/entity/entity-subtype-list.directive.js
  40. 11
      ui/src/app/entity/entity-subtype-select.directive.js
  41. 5
      ui/src/app/locale/locale.constant-en_US.json

3
application/src/main/java/org/thingsboard/server/controller/BaseController.java

@ -360,6 +360,9 @@ public abstract class BaseController {
case ENTITY_VIEW:
checkEntityViewId(new EntityViewId(entityId.getId()), operation);
return;
case EDGE:
checkEdgeId(new EdgeId(entityId.getId()), operation);
return;
default:
throw new IllegalArgumentException("Unsupported entity type: " + entityId.getEntityType());
}

218
application/src/main/java/org/thingsboard/server/controller/DashboardController.java

@ -15,7 +15,6 @@
*/
package org.thingsboard.server.controller;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
@ -32,10 +31,13 @@ import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.DashboardInfo;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.ShortCustomerInfo;
import org.thingsboard.server.common.data.ShortEdgeInfo;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
@ -473,4 +475,218 @@ public class DashboardController extends BaseController {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/{edgeId}/dashboard/{dashboardId}", method = RequestMethod.POST)
@ResponseBody
public Dashboard assignDashboardToEdge(@PathVariable("edgeId") String strEdgeId,
@PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException {
checkParameter("edgeId", strEdgeId);
checkParameter(DASHBOARD_ID, strDashboardId);
try {
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
Edge edge = checkEdgeId(edgeId, Operation.READ);
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
checkDashboardId(dashboardId, Operation.ASSIGN_TO_EDGE);
Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToEdge(getCurrentUser().getTenantId(), dashboardId, edgeId));
logEntityAction(dashboardId, savedDashboard,
null,
ActionType.ASSIGNED_TO_EDGE, null, strDashboardId, strEdgeId, edge.getName());
return savedDashboard;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.DASHBOARD), null,
null,
ActionType.ASSIGNED_TO_EDGE, e, strDashboardId, strEdgeId);
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/{edgeId}/dashboard/{dashboardId}", method = RequestMethod.DELETE)
@ResponseBody
public Dashboard unassignDashboardFromEdge(@PathVariable("edgeId") String strEdgeId,
@PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException {
checkParameter("edgeId", strEdgeId);
checkParameter(DASHBOARD_ID, strDashboardId);
try {
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
Edge edge = checkEdgeId(edgeId, Operation.READ);
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId, Operation.UNASSIGN_FROM_EDGE);
Dashboard savedDashboard = checkNotNull(dashboardService.unassignDashboardFromEdge(getCurrentUser().getTenantId(), dashboardId, edgeId));
logEntityAction(dashboardId, dashboard,
null,
ActionType.UNASSIGNED_FROM_EDGE, null, strDashboardId, edge.getId().toString(), edge.getName());
return savedDashboard;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.DASHBOARD), null,
null,
ActionType.UNASSIGNED_FROM_EDGE, e, strDashboardId);
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/dashboard/{dashboardId}/edges", method = RequestMethod.POST)
@ResponseBody
public Dashboard updateDashboardEdges(@PathVariable(DASHBOARD_ID) String strDashboardId,
@RequestBody String[] strEdgeIds) throws ThingsboardException {
checkParameter(DASHBOARD_ID, strDashboardId);
try {
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId, Operation.ASSIGN_TO_EDGE);
Set<EdgeId> edgeIds = new HashSet<>();
if (strEdgeIds != null) {
for (String strEdgeId : strEdgeIds) {
edgeIds.add(new EdgeId(toUUID(strEdgeId)));
}
}
Set<EdgeId> addedEdgeIds = new HashSet<>();
Set<EdgeId> removedEdgeIds = new HashSet<>();
for (EdgeId edgeId : edgeIds) {
if (!dashboard.isAssignedToEdge(edgeId)) {
addedEdgeIds.add(edgeId);
}
}
Set<ShortEdgeInfo> assignedEdges = dashboard.getAssignedEdges();
if (assignedEdges != null) {
for (ShortEdgeInfo edgeInfo : assignedEdges) {
if (!edgeIds.contains(edgeInfo.getEdgeId())) {
removedEdgeIds.add(edgeInfo.getEdgeId());
}
}
}
if (addedEdgeIds.isEmpty() && removedEdgeIds.isEmpty()) {
return dashboard;
} else {
Dashboard savedDashboard = null;
for (EdgeId edgeId : addedEdgeIds) {
savedDashboard = checkNotNull(dashboardService.assignDashboardToEdge(getCurrentUser().getTenantId(), dashboardId, edgeId));
ShortEdgeInfo edgeInfo = savedDashboard.getAssignedEdgeInfo(edgeId);
logEntityAction(dashboardId, savedDashboard,
null,
ActionType.ASSIGNED_TO_EDGE, null, strDashboardId, edgeId.toString(), edgeInfo.getTitle());
}
for (EdgeId edgeId : removedEdgeIds) {
ShortEdgeInfo edgeInfo = dashboard.getAssignedEdgeInfo(edgeId);
savedDashboard = checkNotNull(dashboardService.unassignDashboardFromEdge(getCurrentUser().getTenantId(), dashboardId, edgeId));
logEntityAction(dashboardId, dashboard,
null,
ActionType.UNASSIGNED_FROM_EDGE, null, strDashboardId, edgeId.toString(), edgeInfo.getTitle());
}
return savedDashboard;
}
} catch (Exception e) {
logEntityAction(emptyId(EntityType.DASHBOARD), null,
null,
ActionType.ASSIGNED_TO_EDGE, e, strDashboardId);
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/dashboard/{dashboardId}/edges/add", method = RequestMethod.POST)
@ResponseBody
public Dashboard addDashboardEdges(@PathVariable(DASHBOARD_ID) String strDashboardId,
@RequestBody String[] strEdgeIds) throws ThingsboardException {
checkParameter(DASHBOARD_ID, strDashboardId);
try {
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId, Operation.ASSIGN_TO_EDGE);
Set<EdgeId> edgeIds = new HashSet<>();
if (strEdgeIds != null) {
for (String strEdgeId : strEdgeIds) {
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
if (!dashboard.isAssignedToEdge(edgeId)) {
edgeIds.add(edgeId);
}
}
}
if (edgeIds.isEmpty()) {
return dashboard;
} else {
Dashboard savedDashboard = null;
for (EdgeId edgeId : edgeIds) {
savedDashboard = checkNotNull(dashboardService.assignDashboardToEdge(getCurrentUser().getTenantId(), dashboardId, edgeId));
ShortEdgeInfo edgeInfo = savedDashboard.getAssignedEdgeInfo(edgeId);
logEntityAction(dashboardId, savedDashboard,
null,
ActionType.ASSIGNED_TO_EDGE, null, strDashboardId, edgeId.toString(), edgeInfo.getTitle());
}
return savedDashboard;
}
} catch (Exception e) {
logEntityAction(emptyId(EntityType.DASHBOARD), null,
null,
ActionType.ASSIGNED_TO_EDGE, e, strDashboardId);
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/dashboard/{dashboardId}/edges/remove", method = RequestMethod.POST)
@ResponseBody
public Dashboard removeDashboardEdges(@PathVariable(DASHBOARD_ID) String strDashboardId,
@RequestBody String[] strEdgeIds) throws ThingsboardException {
checkParameter(DASHBOARD_ID, strDashboardId);
try {
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId, Operation.UNASSIGN_FROM_EDGE);
Set<EdgeId> edgeIds = new HashSet<>();
if (strEdgeIds != null) {
for (String strEdgeId : strEdgeIds) {
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
if (dashboard.isAssignedToEdge(edgeId)) {
edgeIds.add(edgeId);
}
}
}
if (edgeIds.isEmpty()) {
return dashboard;
} else {
Dashboard savedDashboard = null;
for (EdgeId edgeId : edgeIds) {
ShortEdgeInfo edgeInfo = dashboard.getAssignedEdgeInfo(edgeId);
savedDashboard = checkNotNull(dashboardService.unassignDashboardFromEdge(getCurrentUser().getTenantId(), dashboardId, edgeId));
logEntityAction(dashboardId, dashboard,
null,
ActionType.UNASSIGNED_FROM_EDGE, null, strDashboardId, edgeId.toString(), edgeInfo.getTitle());
}
return savedDashboard;
}
} catch (Exception e) {
logEntityAction(emptyId(EntityType.DASHBOARD), null,
null,
ActionType.UNASSIGNED_FROM_EDGE, e, strDashboardId);
throw handleException(e);
}
}
}

3
application/src/main/java/org/thingsboard/server/service/security/permission/Operation.java

@ -18,6 +18,7 @@ package org.thingsboard.server.service.security.permission;
public enum Operation {
ALL, CREATE, READ, WRITE, DELETE, ASSIGN_TO_CUSTOMER, UNASSIGN_FROM_CUSTOMER, RPC_CALL,
READ_CREDENTIALS, WRITE_CREDENTIALS, READ_ATTRIBUTES, WRITE_ATTRIBUTES, READ_TELEMETRY, WRITE_TELEMETRY, CLAIM_DEVICES
READ_CREDENTIALS, WRITE_CREDENTIALS, READ_ATTRIBUTES, WRITE_ATTRIBUTES, READ_TELEMETRY, WRITE_TELEMETRY, CLAIM_DEVICES,
ASSIGN_TO_EDGE, UNASSIGN_FROM_EDGE
}

2
application/src/test/java/org/thingsboard/server/controller/BaseEdgeControllerTest.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,

8
common/dao-api/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java

@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.DashboardInfo;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
@ -54,4 +55,11 @@ public interface DashboardService {
void updateCustomerDashboards(TenantId tenantId, CustomerId customerId);
Dashboard assignDashboardToEdge(TenantId tenantId, DashboardId dashboardId, EdgeId edgeId);
Dashboard unassignDashboardFromEdge(TenantId tenantId, DashboardId dashboardId, EdgeId edgeId);
void unassignEdgeDashboards(TenantId tenantId, EdgeId edgeId);
void updateEdgeDashboards(TenantId tenantId, EdgeId edgeId);
}

2
common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.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,

56
common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java

@ -16,8 +16,12 @@
package org.thingsboard.server.common.data;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.Setter;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import java.util.*;
@ -28,6 +32,9 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa
private String title;
private Set<ShortCustomerInfo> assignedCustomers;
@Getter @Setter
private Set<ShortEdgeInfo> assignedEdges;
public DashboardInfo() {
super();
}
@ -116,6 +123,55 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa
}
}
public boolean isAssignedToEdge(EdgeId edgeId) {
return this.assignedEdges != null && this.assignedEdges.contains(new ShortEdgeInfo(edgeId, null));
}
public ShortEdgeInfo getAssignedEdgeInfo(EdgeId edgeId) {
if (this.assignedEdges != null) {
for (ShortEdgeInfo edgeInfo : this.assignedEdges) {
if (edgeInfo.getEdgeId().equals(edgeId)) {
return edgeInfo;
}
}
}
return null;
}
public boolean addAssignedEdge(Edge edge) {
ShortEdgeInfo edgeInfo = edge.toShortEdgeInfo();
if (this.assignedEdges != null && this.assignedEdges.contains(edgeInfo)) {
return false;
} else {
if (this.assignedEdges == null) {
this.assignedEdges = new HashSet<>();
}
this.assignedEdges.add(edgeInfo);
return true;
}
}
public boolean updateAssignedEdge(Edge edge) {
ShortEdgeInfo edgeInfo = edge.toShortEdgeInfo();
if (this.assignedEdges != null && this.assignedEdges.contains(edgeInfo)) {
this.assignedEdges.remove(edgeInfo);
this.assignedEdges.add(edgeInfo);
return true;
} else {
return false;
}
}
public boolean removeAssignedEdge(Edge edge) {
ShortEdgeInfo edgeInfo = edge.toShortEdgeInfo();
if (this.assignedEdges != null && this.assignedEdges.contains(edgeInfo)) {
this.assignedEdges.remove(edgeInfo);
return true;
} else {
return false;
}
}
@Override
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
public String getName() {

15
common/data/src/main/java/org/thingsboard/server/common/data/ShortEdgeInfo.java

@ -0,0 +1,15 @@
package org.thingsboard.server.common.data;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.thingsboard.server.common.data.id.EdgeId;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ShortEdgeInfo {
private EdgeId edgeId;
private String title;
}

4
common/data/src/main/java/org/thingsboard/server/common/data/audit/ActionType.java

@ -40,7 +40,9 @@ public enum ActionType {
ALARM_CLEAR(false),
LOGIN(false),
LOGOUT(false),
LOCKOUT(false);
LOCKOUT(false),
ASSIGNED_TO_EDGE(false), // log edge name
UNASSIGNED_FROM_EDGE(false); // log edge name
private final boolean isRead;

11
common/data/src/main/java/org/thingsboard/server/common/data/edge/Edge.java

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.common.data.edge;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.EqualsAndHashCode;
import lombok.Getter;
@ -24,6 +25,8 @@ import org.thingsboard.server.common.data.HasCustomerId;
import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo;
import org.thingsboard.server.common.data.ShortCustomerInfo;
import org.thingsboard.server.common.data.ShortEdgeInfo;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
@ -40,8 +43,8 @@ public class Edge extends SearchTextBasedWithAdditionalInfo<EdgeId> implements H
private CustomerId customerId;
private String name;
private String type;
private String label;
private transient JsonNode configuration;
private transient JsonNode additionalInfo;
public Edge() {
super();
@ -58,7 +61,11 @@ public class Edge extends SearchTextBasedWithAdditionalInfo<EdgeId> implements H
this.type = edge.getType();
this.name = edge.getName();
this.configuration = edge.getConfiguration();
this.additionalInfo = edge.getAdditionalInfo();
}
@JsonIgnore
public ShortEdgeInfo toShortEdgeInfo() {
return new ShortEdgeInfo(id, name);
}
@Override

3
common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationTypeGroup.java

@ -21,6 +21,7 @@ public enum RelationTypeGroup {
ALARM,
DASHBOARD,
RULE_CHAIN,
RULE_NODE
RULE_NODE,
EDGE
}

16
dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java

@ -260,6 +260,22 @@ public class AuditLogServiceImpl implements AuditLogService {
actionData.put("os", os);
actionData.put("device", device);
break;
case ASSIGNED_TO_EDGE:
strEntityId = extractParameter(String.class, 0, additionalInfo);
String strEdgeId = extractParameter(String.class, 1, additionalInfo);
String strEdgeName = extractParameter(String.class, 2, additionalInfo);
actionData.put("entityId", strEntityId);
actionData.put("assignedEdgeId", strEdgeId);
actionData.put("assignedEdgeName", strEdgeName);
break;
case UNASSIGNED_FROM_EDGE:
strEntityId = extractParameter(String.class, 0, additionalInfo);
strEdgeId = extractParameter(String.class, 1, additionalInfo);
strEdgeName = extractParameter(String.class, 2, additionalInfo);
actionData.put("entityId", strEntityId);
actionData.put("unassignedEdgeId", strEdgeId);
actionData.put("unassignedEdgeName", strEdgeName);
break;
}
return actionData;
}

16
dao/src/main/java/org/thingsboard/server/dao/dashboard/CassandraDashboardInfoDao.java

@ -23,6 +23,7 @@ import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.DashboardInfo;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.page.TimePageLink;
@ -88,4 +89,19 @@ public class CassandraDashboardInfoDao extends CassandraAbstractSearchTextDao<Da
});
}
@Override
public ListenableFuture<List<DashboardInfo>> findDashboardsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) {
log.debug("Try to find dashboards by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink);
ListenableFuture<List<EntityRelation>> relations = relationDao.findRelations(new TenantId(tenantId), new EdgeId(edgeId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE, EntityType.DASHBOARD, pageLink);
return Futures.transformAsync(relations, input -> {
List<ListenableFuture<DashboardInfo>> dashboardFutures = new ArrayList<>(input.size());
for (EntityRelation relation : input) {
dashboardFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId()));
}
return Futures.successfulAsList(dashboardFutures);
});
}
}

11
dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardInfoDao.java

@ -48,4 +48,15 @@ public interface DashboardInfoDao extends Dao<DashboardInfo> {
*/
ListenableFuture<List<DashboardInfo>> findDashboardsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TimePageLink pageLink);
/**
* Find dashboards by tenantId, edgeId and page link.
*
* @param tenantId the tenantId
* @param edgeId the edgeId
* @param pageLink the page link
* @return the list of dashboard objects
*/
ListenableFuture<List<DashboardInfo>> findDashboardsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink);
}

129
dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java

@ -26,8 +26,10 @@ import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.DashboardInfo;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
@ -36,6 +38,7 @@ import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.dao.customer.CustomerDao;
import org.thingsboard.server.dao.edge.EdgeDao;
import org.thingsboard.server.dao.entity.AbstractEntityService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.service.DataValidator;
@ -67,6 +70,9 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
@Autowired
private CustomerDao customerDao;
@Autowired
private EdgeDao edgeDao;
@Override
public Dashboard findDashboardById(TenantId tenantId, DashboardId dashboardId) {
@ -228,6 +234,80 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
new CustomerDashboardsUpdater(customer).removeEntities(tenantId, customer);
}
@Override
public Dashboard assignDashboardToEdge(TenantId tenantId, DashboardId dashboardId, EdgeId edgeId) {
Dashboard dashboard = findDashboardById(tenantId, dashboardId);
Edge edge = edgeDao.findById(tenantId, edgeId.getId());
if (edge == null) {
throw new DataValidationException("Can't assign dashboard to non-existent edge!");
}
if (!edge.getTenantId().getId().equals(dashboard.getTenantId().getId())) {
throw new DataValidationException("Can't assign dashboard to edge from different tenant!");
}
if (dashboard.addAssignedEdge(edge)) {
try {
createRelation(tenantId, new EntityRelation(edgeId, dashboardId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE));
} catch (ExecutionException | InterruptedException e) {
log.warn("[{}] Failed to create dashboard relation. Edge Id: [{}]", dashboardId, edgeId);
throw new RuntimeException(e);
}
return saveDashboard(dashboard);
} else {
return dashboard;
}
}
@Override
public Dashboard unassignDashboardFromEdge(TenantId tenantId, DashboardId dashboardId, EdgeId edgeId) {
Dashboard dashboard = findDashboardById(tenantId, dashboardId);
Edge edge = edgeDao.findById(tenantId, edgeId.getId());
if (edge == null) {
throw new DataValidationException("Can't unassign dashboard from non-existent edge!");
}
if (dashboard.removeAssignedEdge(edge)) {
try {
deleteRelation(tenantId, new EntityRelation(edgeId, dashboardId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE));
} catch (ExecutionException | InterruptedException e) {
log.warn("[{}] Failed to delete dashboard relation. Edge Id: [{}]", dashboardId, edgeId);
throw new RuntimeException(e);
}
return saveDashboard(dashboard);
} else {
return dashboard;
}
}
@Override
public void unassignEdgeDashboards(TenantId tenantId, EdgeId edgeId) {
log.trace("Executing unassignEdgeDashboards, edgeId [{}]", edgeId);
Validator.validateId(edgeId, "Incorrect edgeId " + edgeId);
Edge edge = edgeDao.findById(tenantId, edgeId.getId());
if (edge == null) {
throw new DataValidationException("Can't unassign dashboards from non-existent edge!");
}
new EdgeDashboardsUnassigner(edge).removeEntities(tenantId, edge);
}
@Override
public void updateEdgeDashboards(TenantId tenantId, EdgeId edgeId) {
log.trace("Executing updateEdgeDashboards, edgeId [{}]", edgeId);
Validator.validateId(edgeId, "Incorrect edgeId " + edgeId);
Edge edge = edgeDao.findById(tenantId, edgeId.getId());
if (edge == null) {
throw new DataValidationException("Can't update dashboards for non-existent edge!");
}
new EdgeDashboardsUpdater(edge).removeEntities(tenantId, edge);
}
private Dashboard updateAssignedEdge(TenantId tenantId, DashboardId dashboardId, Edge edge) {
Dashboard dashboard = findDashboardById(tenantId, dashboardId);
if (dashboard.updateAssignedEdge(edge)) {
return saveDashboard(dashboard);
} else {
return dashboard;
}
}
private DataValidator<Dashboard> dashboardValidator =
new DataValidator<Dashboard>() {
@Override
@ -310,4 +390,53 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
}
private class EdgeDashboardsUnassigner extends TimePaginatedRemover<Edge, DashboardInfo> {
private Edge edge;
EdgeDashboardsUnassigner(Edge edge) {
this.edge = edge;
}
@Override
protected List<DashboardInfo> findEntities(TenantId tenantId, Edge edge, TimePageLink pageLink) {
try {
return dashboardInfoDao.findDashboardsByTenantIdAndEdgeId(edge.getTenantId().getId(), edge.getId().getId(), pageLink).get();
} catch (InterruptedException | ExecutionException e) {
log.warn("Failed to get dashboards by tenantId [{}] and edgeId [{}].", edge.getTenantId().getId(), edge.getId().getId());
throw new RuntimeException(e);
}
}
@Override
protected void removeEntity(TenantId tenantId, DashboardInfo entity) {
unassignDashboardFromEdge(edge.getTenantId(), new DashboardId(entity.getUuidId()), this.edge.getId());
}
}
private class EdgeDashboardsUpdater extends TimePaginatedRemover<Edge, DashboardInfo> {
private Edge edge;
EdgeDashboardsUpdater(Edge edge) {
this.edge = edge;
}
@Override
protected List<DashboardInfo> findEntities(TenantId tenantId, Edge edge, TimePageLink pageLink) {
try {
return dashboardInfoDao.findDashboardsByTenantIdAndEdgeId(edge.getTenantId().getId(), edge.getId().getId(), pageLink).get();
} catch (InterruptedException | ExecutionException e) {
log.warn("Failed to get dashboards by tenantId [{}] and edgeId [{}].", edge.getTenantId().getId(), edge.getId().getId());
throw new RuntimeException(e);
}
}
@Override
protected void removeEntity(TenantId tenantId, DashboardInfo entity) {
updateAssignedEdge(edge.getTenantId(), new DashboardId(entity.getUuidId()), this.edge);
}
}
}

10
dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeService.java

@ -41,6 +41,7 @@ import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.EntitySearchDirection;
import org.thingsboard.server.dao.customer.CustomerDao;
import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.entity.AbstractEntityService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.service.DataValidator;
@ -84,6 +85,9 @@ public class BaseEdgeService extends AbstractEntityService implements EdgeServic
@Autowired
private CacheManager cacheManager;
@Autowired
private DashboardService dashboardService;
@Override
public Edge findEdgeById(TenantId tenantId, EdgeId edgeId) {
log.trace("Executing findEdgeById [{}]", edgeId);
@ -112,7 +116,9 @@ public class BaseEdgeService extends AbstractEntityService implements EdgeServic
public Edge saveEdge(Edge edge) {
log.trace("Executing saveEdge [{}]", edge);
edgeValidator.validate(edge, Edge::getTenantId);
return edgeDao.save(edge.getTenantId(), edge);
Edge savedEdge = edgeDao.save(edge.getTenantId(), edge);
dashboardService.updateEdgeDashboards(savedEdge.getTenantId(), savedEdge.getId());
return savedEdge;
}
@Override
@ -144,6 +150,8 @@ public class BaseEdgeService extends AbstractEntityService implements EdgeServic
Cache cache = cacheManager.getCache(EDGE_CACHE);
cache.evict(list);
dashboardService.unassignEdgeDashboards(tenantId, edgeId);
edgeDao.removeById(tenantId, edgeId.getId());
}

3
dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java

@ -292,6 +292,7 @@ public class ModelConstants {
public static final String DASHBOARD_TITLE_PROPERTY = TITLE_PROPERTY;
public static final String DASHBOARD_CONFIGURATION_PROPERTY = "configuration";
public static final String DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY = "assigned_customers";
public static final String DASHBOARD_ASSIGNED_EDGES_PROPERTY = "assigned_edges";
public static final String DASHBOARD_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "dashboard_by_tenant_and_search_text";
@ -335,6 +336,7 @@ public class ModelConstants {
public static final String RULE_CHAIN_FIRST_RULE_NODE_ID_PROPERTY = "first_rule_node_id";
public static final String RULE_CHAIN_ROOT_PROPERTY = "root";
public static final String RULE_CHAIN_CONFIGURATION_PROPERTY = "configuration";
public static final String RULE_CHAIN_ASSIGNED_EDGES_PROPERTY = "assigned_edges";
public static final String RULE_CHAIN_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "rule_chain_by_tenant_and_search_text";
@ -354,6 +356,7 @@ public class ModelConstants {
public static final String EDGE_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY;
public static final String EDGE_CUSTOMER_ID_PROPERTY = CUSTOMER_ID_PROPERTY;
public static final String EDGE_NAME_PROPERTY = "name";
public static final String EDGE_LABEL_PROPERTY = "label";
public static final String EDGE_TYPE_PROPERTY = "type";
public static final String EDGE_CONFIGURATION_PROPERTY = "configuration";
public static final String EDGE_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY;

6
dao/src/main/java/org/thingsboard/server/dao/model/nosql/EdgeEntity.java

@ -35,6 +35,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.EDGE_ADDITIONAL_IN
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_COLUMN_FAMILY_NAME;
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_CONFIGURATION_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_CUSTOMER_ID_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_LABEL_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_NAME_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_TENANT_ID_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_TYPE_PROPERTY;
@ -63,6 +64,9 @@ public class EdgeEntity implements SearchTextEntity<Edge> {
@Column(name = EDGE_NAME_PROPERTY)
private String name;
@Column(name = EDGE_LABEL_PROPERTY)
private String label;
@Column(name = SEARCH_TEXT_PROPERTY)
private String searchText;
@ -85,6 +89,7 @@ public class EdgeEntity implements SearchTextEntity<Edge> {
}
this.type = edge.getType();
this.name = edge.getName();
this.label = edge.getLabel();
this.configuration = edge.getConfiguration();
this.additionalInfo = edge.getAdditionalInfo();
}
@ -106,6 +111,7 @@ public class EdgeEntity implements SearchTextEntity<Edge> {
}
edge.setType(type);
edge.setName(name);
edge.setLabel(label);
edge.setConfiguration(configuration);
edge.setAdditionalInfo(additionalInfo);
return edge;

20
dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardEntity.java

@ -28,6 +28,7 @@ import org.hibernate.annotations.TypeDef;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.ShortCustomerInfo;
import org.thingsboard.server.common.data.ShortEdgeInfo;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.model.BaseSqlEntity;
@ -52,6 +53,8 @@ public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements S
private static final ObjectMapper objectMapper = new ObjectMapper();
private static final JavaType assignedCustomersType =
objectMapper.getTypeFactory().constructCollectionType(HashSet.class, ShortCustomerInfo.class);
private static final JavaType assignedEdgesType =
objectMapper.getTypeFactory().constructCollectionType(HashSet.class, ShortEdgeInfo.class);
@Column(name = ModelConstants.DASHBOARD_TENANT_ID_PROPERTY)
private String tenantId;
@ -65,6 +68,9 @@ public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements S
@Column(name = ModelConstants.DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY)
private String assignedCustomers;
@Column(name = ModelConstants.DASHBOARD_ASSIGNED_EDGES_PROPERTY)
private String assignedEdges;
@Type(type = "json")
@Column(name = ModelConstants.DASHBOARD_CONFIGURATION_PROPERTY)
private JsonNode configuration;
@ -88,6 +94,13 @@ public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements S
log.error("Unable to serialize assigned customers to string!", e);
}
}
if (dashboard.getAssignedEdges() != null) {
try {
this.assignedEdges = objectMapper.writeValueAsString(dashboard.getAssignedEdges());
} catch (JsonProcessingException e) {
log.error("Unable to serialize assigned edges to string!", e);
}
}
this.configuration = dashboard.getConfiguration();
}
@ -116,6 +129,13 @@ public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements S
log.warn("Unable to parse assigned customers!", e);
}
}
if (!StringUtils.isEmpty(assignedEdges)) {
try {
dashboard.setAssignedEdges(objectMapper.readValue(assignedEdges, assignedEdgesType));
} catch (IOException e) {
log.warn("Unable to parse assigned edges!", e);
}
}
dashboard.setConfiguration(configuration);
return dashboard;
}

6
dao/src/main/java/org/thingsboard/server/dao/model/sql/EdgeEntity.java

@ -38,6 +38,7 @@ 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_TENANT_ID_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_TYPE_PROPERTY;
@ -62,6 +63,9 @@ public class EdgeEntity extends BaseSqlEntity<Edge> implements SearchTextEntity<
@Column(name = EDGE_NAME_PROPERTY)
private String name;
@Column(name = EDGE_LABEL_PROPERTY)
private String label;
@Column(name = SEARCH_TEXT_PROPERTY)
private String searchText;
@ -89,6 +93,7 @@ public class EdgeEntity extends BaseSqlEntity<Edge> implements SearchTextEntity<
}
this.type = edge.getType();
this.name = edge.getName();
this.label = edge.getLabel();
this.configuration = edge.getConfiguration();
this.additionalInfo = edge.getAdditionalInfo();
}
@ -119,6 +124,7 @@ public class EdgeEntity extends BaseSqlEntity<Edge> implements SearchTextEntity<
}
edge.setType(type);
edge.setName(name);
edge.setLabel(label);
edge.setConfiguration(configuration);
edge.setAdditionalInfo(additionalInfo);
return edge;

16
dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDao.java

@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.DashboardInfo;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.UUIDConverter;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.page.TimePageLink;
@ -93,4 +94,19 @@ public class JpaDashboardInfoDao extends JpaAbstractSearchTextDao<DashboardInfoE
return Futures.successfulAsList(dashboardFutures);
});
}
@Override
public ListenableFuture<List<DashboardInfo>> findDashboardsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) {
log.debug("Try to find dashboards by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink);
ListenableFuture<List<EntityRelation>> relations = relationDao.findRelations(new TenantId(tenantId), new EdgeId(edgeId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE, EntityType.DASHBOARD, pageLink);
return Futures.transformAsync(relations, input -> {
List<ListenableFuture<DashboardInfo>> dashboardFutures = new ArrayList<>(input.size());
for (EntityRelation relation : input) {
dashboardFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId()));
}
return Futures.successfulAsList(dashboardFutures);
});
}
}

2
dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeRepository.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,

2
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,

1
dao/src/main/resources/sql/schema-entities.sql

@ -256,6 +256,7 @@ CREATE TABLE IF NOT EXISTS edge (
configuration varchar(10000000),
type varchar(255),
name varchar(255),
label varchar(255),
search_text varchar(255),
tenant_id varchar(31)
);

36
dao/src/test/java/org/thingsboard/server/dao/service/BaseDashboardServiceTest.java

@ -25,7 +25,9 @@ import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.DashboardInfo;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
@ -328,4 +330,38 @@ public abstract class BaseDashboardServiceTest extends AbstractServiceTest {
tenantService.deleteTenant(tenantId);
}
@Test(expected = DataValidationException.class)
public void testAssignDashboardToNonExistentEdge() {
Dashboard dashboard = new Dashboard();
dashboard.setTitle("My dashboard");
dashboard.setTenantId(tenantId);
dashboard = dashboardService.saveDashboard(dashboard);
try {
dashboardService.assignDashboardToEdge(tenantId, dashboard.getId(), new EdgeId(UUIDs.timeBased()));
} finally {
dashboardService.deleteDashboard(tenantId, dashboard.getId());
}
}
@Test(expected = DataValidationException.class)
public void testAssignDashboardToEdgeFromDifferentTenant() {
Dashboard dashboard = new Dashboard();
dashboard.setTitle("My dashboard");
dashboard.setTenantId(tenantId);
dashboard = dashboardService.saveDashboard(dashboard);
Tenant tenant = new Tenant();
tenant.setTitle("Test different tenant");
tenant = tenantService.saveTenant(tenant);
Edge edge = new Edge();
edge.setTenantId(tenant.getId());
edge.setName("Test different edge");
edge = edgeService.saveEdge(edge);
try {
dashboardService.assignDashboardToEdge(tenantId, dashboard.getId(), edge.getId());
} finally {
dashboardService.deleteDashboard(tenantId, dashboard.getId());
tenantService.deleteTenant(tenant.getId());
}
}
}

2
dao/src/test/java/org/thingsboard/server/dao/service/BaseEdgeServiceTest.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,

2
dao/src/test/java/org/thingsboard/server/dao/service/nosql/EdgeServiceNoSqlTest.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,

2
dao/src/test/java/org/thingsboard/server/dao/service/sql/EdgeServiceSqlTest.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,

88
ui/src/app/api/edge.service.js

@ -18,14 +18,19 @@ export default angular.module('thingsboard.api.edge', [])
.name;
/*@ngInject*/
function EdgeService($http, $q) {
function EdgeService($http, $q, customerService) {
var service = {
getEdges: getEdges,
getEdgesByIds: getEdgesByIds,
getEdge: getEdge,
deleteEdge: deleteEdge,
saveEdge: saveEdge
saveEdge: saveEdge,
getEdgeTypes: getEdgeTypes,
getTenantEdges: getTenantEdges,
assignEdgeToCustomer: assignEdgeToCustomer,
unassignEdgeFromCustomer: unassignEdgeFromCustomer,
makeEdgePublic: makeEdgePublic
};
return service;
@ -108,4 +113,83 @@ function EdgeService($http, $q) {
});
return deferred.promise;
}
function getEdgeTypes(config) {
var deferred = $q.defer();
var url = '/api/edge/types';
$http.get(url, config).then(function success(response) {
deferred.resolve(response.data);
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function getTenantEdges(pageLink, applyCustomersInfo, config, type) {
var deferred = $q.defer();
var url = '/api/tenant/edges?limit=' + pageLink.limit;
if (angular.isDefined(pageLink.textSearch)) {
url += '&textSearch=' + pageLink.textSearch;
}
if (angular.isDefined(pageLink.idOffset)) {
url += '&idOffset=' + pageLink.idOffset;
}
if (angular.isDefined(pageLink.textOffset)) {
url += '&textOffset=' + pageLink.textOffset;
}
if (angular.isDefined(type) && type.length) {
url += '&type=' + type;
}
$http.get(url, config).then(function success(response) {
if (applyCustomersInfo) {
customerService.applyAssignedCustomersInfo(response.data.data).then(
function success(data) {
response.data.data = data;
deferred.resolve(response.data);
},
function fail() {
deferred.reject();
}
);
} else {
deferred.resolve(response.data);
}
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function assignEdgeToCustomer(customerId, edgeId) {
var deferred = $q.defer();
var url = '/api/customer/' + customerId + '/edge/' + edgeId;
$http.post(url, null).then(function success(response) {
deferred.resolve(response.data);
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function unassignEdgeFromCustomer(edgeId) {
var deferred = $q.defer();
var url = '/api/customer/edge/' + edgeId;
$http.delete(url).then(function success(response) {
deferred.resolve(response.data);
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function makeEdgePublic(edgeId) {
var deferred = $q.defer();
var url = '/api/customer/public/edge/' + edgeId;
$http.post(url, null).then(function success(response) {
deferred.resolve(response.data);
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
}

35
ui/src/app/api/entity.service.js

@ -22,7 +22,7 @@ export default angular.module('thingsboard.api.entity', [thingsboardTypes])
/*@ngInject*/
function EntityService($http, $q, $filter, $translate, $log, userService, deviceService, assetService, tenantService,
customerService, ruleChainService, dashboardService, entityRelationService, attributeService,
entityViewService, types, utils) {
entityViewService, edgeService, types, utils) {
var service = {
getEntity: getEntity,
getEntities: getEntities,
@ -77,6 +77,9 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
case types.entityType.alarm:
$log.error('Get Alarm Entity is not implemented!');
break;
case types.entityType.edge:
promise = edgeService.getEdge(entityId, true, config);
break;
}
return promise;
}
@ -159,6 +162,10 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
case types.entityType.alarm:
$log.error('Get Alarm Entity is not implemented!');
break;
case types.entityType.edge:
promise = getEntitiesByIdsPromise(
(id) => edgeService.getEdge(id, config), entityIds);
break;
}
return promise;
}
@ -285,6 +292,13 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
case types.entityType.alarm:
$log.error('Get Alarm Entities is not implemented!');
break;
case types.entityType.edge:
if (user.authority === 'CUSTOMER_USER') {
promise = edgeService.getCustomerEdges(customerId, pageLink, false, config, subType);
} else {
promise = edgeService.getTenantEdges(pageLink, false, config, subType);
}
break;
}
return promise;
}
@ -655,6 +669,9 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
} else if (filter.type == types.aliasFilterType.entityViewSearchQuery.value) {
searchQuery.entityViewTypes = filter.entityViewTypes;
findByQueryPromise = entityViewService.findByQuery(searchQuery, false, {ignoreLoading: true});
} else if (filter.type == types.aliasFilterType.edgeSearchQuery.value) {
searchQuery.edgeTypes = filter.edgeTypes;
findByQueryPromise = edgeService.findByQuery(searchQuery, false, {ignoreLoading: true});
}
findByQueryPromise.then(
function success(entities) {
@ -699,6 +716,8 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
return entityTypes.indexOf(types.entityType.device) > -1 ? true : false;
case types.aliasFilterType.entityViewType.value:
return entityTypes.indexOf(types.entityType.entityView) > -1 ? true : false;
case types.aliasFilterType.edgeType.value:
return entityTypes.indexOf(types.entityType.edge) > -1 ? true : false;
case types.aliasFilterType.relationsQuery.value:
if (filter.filters && filter.filters.length) {
var match = false;
@ -726,6 +745,8 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
return entityTypes.indexOf(types.entityType.device) > -1 ? true : false;
case types.aliasFilterType.entityViewSearchQuery.value:
return entityTypes.indexOf(types.entityType.entityView) > -1 ? true : false;
case types.aliasFilterType.edgeSearchQuery.value:
return entityTypes.indexOf(types.entityType.edge) > -1 ? true : false;
}
}
return false;
@ -755,6 +776,8 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
return entityType === types.entityType.device;
case types.aliasFilterType.entityViewSearchQuery.value:
return entityType === types.entityType.entityView;
case types.aliasFilterType.edgeSearchQuery.value:
return entityType === types.entityType.edge;
}
return false;
}
@ -796,6 +819,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
entityTypes.device = types.entityType.device;
entityTypes.asset = types.entityType.asset;
entityTypes.entityView = types.entityType.entityView;
entityTypes.edge = types.entityType.edge;
entityTypes.tenant = types.entityType.tenant;
entityTypes.customer = types.entityType.customer;
entityTypes.dashboard = types.entityType.dashboard;
@ -807,6 +831,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
entityTypes.device = types.entityType.device;
entityTypes.asset = types.entityType.asset;
entityTypes.entityView = types.entityType.entityView;
entityTypes.edge = types.entityType.edge;
entityTypes.customer = types.entityType.customer;
entityTypes.dashboard = types.entityType.dashboard;
if (useAliasEntityTypes) {
@ -1041,6 +1066,8 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
findByQueryPromise = assetService.findByQuery(entitySearchQuery, true, {ignoreLoading: true});
} else if (entityType == types.entityType.device) {
findByQueryPromise = deviceService.findByQuery(entitySearchQuery, true, {ignoreLoading: true});
} else if (entityType == types.entityType.entityView) {
findByQueryPromise = entityViewService.findByQuery(entitySearchQuery, true, {ignoreLoading: true});
}
findByQueryPromise.then(
function success(entities) {
@ -1220,6 +1247,8 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
return deviceService.deleteDevice(entityId.id);
} else if (entityId.entityType == types.entityType.entityView) {
return entityViewService.deleteEntityView(entityId.id);
} else if (entityId.entityType == types.entityType.edge) {
return edgeService.deleteEdge(entityId.id);
}
}
@ -1327,6 +1356,8 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
return deviceService.saveDevice(entity);
} else if (entityType == types.entityType.entityView) {
return entityViewService.saveEntityView(entity);
} else if (entityType == types.entityType.edge) {
return edgeService.saveEdge(entity);
}
}
@ -1457,6 +1488,8 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
searchQuery.deviceTypes = entitySubTypes;
} else if (entityType == types.entityType.entityView) {
searchQuery.entityViewTypes = entitySubTypes;
} else if (entityType == types.entityType.edge) {
searchQuery.edgeTypes = entitySubTypes;
} else {
return null; //Not supported
}

14
ui/src/app/common/types.constant.js

@ -222,6 +222,12 @@ export default angular.module('thingsboard.types', [])
},
"LOCKOUT": {
name: "audit-log.type-lockout"
},
"ASSIGNED_TO_EDGE": {
name: "audit-log.type-assigned-to-edge"
},
"UNASSIGNED_FROM_EDGE": {
name: "audit-log.type-unassigned-from-edge"
}
},
auditLogActionStatus: {
@ -269,6 +275,10 @@ export default angular.module('thingsboard.types', [])
value: 'entityViewType',
name: 'alias.filter-type-entity-view-type'
},
edgeType: {
value: 'edgeType',
name: 'alias.filter-type-edge-type'
},
relationsQuery: {
value: 'relationsQuery',
name: 'alias.filter-type-relations-query'
@ -284,6 +294,10 @@ export default angular.module('thingsboard.types', [])
entityViewSearchQuery: {
value: 'entityViewSearchQuery',
name: 'alias.filter-type-entity-view-search-query'
},
edgeSearchQuery: {
value: 'edgeSearchQuery',
name: 'alias.filter-type-edge-search-query'
}
},
direction: {

4
ui/src/app/edge/edge-card.tpl.html

@ -16,6 +16,8 @@
-->
<div flex layout="column">
<div flex style="text-transform: uppercase;">{{ vm.types.edgeType[vm.item.type].name | translate }}</div>
<div style="text-transform: uppercase; padding-bottom: 5px;">{{vm.item.type}}</div>
<div class="tb-card-description">{{vm.item.additionalInfo.description}}</div>
<div style="padding-top: 5px;" class="tb-small" ng-show="vm.isAssignedToCustomer()">{{'edge.assignedToCustomer' | translate}} '{{vm.item.assignedCustomer.title}}'</div>
<div style="padding-top: 5px;" class="tb-small" ng-show="vm.isPublic()">{{'edge.public' | translate}}</div>
</div>

33
ui/src/app/edge/edge-fieldset.tpl.html

@ -15,12 +15,15 @@
limitations under the License.
-->
<md-button ng-click="onExportEdge({event: $event})"
ng-show="!isEdit"
class="md-raised md-primary">{{ 'edge.export' | translate }}</md-button>
<md-button ng-click="onAssignToCustomer({event: $event})"
ng-show="!isEdit && edgeScope === 'tenant' && !isAssignedToCustomer"
class="md-raised md-primary">{{ 'edge.assign-to-customer' | translate }}</md-button>
<md-button ng-click="onUnassignFromCustomer({event: $event, isPublic: isPublic})"
ng-show="!isEdit && (edgeScope === 'customer' || edgeScope === 'tenant') && isAssignedToCustomer"
class="md-raised md-primary">{{ isPublic ? 'edge.make-private' : 'edge.unassign-from-customer' | translate }}</md-button>
<md-button ng-click="onDeleteEdge({event: $event})"
ng-if="'edge' | hasGenericPermission:'delete'"
ng-show="!isEdit" class="md-raised md-primary">{{ 'edge.delete' | translate }}</md-button>
ng-show="!isEdit && edgeScope === 'tenant'"
class="md-raised md-primary">{{ 'edge.delete' | translate }}</md-button>
<div layout="row">
<md-button ngclipboard data-clipboard-action="copy"
@ -33,6 +36,15 @@
</div>
<md-content class="md-padding" layout="column">
<md-input-container class="md-block"
ng-show="!isEdit && isAssignedToCustomer && !isPublic && edgeScope === 'tenant'">
<label translate>edge.assignedToCustomer</label>
<input ng-model="assignedCustomer.title" disabled>
</md-input-container>
<div class="tb-small" style="padding-bottom: 10px; padding-left: 2px;"
ng-show="!isEdit && isPublic && (edgeScope === 'customer' || edgeScope === 'tenant')">
{{ 'edge.edge-public' | translate }}
</div>
<fieldset ng-disabled="$root.loading || !isEdit">
<md-input-container class="md-block">
<label translate>edge.name</label>
@ -41,6 +53,17 @@
<div translate ng-message="required">edge.name-required</div>
</div>
</md-input-container>
<tb-entity-subtype-autocomplete
ng-disabled="$root.loading || !isEdit"
tb-required="true"
the-form="theForm"
ng-model="edge.type"
entity-type="types.entityType.edge">
</tb-entity-subtype-autocomplete>
<md-input-container class="md-block">
<label translate>edge.label</label>
<input name="label" ng-model="edge.label">
</md-input-container>
<md-input-container class="md-block">
<label translate>edge.description</label>
<textarea ng-model="edge.additionalInfo.description" rows="2"></textarea>

29
ui/src/app/edge/edge.directive.js

@ -20,12 +20,33 @@ 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) {
export default function EdgeDirective($compile, $templateCache, $translate, $mdDialog, $document, toast, types, customerService) {
var linker = function (scope, element) {
var template = $templateCache.get(edgeFieldsetTemplate);
element.html(template);
scope.types = types;
scope.isAssignedToCustomer = false;
scope.isPublic = false;
scope.assignedCustomer = null;
scope.$watch('edge', function(newVal) {
if (newVal) {
if (scope.edge.customerId && scope.edge.customerId.id !== types.id.nullUid) {
scope.isAssignedToCustomer = true;
customerService.getShortCustomerInfo(scope.edge.customerId.id).then(
function success(customer) {
scope.assignedCustomer = customer;
scope.isPublic = customer.isPublic;
}
);
} else {
scope.isAssignedToCustomer = false;
scope.isPublic = false;
scope.assignedCustomer = null;
}
}
});
scope.onEdgeIdCopied = function() {
toast.showSuccess($translate.instant('edge.idCopiedMessage'), 750, angular.element(element).parent().parent(), 'bottom left');
@ -40,9 +61,11 @@ export default function EdgeDirective($compile, $templateCache, $translate, $mdD
scope: {
edge: '=',
isEdit: '=',
edgeScope: '=',
theForm: '=',
isCreate: '<',
onExportEdge: '&',
onAssignToCustomer: '&',
onMakePublic: '&',
onUnassignFromCustomer: '&',
onDeleteEdge: '&'
}
};

33
ui/src/app/edge/edge.routes.js

@ -20,13 +20,35 @@ import edgesTemplate from './edges.tpl.html';
/* eslint-enable import/no-unresolved, import/default */
/*@ngInject*/
export default function EdgeRoutes($stateProvider) {
export default function EdgeRoutes($stateProvider, types) {
$stateProvider
.state('home.edges', {
url: '/edges',
params: {'topIndex': 0},
module: 'private',
auth: ['TENANT_ADMIN', 'CUSTOMER_USER'],
views: {
"content@home": {
templateUrl: edgesTemplate,
controller: 'EdgeController',
controllerAs: 'vm'
}
},
data: {
edgesType: 'tenant',
searchEnabled: true,
searchByEntitySubtype: true,
searchEntityType: types.entityType.edge,
pageTitle: 'edge.edges'
},
ncyBreadcrumb: {
label: '{"icon": "transform", "label": "edge.edges"}'
}
})
.state('home.customers.edges', {
url: '/:customerId/edges',
params: {'topIndex': 0},
module: 'private',
auth: ['TENANT_ADMIN'],
views: {
"content@home": {
@ -36,11 +58,14 @@ export default function EdgeRoutes($stateProvider) {
}
},
data: {
edgesType: 'customer',
searchEnabled: true,
pageTitle: 'edge.edges'
searchByEntitySubtype: true,
searchEntityType: types.entityType.edge,
pageTitle: 'customer.edges'
},
ncyBreadcrumb: {
label: '{"icon": "transform", "label": "edge.edges"}'
label: '{"icon": "transform", "label": "{{ vm.customerEdgesTitle }}", "translate": "false"}'
}
});
}

20
ui/src/app/edge/edges.tpl.html

@ -23,22 +23,24 @@
id="tabs" md-border-bottom flex class="tb-absolute-fill">
<md-tab label="{{ 'edge.details' | translate }}">
<tb-edge edge="vm.grid.operatingItem()"
is-edit="vm.grid.detailsConfig.isDetailsEditMode"
the-form="vm.grid.detailsForm"
on-delete-edge="vm.grid.deleteItem(event, vm.grid.detailsConfig.currentItem)"></tb-edge>
is-edit="vm.grid.detailsConfig.isDetailsEditMode"
edge-scope="vm.edgesScope"
the-form="vm.grid.detailsForm"
on-assign-to-customer="vm.assignToCustomer(event, [ vm.grid.detailsConfig.currentItem.id.id ])"
on-make-public="vm.makePublic(event, vm.grid.detailsConfig.currentItem)"
on-unassign-from-customer="vm.unassignFromCustomer(event, vm.grid.detailsConfig.currentItem, isPublic)"
on-delete-edge="vm.grid.deleteItem(event, vm.grid.detailsConfig.currentItem)"></tb-edge>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && ('edge' | hasGenericPermission:'readAttributes')" md-on-select="vm.grid.triggerResize()" label="{{ 'attribute.attributes' | translate }}">
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" md-on-select="vm.grid.triggerResize()" label="{{ 'attribute.attributes' | translate }}">
<tb-attribute-table flex
readonly="!('edge' | hasGenericPermission:'writeAttributes')"
entity-id="vm.grid.operatingItem().id.id"
entity-type="{{vm.types.entityType.edge}}"
entity-name="vm.grid.operatingItem().name"
default-attribute-scope="{{vm.types.attributesScope.server.value}}">
</tb-attribute-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && ('edge' | hasGenericPermission:'readTelemetry')" md-on-select="vm.grid.triggerResize()" label="{{ 'attribute.latest-telemetry' | translate }}">
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" md-on-select="vm.grid.triggerResize()" label="{{ 'attribute.latest-telemetry' | translate }}">
<tb-attribute-table flex
readonly="!('edge' | hasGenericPermission:'writeTelemetry')"
entity-id="vm.grid.operatingItem().id.id"
entity-type="{{vm.types.entityType.edge}}"
entity-name="vm.grid.operatingItem().name"
@ -46,7 +48,7 @@
disable-attribute-scope-selection="true">
</tb-attribute-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && ('alarm' | hasGenericPermission:'read')" md-on-select="vm.grid.triggerResize()" label="{{ 'alarm.alarms' | translate }}">
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" md-on-select="vm.grid.triggerResize()" label="{{ 'alarm.alarms' | translate }}">
<tb-alarm-table flex entity-type="vm.types.entityType.edge"
entity-id="vm.grid.operatingItem().id.id">
</tb-alarm-table>
@ -65,7 +67,7 @@
entity-type="{{vm.types.entityType.edge}}">
</tb-relation-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && ('auditLog' | hasGenericPermission:'read')" md-on-select="vm.grid.triggerResize()" label="{{ 'audit-log.audit-logs' | translate }}">
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.grid.isTenantAdmin()" md-on-select="vm.grid.triggerResize()" label="{{ 'audit-log.audit-logs' | translate }}">
<tb-audit-log-table flex entity-type="vm.types.entityType.edge"
entity-id="vm.grid.operatingItem().id.id"
audit-log-mode="{{vm.types.auditLogMode.entity}}">

18
ui/src/app/edge/index.js

@ -13,13 +13,29 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import uiRouter from 'angular-ui-router';
import thingsboardGrid from '../components/grid.directive';
import thingsboardApiUser from '../api/user.service';
import thingsboardApiEdge from '../api/edge.service';
import thingsboardApiCustomer from '../api/customer.service';
import EdgeRoutes from './edge.routes';
import {EdgeController, EdgeCardController} from './edge.controller';
import AssignEdgeToCustomerController from './assign-to-customer.controller';
import AddEdgesToCustomerController from './add-edges-to-customer.controller';
import EdgeDirective from './edge.directive';
export default angular.module('thingsboard.edge', [])
export default angular.module('thingsboard.edge', [
uiRouter,
thingsboardGrid,
thingsboardApiUser,
thingsboardApiEdge,
thingsboardApiCustomer
])
.config(EdgeRoutes)
.controller('EdgeController', EdgeController)
.controller('EdgeCardController', EdgeCardController)
.controller('AssignEdgeToCustomerController', AssignEdgeToCustomerController)
.controller('AddEdgesToCustomerController', AddEdgesToCustomerController)
.directive('tbEdge', EdgeDirective)
.name;

11
ui/src/app/entity/entity-subtype-autocomplete.directive.js

@ -22,7 +22,7 @@ import entitySubtypeAutocompleteTemplate from './entity-subtype-autocomplete.tpl
/* eslint-enable import/no-unresolved, import/default */
/*@ngInject*/
export default function EntitySubtypeAutocomplete($compile, $templateCache, $q, $filter, assetService, deviceService, entityViewService, types) {
export default function EntitySubtypeAutocomplete($compile, $templateCache, $q, $filter, assetService, deviceService, entityViewService, edgeService, types) {
var linker = function (scope, element, attrs, ngModelCtrl) {
var template = $templateCache.get(entitySubtypeAutocompleteTemplate);
@ -103,6 +103,8 @@ export default function EntitySubtypeAutocomplete($compile, $templateCache, $q,
entitySubtypesPromise = deviceService.getDeviceTypes({ignoreLoading: true});
} else if (scope.entityType == types.entityType.entityView) {
entitySubtypesPromise = entityViewService.getEntityViewTypes({ignoreLoading: true});
} else if (scope.entityType == types.entityType.edge) {
entitySubtypesPromise = edgeService.getEdgeTypes({ignoreLoading: true});
}
if (entitySubtypesPromise) {
entitySubtypesPromise.then(
@ -148,6 +150,13 @@ export default function EntitySubtypeAutocomplete($compile, $templateCache, $q,
scope.$on('entityViewSaved', function() {
scope.entitySubtypes = null;
});
} else if (scope.entityType == types.entityType.edge) {
scope.selectEntitySubtypeText = 'edge.select-edge-type';
scope.entitySubtypeText = 'edge.edge-type';
scope.entitySubtypeRequiredText = 'edge.edge-type-required';
scope.$on('edgeSaved', function() {
scope.entitySubtypes = null;
});
}
}

10
ui/src/app/entity/entity-subtype-list.directive.js

@ -22,7 +22,7 @@ import entitySubtypeListTemplate from './entity-subtype-list.tpl.html';
import './entity-subtype-list.scss';
/*@ngInject*/
export default function EntitySubtypeListDirective($compile, $templateCache, $q, $mdUtil, $translate, $filter, types, assetService, deviceService, entityViewService) {
export default function EntitySubtypeListDirective($compile, $templateCache, $q, $mdUtil, $translate, $filter, types, assetService, deviceService, entityViewService, edgeService) {
var linker = function (scope, element, attrs, ngModelCtrl) {
@ -53,6 +53,12 @@ export default function EntitySubtypeListDirective($compile, $templateCache, $q,
scope.secondaryPlaceholder = '+' + $translate.instant('entity-view.entity-view-type');
scope.noSubtypesMathingText = 'entity-view.no-entity-view-types-matching';
scope.subtypeListEmptyText = 'entity-view.entity-view-type-list-empty';
} else if (scope.entityType == types.entityType.edge) {
scope.placeholder = scope.tbRequired ? $translate.instant('edge.enter-edge-type')
: $translate.instant('edge.any-edge');
scope.secondaryPlaceholder = '+' + $translate.instant('edge.edge-type');
scope.noSubtypesMathingText = 'edge.no-edge-types-matching';
scope.subtypeListEmptyText = 'edge.edge-type-list-empty';
}
scope.$watch('tbRequired', function () {
@ -105,6 +111,8 @@ export default function EntitySubtypeListDirective($compile, $templateCache, $q,
entitySubtypesPromise = deviceService.getDeviceTypes({ignoreLoading: true});
} else if (scope.entityType == types.entityType.entityView) {
entitySubtypesPromise = entityViewService.getEntityViewTypes({ignoreLoading: true});
} else if (scope.entityType == types.entityType.edge) {
entitySubtypesPromise = edgeService.getEdgeTypes({ignoreLoading: true});
}
if (entitySubtypesPromise) {
entitySubtypesPromise.then(

11
ui/src/app/entity/entity-subtype-select.directive.js

@ -22,7 +22,7 @@ import entitySubtypeSelectTemplate from './entity-subtype-select.tpl.html';
/* eslint-enable import/no-unresolved, import/default */
/*@ngInject*/
export default function EntitySubtypeSelect($compile, $templateCache, $translate, assetService, deviceService, entityViewService, types) {
export default function EntitySubtypeSelect($compile, $templateCache, $translate, assetService, deviceService, entityViewService, edgeService, types) {
var linker = function (scope, element, attrs, ngModelCtrl) {
var template = $templateCache.get(entitySubtypeSelectTemplate);
@ -77,6 +77,8 @@ export default function EntitySubtypeSelect($compile, $templateCache, $translate
entitySubtypesPromise = deviceService.getDeviceTypes({ignoreLoading: true});
} else if (scope.entityType == types.entityType.entityView) {
entitySubtypesPromise = entityViewService.getEntityViewTypes({ignoreLoading: true});
} else if (scope.entityType == types.entityType.edge) {
entitySubtypesPromise = edgeService.getEdgeTypes({ignoreLoading: true});
}
if (entitySubtypesPromise) {
entitySubtypesPromise.then(
@ -105,6 +107,9 @@ export default function EntitySubtypeSelect($compile, $templateCache, $translate
} else if (scope.entityType == types.entityType.entityView) {
scope.entitySubtypeTitle = 'entity-view.entity-view-type';
scope.entitySubtypeRequiredText = 'entity-view.entity-view-type-required';
} else if (scope.entityType == types.entityType.edge) {
scope.entitySubtypeTitle = 'edge.edge-type';
scope.entitySubtypeRequiredText = 'edge.edge-type-required';
}
scope.entitySubtypes.length = 0;
if (scope.entitySubtypesList && scope.entitySubtypesList.length) {
@ -125,6 +130,10 @@ export default function EntitySubtypeSelect($compile, $templateCache, $translate
scope.$on('entityViewSaved', function() {
loadSubTypes();
});
} else if (scope.entityType == types.entityType.edge) {
scope.$on('edgeSaved', function() {
loadSubTypes();
});
}
}
}

5
ui/src/app/locale/locale.constant-en_US.json

@ -319,6 +319,8 @@
"type-credentials-updated": "Credentials updated",
"type-assigned-to-customer": "Assigned to Customer",
"type-unassigned-from-customer": "Unassigned from Customer",
"type-assigned-to-edge": "Assigned to Edge",
"type-unassigned-from-edge": "Unassigned from Edge",
"type-activated": "Activated",
"type-suspended": "Suspended",
"type-credentials-read": "Credentials read",
@ -731,6 +733,9 @@
"idCopiedMessage": "Edge Id has been copied to clipboard",
"permissions": "Permissions",
"edge-required": "Edge required",
"edge-type": "Edge type",
"edge-type-required": "Edge type is required.",
"select-edge-type": "Select edge type"
},
"error": {
"unable-to-connect": "Unable to connect to the server! Please check your internet connection.",

Loading…
Cancel
Save