Commit efd565d8b607ae384ef925a634d0002d4d79faec

Authored by Volodymyr Babak
1 parent 66af90bf

Added root rule chain for the edge. Part 2

Showing 31 changed files with 542 additions and 87 deletions
@@ -18,8 +18,13 @@ CREATE TABLE IF NOT EXISTS edge ( @@ -18,8 +18,13 @@ CREATE TABLE IF NOT EXISTS edge (
18 id varchar(31) NOT NULL CONSTRAINT edge_pkey PRIMARY KEY, 18 id varchar(31) NOT NULL CONSTRAINT edge_pkey PRIMARY KEY,
19 additional_info varchar, 19 additional_info varchar,
20 customer_id varchar(31), 20 customer_id varchar(31),
  21 + root_rule_chain_id varchar(31),
21 configuration varchar(10000000), 22 configuration varchar(10000000),
  23 + type varchar(255),
22 name varchar(255), 24 name varchar(255),
  25 + label varchar(255),
  26 + routing_key varchar(255),
  27 + secret varchar(255),
23 search_text varchar(255), 28 search_text varchar(255),
24 tenant_id varchar(31) 29 tenant_id varchar(31)
25 ); 30 );
@@ -39,6 +39,7 @@ import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; @@ -39,6 +39,7 @@ import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
39 import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; 39 import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
40 import org.thingsboard.server.common.data.relation.EntityRelation; 40 import org.thingsboard.server.common.data.relation.EntityRelation;
41 import org.thingsboard.server.common.data.rule.RuleChain; 41 import org.thingsboard.server.common.data.rule.RuleChain;
  42 +import org.thingsboard.server.common.data.rule.RuleChainType;
42 import org.thingsboard.server.common.data.rule.RuleNode; 43 import org.thingsboard.server.common.data.rule.RuleNode;
43 import org.thingsboard.server.common.msg.TbMsg; 44 import org.thingsboard.server.common.msg.TbMsg;
44 import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; 45 import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
@@ -97,17 +98,19 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh @@ -97,17 +98,19 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
97 if (!started) { 98 if (!started) {
98 RuleChain ruleChain = service.findRuleChainById(tenantId, entityId); 99 RuleChain ruleChain = service.findRuleChainById(tenantId, entityId);
99 if (ruleChain != null) { 100 if (ruleChain != null) {
100 - ruleChainName = ruleChain.getName();  
101 - List<RuleNode> ruleNodeList = service.getRuleChainNodes(tenantId, entityId);  
102 - log.trace("[{}][{}] Starting rule chain with {} nodes", tenantId, entityId, ruleNodeList.size());  
103 - // Creating and starting the actors;  
104 - for (RuleNode ruleNode : ruleNodeList) {  
105 - log.trace("[{}][{}] Creating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode);  
106 - ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode);  
107 - nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode)); 101 + if (ruleChain.getType().equals(RuleChainType.SYSTEM)) {
  102 + ruleChainName = ruleChain.getName();
  103 + List<RuleNode> ruleNodeList = service.getRuleChainNodes(tenantId, entityId);
  104 + log.trace("[{}][{}] Starting rule chain with {} nodes", tenantId, entityId, ruleNodeList.size());
  105 + // Creating and starting the actors;
  106 + for (RuleNode ruleNode : ruleNodeList) {
  107 + log.trace("[{}][{}] Creating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode);
  108 + ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode);
  109 + nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode));
  110 + }
  111 + initRoutes(ruleChain, ruleNodeList);
  112 + started = true;
108 } 113 }
109 - initRoutes(ruleChain, ruleNodeList);  
110 - started = true;  
111 } 114 }
112 } else { 115 } else {
113 onUpdate(context); 116 onUpdate(context);
@@ -118,31 +121,35 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh @@ -118,31 +121,35 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
118 public void onUpdate(ActorContext context) { 121 public void onUpdate(ActorContext context) {
119 RuleChain ruleChain = service.findRuleChainById(tenantId, entityId); 122 RuleChain ruleChain = service.findRuleChainById(tenantId, entityId);
120 if (ruleChain != null) { 123 if (ruleChain != null) {
121 - ruleChainName = ruleChain.getName();  
122 - List<RuleNode> ruleNodeList = service.getRuleChainNodes(tenantId, entityId);  
123 - log.trace("[{}][{}] Updating rule chain with {} nodes", tenantId, entityId, ruleNodeList.size());  
124 - for (RuleNode ruleNode : ruleNodeList) {  
125 - RuleNodeCtx existing = nodeActors.get(ruleNode.getId());  
126 - if (existing == null) {  
127 - log.trace("[{}][{}] Creating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode);  
128 - ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode);  
129 - nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode));  
130 - } else {  
131 - log.trace("[{}][{}] Updating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode);  
132 - existing.setSelf(ruleNode);  
133 - existing.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, existing.getSelf().getId(), ComponentLifecycleEvent.UPDATED), self); 124 + if (ruleChain.getType().equals(RuleChainType.SYSTEM)) {
  125 + ruleChainName = ruleChain.getName();
  126 + List<RuleNode> ruleNodeList = service.getRuleChainNodes(tenantId, entityId);
  127 + log.trace("[{}][{}] Updating rule chain with {} nodes", tenantId, entityId, ruleNodeList.size());
  128 + for (RuleNode ruleNode : ruleNodeList) {
  129 + RuleNodeCtx existing = nodeActors.get(ruleNode.getId());
  130 + if (existing == null) {
  131 + log.trace("[{}][{}] Creating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode);
  132 + ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode);
  133 + nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode));
  134 + } else {
  135 + log.trace("[{}][{}] Updating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode);
  136 + existing.setSelf(ruleNode);
  137 + existing.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, existing.getSelf().getId(), ComponentLifecycleEvent.UPDATED), self);
  138 + }
134 } 139 }
135 - }  
136 140
137 - Set<RuleNodeId> existingNodes = ruleNodeList.stream().map(RuleNode::getId).collect(Collectors.toSet());  
138 - List<RuleNodeId> removedRules = nodeActors.keySet().stream().filter(node -> !existingNodes.contains(node)).collect(Collectors.toList());  
139 - removedRules.forEach(ruleNodeId -> {  
140 - log.trace("[{}][{}] Removing rule node [{}]", tenantId, entityId, ruleNodeId);  
141 - RuleNodeCtx removed = nodeActors.remove(ruleNodeId);  
142 - removed.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, removed.getSelf().getId(), ComponentLifecycleEvent.DELETED), self);  
143 - }); 141 + Set<RuleNodeId> existingNodes = ruleNodeList.stream().map(RuleNode::getId).collect(Collectors.toSet());
  142 + List<RuleNodeId> removedRules = nodeActors.keySet().stream().filter(node -> !existingNodes.contains(node)).collect(Collectors.toList());
  143 + removedRules.forEach(ruleNodeId -> {
  144 + log.trace("[{}][{}] Removing rule node [{}]", tenantId, entityId, ruleNodeId);
  145 + RuleNodeCtx removed = nodeActors.remove(ruleNodeId);
  146 + removed.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, removed.getSelf().getId(), ComponentLifecycleEvent.DELETED), self);
  147 + });
144 148
145 - initRoutes(ruleChain, ruleNodeList); 149 + initRoutes(ruleChain, ruleNodeList);
  150 + } else if (ruleChain.getType().equals(RuleChainType.EDGE)){
  151 + stop(context);
  152 + }
146 } 153 }
147 } 154 }
148 155
@@ -139,7 +139,7 @@ public class TenantActor extends RuleChainManagerActor { @@ -139,7 +139,7 @@ public class TenantActor extends RuleChainManagerActor {
139 RuleChain ruleChain = null; 139 RuleChain ruleChain = null;
140 if (msg.getEntityId().getEntityType() == EntityType.RULE_CHAIN) { 140 if (msg.getEntityId().getEntityType() == EntityType.RULE_CHAIN) {
141 ruleChain = systemContext.getRuleChainService().findRuleChainById(tenantId, new RuleChainId(msg.getEntityId().getId())); 141 ruleChain = systemContext.getRuleChainService().findRuleChainById(tenantId, new RuleChainId(msg.getEntityId().getId()));
142 - if (RuleChainType.SYSTEM.equals(ruleChain.getType())) { 142 + if (ruleChain !=null && !RuleChainType.SYSTEM.equals(ruleChain.getType())) {
143 log.debug("[{}] Non SYSTEM rule chains are ignored and not started. Current rule chain type [{}]", tenantId, ruleChain.getType()); 143 log.debug("[{}] Non SYSTEM rule chains are ignored and not started. Current rule chain type [{}]", tenantId, ruleChain.getType());
144 return; 144 return;
145 } 145 }
@@ -30,6 +30,7 @@ import org.thingsboard.server.common.data.Customer; @@ -30,6 +30,7 @@ import org.thingsboard.server.common.data.Customer;
30 import org.thingsboard.server.common.data.Device; 30 import org.thingsboard.server.common.data.Device;
31 import org.thingsboard.server.common.data.EntitySubtype; 31 import org.thingsboard.server.common.data.EntitySubtype;
32 import org.thingsboard.server.common.data.EntityType; 32 import org.thingsboard.server.common.data.EntityType;
  33 +import org.thingsboard.server.common.data.Tenant;
33 import org.thingsboard.server.common.data.audit.ActionType; 34 import org.thingsboard.server.common.data.audit.ActionType;
34 import org.thingsboard.server.common.data.device.DeviceSearchQuery; 35 import org.thingsboard.server.common.data.device.DeviceSearchQuery;
35 import org.thingsboard.server.common.data.edge.Edge; 36 import org.thingsboard.server.common.data.edge.Edge;
@@ -37,9 +38,12 @@ import org.thingsboard.server.common.data.edge.EdgeSearchQuery; @@ -37,9 +38,12 @@ import org.thingsboard.server.common.data.edge.EdgeSearchQuery;
37 import org.thingsboard.server.common.data.exception.ThingsboardException; 38 import org.thingsboard.server.common.data.exception.ThingsboardException;
38 import org.thingsboard.server.common.data.id.CustomerId; 39 import org.thingsboard.server.common.data.id.CustomerId;
39 import org.thingsboard.server.common.data.id.EdgeId; 40 import org.thingsboard.server.common.data.id.EdgeId;
  41 +import org.thingsboard.server.common.data.id.RuleChainId;
40 import org.thingsboard.server.common.data.id.TenantId; 42 import org.thingsboard.server.common.data.id.TenantId;
41 import org.thingsboard.server.common.data.page.TextPageData; 43 import org.thingsboard.server.common.data.page.TextPageData;
42 import org.thingsboard.server.common.data.page.TextPageLink; 44 import org.thingsboard.server.common.data.page.TextPageLink;
  45 +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
  46 +import org.thingsboard.server.common.data.rule.RuleChain;
43 import org.thingsboard.server.dao.exception.IncorrectParameterException; 47 import org.thingsboard.server.dao.exception.IncorrectParameterException;
44 import org.thingsboard.server.dao.model.ModelConstants; 48 import org.thingsboard.server.dao.model.ModelConstants;
45 import org.thingsboard.server.service.security.model.SecurityUser; 49 import org.thingsboard.server.service.security.model.SecurityUser;
@@ -74,7 +78,8 @@ public class EdgeController extends BaseController { @@ -74,7 +78,8 @@ public class EdgeController extends BaseController {
74 @ResponseBody 78 @ResponseBody
75 public Edge saveEdge(@RequestBody Edge edge) throws ThingsboardException { 79 public Edge saveEdge(@RequestBody Edge edge) throws ThingsboardException {
76 try { 80 try {
77 - edge.setTenantId(getCurrentUser().getTenantId()); 81 + TenantId tenantId = getCurrentUser().getTenantId();
  82 + edge.setTenantId(tenantId);
78 boolean created = edge.getId() == null; 83 boolean created = edge.getId() == null;
79 84
80 Operation operation = created ? Operation.CREATE : Operation.WRITE; 85 Operation operation = created ? Operation.CREATE : Operation.WRITE;
@@ -84,6 +89,12 @@ public class EdgeController extends BaseController { @@ -84,6 +89,12 @@ public class EdgeController extends BaseController {
84 89
85 Edge result = checkNotNull(edgeService.saveEdge(edge)); 90 Edge result = checkNotNull(edgeService.saveEdge(edge));
86 91
  92 + if (created) {
  93 + RuleChain rootTenantRuleChain = ruleChainService.getRootTenantRuleChain(tenantId);
  94 + ruleChainService.assignRuleChainToEdge(tenantId, rootTenantRuleChain.getId(), result.getId());
  95 + edgeService.setRootRuleChain(tenantId, result, rootTenantRuleChain.getId());
  96 + }
  97 +
87 logEntityAction(result.getId(), result, null, created ? ActionType.ADDED : ActionType.UPDATED, null); 98 logEntityAction(result.getId(), result, null, created ? ActionType.ADDED : ActionType.UPDATED, null);
88 return result; 99 return result;
89 } catch (Exception e) { 100 } catch (Exception e) {
@@ -250,6 +261,36 @@ public class EdgeController extends BaseController { @@ -250,6 +261,36 @@ public class EdgeController extends BaseController {
250 } 261 }
251 } 262 }
252 263
  264 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
  265 + @RequestMapping(value = "/edge/{edgeId}/{ruleChainId}/root", method = RequestMethod.POST)
  266 + @ResponseBody
  267 + public Edge setRootRuleChain(@PathVariable(EDGE_ID) String strEdgeId,
  268 + @PathVariable("ruleChainId") String strRuleChainId) throws ThingsboardException {
  269 + checkParameter(EDGE_ID, strEdgeId);
  270 + checkParameter("ruleChainId", strRuleChainId);
  271 + try {
  272 + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
  273 + checkRuleChain(ruleChainId, Operation.WRITE);
  274 +
  275 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  276 + Edge edge = checkEdgeId(edgeId, Operation.WRITE);
  277 + accessControlService.checkPermission(getCurrentUser(), Resource.EDGE, Operation.WRITE,
  278 + edge.getId(), edge);
  279 +
  280 + Edge updatedEdge = edgeService.setRootRuleChain(getTenantId(), edge, ruleChainId);
  281 +
  282 + logEntityAction(updatedEdge.getId(), updatedEdge, null, ActionType.UPDATED, null);
  283 +
  284 + return updatedEdge;
  285 + } catch (Exception e) {
  286 + logEntityAction(emptyId(EntityType.EDGE),
  287 + null,
  288 + null,
  289 + ActionType.UPDATED, e, strEdgeId);
  290 + throw handleException(e);
  291 + }
  292 + }
  293 +
253 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") 294 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
254 @RequestMapping(value = "/customer/{customerId}/edges", params = {"limit"}, method = RequestMethod.GET) 295 @RequestMapping(value = "/customer/{customerId}/edges", params = {"limit"}, method = RequestMethod.GET)
255 @ResponseBody 296 @ResponseBody
@@ -59,6 +59,7 @@ import org.thingsboard.server.common.data.rule.RuleNode; @@ -59,6 +59,7 @@ import org.thingsboard.server.common.data.rule.RuleNode;
59 import org.thingsboard.server.common.msg.TbMsg; 59 import org.thingsboard.server.common.msg.TbMsg;
60 import org.thingsboard.server.common.msg.TbMsgMetaData; 60 import org.thingsboard.server.common.msg.TbMsgMetaData;
61 import org.thingsboard.server.dao.event.EventService; 61 import org.thingsboard.server.dao.event.EventService;
  62 +import org.thingsboard.server.dao.exception.DataValidationException;
62 import org.thingsboard.server.service.script.JsInvokeService; 63 import org.thingsboard.server.service.script.JsInvokeService;
63 import org.thingsboard.server.service.script.RuleNodeJsScriptEngine; 64 import org.thingsboard.server.service.script.RuleNodeJsScriptEngine;
64 import org.thingsboard.server.service.security.permission.Operation; 65 import org.thingsboard.server.service.security.permission.Operation;
@@ -18,10 +18,12 @@ package org.thingsboard.server.dao.edge; @@ -18,10 +18,12 @@ package org.thingsboard.server.dao.edge;
18 import com.google.common.util.concurrent.ListenableFuture; 18 import com.google.common.util.concurrent.ListenableFuture;
19 import org.thingsboard.server.common.data.EntitySubtype; 19 import org.thingsboard.server.common.data.EntitySubtype;
20 import org.thingsboard.server.common.data.Event; 20 import org.thingsboard.server.common.data.Event;
  21 +import org.thingsboard.server.common.data.Tenant;
21 import org.thingsboard.server.common.data.edge.Edge; 22 import org.thingsboard.server.common.data.edge.Edge;
22 import org.thingsboard.server.common.data.edge.EdgeSearchQuery; 23 import org.thingsboard.server.common.data.edge.EdgeSearchQuery;
23 import org.thingsboard.server.common.data.id.CustomerId; 24 import org.thingsboard.server.common.data.id.CustomerId;
24 import org.thingsboard.server.common.data.id.EdgeId; 25 import org.thingsboard.server.common.data.id.EdgeId;
  26 +import org.thingsboard.server.common.data.id.RuleChainId;
25 import org.thingsboard.server.common.data.id.TenantId; 27 import org.thingsboard.server.common.data.id.TenantId;
26 import org.thingsboard.server.common.data.page.TextPageData; 28 import org.thingsboard.server.common.data.page.TextPageData;
27 import org.thingsboard.server.common.data.page.TextPageLink; 29 import org.thingsboard.server.common.data.page.TextPageLink;
@@ -73,6 +75,8 @@ public interface EdgeService { @@ -73,6 +75,8 @@ public interface EdgeService {
73 void pushEventToEdge(TenantId tenantId, TbMsg tbMsg); 75 void pushEventToEdge(TenantId tenantId, TbMsg tbMsg);
74 76
75 TimePageData<Event> findQueueEvents(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink); 77 TimePageData<Event> findQueueEvents(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink);
  78 +
  79 + Edge setRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId);
76 } 80 }
77 81
78 82
@@ -124,7 +124,7 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa @@ -124,7 +124,7 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa
124 } 124 }
125 125
126 public boolean isAssignedToEdge(EdgeId edgeId) { 126 public boolean isAssignedToEdge(EdgeId edgeId) {
127 - return this.assignedEdges != null && this.assignedEdges.contains(new ShortEdgeInfo(edgeId, null)); 127 + return this.assignedEdges != null && this.assignedEdges.contains(new ShortEdgeInfo(edgeId, null, null));
128 } 128 }
129 129
130 public ShortEdgeInfo getAssignedEdgeInfo(EdgeId edgeId) { 130 public ShortEdgeInfo getAssignedEdgeInfo(EdgeId edgeId) {
@@ -19,6 +19,7 @@ import lombok.AllArgsConstructor; @@ -19,6 +19,7 @@ import lombok.AllArgsConstructor;
19 import lombok.Getter; 19 import lombok.Getter;
20 import lombok.Setter; 20 import lombok.Setter;
21 import org.thingsboard.server.common.data.id.EdgeId; 21 import org.thingsboard.server.common.data.id.EdgeId;
  22 +import org.thingsboard.server.common.data.id.RuleChainId;
22 23
23 @AllArgsConstructor 24 @AllArgsConstructor
24 public class ShortEdgeInfo { 25 public class ShortEdgeInfo {
@@ -29,6 +30,9 @@ public class ShortEdgeInfo { @@ -29,6 +30,9 @@ public class ShortEdgeInfo {
29 @Getter @Setter 30 @Getter @Setter
30 private String title; 31 private String title;
31 32
  33 + @Getter @Setter
  34 + private RuleChainId rootRuleChainId;
  35 +
32 @Override 36 @Override
33 public boolean equals(Object o) { 37 public boolean equals(Object o) {
34 if (this == o) return true; 38 if (this == o) return true;
@@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.ShortCustomerInfo; @@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.ShortCustomerInfo;
29 import org.thingsboard.server.common.data.ShortEdgeInfo; 29 import org.thingsboard.server.common.data.ShortEdgeInfo;
30 import org.thingsboard.server.common.data.id.CustomerId; 30 import org.thingsboard.server.common.data.id.CustomerId;
31 import org.thingsboard.server.common.data.id.EdgeId; 31 import org.thingsboard.server.common.data.id.EdgeId;
  32 +import org.thingsboard.server.common.data.id.RuleChainId;
32 import org.thingsboard.server.common.data.id.TenantId; 33 import org.thingsboard.server.common.data.id.TenantId;
33 34
34 @EqualsAndHashCode(callSuper = true) 35 @EqualsAndHashCode(callSuper = true)
@@ -41,6 +42,7 @@ public class Edge extends SearchTextBasedWithAdditionalInfo<EdgeId> implements H @@ -41,6 +42,7 @@ public class Edge extends SearchTextBasedWithAdditionalInfo<EdgeId> implements H
41 42
42 private TenantId tenantId; 43 private TenantId tenantId;
43 private CustomerId customerId; 44 private CustomerId customerId;
  45 + private RuleChainId rootRuleChainId;
44 private String name; 46 private String name;
45 private String type; 47 private String type;
46 private String label; 48 private String label;
@@ -60,6 +62,7 @@ public class Edge extends SearchTextBasedWithAdditionalInfo<EdgeId> implements H @@ -60,6 +62,7 @@ public class Edge extends SearchTextBasedWithAdditionalInfo<EdgeId> implements H
60 super(edge); 62 super(edge);
61 this.tenantId = edge.getTenantId(); 63 this.tenantId = edge.getTenantId();
62 this.customerId = edge.getCustomerId(); 64 this.customerId = edge.getCustomerId();
  65 + this.rootRuleChainId = edge.getRootRuleChainId();
63 this.type = edge.getType(); 66 this.type = edge.getType();
64 this.name = edge.getName(); 67 this.name = edge.getName();
65 this.routingKey = edge.getRoutingKey(); 68 this.routingKey = edge.getRoutingKey();
@@ -69,7 +72,7 @@ public class Edge extends SearchTextBasedWithAdditionalInfo<EdgeId> implements H @@ -69,7 +72,7 @@ public class Edge extends SearchTextBasedWithAdditionalInfo<EdgeId> implements H
69 72
70 @JsonIgnore 73 @JsonIgnore
71 public ShortEdgeInfo toShortEdgeInfo() { 74 public ShortEdgeInfo toShortEdgeInfo() {
72 - return new ShortEdgeInfo(id, name); 75 + return new ShortEdgeInfo(id, name, rootRuleChainId);
73 } 76 }
74 77
75 @Override 78 @Override
@@ -94,7 +94,7 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo<RuleChainId> im @@ -94,7 +94,7 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo<RuleChainId> im
94 } 94 }
95 95
96 public boolean isAssignedToEdge(EdgeId edgeId) { 96 public boolean isAssignedToEdge(EdgeId edgeId) {
97 - return this.assignedEdges != null && this.assignedEdges.contains(new ShortEdgeInfo(edgeId, null)); 97 + return this.assignedEdges != null && this.assignedEdges.contains(new ShortEdgeInfo(edgeId, null, null));
98 } 98 }
99 99
100 public ShortEdgeInfo getAssignedEdgeInfo(EdgeId edgeId) { 100 public ShortEdgeInfo getAssignedEdgeInfo(EdgeId edgeId) {
@@ -44,6 +44,7 @@ import org.thingsboard.server.common.data.edge.EdgeSearchQuery; @@ -44,6 +44,7 @@ import org.thingsboard.server.common.data.edge.EdgeSearchQuery;
44 import org.thingsboard.server.common.data.id.CustomerId; 44 import org.thingsboard.server.common.data.id.CustomerId;
45 import org.thingsboard.server.common.data.id.EdgeId; 45 import org.thingsboard.server.common.data.id.EdgeId;
46 import org.thingsboard.server.common.data.id.EntityId; 46 import org.thingsboard.server.common.data.id.EntityId;
  47 +import org.thingsboard.server.common.data.id.RuleChainId;
47 import org.thingsboard.server.common.data.id.TenantId; 48 import org.thingsboard.server.common.data.id.TenantId;
48 import org.thingsboard.server.common.data.page.TextPageData; 49 import org.thingsboard.server.common.data.page.TextPageData;
49 import org.thingsboard.server.common.data.page.TextPageLink; 50 import org.thingsboard.server.common.data.page.TextPageLink;
@@ -472,6 +473,14 @@ public class BaseEdgeService extends AbstractEntityService implements EdgeServic @@ -472,6 +473,14 @@ public class BaseEdgeService extends AbstractEntityService implements EdgeServic
472 return eventService.findEvents(tenantId, edgeId, DataConstants.EDGE_QUEUE_EVENT_TYPE, pageLink); 473 return eventService.findEvents(tenantId, edgeId, DataConstants.EDGE_QUEUE_EVENT_TYPE, pageLink);
473 } 474 }
474 475
  476 + @Override
  477 + public Edge setRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) {
  478 + edge.setRootRuleChainId(ruleChainId);
  479 + Edge saveEdge = saveEdge(edge);
  480 + ruleChainService.updateEdgeRuleChains(tenantId, saveEdge.getId());
  481 + return saveEdge;
  482 + }
  483 +
475 private DataValidator<Edge> edgeValidator = 484 private DataValidator<Edge> edgeValidator =
476 new DataValidator<Edge>() { 485 new DataValidator<Edge>() {
477 486
@@ -359,6 +359,7 @@ public class ModelConstants { @@ -359,6 +359,7 @@ public class ModelConstants {
359 public static final String EDGE_COLUMN_FAMILY_NAME = "edge"; 359 public static final String EDGE_COLUMN_FAMILY_NAME = "edge";
360 public static final String EDGE_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; 360 public static final String EDGE_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY;
361 public static final String EDGE_CUSTOMER_ID_PROPERTY = CUSTOMER_ID_PROPERTY; 361 public static final String EDGE_CUSTOMER_ID_PROPERTY = CUSTOMER_ID_PROPERTY;
  362 + public static final String EDGE_ROOT_RULE_CHAIN_ID_PROPERTY = "root_rule_chain_id";
362 public static final String EDGE_NAME_PROPERTY = "name"; 363 public static final String EDGE_NAME_PROPERTY = "name";
363 public static final String EDGE_LABEL_PROPERTY = "label"; 364 public static final String EDGE_LABEL_PROPERTY = "label";
364 public static final String EDGE_TYPE_PROPERTY = "type"; 365 public static final String EDGE_TYPE_PROPERTY = "type";
@@ -25,6 +25,7 @@ import lombok.Data; @@ -25,6 +25,7 @@ import lombok.Data;
25 import org.thingsboard.server.common.data.edge.Edge; 25 import org.thingsboard.server.common.data.edge.Edge;
26 import org.thingsboard.server.common.data.id.CustomerId; 26 import org.thingsboard.server.common.data.id.CustomerId;
27 import org.thingsboard.server.common.data.id.EdgeId; 27 import org.thingsboard.server.common.data.id.EdgeId;
  28 +import org.thingsboard.server.common.data.id.RuleChainId;
28 import org.thingsboard.server.common.data.id.TenantId; 29 import org.thingsboard.server.common.data.id.TenantId;
29 import org.thingsboard.server.dao.model.SearchTextEntity; 30 import org.thingsboard.server.dao.model.SearchTextEntity;
30 import org.thingsboard.server.dao.model.type.JsonCodec; 31 import org.thingsboard.server.dao.model.type.JsonCodec;
@@ -37,6 +38,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.EDGE_CONFIGURATION @@ -37,6 +38,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.EDGE_CONFIGURATION
37 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_CUSTOMER_ID_PROPERTY; 38 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_CUSTOMER_ID_PROPERTY;
38 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_LABEL_PROPERTY; 39 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_LABEL_PROPERTY;
39 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_NAME_PROPERTY; 40 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_NAME_PROPERTY;
  41 +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_ROOT_RULE_CHAIN_ID_PROPERTY;
40 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_ROUTING_KEY_PROPERTY; 42 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_ROUTING_KEY_PROPERTY;
41 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_SECRET_PROPERTY; 43 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_SECRET_PROPERTY;
42 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_TENANT_ID_PROPERTY; 44 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_TENANT_ID_PROPERTY;
@@ -60,6 +62,9 @@ public class EdgeEntity implements SearchTextEntity<Edge> { @@ -60,6 +62,9 @@ public class EdgeEntity implements SearchTextEntity<Edge> {
60 @Column(name = EDGE_CUSTOMER_ID_PROPERTY) 62 @Column(name = EDGE_CUSTOMER_ID_PROPERTY)
61 private UUID customerId; 63 private UUID customerId;
62 64
  65 + @Column(name = EDGE_ROOT_RULE_CHAIN_ID_PROPERTY)
  66 + private UUID rootRuleChainId;
  67 +
63 @Column(name = EDGE_TYPE_PROPERTY) 68 @Column(name = EDGE_TYPE_PROPERTY)
64 private String type; 69 private String type;
65 70
@@ -95,6 +100,12 @@ public class EdgeEntity implements SearchTextEntity<Edge> { @@ -95,6 +100,12 @@ public class EdgeEntity implements SearchTextEntity<Edge> {
95 if (edge.getTenantId() != null) { 100 if (edge.getTenantId() != null) {
96 this.tenantId = edge.getTenantId().getId(); 101 this.tenantId = edge.getTenantId().getId();
97 } 102 }
  103 + if (edge.getCustomerId() != null) {
  104 + this.customerId = edge.getCustomerId().getId();
  105 + }
  106 + if (edge.getRootRuleChainId() != null) {
  107 + this.rootRuleChainId = edge.getRootRuleChainId().getId();
  108 + }
98 this.type = edge.getType(); 109 this.type = edge.getType();
99 this.name = edge.getName(); 110 this.name = edge.getName();
100 this.label = edge.getLabel(); 111 this.label = edge.getLabel();
@@ -119,6 +130,9 @@ public class EdgeEntity implements SearchTextEntity<Edge> { @@ -119,6 +130,9 @@ public class EdgeEntity implements SearchTextEntity<Edge> {
119 if (customerId != null) { 130 if (customerId != null) {
120 edge.setCustomerId(new CustomerId(customerId)); 131 edge.setCustomerId(new CustomerId(customerId));
121 } 132 }
  133 + if (rootRuleChainId != null) {
  134 + edge.setRootRuleChainId(new RuleChainId(rootRuleChainId));
  135 + }
122 edge.setType(type); 136 edge.setType(type);
123 edge.setName(name); 137 edge.setName(name);
124 edge.setLabel(label); 138 edge.setLabel(label);
@@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at 6 * You may obtain a copy of the License at
7 * 7 *
8 - * http://www.apache.org/licenses/LICENSE-2.0 8 + * http://www.apache.org/licenses/LICENSE-2.0
9 * 9 *
10 * Unless required by applicable law or agreed to in writing, software 10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, 11 * distributed under the License is distributed on an "AS IS" BASIS,
@@ -25,6 +25,7 @@ import org.thingsboard.server.common.data.UUIDConverter; @@ -25,6 +25,7 @@ import org.thingsboard.server.common.data.UUIDConverter;
25 import org.thingsboard.server.common.data.edge.Edge; 25 import org.thingsboard.server.common.data.edge.Edge;
26 import org.thingsboard.server.common.data.id.CustomerId; 26 import org.thingsboard.server.common.data.id.CustomerId;
27 import org.thingsboard.server.common.data.id.EdgeId; 27 import org.thingsboard.server.common.data.id.EdgeId;
  28 +import org.thingsboard.server.common.data.id.RuleChainId;
28 import org.thingsboard.server.common.data.id.TenantId; 29 import org.thingsboard.server.common.data.id.TenantId;
29 import org.thingsboard.server.dao.model.BaseSqlEntity; 30 import org.thingsboard.server.dao.model.BaseSqlEntity;
30 import org.thingsboard.server.dao.model.ModelConstants; 31 import org.thingsboard.server.dao.model.ModelConstants;
@@ -39,6 +40,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.EDGE_COLUMN_FAMILY @@ -39,6 +40,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.EDGE_COLUMN_FAMILY
39 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_CUSTOMER_ID_PROPERTY; 40 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_CUSTOMER_ID_PROPERTY;
40 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_LABEL_PROPERTY; 41 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_LABEL_PROPERTY;
41 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_NAME_PROPERTY; 42 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_NAME_PROPERTY;
  43 +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_ROOT_RULE_CHAIN_ID_PROPERTY;
42 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_ROUTING_KEY_PROPERTY; 44 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_ROUTING_KEY_PROPERTY;
43 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_SECRET_PROPERTY; 45 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_SECRET_PROPERTY;
44 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_TENANT_ID_PROPERTY; 46 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_TENANT_ID_PROPERTY;
@@ -58,6 +60,9 @@ public class EdgeEntity extends BaseSqlEntity<Edge> implements SearchTextEntity< @@ -58,6 +60,9 @@ public class EdgeEntity extends BaseSqlEntity<Edge> implements SearchTextEntity<
58 @Column(name = EDGE_CUSTOMER_ID_PROPERTY) 60 @Column(name = EDGE_CUSTOMER_ID_PROPERTY)
59 private String customerId; 61 private String customerId;
60 62
  63 + @Column(name = EDGE_ROOT_RULE_CHAIN_ID_PROPERTY)
  64 + private String rootRuleChainId;
  65 +
61 @Column(name = EDGE_TYPE_PROPERTY) 66 @Column(name = EDGE_TYPE_PROPERTY)
62 private String type; 67 private String type;
63 68
@@ -98,6 +103,9 @@ public class EdgeEntity extends BaseSqlEntity<Edge> implements SearchTextEntity< @@ -98,6 +103,9 @@ public class EdgeEntity extends BaseSqlEntity<Edge> implements SearchTextEntity<
98 if (edge.getCustomerId() != null) { 103 if (edge.getCustomerId() != null) {
99 this.customerId = UUIDConverter.fromTimeUUID(edge.getCustomerId().getId()); 104 this.customerId = UUIDConverter.fromTimeUUID(edge.getCustomerId().getId());
100 } 105 }
  106 + if (edge.getRootRuleChainId() != null) {
  107 + this.rootRuleChainId = UUIDConverter.fromTimeUUID(edge.getRootRuleChainId().getId());
  108 + }
101 this.type = edge.getType(); 109 this.type = edge.getType();
102 this.name = edge.getName(); 110 this.name = edge.getName();
103 this.label = edge.getLabel(); 111 this.label = edge.getLabel();
@@ -131,6 +139,9 @@ public class EdgeEntity extends BaseSqlEntity<Edge> implements SearchTextEntity< @@ -131,6 +139,9 @@ public class EdgeEntity extends BaseSqlEntity<Edge> implements SearchTextEntity<
131 if (customerId != null) { 139 if (customerId != null) {
132 edge.setCustomerId(new CustomerId(UUIDConverter.fromString(customerId))); 140 edge.setCustomerId(new CustomerId(UUIDConverter.fromString(customerId)));
133 } 141 }
  142 + if (rootRuleChainId != null) {
  143 + edge.setRootRuleChainId(new RuleChainId(UUIDConverter.fromString(rootRuleChainId)));
  144 + }
134 edge.setType(type); 145 edge.setType(type);
135 edge.setName(name); 146 edge.setName(name);
136 edge.setLabel(label); 147 edge.setLabel(label);
@@ -25,6 +25,7 @@ import org.springframework.beans.factory.annotation.Autowired; @@ -25,6 +25,7 @@ import org.springframework.beans.factory.annotation.Autowired;
25 import org.springframework.stereotype.Service; 25 import org.springframework.stereotype.Service;
26 import org.thingsboard.server.common.data.BaseData; 26 import org.thingsboard.server.common.data.BaseData;
27 import org.thingsboard.server.common.data.EntityType; 27 import org.thingsboard.server.common.data.EntityType;
  28 +import org.thingsboard.server.common.data.ShortEdgeInfo;
28 import org.thingsboard.server.common.data.Tenant; 29 import org.thingsboard.server.common.data.Tenant;
29 import org.thingsboard.server.common.data.edge.Edge; 30 import org.thingsboard.server.common.data.edge.Edge;
30 import org.thingsboard.server.common.data.id.EdgeId; 31 import org.thingsboard.server.common.data.id.EdgeId;
@@ -42,6 +43,7 @@ import org.thingsboard.server.common.data.rule.NodeConnectionInfo; @@ -42,6 +43,7 @@ import org.thingsboard.server.common.data.rule.NodeConnectionInfo;
42 import org.thingsboard.server.common.data.rule.RuleChain; 43 import org.thingsboard.server.common.data.rule.RuleChain;
43 import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo; 44 import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo;
44 import org.thingsboard.server.common.data.rule.RuleChainMetaData; 45 import org.thingsboard.server.common.data.rule.RuleChainMetaData;
  46 +import org.thingsboard.server.common.data.rule.RuleChainType;
45 import org.thingsboard.server.common.data.rule.RuleNode; 47 import org.thingsboard.server.common.data.rule.RuleNode;
46 import org.thingsboard.server.dao.edge.EdgeDao; 48 import org.thingsboard.server.dao.edge.EdgeDao;
47 import org.thingsboard.server.dao.edge.EdgeService; 49 import org.thingsboard.server.dao.edge.EdgeService;
@@ -116,6 +118,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC @@ -116,6 +118,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
116 createRelation(tenantId, new EntityRelation(ruleChain.getTenantId(), ruleChain.getId(), 118 createRelation(tenantId, new EntityRelation(ruleChain.getTenantId(), ruleChain.getId(),
117 EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN)); 119 EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN));
118 ruleChain.setRoot(true); 120 ruleChain.setRoot(true);
  121 + ruleChain.setType(RuleChainType.SYSTEM);
119 ruleChainDao.save(tenantId, ruleChain); 122 ruleChainDao.save(tenantId, ruleChain);
120 return true; 123 return true;
121 } catch (ExecutionException | InterruptedException e) { 124 } catch (ExecutionException | InterruptedException e) {
@@ -359,8 +362,17 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC @@ -359,8 +362,17 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
359 public void deleteRuleChainById(TenantId tenantId, RuleChainId ruleChainId) { 362 public void deleteRuleChainById(TenantId tenantId, RuleChainId ruleChainId) {
360 Validator.validateId(ruleChainId, "Incorrect rule chain id for delete request."); 363 Validator.validateId(ruleChainId, "Incorrect rule chain id for delete request.");
361 RuleChain ruleChain = ruleChainDao.findById(tenantId, ruleChainId.getId()); 364 RuleChain ruleChain = ruleChainDao.findById(tenantId, ruleChainId.getId());
362 - if (ruleChain != null && ruleChain.isRoot()) {  
363 - throw new DataValidationException("Deletion of Root Tenant Rule Chain is prohibited!"); 365 + if (ruleChain != null) {
  366 + if (ruleChain.isRoot()) {
  367 + throw new DataValidationException("Deletion of Root Tenant Rule Chain is prohibited!");
  368 + }
  369 + if (ruleChain.getAssignedEdges() != null && !ruleChain.getAssignedEdges().isEmpty()) {
  370 + for (ShortEdgeInfo assignedEdge : ruleChain.getAssignedEdges()) {
  371 + if (assignedEdge.getRootRuleChainId() != null && assignedEdge.getRootRuleChainId().equals(ruleChainId)) {
  372 + 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!");
  373 + }
  374 + }
  375 + }
364 } 376 }
365 checkRuleNodesAndDelete(tenantId, ruleChainId); 377 checkRuleNodesAndDelete(tenantId, ruleChainId);
366 } 378 }
@@ -398,13 +410,16 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC @@ -398,13 +410,16 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
398 RuleChain ruleChain = findRuleChainById(tenantId, ruleChainId); 410 RuleChain ruleChain = findRuleChainById(tenantId, ruleChainId);
399 Edge edge = edgeDao.findById(tenantId, edgeId.getId()); 411 Edge edge = edgeDao.findById(tenantId, edgeId.getId());
400 if (edge == null) { 412 if (edge == null) {
401 - throw new DataValidationException("Can't unassign ruleChain from non-existent edge!"); 413 + throw new DataValidationException("Can't unassign rule chain from non-existent edge!");
  414 + }
  415 + if (edge.getRootRuleChainId() != null && edge.getRootRuleChainId().equals(ruleChainId)) {
  416 + throw new DataValidationException("Can't unassign root rule chain from edge [" + edge.getName() + "]. Please assign another root rule chain first!");
402 } 417 }
403 if (ruleChain.removeAssignedEdge(edge)) { 418 if (ruleChain.removeAssignedEdge(edge)) {
404 try { 419 try {
405 deleteRelation(tenantId, new EntityRelation(edgeId, ruleChainId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE)); 420 deleteRelation(tenantId, new EntityRelation(edgeId, ruleChainId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE));
406 } catch (ExecutionException | InterruptedException e) { 421 } catch (ExecutionException | InterruptedException e) {
407 - log.warn("[{}] Failed to delete ruleChain relation. Edge Id: [{}]", ruleChainId, edgeId); 422 + log.warn("[{}] Failed to delete rule chain relation. Edge Id: [{}]", ruleChainId, edgeId);
408 throw new RuntimeException(e); 423 throw new RuntimeException(e);
409 } 424 }
410 return saveRuleChain(ruleChain); 425 return saveRuleChain(ruleChain);
@@ -442,13 +457,13 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC @@ -442,13 +457,13 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
442 Validator.validateId(tenantId, "Incorrect tenantId " + tenantId); 457 Validator.validateId(tenantId, "Incorrect tenantId " + tenantId);
443 Validator.validateId(edgeId, "Incorrect customerId " + edgeId); 458 Validator.validateId(edgeId, "Incorrect customerId " + edgeId);
444 Validator.validatePageLink(pageLink, "Incorrect page link " + pageLink); 459 Validator.validatePageLink(pageLink, "Incorrect page link " + pageLink);
445 - ListenableFuture<List<RuleChain>> dashboards = ruleChainDao.findRuleChainsByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink); 460 + ListenableFuture<List<RuleChain>> ruleChains = ruleChainDao.findRuleChainsByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink);
446 461
447 - return Futures.transform(dashboards, new Function<List<RuleChain>, TimePageData<RuleChain>>() { 462 + return Futures.transform(ruleChains, new Function<List<RuleChain>, TimePageData<RuleChain>>() {
448 @Nullable 463 @Nullable
449 @Override 464 @Override
450 - public TimePageData<RuleChain> apply(@Nullable List<RuleChain> RuleChain) {  
451 - return new TimePageData<>(RuleChain, pageLink); 465 + public TimePageData<RuleChain> apply(@Nullable List<RuleChain> ruleChain) {
  466 + return new TimePageData<>(ruleChain, pageLink);
452 } 467 }
453 }); 468 });
454 } 469 }
@@ -47,5 +47,4 @@ public interface RuleChainDao extends Dao<RuleChain> { @@ -47,5 +47,4 @@ public interface RuleChainDao extends Dao<RuleChain> {
47 * @return the list of rule chain objects 47 * @return the list of rule chain objects
48 */ 48 */
49 ListenableFuture<List<RuleChain>> findRuleChainsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink); 49 ListenableFuture<List<RuleChain>> findRuleChainsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink);
50 -  
51 } 50 }
@@ -88,5 +88,4 @@ public class JpaRuleChainDao extends JpaAbstractSearchTextDao<RuleChainEntity, R @@ -88,5 +88,4 @@ public class JpaRuleChainDao extends JpaAbstractSearchTextDao<RuleChainEntity, R
88 return Futures.successfulAsList(ruleChainFutures); 88 return Futures.successfulAsList(ruleChainFutures);
89 }); 89 });
90 } 90 }
91 -  
92 } 91 }
@@ -254,6 +254,7 @@ CREATE TABLE IF NOT EXISTS edge ( @@ -254,6 +254,7 @@ CREATE TABLE IF NOT EXISTS edge (
254 id varchar(31) NOT NULL CONSTRAINT edge_pkey PRIMARY KEY, 254 id varchar(31) NOT NULL CONSTRAINT edge_pkey PRIMARY KEY,
255 additional_info varchar, 255 additional_info varchar,
256 customer_id varchar(31), 256 customer_id varchar(31),
  257 + root_rule_chain_id varchar(31),
257 configuration varchar(10000000), 258 configuration varchar(10000000),
258 type varchar(255), 259 type varchar(255),
259 name varchar(255), 260 name varchar(255),
@@ -31,7 +31,8 @@ function EdgeService($http, $q, customerService) { @@ -31,7 +31,8 @@ function EdgeService($http, $q, customerService) {
31 getCustomerEdges: getCustomerEdges, 31 getCustomerEdges: getCustomerEdges,
32 assignEdgeToCustomer: assignEdgeToCustomer, 32 assignEdgeToCustomer: assignEdgeToCustomer,
33 unassignEdgeFromCustomer: unassignEdgeFromCustomer, 33 unassignEdgeFromCustomer: unassignEdgeFromCustomer,
34 - makeEdgePublic: makeEdgePublic 34 + makeEdgePublic: makeEdgePublic,
  35 + setRootRuleChain: setRootRuleChain
35 }; 36 };
36 37
37 return service; 38 return service;
@@ -229,4 +230,15 @@ function EdgeService($http, $q, customerService) { @@ -229,4 +230,15 @@ function EdgeService($http, $q, customerService) {
229 }); 230 });
230 return deferred.promise; 231 return deferred.promise;
231 } 232 }
  233 +
  234 + function setRootRuleChain(edgeId, ruleChainId) {
  235 + var deferred = $q.defer();
  236 + var url = '/api/edge/' + edgeId + '/' + ruleChainId + '/root';
  237 + $http.post(url).then(function success(response) {
  238 + deferred.resolve(response.data);
  239 + }, function fail() {
  240 + deferred.reject();
  241 + });
  242 + return deferred.promise;
  243 + }
232 } 244 }
@@ -19,6 +19,7 @@ import addEdgeTemplate from './add-edge.tpl.html'; @@ -19,6 +19,7 @@ import addEdgeTemplate from './add-edge.tpl.html';
19 import edgeCard from './edge-card.tpl.html'; 19 import edgeCard from './edge-card.tpl.html';
20 import assignToCustomerTemplate from './assign-to-customer.tpl.html'; 20 import assignToCustomerTemplate from './assign-to-customer.tpl.html';
21 import addEdgesToCustomerTemplate from './add-edges-to-customer.tpl.html'; 21 import addEdgesToCustomerTemplate from './add-edges-to-customer.tpl.html';
  22 +import setRootRuleChainToEdgesTemplate from './set-root-rule-chain-to-edges.tpl.html';
22 23
23 /* eslint-enable import/no-unresolved, import/default */ 24 /* eslint-enable import/no-unresolved, import/default */
24 25
@@ -47,8 +48,8 @@ export function EdgeCardController(types) { @@ -47,8 +48,8 @@ export function EdgeCardController(types) {
47 48
48 49
49 /*@ngInject*/ 50 /*@ngInject*/
50 -export function EdgeController($rootScope, userService, edgeService, customerService, $state, $stateParams,  
51 - $document, $mdDialog, $q, $translate, types, importExport) { 51 +export function EdgeController($rootScope, userService, edgeService, customerService, ruleChainService,
  52 + $state, $stateParams, $document, $mdDialog, $q, $translate, types, importExport) {
52 53
53 var customerId = $stateParams.customerId; 54 var customerId = $stateParams.customerId;
54 55
@@ -295,6 +296,19 @@ export function EdgeController($rootScope, userService, edgeService, customerSer @@ -295,6 +296,19 @@ export function EdgeController($rootScope, userService, edgeService, customerSer
295 edgeGroupActionsList.push( 296 edgeGroupActionsList.push(
296 { 297 {
297 onAction: function ($event, items) { 298 onAction: function ($event, items) {
  299 + setRootRuleChainToEdges($event, items);
  300 + },
  301 + name: function() { return $translate.instant('edge.set-root-rule-chain-to-edges') },
  302 + details: function(selectedCount) {
  303 + return $translate.instant('edge.set-root-rule-chain-to-edges-text', {count: selectedCount}, "messageformat");
  304 + },
  305 + icon: "flag"
  306 + }
  307 + );
  308 +
  309 + edgeGroupActionsList.push(
  310 + {
  311 + onAction: function ($event, items) {
298 assignEdgesToCustomer($event, items); 312 assignEdgesToCustomer($event, items);
299 }, 313 },
300 name: function() { return $translate.instant('edge.assign-edges') }, 314 name: function() { return $translate.instant('edge.assign-edges') },
@@ -305,8 +319,6 @@ export function EdgeController($rootScope, userService, edgeService, customerSer @@ -305,8 +319,6 @@ export function EdgeController($rootScope, userService, edgeService, customerSer
305 } 319 }
306 ); 320 );
307 321
308 -  
309 -  
310 edgeGroupActionsList.push( 322 edgeGroupActionsList.push(
311 { 323 {
312 onAction: function ($event) { 324 onAction: function ($event) {
@@ -318,9 +330,7 @@ export function EdgeController($rootScope, userService, edgeService, customerSer @@ -318,9 +330,7 @@ export function EdgeController($rootScope, userService, edgeService, customerSer
318 } 330 }
319 ); 331 );
320 332
321 -  
322 -  
323 - } else if (vm.edgesScope === 'customer' || vm.edgesScope === 'customer_user') { 333 + } else if (vm.edgesScope === 'customer' || vm.edgesScope === 'customer_user') {
324 fetchEdgesFunction = function (pageLink, edgeType) { 334 fetchEdgesFunction = function (pageLink, edgeType) {
325 return edgeService.getCustomerEdges(customerId, pageLink, true, null, edgeType); 335 return edgeService.getCustomerEdges(customerId, pageLink, true, null, edgeType);
326 }; 336 };
@@ -524,6 +534,50 @@ export function EdgeController($rootScope, userService, edgeService, customerSer @@ -524,6 +534,50 @@ export function EdgeController($rootScope, userService, edgeService, customerSer
524 }); 534 });
525 } 535 }
526 536
  537 + function setRootRuleChainToEdges($event, items) {
  538 + var edgeIds = [];
  539 + for (var id in items.selections) {
  540 + edgeIds.push(id);
  541 + }
  542 + setRootRuleChain($event, edgeIds);
  543 + }
  544 +
  545 + function setRootRuleChain($event, edgeIds) {
  546 + if ($event) {
  547 + $event.stopPropagation();
  548 + }
  549 + var pageSize = 10;
  550 + ruleChainService.getRuleChains({limit: pageSize, textSearch: ''}).then(
  551 + function success(_ruleChains) {
  552 + var ruleChains = {
  553 + pageSize: pageSize,
  554 + data: _ruleChains.data,
  555 + nextPageLink: _ruleChains.nextPageLink,
  556 + selection: null,
  557 + hasNext: _ruleChains.hasNext,
  558 + pending: false
  559 + };
  560 + if (ruleChains.hasNext) {
  561 + ruleChains.nextPageLink.limit = pageSize;
  562 + }
  563 + $mdDialog.show({
  564 + controller: 'SetRootRuleChainToEdgesController',
  565 + controllerAs: 'vm',
  566 + templateUrl: setRootRuleChainToEdgesTemplate,
  567 + locals: {edgeIds: edgeIds, ruleChains: ruleChains},
  568 + parent: angular.element($document[0].body),
  569 + fullscreen: true,
  570 + targetEvent: $event
  571 + }).then(function () {
  572 + vm.grid.refreshList();
  573 + }, function () {
  574 + });
  575 + },
  576 + function fail() {
  577 + });
  578 + }
  579 +
  580 +
527 function assignEdgesToCustomer($event, items) { 581 function assignEdgesToCustomer($event, items) {
528 var edgeIds = []; 582 var edgeIds = [];
529 for (var id in items.selections) { 583 for (var id in items.selections) {
@@ -23,6 +23,7 @@ import EdgeRoutes from './edge.routes'; @@ -23,6 +23,7 @@ import EdgeRoutes from './edge.routes';
23 import {EdgeController, EdgeCardController} from './edge.controller'; 23 import {EdgeController, EdgeCardController} from './edge.controller';
24 import AssignEdgeToCustomerController from './assign-to-customer.controller'; 24 import AssignEdgeToCustomerController from './assign-to-customer.controller';
25 import AddEdgesToCustomerController from './add-edges-to-customer.controller'; 25 import AddEdgesToCustomerController from './add-edges-to-customer.controller';
  26 +import SetRootRuleChainToEdgesController from './set-root-rule-chain-to-edges.controller';
26 import EdgeDirective from './edge.directive'; 27 import EdgeDirective from './edge.directive';
27 28
28 export default angular.module('thingsboard.edge', [ 29 export default angular.module('thingsboard.edge', [
@@ -37,5 +38,6 @@ export default angular.module('thingsboard.edge', [ @@ -37,5 +38,6 @@ export default angular.module('thingsboard.edge', [
37 .controller('EdgeCardController', EdgeCardController) 38 .controller('EdgeCardController', EdgeCardController)
38 .controller('AssignEdgeToCustomerController', AssignEdgeToCustomerController) 39 .controller('AssignEdgeToCustomerController', AssignEdgeToCustomerController)
39 .controller('AddEdgesToCustomerController', AddEdgesToCustomerController) 40 .controller('AddEdgesToCustomerController', AddEdgesToCustomerController)
  41 + .controller('SetRootRuleChainToEdgesController', SetRootRuleChainToEdgesController)
40 .directive('tbEdge', EdgeDirective) 42 .directive('tbEdge', EdgeDirective)
41 .name; 43 .name;
  1 +/*
  2 + * Copyright © 2016-2019 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +/*@ngInject*/
  17 +export default function SetRootRuleChainToEdgesController(ruleChainService, edgeService, $mdDialog, $q, edgeIds, ruleChains) {
  18 +
  19 + var vm = this;
  20 +
  21 + vm.ruleChains = ruleChains;
  22 + vm.searchText = '';
  23 +
  24 + vm.assign = assign;
  25 + vm.cancel = cancel;
  26 + vm.isRuleChainSelected = isRuleChainSelected;
  27 + vm.hasData = hasData;
  28 + vm.noData = noData;
  29 + vm.searchRuleChainTextUpdated = searchRuleChainTextUpdated;
  30 + vm.toggleRuleChainSelection = toggleRuleChainSelection;
  31 +
  32 + vm.theRuleChains = {
  33 + getItemAtIndex: function (index) {
  34 + if (index > vm.ruleChains.data.length) {
  35 + vm.theRuleChains.fetchMoreItems_(index);
  36 + return null;
  37 + }
  38 + var item = vm.ruleChains.data[index];
  39 + if (item) {
  40 + item.indexNumber = index + 1;
  41 + }
  42 + return item;
  43 + },
  44 +
  45 + getLength: function () {
  46 + if (vm.ruleChains.hasNext) {
  47 + return vm.ruleChains.data.length + vm.ruleChains.nextPageLink.limit;
  48 + } else {
  49 + return vm.ruleChains.data.length;
  50 + }
  51 + },
  52 +
  53 + fetchMoreItems_: function () {
  54 + if (vm.ruleChains.hasNext && !vm.ruleChains.pending) {
  55 + vm.ruleChains.pending = true;
  56 + ruleChainService.getRuleChains(vm.ruleChains.nextPageLink).then(
  57 + function success(ruleChains) {
  58 + vm.ruleChains.data = vm.ruleChains.data.concat(ruleChains.data);
  59 + vm.ruleChains.nextPageLink = ruleChains.nextPageLink;
  60 + vm.ruleChains.hasNext = ruleChains.hasNext;
  61 + if (vm.ruleChains.hasNext) {
  62 + vm.ruleChains.nextPageLink.limit = vm.ruleChains.pageSize;
  63 + }
  64 + vm.ruleChains.pending = false;
  65 + },
  66 + function fail() {
  67 + vm.ruleChains.hasNext = false;
  68 + vm.ruleChains.pending = false;
  69 + });
  70 + }
  71 + }
  72 + };
  73 +
  74 + function cancel() {
  75 + $mdDialog.cancel();
  76 + }
  77 +
  78 + function assign() {
  79 + var assignTasks = [];
  80 + for (var i=0;i<edgeIds.length;i++) {
  81 + assignTasks.push(ruleChainService.assignRuleChainToEdge(edgeIds[i], vm.ruleChains.selection.id.id));
  82 + }
  83 + $q.all(assignTasks).then(function () {
  84 + var setRootTasks = [];
  85 + for (var j=0;j<edgeIds.length;j++) {
  86 + setRootTasks.push(edgeService.setRootRuleChain(edgeIds[j], vm.ruleChains.selection.id.id));
  87 + }
  88 + $q.all(setRootTasks).then(function () {
  89 + $mdDialog.hide();
  90 + });
  91 + });
  92 + }
  93 +
  94 + function noData() {
  95 + return vm.ruleChains.data.length == 0 && !vm.ruleChains.hasNext;
  96 + }
  97 +
  98 + function hasData() {
  99 + return vm.ruleChains.data.length > 0;
  100 + }
  101 +
  102 + function toggleRuleChainSelection($event, ruleChain) {
  103 + $event.stopPropagation();
  104 + if (vm.isRuleChainSelected(ruleChain)) {
  105 + vm.ruleChains.selection = null;
  106 + } else {
  107 + vm.ruleChains.selection = ruleChain;
  108 + }
  109 + }
  110 +
  111 + function isRuleChainSelected(ruleChain) {
  112 + return vm.ruleChains.selection != null && ruleChain &&
  113 + ruleChain.id.id === vm.ruleChains.selection.id.id;
  114 + }
  115 +
  116 + function searchRuleChainTextUpdated() {
  117 + vm.ruleChains = {
  118 + pageSize: vm.ruleChains.pageSize,
  119 + data: [],
  120 + nextPageLink: {
  121 + limit: vm.ruleChains.pageSize,
  122 + textSearch: vm.searchText
  123 + },
  124 + selection: null,
  125 + hasNext: true,
  126 + pending: false
  127 + };
  128 + }
  129 +}
  1 +<!--
  2 +
  3 + Copyright © 2016-2019 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +<md-dialog aria-label="{{ 'edge.set-root-rule-chain-to-edges' | translate }}">
  19 + <form name="theForm" ng-submit="vm.assign()">
  20 + <md-toolbar>
  21 + <div class="md-toolbar-tools">
  22 + <h2 translate>edge.set-root-rule-chain-to-edges</h2>
  23 + <span flex></span>
  24 + <md-button class="md-icon-button" ng-click="vm.cancel()">
  25 + <ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon>
  26 + </md-button>
  27 + </div>
  28 + </md-toolbar>
  29 + <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
  30 + <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
  31 + <md-dialog-content>
  32 + <div class="md-dialog-content">
  33 + <fieldset>
  34 + <span translate>edge.set-root-rule-chain-text</span>
  35 + <md-input-container class="md-block" style='margin-bottom: 0px;'>
  36 + <label>&nbsp;</label>
  37 + <md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons">
  38 + search
  39 + </md-icon>
  40 + <input id="rule-chain-search" autofocus ng-model="vm.searchText"
  41 + ng-change="vm.searchRuleChainTextUpdated()"
  42 + placeholder="{{ 'common.enter-search' | translate }}"/>
  43 + </md-input-container>
  44 + <div style='min-height: 150px;'>
  45 + <span translate layout-align="center center"
  46 + style="text-transform: uppercase; display: flex; height: 150px;"
  47 + class="md-subhead"
  48 + ng-show="vm.noData()">rulechain.no-rulechains-text</span>
  49 + <md-virtual-repeat-container ng-show="vm.hasData()"
  50 + tb-scope-element="repeatContainer" md-top-index="vm.topIndex" flex
  51 + style='min-height: 150px; width: 100%;'>
  52 + <md-list>
  53 + <md-list-item md-virtual-repeat="ruleChain in vm.theRuleChains" md-on-demand
  54 + class="repeated-item" flex>
  55 + <md-checkbox ng-click="vm.toggleRuleChainSelection($event, ruleChain)"
  56 + aria-label="{{ 'item.selected' | translate }}"
  57 + ng-checked="vm.isRuleChainSelected(ruleChain)"></md-checkbox>
  58 + <span> {{ ruleChain.name }} </span>
  59 + </md-list-item>
  60 + </md-list>
  61 + </md-virtual-repeat-container>
  62 + </div>
  63 + </fieldset>
  64 + </div>
  65 + </md-dialog-content>
  66 + <md-dialog-actions layout="row">
  67 + <span flex></span>
  68 + <md-button ng-disabled="$root.loading || vm.ruleChains.selection==null" type="submit" class="md-raised md-primary">
  69 + {{ 'action.assign' | translate }}
  70 + </md-button>
  71 + <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
  72 + translate }}
  73 + </md-button>
  74 + </md-dialog-actions>
  75 + </form>
  76 +</md-dialog>
@@ -792,6 +792,7 @@ @@ -792,6 +792,7 @@
792 "dashboards": "Edge Dashboards", 792 "dashboards": "Edge Dashboards",
793 "manage-edge-rulechains": "Manage edge rule chains", 793 "manage-edge-rulechains": "Manage edge rule chains",
794 "rulechains": "Edge Rule Chains", 794 "rulechains": "Edge Rule Chains",
  795 + "rulechain": "Edge Rule Chain",
795 "edge-key": "Edge key", 796 "edge-key": "Edge key",
796 "copy-edge-key": "Copy edge key", 797 "copy-edge-key": "Copy edge key",
797 "edge-key-copied-message": "Edge key has been copied to clipboard", 798 "edge-key-copied-message": "Edge key has been copied to clipboard",
@@ -803,7 +804,10 @@ @@ -803,7 +804,10 @@
803 "manage-edge-entity-views": "Manage edge entity views", 804 "manage-edge-entity-views": "Manage edge entity views",
804 "assets": "Edge assets", 805 "assets": "Edge assets",
805 "devices": "Edge devices", 806 "devices": "Edge devices",
806 - "entity-views": "Edge entity views" 807 + "entity-views": "Edge entity views",
  808 + "set-root-rule-chain-text": "Please select root rule chain for edge(s)",
  809 + "set-root-rule-chain-to-edges": "Set root rule chain for Edge(s)",
  810 + "set-root-rule-chain-to-edges-text": "Set root rule chain for { count, plural, 1 {1 edge} other {# edges} }"
807 }, 811 },
808 "error": { 812 "error": {
809 "unable-to-connect": "Unable to connect to the server! Please check your internet connection.", 813 "unable-to-connect": "Unable to connect to the server! Please check your internet connection.",
@@ -31,7 +31,7 @@ @@ -31,7 +31,7 @@
31 <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span> 31 <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
32 <md-dialog-content> 32 <md-dialog-content>
33 <div class="md-dialog-content"> 33 <div class="md-dialog-content">
34 - <tb-rule-chain rule-chain="vm.item" is-edit="true" the-form="theForm"></tb-rule-chain> 34 + <tb-rule-chain rule-chain="vm.item" is-edit="true" rule-chain-scope="'tenant'" the-form="theForm"></tb-rule-chain>
35 </div> 35 </div>
36 </md-dialog-content> 36 </md-dialog-content>
37 <md-dialog-actions layout="row"> 37 <md-dialog-actions layout="row">
@@ -54,7 +54,7 @@ export default function AddRuleChainsToEdgeController(ruleChainService, $mdDialo @@ -54,7 +54,7 @@ export default function AddRuleChainsToEdgeController(ruleChainService, $mdDialo
54 vm.ruleChains.pending = true; 54 vm.ruleChains.pending = true;
55 ruleChainService.getRuleChains(vm.ruleChains.nextPageLink).then( 55 ruleChainService.getRuleChains(vm.ruleChains.nextPageLink).then(
56 function success(ruleChains) { 56 function success(ruleChains) {
57 - vm.ruleChains.data = vm.ruleChains.data.concat(filterNonRootRuleChains(ruleChains.data)); 57 + vm.ruleChains.data = ruleChains.data;
58 vm.ruleChains.nextPageLink = ruleChains.nextPageLink; 58 vm.ruleChains.nextPageLink = ruleChains.nextPageLink;
59 vm.ruleChains.hasNext = ruleChains.hasNext; 59 vm.ruleChains.hasNext = ruleChains.hasNext;
60 if (vm.ruleChains.hasNext) { 60 if (vm.ruleChains.hasNext) {
@@ -119,12 +119,4 @@ export default function AddRuleChainsToEdgeController(ruleChainService, $mdDialo @@ -119,12 +119,4 @@ export default function AddRuleChainsToEdgeController(ruleChainService, $mdDialo
119 pending: false 119 pending: false
120 }; 120 };
121 } 121 }
122 -  
123 - function filterNonRootRuleChains(ruleChains) {  
124 - return $filter('filter')(ruleChains, isNonRootRuleChain);  
125 - }  
126 -  
127 - function isNonRootRuleChain(ruleChain) {  
128 - return ruleChain && !ruleChain.root;  
129 - }  
130 } 122 }
@@ -16,4 +16,4 @@ @@ -16,4 +16,4 @@
16 16
17 --> 17 -->
18 <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> 18 <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>
19 -<div ng-if="item && item.root" translate>rulechain.root</div> 19 +<div ng-if="(vm.parentCtl.ruleChainsScope === 'tenant' && item && item.root) || (vm.parentCtl.ruleChainsScope === 'edge' && vm.parentCtl.isRootRuleChain(item))" translate>rulechain.root</div>
@@ -51,7 +51,7 @@ @@ -51,7 +51,7 @@
51 </md-input-container> 51 </md-input-container>
52 <md-input-container class="md-block"> 52 <md-input-container class="md-block">
53 <label translate>rulechain.type</label> 53 <label translate>rulechain.type</label>
54 - <md-select ng-disabled="$root.loading || !isEdit" name="type" ng-model="ruleChain.type"> 54 + <md-select ng-disabled="$root.loading || !isEdit || ruleChainScope !== 'tenant' || ruleChain.root === true" name="type" ng-model="ruleChain.type">
55 <md-option ng-repeat="ruleChainType in ruleChainTypes" value="{{ruleChainType}}"> 55 <md-option ng-repeat="ruleChainType in ruleChainTypes" value="{{ruleChainType}}">
56 {{ruleChainType}} 56 {{ruleChainType}}
57 </md-option> 57 </md-option>
@@ -49,7 +49,7 @@ export default function RuleChainDirective($compile, $templateCache, $mdDialog, @@ -49,7 +49,7 @@ export default function RuleChainDirective($compile, $templateCache, $mdDialog,
49 theForm: '=', 49 theForm: '=',
50 onSetRootRuleChain: '&', 50 onSetRootRuleChain: '&',
51 onExportRuleChain: '&', 51 onExportRuleChain: '&',
52 - onDeleteRuleChain: '&', 52 + onDeleteRuleChain: '&'
53 } 53 }
54 }; 54 };
55 } 55 }
@@ -145,5 +145,43 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider @@ -145,5 +145,43 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider
145 ncyBreadcrumb: { 145 ncyBreadcrumb: {
146 label: '{"icon": "settings_ethernet", "label": "{{ vm.edgeRuleChainsTitle }}", "translate": "false"}' 146 label: '{"icon": "settings_ethernet", "label": "{{ vm.edgeRuleChainsTitle }}", "translate": "false"}'
147 } 147 }
  148 + })
  149 + .state('home.edges.ruleChains.ruleChain', {
  150 + url: '/:ruleChainId',
  151 + reloadOnSearch: false,
  152 + module: 'private',
  153 + auth: ['SYS_ADMIN', 'TENANT_ADMIN'],
  154 + views: {
  155 + "content@home": {
  156 + templateUrl: ruleChainTemplate,
  157 + controller: 'RuleChainController',
  158 + controllerAs: 'vm'
  159 + }
  160 + },
  161 + resolve: {
  162 + ruleChain:
  163 + /*@ngInject*/
  164 + function($stateParams, ruleChainService) {
  165 + return ruleChainService.getRuleChain($stateParams.ruleChainId);
  166 + },
  167 + ruleChainMetaData:
  168 + /*@ngInject*/
  169 + function($stateParams, ruleChainService) {
  170 + return ruleChainService.getRuleChainMetaData($stateParams.ruleChainId);
  171 + },
  172 + ruleNodeComponents:
  173 + /*@ngInject*/
  174 + function($stateParams, ruleChainService) {
  175 + return ruleChainService.getRuleNodeComponents();
  176 + }
  177 + },
  178 + data: {
  179 + import: false,
  180 + searchEnabled: false,
  181 + pageTitle: 'edge.rulechain'
  182 + },
  183 + ncyBreadcrumb: {
  184 + label: '{"icon": "settings_ethernet", "label": "edge.rulechain"}'
  185 + }
148 }); 186 });
149 } 187 }
@@ -25,7 +25,7 @@ import addRuleChainsToEdgeTemplate from "./add-rulechains-to-edge.tpl.html"; @@ -25,7 +25,7 @@ import addRuleChainsToEdgeTemplate from "./add-rulechains-to-edge.tpl.html";
25 import './rulechain-card.scss'; 25 import './rulechain-card.scss';
26 26
27 /*@ngInject*/ 27 /*@ngInject*/
28 -export default function RuleChainsController(ruleChainService, userService, importExport, $state, 28 +export default function RuleChainsController(ruleChainService, userService, edgeService, importExport, $state,
29 $stateParams, $filter, $translate, $mdDialog, $document, $q, types) { 29 $stateParams, $filter, $translate, $mdDialog, $document, $q, types) {
30 30
31 var vm = this; 31 var vm = this;
@@ -107,6 +107,11 @@ export default function RuleChainsController(ruleChainService, userService, impo @@ -107,6 +107,11 @@ export default function RuleChainsController(ruleChainService, userService, impo
107 107
108 if (edgeId) { 108 if (edgeId) {
109 vm.edgeRuleChainsTitle = $translate.instant('edge.rulechains'); 109 vm.edgeRuleChainsTitle = $translate.instant('edge.rulechains');
  110 + edgeService.getEdge(edgeId).then(
  111 + function success(edge) {
  112 + vm.edge = edge;
  113 + }
  114 + );
110 } 115 }
111 116
112 if (vm.ruleChainsScope === 'tenant') { 117 if (vm.ruleChainsScope === 'tenant') {
@@ -133,8 +138,7 @@ export default function RuleChainsController(ruleChainService, userService, impo @@ -133,8 +138,7 @@ export default function RuleChainsController(ruleChainService, userService, impo
133 }, 138 },
134 name: function() { return $translate.instant('action.assign') }, 139 name: function() { return $translate.instant('action.assign') },
135 details: function() { return $translate.instant('rulechain.manage-assigned-edges') }, 140 details: function() { return $translate.instant('rulechain.manage-assigned-edges') },
136 - icon: "wifi_tethering",  
137 - isEnabled: isNonRootRuleChain 141 + icon: "wifi_tethering"
138 }); 142 });
139 143
140 ruleChainActionsList.push({ 144 ruleChainActionsList.push({
@@ -214,6 +218,16 @@ export default function RuleChainsController(ruleChainService, userService, impo @@ -214,6 +218,16 @@ export default function RuleChainsController(ruleChainService, userService, impo
214 return ruleChainService.unassignRuleChainFromEdge(edgeId, ruleChainId); 218 return ruleChainService.unassignRuleChainFromEdge(edgeId, ruleChainId);
215 }; 219 };
216 220
  221 + ruleChainActionsList.push({
  222 + onAction: function ($event, item) {
  223 + setRootRuleChain($event, item);
  224 + },
  225 + name: function() { return $translate.instant('rulechain.set-root') },
  226 + details: function() { return $translate.instant('rulechain.set-root') },
  227 + icon: "flag",
  228 + isEnabled: isNonRootRuleChain
  229 + });
  230 +
217 ruleChainActionsList.push( 231 ruleChainActionsList.push(
218 { 232 {
219 onAction: function ($event, item) { 233 onAction: function ($event, item) {
@@ -221,7 +235,8 @@ export default function RuleChainsController(ruleChainService, userService, impo @@ -221,7 +235,8 @@ export default function RuleChainsController(ruleChainService, userService, impo
221 }, 235 },
222 name: function() { return $translate.instant('action.unassign') }, 236 name: function() { return $translate.instant('action.unassign') },
223 details: function() { return $translate.instant('rulechain.unassign-from-edge') }, 237 details: function() { return $translate.instant('rulechain.unassign-from-edge') },
224 - icon: "assignment_return" 238 + icon: "assignment_return",
  239 + isEnabled: isNonRootRuleChain
225 } 240 }
226 ); 241 );
227 242
@@ -288,7 +303,13 @@ export default function RuleChainsController(ruleChainService, userService, impo @@ -288,7 +303,13 @@ export default function RuleChainsController(ruleChainService, userService, impo
288 if ($event) { 303 if ($event) {
289 $event.stopPropagation(); 304 $event.stopPropagation();
290 } 305 }
291 - $state.go('home.ruleChains.ruleChain', {ruleChainId: ruleChain.id.id}); 306 + if (vm.ruleChainsScope === 'edge') {
  307 + $state.go('home.edges.ruleChains.ruleChain', {
  308 + ruleChainId: ruleChain.id.id
  309 + });
  310 + } else {
  311 + $state.go('home.ruleChains.ruleChain', {ruleChainId: ruleChain.id.id});
  312 + }
292 } 313 }
293 314
294 function deleteRuleChain(ruleChainId) { 315 function deleteRuleChain(ruleChainId) {
@@ -300,11 +321,19 @@ export default function RuleChainsController(ruleChainService, userService, impo @@ -300,11 +321,19 @@ export default function RuleChainsController(ruleChainService, userService, impo
300 } 321 }
301 322
302 function isRootRuleChain(ruleChain) { 323 function isRootRuleChain(ruleChain) {
303 - return ruleChain && ruleChain.root; 324 + if (angular.isDefined(vm.edge) && vm.edge != null) {
  325 + return angular.isDefined(vm.edge.rootRuleChainId) && vm.edge.rootRuleChainId != null && vm.edge.rootRuleChainId.id === ruleChain.id.id;
  326 + } else {
  327 + return ruleChain && ruleChain.root;
  328 + }
304 } 329 }
305 330
306 function isNonRootRuleChain(ruleChain) { 331 function isNonRootRuleChain(ruleChain) {
307 - return ruleChain && !ruleChain.root; 332 + if (angular.isDefined(vm.edge) && vm.edge != null) {
  333 + return angular.isDefined(vm.edge.rootRuleChainId) && vm.edge.rootRuleChainId != null && vm.edge.rootRuleChainId.id !== ruleChain.id.id;
  334 + } else {
  335 + return ruleChain && !ruleChain.root;
  336 + }
308 } 337 }
309 338
310 function exportRuleChain($event, ruleChain) { 339 function exportRuleChain($event, ruleChain) {
@@ -322,11 +351,20 @@ export default function RuleChainsController(ruleChainService, userService, impo @@ -322,11 +351,20 @@ export default function RuleChainsController(ruleChainService, userService, impo
322 .cancel($translate.instant('action.no')) 351 .cancel($translate.instant('action.no'))
323 .ok($translate.instant('action.yes')); 352 .ok($translate.instant('action.yes'));
324 $mdDialog.show(confirm).then(function () { 353 $mdDialog.show(confirm).then(function () {
325 - ruleChainService.setRootRuleChain(ruleChain.id.id).then(  
326 - () => {  
327 - vm.grid.refreshList();  
328 - }  
329 - ); 354 + if (angular.isDefined(vm.edge) && vm.edge != null) {
  355 + edgeService.setRootRuleChain(vm.edge.id.id, ruleChain.id.id).then(
  356 + (edge) => {
  357 + vm.edge = edge;
  358 + vm.grid.refreshList();
  359 + }
  360 + );
  361 + } else {
  362 + ruleChainService.setRootRuleChain(ruleChain.id.id).then(
  363 + () => {
  364 + vm.grid.refreshList();
  365 + }
  366 + );
  367 + }
330 }); 368 });
331 } 369 }
332 370
@@ -396,7 +434,7 @@ export default function RuleChainsController(ruleChainService, userService, impo @@ -396,7 +434,7 @@ export default function RuleChainsController(ruleChainService, userService, impo
396 function success(_ruleChains) { 434 function success(_ruleChains) {
397 var ruleChains = { 435 var ruleChains = {
398 pageSize: pageSize, 436 pageSize: pageSize,
399 - data: filterNonRootRuleChains(_ruleChains.data), 437 + data: _ruleChains.data,
400 nextPageLink: _ruleChains.nextPageLink, 438 nextPageLink: _ruleChains.nextPageLink,
401 selections: {}, 439 selections: {},
402 selectedCount: 0, 440 selectedCount: 0,
@@ -423,10 +461,6 @@ export default function RuleChainsController(ruleChainService, userService, impo @@ -423,10 +461,6 @@ export default function RuleChainsController(ruleChainService, userService, impo
423 }); 461 });
424 } 462 }
425 463
426 - function filterNonRootRuleChains(ruleChains) {  
427 - return $filter('filter')(ruleChains, isNonRootRuleChain);  
428 - }  
429 -  
430 function unassignFromEdge($event, ruleChain, edgeId) { 464 function unassignFromEdge($event, ruleChain, edgeId) {
431 if ($event) { 465 if ($event) {
432 $event.stopPropagation(); 466 $event.stopPropagation();