Browse Source

Added root rule chain for the edge. Part 2

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -359,6 +359,7 @@ public class ModelConstants {
public static final String EDGE_COLUMN_FAMILY_NAME = "edge";
public static final String EDGE_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY;
public static final String EDGE_CUSTOMER_ID_PROPERTY = CUSTOMER_ID_PROPERTY;
public static final String EDGE_ROOT_RULE_CHAIN_ID_PROPERTY = "root_rule_chain_id";
public static final String EDGE_NAME_PROPERTY = "name";
public static final String EDGE_LABEL_PROPERTY = "label";
public static final String EDGE_TYPE_PROPERTY = "type";

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Loading…
Cancel
Save