Browse Source

Rule chains refactoring:

pull/2818/head
Volodymyr Babak 7 years ago
parent
commit
c409e2d9c1
  1. 9
      application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
  2. 74
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java
  3. 3
      common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java
  4. 12
      common/edge-api/src/main/proto/edge.proto
  5. 8
      dao/src/main/java/org/thingsboard/server/dao/component/BaseComponentDescriptorService.java
  6. 16
      dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java
  7. 1
      dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
  8. 12
      dao/src/main/java/org/thingsboard/server/dao/model/nosql/RuleChainEntity.java
  9. 12
      dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleChainEntity.java
  10. 15
      dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java
  11. 17
      dao/src/main/java/org/thingsboard/server/dao/rule/CassandraRuleChainDao.java
  12. 11
      dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java
  13. 14
      dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java
  14. 10
      dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleChainRepository.java
  15. 14
      dao/src/main/resources/cassandra/schema-entities.cql
  16. 2
      ui/src/app/api/entity.service.js
  17. 5
      ui/src/app/api/rule-chain.service.js
  18. 2
      ui/src/app/common/types.constant.js
  19. 3
      ui/src/app/locale/locale.constant-en_US.json
  20. 2
      ui/src/app/rulechain/add-rulechain.tpl.html
  21. 2
      ui/src/app/rulechain/add-rulenode.tpl.html
  22. 8
      ui/src/app/rulechain/rulechain-fieldset.tpl.html
  23. 9
      ui/src/app/rulechain/rulechain.controller.js
  24. 7
      ui/src/app/rulechain/rulechain.directive.js
  25. 82
      ui/src/app/rulechain/rulechain.routes.js
  26. 72
      ui/src/app/rulechain/rulechains.controller.js
  27. 1
      ui/src/app/rulechain/rulenode-fieldset.tpl.html
  28. 1
      ui/src/app/rulechain/rulenode.directive.js
  29. 26
      ui/src/app/services/menu.service.js

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

@ -55,6 +55,7 @@ import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.TbMsgMetaData;
@ -231,13 +232,19 @@ public class RuleChainController extends BaseController {
@ResponseBody @ResponseBody
public TextPageData<RuleChain> getRuleChains( public TextPageData<RuleChain> getRuleChains(
@RequestParam int limit, @RequestParam int limit,
@RequestParam(value = "type", required = false) String typeStr,
@RequestParam(required = false) String textSearch, @RequestParam(required = false) String textSearch,
@RequestParam(required = false) String idOffset, @RequestParam(required = false) String idOffset,
@RequestParam(required = false) String textOffset) throws ThingsboardException { @RequestParam(required = false) String textOffset) throws ThingsboardException {
try { try {
TenantId tenantId = getCurrentUser().getTenantId(); TenantId tenantId = getCurrentUser().getTenantId();
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
return checkNotNull(ruleChainService.findTenantRuleChains(tenantId, pageLink)); if (typeStr != null && typeStr.trim().length() > 0) {
RuleChainType type = RuleChainType.valueOf(typeStr);
return checkNotNull(ruleChainService.findTenantRuleChainsByType(tenantId, type, pageLink));
} else {
return checkNotNull(ruleChainService.findTenantRuleChains(tenantId, pageLink));
}
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }

74
application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java

@ -37,6 +37,7 @@ import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.alarm.AlarmStatus; import org.thingsboard.server.common.data.alarm.AlarmStatus;
import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeQueueEntry; import org.thingsboard.server.common.data.edge.EdgeQueueEntry;
import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.AssetId;
@ -44,10 +45,12 @@ import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
import org.thingsboard.server.common.data.kv.DataType;
import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.common.data.kv.LongDataEntry;
import org.thingsboard.server.common.data.page.TimePageData; import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.page.TimePageLink;
@ -59,6 +62,7 @@ import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo;
import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;
import org.thingsboard.server.common.msg.session.SessionMsgType; import org.thingsboard.server.common.msg.session.SessionMsgType;
@ -91,6 +95,7 @@ import org.thingsboard.server.gen.edge.UplinkResponseMsg;
import org.thingsboard.server.gen.edge.UserUpdateMsg; import org.thingsboard.server.gen.edge.UserUpdateMsg;
import org.thingsboard.server.service.edge.EdgeContextComponent; import org.thingsboard.server.service.edge.EdgeContextComponent;
import javax.swing.text.html.parser.Entity;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -102,6 +107,9 @@ import java.util.concurrent.locks.ReentrantLock;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.Consumer; import java.util.function.Consumer;
import static org.thingsboard.server.gen.edge.UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE;
import static org.thingsboard.server.gen.edge.UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE;
@Slf4j @Slf4j
@Data @Data
public final class EdgeGrpcSession implements Cloneable { public final class EdgeGrpcSession implements Cloneable {
@ -175,7 +183,9 @@ public final class EdgeGrpcSession implements Cloneable {
do { do {
pageData = ctx.getEdgeService().findQueueEvents(edge.getTenantId(), edge.getId(), pageLink); pageData = ctx.getEdgeService().findQueueEvents(edge.getTenantId(), edge.getId(), pageLink);
if (!pageData.getData().isEmpty()) { if (!pageData.getData().isEmpty()) {
log.trace("[{}] [{}] event(s) are going to be processed.", this.sessionId, pageData.getData().size());
for (Event event : pageData.getData()) { for (Event event : pageData.getData()) {
log.trace("[{}] Processing event [{}]", this.sessionId, event);
EdgeQueueEntry entry; EdgeQueueEntry entry;
try { try {
entry = objectMapper.treeToValue(event.getBody(), EdgeQueueEntry.class); entry = objectMapper.treeToValue(event.getBody(), EdgeQueueEntry.class);
@ -193,6 +203,9 @@ public final class EdgeGrpcSession implements Cloneable {
processCustomDownlinkMessage(entry); processCustomDownlinkMessage(entry);
break; break;
} }
if (ENTITY_CREATED_RPC_MESSAGE.equals(msgType)) {
pushEntityAttributesToEdge(entry);
}
} catch (Exception e) { } catch (Exception e) {
log.error("Exception during processing records from queue", e); log.error("Exception during processing records from queue", e);
} }
@ -215,6 +228,63 @@ public final class EdgeGrpcSession implements Cloneable {
} }
} }
private void pushEntityAttributesToEdge(EdgeQueueEntry entry) throws IOException {
EntityId entityId = null;
String entityName = null;
switch (entry.getEntityType()) {
case EDGE:
entityId = objectMapper.readValue(entry.getData(), Edge.class).getId();
break;
case DEVICE:
entityId = objectMapper.readValue(entry.getData(), Device.class).getId();
break;
case ASSET:
entityId = objectMapper.readValue(entry.getData(), Asset.class).getId();
break;
case ENTITY_VIEW:
entityId = objectMapper.readValue(entry.getData(), EntityView.class).getId();
break;
case DASHBOARD:
entityId = objectMapper.readValue(entry.getData(), Dashboard.class).getId();
break;
}
if (entityId != null) {
ListenableFuture<List<AttributeKvEntry>> ssAttrFuture = ctx.getAttributesService().findAll(edge.getTenantId(), entityId, DataConstants.SERVER_SCOPE);
Futures.transform(ssAttrFuture, ssAttributes -> {
if (ssAttributes != null && !ssAttributes.isEmpty()) {
try {
TbMsgMetaData metaData = new TbMsgMetaData();
ObjectNode entityNode = objectMapper.createObjectNode();
metaData.putValue("scope", DataConstants.SERVER_SCOPE);
for (AttributeKvEntry attr : ssAttributes) {
if (attr.getDataType() == DataType.BOOLEAN) {
entityNode.put(attr.getKey(), attr.getBooleanValue().get());
} else if (attr.getDataType() == DataType.DOUBLE) {
entityNode.put(attr.getKey(), attr.getDoubleValue().get());
} else if (attr.getDataType() == DataType.LONG) {
entityNode.put(attr.getKey(), attr.getLongValue().get());
} else {
entityNode.put(attr.getKey(), attr.getValueAsString());
}
}
TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), DataConstants.ATTRIBUTES_UPDATED, entityId, metaData, TbMsgDataType.JSON
, objectMapper.writeValueAsString(entityNode)
, null, null, 0L);
log.debug("Sending donwlink entity data msg, entityName [{}], tbMsg [{}]", entityName, tbMsg);
outputStream.onNext(ResponseMsg.newBuilder()
.setDownlinkMsg(constructDownlinkEntityDataMsg(entityName, tbMsg))
.build());
} catch (Exception e) {
log.error("[{}] Failed to send attribute updates to the edge", edge.getName(), e);
}
}
return null;
});
ListenableFuture<List<AttributeKvEntry>> shAttrFuture = ctx.getAttributesService().findAll(edge.getTenantId(), entityId, DataConstants.SHARED_SCOPE);
ListenableFuture<List<AttributeKvEntry>> clAttrFuture = ctx.getAttributesService().findAll(edge.getTenantId(), entityId, DataConstants.CLIENT_SCOPE);
}
}
private void processCustomDownlinkMessage(EdgeQueueEntry entry) throws IOException { private void processCustomDownlinkMessage(EdgeQueueEntry entry) throws IOException {
log.trace("Executing processCustomDownlinkMessage, entry [{}]", entry); log.trace("Executing processCustomDownlinkMessage, entry [{}]", entry);
TbMsg tbMsg = objectMapper.readValue(entry.getData(), TbMsg.class); TbMsg tbMsg = objectMapper.readValue(entry.getData(), TbMsg.class);
@ -411,7 +481,7 @@ public final class EdgeGrpcSession implements Cloneable {
return UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE; return UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE;
case DataConstants.ENTITY_CREATED: case DataConstants.ENTITY_CREATED:
case DataConstants.ENTITY_ASSIGNED_TO_EDGE: case DataConstants.ENTITY_ASSIGNED_TO_EDGE:
return UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE; return ENTITY_CREATED_RPC_MESSAGE;
case DataConstants.ENTITY_DELETED: case DataConstants.ENTITY_DELETED:
case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE: case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE:
return UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE; return UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE;
@ -521,6 +591,8 @@ public final class EdgeGrpcSession implements Cloneable {
private RuleNodeProto constructNode(RuleNode node) throws JsonProcessingException { private RuleNodeProto constructNode(RuleNode node) throws JsonProcessingException {
return RuleNodeProto.newBuilder() return RuleNodeProto.newBuilder()
.setIdMSB(node.getId().getId().getMostSignificantBits())
.setIdLSB(node.getId().getId().getLeastSignificantBits())
.setType(node.getType()) .setType(node.getType())
.setName(node.getName()) .setName(node.getName())
.setDebugMode(node.isDebugMode()) .setDebugMode(node.isDebugMode())

3
common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java

@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.common.data.rule.RuleNode;
import java.util.List; import java.util.List;
@ -62,6 +63,8 @@ public interface RuleChainService {
TextPageData<RuleChain> findTenantRuleChains(TenantId tenantId, TextPageLink pageLink); TextPageData<RuleChain> findTenantRuleChains(TenantId tenantId, TextPageLink pageLink);
TextPageData<RuleChain> findTenantRuleChainsByType(TenantId tenantId, RuleChainType type, TextPageLink pageLink);
void deleteRuleChainById(TenantId tenantId, RuleChainId ruleChainId); void deleteRuleChainById(TenantId tenantId, RuleChainId ruleChainId);
void deleteRuleChainsByTenantId(TenantId tenantId); void deleteRuleChainsByTenantId(TenantId tenantId);

12
common/edge-api/src/main/proto/edge.proto

@ -125,11 +125,13 @@ message RuleChainMetadataUpdateMsg {
} }
message RuleNodeProto { message RuleNodeProto {
string type = 1; int64 idMSB = 1;
string name = 2; int64 idLSB = 2;
bool debugMode = 3; string type = 3;
string configuration = 4; string name = 4;
string additionalInfo = 5; bool debugMode = 5;
string configuration = 6;
string additionalInfo = 7;
} }
message NodeConnectionInfoProto { message NodeConnectionInfoProto {

8
dao/src/main/java/org/thingsboard/server/dao/component/BaseComponentDescriptorService.java

@ -113,16 +113,16 @@ public class BaseComponentDescriptorService implements ComponentDescriptorServic
@Override @Override
protected void validateDataImpl(TenantId tenantId, ComponentDescriptor plugin) { protected void validateDataImpl(TenantId tenantId, ComponentDescriptor plugin) {
if (plugin.getType() == null) { if (plugin.getType() == null) {
throw new DataValidationException("Component type should be specified!."); throw new DataValidationException("Component type should be specified!");
} }
if (plugin.getScope() == null) { if (plugin.getScope() == null) {
throw new DataValidationException("Component scope should be specified!."); throw new DataValidationException("Component scope should be specified!");
} }
if (StringUtils.isEmpty(plugin.getName())) { if (StringUtils.isEmpty(plugin.getName())) {
throw new DataValidationException("Component name should be specified!."); throw new DataValidationException("Component name should be specified!");
} }
if (StringUtils.isEmpty(plugin.getClazz())) { if (StringUtils.isEmpty(plugin.getClazz())) {
throw new DataValidationException("Component clazz should be specified!."); throw new DataValidationException("Component clazz should be specified!");
} }
} }
}; };

16
dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java

@ -54,7 +54,7 @@ public class BaseEventService implements EventService {
public Optional<Event> saveIfNotExists(Event event) { public Optional<Event> saveIfNotExists(Event event) {
eventValidator.validate(event, Event::getTenantId); eventValidator.validate(event, Event::getTenantId);
if (StringUtils.isEmpty(event.getUid())) { if (StringUtils.isEmpty(event.getUid())) {
throw new DataValidationException("Event uid should be specified!."); throw new DataValidationException("Event uid should be specified!");
} }
return eventDao.saveIfNotExists(event); return eventDao.saveIfNotExists(event);
} }
@ -62,16 +62,16 @@ public class BaseEventService implements EventService {
@Override @Override
public Optional<Event> findEvent(TenantId tenantId, EntityId entityId, String eventType, String eventUid) { public Optional<Event> findEvent(TenantId tenantId, EntityId entityId, String eventType, String eventUid) {
if (tenantId == null) { if (tenantId == null) {
throw new DataValidationException("Tenant id should be specified!."); throw new DataValidationException("Tenant id should be specified!");
} }
if (entityId == null) { if (entityId == null) {
throw new DataValidationException("Entity id should be specified!."); throw new DataValidationException("Entity id should be specified!");
} }
if (StringUtils.isEmpty(eventType)) { if (StringUtils.isEmpty(eventType)) {
throw new DataValidationException("Event type should be specified!."); throw new DataValidationException("Event type should be specified!");
} }
if (StringUtils.isEmpty(eventUid)) { if (StringUtils.isEmpty(eventUid)) {
throw new DataValidationException("Event uid should be specified!."); throw new DataValidationException("Event uid should be specified!");
} }
Event event = eventDao.findEvent(tenantId.getId(), entityId, eventType, eventUid); Event event = eventDao.findEvent(tenantId.getId(), entityId, eventType, eventUid);
return event != null ? Optional.of(event) : Optional.empty(); return event != null ? Optional.of(event) : Optional.empty();
@ -99,13 +99,13 @@ public class BaseEventService implements EventService {
@Override @Override
protected void validateDataImpl(TenantId tenantId, Event event) { protected void validateDataImpl(TenantId tenantId, Event event) {
if (event.getEntityId() == null) { if (event.getEntityId() == null) {
throw new DataValidationException("Entity id should be specified!."); throw new DataValidationException("Entity id should be specified!");
} }
if (StringUtils.isEmpty(event.getType())) { if (StringUtils.isEmpty(event.getType())) {
throw new DataValidationException("Event type should be specified!."); throw new DataValidationException("Event type should be specified!");
} }
if (event.getBody() == null) { if (event.getBody() == null) {
throw new DataValidationException("Event body should be specified!."); throw new DataValidationException("Event body should be specified!");
} }
} }
}; };

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

@ -343,6 +343,7 @@ public class ModelConstants {
public static final String RULE_CHAIN_ASSIGNED_EDGES_PROPERTY = "assigned_edges"; public static final String RULE_CHAIN_ASSIGNED_EDGES_PROPERTY = "assigned_edges";
public static final String RULE_CHAIN_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "rule_chain_by_tenant_and_search_text"; public static final String RULE_CHAIN_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "rule_chain_by_tenant_and_search_text";
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";
/** /**
* Cassandra rule node constants. * Cassandra rule node constants.

12
dao/src/main/java/org/thingsboard/server/dao/model/nosql/RuleChainEntity.java

@ -106,11 +106,7 @@ public class RuleChainEntity implements SearchTextEntity<RuleChain> {
} }
this.tenantId = DaoUtil.getId(ruleChain.getTenantId()); this.tenantId = DaoUtil.getId(ruleChain.getTenantId());
this.name = ruleChain.getName(); this.name = ruleChain.getName();
if (ruleChain.getType() != null) { this.type = ruleChain.getType();
this.type = ruleChain.getType();
} else {
this.type = RuleChainType.SYSTEM;
}
this.searchText = ruleChain.getName(); this.searchText = ruleChain.getName();
this.firstRuleNodeId = DaoUtil.getId(ruleChain.getFirstRuleNodeId()); this.firstRuleNodeId = DaoUtil.getId(ruleChain.getFirstRuleNodeId());
this.root = ruleChain.isRoot(); this.root = ruleChain.isRoot();
@ -204,11 +200,7 @@ public class RuleChainEntity implements SearchTextEntity<RuleChain> {
ruleChain.setCreatedTime(UUIDs.unixTimestamp(id)); ruleChain.setCreatedTime(UUIDs.unixTimestamp(id));
ruleChain.setTenantId(new TenantId(tenantId)); ruleChain.setTenantId(new TenantId(tenantId));
ruleChain.setName(name); ruleChain.setName(name);
if (type != null) { ruleChain.setType(type);
ruleChain.setType(type);
} else {
ruleChain.setType(RuleChainType.SYSTEM);
}
if (this.firstRuleNodeId != null) { if (this.firstRuleNodeId != null) {
ruleChain.setFirstRuleNodeId(new RuleNodeId(this.firstRuleNodeId)); ruleChain.setFirstRuleNodeId(new RuleNodeId(this.firstRuleNodeId));
} }

12
dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleChainEntity.java

@ -103,11 +103,7 @@ public class RuleChainEntity extends BaseSqlEntity<RuleChain> implements SearchT
} }
this.tenantId = toString(DaoUtil.getId(ruleChain.getTenantId())); this.tenantId = toString(DaoUtil.getId(ruleChain.getTenantId()));
this.name = ruleChain.getName(); this.name = ruleChain.getName();
if (ruleChain.getType() != null) { this.type = ruleChain.getType();
this.type = ruleChain.getType();
} else {
this.type = RuleChainType.SYSTEM;
}
this.searchText = ruleChain.getName(); this.searchText = ruleChain.getName();
if (ruleChain.getFirstRuleNodeId() != null) { if (ruleChain.getFirstRuleNodeId() != null) {
this.firstRuleNodeId = UUIDConverter.fromTimeUUID(ruleChain.getFirstRuleNodeId().getId()); this.firstRuleNodeId = UUIDConverter.fromTimeUUID(ruleChain.getFirstRuleNodeId().getId());
@ -141,11 +137,7 @@ public class RuleChainEntity extends BaseSqlEntity<RuleChain> implements SearchT
ruleChain.setCreatedTime(UUIDs.unixTimestamp(getId())); ruleChain.setCreatedTime(UUIDs.unixTimestamp(getId()));
ruleChain.setTenantId(new TenantId(toUUID(tenantId))); ruleChain.setTenantId(new TenantId(toUUID(tenantId)));
ruleChain.setName(name); ruleChain.setName(name);
if (type != null) { ruleChain.setType(type);
ruleChain.setType(type);
} else {
ruleChain.setType(RuleChainType.SYSTEM);
}
if (firstRuleNodeId != null) { if (firstRuleNodeId != null) {
ruleChain.setFirstRuleNodeId(new RuleNodeId(UUIDConverter.fromString(firstRuleNodeId))); ruleChain.setFirstRuleNodeId(new RuleNodeId(UUIDConverter.fromString(firstRuleNodeId)));
} }

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

@ -63,6 +63,8 @@ import java.util.Map;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.thingsboard.server.dao.service.Validator.validateString;
/** /**
* Created by igor on 3/12/18. * Created by igor on 3/12/18.
*/ */
@ -358,6 +360,14 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
return new TextPageData<>(ruleChains, pageLink); return new TextPageData<>(ruleChains, pageLink);
} }
@Override
public TextPageData<RuleChain> findTenantRuleChainsByType(TenantId tenantId, RuleChainType type, TextPageLink pageLink) {
Validator.validateId(tenantId, "Incorrect tenant id for search rule chain request.");
Validator.validatePageLink(pageLink, "Incorrect PageLink object for search rule chain request.");
List<RuleChain> ruleChains = ruleChainDao.findRuleChainsByTenantIdAndType(tenantId.getId(), type, pageLink);
return new TextPageData<>(ruleChains, pageLink);
}
@Override @Override
public void deleteRuleChainById(TenantId tenantId, RuleChainId ruleChainId) { public void deleteRuleChainById(TenantId tenantId, RuleChainId ruleChainId) {
Validator.validateId(ruleChainId, "Incorrect rule chain id for delete request."); Validator.validateId(ruleChainId, "Incorrect rule chain id for delete request.");
@ -514,7 +524,10 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
@Override @Override
protected void validateDataImpl(TenantId tenantId, RuleChain ruleChain) { protected void validateDataImpl(TenantId tenantId, RuleChain ruleChain) {
if (StringUtils.isEmpty(ruleChain.getName())) { if (StringUtils.isEmpty(ruleChain.getName())) {
throw new DataValidationException("Rule chain name should be specified!."); throw new DataValidationException("Rule chain name should be specified!");
}
if (ruleChain.getType() == null) {
throw new DataValidationException("Rule chain type should be specified!");
} }
if (ruleChain.getTenantId() == null || ruleChain.getTenantId().isNullUid()) { if (ruleChain.getTenantId() == null || ruleChain.getTenantId().isNullUid()) {
throw new DataValidationException("Rule chain should be assigned to tenant!"); throw new DataValidationException("Rule chain should be assigned to tenant!");

17
dao/src/main/java/org/thingsboard/server/dao/rule/CassandraRuleChainDao.java

@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.model.nosql.RuleChainEntity; import org.thingsboard.server.dao.model.nosql.RuleChainEntity;
import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao; import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao;
@ -35,14 +36,19 @@ import org.thingsboard.server.dao.relation.RelationDao;
import org.thingsboard.server.dao.util.NoSqlDao; import org.thingsboard.server.dao.util.NoSqlDao;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_TENANT_ID_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_TYPE_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME; import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME;
import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME;
import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_COLUMN_FAMILY_NAME; import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_COLUMN_FAMILY_NAME;
import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_TENANT_ID_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_TENANT_ID_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_TYPE_PROPERTY;
@Component @Component
@Slf4j @Slf4j
@ -73,6 +79,17 @@ public class CassandraRuleChainDao extends CassandraAbstractSearchTextDao<RuleCh
return DaoUtil.convertDataList(ruleChainEntities); return DaoUtil.convertDataList(ruleChainEntities);
} }
@Override
public List<RuleChain> findRuleChainsByTenantIdAndType(UUID tenantId, RuleChainType type, TextPageLink pageLink) {
log.debug("Try to find rule chains by tenantId [{}], type [{}] and pageLink [{}]", tenantId, type, pageLink);
List<RuleChainEntity> ruleChainEntities = findPageWithTextSearch(new TenantId(tenantId), RULE_CHAIN_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
Arrays.asList(eq(RULE_CHAIN_TYPE_PROPERTY, type),
eq(RULE_CHAIN_TENANT_ID_PROPERTY, tenantId)),
pageLink);
log.trace("Found rule chains [{}] by tenantId [{}] and pageLink [{}]", ruleChainEntities, tenantId, pageLink);
return DaoUtil.convertDataList(ruleChainEntities);
}
@Override @Override
public ListenableFuture<List<RuleChain>> findRuleChainsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) { public ListenableFuture<List<RuleChain>> findRuleChainsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) {
log.debug("Try to find rule chains by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink); log.debug("Try to find rule chains by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink);

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

@ -19,6 +19,7 @@ import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.Dao;
import java.util.List; import java.util.List;
@ -38,6 +39,16 @@ public interface RuleChainDao extends Dao<RuleChain> {
*/ */
List<RuleChain> findRuleChainsByTenantId(UUID tenantId, TextPageLink pageLink); List<RuleChain> findRuleChainsByTenantId(UUID tenantId, TextPageLink pageLink);
/**
* Find rule chains by tenantId, type and page link.
*
* @param tenantId the tenantId
* @param type the type
* @param pageLink the page link
* @return the list of rule chain objects
*/
List<RuleChain> findRuleChainsByTenantIdAndType(UUID tenantId, RuleChainType type, TextPageLink pageLink);
/** /**
* Find rule chains by tenantId, edgeId and page link. * Find rule chains by tenantId, edgeId and page link.
* *

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

@ -31,6 +31,7 @@ import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.model.sql.RuleChainEntity; import org.thingsboard.server.dao.model.sql.RuleChainEntity;
import org.thingsboard.server.dao.relation.RelationDao; import org.thingsboard.server.dao.relation.RelationDao;
@ -68,6 +69,7 @@ public class JpaRuleChainDao extends JpaAbstractSearchTextDao<RuleChainEntity, R
@Override @Override
public List<RuleChain> findRuleChainsByTenantId(UUID tenantId, TextPageLink pageLink) { public List<RuleChain> findRuleChainsByTenantId(UUID tenantId, TextPageLink pageLink) {
log.debug("Try to find rule chains by tenantId [{}] and pageLink [{}]", tenantId, pageLink);
return DaoUtil.convertDataList(ruleChainRepository return DaoUtil.convertDataList(ruleChainRepository
.findByTenantId( .findByTenantId(
UUIDConverter.fromTimeUUID(tenantId), UUIDConverter.fromTimeUUID(tenantId),
@ -76,6 +78,18 @@ public class JpaRuleChainDao extends JpaAbstractSearchTextDao<RuleChainEntity, R
new PageRequest(0, pageLink.getLimit()))); new PageRequest(0, pageLink.getLimit())));
} }
@Override
public List<RuleChain> findRuleChainsByTenantIdAndType(UUID tenantId, RuleChainType type, TextPageLink pageLink) {
log.debug("Try to find rule chains by tenantId [{}], type [{}] and pageLink [{}]", tenantId, type, pageLink);
return DaoUtil.convertDataList(ruleChainRepository
.findByTenantIdAndType(
UUIDConverter.fromTimeUUID(tenantId),
type,
Objects.toString(pageLink.getTextSearch(), ""),
pageLink.getIdOffset() == null ? NULL_UUID_STR : UUIDConverter.fromTimeUUID(pageLink.getIdOffset()),
new PageRequest(0, pageLink.getLimit())));
}
@Override @Override
public ListenableFuture<List<RuleChain>> findRuleChainsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) { public ListenableFuture<List<RuleChain>> findRuleChainsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) {
log.debug("Try to find rule chains by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink); log.debug("Try to find rule chains by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink);

10
dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleChainRepository.java

@ -19,6 +19,7 @@ import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.dao.model.sql.RuleChainEntity; import org.thingsboard.server.dao.model.sql.RuleChainEntity;
import org.thingsboard.server.dao.util.SqlDao; import org.thingsboard.server.dao.util.SqlDao;
@ -35,4 +36,13 @@ public interface RuleChainRepository extends CrudRepository<RuleChainEntity, Str
@Param("idOffset") String idOffset, @Param("idOffset") String idOffset,
Pageable pageable); Pageable pageable);
@Query("SELECT rc FROM RuleChainEntity rc WHERE rc.tenantId = :tenantId " +
"AND rc.type = :type " +
"AND LOWER(rc.searchText) LIKE LOWER(CONCAT(:searchText, '%')) " +
"AND rc.id > :idOffset ORDER BY rc.id")
List<RuleChainEntity> findByTenantIdAndType(@Param("tenantId") String tenantId,
@Param("type") RuleChainType type,
@Param("searchText") String searchText,
@Param("idOffset") String idOffset,
Pageable pageable);
} }

14
dao/src/main/resources/cassandra/schema-entities.cql

@ -597,22 +597,30 @@ CREATE TABLE IF NOT EXISTS thingsboard.rule_chain (
id uuid, id uuid,
tenant_id uuid, tenant_id uuid,
name text, name text,
type text,
search_text text, search_text text,
first_rule_node_id uuid, first_rule_node_id uuid,
root boolean, root boolean,
debug_mode boolean, debug_mode boolean,
configuration text, configuration text,
additional_info text, additional_info text,
PRIMARY KEY (id, tenant_id) PRIMARY KEY (id, tenant_id, type)
); );
CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.rule_chain_by_tenant_and_search_text AS CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.rule_chain_by_tenant_and_search_text AS
SELECT * SELECT *
from thingsboard.rule_chain from thingsboard.rule_chain
WHERE tenant_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL WHERE tenant_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL AND type IS NOT NULL
PRIMARY KEY ( tenant_id, search_text, id ) PRIMARY KEY ( tenant_id, search_text, id, type )
WITH CLUSTERING ORDER BY ( search_text ASC, id DESC ); WITH CLUSTERING ORDER BY ( search_text ASC, id DESC );
CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.rule_chain_by_tenant_by_type_and_search_text AS
SELECT *
from thingsboard.rule_chain
WHERE tenant_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL AND type IS NOT NULL
PRIMARY KEY ( tenant_id, type, search_text, id )
WITH CLUSTERING ORDER BY ( type ASC, search_text ASC, id DESC );
CREATE TABLE IF NOT EXISTS thingsboard.rule_node ( CREATE TABLE IF NOT EXISTS thingsboard.rule_node (
id uuid, id uuid,
rule_chain_id uuid, rule_chain_id uuid,

2
ui/src/app/api/entity.service.js

@ -277,7 +277,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
} }
break; break;
case types.entityType.rulechain: case types.entityType.rulechain:
promise = ruleChainService.getRuleChains(pageLink, config); promise = ruleChainService.getRuleChains(pageLink, config, subType);
break; break;
case types.entityType.dashboard: case types.entityType.dashboard:
if (user.authority === 'CUSTOMER_USER') { if (user.authority === 'CUSTOMER_USER') {

5
ui/src/app/api/rule-chain.service.js

@ -46,7 +46,7 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co
return service; return service;
function getRuleChains (pageLink, config) { function getRuleChains (pageLink, config, type) {
var deferred = $q.defer(); var deferred = $q.defer();
var url = '/api/ruleChains?limit=' + pageLink.limit; var url = '/api/ruleChains?limit=' + pageLink.limit;
if (angular.isDefined(pageLink.textSearch)) { if (angular.isDefined(pageLink.textSearch)) {
@ -58,6 +58,9 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co
if (angular.isDefined(pageLink.textOffset)) { if (angular.isDefined(pageLink.textOffset)) {
url += '&textOffset=' + pageLink.textOffset; url += '&textOffset=' + pageLink.textOffset;
} }
if (angular.isDefined(type) && type.length) {
url += '&type=' + type;
}
$http.get(url, config).then(function success(response) { $http.get(url, config).then(function success(response) {
deferred.resolve(prepareRuleChains(response.data)); deferred.resolve(prepareRuleChains(response.data));
}, function fail() { }, function fail() {

2
ui/src/app/common/types.constant.js

@ -607,7 +607,7 @@ export default angular.module('thingsboard.types', [])
} }
}, },
systemRuleChainType: "SYSTEM", systemRuleChainType: "SYSTEM",
ruleChainTypes: ["SYSTEM", "EDGE"], edgeRuleChainType: "EDGE",
ruleNodeTypeComponentTypes: ["FILTER", "ENRICHMENT", "TRANSFORMATION", "ACTION", "EXTERNAL"], ruleNodeTypeComponentTypes: ["FILTER", "ENRICHMENT", "TRANSFORMATION", "ACTION", "EXTERNAL"],
ruleChainNodeComponent: { ruleChainNodeComponent: {
type: 'RULE_CHAIN', type: 'RULE_CHAIN',

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

@ -1391,10 +1391,11 @@
"rulechain": { "rulechain": {
"rulechain": "Rule chain", "rulechain": "Rule chain",
"rulechains": "Rule chains", "rulechains": "Rule chains",
"system-rulechains": "System Rule chains",
"edge-rulechains": "Edge Rule chains",
"root": "Root", "root": "Root",
"delete": "Delete rule chain", "delete": "Delete rule chain",
"name": "Name", "name": "Name",
"type": "Type",
"name-required": "Name is required.", "name-required": "Name is required.",
"description": "Description", "description": "Description",
"add": "Add Rule Chain", "add": "Add Rule Chain",

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

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

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

@ -31,7 +31,7 @@
<span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span> <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content> <md-dialog-content>
<div class="md-dialog-content"> <div class="md-dialog-content">
<tb-rule-node rule-node="vm.ruleNode" rule-chain-id="vm.ruleChainId" is-edit="true" the-form="theForm"></tb-rule-node> <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>
</div> </div>
</md-dialog-content> </md-dialog-content>
<md-dialog-actions layout="row"> <md-dialog-actions layout="row">

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

@ -49,14 +49,6 @@
<div translate ng-message="required">rulechain.name-required</div> <div translate ng-message="required">rulechain.name-required</div>
</div> </div>
</md-input-container> </md-input-container>
<md-input-container class="md-block">
<label translate>rulechain.type</label>
<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>
</md-select>
</md-input-container>
<md-input-container class="md-block"> <md-input-container class="md-block">
<md-checkbox ng-disabled="$root.loading || !isEdit" aria-label="{{ 'rulechain.debug-mode' | translate }}" <md-checkbox ng-disabled="$root.loading || !isEdit" aria-label="{{ 'rulechain.debug-mode' | translate }}"
ng-model="ruleChain.debugMode">{{ 'rulechain.debug-mode' | translate }} ng-model="ruleChain.debugMode">{{ 'rulechain.debug-mode' | translate }}

9
ui/src/app/rulechain/rulechain.controller.js

@ -1179,6 +1179,9 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time
function saveRuleChain() { function saveRuleChain() {
var saveRuleChainPromise; var saveRuleChainPromise;
if (vm.isImport) { if (vm.isImport) {
if (angular.isUndefined(vm.ruleChain.type)) {
vm.ruleChain.type = types.systemRuleChainType;
}
saveRuleChainPromise = ruleChainService.saveRuleChain(vm.ruleChain); saveRuleChainPromise = ruleChainService.saveRuleChain(vm.ruleChain);
} else { } else {
saveRuleChainPromise = $q.when(vm.ruleChain); saveRuleChainPromise = $q.when(vm.ruleChain);
@ -1286,6 +1289,7 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time
ruleNode.configuration = angular.copy(ruleNode.component.configurationDescriptor.nodeDefinition.defaultConfiguration); ruleNode.configuration = angular.copy(ruleNode.component.configurationDescriptor.nodeDefinition.defaultConfiguration);
var ruleChainId = vm.ruleChain.id ? vm.ruleChain.id.id : null; var ruleChainId = vm.ruleChain.id ? vm.ruleChain.id.id : null;
var ruleChainType = vm.ruleChain.type ? vm.ruleChain.type : types.systemRuleChainType;
vm.enableHotKeys = false; vm.enableHotKeys = false;
@ -1294,7 +1298,7 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time
controllerAs: 'vm', controllerAs: 'vm',
templateUrl: addRuleNodeTemplate, templateUrl: addRuleNodeTemplate,
parent: angular.element($document[0].body), parent: angular.element($document[0].body),
locals: {ruleNode: ruleNode, ruleChainId: ruleChainId}, locals: {ruleNode: ruleNode, ruleChainId: ruleChainId, ruleChainType: ruleChainType},
fullscreen: true, fullscreen: true,
targetEvent: $event targetEvent: $event
}).then(function (ruleNode) { }).then(function (ruleNode) {
@ -1365,13 +1369,14 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time
} }
/*@ngInject*/ /*@ngInject*/
export function AddRuleNodeController($scope, $mdDialog, ruleNode, ruleChainId, helpLinks) { export function AddRuleNodeController($scope, $mdDialog, ruleNode, ruleChainId, ruleChainType, helpLinks) {
var vm = this; var vm = this;
vm.helpLinks = helpLinks; vm.helpLinks = helpLinks;
vm.ruleNode = ruleNode; vm.ruleNode = ruleNode;
vm.ruleChainId = ruleChainId; vm.ruleChainId = ruleChainId;
vm.ruleChainType = ruleChainType;
vm.add = add; vm.add = add;
vm.cancel = cancel; vm.cancel = cancel;

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

@ -26,12 +26,6 @@ export default function RuleChainDirective($compile, $templateCache, $mdDialog,
var template = $templateCache.get(ruleChainFieldsetTemplate); var template = $templateCache.get(ruleChainFieldsetTemplate);
element.html(template); element.html(template);
scope.ruleChainTypes = types.ruleChainTypes;
if (angular.isDefined(scope.ruleChain) && scope.ruleChain != null && angular.isUndefined(scope.ruleChain.type)) {
scope.ruleChain.type = types.systemRuleChainType;
}
scope.onRuleChainIdCopied = function() { scope.onRuleChainIdCopied = function() {
toast.showSuccess($translate.instant('rulechain.idCopiedMessage'), 750, angular.element(element).parent().parent(), 'bottom left'); toast.showSuccess($translate.instant('rulechain.idCopiedMessage'), 750, angular.element(element).parent().parent(), 'bottom left');
}; };
@ -44,7 +38,6 @@ export default function RuleChainDirective($compile, $templateCache, $mdDialog,
scope: { scope: {
ruleChain: '=', ruleChain: '=',
isEdit: '=', isEdit: '=',
ruleChainScope: '=',
isReadOnly: '=', isReadOnly: '=',
theForm: '=', theForm: '=',
onSetRootRuleChain: '&', onSetRootRuleChain: '&',

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

@ -29,6 +29,15 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider
$stateProvider $stateProvider
.state('home.ruleChains', { .state('home.ruleChains', {
url: '/ruleChains', url: '/ruleChains',
module: 'private',
auth: ['SYS_ADMIN', 'TENANT_ADMIN'],
redirectTo: 'home.ruleChains.system',
ncyBreadcrumb: {
label: '{"icon": "settings_ethernet", "label": "rulechain.rulechains"}'
}
})
.state('home.ruleChains.system', {
url: '/ruleChains/system',
params: {'topIndex': 0}, params: {'topIndex': 0},
module: 'private', module: 'private',
auth: ['SYS_ADMIN', 'TENANT_ADMIN'], auth: ['SYS_ADMIN', 'TENANT_ADMIN'],
@ -41,11 +50,11 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider
}, },
data: { data: {
searchEnabled: true, searchEnabled: true,
pageTitle: 'rulechain.rulechains', pageTitle: 'rulechain.system-rulechains',
ruleChainsType: 'tenant' ruleChainsType: 'tenant'
}, },
ncyBreadcrumb: { ncyBreadcrumb: {
label: '{"icon": "settings_ethernet", "label": "rulechain.rulechains"}' label: '{"icon": "settings_ethernet", "label": "rulechain.system-rulechains"}'
} }
}).state('home.ruleChains.ruleChain', { }).state('home.ruleChains.ruleChain', {
url: '/:ruleChainId', url: '/:ruleChainId',
@ -124,8 +133,64 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider
ncyBreadcrumb: { ncyBreadcrumb: {
label: '{"icon": "settings_ethernet", "label": "{{ (\'rulechain.import\' | translate) + \': \'+ vm.ruleChain.name }}", "translate": "false"}' label: '{"icon": "settings_ethernet", "label": "{{ (\'rulechain.import\' | translate) + \': \'+ vm.ruleChain.name }}", "translate": "false"}'
} }
}) }).state('home.ruleChains.edge', {
.state('home.edges.ruleChains', { url: '/ruleChains/edge',
params: {'topIndex': 0},
module: 'private',
auth: ['TENANT_ADMIN'],
views: {
"content@home": {
templateUrl: ruleChainsTemplate,
controllerAs: 'vm',
controller: 'RuleChainsController'
}
},
data: {
searchEnabled: true,
pageTitle: 'rulechain.edge-rulechains',
ruleChainsType: 'edges'
},
ncyBreadcrumb: {
label: '{"icon": "settings_ethernet", "label": "rulechain.edge-rulechains"}'
}
}).state('home.ruleChains.edge.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": "{{ vm.ruleChain.name }}", "translate": "false"}'
}
}).state('home.edges.ruleChains', {
url: '/:edgeId/ruleChains', url: '/:edgeId/ruleChains',
params: {'topIndex': 0}, params: {'topIndex': 0},
module: 'private', module: 'private',
@ -138,15 +203,14 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider
} }
}, },
data: { data: {
ruleChainsType: 'edge',
searchEnabled: true, searchEnabled: true,
pageTitle: 'edge.rulechains' pageTitle: 'edge.rulechains',
ruleChainsType: 'edge'
}, },
ncyBreadcrumb: { ncyBreadcrumb: {
label: '{"icon": "settings_ethernet", "label": "{{ vm.edgeRuleChainsTitle }}", "translate": "false"}' label: '{"icon": "settings_ethernet", "label": "{{ vm.edgeRuleChainsTitle }}", "translate": "false"}'
} }
}) }).state('home.edges.ruleChains.ruleChain', {
.state('home.edges.ruleChains.ruleChain', {
url: '/:ruleChainId', url: '/:ruleChainId',
reloadOnSearch: false, reloadOnSearch: false,
module: 'private', module: 'private',
@ -181,7 +245,7 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider
pageTitle: 'edge.rulechain' pageTitle: 'edge.rulechain'
}, },
ncyBreadcrumb: { ncyBreadcrumb: {
label: '{"icon": "settings_ethernet", "label": "edge.rulechain"}' label: '{"icon": "settings_ethernet", "label": "{{ vm.ruleChain.name }}", "translate": "false"}'
} }
}); });
} }

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

@ -116,7 +116,7 @@ export default function RuleChainsController(ruleChainService, userService, edge
if (vm.ruleChainsScope === 'tenant') { if (vm.ruleChainsScope === 'tenant') {
fetchRuleChainsFunction = function (pageLink) { fetchRuleChainsFunction = function (pageLink) {
return fetchRuleChains(pageLink); return fetchRuleChains(pageLink, 'SYSTEM');
}; };
deleteRuleChainFunction = function (ruleChainId) { deleteRuleChainFunction = function (ruleChainId) {
return deleteRuleChain(ruleChainId); return deleteRuleChain(ruleChainId);
@ -132,6 +132,57 @@ export default function RuleChainsController(ruleChainService, userService, edge
isEnabled: isNonRootRuleChain isEnabled: isNonRootRuleChain
}); });
ruleChainActionsList.push({
onAction: function ($event, item) {
vm.grid.deleteItem($event, item);
},
name: function() { return $translate.instant('action.delete') },
details: function() { return $translate.instant('rulechain.delete') },
icon: "delete",
isEnabled: isNonRootRuleChain
});
ruleChainGroupActionsList.push(
{
onAction: function ($event) {
vm.grid.deleteItems($event);
},
name: function() { return $translate.instant('rulechain.delete-rulechains') },
details: deleteRuleChainsActionTitle,
icon: "delete"
}
);
vm.ruleChainGridConfig.addItemActions = [];
vm.ruleChainGridConfig.addItemActions.push({
onAction: function ($event) {
vm.grid.addItem($event);
},
name: function() { return $translate.instant('action.create') },
details: function() { return $translate.instant('rulechain.create-new-rulechain') },
icon: "insert_drive_file"
});
vm.ruleChainGridConfig.addItemActions.push({
onAction: function ($event) {
importExport.importRuleChain($event).then(
function(ruleChainImport) {
$state.go('home.ruleChains.importRuleChain', {ruleChainImport:ruleChainImport});
}
);
},
name: function() { return $translate.instant('action.import') },
details: function() { return $translate.instant('rulechain.import') },
icon: "file_upload"
});
} else if (vm.ruleChainsScope === 'edges') {
fetchRuleChainsFunction = function (pageLink) {
return fetchRuleChains(pageLink, 'EDGE');
};
deleteRuleChainFunction = function (ruleChainId) {
return deleteRuleChain(ruleChainId);
};
ruleChainActionsList.push({ ruleChainActionsList.push({
onAction: function ($event, item) { onAction: function ($event, item) {
manageAssignedEdges($event, item); manageAssignedEdges($event, item);
@ -209,7 +260,6 @@ export default function RuleChainsController(ruleChainService, userService, edge
details: function() { return $translate.instant('rulechain.import') }, details: function() { return $translate.instant('rulechain.import') },
icon: "file_upload" icon: "file_upload"
}); });
} else if (vm.ruleChainsScope === 'edge') { } else if (vm.ruleChainsScope === 'edge') {
fetchRuleChainsFunction = function (pageLink) { fetchRuleChainsFunction = function (pageLink) {
return ruleChainService.getEdgeRuleChains(edgeId, pageLink); return ruleChainService.getEdgeRuleChains(edgeId, pageLink);
@ -291,11 +341,18 @@ export default function RuleChainsController(ruleChainService, userService, edge
vm.grid = grid; vm.grid = grid;
} }
function fetchRuleChains(pageLink) { function fetchRuleChains(pageLink, type) {
return ruleChainService.getRuleChains(pageLink); return ruleChainService.getRuleChains(pageLink, null, type);
} }
function saveRuleChain(ruleChain) { function saveRuleChain(ruleChain) {
if (angular.isUndefined(ruleChain.type)) {
if (vm.ruleChainsScope === 'edges') {
ruleChain.type = types.edgeRuleChainType;
} else {
ruleChain.type = types.systemRuleChainType;
}
}
return ruleChainService.saveRuleChain(ruleChain); return ruleChainService.saveRuleChain(ruleChain);
} }
@ -303,10 +360,11 @@ export default function RuleChainsController(ruleChainService, userService, edge
if ($event) { if ($event) {
$event.stopPropagation(); $event.stopPropagation();
} }
if (vm.ruleChainsScope === 'edge') { if (vm.ruleChainsScope === 'edge') {
$state.go('home.edges.ruleChains.ruleChain', { $state.go('home.edges.ruleChains.ruleChain', {ruleChainId: ruleChain.id.id, edgeId: vm.edge.id.id});
ruleChainId: ruleChain.id.id } else if (vm.ruleChainsScope === 'edges') {
}); $state.go('home.ruleChains.edge.ruleChain', {ruleChainId: ruleChain.id.id});
} else { } else {
$state.go('home.ruleChains.ruleChain', {ruleChainId: ruleChain.id.id}); $state.go('home.ruleChains.ruleChain', {ruleChainId: ruleChain.id.id});
} }

1
ui/src/app/rulechain/rulenode-fieldset.tpl.html

@ -53,6 +53,7 @@
tb-required="true" tb-required="true"
exclude-entity-ids="[ruleChainId]" exclude-entity-ids="[ruleChainId]"
entity-type="types.entityType.rulechain" entity-type="types.entityType.rulechain"
entity-subtype="ruleChainType"
ng-model="params.targetRuleChainId"> ng-model="params.targetRuleChainId">
</tb-entity-autocomplete> </tb-entity-autocomplete>
<md-input-container class="md-block"> <md-input-container class="md-block">

1
ui/src/app/rulechain/rulenode.directive.js

@ -70,6 +70,7 @@ export default function RuleNodeDirective($compile, $templateCache, ruleChainSer
link: linker, link: linker,
scope: { scope: {
ruleChainId: '=', ruleChainId: '=',
ruleChainType: '=',
ruleNode: '=', ruleNode: '=',
isEdit: '=', isEdit: '=',
isReadOnly: '=', isReadOnly: '=',

26
ui/src/app/services/menu.service.js

@ -156,9 +156,24 @@ function Menu(userService, $state, $rootScope) {
}, },
{ {
name: 'rulechain.rulechains', name: 'rulechain.rulechains',
type: 'link', type: 'toggle',
state: 'home.ruleChains', state: 'home.ruleChains',
icon: 'settings_ethernet' height: '80px',
icon: 'settings_ethernet',
pages: [
{
name: 'rulechain.system-rulechains',
type: 'link',
state: 'home.ruleChains.system',
icon: 'settings_ethernet'
},
{
name: 'rulechain.edge-rulechains',
type: 'link',
state: 'home.ruleChains.edge',
icon: 'router'
}
]
}, },
{ {
name: 'customer.customers', name: 'customer.customers',
@ -214,9 +229,14 @@ function Menu(userService, $state, $rootScope) {
name: 'rulechain.management', name: 'rulechain.management',
places: [ places: [
{ {
name: 'rulechain.rulechains', name: 'rulechain.system-rulechains',
icon: 'settings_ethernet', icon: 'settings_ethernet',
state: 'home.ruleChains' state: 'home.ruleChains'
},
{
name: 'rulechain.edge-rulechains',
icon: 'router',
state: 'home.edgesRuleChains'
} }
] ]
}, },

Loading…
Cancel
Save