From 00ce0707d71e0741f3d666e5a36e8fc504bd318f Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Tue, 8 Oct 2019 18:47:34 +0300 Subject: [PATCH] Added Edge UI support and assignedEdges to Dashboard --- .../server/controller/BaseController.java | 3 + .../controller/DashboardController.java | 218 +++++++++++++++++- .../security/permission/Operation.java | 3 +- .../controller/BaseEdgeControllerTest.java | 2 +- .../dao/dashboard/DashboardService.java | 8 + .../server/dao/edge/EdgeService.java | 2 +- .../server/common/data/DashboardInfo.java | 56 +++++ .../server/common/data/ShortEdgeInfo.java | 15 ++ .../server/common/data/audit/ActionType.java | 4 +- .../server/common/data/edge/Edge.java | 11 +- .../data/relation/RelationTypeGroup.java | 3 +- .../server/dao/audit/AuditLogServiceImpl.java | 16 ++ .../dashboard/CassandraDashboardInfoDao.java | 16 ++ .../dao/dashboard/DashboardInfoDao.java | 11 + .../dao/dashboard/DashboardServiceImpl.java | 129 +++++++++++ .../server/dao/edge/BaseEdgeService.java | 10 +- .../server/dao/model/ModelConstants.java | 3 + .../server/dao/model/nosql/EdgeEntity.java | 6 + .../server/dao/model/sql/DashboardEntity.java | 20 ++ .../server/dao/model/sql/EdgeEntity.java | 6 + .../sql/dashboard/JpaDashboardInfoDao.java | 16 ++ .../server/dao/sql/edge/EdgeRepository.java | 2 +- .../server/dao/sql/edge/JpaEdgeDao.java | 2 +- .../main/resources/sql/schema-entities.sql | 1 + .../dao/service/BaseDashboardServiceTest.java | 36 +++ .../dao/service/BaseEdgeServiceTest.java | 2 +- .../service/nosql/EdgeServiceNoSqlTest.java | 2 +- .../dao/service/sql/EdgeServiceSqlTest.java | 2 +- ui/src/app/api/edge.service.js | 88 ++++++- ui/src/app/api/entity.service.js | 35 ++- ui/src/app/common/types.constant.js | 14 ++ ui/src/app/edge/edge-card.tpl.html | 4 +- ui/src/app/edge/edge-fieldset.tpl.html | 33 ++- ui/src/app/edge/edge.directive.js | 29 ++- ui/src/app/edge/edge.routes.js | 33 ++- ui/src/app/edge/edges.tpl.html | 20 +- ui/src/app/edge/index.js | 18 +- .../entity-subtype-autocomplete.directive.js | 11 +- .../entity/entity-subtype-list.directive.js | 10 +- .../entity/entity-subtype-select.directive.js | 11 +- ui/src/app/locale/locale.constant-en_US.json | 5 + 41 files changed, 873 insertions(+), 43 deletions(-) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/ShortEdgeInfo.java diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index 23044b9733..8c1b526ed6 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/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()); } diff --git a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java index e47e2d1a06..252eeb90cd 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java +++ b/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 edgeIds = new HashSet<>(); + if (strEdgeIds != null) { + for (String strEdgeId : strEdgeIds) { + edgeIds.add(new EdgeId(toUUID(strEdgeId))); + } + } + + Set addedEdgeIds = new HashSet<>(); + Set removedEdgeIds = new HashSet<>(); + for (EdgeId edgeId : edgeIds) { + if (!dashboard.isAssignedToEdge(edgeId)) { + addedEdgeIds.add(edgeId); + } + } + + Set 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 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 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); + } + } } diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/Operation.java b/application/src/main/java/org/thingsboard/server/service/security/permission/Operation.java index 1d81cdaf43..c11e50da09 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/Operation.java +++ b/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 } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseEdgeControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseEdgeControllerTest.java index 9eca138e7e..9f982947f3 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseEdgeControllerTest.java +++ b/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, diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java index 2d9461cc66..696fb0564d 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java +++ b/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); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java index d01411fb16..60ea2919ee 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java @@ -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, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java index 4b8e92d963..c3447e8361 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java +++ b/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 implements HasNa private String title; private Set assignedCustomers; + @Getter @Setter + private Set assignedEdges; + public DashboardInfo() { super(); } @@ -116,6 +123,55 @@ public class DashboardInfo extends SearchTextBased 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() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ShortEdgeInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/ShortEdgeInfo.java new file mode 100644 index 0000000000..48f2a5faed --- /dev/null +++ b/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; +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/audit/ActionType.java b/common/data/src/main/java/org/thingsboard/server/common/data/audit/ActionType.java index 77357d2294..a42f8da101 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/audit/ActionType.java +++ b/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; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/edge/Edge.java b/common/data/src/main/java/org/thingsboard/server/common/data/edge/Edge.java index 5efd284d11..8d95ace9dc 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/edge/Edge.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/edge/Edge.java @@ -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 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 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 diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationTypeGroup.java b/common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationTypeGroup.java index 808bb7a772..b38faa1f15 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationTypeGroup.java +++ b/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 } diff --git a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java index 436415d9d9..f3618c5b3a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java +++ b/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; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/CassandraDashboardInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/CassandraDashboardInfoDao.java index 6038bd632a..ab0919ae9c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/CassandraDashboardInfoDao.java +++ b/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> findDashboardsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) { + log.debug("Try to find dashboards by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink); + + ListenableFuture> relations = relationDao.findRelations(new TenantId(tenantId), new EdgeId(edgeId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE, EntityType.DASHBOARD, pageLink); + + return Futures.transformAsync(relations, input -> { + List> dashboardFutures = new ArrayList<>(input.size()); + for (EntityRelation relation : input) { + dashboardFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId())); + } + return Futures.successfulAsList(dashboardFutures); + }); + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardInfoDao.java index a90ba4b464..bd244ff762 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardInfoDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardInfoDao.java @@ -48,4 +48,15 @@ public interface DashboardInfoDao extends Dao { */ ListenableFuture> 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> findDashboardsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java index 83d4c55068..a9aded5968 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java +++ b/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 dashboardValidator = new DataValidator() { @Override @@ -310,4 +390,53 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb } + private class EdgeDashboardsUnassigner extends TimePaginatedRemover { + + private Edge edge; + + EdgeDashboardsUnassigner(Edge edge) { + this.edge = edge; + } + + @Override + protected List 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 { + + private Edge edge; + + EdgeDashboardsUpdater(Edge edge) { + this.edge = edge; + } + + @Override + protected List 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); + } + + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeService.java b/dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeService.java index ecebfcb8fc..52c63b92bc 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeService.java @@ -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()); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index 2fdf80267b..a91cf0b1eb 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -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; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/EdgeEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/EdgeEntity.java index ee658e536a..413961b406 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/EdgeEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/EdgeEntity.java @@ -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 { @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 { } 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.setType(type); edge.setName(name); + edge.setLabel(label); edge.setConfiguration(configuration); edge.setAdditionalInfo(additionalInfo); return edge; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardEntity.java index 8ead395689..3da4b07823 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardEntity.java +++ b/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 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 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 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 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; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/EdgeEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/EdgeEntity.java index 637a069e70..6c0ee3eb6e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/EdgeEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/EdgeEntity.java @@ -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 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 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 implements SearchTextEntity< } edge.setType(type); edge.setName(name); + edge.setLabel(label); edge.setConfiguration(configuration); edge.setAdditionalInfo(additionalInfo); return edge; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDao.java index 8e3e0e0c1e..761231df36 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDao.java +++ b/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> findDashboardsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) { + log.debug("Try to find dashboards by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink); + + ListenableFuture> relations = relationDao.findRelations(new TenantId(tenantId), new EdgeId(edgeId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE, EntityType.DASHBOARD, pageLink); + + return Futures.transformAsync(relations, input -> { + List> dashboardFutures = new ArrayList<>(input.size()); + for (EntityRelation relation : input) { + dashboardFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId())); + } + return Futures.successfulAsList(dashboardFutures); + }); + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeRepository.java index d688fa5f13..553e4b6879 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeRepository.java @@ -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, diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java index 476a4a8e8f..db0b305819 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java @@ -5,7 +5,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 36f4c5c168..2c20110478 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/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) ); \ No newline at end of file diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDashboardServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDashboardServiceTest.java index d8fecd04f0..b855e7d1db 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDashboardServiceTest.java +++ b/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()); + } + } + } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseEdgeServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseEdgeServiceTest.java index 5ce90869d6..baf7d4a964 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseEdgeServiceTest.java +++ b/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, diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/nosql/EdgeServiceNoSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/nosql/EdgeServiceNoSqlTest.java index 5f949ecbdf..08058884d8 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/nosql/EdgeServiceNoSqlTest.java +++ b/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, diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/sql/EdgeServiceSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/sql/EdgeServiceSqlTest.java index abe0ed4ace..1dbc8dd1ae 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/sql/EdgeServiceSqlTest.java +++ b/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, diff --git a/ui/src/app/api/edge.service.js b/ui/src/app/api/edge.service.js index 11bad6bc8e..0edb2afe63 100644 --- a/ui/src/app/api/edge.service.js +++ b/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; + } } diff --git a/ui/src/app/api/entity.service.js b/ui/src/app/api/entity.service.js index f39615c933..2adac9e6a5 100644 --- a/ui/src/app/api/entity.service.js +++ b/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 } diff --git a/ui/src/app/common/types.constant.js b/ui/src/app/common/types.constant.js index 354cf9fccb..35e7ccd7b2 100644 --- a/ui/src/app/common/types.constant.js +++ b/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: { diff --git a/ui/src/app/edge/edge-card.tpl.html b/ui/src/app/edge/edge-card.tpl.html index 4ce4e28176..7a91b07ce2 100644 --- a/ui/src/app/edge/edge-card.tpl.html +++ b/ui/src/app/edge/edge-card.tpl.html @@ -16,6 +16,8 @@ -->
-
{{ vm.types.edgeType[vm.item.type].name | translate }}
+
{{vm.item.type}}
{{vm.item.additionalInfo.description}}
+
{{'edge.assignedToCustomer' | translate}} '{{vm.item.assignedCustomer.title}}'
+
{{'edge.public' | translate}}
diff --git a/ui/src/app/edge/edge-fieldset.tpl.html b/ui/src/app/edge/edge-fieldset.tpl.html index c4990c8b8b..34ed7bfe07 100644 --- a/ui/src/app/edge/edge-fieldset.tpl.html +++ b/ui/src/app/edge/edge-fieldset.tpl.html @@ -15,12 +15,15 @@ limitations under the License. --> -{{ 'edge.export' | translate }} +{{ 'edge.assign-to-customer' | translate }} +{{ isPublic ? 'edge.make-private' : 'edge.unassign-from-customer' | translate }} {{ 'edge.delete' | translate }} + ng-show="!isEdit && edgeScope === 'tenant'" + class="md-raised md-primary">{{ 'edge.delete' | translate }}
+ + + + +
+ {{ 'edge.edge-public' | translate }} +
@@ -41,6 +53,17 @@
edge.name-required
+ + + + + + diff --git a/ui/src/app/edge/edge.directive.js b/ui/src/app/edge/edge.directive.js index 2261421aa4..990efbe0a6 100644 --- a/ui/src/app/edge/edge.directive.js +++ b/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: '&' } }; diff --git a/ui/src/app/edge/edge.routes.js b/ui/src/app/edge/edge.routes.js index 1c3199a085..9c78e9d7e0 100644 --- a/ui/src/app/edge/edge.routes.js +++ b/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"}' } }); } diff --git a/ui/src/app/edge/edges.tpl.html b/ui/src/app/edge/edges.tpl.html index 9d296c4a5b..f7c38d0d3f 100644 --- a/ui/src/app/edge/edges.tpl.html +++ b/ui/src/app/edge/edges.tpl.html @@ -23,22 +23,24 @@ id="tabs" md-border-bottom flex class="tb-absolute-fill"> + 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)"> - + - + - + @@ -65,7 +67,7 @@ entity-type="{{vm.types.entityType.edge}}"> - + diff --git a/ui/src/app/edge/index.js b/ui/src/app/edge/index.js index 604fba2053..55d9231fdc 100644 --- a/ui/src/app/edge/index.js +++ b/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; diff --git a/ui/src/app/entity/entity-subtype-autocomplete.directive.js b/ui/src/app/entity/entity-subtype-autocomplete.directive.js index 213b4020c9..e839a56a8f 100644 --- a/ui/src/app/entity/entity-subtype-autocomplete.directive.js +++ b/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; + }); } } diff --git a/ui/src/app/entity/entity-subtype-list.directive.js b/ui/src/app/entity/entity-subtype-list.directive.js index 196fa9f693..977c212c99 100644 --- a/ui/src/app/entity/entity-subtype-list.directive.js +++ b/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( diff --git a/ui/src/app/entity/entity-subtype-select.directive.js b/ui/src/app/entity/entity-subtype-select.directive.js index 2ca20816a7..85813b6f77 100644 --- a/ui/src/app/entity/entity-subtype-select.directive.js +++ b/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(); + }); } } } diff --git a/ui/src/app/locale/locale.constant-en_US.json b/ui/src/app/locale/locale.constant-en_US.json index 95b4e22fb4..6d61acd20d 100644 --- a/ui/src/app/locale/locale.constant-en_US.json +++ b/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.",