Commit c409e2d9c1d05231c849649be479b8a20abd0ee0

Authored by Volodymyr Babak
1 parent 0123a352

Rule chains refactoring:

Showing 29 changed files with 365 additions and 85 deletions
@@ -55,6 +55,7 @@ import org.thingsboard.server.common.data.page.TimePageLink; @@ -55,6 +55,7 @@ import org.thingsboard.server.common.data.page.TimePageLink;
55 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; 55 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
56 import org.thingsboard.server.common.data.rule.RuleChain; 56 import org.thingsboard.server.common.data.rule.RuleChain;
57 import org.thingsboard.server.common.data.rule.RuleChainMetaData; 57 import org.thingsboard.server.common.data.rule.RuleChainMetaData;
  58 +import org.thingsboard.server.common.data.rule.RuleChainType;
58 import org.thingsboard.server.common.data.rule.RuleNode; 59 import org.thingsboard.server.common.data.rule.RuleNode;
59 import org.thingsboard.server.common.msg.TbMsg; 60 import org.thingsboard.server.common.msg.TbMsg;
60 import org.thingsboard.server.common.msg.TbMsgMetaData; 61 import org.thingsboard.server.common.msg.TbMsgMetaData;
@@ -231,13 +232,19 @@ public class RuleChainController extends BaseController { @@ -231,13 +232,19 @@ public class RuleChainController extends BaseController {
231 @ResponseBody 232 @ResponseBody
232 public TextPageData<RuleChain> getRuleChains( 233 public TextPageData<RuleChain> getRuleChains(
233 @RequestParam int limit, 234 @RequestParam int limit,
  235 + @RequestParam(value = "type", required = false) String typeStr,
234 @RequestParam(required = false) String textSearch, 236 @RequestParam(required = false) String textSearch,
235 @RequestParam(required = false) String idOffset, 237 @RequestParam(required = false) String idOffset,
236 @RequestParam(required = false) String textOffset) throws ThingsboardException { 238 @RequestParam(required = false) String textOffset) throws ThingsboardException {
237 try { 239 try {
238 TenantId tenantId = getCurrentUser().getTenantId(); 240 TenantId tenantId = getCurrentUser().getTenantId();
239 TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); 241 TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
240 - return checkNotNull(ruleChainService.findTenantRuleChains(tenantId, pageLink)); 242 + if (typeStr != null && typeStr.trim().length() > 0) {
  243 + RuleChainType type = RuleChainType.valueOf(typeStr);
  244 + return checkNotNull(ruleChainService.findTenantRuleChainsByType(tenantId, type, pageLink));
  245 + } else {
  246 + return checkNotNull(ruleChainService.findTenantRuleChains(tenantId, pageLink));
  247 + }
241 } catch (Exception e) { 248 } catch (Exception e) {
242 throw handleException(e); 249 throw handleException(e);
243 } 250 }
@@ -37,6 +37,7 @@ import org.thingsboard.server.common.data.alarm.Alarm; @@ -37,6 +37,7 @@ import org.thingsboard.server.common.data.alarm.Alarm;
37 import org.thingsboard.server.common.data.alarm.AlarmSeverity; 37 import org.thingsboard.server.common.data.alarm.AlarmSeverity;
38 import org.thingsboard.server.common.data.alarm.AlarmStatus; 38 import org.thingsboard.server.common.data.alarm.AlarmStatus;
39 import org.thingsboard.server.common.data.asset.Asset; 39 import org.thingsboard.server.common.data.asset.Asset;
  40 +import org.thingsboard.server.common.data.audit.ActionType;
40 import org.thingsboard.server.common.data.edge.Edge; 41 import org.thingsboard.server.common.data.edge.Edge;
41 import org.thingsboard.server.common.data.edge.EdgeQueueEntry; 42 import org.thingsboard.server.common.data.edge.EdgeQueueEntry;
42 import org.thingsboard.server.common.data.id.AssetId; 43 import org.thingsboard.server.common.data.id.AssetId;
@@ -44,10 +45,12 @@ import org.thingsboard.server.common.data.id.CustomerId; @@ -44,10 +45,12 @@ import org.thingsboard.server.common.data.id.CustomerId;
44 import org.thingsboard.server.common.data.id.DeviceId; 45 import org.thingsboard.server.common.data.id.DeviceId;
45 import org.thingsboard.server.common.data.id.EdgeId; 46 import org.thingsboard.server.common.data.id.EdgeId;
46 import org.thingsboard.server.common.data.id.EntityId; 47 import org.thingsboard.server.common.data.id.EntityId;
  48 +import org.thingsboard.server.common.data.id.EntityIdFactory;
47 import org.thingsboard.server.common.data.id.EntityViewId; 49 import org.thingsboard.server.common.data.id.EntityViewId;
48 import org.thingsboard.server.common.data.id.TenantId; 50 import org.thingsboard.server.common.data.id.TenantId;
49 import org.thingsboard.server.common.data.kv.AttributeKvEntry; 51 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
50 import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; 52 import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
  53 +import org.thingsboard.server.common.data.kv.DataType;
51 import org.thingsboard.server.common.data.kv.LongDataEntry; 54 import org.thingsboard.server.common.data.kv.LongDataEntry;
52 import org.thingsboard.server.common.data.page.TimePageData; 55 import org.thingsboard.server.common.data.page.TimePageData;
53 import org.thingsboard.server.common.data.page.TimePageLink; 56 import org.thingsboard.server.common.data.page.TimePageLink;
@@ -59,6 +62,7 @@ import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo; @@ -59,6 +62,7 @@ import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo;
59 import org.thingsboard.server.common.data.rule.RuleChainMetaData; 62 import org.thingsboard.server.common.data.rule.RuleChainMetaData;
60 import org.thingsboard.server.common.data.rule.RuleNode; 63 import org.thingsboard.server.common.data.rule.RuleNode;
61 import org.thingsboard.server.common.msg.TbMsg; 64 import org.thingsboard.server.common.msg.TbMsg;
  65 +import org.thingsboard.server.common.msg.TbMsgDataType;
62 import org.thingsboard.server.common.msg.TbMsgMetaData; 66 import org.thingsboard.server.common.msg.TbMsgMetaData;
63 import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; 67 import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;
64 import org.thingsboard.server.common.msg.session.SessionMsgType; 68 import org.thingsboard.server.common.msg.session.SessionMsgType;
@@ -91,6 +95,7 @@ import org.thingsboard.server.gen.edge.UplinkResponseMsg; @@ -91,6 +95,7 @@ import org.thingsboard.server.gen.edge.UplinkResponseMsg;
91 import org.thingsboard.server.gen.edge.UserUpdateMsg; 95 import org.thingsboard.server.gen.edge.UserUpdateMsg;
92 import org.thingsboard.server.service.edge.EdgeContextComponent; 96 import org.thingsboard.server.service.edge.EdgeContextComponent;
93 97
  98 +import javax.swing.text.html.parser.Entity;
94 import java.io.IOException; 99 import java.io.IOException;
95 import java.util.ArrayList; 100 import java.util.ArrayList;
96 import java.util.Collections; 101 import java.util.Collections;
@@ -102,6 +107,9 @@ import java.util.concurrent.locks.ReentrantLock; @@ -102,6 +107,9 @@ import java.util.concurrent.locks.ReentrantLock;
102 import java.util.function.BiConsumer; 107 import java.util.function.BiConsumer;
103 import java.util.function.Consumer; 108 import java.util.function.Consumer;
104 109
  110 +import static org.thingsboard.server.gen.edge.UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE;
  111 +import static org.thingsboard.server.gen.edge.UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE;
  112 +
105 @Slf4j 113 @Slf4j
106 @Data 114 @Data
107 public final class EdgeGrpcSession implements Cloneable { 115 public final class EdgeGrpcSession implements Cloneable {
@@ -175,7 +183,9 @@ public final class EdgeGrpcSession implements Cloneable { @@ -175,7 +183,9 @@ public final class EdgeGrpcSession implements Cloneable {
175 do { 183 do {
176 pageData = ctx.getEdgeService().findQueueEvents(edge.getTenantId(), edge.getId(), pageLink); 184 pageData = ctx.getEdgeService().findQueueEvents(edge.getTenantId(), edge.getId(), pageLink);
177 if (!pageData.getData().isEmpty()) { 185 if (!pageData.getData().isEmpty()) {
  186 + log.trace("[{}] [{}] event(s) are going to be processed.", this.sessionId, pageData.getData().size());
178 for (Event event : pageData.getData()) { 187 for (Event event : pageData.getData()) {
  188 + log.trace("[{}] Processing event [{}]", this.sessionId, event);
179 EdgeQueueEntry entry; 189 EdgeQueueEntry entry;
180 try { 190 try {
181 entry = objectMapper.treeToValue(event.getBody(), EdgeQueueEntry.class); 191 entry = objectMapper.treeToValue(event.getBody(), EdgeQueueEntry.class);
@@ -193,6 +203,9 @@ public final class EdgeGrpcSession implements Cloneable { @@ -193,6 +203,9 @@ public final class EdgeGrpcSession implements Cloneable {
193 processCustomDownlinkMessage(entry); 203 processCustomDownlinkMessage(entry);
194 break; 204 break;
195 } 205 }
  206 + if (ENTITY_CREATED_RPC_MESSAGE.equals(msgType)) {
  207 + pushEntityAttributesToEdge(entry);
  208 + }
196 } catch (Exception e) { 209 } catch (Exception e) {
197 log.error("Exception during processing records from queue", e); 210 log.error("Exception during processing records from queue", e);
198 } 211 }
@@ -215,6 +228,63 @@ public final class EdgeGrpcSession implements Cloneable { @@ -215,6 +228,63 @@ public final class EdgeGrpcSession implements Cloneable {
215 } 228 }
216 } 229 }
217 230
  231 + private void pushEntityAttributesToEdge(EdgeQueueEntry entry) throws IOException {
  232 + EntityId entityId = null;
  233 + String entityName = null;
  234 + switch (entry.getEntityType()) {
  235 + case EDGE:
  236 + entityId = objectMapper.readValue(entry.getData(), Edge.class).getId();
  237 + break;
  238 + case DEVICE:
  239 + entityId = objectMapper.readValue(entry.getData(), Device.class).getId();
  240 + break;
  241 + case ASSET:
  242 + entityId = objectMapper.readValue(entry.getData(), Asset.class).getId();
  243 + break;
  244 + case ENTITY_VIEW:
  245 + entityId = objectMapper.readValue(entry.getData(), EntityView.class).getId();
  246 + break;
  247 + case DASHBOARD:
  248 + entityId = objectMapper.readValue(entry.getData(), Dashboard.class).getId();
  249 + break;
  250 + }
  251 + if (entityId != null) {
  252 + ListenableFuture<List<AttributeKvEntry>> ssAttrFuture = ctx.getAttributesService().findAll(edge.getTenantId(), entityId, DataConstants.SERVER_SCOPE);
  253 + Futures.transform(ssAttrFuture, ssAttributes -> {
  254 + if (ssAttributes != null && !ssAttributes.isEmpty()) {
  255 + try {
  256 + TbMsgMetaData metaData = new TbMsgMetaData();
  257 + ObjectNode entityNode = objectMapper.createObjectNode();
  258 + metaData.putValue("scope", DataConstants.SERVER_SCOPE);
  259 + for (AttributeKvEntry attr : ssAttributes) {
  260 + if (attr.getDataType() == DataType.BOOLEAN) {
  261 + entityNode.put(attr.getKey(), attr.getBooleanValue().get());
  262 + } else if (attr.getDataType() == DataType.DOUBLE) {
  263 + entityNode.put(attr.getKey(), attr.getDoubleValue().get());
  264 + } else if (attr.getDataType() == DataType.LONG) {
  265 + entityNode.put(attr.getKey(), attr.getLongValue().get());
  266 + } else {
  267 + entityNode.put(attr.getKey(), attr.getValueAsString());
  268 + }
  269 + }
  270 + TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), DataConstants.ATTRIBUTES_UPDATED, entityId, metaData, TbMsgDataType.JSON
  271 + , objectMapper.writeValueAsString(entityNode)
  272 + , null, null, 0L);
  273 + log.debug("Sending donwlink entity data msg, entityName [{}], tbMsg [{}]", entityName, tbMsg);
  274 + outputStream.onNext(ResponseMsg.newBuilder()
  275 + .setDownlinkMsg(constructDownlinkEntityDataMsg(entityName, tbMsg))
  276 + .build());
  277 + } catch (Exception e) {
  278 + log.error("[{}] Failed to send attribute updates to the edge", edge.getName(), e);
  279 + }
  280 + }
  281 + return null;
  282 + });
  283 + ListenableFuture<List<AttributeKvEntry>> shAttrFuture = ctx.getAttributesService().findAll(edge.getTenantId(), entityId, DataConstants.SHARED_SCOPE);
  284 + ListenableFuture<List<AttributeKvEntry>> clAttrFuture = ctx.getAttributesService().findAll(edge.getTenantId(), entityId, DataConstants.CLIENT_SCOPE);
  285 + }
  286 + }
  287 +
218 private void processCustomDownlinkMessage(EdgeQueueEntry entry) throws IOException { 288 private void processCustomDownlinkMessage(EdgeQueueEntry entry) throws IOException {
219 log.trace("Executing processCustomDownlinkMessage, entry [{}]", entry); 289 log.trace("Executing processCustomDownlinkMessage, entry [{}]", entry);
220 TbMsg tbMsg = objectMapper.readValue(entry.getData(), TbMsg.class); 290 TbMsg tbMsg = objectMapper.readValue(entry.getData(), TbMsg.class);
@@ -411,7 +481,7 @@ public final class EdgeGrpcSession implements Cloneable { @@ -411,7 +481,7 @@ public final class EdgeGrpcSession implements Cloneable {
411 return UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE; 481 return UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE;
412 case DataConstants.ENTITY_CREATED: 482 case DataConstants.ENTITY_CREATED:
413 case DataConstants.ENTITY_ASSIGNED_TO_EDGE: 483 case DataConstants.ENTITY_ASSIGNED_TO_EDGE:
414 - return UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE; 484 + return ENTITY_CREATED_RPC_MESSAGE;
415 case DataConstants.ENTITY_DELETED: 485 case DataConstants.ENTITY_DELETED:
416 case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE: 486 case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE:
417 return UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE; 487 return UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE;
@@ -521,6 +591,8 @@ public final class EdgeGrpcSession implements Cloneable { @@ -521,6 +591,8 @@ public final class EdgeGrpcSession implements Cloneable {
521 591
522 private RuleNodeProto constructNode(RuleNode node) throws JsonProcessingException { 592 private RuleNodeProto constructNode(RuleNode node) throws JsonProcessingException {
523 return RuleNodeProto.newBuilder() 593 return RuleNodeProto.newBuilder()
  594 + .setIdMSB(node.getId().getId().getMostSignificantBits())
  595 + .setIdLSB(node.getId().getId().getLeastSignificantBits())
524 .setType(node.getType()) 596 .setType(node.getType())
525 .setName(node.getName()) 597 .setName(node.getName())
526 .setDebugMode(node.isDebugMode()) 598 .setDebugMode(node.isDebugMode())
@@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.page.TimePageLink; @@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.page.TimePageLink;
27 import org.thingsboard.server.common.data.relation.EntityRelation; 27 import org.thingsboard.server.common.data.relation.EntityRelation;
28 import org.thingsboard.server.common.data.rule.RuleChain; 28 import org.thingsboard.server.common.data.rule.RuleChain;
29 import org.thingsboard.server.common.data.rule.RuleChainMetaData; 29 import org.thingsboard.server.common.data.rule.RuleChainMetaData;
  30 +import org.thingsboard.server.common.data.rule.RuleChainType;
30 import org.thingsboard.server.common.data.rule.RuleNode; 31 import org.thingsboard.server.common.data.rule.RuleNode;
31 32
32 import java.util.List; 33 import java.util.List;
@@ -62,6 +63,8 @@ public interface RuleChainService { @@ -62,6 +63,8 @@ public interface RuleChainService {
62 63
63 TextPageData<RuleChain> findTenantRuleChains(TenantId tenantId, TextPageLink pageLink); 64 TextPageData<RuleChain> findTenantRuleChains(TenantId tenantId, TextPageLink pageLink);
64 65
  66 + TextPageData<RuleChain> findTenantRuleChainsByType(TenantId tenantId, RuleChainType type, TextPageLink pageLink);
  67 +
65 void deleteRuleChainById(TenantId tenantId, RuleChainId ruleChainId); 68 void deleteRuleChainById(TenantId tenantId, RuleChainId ruleChainId);
66 69
67 void deleteRuleChainsByTenantId(TenantId tenantId); 70 void deleteRuleChainsByTenantId(TenantId tenantId);
@@ -125,11 +125,13 @@ message RuleChainMetadataUpdateMsg { @@ -125,11 +125,13 @@ message RuleChainMetadataUpdateMsg {
125 } 125 }
126 126
127 message RuleNodeProto { 127 message RuleNodeProto {
128 - string type = 1;  
129 - string name = 2;  
130 - bool debugMode = 3;  
131 - string configuration = 4;  
132 - string additionalInfo = 5; 128 + int64 idMSB = 1;
  129 + int64 idLSB = 2;
  130 + string type = 3;
  131 + string name = 4;
  132 + bool debugMode = 5;
  133 + string configuration = 6;
  134 + string additionalInfo = 7;
133 } 135 }
134 136
135 message NodeConnectionInfoProto { 137 message NodeConnectionInfoProto {
@@ -113,16 +113,16 @@ public class BaseComponentDescriptorService implements ComponentDescriptorServic @@ -113,16 +113,16 @@ public class BaseComponentDescriptorService implements ComponentDescriptorServic
113 @Override 113 @Override
114 protected void validateDataImpl(TenantId tenantId, ComponentDescriptor plugin) { 114 protected void validateDataImpl(TenantId tenantId, ComponentDescriptor plugin) {
115 if (plugin.getType() == null) { 115 if (plugin.getType() == null) {
116 - throw new DataValidationException("Component type should be specified!."); 116 + throw new DataValidationException("Component type should be specified!");
117 } 117 }
118 if (plugin.getScope() == null) { 118 if (plugin.getScope() == null) {
119 - throw new DataValidationException("Component scope should be specified!."); 119 + throw new DataValidationException("Component scope should be specified!");
120 } 120 }
121 if (StringUtils.isEmpty(plugin.getName())) { 121 if (StringUtils.isEmpty(plugin.getName())) {
122 - throw new DataValidationException("Component name should be specified!."); 122 + throw new DataValidationException("Component name should be specified!");
123 } 123 }
124 if (StringUtils.isEmpty(plugin.getClazz())) { 124 if (StringUtils.isEmpty(plugin.getClazz())) {
125 - throw new DataValidationException("Component clazz should be specified!."); 125 + throw new DataValidationException("Component clazz should be specified!");
126 } 126 }
127 } 127 }
128 }; 128 };
@@ -54,7 +54,7 @@ public class BaseEventService implements EventService { @@ -54,7 +54,7 @@ public class BaseEventService implements EventService {
54 public Optional<Event> saveIfNotExists(Event event) { 54 public Optional<Event> saveIfNotExists(Event event) {
55 eventValidator.validate(event, Event::getTenantId); 55 eventValidator.validate(event, Event::getTenantId);
56 if (StringUtils.isEmpty(event.getUid())) { 56 if (StringUtils.isEmpty(event.getUid())) {
57 - throw new DataValidationException("Event uid should be specified!."); 57 + throw new DataValidationException("Event uid should be specified!");
58 } 58 }
59 return eventDao.saveIfNotExists(event); 59 return eventDao.saveIfNotExists(event);
60 } 60 }
@@ -62,16 +62,16 @@ public class BaseEventService implements EventService { @@ -62,16 +62,16 @@ public class BaseEventService implements EventService {
62 @Override 62 @Override
63 public Optional<Event> findEvent(TenantId tenantId, EntityId entityId, String eventType, String eventUid) { 63 public Optional<Event> findEvent(TenantId tenantId, EntityId entityId, String eventType, String eventUid) {
64 if (tenantId == null) { 64 if (tenantId == null) {
65 - throw new DataValidationException("Tenant id should be specified!."); 65 + throw new DataValidationException("Tenant id should be specified!");
66 } 66 }
67 if (entityId == null) { 67 if (entityId == null) {
68 - throw new DataValidationException("Entity id should be specified!."); 68 + throw new DataValidationException("Entity id should be specified!");
69 } 69 }
70 if (StringUtils.isEmpty(eventType)) { 70 if (StringUtils.isEmpty(eventType)) {
71 - throw new DataValidationException("Event type should be specified!."); 71 + throw new DataValidationException("Event type should be specified!");
72 } 72 }
73 if (StringUtils.isEmpty(eventUid)) { 73 if (StringUtils.isEmpty(eventUid)) {
74 - throw new DataValidationException("Event uid should be specified!."); 74 + throw new DataValidationException("Event uid should be specified!");
75 } 75 }
76 Event event = eventDao.findEvent(tenantId.getId(), entityId, eventType, eventUid); 76 Event event = eventDao.findEvent(tenantId.getId(), entityId, eventType, eventUid);
77 return event != null ? Optional.of(event) : Optional.empty(); 77 return event != null ? Optional.of(event) : Optional.empty();
@@ -99,13 +99,13 @@ public class BaseEventService implements EventService { @@ -99,13 +99,13 @@ public class BaseEventService implements EventService {
99 @Override 99 @Override
100 protected void validateDataImpl(TenantId tenantId, Event event) { 100 protected void validateDataImpl(TenantId tenantId, Event event) {
101 if (event.getEntityId() == null) { 101 if (event.getEntityId() == null) {
102 - throw new DataValidationException("Entity id should be specified!."); 102 + throw new DataValidationException("Entity id should be specified!");
103 } 103 }
104 if (StringUtils.isEmpty(event.getType())) { 104 if (StringUtils.isEmpty(event.getType())) {
105 - throw new DataValidationException("Event type should be specified!."); 105 + throw new DataValidationException("Event type should be specified!");
106 } 106 }
107 if (event.getBody() == null) { 107 if (event.getBody() == null) {
108 - throw new DataValidationException("Event body should be specified!."); 108 + throw new DataValidationException("Event body should be specified!");
109 } 109 }
110 } 110 }
111 }; 111 };
@@ -343,6 +343,7 @@ public class ModelConstants { @@ -343,6 +343,7 @@ public class ModelConstants {
343 public static final String RULE_CHAIN_ASSIGNED_EDGES_PROPERTY = "assigned_edges"; 343 public static final String RULE_CHAIN_ASSIGNED_EDGES_PROPERTY = "assigned_edges";
344 344
345 public static final String RULE_CHAIN_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "rule_chain_by_tenant_and_search_text"; 345 public static final String RULE_CHAIN_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "rule_chain_by_tenant_and_search_text";
  346 + public static final String RULE_CHAIN_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "rule_chain_by_tenant_by_type_and_search_text";
346 347
347 /** 348 /**
348 * Cassandra rule node constants. 349 * Cassandra rule node constants.
@@ -106,11 +106,7 @@ public class RuleChainEntity implements SearchTextEntity<RuleChain> { @@ -106,11 +106,7 @@ public class RuleChainEntity implements SearchTextEntity<RuleChain> {
106 } 106 }
107 this.tenantId = DaoUtil.getId(ruleChain.getTenantId()); 107 this.tenantId = DaoUtil.getId(ruleChain.getTenantId());
108 this.name = ruleChain.getName(); 108 this.name = ruleChain.getName();
109 - if (ruleChain.getType() != null) {  
110 - this.type = ruleChain.getType();  
111 - } else {  
112 - this.type = RuleChainType.SYSTEM;  
113 - } 109 + this.type = ruleChain.getType();
114 this.searchText = ruleChain.getName(); 110 this.searchText = ruleChain.getName();
115 this.firstRuleNodeId = DaoUtil.getId(ruleChain.getFirstRuleNodeId()); 111 this.firstRuleNodeId = DaoUtil.getId(ruleChain.getFirstRuleNodeId());
116 this.root = ruleChain.isRoot(); 112 this.root = ruleChain.isRoot();
@@ -204,11 +200,7 @@ public class RuleChainEntity implements SearchTextEntity<RuleChain> { @@ -204,11 +200,7 @@ public class RuleChainEntity implements SearchTextEntity<RuleChain> {
204 ruleChain.setCreatedTime(UUIDs.unixTimestamp(id)); 200 ruleChain.setCreatedTime(UUIDs.unixTimestamp(id));
205 ruleChain.setTenantId(new TenantId(tenantId)); 201 ruleChain.setTenantId(new TenantId(tenantId));
206 ruleChain.setName(name); 202 ruleChain.setName(name);
207 - if (type != null) {  
208 - ruleChain.setType(type);  
209 - } else {  
210 - ruleChain.setType(RuleChainType.SYSTEM);  
211 - } 203 + ruleChain.setType(type);
212 if (this.firstRuleNodeId != null) { 204 if (this.firstRuleNodeId != null) {
213 ruleChain.setFirstRuleNodeId(new RuleNodeId(this.firstRuleNodeId)); 205 ruleChain.setFirstRuleNodeId(new RuleNodeId(this.firstRuleNodeId));
214 } 206 }
@@ -103,11 +103,7 @@ public class RuleChainEntity extends BaseSqlEntity<RuleChain> implements SearchT @@ -103,11 +103,7 @@ public class RuleChainEntity extends BaseSqlEntity<RuleChain> implements SearchT
103 } 103 }
104 this.tenantId = toString(DaoUtil.getId(ruleChain.getTenantId())); 104 this.tenantId = toString(DaoUtil.getId(ruleChain.getTenantId()));
105 this.name = ruleChain.getName(); 105 this.name = ruleChain.getName();
106 - if (ruleChain.getType() != null) {  
107 - this.type = ruleChain.getType();  
108 - } else {  
109 - this.type = RuleChainType.SYSTEM;  
110 - } 106 + this.type = ruleChain.getType();
111 this.searchText = ruleChain.getName(); 107 this.searchText = ruleChain.getName();
112 if (ruleChain.getFirstRuleNodeId() != null) { 108 if (ruleChain.getFirstRuleNodeId() != null) {
113 this.firstRuleNodeId = UUIDConverter.fromTimeUUID(ruleChain.getFirstRuleNodeId().getId()); 109 this.firstRuleNodeId = UUIDConverter.fromTimeUUID(ruleChain.getFirstRuleNodeId().getId());
@@ -141,11 +137,7 @@ public class RuleChainEntity extends BaseSqlEntity<RuleChain> implements SearchT @@ -141,11 +137,7 @@ public class RuleChainEntity extends BaseSqlEntity<RuleChain> implements SearchT
141 ruleChain.setCreatedTime(UUIDs.unixTimestamp(getId())); 137 ruleChain.setCreatedTime(UUIDs.unixTimestamp(getId()));
142 ruleChain.setTenantId(new TenantId(toUUID(tenantId))); 138 ruleChain.setTenantId(new TenantId(toUUID(tenantId)));
143 ruleChain.setName(name); 139 ruleChain.setName(name);
144 - if (type != null) {  
145 - ruleChain.setType(type);  
146 - } else {  
147 - ruleChain.setType(RuleChainType.SYSTEM);  
148 - } 140 + ruleChain.setType(type);
149 if (firstRuleNodeId != null) { 141 if (firstRuleNodeId != null) {
150 ruleChain.setFirstRuleNodeId(new RuleNodeId(UUIDConverter.fromString(firstRuleNodeId))); 142 ruleChain.setFirstRuleNodeId(new RuleNodeId(UUIDConverter.fromString(firstRuleNodeId)));
151 } 143 }
@@ -63,6 +63,8 @@ import java.util.Map; @@ -63,6 +63,8 @@ import java.util.Map;
63 import java.util.concurrent.ExecutionException; 63 import java.util.concurrent.ExecutionException;
64 import java.util.stream.Collectors; 64 import java.util.stream.Collectors;
65 65
  66 +import static org.thingsboard.server.dao.service.Validator.validateString;
  67 +
66 /** 68 /**
67 * Created by igor on 3/12/18. 69 * Created by igor on 3/12/18.
68 */ 70 */
@@ -359,6 +361,14 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC @@ -359,6 +361,14 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
359 } 361 }
360 362
361 @Override 363 @Override
  364 + public TextPageData<RuleChain> findTenantRuleChainsByType(TenantId tenantId, RuleChainType type, TextPageLink pageLink) {
  365 + Validator.validateId(tenantId, "Incorrect tenant id for search rule chain request.");
  366 + Validator.validatePageLink(pageLink, "Incorrect PageLink object for search rule chain request.");
  367 + List<RuleChain> ruleChains = ruleChainDao.findRuleChainsByTenantIdAndType(tenantId.getId(), type, pageLink);
  368 + return new TextPageData<>(ruleChains, pageLink);
  369 + }
  370 +
  371 + @Override
362 public void deleteRuleChainById(TenantId tenantId, RuleChainId ruleChainId) { 372 public void deleteRuleChainById(TenantId tenantId, RuleChainId ruleChainId) {
363 Validator.validateId(ruleChainId, "Incorrect rule chain id for delete request."); 373 Validator.validateId(ruleChainId, "Incorrect rule chain id for delete request.");
364 RuleChain ruleChain = ruleChainDao.findById(tenantId, ruleChainId.getId()); 374 RuleChain ruleChain = ruleChainDao.findById(tenantId, ruleChainId.getId());
@@ -514,7 +524,10 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC @@ -514,7 +524,10 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
514 @Override 524 @Override
515 protected void validateDataImpl(TenantId tenantId, RuleChain ruleChain) { 525 protected void validateDataImpl(TenantId tenantId, RuleChain ruleChain) {
516 if (StringUtils.isEmpty(ruleChain.getName())) { 526 if (StringUtils.isEmpty(ruleChain.getName())) {
517 - throw new DataValidationException("Rule chain name should be specified!."); 527 + throw new DataValidationException("Rule chain name should be specified!");
  528 + }
  529 + if (ruleChain.getType() == null) {
  530 + throw new DataValidationException("Rule chain type should be specified!");
518 } 531 }
519 if (ruleChain.getTenantId() == null || ruleChain.getTenantId().isNullUid()) { 532 if (ruleChain.getTenantId() == null || ruleChain.getTenantId().isNullUid()) {
520 throw new DataValidationException("Rule chain should be assigned to tenant!"); 533 throw new DataValidationException("Rule chain should be assigned to tenant!");
@@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.page.TimePageLink; @@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.page.TimePageLink;
28 import org.thingsboard.server.common.data.relation.EntityRelation; 28 import org.thingsboard.server.common.data.relation.EntityRelation;
29 import org.thingsboard.server.common.data.relation.RelationTypeGroup; 29 import org.thingsboard.server.common.data.relation.RelationTypeGroup;
30 import org.thingsboard.server.common.data.rule.RuleChain; 30 import org.thingsboard.server.common.data.rule.RuleChain;
  31 +import org.thingsboard.server.common.data.rule.RuleChainType;
31 import org.thingsboard.server.dao.DaoUtil; 32 import org.thingsboard.server.dao.DaoUtil;
32 import org.thingsboard.server.dao.model.nosql.RuleChainEntity; 33 import org.thingsboard.server.dao.model.nosql.RuleChainEntity;
33 import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao; 34 import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao;
@@ -35,14 +36,19 @@ import org.thingsboard.server.dao.relation.RelationDao; @@ -35,14 +36,19 @@ import org.thingsboard.server.dao.relation.RelationDao;
35 import org.thingsboard.server.dao.util.NoSqlDao; 36 import org.thingsboard.server.dao.util.NoSqlDao;
36 37
37 import java.util.ArrayList; 38 import java.util.ArrayList;
  39 +import java.util.Arrays;
38 import java.util.Collections; 40 import java.util.Collections;
39 import java.util.List; 41 import java.util.List;
40 import java.util.UUID; 42 import java.util.UUID;
41 43
42 import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; 44 import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
  45 +import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_TENANT_ID_PROPERTY;
  46 +import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_TYPE_PROPERTY;
43 import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME; 47 import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME;
  48 +import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME;
44 import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_COLUMN_FAMILY_NAME; 49 import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_COLUMN_FAMILY_NAME;
45 import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_TENANT_ID_PROPERTY; 50 import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_TENANT_ID_PROPERTY;
  51 +import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_TYPE_PROPERTY;
46 52
47 @Component 53 @Component
48 @Slf4j 54 @Slf4j
@@ -74,6 +80,17 @@ public class CassandraRuleChainDao extends CassandraAbstractSearchTextDao<RuleCh @@ -74,6 +80,17 @@ public class CassandraRuleChainDao extends CassandraAbstractSearchTextDao<RuleCh
74 } 80 }
75 81
76 @Override 82 @Override
  83 + public List<RuleChain> findRuleChainsByTenantIdAndType(UUID tenantId, RuleChainType type, TextPageLink pageLink) {
  84 + log.debug("Try to find rule chains by tenantId [{}], type [{}] and pageLink [{}]", tenantId, type, pageLink);
  85 + List<RuleChainEntity> ruleChainEntities = findPageWithTextSearch(new TenantId(tenantId), RULE_CHAIN_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
  86 + Arrays.asList(eq(RULE_CHAIN_TYPE_PROPERTY, type),
  87 + eq(RULE_CHAIN_TENANT_ID_PROPERTY, tenantId)),
  88 + pageLink);
  89 + log.trace("Found rule chains [{}] by tenantId [{}] and pageLink [{}]", ruleChainEntities, tenantId, pageLink);
  90 + return DaoUtil.convertDataList(ruleChainEntities);
  91 + }
  92 +
  93 + @Override
77 public ListenableFuture<List<RuleChain>> findRuleChainsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) { 94 public ListenableFuture<List<RuleChain>> findRuleChainsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) {
78 log.debug("Try to find rule chains by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink); 95 log.debug("Try to find rule chains by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink);
79 ListenableFuture<List<EntityRelation>> relations = relationDao.findRelations(new TenantId(tenantId), new EdgeId(edgeId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE, EntityType.DASHBOARD, pageLink); 96 ListenableFuture<List<EntityRelation>> relations = relationDao.findRelations(new TenantId(tenantId), new EdgeId(edgeId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE, EntityType.DASHBOARD, pageLink);
@@ -19,6 +19,7 @@ import com.google.common.util.concurrent.ListenableFuture; @@ -19,6 +19,7 @@ import com.google.common.util.concurrent.ListenableFuture;
19 import org.thingsboard.server.common.data.page.TextPageLink; 19 import org.thingsboard.server.common.data.page.TextPageLink;
20 import org.thingsboard.server.common.data.page.TimePageLink; 20 import org.thingsboard.server.common.data.page.TimePageLink;
21 import org.thingsboard.server.common.data.rule.RuleChain; 21 import org.thingsboard.server.common.data.rule.RuleChain;
  22 +import org.thingsboard.server.common.data.rule.RuleChainType;
22 import org.thingsboard.server.dao.Dao; 23 import org.thingsboard.server.dao.Dao;
23 24
24 import java.util.List; 25 import java.util.List;
@@ -39,6 +40,16 @@ public interface RuleChainDao extends Dao<RuleChain> { @@ -39,6 +40,16 @@ public interface RuleChainDao extends Dao<RuleChain> {
39 List<RuleChain> findRuleChainsByTenantId(UUID tenantId, TextPageLink pageLink); 40 List<RuleChain> findRuleChainsByTenantId(UUID tenantId, TextPageLink pageLink);
40 41
41 /** 42 /**
  43 + * Find rule chains by tenantId, type and page link.
  44 + *
  45 + * @param tenantId the tenantId
  46 + * @param type the type
  47 + * @param pageLink the page link
  48 + * @return the list of rule chain objects
  49 + */
  50 + List<RuleChain> findRuleChainsByTenantIdAndType(UUID tenantId, RuleChainType type, TextPageLink pageLink);
  51 +
  52 + /**
42 * Find rule chains by tenantId, edgeId and page link. 53 * Find rule chains by tenantId, edgeId and page link.
43 * 54 *
44 * @param tenantId the tenantId 55 * @param tenantId the tenantId
@@ -31,6 +31,7 @@ import org.thingsboard.server.common.data.page.TimePageLink; @@ -31,6 +31,7 @@ import org.thingsboard.server.common.data.page.TimePageLink;
31 import org.thingsboard.server.common.data.relation.EntityRelation; 31 import org.thingsboard.server.common.data.relation.EntityRelation;
32 import org.thingsboard.server.common.data.relation.RelationTypeGroup; 32 import org.thingsboard.server.common.data.relation.RelationTypeGroup;
33 import org.thingsboard.server.common.data.rule.RuleChain; 33 import org.thingsboard.server.common.data.rule.RuleChain;
  34 +import org.thingsboard.server.common.data.rule.RuleChainType;
34 import org.thingsboard.server.dao.DaoUtil; 35 import org.thingsboard.server.dao.DaoUtil;
35 import org.thingsboard.server.dao.model.sql.RuleChainEntity; 36 import org.thingsboard.server.dao.model.sql.RuleChainEntity;
36 import org.thingsboard.server.dao.relation.RelationDao; 37 import org.thingsboard.server.dao.relation.RelationDao;
@@ -68,6 +69,7 @@ public class JpaRuleChainDao extends JpaAbstractSearchTextDao<RuleChainEntity, R @@ -68,6 +69,7 @@ public class JpaRuleChainDao extends JpaAbstractSearchTextDao<RuleChainEntity, R
68 69
69 @Override 70 @Override
70 public List<RuleChain> findRuleChainsByTenantId(UUID tenantId, TextPageLink pageLink) { 71 public List<RuleChain> findRuleChainsByTenantId(UUID tenantId, TextPageLink pageLink) {
  72 + log.debug("Try to find rule chains by tenantId [{}] and pageLink [{}]", tenantId, pageLink);
71 return DaoUtil.convertDataList(ruleChainRepository 73 return DaoUtil.convertDataList(ruleChainRepository
72 .findByTenantId( 74 .findByTenantId(
73 UUIDConverter.fromTimeUUID(tenantId), 75 UUIDConverter.fromTimeUUID(tenantId),
@@ -77,6 +79,18 @@ public class JpaRuleChainDao extends JpaAbstractSearchTextDao<RuleChainEntity, R @@ -77,6 +79,18 @@ public class JpaRuleChainDao extends JpaAbstractSearchTextDao<RuleChainEntity, R
77 } 79 }
78 80
79 @Override 81 @Override
  82 + public List<RuleChain> findRuleChainsByTenantIdAndType(UUID tenantId, RuleChainType type, TextPageLink pageLink) {
  83 + log.debug("Try to find rule chains by tenantId [{}], type [{}] and pageLink [{}]", tenantId, type, pageLink);
  84 + return DaoUtil.convertDataList(ruleChainRepository
  85 + .findByTenantIdAndType(
  86 + UUIDConverter.fromTimeUUID(tenantId),
  87 + type,
  88 + Objects.toString(pageLink.getTextSearch(), ""),
  89 + pageLink.getIdOffset() == null ? NULL_UUID_STR : UUIDConverter.fromTimeUUID(pageLink.getIdOffset()),
  90 + new PageRequest(0, pageLink.getLimit())));
  91 + }
  92 +
  93 + @Override
80 public ListenableFuture<List<RuleChain>> findRuleChainsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) { 94 public ListenableFuture<List<RuleChain>> findRuleChainsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) {
81 log.debug("Try to find rule chains by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink); 95 log.debug("Try to find rule chains by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink);
82 ListenableFuture<List<EntityRelation>> relations = relationDao.findRelations(new TenantId(tenantId), new EdgeId(edgeId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE, EntityType.RULE_CHAIN, pageLink); 96 ListenableFuture<List<EntityRelation>> relations = relationDao.findRelations(new TenantId(tenantId), new EdgeId(edgeId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE, EntityType.RULE_CHAIN, pageLink);
@@ -19,6 +19,7 @@ import org.springframework.data.domain.Pageable; @@ -19,6 +19,7 @@ import org.springframework.data.domain.Pageable;
19 import org.springframework.data.jpa.repository.Query; 19 import org.springframework.data.jpa.repository.Query;
20 import org.springframework.data.repository.CrudRepository; 20 import org.springframework.data.repository.CrudRepository;
21 import org.springframework.data.repository.query.Param; 21 import org.springframework.data.repository.query.Param;
  22 +import org.thingsboard.server.common.data.rule.RuleChainType;
22 import org.thingsboard.server.dao.model.sql.RuleChainEntity; 23 import org.thingsboard.server.dao.model.sql.RuleChainEntity;
23 import org.thingsboard.server.dao.util.SqlDao; 24 import org.thingsboard.server.dao.util.SqlDao;
24 25
@@ -35,4 +36,13 @@ public interface RuleChainRepository extends CrudRepository<RuleChainEntity, Str @@ -35,4 +36,13 @@ public interface RuleChainRepository extends CrudRepository<RuleChainEntity, Str
35 @Param("idOffset") String idOffset, 36 @Param("idOffset") String idOffset,
36 Pageable pageable); 37 Pageable pageable);
37 38
  39 + @Query("SELECT rc FROM RuleChainEntity rc WHERE rc.tenantId = :tenantId " +
  40 + "AND rc.type = :type " +
  41 + "AND LOWER(rc.searchText) LIKE LOWER(CONCAT(:searchText, '%')) " +
  42 + "AND rc.id > :idOffset ORDER BY rc.id")
  43 + List<RuleChainEntity> findByTenantIdAndType(@Param("tenantId") String tenantId,
  44 + @Param("type") RuleChainType type,
  45 + @Param("searchText") String searchText,
  46 + @Param("idOffset") String idOffset,
  47 + Pageable pageable);
38 } 48 }
@@ -597,22 +597,30 @@ CREATE TABLE IF NOT EXISTS thingsboard.rule_chain ( @@ -597,22 +597,30 @@ CREATE TABLE IF NOT EXISTS thingsboard.rule_chain (
597 id uuid, 597 id uuid,
598 tenant_id uuid, 598 tenant_id uuid,
599 name text, 599 name text,
  600 + type text,
600 search_text text, 601 search_text text,
601 first_rule_node_id uuid, 602 first_rule_node_id uuid,
602 root boolean, 603 root boolean,
603 debug_mode boolean, 604 debug_mode boolean,
604 configuration text, 605 configuration text,
605 additional_info text, 606 additional_info text,
606 - PRIMARY KEY (id, tenant_id) 607 + PRIMARY KEY (id, tenant_id, type)
607 ); 608 );
608 609
609 CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.rule_chain_by_tenant_and_search_text AS 610 CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.rule_chain_by_tenant_and_search_text AS
610 SELECT * 611 SELECT *
611 from thingsboard.rule_chain 612 from thingsboard.rule_chain
612 - WHERE tenant_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL  
613 - PRIMARY KEY ( tenant_id, search_text, id ) 613 + WHERE tenant_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL AND type IS NOT NULL
  614 + PRIMARY KEY ( tenant_id, search_text, id, type )
614 WITH CLUSTERING ORDER BY ( search_text ASC, id DESC ); 615 WITH CLUSTERING ORDER BY ( search_text ASC, id DESC );
615 616
  617 +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.rule_chain_by_tenant_by_type_and_search_text AS
  618 + SELECT *
  619 + from thingsboard.rule_chain
  620 + WHERE tenant_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL AND type IS NOT NULL
  621 + PRIMARY KEY ( tenant_id, type, search_text, id )
  622 + WITH CLUSTERING ORDER BY ( type ASC, search_text ASC, id DESC );
  623 +
616 CREATE TABLE IF NOT EXISTS thingsboard.rule_node ( 624 CREATE TABLE IF NOT EXISTS thingsboard.rule_node (
617 id uuid, 625 id uuid,
618 rule_chain_id uuid, 626 rule_chain_id uuid,
@@ -277,7 +277,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device @@ -277,7 +277,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
277 } 277 }
278 break; 278 break;
279 case types.entityType.rulechain: 279 case types.entityType.rulechain:
280 - promise = ruleChainService.getRuleChains(pageLink, config); 280 + promise = ruleChainService.getRuleChains(pageLink, config, subType);
281 break; 281 break;
282 case types.entityType.dashboard: 282 case types.entityType.dashboard:
283 if (user.authority === 'CUSTOMER_USER') { 283 if (user.authority === 'CUSTOMER_USER') {
@@ -46,7 +46,7 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co @@ -46,7 +46,7 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co
46 46
47 return service; 47 return service;
48 48
49 - function getRuleChains (pageLink, config) { 49 + function getRuleChains (pageLink, config, type) {
50 var deferred = $q.defer(); 50 var deferred = $q.defer();
51 var url = '/api/ruleChains?limit=' + pageLink.limit; 51 var url = '/api/ruleChains?limit=' + pageLink.limit;
52 if (angular.isDefined(pageLink.textSearch)) { 52 if (angular.isDefined(pageLink.textSearch)) {
@@ -58,6 +58,9 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co @@ -58,6 +58,9 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co
58 if (angular.isDefined(pageLink.textOffset)) { 58 if (angular.isDefined(pageLink.textOffset)) {
59 url += '&textOffset=' + pageLink.textOffset; 59 url += '&textOffset=' + pageLink.textOffset;
60 } 60 }
  61 + if (angular.isDefined(type) && type.length) {
  62 + url += '&type=' + type;
  63 + }
61 $http.get(url, config).then(function success(response) { 64 $http.get(url, config).then(function success(response) {
62 deferred.resolve(prepareRuleChains(response.data)); 65 deferred.resolve(prepareRuleChains(response.data));
63 }, function fail() { 66 }, function fail() {
@@ -607,7 +607,7 @@ export default angular.module('thingsboard.types', []) @@ -607,7 +607,7 @@ export default angular.module('thingsboard.types', [])
607 } 607 }
608 }, 608 },
609 systemRuleChainType: "SYSTEM", 609 systemRuleChainType: "SYSTEM",
610 - ruleChainTypes: ["SYSTEM", "EDGE"], 610 + edgeRuleChainType: "EDGE",
611 ruleNodeTypeComponentTypes: ["FILTER", "ENRICHMENT", "TRANSFORMATION", "ACTION", "EXTERNAL"], 611 ruleNodeTypeComponentTypes: ["FILTER", "ENRICHMENT", "TRANSFORMATION", "ACTION", "EXTERNAL"],
612 ruleChainNodeComponent: { 612 ruleChainNodeComponent: {
613 type: 'RULE_CHAIN', 613 type: 'RULE_CHAIN',
@@ -1391,10 +1391,11 @@ @@ -1391,10 +1391,11 @@
1391 "rulechain": { 1391 "rulechain": {
1392 "rulechain": "Rule chain", 1392 "rulechain": "Rule chain",
1393 "rulechains": "Rule chains", 1393 "rulechains": "Rule chains",
  1394 + "system-rulechains": "System Rule chains",
  1395 + "edge-rulechains": "Edge Rule chains",
1394 "root": "Root", 1396 "root": "Root",
1395 "delete": "Delete rule chain", 1397 "delete": "Delete rule chain",
1396 "name": "Name", 1398 "name": "Name",
1397 - "type": "Type",  
1398 "name-required": "Name is required.", 1399 "name-required": "Name is required.",
1399 "description": "Description", 1400 "description": "Description",
1400 "add": "Add Rule Chain", 1401 "add": "Add Rule Chain",
@@ -31,7 +31,7 @@ @@ -31,7 +31,7 @@
31 <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span> 31 <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
32 <md-dialog-content> 32 <md-dialog-content>
33 <div class="md-dialog-content"> 33 <div class="md-dialog-content">
34 - <tb-rule-chain rule-chain="vm.item" is-edit="true" rule-chain-scope="'tenant'" the-form="theForm"></tb-rule-chain> 34 + <tb-rule-chain rule-chain="vm.item" is-edit="true" the-form="theForm"></tb-rule-chain>
35 </div> 35 </div>
36 </md-dialog-content> 36 </md-dialog-content>
37 <md-dialog-actions layout="row"> 37 <md-dialog-actions layout="row">
@@ -31,7 +31,7 @@ @@ -31,7 +31,7 @@
31 <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span> 31 <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
32 <md-dialog-content> 32 <md-dialog-content>
33 <div class="md-dialog-content"> 33 <div class="md-dialog-content">
34 - <tb-rule-node rule-node="vm.ruleNode" rule-chain-id="vm.ruleChainId" is-edit="true" the-form="theForm"></tb-rule-node> 34 + <tb-rule-node rule-node="vm.ruleNode" rule-chain-id="vm.ruleChainId" rule-chain-type="vm.ruleChainType" is-edit="true" the-form="theForm"></tb-rule-node>
35 </div> 35 </div>
36 </md-dialog-content> 36 </md-dialog-content>
37 <md-dialog-actions layout="row"> 37 <md-dialog-actions layout="row">
@@ -50,14 +50,6 @@ @@ -50,14 +50,6 @@
50 </div> 50 </div>
51 </md-input-container> 51 </md-input-container>
52 <md-input-container class="md-block"> 52 <md-input-container class="md-block">
53 - <label translate>rulechain.type</label>  
54 - <md-select ng-disabled="$root.loading || !isEdit || ruleChainScope !== 'tenant' || ruleChain.root === true" name="type" ng-model="ruleChain.type">  
55 - <md-option ng-repeat="ruleChainType in ruleChainTypes" value="{{ruleChainType}}">  
56 - {{ruleChainType}}  
57 - </md-option>  
58 - </md-select>  
59 - </md-input-container>  
60 - <md-input-container class="md-block">  
61 <md-checkbox ng-disabled="$root.loading || !isEdit" aria-label="{{ 'rulechain.debug-mode' | translate }}" 53 <md-checkbox ng-disabled="$root.loading || !isEdit" aria-label="{{ 'rulechain.debug-mode' | translate }}"
62 ng-model="ruleChain.debugMode">{{ 'rulechain.debug-mode' | translate }} 54 ng-model="ruleChain.debugMode">{{ 'rulechain.debug-mode' | translate }}
63 </md-checkbox> 55 </md-checkbox>
@@ -1179,6 +1179,9 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time @@ -1179,6 +1179,9 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time
1179 function saveRuleChain() { 1179 function saveRuleChain() {
1180 var saveRuleChainPromise; 1180 var saveRuleChainPromise;
1181 if (vm.isImport) { 1181 if (vm.isImport) {
  1182 + if (angular.isUndefined(vm.ruleChain.type)) {
  1183 + vm.ruleChain.type = types.systemRuleChainType;
  1184 + }
1182 saveRuleChainPromise = ruleChainService.saveRuleChain(vm.ruleChain); 1185 saveRuleChainPromise = ruleChainService.saveRuleChain(vm.ruleChain);
1183 } else { 1186 } else {
1184 saveRuleChainPromise = $q.when(vm.ruleChain); 1187 saveRuleChainPromise = $q.when(vm.ruleChain);
@@ -1286,6 +1289,7 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time @@ -1286,6 +1289,7 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time
1286 ruleNode.configuration = angular.copy(ruleNode.component.configurationDescriptor.nodeDefinition.defaultConfiguration); 1289 ruleNode.configuration = angular.copy(ruleNode.component.configurationDescriptor.nodeDefinition.defaultConfiguration);
1287 1290
1288 var ruleChainId = vm.ruleChain.id ? vm.ruleChain.id.id : null; 1291 var ruleChainId = vm.ruleChain.id ? vm.ruleChain.id.id : null;
  1292 + var ruleChainType = vm.ruleChain.type ? vm.ruleChain.type : types.systemRuleChainType;
1289 1293
1290 vm.enableHotKeys = false; 1294 vm.enableHotKeys = false;
1291 1295
@@ -1294,7 +1298,7 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time @@ -1294,7 +1298,7 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time
1294 controllerAs: 'vm', 1298 controllerAs: 'vm',
1295 templateUrl: addRuleNodeTemplate, 1299 templateUrl: addRuleNodeTemplate,
1296 parent: angular.element($document[0].body), 1300 parent: angular.element($document[0].body),
1297 - locals: {ruleNode: ruleNode, ruleChainId: ruleChainId}, 1301 + locals: {ruleNode: ruleNode, ruleChainId: ruleChainId, ruleChainType: ruleChainType},
1298 fullscreen: true, 1302 fullscreen: true,
1299 targetEvent: $event 1303 targetEvent: $event
1300 }).then(function (ruleNode) { 1304 }).then(function (ruleNode) {
@@ -1365,13 +1369,14 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time @@ -1365,13 +1369,14 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time
1365 } 1369 }
1366 1370
1367 /*@ngInject*/ 1371 /*@ngInject*/
1368 -export function AddRuleNodeController($scope, $mdDialog, ruleNode, ruleChainId, helpLinks) { 1372 +export function AddRuleNodeController($scope, $mdDialog, ruleNode, ruleChainId, ruleChainType, helpLinks) {
1369 1373
1370 var vm = this; 1374 var vm = this;
1371 1375
1372 vm.helpLinks = helpLinks; 1376 vm.helpLinks = helpLinks;
1373 vm.ruleNode = ruleNode; 1377 vm.ruleNode = ruleNode;
1374 vm.ruleChainId = ruleChainId; 1378 vm.ruleChainId = ruleChainId;
  1379 + vm.ruleChainType = ruleChainType;
1375 1380
1376 vm.add = add; 1381 vm.add = add;
1377 vm.cancel = cancel; 1382 vm.cancel = cancel;
@@ -26,12 +26,6 @@ export default function RuleChainDirective($compile, $templateCache, $mdDialog, @@ -26,12 +26,6 @@ export default function RuleChainDirective($compile, $templateCache, $mdDialog,
26 var template = $templateCache.get(ruleChainFieldsetTemplate); 26 var template = $templateCache.get(ruleChainFieldsetTemplate);
27 element.html(template); 27 element.html(template);
28 28
29 - scope.ruleChainTypes = types.ruleChainTypes;  
30 -  
31 - if (angular.isDefined(scope.ruleChain) && scope.ruleChain != null && angular.isUndefined(scope.ruleChain.type)) {  
32 - scope.ruleChain.type = types.systemRuleChainType;  
33 - }  
34 -  
35 scope.onRuleChainIdCopied = function() { 29 scope.onRuleChainIdCopied = function() {
36 toast.showSuccess($translate.instant('rulechain.idCopiedMessage'), 750, angular.element(element).parent().parent(), 'bottom left'); 30 toast.showSuccess($translate.instant('rulechain.idCopiedMessage'), 750, angular.element(element).parent().parent(), 'bottom left');
37 }; 31 };
@@ -44,7 +38,6 @@ export default function RuleChainDirective($compile, $templateCache, $mdDialog, @@ -44,7 +38,6 @@ export default function RuleChainDirective($compile, $templateCache, $mdDialog,
44 scope: { 38 scope: {
45 ruleChain: '=', 39 ruleChain: '=',
46 isEdit: '=', 40 isEdit: '=',
47 - ruleChainScope: '=',  
48 isReadOnly: '=', 41 isReadOnly: '=',
49 theForm: '=', 42 theForm: '=',
50 onSetRootRuleChain: '&', 43 onSetRootRuleChain: '&',
@@ -29,6 +29,15 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider @@ -29,6 +29,15 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider
29 $stateProvider 29 $stateProvider
30 .state('home.ruleChains', { 30 .state('home.ruleChains', {
31 url: '/ruleChains', 31 url: '/ruleChains',
  32 + module: 'private',
  33 + auth: ['SYS_ADMIN', 'TENANT_ADMIN'],
  34 + redirectTo: 'home.ruleChains.system',
  35 + ncyBreadcrumb: {
  36 + label: '{"icon": "settings_ethernet", "label": "rulechain.rulechains"}'
  37 + }
  38 + })
  39 + .state('home.ruleChains.system', {
  40 + url: '/ruleChains/system',
32 params: {'topIndex': 0}, 41 params: {'topIndex': 0},
33 module: 'private', 42 module: 'private',
34 auth: ['SYS_ADMIN', 'TENANT_ADMIN'], 43 auth: ['SYS_ADMIN', 'TENANT_ADMIN'],
@@ -41,11 +50,11 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider @@ -41,11 +50,11 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider
41 }, 50 },
42 data: { 51 data: {
43 searchEnabled: true, 52 searchEnabled: true,
44 - pageTitle: 'rulechain.rulechains', 53 + pageTitle: 'rulechain.system-rulechains',
45 ruleChainsType: 'tenant' 54 ruleChainsType: 'tenant'
46 }, 55 },
47 ncyBreadcrumb: { 56 ncyBreadcrumb: {
48 - label: '{"icon": "settings_ethernet", "label": "rulechain.rulechains"}' 57 + label: '{"icon": "settings_ethernet", "label": "rulechain.system-rulechains"}'
49 } 58 }
50 }).state('home.ruleChains.ruleChain', { 59 }).state('home.ruleChains.ruleChain', {
51 url: '/:ruleChainId', 60 url: '/:ruleChainId',
@@ -124,8 +133,64 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider @@ -124,8 +133,64 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider
124 ncyBreadcrumb: { 133 ncyBreadcrumb: {
125 label: '{"icon": "settings_ethernet", "label": "{{ (\'rulechain.import\' | translate) + \': \'+ vm.ruleChain.name }}", "translate": "false"}' 134 label: '{"icon": "settings_ethernet", "label": "{{ (\'rulechain.import\' | translate) + \': \'+ vm.ruleChain.name }}", "translate": "false"}'
126 } 135 }
127 - })  
128 - .state('home.edges.ruleChains', { 136 + }).state('home.ruleChains.edge', {
  137 + url: '/ruleChains/edge',
  138 + params: {'topIndex': 0},
  139 + module: 'private',
  140 + auth: ['TENANT_ADMIN'],
  141 + views: {
  142 + "content@home": {
  143 + templateUrl: ruleChainsTemplate,
  144 + controllerAs: 'vm',
  145 + controller: 'RuleChainsController'
  146 + }
  147 + },
  148 + data: {
  149 + searchEnabled: true,
  150 + pageTitle: 'rulechain.edge-rulechains',
  151 + ruleChainsType: 'edges'
  152 + },
  153 + ncyBreadcrumb: {
  154 + label: '{"icon": "settings_ethernet", "label": "rulechain.edge-rulechains"}'
  155 + }
  156 + }).state('home.ruleChains.edge.ruleChain', {
  157 + url: '/:ruleChainId',
  158 + reloadOnSearch: false,
  159 + module: 'private',
  160 + auth: ['SYS_ADMIN', 'TENANT_ADMIN'],
  161 + views: {
  162 + "content@home": {
  163 + templateUrl: ruleChainTemplate,
  164 + controller: 'RuleChainController',
  165 + controllerAs: 'vm'
  166 + }
  167 + },
  168 + resolve: {
  169 + ruleChain:
  170 + /*@ngInject*/
  171 + function($stateParams, ruleChainService) {
  172 + return ruleChainService.getRuleChain($stateParams.ruleChainId);
  173 + },
  174 + ruleChainMetaData:
  175 + /*@ngInject*/
  176 + function($stateParams, ruleChainService) {
  177 + return ruleChainService.getRuleChainMetaData($stateParams.ruleChainId);
  178 + },
  179 + ruleNodeComponents:
  180 + /*@ngInject*/
  181 + function($stateParams, ruleChainService) {
  182 + return ruleChainService.getRuleNodeComponents();
  183 + }
  184 + },
  185 + data: {
  186 + import: false,
  187 + searchEnabled: false,
  188 + pageTitle: 'edge.rulechain'
  189 + },
  190 + ncyBreadcrumb: {
  191 + label: '{"icon": "settings_ethernet", "label": "{{ vm.ruleChain.name }}", "translate": "false"}'
  192 + }
  193 + }).state('home.edges.ruleChains', {
129 url: '/:edgeId/ruleChains', 194 url: '/:edgeId/ruleChains',
130 params: {'topIndex': 0}, 195 params: {'topIndex': 0},
131 module: 'private', 196 module: 'private',
@@ -138,15 +203,14 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider @@ -138,15 +203,14 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider
138 } 203 }
139 }, 204 },
140 data: { 205 data: {
141 - ruleChainsType: 'edge',  
142 searchEnabled: true, 206 searchEnabled: true,
143 - pageTitle: 'edge.rulechains' 207 + pageTitle: 'edge.rulechains',
  208 + ruleChainsType: 'edge'
144 }, 209 },
145 ncyBreadcrumb: { 210 ncyBreadcrumb: {
146 label: '{"icon": "settings_ethernet", "label": "{{ vm.edgeRuleChainsTitle }}", "translate": "false"}' 211 label: '{"icon": "settings_ethernet", "label": "{{ vm.edgeRuleChainsTitle }}", "translate": "false"}'
147 } 212 }
148 - })  
149 - .state('home.edges.ruleChains.ruleChain', { 213 + }).state('home.edges.ruleChains.ruleChain', {
150 url: '/:ruleChainId', 214 url: '/:ruleChainId',
151 reloadOnSearch: false, 215 reloadOnSearch: false,
152 module: 'private', 216 module: 'private',
@@ -181,7 +245,7 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider @@ -181,7 +245,7 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider
181 pageTitle: 'edge.rulechain' 245 pageTitle: 'edge.rulechain'
182 }, 246 },
183 ncyBreadcrumb: { 247 ncyBreadcrumb: {
184 - label: '{"icon": "settings_ethernet", "label": "edge.rulechain"}' 248 + label: '{"icon": "settings_ethernet", "label": "{{ vm.ruleChain.name }}", "translate": "false"}'
185 } 249 }
186 }); 250 });
187 } 251 }
@@ -116,7 +116,7 @@ export default function RuleChainsController(ruleChainService, userService, edge @@ -116,7 +116,7 @@ export default function RuleChainsController(ruleChainService, userService, edge
116 116
117 if (vm.ruleChainsScope === 'tenant') { 117 if (vm.ruleChainsScope === 'tenant') {
118 fetchRuleChainsFunction = function (pageLink) { 118 fetchRuleChainsFunction = function (pageLink) {
119 - return fetchRuleChains(pageLink); 119 + return fetchRuleChains(pageLink, 'SYSTEM');
120 }; 120 };
121 deleteRuleChainFunction = function (ruleChainId) { 121 deleteRuleChainFunction = function (ruleChainId) {
122 return deleteRuleChain(ruleChainId); 122 return deleteRuleChain(ruleChainId);
@@ -134,6 +134,57 @@ export default function RuleChainsController(ruleChainService, userService, edge @@ -134,6 +134,57 @@ export default function RuleChainsController(ruleChainService, userService, edge
134 134
135 ruleChainActionsList.push({ 135 ruleChainActionsList.push({
136 onAction: function ($event, item) { 136 onAction: function ($event, item) {
  137 + vm.grid.deleteItem($event, item);
  138 + },
  139 + name: function() { return $translate.instant('action.delete') },
  140 + details: function() { return $translate.instant('rulechain.delete') },
  141 + icon: "delete",
  142 + isEnabled: isNonRootRuleChain
  143 + });
  144 +
  145 + ruleChainGroupActionsList.push(
  146 + {
  147 + onAction: function ($event) {
  148 + vm.grid.deleteItems($event);
  149 + },
  150 + name: function() { return $translate.instant('rulechain.delete-rulechains') },
  151 + details: deleteRuleChainsActionTitle,
  152 + icon: "delete"
  153 + }
  154 + );
  155 +
  156 + vm.ruleChainGridConfig.addItemActions = [];
  157 + vm.ruleChainGridConfig.addItemActions.push({
  158 + onAction: function ($event) {
  159 + vm.grid.addItem($event);
  160 + },
  161 + name: function() { return $translate.instant('action.create') },
  162 + details: function() { return $translate.instant('rulechain.create-new-rulechain') },
  163 + icon: "insert_drive_file"
  164 + });
  165 + vm.ruleChainGridConfig.addItemActions.push({
  166 + onAction: function ($event) {
  167 + importExport.importRuleChain($event).then(
  168 + function(ruleChainImport) {
  169 + $state.go('home.ruleChains.importRuleChain', {ruleChainImport:ruleChainImport});
  170 + }
  171 + );
  172 + },
  173 + name: function() { return $translate.instant('action.import') },
  174 + details: function() { return $translate.instant('rulechain.import') },
  175 + icon: "file_upload"
  176 + });
  177 +
  178 + } else if (vm.ruleChainsScope === 'edges') {
  179 + fetchRuleChainsFunction = function (pageLink) {
  180 + return fetchRuleChains(pageLink, 'EDGE');
  181 + };
  182 + deleteRuleChainFunction = function (ruleChainId) {
  183 + return deleteRuleChain(ruleChainId);
  184 + };
  185 +
  186 + ruleChainActionsList.push({
  187 + onAction: function ($event, item) {
137 manageAssignedEdges($event, item); 188 manageAssignedEdges($event, item);
138 }, 189 },
139 name: function() { return $translate.instant('action.assign') }, 190 name: function() { return $translate.instant('action.assign') },
@@ -209,7 +260,6 @@ export default function RuleChainsController(ruleChainService, userService, edge @@ -209,7 +260,6 @@ export default function RuleChainsController(ruleChainService, userService, edge
209 details: function() { return $translate.instant('rulechain.import') }, 260 details: function() { return $translate.instant('rulechain.import') },
210 icon: "file_upload" 261 icon: "file_upload"
211 }); 262 });
212 -  
213 } else if (vm.ruleChainsScope === 'edge') { 263 } else if (vm.ruleChainsScope === 'edge') {
214 fetchRuleChainsFunction = function (pageLink) { 264 fetchRuleChainsFunction = function (pageLink) {
215 return ruleChainService.getEdgeRuleChains(edgeId, pageLink); 265 return ruleChainService.getEdgeRuleChains(edgeId, pageLink);
@@ -291,11 +341,18 @@ export default function RuleChainsController(ruleChainService, userService, edge @@ -291,11 +341,18 @@ export default function RuleChainsController(ruleChainService, userService, edge
291 vm.grid = grid; 341 vm.grid = grid;
292 } 342 }
293 343
294 - function fetchRuleChains(pageLink) {  
295 - return ruleChainService.getRuleChains(pageLink); 344 + function fetchRuleChains(pageLink, type) {
  345 + return ruleChainService.getRuleChains(pageLink, null, type);
296 } 346 }
297 347
298 function saveRuleChain(ruleChain) { 348 function saveRuleChain(ruleChain) {
  349 + if (angular.isUndefined(ruleChain.type)) {
  350 + if (vm.ruleChainsScope === 'edges') {
  351 + ruleChain.type = types.edgeRuleChainType;
  352 + } else {
  353 + ruleChain.type = types.systemRuleChainType;
  354 + }
  355 + }
299 return ruleChainService.saveRuleChain(ruleChain); 356 return ruleChainService.saveRuleChain(ruleChain);
300 } 357 }
301 358
@@ -303,10 +360,11 @@ export default function RuleChainsController(ruleChainService, userService, edge @@ -303,10 +360,11 @@ export default function RuleChainsController(ruleChainService, userService, edge
303 if ($event) { 360 if ($event) {
304 $event.stopPropagation(); 361 $event.stopPropagation();
305 } 362 }
  363 +
306 if (vm.ruleChainsScope === 'edge') { 364 if (vm.ruleChainsScope === 'edge') {
307 - $state.go('home.edges.ruleChains.ruleChain', {  
308 - ruleChainId: ruleChain.id.id  
309 - }); 365 + $state.go('home.edges.ruleChains.ruleChain', {ruleChainId: ruleChain.id.id, edgeId: vm.edge.id.id});
  366 + } else if (vm.ruleChainsScope === 'edges') {
  367 + $state.go('home.ruleChains.edge.ruleChain', {ruleChainId: ruleChain.id.id});
310 } else { 368 } else {
311 $state.go('home.ruleChains.ruleChain', {ruleChainId: ruleChain.id.id}); 369 $state.go('home.ruleChains.ruleChain', {ruleChainId: ruleChain.id.id});
312 } 370 }
@@ -53,6 +53,7 @@ @@ -53,6 +53,7 @@
53 tb-required="true" 53 tb-required="true"
54 exclude-entity-ids="[ruleChainId]" 54 exclude-entity-ids="[ruleChainId]"
55 entity-type="types.entityType.rulechain" 55 entity-type="types.entityType.rulechain"
  56 + entity-subtype="ruleChainType"
56 ng-model="params.targetRuleChainId"> 57 ng-model="params.targetRuleChainId">
57 </tb-entity-autocomplete> 58 </tb-entity-autocomplete>
58 <md-input-container class="md-block"> 59 <md-input-container class="md-block">
@@ -70,6 +70,7 @@ export default function RuleNodeDirective($compile, $templateCache, ruleChainSer @@ -70,6 +70,7 @@ export default function RuleNodeDirective($compile, $templateCache, ruleChainSer
70 link: linker, 70 link: linker,
71 scope: { 71 scope: {
72 ruleChainId: '=', 72 ruleChainId: '=',
  73 + ruleChainType: '=',
73 ruleNode: '=', 74 ruleNode: '=',
74 isEdit: '=', 75 isEdit: '=',
75 isReadOnly: '=', 76 isReadOnly: '=',
@@ -156,9 +156,24 @@ function Menu(userService, $state, $rootScope) { @@ -156,9 +156,24 @@ function Menu(userService, $state, $rootScope) {
156 }, 156 },
157 { 157 {
158 name: 'rulechain.rulechains', 158 name: 'rulechain.rulechains',
159 - type: 'link', 159 + type: 'toggle',
160 state: 'home.ruleChains', 160 state: 'home.ruleChains',
161 - icon: 'settings_ethernet' 161 + height: '80px',
  162 + icon: 'settings_ethernet',
  163 + pages: [
  164 + {
  165 + name: 'rulechain.system-rulechains',
  166 + type: 'link',
  167 + state: 'home.ruleChains.system',
  168 + icon: 'settings_ethernet'
  169 + },
  170 + {
  171 + name: 'rulechain.edge-rulechains',
  172 + type: 'link',
  173 + state: 'home.ruleChains.edge',
  174 + icon: 'router'
  175 + }
  176 + ]
162 }, 177 },
163 { 178 {
164 name: 'customer.customers', 179 name: 'customer.customers',
@@ -214,9 +229,14 @@ function Menu(userService, $state, $rootScope) { @@ -214,9 +229,14 @@ function Menu(userService, $state, $rootScope) {
214 name: 'rulechain.management', 229 name: 'rulechain.management',
215 places: [ 230 places: [
216 { 231 {
217 - name: 'rulechain.rulechains', 232 + name: 'rulechain.system-rulechains',
218 icon: 'settings_ethernet', 233 icon: 'settings_ethernet',
219 state: 'home.ruleChains' 234 state: 'home.ruleChains'
  235 + },
  236 + {
  237 + name: 'rulechain.edge-rulechains',
  238 + icon: 'router',
  239 + state: 'home.edgesRuleChains'
220 } 240 }
221 ] 241 ]
222 }, 242 },