Commit efd565d8b607ae384ef925a634d0002d4d79faec
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 | 18 | id varchar(31) NOT NULL CONSTRAINT edge_pkey PRIMARY KEY, |
19 | 19 | additional_info varchar, |
20 | 20 | customer_id varchar(31), |
21 | + root_rule_chain_id varchar(31), | |
21 | 22 | configuration varchar(10000000), |
23 | + type varchar(255), | |
22 | 24 | name varchar(255), |
25 | + label varchar(255), | |
26 | + routing_key varchar(255), | |
27 | + secret varchar(255), | |
23 | 28 | search_text varchar(255), |
24 | 29 | tenant_id varchar(31) |
25 | 30 | ); | ... | ... |
... | ... | @@ -39,6 +39,7 @@ import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; |
39 | 39 | import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; |
40 | 40 | import org.thingsboard.server.common.data.relation.EntityRelation; |
41 | 41 | import org.thingsboard.server.common.data.rule.RuleChain; |
42 | +import org.thingsboard.server.common.data.rule.RuleChainType; | |
42 | 43 | import org.thingsboard.server.common.data.rule.RuleNode; |
43 | 44 | import org.thingsboard.server.common.msg.TbMsg; |
44 | 45 | import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; |
... | ... | @@ -97,17 +98,19 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh |
97 | 98 | if (!started) { |
98 | 99 | RuleChain ruleChain = service.findRuleChainById(tenantId, entityId); |
99 | 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 | 115 | } else { |
113 | 116 | onUpdate(context); |
... | ... | @@ -118,31 +121,35 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh |
118 | 121 | public void onUpdate(ActorContext context) { |
119 | 122 | RuleChain ruleChain = service.findRuleChainById(tenantId, entityId); |
120 | 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 | 139 | RuleChain ruleChain = null; |
140 | 140 | if (msg.getEntityId().getEntityType() == EntityType.RULE_CHAIN) { |
141 | 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 | 143 | log.debug("[{}] Non SYSTEM rule chains are ignored and not started. Current rule chain type [{}]", tenantId, ruleChain.getType()); |
144 | 144 | return; |
145 | 145 | } | ... | ... |
... | ... | @@ -30,6 +30,7 @@ import org.thingsboard.server.common.data.Customer; |
30 | 30 | import org.thingsboard.server.common.data.Device; |
31 | 31 | import org.thingsboard.server.common.data.EntitySubtype; |
32 | 32 | import org.thingsboard.server.common.data.EntityType; |
33 | +import org.thingsboard.server.common.data.Tenant; | |
33 | 34 | import org.thingsboard.server.common.data.audit.ActionType; |
34 | 35 | import org.thingsboard.server.common.data.device.DeviceSearchQuery; |
35 | 36 | import org.thingsboard.server.common.data.edge.Edge; |
... | ... | @@ -37,9 +38,12 @@ import org.thingsboard.server.common.data.edge.EdgeSearchQuery; |
37 | 38 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
38 | 39 | import org.thingsboard.server.common.data.id.CustomerId; |
39 | 40 | import org.thingsboard.server.common.data.id.EdgeId; |
41 | +import org.thingsboard.server.common.data.id.RuleChainId; | |
40 | 42 | import org.thingsboard.server.common.data.id.TenantId; |
41 | 43 | import org.thingsboard.server.common.data.page.TextPageData; |
42 | 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 | 47 | import org.thingsboard.server.dao.exception.IncorrectParameterException; |
44 | 48 | import org.thingsboard.server.dao.model.ModelConstants; |
45 | 49 | import org.thingsboard.server.service.security.model.SecurityUser; |
... | ... | @@ -74,7 +78,8 @@ public class EdgeController extends BaseController { |
74 | 78 | @ResponseBody |
75 | 79 | public Edge saveEdge(@RequestBody Edge edge) throws ThingsboardException { |
76 | 80 | try { |
77 | - edge.setTenantId(getCurrentUser().getTenantId()); | |
81 | + TenantId tenantId = getCurrentUser().getTenantId(); | |
82 | + edge.setTenantId(tenantId); | |
78 | 83 | boolean created = edge.getId() == null; |
79 | 84 | |
80 | 85 | Operation operation = created ? Operation.CREATE : Operation.WRITE; |
... | ... | @@ -84,6 +89,12 @@ public class EdgeController extends BaseController { |
84 | 89 | |
85 | 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 | 98 | logEntityAction(result.getId(), result, null, created ? ActionType.ADDED : ActionType.UPDATED, null); |
88 | 99 | return result; |
89 | 100 | } catch (Exception e) { |
... | ... | @@ -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 | 294 | @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") |
254 | 295 | @RequestMapping(value = "/customer/{customerId}/edges", params = {"limit"}, method = RequestMethod.GET) |
255 | 296 | @ResponseBody | ... | ... |
... | ... | @@ -59,6 +59,7 @@ import org.thingsboard.server.common.data.rule.RuleNode; |
59 | 59 | import org.thingsboard.server.common.msg.TbMsg; |
60 | 60 | import org.thingsboard.server.common.msg.TbMsgMetaData; |
61 | 61 | import org.thingsboard.server.dao.event.EventService; |
62 | +import org.thingsboard.server.dao.exception.DataValidationException; | |
62 | 63 | import org.thingsboard.server.service.script.JsInvokeService; |
63 | 64 | import org.thingsboard.server.service.script.RuleNodeJsScriptEngine; |
64 | 65 | import org.thingsboard.server.service.security.permission.Operation; | ... | ... |
... | ... | @@ -18,10 +18,12 @@ package org.thingsboard.server.dao.edge; |
18 | 18 | import com.google.common.util.concurrent.ListenableFuture; |
19 | 19 | import org.thingsboard.server.common.data.EntitySubtype; |
20 | 20 | import org.thingsboard.server.common.data.Event; |
21 | +import org.thingsboard.server.common.data.Tenant; | |
21 | 22 | import org.thingsboard.server.common.data.edge.Edge; |
22 | 23 | import org.thingsboard.server.common.data.edge.EdgeSearchQuery; |
23 | 24 | import org.thingsboard.server.common.data.id.CustomerId; |
24 | 25 | import org.thingsboard.server.common.data.id.EdgeId; |
26 | +import org.thingsboard.server.common.data.id.RuleChainId; | |
25 | 27 | import org.thingsboard.server.common.data.id.TenantId; |
26 | 28 | import org.thingsboard.server.common.data.page.TextPageData; |
27 | 29 | import org.thingsboard.server.common.data.page.TextPageLink; |
... | ... | @@ -73,6 +75,8 @@ public interface EdgeService { |
73 | 75 | void pushEventToEdge(TenantId tenantId, TbMsg tbMsg); |
74 | 76 | |
75 | 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 | 124 | } |
125 | 125 | |
126 | 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 | 130 | public ShortEdgeInfo getAssignedEdgeInfo(EdgeId edgeId) { | ... | ... |
... | ... | @@ -19,6 +19,7 @@ import lombok.AllArgsConstructor; |
19 | 19 | import lombok.Getter; |
20 | 20 | import lombok.Setter; |
21 | 21 | import org.thingsboard.server.common.data.id.EdgeId; |
22 | +import org.thingsboard.server.common.data.id.RuleChainId; | |
22 | 23 | |
23 | 24 | @AllArgsConstructor |
24 | 25 | public class ShortEdgeInfo { |
... | ... | @@ -29,6 +30,9 @@ public class ShortEdgeInfo { |
29 | 30 | @Getter @Setter |
30 | 31 | private String title; |
31 | 32 | |
33 | + @Getter @Setter | |
34 | + private RuleChainId rootRuleChainId; | |
35 | + | |
32 | 36 | @Override |
33 | 37 | public boolean equals(Object o) { |
34 | 38 | if (this == o) return true; | ... | ... |
... | ... | @@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.ShortCustomerInfo; |
29 | 29 | import org.thingsboard.server.common.data.ShortEdgeInfo; |
30 | 30 | import org.thingsboard.server.common.data.id.CustomerId; |
31 | 31 | import org.thingsboard.server.common.data.id.EdgeId; |
32 | +import org.thingsboard.server.common.data.id.RuleChainId; | |
32 | 33 | import org.thingsboard.server.common.data.id.TenantId; |
33 | 34 | |
34 | 35 | @EqualsAndHashCode(callSuper = true) |
... | ... | @@ -41,6 +42,7 @@ public class Edge extends SearchTextBasedWithAdditionalInfo<EdgeId> implements H |
41 | 42 | |
42 | 43 | private TenantId tenantId; |
43 | 44 | private CustomerId customerId; |
45 | + private RuleChainId rootRuleChainId; | |
44 | 46 | private String name; |
45 | 47 | private String type; |
46 | 48 | private String label; |
... | ... | @@ -60,6 +62,7 @@ public class Edge extends SearchTextBasedWithAdditionalInfo<EdgeId> implements H |
60 | 62 | super(edge); |
61 | 63 | this.tenantId = edge.getTenantId(); |
62 | 64 | this.customerId = edge.getCustomerId(); |
65 | + this.rootRuleChainId = edge.getRootRuleChainId(); | |
63 | 66 | this.type = edge.getType(); |
64 | 67 | this.name = edge.getName(); |
65 | 68 | this.routingKey = edge.getRoutingKey(); |
... | ... | @@ -69,7 +72,7 @@ public class Edge extends SearchTextBasedWithAdditionalInfo<EdgeId> implements H |
69 | 72 | |
70 | 73 | @JsonIgnore |
71 | 74 | public ShortEdgeInfo toShortEdgeInfo() { |
72 | - return new ShortEdgeInfo(id, name); | |
75 | + return new ShortEdgeInfo(id, name, rootRuleChainId); | |
73 | 76 | } |
74 | 77 | |
75 | 78 | @Override | ... | ... |
... | ... | @@ -94,7 +94,7 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo<RuleChainId> im |
94 | 94 | } |
95 | 95 | |
96 | 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 | 100 | public ShortEdgeInfo getAssignedEdgeInfo(EdgeId edgeId) { | ... | ... |
... | ... | @@ -44,6 +44,7 @@ import org.thingsboard.server.common.data.edge.EdgeSearchQuery; |
44 | 44 | import org.thingsboard.server.common.data.id.CustomerId; |
45 | 45 | import org.thingsboard.server.common.data.id.EdgeId; |
46 | 46 | import org.thingsboard.server.common.data.id.EntityId; |
47 | +import org.thingsboard.server.common.data.id.RuleChainId; | |
47 | 48 | import org.thingsboard.server.common.data.id.TenantId; |
48 | 49 | import org.thingsboard.server.common.data.page.TextPageData; |
49 | 50 | import org.thingsboard.server.common.data.page.TextPageLink; |
... | ... | @@ -472,6 +473,14 @@ public class BaseEdgeService extends AbstractEntityService implements EdgeServic |
472 | 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 | 484 | private DataValidator<Edge> edgeValidator = |
476 | 485 | new DataValidator<Edge>() { |
477 | 486 | ... | ... |
... | ... | @@ -359,6 +359,7 @@ public class ModelConstants { |
359 | 359 | public static final String EDGE_COLUMN_FAMILY_NAME = "edge"; |
360 | 360 | public static final String EDGE_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; |
361 | 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 | 363 | public static final String EDGE_NAME_PROPERTY = "name"; |
363 | 364 | public static final String EDGE_LABEL_PROPERTY = "label"; |
364 | 365 | public static final String EDGE_TYPE_PROPERTY = "type"; | ... | ... |
... | ... | @@ -25,6 +25,7 @@ import lombok.Data; |
25 | 25 | import org.thingsboard.server.common.data.edge.Edge; |
26 | 26 | import org.thingsboard.server.common.data.id.CustomerId; |
27 | 27 | import org.thingsboard.server.common.data.id.EdgeId; |
28 | +import org.thingsboard.server.common.data.id.RuleChainId; | |
28 | 29 | import org.thingsboard.server.common.data.id.TenantId; |
29 | 30 | import org.thingsboard.server.dao.model.SearchTextEntity; |
30 | 31 | import org.thingsboard.server.dao.model.type.JsonCodec; |
... | ... | @@ -37,6 +38,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.EDGE_CONFIGURATION |
37 | 38 | import static org.thingsboard.server.dao.model.ModelConstants.EDGE_CUSTOMER_ID_PROPERTY; |
38 | 39 | import static org.thingsboard.server.dao.model.ModelConstants.EDGE_LABEL_PROPERTY; |
39 | 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 | 42 | import static org.thingsboard.server.dao.model.ModelConstants.EDGE_ROUTING_KEY_PROPERTY; |
41 | 43 | import static org.thingsboard.server.dao.model.ModelConstants.EDGE_SECRET_PROPERTY; |
42 | 44 | import static org.thingsboard.server.dao.model.ModelConstants.EDGE_TENANT_ID_PROPERTY; |
... | ... | @@ -60,6 +62,9 @@ public class EdgeEntity implements SearchTextEntity<Edge> { |
60 | 62 | @Column(name = EDGE_CUSTOMER_ID_PROPERTY) |
61 | 63 | private UUID customerId; |
62 | 64 | |
65 | + @Column(name = EDGE_ROOT_RULE_CHAIN_ID_PROPERTY) | |
66 | + private UUID rootRuleChainId; | |
67 | + | |
63 | 68 | @Column(name = EDGE_TYPE_PROPERTY) |
64 | 69 | private String type; |
65 | 70 | |
... | ... | @@ -95,6 +100,12 @@ public class EdgeEntity implements SearchTextEntity<Edge> { |
95 | 100 | if (edge.getTenantId() != null) { |
96 | 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 | 109 | this.type = edge.getType(); |
99 | 110 | this.name = edge.getName(); |
100 | 111 | this.label = edge.getLabel(); |
... | ... | @@ -119,6 +130,9 @@ public class EdgeEntity implements SearchTextEntity<Edge> { |
119 | 130 | if (customerId != null) { |
120 | 131 | edge.setCustomerId(new CustomerId(customerId)); |
121 | 132 | } |
133 | + if (rootRuleChainId != null) { | |
134 | + edge.setRootRuleChainId(new RuleChainId(rootRuleChainId)); | |
135 | + } | |
122 | 136 | edge.setType(type); |
123 | 137 | edge.setName(name); |
124 | 138 | edge.setLabel(label); | ... | ... |
... | ... | @@ -5,7 +5,7 @@ |
5 | 5 | * you may not use this file except in compliance with the License. |
6 | 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 | 10 | * Unless required by applicable law or agreed to in writing, software |
11 | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
... | ... | @@ -25,6 +25,7 @@ import org.thingsboard.server.common.data.UUIDConverter; |
25 | 25 | import org.thingsboard.server.common.data.edge.Edge; |
26 | 26 | import org.thingsboard.server.common.data.id.CustomerId; |
27 | 27 | import org.thingsboard.server.common.data.id.EdgeId; |
28 | +import org.thingsboard.server.common.data.id.RuleChainId; | |
28 | 29 | import org.thingsboard.server.common.data.id.TenantId; |
29 | 30 | import org.thingsboard.server.dao.model.BaseSqlEntity; |
30 | 31 | import org.thingsboard.server.dao.model.ModelConstants; |
... | ... | @@ -39,6 +40,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.EDGE_COLUMN_FAMILY |
39 | 40 | import static org.thingsboard.server.dao.model.ModelConstants.EDGE_CUSTOMER_ID_PROPERTY; |
40 | 41 | import static org.thingsboard.server.dao.model.ModelConstants.EDGE_LABEL_PROPERTY; |
41 | 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 | 44 | import static org.thingsboard.server.dao.model.ModelConstants.EDGE_ROUTING_KEY_PROPERTY; |
43 | 45 | import static org.thingsboard.server.dao.model.ModelConstants.EDGE_SECRET_PROPERTY; |
44 | 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 | 60 | @Column(name = EDGE_CUSTOMER_ID_PROPERTY) |
59 | 61 | private String customerId; |
60 | 62 | |
63 | + @Column(name = EDGE_ROOT_RULE_CHAIN_ID_PROPERTY) | |
64 | + private String rootRuleChainId; | |
65 | + | |
61 | 66 | @Column(name = EDGE_TYPE_PROPERTY) |
62 | 67 | private String type; |
63 | 68 | |
... | ... | @@ -98,6 +103,9 @@ public class EdgeEntity extends BaseSqlEntity<Edge> implements SearchTextEntity< |
98 | 103 | if (edge.getCustomerId() != null) { |
99 | 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 | 109 | this.type = edge.getType(); |
102 | 110 | this.name = edge.getName(); |
103 | 111 | this.label = edge.getLabel(); |
... | ... | @@ -131,6 +139,9 @@ public class EdgeEntity extends BaseSqlEntity<Edge> implements SearchTextEntity< |
131 | 139 | if (customerId != null) { |
132 | 140 | edge.setCustomerId(new CustomerId(UUIDConverter.fromString(customerId))); |
133 | 141 | } |
142 | + if (rootRuleChainId != null) { | |
143 | + edge.setRootRuleChainId(new RuleChainId(UUIDConverter.fromString(rootRuleChainId))); | |
144 | + } | |
134 | 145 | edge.setType(type); |
135 | 146 | edge.setName(name); |
136 | 147 | edge.setLabel(label); | ... | ... |
... | ... | @@ -25,6 +25,7 @@ import org.springframework.beans.factory.annotation.Autowired; |
25 | 25 | import org.springframework.stereotype.Service; |
26 | 26 | import org.thingsboard.server.common.data.BaseData; |
27 | 27 | import org.thingsboard.server.common.data.EntityType; |
28 | +import org.thingsboard.server.common.data.ShortEdgeInfo; | |
28 | 29 | import org.thingsboard.server.common.data.Tenant; |
29 | 30 | import org.thingsboard.server.common.data.edge.Edge; |
30 | 31 | import org.thingsboard.server.common.data.id.EdgeId; |
... | ... | @@ -42,6 +43,7 @@ import org.thingsboard.server.common.data.rule.NodeConnectionInfo; |
42 | 43 | import org.thingsboard.server.common.data.rule.RuleChain; |
43 | 44 | import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo; |
44 | 45 | import org.thingsboard.server.common.data.rule.RuleChainMetaData; |
46 | +import org.thingsboard.server.common.data.rule.RuleChainType; | |
45 | 47 | import org.thingsboard.server.common.data.rule.RuleNode; |
46 | 48 | import org.thingsboard.server.dao.edge.EdgeDao; |
47 | 49 | import org.thingsboard.server.dao.edge.EdgeService; |
... | ... | @@ -116,6 +118,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC |
116 | 118 | createRelation(tenantId, new EntityRelation(ruleChain.getTenantId(), ruleChain.getId(), |
117 | 119 | EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN)); |
118 | 120 | ruleChain.setRoot(true); |
121 | + ruleChain.setType(RuleChainType.SYSTEM); | |
119 | 122 | ruleChainDao.save(tenantId, ruleChain); |
120 | 123 | return true; |
121 | 124 | } catch (ExecutionException | InterruptedException e) { |
... | ... | @@ -359,8 +362,17 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC |
359 | 362 | public void deleteRuleChainById(TenantId tenantId, RuleChainId ruleChainId) { |
360 | 363 | Validator.validateId(ruleChainId, "Incorrect rule chain id for delete request."); |
361 | 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 | 377 | checkRuleNodesAndDelete(tenantId, ruleChainId); |
366 | 378 | } |
... | ... | @@ -398,13 +410,16 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC |
398 | 410 | RuleChain ruleChain = findRuleChainById(tenantId, ruleChainId); |
399 | 411 | Edge edge = edgeDao.findById(tenantId, edgeId.getId()); |
400 | 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 | 418 | if (ruleChain.removeAssignedEdge(edge)) { |
404 | 419 | try { |
405 | 420 | deleteRelation(tenantId, new EntityRelation(edgeId, ruleChainId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE)); |
406 | 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 | 423 | throw new RuntimeException(e); |
409 | 424 | } |
410 | 425 | return saveRuleChain(ruleChain); |
... | ... | @@ -442,13 +457,13 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC |
442 | 457 | Validator.validateId(tenantId, "Incorrect tenantId " + tenantId); |
443 | 458 | Validator.validateId(edgeId, "Incorrect customerId " + edgeId); |
444 | 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 | 463 | @Nullable |
449 | 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 | } | ... | ... |
... | ... | @@ -254,6 +254,7 @@ CREATE TABLE IF NOT EXISTS edge ( |
254 | 254 | id varchar(31) NOT NULL CONSTRAINT edge_pkey PRIMARY KEY, |
255 | 255 | additional_info varchar, |
256 | 256 | customer_id varchar(31), |
257 | + root_rule_chain_id varchar(31), | |
257 | 258 | configuration varchar(10000000), |
258 | 259 | type varchar(255), |
259 | 260 | name varchar(255), | ... | ... |
... | ... | @@ -31,7 +31,8 @@ function EdgeService($http, $q, customerService) { |
31 | 31 | getCustomerEdges: getCustomerEdges, |
32 | 32 | assignEdgeToCustomer: assignEdgeToCustomer, |
33 | 33 | unassignEdgeFromCustomer: unassignEdgeFromCustomer, |
34 | - makeEdgePublic: makeEdgePublic | |
34 | + makeEdgePublic: makeEdgePublic, | |
35 | + setRootRuleChain: setRootRuleChain | |
35 | 36 | }; |
36 | 37 | |
37 | 38 | return service; |
... | ... | @@ -229,4 +230,15 @@ function EdgeService($http, $q, customerService) { |
229 | 230 | }); |
230 | 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 | 19 | import edgeCard from './edge-card.tpl.html'; |
20 | 20 | import assignToCustomerTemplate from './assign-to-customer.tpl.html'; |
21 | 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 | 24 | /* eslint-enable import/no-unresolved, import/default */ |
24 | 25 | |
... | ... | @@ -47,8 +48,8 @@ export function EdgeCardController(types) { |
47 | 48 | |
48 | 49 | |
49 | 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 | 54 | var customerId = $stateParams.customerId; |
54 | 55 | |
... | ... | @@ -295,6 +296,19 @@ export function EdgeController($rootScope, userService, edgeService, customerSer |
295 | 296 | edgeGroupActionsList.push( |
296 | 297 | { |
297 | 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 | 312 | assignEdgesToCustomer($event, items); |
299 | 313 | }, |
300 | 314 | name: function() { return $translate.instant('edge.assign-edges') }, |
... | ... | @@ -305,8 +319,6 @@ export function EdgeController($rootScope, userService, edgeService, customerSer |
305 | 319 | } |
306 | 320 | ); |
307 | 321 | |
308 | - | |
309 | - | |
310 | 322 | edgeGroupActionsList.push( |
311 | 323 | { |
312 | 324 | onAction: function ($event) { |
... | ... | @@ -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 | 334 | fetchEdgesFunction = function (pageLink, edgeType) { |
325 | 335 | return edgeService.getCustomerEdges(customerId, pageLink, true, null, edgeType); |
326 | 336 | }; |
... | ... | @@ -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 | 581 | function assignEdgesToCustomer($event, items) { |
528 | 582 | var edgeIds = []; |
529 | 583 | for (var id in items.selections) { | ... | ... |
... | ... | @@ -23,6 +23,7 @@ import EdgeRoutes from './edge.routes'; |
23 | 23 | import {EdgeController, EdgeCardController} from './edge.controller'; |
24 | 24 | import AssignEdgeToCustomerController from './assign-to-customer.controller'; |
25 | 25 | import AddEdgesToCustomerController from './add-edges-to-customer.controller'; |
26 | +import SetRootRuleChainToEdgesController from './set-root-rule-chain-to-edges.controller'; | |
26 | 27 | import EdgeDirective from './edge.directive'; |
27 | 28 | |
28 | 29 | export default angular.module('thingsboard.edge', [ |
... | ... | @@ -37,5 +38,6 @@ export default angular.module('thingsboard.edge', [ |
37 | 38 | .controller('EdgeCardController', EdgeCardController) |
38 | 39 | .controller('AssignEdgeToCustomerController', AssignEdgeToCustomerController) |
39 | 40 | .controller('AddEdgesToCustomerController', AddEdgesToCustomerController) |
41 | + .controller('SetRootRuleChainToEdgesController', SetRootRuleChainToEdgesController) | |
40 | 42 | .directive('tbEdge', EdgeDirective) |
41 | 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> </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> | |
\ No newline at end of file | ... | ... |
... | ... | @@ -792,6 +792,7 @@ |
792 | 792 | "dashboards": "Edge Dashboards", |
793 | 793 | "manage-edge-rulechains": "Manage edge rule chains", |
794 | 794 | "rulechains": "Edge Rule Chains", |
795 | + "rulechain": "Edge Rule Chain", | |
795 | 796 | "edge-key": "Edge key", |
796 | 797 | "copy-edge-key": "Copy edge key", |
797 | 798 | "edge-key-copied-message": "Edge key has been copied to clipboard", |
... | ... | @@ -803,7 +804,10 @@ |
803 | 804 | "manage-edge-entity-views": "Manage edge entity views", |
804 | 805 | "assets": "Edge assets", |
805 | 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 | 812 | "error": { |
809 | 813 | "unable-to-connect": "Unable to connect to the server! Please check your internet connection.", | ... | ... |
... | ... | @@ -31,7 +31,7 @@ |
31 | 31 | <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span> |
32 | 32 | <md-dialog-content> |
33 | 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 | 35 | </div> |
36 | 36 | </md-dialog-content> |
37 | 37 | <md-dialog-actions layout="row"> | ... | ... |
... | ... | @@ -54,7 +54,7 @@ export default function AddRuleChainsToEdgeController(ruleChainService, $mdDialo |
54 | 54 | vm.ruleChains.pending = true; |
55 | 55 | ruleChainService.getRuleChains(vm.ruleChains.nextPageLink).then( |
56 | 56 | function success(ruleChains) { |
57 | - vm.ruleChains.data = vm.ruleChains.data.concat(filterNonRootRuleChains(ruleChains.data)); | |
57 | + vm.ruleChains.data = ruleChains.data; | |
58 | 58 | vm.ruleChains.nextPageLink = ruleChains.nextPageLink; |
59 | 59 | vm.ruleChains.hasNext = ruleChains.hasNext; |
60 | 60 | if (vm.ruleChains.hasNext) { |
... | ... | @@ -119,12 +119,4 @@ export default function AddRuleChainsToEdgeController(ruleChainService, $mdDialo |
119 | 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 | } |
\ No newline at end of file | ... | ... |
... | ... | @@ -16,4 +16,4 @@ |
16 | 16 | |
17 | 17 | --> |
18 | 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 | 51 | </md-input-container> |
52 | 52 | <md-input-container class="md-block"> |
53 | 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 | 55 | <md-option ng-repeat="ruleChainType in ruleChainTypes" value="{{ruleChainType}}"> |
56 | 56 | {{ruleChainType}} |
57 | 57 | </md-option> | ... | ... |
... | ... | @@ -145,5 +145,43 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider |
145 | 145 | ncyBreadcrumb: { |
146 | 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 | } |
\ No newline at end of file | ... | ... |
... | ... | @@ -25,7 +25,7 @@ import addRuleChainsToEdgeTemplate from "./add-rulechains-to-edge.tpl.html"; |
25 | 25 | import './rulechain-card.scss'; |
26 | 26 | |
27 | 27 | /*@ngInject*/ |
28 | -export default function RuleChainsController(ruleChainService, userService, importExport, $state, | |
28 | +export default function RuleChainsController(ruleChainService, userService, edgeService, importExport, $state, | |
29 | 29 | $stateParams, $filter, $translate, $mdDialog, $document, $q, types) { |
30 | 30 | |
31 | 31 | var vm = this; |
... | ... | @@ -107,6 +107,11 @@ export default function RuleChainsController(ruleChainService, userService, impo |
107 | 107 | |
108 | 108 | if (edgeId) { |
109 | 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 | 117 | if (vm.ruleChainsScope === 'tenant') { |
... | ... | @@ -133,8 +138,7 @@ export default function RuleChainsController(ruleChainService, userService, impo |
133 | 138 | }, |
134 | 139 | name: function() { return $translate.instant('action.assign') }, |
135 | 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 | 144 | ruleChainActionsList.push({ |
... | ... | @@ -214,6 +218,16 @@ export default function RuleChainsController(ruleChainService, userService, impo |
214 | 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 | 231 | ruleChainActionsList.push( |
218 | 232 | { |
219 | 233 | onAction: function ($event, item) { |
... | ... | @@ -221,7 +235,8 @@ export default function RuleChainsController(ruleChainService, userService, impo |
221 | 235 | }, |
222 | 236 | name: function() { return $translate.instant('action.unassign') }, |
223 | 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 | 303 | if ($event) { |
289 | 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 | 315 | function deleteRuleChain(ruleChainId) { |
... | ... | @@ -300,11 +321,19 @@ export default function RuleChainsController(ruleChainService, userService, impo |
300 | 321 | } |
301 | 322 | |
302 | 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 | 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 | 339 | function exportRuleChain($event, ruleChain) { |
... | ... | @@ -322,11 +351,20 @@ export default function RuleChainsController(ruleChainService, userService, impo |
322 | 351 | .cancel($translate.instant('action.no')) |
323 | 352 | .ok($translate.instant('action.yes')); |
324 | 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 | 434 | function success(_ruleChains) { |
397 | 435 | var ruleChains = { |
398 | 436 | pageSize: pageSize, |
399 | - data: filterNonRootRuleChains(_ruleChains.data), | |
437 | + data: _ruleChains.data, | |
400 | 438 | nextPageLink: _ruleChains.nextPageLink, |
401 | 439 | selections: {}, |
402 | 440 | selectedCount: 0, |
... | ... | @@ -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 | 464 | function unassignFromEdge($event, ruleChain, edgeId) { |
431 | 465 | if ($event) { |
432 | 466 | $event.stopPropagation(); | ... | ... |