Browse Source

Added root rule chain for the edge. Part 2

pull/2818/head
Volodymyr Babak 7 years ago
parent
commit
efd565d8b6
  1. 5
      application/src/main/data/upgrade/2.4.x/schema_update.sql
  2. 71
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java
  3. 2
      application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java
  4. 43
      application/src/main/java/org/thingsboard/server/controller/EdgeController.java
  5. 1
      application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
  6. 4
      common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java
  7. 2
      common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java
  8. 4
      common/data/src/main/java/org/thingsboard/server/common/data/ShortEdgeInfo.java
  9. 5
      common/data/src/main/java/org/thingsboard/server/common/data/edge/Edge.java
  10. 2
      common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java
  11. 9
      dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeService.java
  12. 1
      dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
  13. 14
      dao/src/main/java/org/thingsboard/server/dao/model/nosql/EdgeEntity.java
  14. 13
      dao/src/main/java/org/thingsboard/server/dao/model/sql/EdgeEntity.java
  15. 31
      dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java
  16. 1
      dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java
  17. 1
      dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java
  18. 1
      dao/src/main/resources/sql/schema-entities.sql
  19. 14
      ui/src/app/api/edge.service.js
  20. 68
      ui/src/app/edge/edge.controller.js
  21. 2
      ui/src/app/edge/index.js
  22. 129
      ui/src/app/edge/set-root-rule-chain-to-edges.controller.js
  23. 76
      ui/src/app/edge/set-root-rule-chain-to-edges.tpl.html
  24. 6
      ui/src/app/locale/locale.constant-en_US.json
  25. 2
      ui/src/app/rulechain/add-rulechain.tpl.html
  26. 10
      ui/src/app/rulechain/add-rulechains-to-edge.controller.js
  27. 2
      ui/src/app/rulechain/rulechain-card.tpl.html
  28. 2
      ui/src/app/rulechain/rulechain-fieldset.tpl.html
  29. 2
      ui/src/app/rulechain/rulechain.directive.js
  30. 38
      ui/src/app/rulechain/rulechain.routes.js
  31. 68
      ui/src/app/rulechain/rulechains.controller.js

5
application/src/main/data/upgrade/2.4.x/schema_update.sql

@ -18,8 +18,13 @@ CREATE TABLE IF NOT EXISTS edge (
id varchar(31) NOT NULL CONSTRAINT edge_pkey PRIMARY KEY, id varchar(31) NOT NULL CONSTRAINT edge_pkey PRIMARY KEY,
additional_info varchar, additional_info varchar,
customer_id varchar(31), customer_id varchar(31),
root_rule_chain_id varchar(31),
configuration varchar(10000000), configuration varchar(10000000),
type varchar(255),
name varchar(255), name varchar(255),
label varchar(255),
routing_key varchar(255),
secret varchar(255),
search_text varchar(255), search_text varchar(255),
tenant_id varchar(31) tenant_id varchar(31)
); );

71
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java

@ -39,6 +39,7 @@ import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
@ -97,17 +98,19 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
if (!started) { if (!started) {
RuleChain ruleChain = service.findRuleChainById(tenantId, entityId); RuleChain ruleChain = service.findRuleChainById(tenantId, entityId);
if (ruleChain != null) { if (ruleChain != null) {
ruleChainName = ruleChain.getName(); if (ruleChain.getType().equals(RuleChainType.SYSTEM)) {
List<RuleNode> ruleNodeList = service.getRuleChainNodes(tenantId, entityId); ruleChainName = ruleChain.getName();
log.trace("[{}][{}] Starting rule chain with {} nodes", tenantId, entityId, ruleNodeList.size()); List<RuleNode> ruleNodeList = service.getRuleChainNodes(tenantId, entityId);
// Creating and starting the actors; log.trace("[{}][{}] Starting rule chain with {} nodes", tenantId, entityId, ruleNodeList.size());
for (RuleNode ruleNode : ruleNodeList) { // Creating and starting the actors;
log.trace("[{}][{}] Creating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode); for (RuleNode ruleNode : ruleNodeList) {
ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode); log.trace("[{}][{}] Creating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode);
nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode)); ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode);
nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode));
}
initRoutes(ruleChain, ruleNodeList);
started = true;
} }
initRoutes(ruleChain, ruleNodeList);
started = true;
} }
} else { } else {
onUpdate(context); onUpdate(context);
@ -118,31 +121,35 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
public void onUpdate(ActorContext context) { public void onUpdate(ActorContext context) {
RuleChain ruleChain = service.findRuleChainById(tenantId, entityId); RuleChain ruleChain = service.findRuleChainById(tenantId, entityId);
if (ruleChain != null) { if (ruleChain != null) {
ruleChainName = ruleChain.getName(); if (ruleChain.getType().equals(RuleChainType.SYSTEM)) {
List<RuleNode> ruleNodeList = service.getRuleChainNodes(tenantId, entityId); ruleChainName = ruleChain.getName();
log.trace("[{}][{}] Updating rule chain with {} nodes", tenantId, entityId, ruleNodeList.size()); List<RuleNode> ruleNodeList = service.getRuleChainNodes(tenantId, entityId);
for (RuleNode ruleNode : ruleNodeList) { log.trace("[{}][{}] Updating rule chain with {} nodes", tenantId, entityId, ruleNodeList.size());
RuleNodeCtx existing = nodeActors.get(ruleNode.getId()); for (RuleNode ruleNode : ruleNodeList) {
if (existing == null) { RuleNodeCtx existing = nodeActors.get(ruleNode.getId());
log.trace("[{}][{}] Creating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode); if (existing == null) {
ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode); log.trace("[{}][{}] Creating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode);
nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode)); ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode);
} else { nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode));
log.trace("[{}][{}] Updating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode); } else {
existing.setSelf(ruleNode); log.trace("[{}][{}] Updating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode);
existing.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, existing.getSelf().getId(), ComponentLifecycleEvent.UPDATED), self); existing.setSelf(ruleNode);
existing.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, existing.getSelf().getId(), ComponentLifecycleEvent.UPDATED), self);
}
} }
}
Set<RuleNodeId> existingNodes = ruleNodeList.stream().map(RuleNode::getId).collect(Collectors.toSet()); Set<RuleNodeId> existingNodes = ruleNodeList.stream().map(RuleNode::getId).collect(Collectors.toSet());
List<RuleNodeId> removedRules = nodeActors.keySet().stream().filter(node -> !existingNodes.contains(node)).collect(Collectors.toList()); List<RuleNodeId> removedRules = nodeActors.keySet().stream().filter(node -> !existingNodes.contains(node)).collect(Collectors.toList());
removedRules.forEach(ruleNodeId -> { removedRules.forEach(ruleNodeId -> {
log.trace("[{}][{}] Removing rule node [{}]", tenantId, entityId, ruleNodeId); log.trace("[{}][{}] Removing rule node [{}]", tenantId, entityId, ruleNodeId);
RuleNodeCtx removed = nodeActors.remove(ruleNodeId); RuleNodeCtx removed = nodeActors.remove(ruleNodeId);
removed.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, removed.getSelf().getId(), ComponentLifecycleEvent.DELETED), self); removed.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, removed.getSelf().getId(), ComponentLifecycleEvent.DELETED), self);
}); });
initRoutes(ruleChain, ruleNodeList); initRoutes(ruleChain, ruleNodeList);
} else if (ruleChain.getType().equals(RuleChainType.EDGE)){
stop(context);
}
} }
} }

2
application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java

@ -139,7 +139,7 @@ public class TenantActor extends RuleChainManagerActor {
RuleChain ruleChain = null; RuleChain ruleChain = null;
if (msg.getEntityId().getEntityType() == EntityType.RULE_CHAIN) { if (msg.getEntityId().getEntityType() == EntityType.RULE_CHAIN) {
ruleChain = systemContext.getRuleChainService().findRuleChainById(tenantId, new RuleChainId(msg.getEntityId().getId())); ruleChain = systemContext.getRuleChainService().findRuleChainById(tenantId, new RuleChainId(msg.getEntityId().getId()));
if (RuleChainType.SYSTEM.equals(ruleChain.getType())) { if (ruleChain !=null && !RuleChainType.SYSTEM.equals(ruleChain.getType())) {
log.debug("[{}] Non SYSTEM rule chains are ignored and not started. Current rule chain type [{}]", tenantId, ruleChain.getType()); log.debug("[{}] Non SYSTEM rule chains are ignored and not started. Current rule chain type [{}]", tenantId, ruleChain.getType());
return; return;
} }

43
application/src/main/java/org/thingsboard/server/controller/EdgeController.java

@ -30,6 +30,7 @@ import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.device.DeviceSearchQuery; import org.thingsboard.server.common.data.device.DeviceSearchQuery;
import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.Edge;
@ -37,9 +38,12 @@ import org.thingsboard.server.common.data.edge.EdgeSearchQuery;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.SecurityUser;
@ -74,7 +78,8 @@ public class EdgeController extends BaseController {
@ResponseBody @ResponseBody
public Edge saveEdge(@RequestBody Edge edge) throws ThingsboardException { public Edge saveEdge(@RequestBody Edge edge) throws ThingsboardException {
try { try {
edge.setTenantId(getCurrentUser().getTenantId()); TenantId tenantId = getCurrentUser().getTenantId();
edge.setTenantId(tenantId);
boolean created = edge.getId() == null; boolean created = edge.getId() == null;
Operation operation = created ? Operation.CREATE : Operation.WRITE; Operation operation = created ? Operation.CREATE : Operation.WRITE;
@ -84,6 +89,12 @@ public class EdgeController extends BaseController {
Edge result = checkNotNull(edgeService.saveEdge(edge)); Edge result = checkNotNull(edgeService.saveEdge(edge));
if (created) {
RuleChain rootTenantRuleChain = ruleChainService.getRootTenantRuleChain(tenantId);
ruleChainService.assignRuleChainToEdge(tenantId, rootTenantRuleChain.getId(), result.getId());
edgeService.setRootRuleChain(tenantId, result, rootTenantRuleChain.getId());
}
logEntityAction(result.getId(), result, null, created ? ActionType.ADDED : ActionType.UPDATED, null); logEntityAction(result.getId(), result, null, created ? ActionType.ADDED : ActionType.UPDATED, null);
return result; return result;
} catch (Exception e) { } catch (Exception e) {
@ -250,6 +261,36 @@ public class EdgeController extends BaseController {
} }
} }
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/{edgeId}/{ruleChainId}/root", method = RequestMethod.POST)
@ResponseBody
public Edge setRootRuleChain(@PathVariable(EDGE_ID) String strEdgeId,
@PathVariable("ruleChainId") String strRuleChainId) throws ThingsboardException {
checkParameter(EDGE_ID, strEdgeId);
checkParameter("ruleChainId", strRuleChainId);
try {
RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
checkRuleChain(ruleChainId, Operation.WRITE);
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
Edge edge = checkEdgeId(edgeId, Operation.WRITE);
accessControlService.checkPermission(getCurrentUser(), Resource.EDGE, Operation.WRITE,
edge.getId(), edge);
Edge updatedEdge = edgeService.setRootRuleChain(getTenantId(), edge, ruleChainId);
logEntityAction(updatedEdge.getId(), updatedEdge, null, ActionType.UPDATED, null);
return updatedEdge;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.EDGE),
null,
null,
ActionType.UPDATED, e, strEdgeId);
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/customer/{customerId}/edges", params = {"limit"}, method = RequestMethod.GET) @RequestMapping(value = "/customer/{customerId}/edges", params = {"limit"}, method = RequestMethod.GET)
@ResponseBody @ResponseBody

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

@ -59,6 +59,7 @@ import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.dao.event.EventService; import org.thingsboard.server.dao.event.EventService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.service.script.JsInvokeService; import org.thingsboard.server.service.script.JsInvokeService;
import org.thingsboard.server.service.script.RuleNodeJsScriptEngine; import org.thingsboard.server.service.script.RuleNodeJsScriptEngine;
import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Operation;

4
common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java

@ -18,10 +18,12 @@ package org.thingsboard.server.dao.edge;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.Event; import org.thingsboard.server.common.data.Event;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeSearchQuery; import org.thingsboard.server.common.data.edge.EdgeSearchQuery;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TextPageLink;
@ -73,6 +75,8 @@ public interface EdgeService {
void pushEventToEdge(TenantId tenantId, TbMsg tbMsg); void pushEventToEdge(TenantId tenantId, TbMsg tbMsg);
TimePageData<Event> findQueueEvents(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink); TimePageData<Event> findQueueEvents(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink);
Edge setRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId);
} }

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

@ -124,7 +124,7 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa
} }
public boolean isAssignedToEdge(EdgeId edgeId) { public boolean isAssignedToEdge(EdgeId edgeId) {
return this.assignedEdges != null && this.assignedEdges.contains(new ShortEdgeInfo(edgeId, null)); return this.assignedEdges != null && this.assignedEdges.contains(new ShortEdgeInfo(edgeId, null, null));
} }
public ShortEdgeInfo getAssignedEdgeInfo(EdgeId edgeId) { public ShortEdgeInfo getAssignedEdgeInfo(EdgeId edgeId) {

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

@ -19,6 +19,7 @@ import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.RuleChainId;
@AllArgsConstructor @AllArgsConstructor
public class ShortEdgeInfo { public class ShortEdgeInfo {
@ -29,6 +30,9 @@ public class ShortEdgeInfo {
@Getter @Setter @Getter @Setter
private String title; private String title;
@Getter @Setter
private RuleChainId rootRuleChainId;
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) return true; if (this == o) return true;

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

@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.ShortCustomerInfo;
import org.thingsboard.server.common.data.ShortEdgeInfo; import org.thingsboard.server.common.data.ShortEdgeInfo;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@ -41,6 +42,7 @@ public class Edge extends SearchTextBasedWithAdditionalInfo<EdgeId> implements H
private TenantId tenantId; private TenantId tenantId;
private CustomerId customerId; private CustomerId customerId;
private RuleChainId rootRuleChainId;
private String name; private String name;
private String type; private String type;
private String label; private String label;
@ -60,6 +62,7 @@ public class Edge extends SearchTextBasedWithAdditionalInfo<EdgeId> implements H
super(edge); super(edge);
this.tenantId = edge.getTenantId(); this.tenantId = edge.getTenantId();
this.customerId = edge.getCustomerId(); this.customerId = edge.getCustomerId();
this.rootRuleChainId = edge.getRootRuleChainId();
this.type = edge.getType(); this.type = edge.getType();
this.name = edge.getName(); this.name = edge.getName();
this.routingKey = edge.getRoutingKey(); this.routingKey = edge.getRoutingKey();
@ -69,7 +72,7 @@ public class Edge extends SearchTextBasedWithAdditionalInfo<EdgeId> implements H
@JsonIgnore @JsonIgnore
public ShortEdgeInfo toShortEdgeInfo() { public ShortEdgeInfo toShortEdgeInfo() {
return new ShortEdgeInfo(id, name); return new ShortEdgeInfo(id, name, rootRuleChainId);
} }
@Override @Override

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

@ -94,7 +94,7 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo<RuleChainId> im
} }
public boolean isAssignedToEdge(EdgeId edgeId) { public boolean isAssignedToEdge(EdgeId edgeId) {
return this.assignedEdges != null && this.assignedEdges.contains(new ShortEdgeInfo(edgeId, null)); return this.assignedEdges != null && this.assignedEdges.contains(new ShortEdgeInfo(edgeId, null, null));
} }
public ShortEdgeInfo getAssignedEdgeInfo(EdgeId edgeId) { public ShortEdgeInfo getAssignedEdgeInfo(EdgeId edgeId) {

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

@ -44,6 +44,7 @@ import org.thingsboard.server.common.data.edge.EdgeSearchQuery;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TextPageLink;
@ -472,6 +473,14 @@ public class BaseEdgeService extends AbstractEntityService implements EdgeServic
return eventService.findEvents(tenantId, edgeId, DataConstants.EDGE_QUEUE_EVENT_TYPE, pageLink); return eventService.findEvents(tenantId, edgeId, DataConstants.EDGE_QUEUE_EVENT_TYPE, pageLink);
} }
@Override
public Edge setRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) {
edge.setRootRuleChainId(ruleChainId);
Edge saveEdge = saveEdge(edge);
ruleChainService.updateEdgeRuleChains(tenantId, saveEdge.getId());
return saveEdge;
}
private DataValidator<Edge> edgeValidator = private DataValidator<Edge> edgeValidator =
new DataValidator<Edge>() { new DataValidator<Edge>() {

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

@ -359,6 +359,7 @@ public class ModelConstants {
public static final String EDGE_COLUMN_FAMILY_NAME = "edge"; public static final String EDGE_COLUMN_FAMILY_NAME = "edge";
public static final String EDGE_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; 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_CUSTOMER_ID_PROPERTY = CUSTOMER_ID_PROPERTY;
public static final String EDGE_ROOT_RULE_CHAIN_ID_PROPERTY = "root_rule_chain_id";
public static final String EDGE_NAME_PROPERTY = "name"; public static final String EDGE_NAME_PROPERTY = "name";
public static final String EDGE_LABEL_PROPERTY = "label"; public static final String EDGE_LABEL_PROPERTY = "label";
public static final String EDGE_TYPE_PROPERTY = "type"; public static final String EDGE_TYPE_PROPERTY = "type";

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

@ -25,6 +25,7 @@ import lombok.Data;
import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.model.SearchTextEntity; import org.thingsboard.server.dao.model.SearchTextEntity;
import org.thingsboard.server.dao.model.type.JsonCodec; import org.thingsboard.server.dao.model.type.JsonCodec;
@ -37,6 +38,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.EDGE_CONFIGURATION
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_CUSTOMER_ID_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_LABEL_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_NAME_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.EDGE_NAME_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_ROOT_RULE_CHAIN_ID_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_ROUTING_KEY_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.EDGE_ROUTING_KEY_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_SECRET_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.EDGE_SECRET_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_TENANT_ID_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.EDGE_TENANT_ID_PROPERTY;
@ -60,6 +62,9 @@ public class EdgeEntity implements SearchTextEntity<Edge> {
@Column(name = EDGE_CUSTOMER_ID_PROPERTY) @Column(name = EDGE_CUSTOMER_ID_PROPERTY)
private UUID customerId; private UUID customerId;
@Column(name = EDGE_ROOT_RULE_CHAIN_ID_PROPERTY)
private UUID rootRuleChainId;
@Column(name = EDGE_TYPE_PROPERTY) @Column(name = EDGE_TYPE_PROPERTY)
private String type; private String type;
@ -95,6 +100,12 @@ public class EdgeEntity implements SearchTextEntity<Edge> {
if (edge.getTenantId() != null) { if (edge.getTenantId() != null) {
this.tenantId = edge.getTenantId().getId(); this.tenantId = edge.getTenantId().getId();
} }
if (edge.getCustomerId() != null) {
this.customerId = edge.getCustomerId().getId();
}
if (edge.getRootRuleChainId() != null) {
this.rootRuleChainId = edge.getRootRuleChainId().getId();
}
this.type = edge.getType(); this.type = edge.getType();
this.name = edge.getName(); this.name = edge.getName();
this.label = edge.getLabel(); this.label = edge.getLabel();
@ -119,6 +130,9 @@ public class EdgeEntity implements SearchTextEntity<Edge> {
if (customerId != null) { if (customerId != null) {
edge.setCustomerId(new CustomerId(customerId)); edge.setCustomerId(new CustomerId(customerId));
} }
if (rootRuleChainId != null) {
edge.setRootRuleChainId(new RuleChainId(rootRuleChainId));
}
edge.setType(type); edge.setType(type);
edge.setName(name); edge.setName(name);
edge.setLabel(label); edge.setLabel(label);

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

@ -5,7 +5,7 @@
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * 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 * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
@ -25,6 +25,7 @@ import org.thingsboard.server.common.data.UUIDConverter;
import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.ModelConstants;
@ -39,6 +40,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.EDGE_COLUMN_FAMILY
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_CUSTOMER_ID_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_LABEL_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_NAME_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.EDGE_NAME_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_ROOT_RULE_CHAIN_ID_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_ROUTING_KEY_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.EDGE_ROUTING_KEY_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_SECRET_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.EDGE_SECRET_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_TENANT_ID_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.EDGE_TENANT_ID_PROPERTY;
@ -58,6 +60,9 @@ public class EdgeEntity extends BaseSqlEntity<Edge> implements SearchTextEntity<
@Column(name = EDGE_CUSTOMER_ID_PROPERTY) @Column(name = EDGE_CUSTOMER_ID_PROPERTY)
private String customerId; private String customerId;
@Column(name = EDGE_ROOT_RULE_CHAIN_ID_PROPERTY)
private String rootRuleChainId;
@Column(name = EDGE_TYPE_PROPERTY) @Column(name = EDGE_TYPE_PROPERTY)
private String type; private String type;
@ -98,6 +103,9 @@ public class EdgeEntity extends BaseSqlEntity<Edge> implements SearchTextEntity<
if (edge.getCustomerId() != null) { if (edge.getCustomerId() != null) {
this.customerId = UUIDConverter.fromTimeUUID(edge.getCustomerId().getId()); this.customerId = UUIDConverter.fromTimeUUID(edge.getCustomerId().getId());
} }
if (edge.getRootRuleChainId() != null) {
this.rootRuleChainId = UUIDConverter.fromTimeUUID(edge.getRootRuleChainId().getId());
}
this.type = edge.getType(); this.type = edge.getType();
this.name = edge.getName(); this.name = edge.getName();
this.label = edge.getLabel(); this.label = edge.getLabel();
@ -131,6 +139,9 @@ public class EdgeEntity extends BaseSqlEntity<Edge> implements SearchTextEntity<
if (customerId != null) { if (customerId != null) {
edge.setCustomerId(new CustomerId(UUIDConverter.fromString(customerId))); edge.setCustomerId(new CustomerId(UUIDConverter.fromString(customerId)));
} }
if (rootRuleChainId != null) {
edge.setRootRuleChainId(new RuleChainId(UUIDConverter.fromString(rootRuleChainId)));
}
edge.setType(type); edge.setType(type);
edge.setName(name); edge.setName(name);
edge.setLabel(label); edge.setLabel(label);

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

@ -25,6 +25,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.BaseData;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.ShortEdgeInfo;
import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EdgeId;
@ -42,6 +43,7 @@ import org.thingsboard.server.common.data.rule.NodeConnectionInfo;
import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo; import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo;
import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.server.dao.edge.EdgeDao; import org.thingsboard.server.dao.edge.EdgeDao;
import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.edge.EdgeService;
@ -116,6 +118,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
createRelation(tenantId, new EntityRelation(ruleChain.getTenantId(), ruleChain.getId(), createRelation(tenantId, new EntityRelation(ruleChain.getTenantId(), ruleChain.getId(),
EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN)); EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN));
ruleChain.setRoot(true); ruleChain.setRoot(true);
ruleChain.setType(RuleChainType.SYSTEM);
ruleChainDao.save(tenantId, ruleChain); ruleChainDao.save(tenantId, ruleChain);
return true; return true;
} catch (ExecutionException | InterruptedException e) { } catch (ExecutionException | InterruptedException e) {
@ -359,8 +362,17 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
public void deleteRuleChainById(TenantId tenantId, RuleChainId ruleChainId) { public void deleteRuleChainById(TenantId tenantId, RuleChainId ruleChainId) {
Validator.validateId(ruleChainId, "Incorrect rule chain id for delete request."); Validator.validateId(ruleChainId, "Incorrect rule chain id for delete request.");
RuleChain ruleChain = ruleChainDao.findById(tenantId, ruleChainId.getId()); RuleChain ruleChain = ruleChainDao.findById(tenantId, ruleChainId.getId());
if (ruleChain != null && ruleChain.isRoot()) { if (ruleChain != null) {
throw new DataValidationException("Deletion of Root Tenant Rule Chain is prohibited!"); if (ruleChain.isRoot()) {
throw new DataValidationException("Deletion of Root Tenant Rule Chain is prohibited!");
}
if (ruleChain.getAssignedEdges() != null && !ruleChain.getAssignedEdges().isEmpty()) {
for (ShortEdgeInfo assignedEdge : ruleChain.getAssignedEdges()) {
if (assignedEdge.getRootRuleChainId() != null && assignedEdge.getRootRuleChainId().equals(ruleChainId)) {
throw new DataValidationException("Can't delete rule chain that is root for edge [" + assignedEdge.getTitle() + "]. Please assign another root rule chain first to the edge!");
}
}
}
} }
checkRuleNodesAndDelete(tenantId, ruleChainId); checkRuleNodesAndDelete(tenantId, ruleChainId);
} }
@ -398,13 +410,16 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
RuleChain ruleChain = findRuleChainById(tenantId, ruleChainId); RuleChain ruleChain = findRuleChainById(tenantId, ruleChainId);
Edge edge = edgeDao.findById(tenantId, edgeId.getId()); Edge edge = edgeDao.findById(tenantId, edgeId.getId());
if (edge == null) { if (edge == null) {
throw new DataValidationException("Can't unassign ruleChain from non-existent edge!"); throw new DataValidationException("Can't unassign rule chain from non-existent edge!");
}
if (edge.getRootRuleChainId() != null && edge.getRootRuleChainId().equals(ruleChainId)) {
throw new DataValidationException("Can't unassign root rule chain from edge [" + edge.getName() + "]. Please assign another root rule chain first!");
} }
if (ruleChain.removeAssignedEdge(edge)) { if (ruleChain.removeAssignedEdge(edge)) {
try { try {
deleteRelation(tenantId, new EntityRelation(edgeId, ruleChainId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE)); deleteRelation(tenantId, new EntityRelation(edgeId, ruleChainId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE));
} catch (ExecutionException | InterruptedException e) { } catch (ExecutionException | InterruptedException e) {
log.warn("[{}] Failed to delete ruleChain relation. Edge Id: [{}]", ruleChainId, edgeId); log.warn("[{}] Failed to delete rule chain relation. Edge Id: [{}]", ruleChainId, edgeId);
throw new RuntimeException(e); throw new RuntimeException(e);
} }
return saveRuleChain(ruleChain); return saveRuleChain(ruleChain);
@ -442,13 +457,13 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
Validator.validateId(tenantId, "Incorrect tenantId " + tenantId); Validator.validateId(tenantId, "Incorrect tenantId " + tenantId);
Validator.validateId(edgeId, "Incorrect customerId " + edgeId); Validator.validateId(edgeId, "Incorrect customerId " + edgeId);
Validator.validatePageLink(pageLink, "Incorrect page link " + pageLink); Validator.validatePageLink(pageLink, "Incorrect page link " + pageLink);
ListenableFuture<List<RuleChain>> dashboards = ruleChainDao.findRuleChainsByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink); ListenableFuture<List<RuleChain>> ruleChains = ruleChainDao.findRuleChainsByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink);
return Futures.transform(dashboards, new Function<List<RuleChain>, TimePageData<RuleChain>>() { return Futures.transform(ruleChains, new Function<List<RuleChain>, TimePageData<RuleChain>>() {
@Nullable @Nullable
@Override @Override
public TimePageData<RuleChain> apply(@Nullable List<RuleChain> RuleChain) { public TimePageData<RuleChain> apply(@Nullable List<RuleChain> ruleChain) {
return new TimePageData<>(RuleChain, pageLink); return new TimePageData<>(ruleChain, pageLink);
} }
}); });
} }

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

@ -47,5 +47,4 @@ public interface RuleChainDao extends Dao<RuleChain> {
* @return the list of rule chain objects * @return the list of rule chain objects
*/ */
ListenableFuture<List<RuleChain>> findRuleChainsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink); ListenableFuture<List<RuleChain>> findRuleChainsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink);
} }

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

@ -88,5 +88,4 @@ public class JpaRuleChainDao extends JpaAbstractSearchTextDao<RuleChainEntity, R
return Futures.successfulAsList(ruleChainFutures); return Futures.successfulAsList(ruleChainFutures);
}); });
} }
} }

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

@ -254,6 +254,7 @@ CREATE TABLE IF NOT EXISTS edge (
id varchar(31) NOT NULL CONSTRAINT edge_pkey PRIMARY KEY, id varchar(31) NOT NULL CONSTRAINT edge_pkey PRIMARY KEY,
additional_info varchar, additional_info varchar,
customer_id varchar(31), customer_id varchar(31),
root_rule_chain_id varchar(31),
configuration varchar(10000000), configuration varchar(10000000),
type varchar(255), type varchar(255),
name varchar(255), name varchar(255),

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

@ -31,7 +31,8 @@ function EdgeService($http, $q, customerService) {
getCustomerEdges: getCustomerEdges, getCustomerEdges: getCustomerEdges,
assignEdgeToCustomer: assignEdgeToCustomer, assignEdgeToCustomer: assignEdgeToCustomer,
unassignEdgeFromCustomer: unassignEdgeFromCustomer, unassignEdgeFromCustomer: unassignEdgeFromCustomer,
makeEdgePublic: makeEdgePublic makeEdgePublic: makeEdgePublic,
setRootRuleChain: setRootRuleChain
}; };
return service; return service;
@ -229,4 +230,15 @@ function EdgeService($http, $q, customerService) {
}); });
return deferred.promise; return deferred.promise;
} }
function setRootRuleChain(edgeId, ruleChainId) {
var deferred = $q.defer();
var url = '/api/edge/' + edgeId + '/' + ruleChainId + '/root';
$http.post(url).then(function success(response) {
deferred.resolve(response.data);
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
} }

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

@ -19,6 +19,7 @@ import addEdgeTemplate from './add-edge.tpl.html';
import edgeCard from './edge-card.tpl.html'; import edgeCard from './edge-card.tpl.html';
import assignToCustomerTemplate from './assign-to-customer.tpl.html'; import assignToCustomerTemplate from './assign-to-customer.tpl.html';
import addEdgesToCustomerTemplate from './add-edges-to-customer.tpl.html'; import addEdgesToCustomerTemplate from './add-edges-to-customer.tpl.html';
import setRootRuleChainToEdgesTemplate from './set-root-rule-chain-to-edges.tpl.html';
/* eslint-enable import/no-unresolved, import/default */ /* eslint-enable import/no-unresolved, import/default */
@ -47,8 +48,8 @@ export function EdgeCardController(types) {
/*@ngInject*/ /*@ngInject*/
export function EdgeController($rootScope, userService, edgeService, customerService, $state, $stateParams, export function EdgeController($rootScope, userService, edgeService, customerService, ruleChainService,
$document, $mdDialog, $q, $translate, types, importExport) { $state, $stateParams, $document, $mdDialog, $q, $translate, types, importExport) {
var customerId = $stateParams.customerId; var customerId = $stateParams.customerId;
@ -292,6 +293,19 @@ export function EdgeController($rootScope, userService, edgeService, customerSer
} }
); );
edgeGroupActionsList.push(
{
onAction: function ($event, items) {
setRootRuleChainToEdges($event, items);
},
name: function() { return $translate.instant('edge.set-root-rule-chain-to-edges') },
details: function(selectedCount) {
return $translate.instant('edge.set-root-rule-chain-to-edges-text', {count: selectedCount}, "messageformat");
},
icon: "flag"
}
);
edgeGroupActionsList.push( edgeGroupActionsList.push(
{ {
onAction: function ($event, items) { onAction: function ($event, items) {
@ -305,8 +319,6 @@ export function EdgeController($rootScope, userService, edgeService, customerSer
} }
); );
edgeGroupActionsList.push( edgeGroupActionsList.push(
{ {
onAction: function ($event) { onAction: function ($event) {
@ -318,9 +330,7 @@ export function EdgeController($rootScope, userService, edgeService, customerSer
} }
); );
} else if (vm.edgesScope === 'customer' || vm.edgesScope === 'customer_user') {
} else if (vm.edgesScope === 'customer' || vm.edgesScope === 'customer_user') {
fetchEdgesFunction = function (pageLink, edgeType) { fetchEdgesFunction = function (pageLink, edgeType) {
return edgeService.getCustomerEdges(customerId, pageLink, true, null, edgeType); return edgeService.getCustomerEdges(customerId, pageLink, true, null, edgeType);
}; };
@ -524,6 +534,50 @@ export function EdgeController($rootScope, userService, edgeService, customerSer
}); });
} }
function setRootRuleChainToEdges($event, items) {
var edgeIds = [];
for (var id in items.selections) {
edgeIds.push(id);
}
setRootRuleChain($event, edgeIds);
}
function setRootRuleChain($event, edgeIds) {
if ($event) {
$event.stopPropagation();
}
var pageSize = 10;
ruleChainService.getRuleChains({limit: pageSize, textSearch: ''}).then(
function success(_ruleChains) {
var ruleChains = {
pageSize: pageSize,
data: _ruleChains.data,
nextPageLink: _ruleChains.nextPageLink,
selection: null,
hasNext: _ruleChains.hasNext,
pending: false
};
if (ruleChains.hasNext) {
ruleChains.nextPageLink.limit = pageSize;
}
$mdDialog.show({
controller: 'SetRootRuleChainToEdgesController',
controllerAs: 'vm',
templateUrl: setRootRuleChainToEdgesTemplate,
locals: {edgeIds: edgeIds, ruleChains: ruleChains},
parent: angular.element($document[0].body),
fullscreen: true,
targetEvent: $event
}).then(function () {
vm.grid.refreshList();
}, function () {
});
},
function fail() {
});
}
function assignEdgesToCustomer($event, items) { function assignEdgesToCustomer($event, items) {
var edgeIds = []; var edgeIds = [];
for (var id in items.selections) { for (var id in items.selections) {

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

@ -23,6 +23,7 @@ import EdgeRoutes from './edge.routes';
import {EdgeController, EdgeCardController} from './edge.controller'; import {EdgeController, EdgeCardController} from './edge.controller';
import AssignEdgeToCustomerController from './assign-to-customer.controller'; import AssignEdgeToCustomerController from './assign-to-customer.controller';
import AddEdgesToCustomerController from './add-edges-to-customer.controller'; import AddEdgesToCustomerController from './add-edges-to-customer.controller';
import SetRootRuleChainToEdgesController from './set-root-rule-chain-to-edges.controller';
import EdgeDirective from './edge.directive'; import EdgeDirective from './edge.directive';
export default angular.module('thingsboard.edge', [ export default angular.module('thingsboard.edge', [
@ -37,5 +38,6 @@ export default angular.module('thingsboard.edge', [
.controller('EdgeCardController', EdgeCardController) .controller('EdgeCardController', EdgeCardController)
.controller('AssignEdgeToCustomerController', AssignEdgeToCustomerController) .controller('AssignEdgeToCustomerController', AssignEdgeToCustomerController)
.controller('AddEdgesToCustomerController', AddEdgesToCustomerController) .controller('AddEdgesToCustomerController', AddEdgesToCustomerController)
.controller('SetRootRuleChainToEdgesController', SetRootRuleChainToEdgesController)
.directive('tbEdge', EdgeDirective) .directive('tbEdge', EdgeDirective)
.name; .name;

129
ui/src/app/edge/set-root-rule-chain-to-edges.controller.js

@ -0,0 +1,129 @@
/*
* 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 SetRootRuleChainToEdgesController(ruleChainService, edgeService, $mdDialog, $q, edgeIds, ruleChains) {
var vm = this;
vm.ruleChains = ruleChains;
vm.searchText = '';
vm.assign = assign;
vm.cancel = cancel;
vm.isRuleChainSelected = isRuleChainSelected;
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(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 assignTasks = [];
for (var i=0;i<edgeIds.length;i++) {
assignTasks.push(ruleChainService.assignRuleChainToEdge(edgeIds[i], vm.ruleChains.selection.id.id));
}
$q.all(assignTasks).then(function () {
var setRootTasks = [];
for (var j=0;j<edgeIds.length;j++) {
setRootTasks.push(edgeService.setRootRuleChain(edgeIds[j], vm.ruleChains.selection.id.id));
}
$q.all(setRootTasks).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();
if (vm.isRuleChainSelected(ruleChain)) {
vm.ruleChains.selection = null;
} else {
vm.ruleChains.selection = ruleChain;
}
}
function isRuleChainSelected(ruleChain) {
return vm.ruleChains.selection != null && ruleChain &&
ruleChain.id.id === vm.ruleChains.selection.id.id;
}
function searchRuleChainTextUpdated() {
vm.ruleChains = {
pageSize: vm.ruleChains.pageSize,
data: [],
nextPageLink: {
limit: vm.ruleChains.pageSize,
textSearch: vm.searchText
},
selection: null,
hasNext: true,
pending: false
};
}
}

76
ui/src/app/edge/set-root-rule-chain-to-edges.tpl.html

@ -0,0 +1,76 @@
<!--
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="{{ 'edge.set-root-rule-chain-to-edges' | translate }}">
<form name="theForm" ng-submit="vm.assign()">
<md-toolbar>
<div class="md-toolbar-tools">
<h2 translate>edge.set-root-rule-chain-to-edges</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>edge.set-root-rule-chain-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="rule-chain-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="vm.isRuleChainSelected(ruleChain)"></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.selection==null" 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>

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

@ -792,6 +792,7 @@
"dashboards": "Edge Dashboards", "dashboards": "Edge Dashboards",
"manage-edge-rulechains": "Manage edge rule chains", "manage-edge-rulechains": "Manage edge rule chains",
"rulechains": "Edge Rule Chains", "rulechains": "Edge Rule Chains",
"rulechain": "Edge Rule Chain",
"edge-key": "Edge key", "edge-key": "Edge key",
"copy-edge-key": "Copy edge key", "copy-edge-key": "Copy edge key",
"edge-key-copied-message": "Edge key has been copied to clipboard", "edge-key-copied-message": "Edge key has been copied to clipboard",
@ -803,7 +804,10 @@
"manage-edge-entity-views": "Manage edge entity views", "manage-edge-entity-views": "Manage edge entity views",
"assets": "Edge assets", "assets": "Edge assets",
"devices": "Edge devices", "devices": "Edge devices",
"entity-views": "Edge entity views" "entity-views": "Edge entity views",
"set-root-rule-chain-text": "Please select root rule chain for edge(s)",
"set-root-rule-chain-to-edges": "Set root rule chain for Edge(s)",
"set-root-rule-chain-to-edges-text": "Set root rule chain for { count, plural, 1 {1 edge} other {# edges} }"
}, },
"error": { "error": {
"unable-to-connect": "Unable to connect to the server! Please check your internet connection.", "unable-to-connect": "Unable to connect to the server! Please check your internet connection.",

2
ui/src/app/rulechain/add-rulechain.tpl.html

@ -31,7 +31,7 @@
<span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span> <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content> <md-dialog-content>
<div class="md-dialog-content"> <div class="md-dialog-content">
<tb-rule-chain rule-chain="vm.item" is-edit="true" the-form="theForm"></tb-rule-chain> <tb-rule-chain rule-chain="vm.item" is-edit="true" rule-chain-scope="'tenant'" the-form="theForm"></tb-rule-chain>
</div> </div>
</md-dialog-content> </md-dialog-content>
<md-dialog-actions layout="row"> <md-dialog-actions layout="row">

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

@ -54,7 +54,7 @@ export default function AddRuleChainsToEdgeController(ruleChainService, $mdDialo
vm.ruleChains.pending = true; vm.ruleChains.pending = true;
ruleChainService.getRuleChains(vm.ruleChains.nextPageLink).then( ruleChainService.getRuleChains(vm.ruleChains.nextPageLink).then(
function success(ruleChains) { function success(ruleChains) {
vm.ruleChains.data = vm.ruleChains.data.concat(filterNonRootRuleChains(ruleChains.data)); vm.ruleChains.data = ruleChains.data;
vm.ruleChains.nextPageLink = ruleChains.nextPageLink; vm.ruleChains.nextPageLink = ruleChains.nextPageLink;
vm.ruleChains.hasNext = ruleChains.hasNext; vm.ruleChains.hasNext = ruleChains.hasNext;
if (vm.ruleChains.hasNext) { if (vm.ruleChains.hasNext) {
@ -119,12 +119,4 @@ export default function AddRuleChainsToEdgeController(ruleChainService, $mdDialo
pending: false pending: false
}; };
} }
function filterNonRootRuleChains(ruleChains) {
return $filter('filter')(ruleChains, isNonRootRuleChain);
}
function isNonRootRuleChain(ruleChain) {
return ruleChain && !ruleChain.root;
}
} }

2
ui/src/app/rulechain/rulechain-card.tpl.html

@ -16,4 +16,4 @@
--> -->
<div class="tb-small tb-rule-chain-assigned-edges" ng-show="vm.parentCtl.ruleChainsScope === 'tenant' && vm.item.assignedEdgesText">{{'rulechain.assigned-to-edges' | translate}}: '{{vm.item.assignedEdgesText}}'</div> <div class="tb-small tb-rule-chain-assigned-edges" ng-show="vm.parentCtl.ruleChainsScope === 'tenant' && vm.item.assignedEdgesText">{{'rulechain.assigned-to-edges' | translate}}: '{{vm.item.assignedEdgesText}}'</div>
<div ng-if="item && item.root" translate>rulechain.root</div> <div ng-if="(vm.parentCtl.ruleChainsScope === 'tenant' && item && item.root) || (vm.parentCtl.ruleChainsScope === 'edge' && vm.parentCtl.isRootRuleChain(item))" translate>rulechain.root</div>

2
ui/src/app/rulechain/rulechain-fieldset.tpl.html

@ -51,7 +51,7 @@
</md-input-container> </md-input-container>
<md-input-container class="md-block"> <md-input-container class="md-block">
<label translate>rulechain.type</label> <label translate>rulechain.type</label>
<md-select ng-disabled="$root.loading || !isEdit" name="type" ng-model="ruleChain.type"> <md-select ng-disabled="$root.loading || !isEdit || ruleChainScope !== 'tenant' || ruleChain.root === true" name="type" ng-model="ruleChain.type">
<md-option ng-repeat="ruleChainType in ruleChainTypes" value="{{ruleChainType}}"> <md-option ng-repeat="ruleChainType in ruleChainTypes" value="{{ruleChainType}}">
{{ruleChainType}} {{ruleChainType}}
</md-option> </md-option>

2
ui/src/app/rulechain/rulechain.directive.js

@ -49,7 +49,7 @@ export default function RuleChainDirective($compile, $templateCache, $mdDialog,
theForm: '=', theForm: '=',
onSetRootRuleChain: '&', onSetRootRuleChain: '&',
onExportRuleChain: '&', onExportRuleChain: '&',
onDeleteRuleChain: '&', onDeleteRuleChain: '&'
} }
}; };
} }

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

@ -145,5 +145,43 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider
ncyBreadcrumb: { ncyBreadcrumb: {
label: '{"icon": "settings_ethernet", "label": "{{ vm.edgeRuleChainsTitle }}", "translate": "false"}' label: '{"icon": "settings_ethernet", "label": "{{ vm.edgeRuleChainsTitle }}", "translate": "false"}'
} }
})
.state('home.edges.ruleChains.ruleChain', {
url: '/:ruleChainId',
reloadOnSearch: false,
module: 'private',
auth: ['SYS_ADMIN', 'TENANT_ADMIN'],
views: {
"content@home": {
templateUrl: ruleChainTemplate,
controller: 'RuleChainController',
controllerAs: 'vm'
}
},
resolve: {
ruleChain:
/*@ngInject*/
function($stateParams, ruleChainService) {
return ruleChainService.getRuleChain($stateParams.ruleChainId);
},
ruleChainMetaData:
/*@ngInject*/
function($stateParams, ruleChainService) {
return ruleChainService.getRuleChainMetaData($stateParams.ruleChainId);
},
ruleNodeComponents:
/*@ngInject*/
function($stateParams, ruleChainService) {
return ruleChainService.getRuleNodeComponents();
}
},
data: {
import: false,
searchEnabled: false,
pageTitle: 'edge.rulechain'
},
ncyBreadcrumb: {
label: '{"icon": "settings_ethernet", "label": "edge.rulechain"}'
}
}); });
} }

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

@ -25,7 +25,7 @@ import addRuleChainsToEdgeTemplate from "./add-rulechains-to-edge.tpl.html";
import './rulechain-card.scss'; import './rulechain-card.scss';
/*@ngInject*/ /*@ngInject*/
export default function RuleChainsController(ruleChainService, userService, importExport, $state, export default function RuleChainsController(ruleChainService, userService, edgeService, importExport, $state,
$stateParams, $filter, $translate, $mdDialog, $document, $q, types) { $stateParams, $filter, $translate, $mdDialog, $document, $q, types) {
var vm = this; var vm = this;
@ -107,6 +107,11 @@ export default function RuleChainsController(ruleChainService, userService, impo
if (edgeId) { if (edgeId) {
vm.edgeRuleChainsTitle = $translate.instant('edge.rulechains'); vm.edgeRuleChainsTitle = $translate.instant('edge.rulechains');
edgeService.getEdge(edgeId).then(
function success(edge) {
vm.edge = edge;
}
);
} }
if (vm.ruleChainsScope === 'tenant') { if (vm.ruleChainsScope === 'tenant') {
@ -133,8 +138,7 @@ export default function RuleChainsController(ruleChainService, userService, impo
}, },
name: function() { return $translate.instant('action.assign') }, name: function() { return $translate.instant('action.assign') },
details: function() { return $translate.instant('rulechain.manage-assigned-edges') }, details: function() { return $translate.instant('rulechain.manage-assigned-edges') },
icon: "wifi_tethering", icon: "wifi_tethering"
isEnabled: isNonRootRuleChain
}); });
ruleChainActionsList.push({ ruleChainActionsList.push({
@ -214,6 +218,16 @@ export default function RuleChainsController(ruleChainService, userService, impo
return ruleChainService.unassignRuleChainFromEdge(edgeId, ruleChainId); return ruleChainService.unassignRuleChainFromEdge(edgeId, 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( ruleChainActionsList.push(
{ {
onAction: function ($event, item) { onAction: function ($event, item) {
@ -221,7 +235,8 @@ export default function RuleChainsController(ruleChainService, userService, impo
}, },
name: function() { return $translate.instant('action.unassign') }, name: function() { return $translate.instant('action.unassign') },
details: function() { return $translate.instant('rulechain.unassign-from-edge') }, details: function() { return $translate.instant('rulechain.unassign-from-edge') },
icon: "assignment_return" icon: "assignment_return",
isEnabled: isNonRootRuleChain
} }
); );
@ -288,7 +303,13 @@ export default function RuleChainsController(ruleChainService, userService, impo
if ($event) { if ($event) {
$event.stopPropagation(); $event.stopPropagation();
} }
$state.go('home.ruleChains.ruleChain', {ruleChainId: ruleChain.id.id}); if (vm.ruleChainsScope === 'edge') {
$state.go('home.edges.ruleChains.ruleChain', {
ruleChainId: ruleChain.id.id
});
} else {
$state.go('home.ruleChains.ruleChain', {ruleChainId: ruleChain.id.id});
}
} }
function deleteRuleChain(ruleChainId) { function deleteRuleChain(ruleChainId) {
@ -300,11 +321,19 @@ export default function RuleChainsController(ruleChainService, userService, impo
} }
function isRootRuleChain(ruleChain) { function isRootRuleChain(ruleChain) {
return ruleChain && ruleChain.root; if (angular.isDefined(vm.edge) && vm.edge != null) {
return angular.isDefined(vm.edge.rootRuleChainId) && vm.edge.rootRuleChainId != null && vm.edge.rootRuleChainId.id === ruleChain.id.id;
} else {
return ruleChain && ruleChain.root;
}
} }
function isNonRootRuleChain(ruleChain) { function isNonRootRuleChain(ruleChain) {
return ruleChain && !ruleChain.root; if (angular.isDefined(vm.edge) && vm.edge != null) {
return angular.isDefined(vm.edge.rootRuleChainId) && vm.edge.rootRuleChainId != null && vm.edge.rootRuleChainId.id !== ruleChain.id.id;
} else {
return ruleChain && !ruleChain.root;
}
} }
function exportRuleChain($event, ruleChain) { function exportRuleChain($event, ruleChain) {
@ -322,11 +351,20 @@ export default function RuleChainsController(ruleChainService, userService, impo
.cancel($translate.instant('action.no')) .cancel($translate.instant('action.no'))
.ok($translate.instant('action.yes')); .ok($translate.instant('action.yes'));
$mdDialog.show(confirm).then(function () { $mdDialog.show(confirm).then(function () {
ruleChainService.setRootRuleChain(ruleChain.id.id).then( if (angular.isDefined(vm.edge) && vm.edge != null) {
() => { edgeService.setRootRuleChain(vm.edge.id.id, ruleChain.id.id).then(
vm.grid.refreshList(); (edge) => {
} vm.edge = edge;
); vm.grid.refreshList();
}
);
} else {
ruleChainService.setRootRuleChain(ruleChain.id.id).then(
() => {
vm.grid.refreshList();
}
);
}
}); });
} }
@ -396,7 +434,7 @@ export default function RuleChainsController(ruleChainService, userService, impo
function success(_ruleChains) { function success(_ruleChains) {
var ruleChains = { var ruleChains = {
pageSize: pageSize, pageSize: pageSize,
data: filterNonRootRuleChains(_ruleChains.data), data: _ruleChains.data,
nextPageLink: _ruleChains.nextPageLink, nextPageLink: _ruleChains.nextPageLink,
selections: {}, selections: {},
selectedCount: 0, selectedCount: 0,
@ -423,10 +461,6 @@ export default function RuleChainsController(ruleChainService, userService, impo
}); });
} }
function filterNonRootRuleChains(ruleChains) {
return $filter('filter')(ruleChains, isNonRootRuleChain);
}
function unassignFromEdge($event, ruleChain, edgeId) { function unassignFromEdge($event, ruleChain, edgeId) {
if ($event) { if ($event) {
$event.stopPropagation(); $event.stopPropagation();

Loading…
Cancel
Save