Browse Source

Added manage edge rule chains functionality

pull/2818/head
Volodymyr Babak 7 years ago
parent
commit
0cca90ff21
  1. 2
      application/src/main/java/org/thingsboard/server/controller/DashboardController.java
  2. 241
      application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
  3. 12
      common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java
  4. 61
      common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java
  5. 29
      dao/src/main/java/org/thingsboard/server/dao/model/nosql/DashboardInfoEntity.java
  6. 32
      dao/src/main/java/org/thingsboard/server/dao/model/nosql/RuleChainEntity.java
  7. 30
      dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleChainEntity.java
  8. 154
      dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java
  9. 26
      dao/src/main/java/org/thingsboard/server/dao/rule/CassandraRuleChainDao.java
  10. 12
      dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java
  11. 26
      dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java
  12. 2
      ui/src/app/api/dashboard.service.js
  13. 115
      ui/src/app/api/rule-chain.service.js
  14. 11
      ui/src/app/dashboard/dashboards.controller.js
  15. 4
      ui/src/app/dashboard/index.js
  16. 2
      ui/src/app/dashboard/manage-assigned-edges.controller.js
  17. 25
      ui/src/app/edge/edge.controller.js
  18. 27
      ui/src/app/locale/locale.constant-en_US.json
  19. 130
      ui/src/app/rulechain/add-rulechains-to-edge.controller.js
  20. 77
      ui/src/app/rulechain/add-rulechains-to-edge.tpl.html
  21. 4
      ui/src/app/rulechain/index.js
  22. 69
      ui/src/app/rulechain/manage-assigned-edges.controller.js
  23. 51
      ui/src/app/rulechain/manage-assigned-edges.tpl.html
  24. 26
      ui/src/app/rulechain/rulechain.routes.js
  25. 327
      ui/src/app/rulechain/rulechains.controller.js

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

@ -690,7 +690,7 @@ public class DashboardController extends BaseController {
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/{edgeId}/dashboards", params = { "limit" }, method = RequestMethod.GET)
@ResponseBody
public TimePageData<DashboardInfo> getEdgeDashboards(

241
application/src/main/java/org/thingsboard/server/controller/RuleChainController.java

@ -40,13 +40,18 @@ import org.thingsboard.server.actors.tenant.DebugTbRateLimits;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.Event;
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.EdgeId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
@ -59,6 +64,7 @@ import org.thingsboard.server.service.script.RuleNodeJsScriptEngine;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -371,4 +377,239 @@ public class RuleChainController extends BaseController {
return objectMapper.writeValueAsString(msgData);
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/{edgeId}/ruleChain/{ruleChainId}", method = RequestMethod.POST)
@ResponseBody
public RuleChain assignRuleChainToEdge(@PathVariable("edgeId") String strEdgeId,
@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
checkParameter("edgeId", strEdgeId);
checkParameter(RULE_CHAIN_ID, strRuleChainId);
try {
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
Edge edge = checkEdgeId(edgeId, Operation.READ);
RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
checkRuleChain(ruleChainId, Operation.ASSIGN_TO_EDGE);
RuleChain savedRuleChain = checkNotNull(ruleChainService.assignRuleChainToEdge(getCurrentUser().getTenantId(), ruleChainId, edgeId));
logEntityAction(ruleChainId, savedRuleChain,
null,
ActionType.ASSIGNED_TO_EDGE, null, strRuleChainId, strEdgeId, edge.getName());
return savedRuleChain;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.RULE_CHAIN), null,
null,
ActionType.ASSIGNED_TO_EDGE, e, strRuleChainId, strEdgeId);
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/{edgeId}/ruleChain/{ruleChainId}", method = RequestMethod.DELETE)
@ResponseBody
public RuleChain unassignRuleChainFromEdge(@PathVariable("edgeId") String strEdgeId,
@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
checkParameter("edgeId", strEdgeId);
checkParameter(RULE_CHAIN_ID, strRuleChainId);
try {
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
Edge edge = checkEdgeId(edgeId, Operation.READ);
RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.UNASSIGN_FROM_EDGE);
RuleChain savedRuleChain = checkNotNull(ruleChainService.unassignRuleChainFromEdge(getCurrentUser().getTenantId(), ruleChainId, edgeId));
logEntityAction(ruleChainId, ruleChain,
null,
ActionType.UNASSIGNED_FROM_EDGE, null, strRuleChainId, edge.getId().toString(), edge.getName());
return savedRuleChain;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.RULE_CHAIN), null,
null,
ActionType.UNASSIGNED_FROM_EDGE, e, strRuleChainId);
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/ruleChain/{ruleChainId}/edges", method = RequestMethod.POST)
@ResponseBody
public RuleChain updateRuleChainEdges(@PathVariable(RULE_CHAIN_ID) String strRuleChainId,
@RequestBody String[] strEdgeIds) throws ThingsboardException {
checkParameter(RULE_CHAIN_ID, strRuleChainId);
try {
RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
RuleChain ruleChain = checkRuleChain(ruleChainId, 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 (!ruleChain.isAssignedToEdge(edgeId)) {
addedEdgeIds.add(edgeId);
}
}
Set<ShortEdgeInfo> assignedEdges = ruleChain.getAssignedEdges();
if (assignedEdges != null) {
for (ShortEdgeInfo edgeInfo : assignedEdges) {
if (!edgeIds.contains(edgeInfo.getEdgeId())) {
removedEdgeIds.add(edgeInfo.getEdgeId());
}
}
}
if (addedEdgeIds.isEmpty() && removedEdgeIds.isEmpty()) {
return ruleChain;
} else {
RuleChain savedRuleChain = null;
for (EdgeId edgeId : addedEdgeIds) {
savedRuleChain = checkNotNull(ruleChainService.assignRuleChainToEdge(getCurrentUser().getTenantId(), ruleChainId, edgeId));
ShortEdgeInfo edgeInfo = savedRuleChain.getAssignedEdgeInfo(edgeId);
logEntityAction(ruleChainId, savedRuleChain,
null,
ActionType.ASSIGNED_TO_EDGE, null, strRuleChainId, edgeId.toString(), edgeInfo.getTitle());
}
for (EdgeId edgeId : removedEdgeIds) {
ShortEdgeInfo edgeInfo = ruleChain.getAssignedEdgeInfo(edgeId);
savedRuleChain = checkNotNull(ruleChainService.unassignRuleChainFromEdge(getCurrentUser().getTenantId(), ruleChainId, edgeId));
logEntityAction(ruleChainId, ruleChain,
null,
ActionType.UNASSIGNED_FROM_EDGE, null, strRuleChainId, edgeId.toString(), edgeInfo.getTitle());
}
return savedRuleChain;
}
} catch (Exception e) {
logEntityAction(emptyId(EntityType.RULE_CHAIN), null,
null,
ActionType.ASSIGNED_TO_EDGE, e, strRuleChainId);
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/ruleChain/{ruleChainId}/edges/add", method = RequestMethod.POST)
@ResponseBody
public RuleChain addRuleChainEdges(@PathVariable(RULE_CHAIN_ID) String strRuleChainId,
@RequestBody String[] strEdgeIds) throws ThingsboardException {
checkParameter(RULE_CHAIN_ID, strRuleChainId);
try {
RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.ASSIGN_TO_EDGE);
Set<EdgeId> edgeIds = new HashSet<>();
if (strEdgeIds != null) {
for (String strEdgeId : strEdgeIds) {
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
if (!ruleChain.isAssignedToEdge(edgeId)) {
edgeIds.add(edgeId);
}
}
}
if (edgeIds.isEmpty()) {
return ruleChain;
} else {
RuleChain savedRuleChain = null;
for (EdgeId edgeId : edgeIds) {
savedRuleChain = checkNotNull(ruleChainService.assignRuleChainToEdge(getCurrentUser().getTenantId(), ruleChainId, edgeId));
ShortEdgeInfo edgeInfo = savedRuleChain.getAssignedEdgeInfo(edgeId);
logEntityAction(ruleChainId, savedRuleChain,
null,
ActionType.ASSIGNED_TO_EDGE, null, strRuleChainId, edgeId.toString(), edgeInfo.getTitle());
}
return savedRuleChain;
}
} catch (Exception e) {
logEntityAction(emptyId(EntityType.RULE_CHAIN), null,
null,
ActionType.ASSIGNED_TO_EDGE, e, strRuleChainId);
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/ruleChain/{ruleChainId}/edges/remove", method = RequestMethod.POST)
@ResponseBody
public RuleChain removeRuleChainEdges(@PathVariable(RULE_CHAIN_ID) String strRuleChainId,
@RequestBody String[] strEdgeIds) throws ThingsboardException {
checkParameter(RULE_CHAIN_ID, strRuleChainId);
try {
RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.UNASSIGN_FROM_EDGE);
Set<EdgeId> edgeIds = new HashSet<>();
if (strEdgeIds != null) {
for (String strEdgeId : strEdgeIds) {
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
if (ruleChain.isAssignedToEdge(edgeId)) {
edgeIds.add(edgeId);
}
}
}
if (edgeIds.isEmpty()) {
return ruleChain;
} else {
RuleChain savedRuleChain = null;
for (EdgeId edgeId : edgeIds) {
ShortEdgeInfo edgeInfo = ruleChain.getAssignedEdgeInfo(edgeId);
savedRuleChain = checkNotNull(ruleChainService.unassignRuleChainFromEdge(getCurrentUser().getTenantId(), ruleChainId, edgeId));
logEntityAction(ruleChainId, ruleChain,
null,
ActionType.UNASSIGNED_FROM_EDGE, null, strRuleChainId, edgeId.toString(), edgeInfo.getTitle());
}
return savedRuleChain;
}
} catch (Exception e) {
logEntityAction(emptyId(EntityType.RULE_CHAIN), null,
null,
ActionType.UNASSIGNED_FROM_EDGE, e, strRuleChainId);
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/{edgeId}/ruleChains", params = { "limit" }, method = RequestMethod.GET)
@ResponseBody
public TimePageData<RuleChain> getEdgeRuleChains(
@PathVariable("edgeId") String strEdgeId,
@RequestParam int limit,
@RequestParam(required = false) Long startTime,
@RequestParam(required = false) Long endTime,
@RequestParam(required = false, defaultValue = "false") boolean ascOrder,
@RequestParam(required = false) String offset) throws ThingsboardException {
checkParameter("edgeId", strEdgeId);
try {
TenantId tenantId = getCurrentUser().getTenantId();
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
checkEdgeId(edgeId, Operation.READ);
TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset);
return checkNotNull(ruleChainService.findRuleChainsByTenantIdAndEdgeId(tenantId, edgeId, pageLink).get());
} catch (Exception e) {
throw handleException(e);
}
}
}

12
common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java

@ -16,11 +16,14 @@
package org.thingsboard.server.dao.rule;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
@ -63,4 +66,13 @@ public interface RuleChainService {
void deleteRuleChainsByTenantId(TenantId tenantId);
RuleChain assignRuleChainToEdge(TenantId tenantId, RuleChainId ruleChainId, EdgeId edgeId);
RuleChain unassignRuleChainFromEdge(TenantId tenantId, RuleChainId ruleChainId, EdgeId edgeId);
void unassignEdgeRuleChains(TenantId tenantId, EdgeId edgeId);
void updateEdgeRuleChains(TenantId tenantId, EdgeId edgeId);
ListenableFuture<TimePageData<RuleChain>> findRuleChainsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink);
}

61
common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java

@ -23,10 +23,16 @@ import lombok.extern.slf4j.Slf4j;
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.ShortEdgeInfo;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.id.TenantId;
import java.util.HashSet;
import java.util.Set;
@Data
@EqualsAndHashCode(callSuper = true)
@Slf4j
@ -40,6 +46,8 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo<RuleChainId> im
private boolean root;
private boolean debugMode;
private transient JsonNode configuration;
private Set<ShortEdgeInfo> assignedEdges;
@JsonIgnore
private byte[] configurationBytes;
@ -57,6 +65,11 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo<RuleChainId> im
this.name = ruleChain.getName();
this.firstRuleNodeId = ruleChain.getFirstRuleNodeId();
this.root = ruleChain.isRoot();
// TODO: VOBA - check that this is needed
// this.debugMode = ruleChain.isDebugMode();
this.assignedEdges = ruleChain.getAssignedEdges();
this.setConfiguration(ruleChain.getConfiguration());
}
@ -78,4 +91,52 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo<RuleChainId> im
setJson(data, json -> this.configuration = json, bytes -> this.configurationBytes = bytes);
}
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;
}
}
}

29
dao/src/main/java/org/thingsboard/server/dao/model/nosql/DashboardInfoEntity.java

@ -28,6 +28,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.DashboardInfo;
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.SearchTextEntity;
@ -37,6 +38,7 @@ import java.util.HashSet;
import java.util.UUID;
import static org.thingsboard.server.dao.model.ModelConstants.DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.DASHBOARD_ASSIGNED_EDGES_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.DASHBOARD_COLUMN_FAMILY_NAME;
import static org.thingsboard.server.dao.model.ModelConstants.DASHBOARD_TENANT_ID_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.DASHBOARD_TITLE_PROPERTY;
@ -52,6 +54,8 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> {
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);
@PartitionKey(value = 0)
@Column(name = ID_PROPERTY)
@ -70,6 +74,9 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> {
@Column(name = DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY)
private String assignedCustomers;
@Column(name = DASHBOARD_ASSIGNED_EDGES_PROPERTY)
private String assignedEdges;
public DashboardInfoEntity() {
super();
}
@ -89,6 +96,13 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> {
log.error("Unable to serialize assigned customers to string!", e);
}
}
if (dashboardInfo.getAssignedEdges() != null) {
try {
this.assignedEdges = objectMapper.writeValueAsString(dashboardInfo.getAssignedEdges());
} catch (JsonProcessingException e) {
log.error("Unable to serialize assigned edges to string!", e);
}
}
}
public UUID getId() {
@ -123,6 +137,14 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> {
this.assignedCustomers = assignedCustomers;
}
public String getAssignedEdges() {
return assignedEdges;
}
public void setAssignedEdges(String assignedEdges) {
this.assignedEdges = assignedEdges;
}
@Override
public String getSearchTextSource() {
return getTitle();
@ -152,6 +174,13 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> {
log.warn("Unable to parse assigned customers!", e);
}
}
if (!StringUtils.isEmpty(assignedEdges)) {
try {
dashboardInfo.setAssignedEdges(objectMapper.readValue(assignedEdges, assignedEdgesType));
} catch (IOException e) {
log.warn("Unable to parse assigned edges!", e);
}
}
return dashboardInfo;
}

32
dao/src/main/java/org/thingsboard/server/dao/model/nosql/RuleChainEntity.java

@ -20,11 +20,17 @@ import com.datastax.driver.mapping.annotations.ClusteringColumn;
import com.datastax.driver.mapping.annotations.Column;
import com.datastax.driver.mapping.annotations.PartitionKey;
import com.datastax.driver.mapping.annotations.Table;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.ShortEdgeInfo;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.id.TenantId;
@ -33,11 +39,14 @@ import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.model.SearchTextEntity;
import org.thingsboard.server.dao.model.type.JsonCodec;
import java.io.IOException;
import java.util.HashSet;
import java.util.UUID;
import static org.thingsboard.server.dao.model.ModelConstants.ADDITIONAL_INFO_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.DEBUG_MODE;
import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_ASSIGNED_EDGES_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_COLUMN_FAMILY_NAME;
import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_CONFIGURATION_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_FIRST_RULE_NODE_ID_PROPERTY;
@ -46,11 +55,16 @@ import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_ROOT_PR
import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_TENANT_ID_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY;
@Slf4j
@Table(name = RULE_CHAIN_COLUMN_FAMILY_NAME)
@EqualsAndHashCode
@ToString
public class RuleChainEntity implements SearchTextEntity<RuleChain> {
private static final ObjectMapper objectMapper = new ObjectMapper();
private static final JavaType assignedEdgesType =
objectMapper.getTypeFactory().constructCollectionType(HashSet.class, ShortEdgeInfo.class);
@PartitionKey
@Column(name = ID_PROPERTY)
private UUID id;
@ -74,6 +88,10 @@ public class RuleChainEntity implements SearchTextEntity<RuleChain> {
@Column(name = ADDITIONAL_INFO_PROPERTY, codec = JsonCodec.class)
private JsonNode additionalInfo;
@Getter @Setter
@Column(name = RULE_CHAIN_ASSIGNED_EDGES_PROPERTY)
private String assignedEdges;
public RuleChainEntity() {
}
@ -89,6 +107,13 @@ public class RuleChainEntity implements SearchTextEntity<RuleChain> {
this.debugMode = ruleChain.isDebugMode();
this.configuration = ruleChain.getConfiguration();
this.additionalInfo = ruleChain.getAdditionalInfo();
if (ruleChain.getAssignedEdges() != null) {
try {
this.assignedEdges = objectMapper.writeValueAsString(ruleChain.getAssignedEdges());
} catch (JsonProcessingException e) {
log.error("Unable to serialize assigned edges to string!", e);
}
}
}
@Override
@ -176,6 +201,13 @@ public class RuleChainEntity implements SearchTextEntity<RuleChain> {
ruleChain.setDebugMode(this.debugMode);
ruleChain.setConfiguration(this.configuration);
ruleChain.setAdditionalInfo(this.additionalInfo);
if (!StringUtils.isEmpty(assignedEdges)) {
try {
ruleChain.setAssignedEdges(objectMapper.readValue(assignedEdges, assignedEdgesType));
} catch (IOException e) {
log.warn("Unable to parse assigned edges!", e);
}
}
return ruleChain;
}

30
dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleChainEntity.java

@ -16,11 +16,17 @@
package org.thingsboard.server.dao.model.sql;
import com.datastax.driver.core.utils.UUIDs;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.ShortEdgeInfo;
import org.thingsboard.server.common.data.UUIDConverter;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.RuleNodeId;
@ -35,14 +41,21 @@ import org.thingsboard.server.dao.util.mapping.JsonStringType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import java.io.IOException;
import java.util.HashSet;
@Data
@Slf4j
@EqualsAndHashCode(callSuper = true)
@Entity
@TypeDef(name = "json", typeClass = JsonStringType.class)
@Table(name = ModelConstants.RULE_CHAIN_COLUMN_FAMILY_NAME)
public class RuleChainEntity extends BaseSqlEntity<RuleChain> implements SearchTextEntity<RuleChain> {
private static final ObjectMapper objectMapper = new ObjectMapper();
private static final JavaType assignedEdgesType =
objectMapper.getTypeFactory().constructCollectionType(HashSet.class, ShortEdgeInfo.class);
@Column(name = ModelConstants.RULE_CHAIN_TENANT_ID_PROPERTY)
private String tenantId;
@ -69,6 +82,9 @@ public class RuleChainEntity extends BaseSqlEntity<RuleChain> implements SearchT
@Column(name = ModelConstants.ADDITIONAL_INFO_PROPERTY)
private JsonNode additionalInfo;
@Column(name = ModelConstants.RULE_CHAIN_ASSIGNED_EDGES_PROPERTY)
private String assignedEdges;
public RuleChainEntity() {
}
@ -86,6 +102,13 @@ public class RuleChainEntity extends BaseSqlEntity<RuleChain> implements SearchT
this.debugMode = ruleChain.isDebugMode();
this.configuration = ruleChain.getConfiguration();
this.additionalInfo = ruleChain.getAdditionalInfo();
if (ruleChain.getAssignedEdges() != null) {
try {
this.assignedEdges = objectMapper.writeValueAsString(ruleChain.getAssignedEdges());
} catch (JsonProcessingException e) {
log.error("Unable to serialize assigned edges to string!", e);
}
}
}
@Override
@ -111,6 +134,13 @@ public class RuleChainEntity extends BaseSqlEntity<RuleChain> implements SearchT
ruleChain.setDebugMode(debugMode);
ruleChain.setConfiguration(configuration);
ruleChain.setAdditionalInfo(additionalInfo);
if (!StringUtils.isEmpty(assignedEdges)) {
try {
ruleChain.setAssignedEdges(objectMapper.readValue(assignedEdges, assignedEdgesType));
} catch (IOException e) {
log.warn("Unable to parse assigned edges!", e);
}
}
return ruleChain;
}
}

154
dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java

@ -15,6 +15,8 @@
*/
package org.thingsboard.server.dao.rule;
import com.google.common.base.Function;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@ -23,12 +25,16 @@ import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.BaseData;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.page.TimePageData;
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.common.data.rule.NodeConnectionInfo;
@ -36,13 +42,16 @@ import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo;
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.rule.RuleNode;
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;
import org.thingsboard.server.dao.service.PaginatedRemover;
import org.thingsboard.server.dao.service.TimePaginatedRemover;
import org.thingsboard.server.dao.service.Validator;
import org.thingsboard.server.dao.tenant.TenantDao;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -66,6 +75,9 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
@Autowired
private TenantDao tenantDao;
@Autowired
private EdgeDao edgeDao;
@Override
public RuleChain saveRuleChain(RuleChain ruleChain) {
ruleChainValidator.validate(ruleChain, RuleChain::getTenantId);
@ -352,6 +364,89 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
tenantRuleChainsRemover.removeEntities(tenantId, tenantId);
}
@Override
public RuleChain assignRuleChainToEdge(TenantId tenantId, RuleChainId ruleChainId, EdgeId edgeId) {
RuleChain ruleChain = findRuleChainById(tenantId, ruleChainId);
Edge edge = edgeDao.findById(tenantId, edgeId.getId());
if (edge == null) {
throw new DataValidationException("Can't assign ruleChain to non-existent edge!");
}
if (!edge.getTenantId().getId().equals(ruleChain.getTenantId().getId())) {
throw new DataValidationException("Can't assign ruleChain to edge from different tenant!");
}
if (ruleChain.addAssignedEdge(edge)) {
try {
createRelation(tenantId, new EntityRelation(edgeId, ruleChainId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE));
} catch (ExecutionException | InterruptedException e) {
log.warn("[{}] Failed to create ruleChain relation. Edge Id: [{}]", ruleChainId, edgeId);
throw new RuntimeException(e);
}
return saveRuleChain(ruleChain);
} else {
return ruleChain;
}
}
@Override
public RuleChain unassignRuleChainFromEdge(TenantId tenantId, RuleChainId ruleChainId, EdgeId edgeId) {
RuleChain ruleChain = findRuleChainById(tenantId, ruleChainId);
Edge edge = edgeDao.findById(tenantId, edgeId.getId());
if (edge == null) {
throw new DataValidationException("Can't unassign ruleChain from non-existent edge!");
}
if (ruleChain.removeAssignedEdge(edge)) {
try {
deleteRelation(tenantId, new EntityRelation(edgeId, ruleChainId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE));
} catch (ExecutionException | InterruptedException e) {
log.warn("[{}] Failed to delete ruleChain relation. Edge Id: [{}]", ruleChainId, edgeId);
throw new RuntimeException(e);
}
return saveRuleChain(ruleChain);
} else {
return ruleChain;
}
}
@Override
public void unassignEdgeRuleChains(TenantId tenantId, EdgeId edgeId) {
log.trace("Executing unassignEdgeRuleChains, edgeId [{}]", edgeId);
Validator.validateId(edgeId, "Incorrect edgeId " + edgeId);
Edge edge = edgeDao.findById(tenantId, edgeId.getId());
if (edge == null) {
throw new DataValidationException("Can't unassign ruleChains from non-existent edge!");
}
new EdgeRuleChainsUnassigner(edge).removeEntities(tenantId, edge);
}
@Override
public void updateEdgeRuleChains(TenantId tenantId, EdgeId edgeId) {
log.trace("Executing updateEdgeRuleChains, edgeId [{}]", edgeId);
Validator.validateId(edgeId, "Incorrect edgeId " + edgeId);
Edge edge = edgeDao.findById(tenantId, edgeId.getId());
if (edge == null) {
throw new DataValidationException("Can't update ruleChains for non-existent edge!");
}
new EdgeRuleChainsUpdater(edge).removeEntities(tenantId, edge);
}
@Override
public ListenableFuture<TimePageData<RuleChain>> findRuleChainsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink) {
log.trace("Executing findRuleChainsByTenantIdAndEdgeId, tenantId [{}], edgeId [{}], pageLink [{}]", tenantId, edgeId, pageLink);
Validator.validateId(tenantId, "Incorrect tenantId " + tenantId);
Validator.validateId(edgeId, "Incorrect customerId " + edgeId);
Validator.validatePageLink(pageLink, "Incorrect page link " + pageLink);
ListenableFuture<List<RuleChain>> dashboards = ruleChainDao.findRuleChainsByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink);
return Futures.transform(dashboards, new Function<List<RuleChain>, TimePageData<RuleChain>>() {
@Nullable
@Override
public TimePageData<RuleChain> apply(@Nullable List<RuleChain> RuleChain) {
return new TimePageData<>(RuleChain, pageLink);
}
});
}
private void checkRuleNodesAndDelete(TenantId tenantId, RuleChainId ruleChainId) {
List<EntityRelation> nodeRelations = getRuleChainToNodeRelations(tenantId, ruleChainId);
for (EntityRelation relation : nodeRelations) {
@ -384,6 +479,15 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
relationService.deleteRelation(tenantId, relation);
}
private RuleChain updateAssignedEdge(TenantId tenantId, RuleChainId ruleChainId, Edge edge) {
RuleChain ruleChain = findRuleChainById(tenantId, ruleChainId);
if (ruleChain.updateAssignedEdge(edge)) {
return saveRuleChain(ruleChain);
} else {
return ruleChain;
}
}
private DataValidator<RuleChain> ruleChainValidator =
new DataValidator<RuleChain>() {
@Override
@ -420,4 +524,54 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
checkRuleNodesAndDelete(tenantId, entity.getId());
}
};
private class EdgeRuleChainsUnassigner extends TimePaginatedRemover<Edge, RuleChain> {
private Edge edge;
EdgeRuleChainsUnassigner(Edge edge) {
this.edge = edge;
}
@Override
protected List<RuleChain> findEntities(TenantId tenantId, Edge edge, TimePageLink pageLink) {
try {
return ruleChainDao.findRuleChainsByTenantIdAndEdgeId(edge.getTenantId().getId(), edge.getId().getId(), pageLink).get();
} catch (InterruptedException | ExecutionException e) {
log.warn("Failed to get ruleChains by tenantId [{}] and edgeId [{}].", edge.getTenantId().getId(), edge.getId().getId());
throw new RuntimeException(e);
}
}
@Override
protected void removeEntity(TenantId tenantId, RuleChain entity) {
unassignRuleChainFromEdge(edge.getTenantId(), new RuleChainId(entity.getUuidId()), this.edge.getId());
}
}
private class EdgeRuleChainsUpdater extends TimePaginatedRemover<Edge, RuleChain> {
private Edge edge;
EdgeRuleChainsUpdater(Edge edge) {
this.edge = edge;
}
@Override
protected List<RuleChain> findEntities(TenantId tenantId, Edge edge, TimePageLink pageLink) {
try {
return ruleChainDao.findRuleChainsByTenantIdAndEdgeId(edge.getTenantId().getId(), edge.getId().getId(), pageLink).get();
} catch (InterruptedException | ExecutionException e) {
log.warn("Failed to get ruleChains by tenantId [{}] and edgeId [{}].", edge.getTenantId().getId(), edge.getId().getId());
throw new RuntimeException(e);
}
}
@Override
protected void removeEntity(TenantId tenantId, RuleChain entity) {
updateAssignedEdge(edge.getTenantId(), new RuleChainId(entity.getUuidId()), this.edge);
}
}
}

26
dao/src/main/java/org/thingsboard/server/dao/rule/CassandraRuleChainDao.java

@ -15,16 +15,26 @@
*/
package org.thingsboard.server.dao.rule;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.EntityType;
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;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.model.nosql.RuleChainEntity;
import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao;
import org.thingsboard.server.dao.relation.RelationDao;
import org.thingsboard.server.dao.util.NoSqlDao;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
@ -39,6 +49,9 @@ import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_TENANT_
@NoSqlDao
public class CassandraRuleChainDao extends CassandraAbstractSearchTextDao<RuleChainEntity, RuleChain> implements RuleChainDao {
@Autowired
private RelationDao relationDao;
@Override
protected Class<RuleChainEntity> getColumnFamilyClass() {
return RuleChainEntity.class;
@ -60,4 +73,17 @@ public class CassandraRuleChainDao extends CassandraAbstractSearchTextDao<RuleCh
return DaoUtil.convertDataList(ruleChainEntities);
}
@Override
public ListenableFuture<List<RuleChain>> findRuleChainsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) {
log.debug("Try to find rule chains 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<RuleChain>> ruleChainFutures = new ArrayList<>(input.size());
for (EntityRelation relation : input) {
ruleChainFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId()));
}
return Futures.successfulAsList(ruleChainFutures);
});
}
}

12
dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java

@ -15,7 +15,9 @@
*/
package org.thingsboard.server.dao.rule;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.dao.Dao;
@ -36,4 +38,14 @@ public interface RuleChainDao extends Dao<RuleChain> {
*/
List<RuleChain> findRuleChainsByTenantId(UUID tenantId, TextPageLink pageLink);
/**
* Find rule chains by tenantId, edgeId and page link.
*
* @param tenantId the tenantId
* @param edgeId the edgeId
* @param pageLink the page link
* @return the list of rule chain objects
*/
ListenableFuture<List<RuleChain>> findRuleChainsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink);
}

26
dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java

@ -15,20 +15,30 @@
*/
package org.thingsboard.server.dao.sql.rule;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.UUIDConverter;
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;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.model.sql.RuleChainEntity;
import org.thingsboard.server.dao.relation.RelationDao;
import org.thingsboard.server.dao.rule.RuleChainDao;
import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
import org.thingsboard.server.dao.util.SqlDao;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
@ -43,6 +53,9 @@ public class JpaRuleChainDao extends JpaAbstractSearchTextDao<RuleChainEntity, R
@Autowired
private RuleChainRepository ruleChainRepository;
@Autowired
private RelationDao relationDao;
@Override
protected Class getEntityClass() {
return RuleChainEntity.class;
@ -63,4 +76,17 @@ public class JpaRuleChainDao extends JpaAbstractSearchTextDao<RuleChainEntity, R
new PageRequest(0, pageLink.getLimit())));
}
@Override
public ListenableFuture<List<RuleChain>> findRuleChainsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) {
log.debug("Try to find rule chains 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.RULE_CHAIN, pageLink);
return Futures.transformAsync(relations, input -> {
List<ListenableFuture<RuleChain>> ruleChainFutures = new ArrayList<>(input.size());
for (EntityRelation relation : input) {
ruleChainFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId()));
}
return Futures.successfulAsList(ruleChainFutures);
});
}
}

2
ui/src/app/api/dashboard.service.js

@ -309,7 +309,7 @@ function DashboardService($rootScope, $http, $q, $location, $filter) {
delete dashboard.publicCustomerId;
delete dashboard.assignedCustomersText;
delete dashboard.assignedCustomersIds;
delete dashboard.assignedEdgeIds;
delete dashboard.assignedEdgesIds;
return dashboard;
}

115
ui/src/app/api/rule-chain.service.js

@ -35,7 +35,13 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co
ruleNodeAllowCustomLinks: ruleNodeAllowCustomLinks,
resolveTargetRuleChains: resolveTargetRuleChains,
testScript: testScript,
getLatestRuleNodeDebugInput: getLatestRuleNodeDebugInput
getLatestRuleNodeDebugInput: getLatestRuleNodeDebugInput,
updateRuleChainEdges: updateRuleChainEdges,
addRuleChainEdges: addRuleChainEdges,
removeRuleChainEdges: removeRuleChainEdges,
getEdgeRuleChains: getEdgeRuleChains,
assignRuleChainToEdge: assignRuleChainToEdge,
unassignRuleChainFromEdge: unassignRuleChainFromEdge
};
return service;
@ -53,7 +59,7 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co
url += '&textOffset=' + pageLink.textOffset;
}
$http.get(url, config).then(function success(response) {
deferred.resolve(response.data);
deferred.resolve(prepareRuleChains(response.data));
}, function fail() {
deferred.reject();
});
@ -64,7 +70,7 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co
var deferred = $q.defer();
var url = '/api/ruleChain/' + ruleChainId;
$http.get(url, config).then(function success(response) {
deferred.resolve(response.data);
deferred.resolve(prepareRuleChain(response.data));
}, function fail() {
deferred.reject();
});
@ -74,8 +80,8 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co
function saveRuleChain(ruleChain) {
var deferred = $q.defer();
var url = '/api/ruleChain';
$http.post(url, ruleChain).then(function success(response) {
deferred.resolve(response.data);
$http.post(url, cleanRuleChain(ruleChain)).then(function success(response) {
deferred.resolve(prepareRuleChain(response.data));
}, function fail() {
deferred.reject();
});
@ -262,7 +268,7 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co
var deferred = $q.defer();
getRuleChain(ruleChainId, {ignoreErrors: true}).then(
(ruleChain) => {
deferred.resolve(ruleChain);
deferred.resolve(prepareRuleChain(ruleChain));
},
() => {
deferred.resolve({
@ -299,4 +305,101 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co
return deferred.promise;
}
function updateRuleChainEdges(ruleChainId, edgeIds) {
var deferred = $q.defer();
var url = '/api/ruleChain/' + ruleChainId + '/edges';
$http.post(url, edgeIds).then(function success(response) {
deferred.resolve(prepareRuleChain(response.data));
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function addRuleChainEdges(ruleChainId, edgeIds) {
var deferred = $q.defer();
var url = '/api/ruleChain/' + ruleChainId + '/edges/add';
$http.post(url, edgeIds).then(function success(response) {
deferred.resolve(prepareRuleChain(response.data));
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function removeRuleChainEdges(ruleChainId, edgeIds) {
var deferred = $q.defer();
var url = '/api/ruleChain/' + ruleChainId + '/edges/remove';
$http.post(url, edgeIds).then(function success(response) {
deferred.resolve(prepareRuleChain(response.data));
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function getEdgeRuleChains(edgeId, pageLink, config) {
var deferred = $q.defer();
var url = '/api/edge/' + edgeId + '/ruleChains?limit=' + pageLink.limit;
if (angular.isDefined(pageLink.idOffset)) {
url += '&offset=' + pageLink.idOffset;
}
$http.get(url, config).then(function success(response) {
response.data = prepareRuleChains(response.data);
if (pageLink.textSearch) {
response.data.data = $filter('filter')(response.data.data, {title: pageLink.textSearch});
}
deferred.resolve(response.data);
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function assignRuleChainToEdge(edgeId, ruleChainId) {
var deferred = $q.defer();
var url = '/api/edge/' + edgeId + '/ruleChain/' + ruleChainId;
$http.post(url, null).then(function success(response) {
deferred.resolve(prepareRuleChain(response.data));
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function unassignRuleChainFromEdge(edgeId, ruleChainId) {
var deferred = $q.defer();
var url = '/api/edge/' + edgeId + '/ruleChain/' + ruleChainId;
$http.delete(url).then(function success(response) {
deferred.resolve(prepareRuleChain(response.data));
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function prepareRuleChains(ruleChainsData) {
if (ruleChainsData.data) {
for (var i = 0; i < ruleChainsData.data.length; i++) {
ruleChainsData.data[i] = prepareRuleChain(ruleChainsData.data[i]);
}
}
return ruleChainsData;
}
function prepareRuleChain(ruleChain) {
ruleChain.assignedEdgesIds = [];
if (ruleChain.assignedEdges && ruleChain.assignedEdges.length) {
for (var j = 0; j < ruleChain.assignedEdges.length; j++) {
var assignedEdge = ruleChain.assignedEdges[j];
ruleChain.assignedEdgesIds.push(assignedEdge.edgeId.id);
}
}
return ruleChain;
}
function cleanRuleChain(ruleChain) {
delete ruleChain.assignedEdgesIds;
return ruleChain;
}
}

11
ui/src/app/dashboard/dashboards.controller.js

@ -157,6 +157,10 @@ export function DashboardsController(userService, dashboardService, customerServ
);
}
if (edgeId) {
vm.edgeDashboardsTitle = $translate.instant('edge.dashboards');
}
if (vm.dashboardsScope === 'tenant') {
fetchDashboardsFunction = function (pageLink) {
return dashboardService.getTenantDashboards(pageLink);
@ -217,10 +221,7 @@ export function DashboardsController(userService, dashboardService, customerServ
},
name: function() { return $translate.instant('action.assign') },
details: function() { return $translate.instant('dashboard.manage-assigned-edges') },
icon: "wifi_tethering",
isEnabled: function(dashboard) {
return dashboard;
}
icon: "wifi_tethering"
});
/*dashboardActionsList.push({
@ -750,7 +751,7 @@ export function DashboardsController(userService, dashboardService, customerServ
$event.stopPropagation();
}
$mdDialog.show({
controller: 'ManageAssignedEdgesController',
controller: 'ManageAssignedEdgesToDashboardController',
controllerAs: 'vm',
templateUrl: manageAssignedEdgesTemplate,
locals: {actionType: actionType, dashboardIds: dashboardIds, assignedEdges: assignedEdges},

4
ui/src/app/dashboard/index.js

@ -42,7 +42,7 @@ import DashboardController from './dashboard.controller';
import DashboardSettingsController from './dashboard-settings.controller';
import AddDashboardsToCustomerController from './add-dashboards-to-customer.controller';
import ManageAssignedCustomersController from './manage-assigned-customers.controller';
import ManageAssignedEdgesController from './manage-assigned-edges.controller';
import ManageAssignedEdgesToDashboardController from './manage-assigned-edges.controller';
import AddWidgetController from './add-widget.controller';
import DashboardDirective from './dashboard.directive';
import EditWidgetDirective from './edit-widget.directive';
@ -78,7 +78,7 @@ export default angular.module('thingsboard.dashboard', [
.controller('DashboardSettingsController', DashboardSettingsController)
.controller('AddDashboardsToCustomerController', AddDashboardsToCustomerController)
.controller('ManageAssignedCustomersController', ManageAssignedCustomersController)
.controller('ManageAssignedEdgesController', ManageAssignedEdgesController)
.controller('ManageAssignedEdgesToDashboardController', ManageAssignedEdgesToDashboardController)
.controller('AddWidgetController', AddWidgetController)
.controller('AddDashboardsToEdgeController', AddDashboardsToEdgeController)
.directive('tbDashboardDetails', DashboardDirective)

2
ui/src/app/dashboard/manage-assigned-edges.controller.js

@ -14,7 +14,7 @@
* limitations under the License.
*/
/*@ngInject*/
export default function ManageAssignedEdgesController($mdDialog, $q, types, dashboardService, actionType, dashboardIds, assignedEdges) {
export default function ManageAssignedEdgesToDashboardController($mdDialog, $q, types, dashboardService, actionType, dashboardIds, assignedEdges) {
var vm = this;

25
ui/src/app/edge/edge.controller.js

@ -229,6 +229,19 @@ export function EdgeController($rootScope, userService, edgeService, customerSer
}
);
edgeActionsList.push(
{
onAction: function ($event, item) {
openEdgeRuleChains($event, item);
},
name: function() { return $translate.instant('rulechain.rulechains') },
details: function() {
return $translate.instant('edge.manage-edge-rulechains');
},
icon: "settings_ethernet"
}
);
edgeActionsList.push(
{
onAction: function ($event, item) {
@ -554,10 +567,10 @@ export function EdgeController($rootScope, userService, edgeService, customerSer
$state.go('home.edges.dashboards', {edgeId: edge.id.id});
}
// function openEdgeRuleChains($event, edge) {
// if ($event) {
// $event.stopPropagation();
// }
// $state.go('home.edges.rule-chains', {edgeId: edge.id.id});
// }
function openEdgeRuleChains($event, edge) {
if ($event) {
$event.stopPropagation();
}
$state.go('home.edges.ruleChains', {edgeId: edge.id.id});
}
}

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

@ -775,7 +775,9 @@
"assign-new-edge": "Assign new edge",
"manage-edge-dashboards": "Manage edge dashboards",
"unassign-from-edge": "Unassign from edge",
"dashboards": "Edge Dashboards"
"dashboards": "Edge Dashboards",
"manage-edge-rulechains": "Manage edge rule chains",
"rulechains": "Edge Rule Chains"
},
"error": {
"unable-to-connect": "Unable to connect to the server! Please check your internet connection.",
@ -1380,7 +1382,28 @@
"no-rulechains-matching": "No rule chains matching '{{entity}}' were found.",
"rulechain-required": "Rule chain is required",
"management": "Rules management",
"debug-mode": "Debug mode"
"debug-mode": "Debug mode",
"assign-rulechains": "Assign rulechains",
"assign-new-rulechain": "Assign new rulechain",
"delete-rulechains": "Delete rulechains",
"unassign-rulechain": "Unassign rulechain",
"unassign-rulechains": "Unassign rulechains",
"unassign-rulechain-title": "Are you sure you want to unassign the rulechain '{{ruleChainTitle}}'?",
"unassign-rulechains-title": "Are you sure you want to unassign { count, plural, 1 {1 rulechain} other {# rulechains} }?",
"manage-assigned-edges": "Manage assigned edges",
"unassign-rulechain-from-edge-text": "After the confirmation the rulechain will be unassigned and won't be accessible by the edge.",
"assigned-edges": "Assigned edges",
"unassign-from-edge": "Unassign from edge",
"unassign-rulechains-from-edge-action-title": "Unassign { count, plural, 1 {1 rulechain} other {# rulechains} } from edge",
"unassign-rulechains-from-edge-text": "After the confirmation all selected rulechains will be unassigned and won't be accessible by the edge.",
"assign-rulechains-to-edge-text": "Assign { count, plural, 1 {1 rulechain} other {# rulechains} } to edges",
"assign-rulechain-to-edge": "Assign Rule Chain(s) To Edge",
"assign-rulechain-to-edge-text": "Please select the rulechains to assign to the edge",
"unassign-rulechains-from-edge-action-text": "Unassign { count, plural, 1 {1 rulechain} other {# rulechains} } from edges",
"assign-to-edges": "Assign Rule Chain(s) To Edges",
"assign-to-edges-text": "Please select the edges to assign the rulechain(s)",
"unassign-from-edges": "Unassign Rule Chain(s) From Edges",
"unassign-from-edges-text": "Please select the edges to unassign from the rulechain(s)"
},
"rulenode": {
"details": "Details",

130
ui/src/app/rulechain/add-rulechains-to-edge.controller.js

@ -0,0 +1,130 @@
/*
* Copyright © 2016-2019 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*@ngInject*/
export default function AddRuleChainsToEdgeController(ruleChainService, $mdDialog, $q, $filter, edgeId, ruleChains) {
var vm = this;
vm.ruleChains = ruleChains;
vm.searchText = '';
vm.assign = assign;
vm.cancel = cancel;
vm.hasData = hasData;
vm.noData = noData;
vm.searchRuleChainTextUpdated = searchRuleChainTextUpdated;
vm.toggleRuleChainSelection = toggleRuleChainSelection;
vm.theRuleChains = {
getItemAtIndex: function (index) {
if (index > vm.ruleChains.data.length) {
vm.theRuleChains.fetchMoreItems_(index);
return null;
}
var item = vm.ruleChains.data[index];
if (item) {
item.indexNumber = index + 1;
}
return item;
},
getLength: function () {
if (vm.ruleChains.hasNext) {
return vm.ruleChains.data.length + vm.ruleChains.nextPageLink.limit;
} else {
return vm.ruleChains.data.length;
}
},
fetchMoreItems_: function () {
if (vm.ruleChains.hasNext && !vm.ruleChains.pending) {
vm.ruleChains.pending = true;
ruleChainService.getRuleChains(vm.ruleChains.nextPageLink).then(
function success(ruleChains) {
vm.ruleChains.data = vm.ruleChains.data.concat(filterNonRootRuleChains(ruleChains.data));
vm.ruleChains.nextPageLink = ruleChains.nextPageLink;
vm.ruleChains.hasNext = ruleChains.hasNext;
if (vm.ruleChains.hasNext) {
vm.ruleChains.nextPageLink.limit = vm.ruleChains.pageSize;
}
vm.ruleChains.pending = false;
},
function fail() {
vm.ruleChains.hasNext = false;
vm.ruleChains.pending = false;
});
}
}
}
function cancel () {
$mdDialog.cancel();
}
function assign () {
var tasks = [];
for (var ruleChainId in vm.ruleChains.selections) {
tasks.push(ruleChainService.assignRuleChainToEdge(edgeId, ruleChainId));
}
$q.all(tasks).then(function () {
$mdDialog.hide();
});
}
function noData () {
return vm.ruleChains.data.length == 0 && !vm.ruleChains.hasNext;
}
function hasData () {
return vm.ruleChains.data.length > 0;
}
function toggleRuleChainSelection ($event, ruleChain) {
$event.stopPropagation();
var selected = angular.isDefined(ruleChain.selected) && ruleChain.selected;
ruleChain.selected = !selected;
if (ruleChain.selected) {
vm.ruleChains.selections[ruleChain.id.id] = true;
vm.ruleChains.selectedCount++;
} else {
delete vm.ruleChains.selections[ruleChain.id.id];
vm.ruleChains.selectedCount--;
}
}
function searchRuleChainTextUpdated () {
vm.ruleChains = {
pageSize: vm.ruleChains.pageSize,
data: [],
nextPageLink: {
limit: vm.ruleChains.pageSize,
textSearch: vm.searchText
},
selections: {},
selectedCount: 0,
hasNext: true,
pending: false
};
}
function filterNonRootRuleChains(ruleChains) {
return $filter('filter')(ruleChains, isNonRootRuleChain);
}
function isNonRootRuleChain(ruleChain) {
return ruleChain && !ruleChain.root;
}
}

77
ui/src/app/rulechain/add-rulechains-to-edge.tpl.html

@ -0,0 +1,77 @@
<!--
Copyright © 2016-2019 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<md-dialog aria-label="{{ 'rulechain.assign-rulechain-to-edge' | translate }}">
<form name="theForm" ng-submit="vm.assign()">
<md-toolbar>
<div class="md-toolbar-tools">
<h2 translate>rulechain.assign-rulechain-to-edge</h2>
<span flex></span>
<md-button class="md-icon-button" ng-click="vm.cancel()">
<ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon>
</md-button>
</div>
</md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content>
<div class="md-dialog-content">
<fieldset>
<span translate>rulechain.assign-rulechain-to-edge-text</span>
<md-input-container class="md-block" style='margin-bottom: 0px;'>
<label>&nbsp;</label>
<md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons">
search
</md-icon>
<input id="rulechain-search" autofocus ng-model="vm.searchText"
ng-change="vm.searchRuleChainTextUpdated()"
placeholder="{{ 'common.enter-search' | translate }}"/>
</md-input-container>
<div style='min-height: 150px;'>
<span translate layout-align="center center"
style="text-transform: uppercase; display: flex; height: 150px;"
class="md-subhead"
ng-show="vm.noData()">rulechain.no-rulechains-text</span>
<md-virtual-repeat-container ng-show="vm.hasData()"
tb-scope-element="repeatContainer" md-top-index="vm.topIndex" flex
style='min-height: 150px; width: 100%;'>
<md-list>
<md-list-item md-virtual-repeat="ruleChain in vm.theRuleChains" md-on-demand
class="repeated-item" flex>
<md-checkbox ng-click="vm.toggleRuleChainSelection($event, ruleChain)"
aria-label="{{ 'item.selected' | translate }}"
ng-checked="ruleChain.selected"></md-checkbox>
<span> {{ ruleChain.name }} </span>
</md-list-item>
</md-list>
</md-virtual-repeat-container>
</div>
</fieldset>
</div>
</md-dialog-content>
<md-dialog-actions layout="row">
<span flex></span>
<md-button ng-disabled="$root.loading || vm.ruleChains.selectedCount == 0" type="submit"
class="md-raised md-primary">
{{ 'action.assign' | translate }}
</md-button>
<md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
translate }}
</md-button>
</md-dialog-actions>
</form>
</md-dialog>

4
ui/src/app/rulechain/index.js

@ -24,6 +24,8 @@ import RuleNodeDirective from './rulenode.directive';
import LinkDirective from './link.directive';
import MessageTypeAutocompleteDirective from './message-type-autocomplete.directive';
import NodeScriptTest from './script/node-script-test.service';
import AddRuleChainsToEdgeController from './add-rulechains-to-edge.controller';
import ManageAssignedEdgesToRuleChainController from "./manage-assigned-edges.controller";
export default angular.module('thingsboard.ruleChain', [])
.config(RuleChainRoutes)
@ -32,6 +34,8 @@ export default angular.module('thingsboard.ruleChain', [])
.controller('AddRuleNodeController', AddRuleNodeController)
.controller('AddRuleNodeLinkController', AddRuleNodeLinkController)
.controller('NodeScriptTestController', NodeScriptTestController)
.controller('ManageAssignedEdgesToRuleChainController', ManageAssignedEdgesToRuleChainController)
.controller('AddRuleChainsToEdgeController', AddRuleChainsToEdgeController)
.directive('tbRuleChain', RuleChainDirective)
.directive('tbRuleNodeDefinedConfig', RuleNodeDefinedConfigDirective)
.directive('tbRuleNodeConfig', RuleNodeConfigDirective)

69
ui/src/app/rulechain/manage-assigned-edges.controller.js

@ -0,0 +1,69 @@
/*
* Copyright © 2016-2019 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*@ngInject*/
export default function ManageAssignedEdgesToRuleChainController($mdDialog, $q, types, ruleChainService, actionType, ruleChainIds, assignedEdges) {
var vm = this;
vm.types = types;
vm.actionType = actionType;
vm.ruleChainIds = ruleChainIds;
vm.assignedEdges = assignedEdges;
if (actionType != 'manage') {
vm.assignedEdges = [];
}
if (actionType == 'manage') {
vm.titleText = 'rulechain.manage-assigned-edges';
vm.labelText = 'rulechain.assigned-edges';
vm.actionName = 'action.update';
} else if (actionType == 'assign') {
vm.titleText = 'rulechain.assign-to-edges';
vm.labelText = 'rulechain.assign-to-edges-text';
vm.actionName = 'action.assign';
} else if (actionType == 'unassign') {
vm.titleText = 'rulechain.unassign-from-edges';
vm.labelText = 'rulechain.unassign-from-edges-text';
vm.actionName = 'action.unassign';
}
vm.submit = submit;
vm.cancel = cancel;
function cancel () {
$mdDialog.cancel();
}
function submit () {
var tasks = [];
for (var i=0;i<vm.ruleChainIds.length;i++) {
var ruleChainId = vm.ruleChainIds[i];
var promise;
if (vm.actionType == 'manage') {
promise = ruleChainService.updateRuleChainEdges(ruleChainId, vm.assignedEdges);
} else if (vm.actionType == 'assign') {
promise = ruleChainService.addRuleChainEdges(ruleChainId, vm.assignedEdges);
} else if (vm.actionType == 'unassign') {
promise = ruleChainService.removeRuleChainEdges(ruleChainId, vm.assignedEdges);
}
tasks.push(promise);
}
$q.all(tasks).then(function () {
$mdDialog.hide();
});
}
}

51
ui/src/app/rulechain/manage-assigned-edges.tpl.html

@ -0,0 +1,51 @@
<!--
Copyright © 2016-2019 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<md-dialog aria-label="{{ vm.titleText | translate }}" style="width: 600px;">
<form name="theForm" ng-submit="vm.submit()">
<md-toolbar>
<div class="md-toolbar-tools">
<h2 translate>{{vm.titleText}}</h2>
<span flex></span>
<md-button class="md-icon-button" ng-click="vm.cancel()">
<ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon>
</md-button>
</div>
</md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content>
<div class="md-dialog-content">
<fieldset>
<span translate>{{vm.labelText}}</span>
<tb-entity-list ng-disabled="$root.loading"
ng-model="vm.assignedEdges"
entity-type="vm.types.entityType.edge"></tb-entity-list>
</fieldset>
</div>
</md-dialog-content>
<md-dialog-actions layout="row">
<span flex></span>
<md-button ng-disabled="$root.loading || !theForm.$dirty || !theForm.$valid" type="submit" class="md-raised md-primary">
{{ vm.actionName | translate }}
</md-button>
<md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
translate }}
</md-button>
</md-dialog-actions>
</form>
</md-dialog>

26
ui/src/app/rulechain/rulechain.routes.js

@ -41,7 +41,8 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider
},
data: {
searchEnabled: true,
pageTitle: 'rulechain.rulechains'
pageTitle: 'rulechain.rulechains',
ruleChainsType: 'tenant'
},
ncyBreadcrumb: {
label: '{"icon": "settings_ethernet", "label": "rulechain.rulechains"}'
@ -123,5 +124,26 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider
ncyBreadcrumb: {
label: '{"icon": "settings_ethernet", "label": "{{ (\'rulechain.import\' | translate) + \': \'+ vm.ruleChain.name }}", "translate": "false"}'
}
})
.state('home.edges.ruleChains', {
url: '/:edgeId/ruleChains',
params: {'topIndex': 0},
module: 'private',
auth: ['TENANT_ADMIN'],
views: {
"content@home": {
templateUrl: ruleChainsTemplate,
controllerAs: 'vm',
controller: 'RuleChainsController'
}
},
data: {
ruleChainsType: 'edge',
searchEnabled: true,
pageTitle: 'edge.rulechains'
},
ncyBreadcrumb: {
label: '{"icon": "settings_ethernet", "label": "{{ vm.edgeRuleChainsTitle }}", "translate": "false"}'
}
});
}
}

327
ui/src/app/rulechain/rulechains.controller.js

@ -17,12 +17,19 @@
import addRuleChainTemplate from './add-rulechain.tpl.html';
import ruleChainCard from './rulechain-card.tpl.html';
import manageAssignedEdgesTemplate from "./manage-assigned-edges.tpl.html";
import addRuleChainsToEdgeTemplate from "./add-rulechains-to-edge.tpl.html";
/* eslint-enable import/no-unresolved, import/default */
/*@ngInject*/
export default function RuleChainsController(ruleChainService, userService, importExport, $state,
$stateParams, $filter, $translate, $mdDialog, types) {
$stateParams, $filter, $translate, $mdDialog, $document, $q, types) {
var vm = this;
var edgeId = $stateParams.edgeId;
vm.ruleChainsScope = $state.$current.data.ruleChainsType;
var ruleChainActionsList = [
{
@ -40,52 +47,10 @@ export default function RuleChainsController(ruleChainService, userService, impo
name: function() { $translate.instant('action.export') },
details: function() { return $translate.instant('rulechain.export') },
icon: "file_download"
},
{
onAction: function ($event, item) {
setRootRuleChain($event, item);
},
name: function() { return $translate.instant('rulechain.set-root') },
details: function() { return $translate.instant('rulechain.set-root') },
icon: "flag",
isEnabled: isNonRootRuleChain
},
{
onAction: function ($event, item) {
vm.grid.deleteItem($event, item);
},
name: function() { return $translate.instant('action.delete') },
details: function() { return $translate.instant('rulechain.delete') },
icon: "delete",
isEnabled: isNonRootRuleChain
}
];
var ruleChainAddItemActionsList = [
{
onAction: function ($event) {
vm.grid.addItem($event);
},
name: function() { return $translate.instant('action.create') },
details: function() { return $translate.instant('rulechain.create-new-rulechain') },
icon: "insert_drive_file"
},
{
onAction: function ($event) {
importExport.importRuleChain($event).then(
function(ruleChainImport) {
$state.go('home.ruleChains.importRuleChain', {ruleChainImport:ruleChainImport});
}
);
},
name: function() { return $translate.instant('action.import') },
details: function() { return $translate.instant('rulechain.import') },
icon: "file_upload"
}
];
var vm = this;
var ruleChainGroupActionsList = [];
vm.types = types;
vm.ruleChainGridConfig = {
@ -98,17 +63,15 @@ export default function RuleChainsController(ruleChainService, userService, impo
deleteItemsActionTitleFunc: deleteRuleChainsActionTitle,
deleteItemsContentFunc: deleteRuleChainsText,
fetchItemsFunc: fetchRuleChains,
saveItemFunc: saveRuleChain,
clickItemFunc: openRuleChain,
deleteItemFunc: deleteRuleChain,
getItemTitleFunc: getRuleChainTitle,
itemCardTemplateUrl: ruleChainCard,
parentCtl: vm,
actionsList: ruleChainActionsList,
addItemActions: ruleChainAddItemActionsList,
groupActionsList: ruleChainGroupActionsList,
onGridInited: gridInited,
@ -133,7 +96,160 @@ export default function RuleChainsController(ruleChainService, userService, impo
vm.exportRuleChain = exportRuleChain;
vm.setRootRuleChain = setRootRuleChain;
initController();
function initController() {
var fetchRuleChainsFunction = null;
var deleteRuleChainFunction = null;
if (edgeId) {
vm.edgeRuleChainsTitle = $translate.instant('edge.rulechains');
}
if (vm.ruleChainsScope === 'tenant') {
fetchRuleChainsFunction = function (pageLink) {
return fetchRuleChains(pageLink);
};
deleteRuleChainFunction = function (ruleChainId) {
return deleteRuleChain(ruleChainId);
};
ruleChainActionsList.push({
onAction: function ($event, item) {
setRootRuleChain($event, item);
},
name: function() { return $translate.instant('rulechain.set-root') },
details: function() { return $translate.instant('rulechain.set-root') },
icon: "flag",
isEnabled: isNonRootRuleChain
});
ruleChainActionsList.push({
onAction: function ($event, item) {
manageAssignedEdges($event, item);
},
name: function() { return $translate.instant('action.assign') },
details: function() { return $translate.instant('rulechain.manage-assigned-edges') },
icon: "wifi_tethering",
isEnabled: isNonRootRuleChain
});
ruleChainActionsList.push({
onAction: function ($event, item) {
vm.grid.deleteItem($event, item);
},
name: function() { return $translate.instant('action.delete') },
details: function() { return $translate.instant('rulechain.delete') },
icon: "delete",
isEnabled: isNonRootRuleChain
});
ruleChainGroupActionsList.push(
{
onAction: function ($event, items) {
assignRuleChainsToEdges($event, items);
},
name: function() { return $translate.instant('rulechain.assign-rulechains') },
details: function(selectedCount) {
return $translate.instant('rulechain.assign-rulechains-to-edge-text', {count: selectedCount}, "messageformat");
},
icon: "wifi_tethering"
}
);
ruleChainGroupActionsList.push(
{
onAction: function ($event, items) {
unassignRuleChainsFromEdges($event, items);
},
name: function() { return $translate.instant('rulechain.unassign-rulechains') },
details: function(selectedCount) {
return $translate.instant('rulechain.unassign-rulechains-from-edge-action-text', {count: selectedCount}, "messageformat");
},
icon: "portable_wifi_off"
}
);
ruleChainGroupActionsList.push(
{
onAction: function ($event) {
vm.grid.deleteItems($event);
},
name: function() { return $translate.instant('rulechain.delete-rulechains') },
details: deleteRuleChainsActionTitle,
icon: "delete"
}
);
vm.ruleChainGridConfig.addItemActions = [];
vm.ruleChainGridConfig.addItemActions.push({
onAction: function ($event) {
vm.grid.addItem($event);
},
name: function() { return $translate.instant('action.create') },
details: function() { return $translate.instant('rulechain.create-new-rulechain') },
icon: "insert_drive_file"
});
vm.ruleChainGridConfig.addItemActions.push({
onAction: function ($event) {
importExport.importRuleChain($event).then(
function(ruleChainImport) {
$state.go('home.ruleChains.importRuleChain', {ruleChainImport:ruleChainImport});
}
);
},
name: function() { return $translate.instant('action.import') },
details: function() { return $translate.instant('rulechain.import') },
icon: "file_upload"
});
} else if (vm.ruleChainsScope === 'edge') {
fetchRuleChainsFunction = function (pageLink) {
return ruleChainService.getEdgeRuleChains(edgeId, pageLink);
};
deleteRuleChainFunction = function (ruleChainId) {
return ruleChainService.unassignRuleChainFromEdge(edgeId, ruleChainId);
};
ruleChainActionsList.push(
{
onAction: function ($event, item) {
unassignFromEdge($event, item, edgeId);
},
name: function() { return $translate.instant('action.unassign') },
details: function() { return $translate.instant('rulechain.unassign-from-edge') },
icon: "assignment_return"
}
);
ruleChainGroupActionsList.push(
{
onAction: function ($event, items) {
unassignRuleChainsFromEdge($event, items, edgeId);
},
name: function() { return $translate.instant('rulechain.unassign-rulechains') },
details: function(selectedCount) {
return $translate.instant('rulechain.unassign-rulechains-from-edge-action-title', {count: selectedCount}, "messageformat");
},
icon: "assignment_return"
}
);
vm.ruleChainGridConfig.addItemAction = {
onAction: function ($event) {
addRuleChainsToEdge($event);
},
name: function() { return $translate.instant('rulechain.assign-rulechains') },
details: function() { return $translate.instant('rulechain.assign-new-rulechain') },
icon: "add"
}
}
vm.ruleChainGridConfig.fetchItemsFunc = fetchRuleChainsFunction;
vm.ruleChainGridConfig.deleteItemFunc = deleteRuleChainFunction;
}
function deleteRuleChainTitle(ruleChain) {
return $translate.instant('rulechain.delete-rulechain-title', {ruleChainName: ruleChain.name});
}
@ -210,6 +326,123 @@ export default function RuleChainsController(ruleChainService, userService, impo
}
);
});
}
function manageAssignedEdges($event, ruleChain) {
showManageAssignedEdgesDialog($event, [ruleChain.id.id], 'manage', ruleChain.assignedEdgesIds);
}
function assignRuleChainsToEdges($event, items) {
var ruleChainIds = [];
for (var id in items.selections) {
ruleChainIds.push(id);
}
showManageAssignedEdgesDialog($event, ruleChainIds, 'assign');
}
function unassignRuleChainsFromEdges($event, items) {
var ruleChainIds = [];
for (var id in items.selections) {
ruleChainIds.push(id);
}
showManageAssignedEdgesDialog($event, ruleChainIds, 'unassign');
}
function unassignRuleChainsFromEdge($event, items, edgeId) {
var confirm = $mdDialog.confirm()
.targetEvent($event)
.title($translate.instant('rulechain.unassign-rulechains-title', {count: items.selectedCount}, 'messageformat'))
.htmlContent($translate.instant('rulechain.unassign-rulechains-from-edge-text'))
.ariaLabel($translate.instant('rulechain.unassign-rulechains'))
.cancel($translate.instant('action.no'))
.ok($translate.instant('action.yes'));
$mdDialog.show(confirm).then(function () {
var tasks = [];
for (var id in items.selections) {
tasks.push(ruleChainService.unassignRuleChainFromEdge(edgeId, id));
}
$q.all(tasks).then(function () {
vm.grid.refreshList();
});
});
}
function showManageAssignedEdgesDialog($event, ruleChainIds, actionType, assignedEdges) {
if ($event) {
$event.stopPropagation();
}
$mdDialog.show({
controller: 'ManageAssignedEdgesToRuleChainController',
controllerAs: 'vm',
templateUrl: manageAssignedEdgesTemplate,
locals: {actionType: actionType, ruleChainIds: ruleChainIds, assignedEdges: assignedEdges},
parent: angular.element($document[0].body),
fullscreen: true,
targetEvent: $event
}).then(function () {
vm.grid.refreshList();
}, function () {
});
}
function addRuleChainsToEdge($event) {
if ($event) {
$event.stopPropagation();
}
var pageSize = 10;
ruleChainService.getRuleChains({limit: pageSize, textSearch: ''}).then(
function success(_ruleChains) {
var ruleChains = {
pageSize: pageSize,
data: filterNonRootRuleChains(_ruleChains.data),
nextPageLink: _ruleChains.nextPageLink,
selections: {},
selectedCount: 0,
hasNext: _ruleChains.hasNext,
pending: false
};
if (ruleChains.hasNext) {
ruleChains.nextPageLink.limit = pageSize;
}
$mdDialog.show({
controller: 'AddRuleChainsToEdgeController',
controllerAs: 'vm',
templateUrl: addRuleChainsToEdgeTemplate,
locals: {edgeId: edgeId, ruleChains: ruleChains},
parent: angular.element($document[0].body),
fullscreen: true,
targetEvent: $event
}).then(function () {
vm.grid.refreshList();
}, function () {
});
},
function fail() {
});
}
function filterNonRootRuleChains(ruleChains) {
return $filter('filter')(ruleChains, isNonRootRuleChain);
}
function unassignFromEdge($event, ruleChain, edgeId) {
if ($event) {
$event.stopPropagation();
}
var title = $translate.instant('rulechain.unassign-rulechain-title', {ruleChainTitle: ruleChain.name});
var content = $translate.instant('rulechain.unassign-rulechain-from-edge-text');
var label = $translate.instant('rulechain.unassign-rulechain');
var confirm = $mdDialog.confirm()
.targetEvent($event)
.title(title)
.htmlContent(content)
.ariaLabel(label)
.cancel($translate.instant('action.no'))
.ok($translate.instant('action.yes'));
$mdDialog.show(confirm).then(function () {
ruleChainService.unassignRuleChainFromEdge(edgeId, ruleChain.id.id).then(function success() {
vm.grid.refreshList();
});
});
}
}

Loading…
Cancel
Save