Feature/relation nodes (#1414)

* modify TbCheckRelationNode

* fix TbCheckRelationNode

* modify TbDeleteRelationNode

* fix rule-nodes description

* fix rule-nodes ui

* fix relation action nodes
This commit is contained in:
ShvaykaD 2019-01-28 16:33:57 +02:00 committed by Valerii Sosliuk
parent 94827253e4
commit b966abb3e6
7 changed files with 104 additions and 26 deletions

View File

@ -39,7 +39,9 @@ import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.EntitySearchDirection; import org.thingsboard.server.common.data.relation.EntitySearchDirection;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.customer.CustomerService;
@ -47,6 +49,7 @@ import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.entityview.EntityViewService;
import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -84,7 +87,7 @@ public abstract class TbAbstractRelationActionNode<C extends TbAbstractRelationA
public void destroy() { public void destroy() {
} }
private ListenableFuture<Boolean> processEntityRelationAction(TbContext ctx, TbMsg msg) { protected ListenableFuture<Boolean> processEntityRelationAction(TbContext ctx, TbMsg msg) {
return Futures.transformAsync(getEntity(ctx, msg), entityContainer -> doProcessEntityRelationAction(ctx, msg, entityContainer)); return Futures.transformAsync(getEntity(ctx, msg), entityContainer -> doProcessEntityRelationAction(ctx, msg, entityContainer));
} }
@ -111,7 +114,7 @@ public abstract class TbAbstractRelationActionNode<C extends TbAbstractRelationA
}); });
} }
protected void processSearchDirection(TbMsg msg, EntityContainer entityContainer) { protected void processSingleSearchDirection(TbMsg msg, EntityContainer entityContainer) {
if (EntitySearchDirection.FROM.name().equals(config.getDirection())) { if (EntitySearchDirection.FROM.name().equals(config.getDirection())) {
fromId = EntityIdFactory.getByTypeAndId(entityContainer.getEntityType().name(), entityContainer.getEntityId().toString()); fromId = EntityIdFactory.getByTypeAndId(entityContainer.getEntityType().name(), entityContainer.getEntityId().toString());
toId = msg.getOriginator(); toId = msg.getOriginator();
@ -121,6 +124,14 @@ public abstract class TbAbstractRelationActionNode<C extends TbAbstractRelationA
} }
} }
protected ListenableFuture<List<EntityRelation>> processListSearchDirection(TbContext ctx, TbMsg msg) {
if (EntitySearchDirection.FROM.name().equals(config.getDirection())) {
return ctx.getRelationService().findByToAndTypeAsync(ctx.getTenantId(), msg.getOriginator(), config.getRelationType(), RelationTypeGroup.COMMON);
} else {
return ctx.getRelationService().findByFromAndTypeAsync(ctx.getTenantId(), msg.getOriginator(), config.getRelationType(), RelationTypeGroup.COMMON);
}
}
@Data @Data
@AllArgsConstructor @AllArgsConstructor
private static class Entitykey { private static class Entitykey {

View File

@ -65,7 +65,7 @@ public class TbCreateRelationNode extends TbAbstractRelationActionNode<TbCreateR
} }
private ListenableFuture<Boolean> createIfAbsent(TbContext ctx, TbMsg msg, EntityContainer entityContainer) { private ListenableFuture<Boolean> createIfAbsent(TbContext ctx, TbMsg msg, EntityContainer entityContainer) {
processSearchDirection(msg, entityContainer); processSingleSearchDirection(msg, entityContainer);
return Futures.transformAsync(ctx.getRelationService().checkRelation(ctx.getTenantId(), fromId, toId, config.getRelationType(), RelationTypeGroup.COMMON), return Futures.transformAsync(ctx.getRelationService().checkRelation(ctx.getTenantId(), fromId, toId, config.getRelationType(), RelationTypeGroup.COMMON),
result -> { result -> {
if (!result) { if (!result) {

View File

@ -25,18 +25,24 @@ import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.rule.engine.api.util.TbNodeUtils;
import org.thingsboard.rule.engine.util.EntityContainer; import org.thingsboard.rule.engine.util.EntityContainer;
import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.data.plugin.ComponentType;
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.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
import java.util.ArrayList;
import java.util.List;
@Slf4j @Slf4j
@RuleNode( @RuleNode(
type = ComponentType.ACTION, type = ComponentType.ACTION,
name = "delete relation", name = "delete relation",
configClazz = TbDeleteRelationNodeConfiguration.class, configClazz = TbDeleteRelationNodeConfiguration.class,
nodeDescription = "Finds target Entity by entity name pattern and then delete a relation to Originator Entity by type and direction.", nodeDescription = "Finds target Entity by entity name pattern and then delete a relation to Originator Entity by type and direction" +
nodeDetails = "If the relation successfully deleted - Message send via <b>Success</b> chain, otherwise <b>Failure</b> chain will be used.", " if 'Delete single entity' is set to true, otherwise rule node will delete all relations to the originator of the message by type and direction.",
nodeDetails = "If the relation(s) successfully deleted - Message send via <b>Success</b> chain, otherwise <b>Failure</b> chain will be used.",
uiResources = {"static/rulenode/rulenode-core-config.js"}, uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbActionNodeDeleteRelationConfig", configDirective ="tbActionNodeDeleteRelationConfig",
icon = "remove_circle" icon = "remove_circle"
) )
public class TbDeleteRelationNode extends TbAbstractRelationActionNode<TbDeleteRelationNodeConfiguration> { public class TbDeleteRelationNode extends TbAbstractRelationActionNode<TbDeleteRelationNodeConfiguration> {
@ -52,22 +58,52 @@ public class TbDeleteRelationNode extends TbAbstractRelationActionNode<TbDeleteR
} }
@Override @Override
protected ListenableFuture<Boolean> doProcessEntityRelationAction(TbContext ctx, TbMsg msg, EntityContainer entityContainer) { protected ListenableFuture<Boolean> processEntityRelationAction(TbContext ctx, TbMsg msg) {
return deleteIfExist(ctx, msg, entityContainer); if(config.isDeleteForSingleEntity()){
return Futures.transformAsync(getEntity(ctx, msg), entityContainer -> doProcessEntityRelationAction(ctx, msg, entityContainer));
} else {
return processList(ctx, msg);
}
} }
private ListenableFuture<Boolean> deleteIfExist(TbContext ctx, TbMsg msg, EntityContainer entityContainer) { @Override
processSearchDirection(msg, entityContainer); protected ListenableFuture<Boolean> doProcessEntityRelationAction(TbContext ctx, TbMsg msg, EntityContainer entityContainer) {
return processSingle(ctx, msg, entityContainer);
}
private ListenableFuture<Boolean> processList(TbContext ctx, TbMsg msg) {
return Futures.transformAsync(processListSearchDirection(ctx, msg), entityRelations -> {
if(entityRelations.isEmpty()){
return Futures.immediateFuture(true);
} else {
List<ListenableFuture<Boolean>> listenableFutureList = new ArrayList<>();
for (EntityRelation entityRelation: entityRelations) {
listenableFutureList.add(ctx.getRelationService().deleteRelationAsync(ctx.getTenantId(), entityRelation));
}
return Futures.transformAsync(Futures.allAsList(listenableFutureList), booleans -> {
for (Boolean bool : booleans) {
if (!bool) {
return Futures.immediateFuture(false);
}
}
return Futures.immediateFuture(true);
});
}
});
}
private ListenableFuture<Boolean> processSingle(TbContext ctx, TbMsg msg, EntityContainer entityContainer) {
processSingleSearchDirection(msg, entityContainer);
return Futures.transformAsync(ctx.getRelationService().checkRelation(ctx.getTenantId(), fromId, toId, config.getRelationType(), RelationTypeGroup.COMMON), return Futures.transformAsync(ctx.getRelationService().checkRelation(ctx.getTenantId(), fromId, toId, config.getRelationType(), RelationTypeGroup.COMMON),
result -> { result -> {
if (result) { if (result) {
return processDeleteRelation(ctx); return processSingleDeleteRelation(ctx);
} }
return Futures.immediateFuture(true); return Futures.immediateFuture(true);
}); });
} }
private ListenableFuture<Boolean> processDeleteRelation(TbContext ctx) { private ListenableFuture<Boolean> processSingleDeleteRelation(TbContext ctx) {
return ctx.getRelationService().deleteRelationAsync(ctx.getTenantId(), fromId, toId, config.getRelationType(), RelationTypeGroup.COMMON); return ctx.getRelationService().deleteRelationAsync(ctx.getTenantId(), fromId, toId, config.getRelationType(), RelationTypeGroup.COMMON);
} }

View File

@ -22,10 +22,12 @@ import org.thingsboard.server.common.data.relation.EntitySearchDirection;
@Data @Data
public class TbDeleteRelationNodeConfiguration extends TbAbstractRelationActionNodeConfiguration implements NodeConfiguration<TbDeleteRelationNodeConfiguration> { public class TbDeleteRelationNodeConfiguration extends TbAbstractRelationActionNodeConfiguration implements NodeConfiguration<TbDeleteRelationNodeConfiguration> {
private boolean deleteForSingleEntity;
@Override @Override
public TbDeleteRelationNodeConfiguration defaultConfiguration() { public TbDeleteRelationNodeConfiguration defaultConfiguration() {
TbDeleteRelationNodeConfiguration configuration = new TbDeleteRelationNodeConfiguration(); TbDeleteRelationNodeConfiguration configuration = new TbDeleteRelationNodeConfiguration();
configuration.setDeleteForSingleEntity(true);
configuration.setDirection(EntitySearchDirection.FROM.name()); configuration.setDirection(EntitySearchDirection.FROM.name());
configuration.setRelationType("Contains"); configuration.setRelationType("Contains");
configuration.setEntityNamePattern(""); configuration.setEntityNamePattern("");

View File

@ -15,6 +15,8 @@
*/ */
package org.thingsboard.rule.engine.filter; package org.thingsboard.rule.engine.filter;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.RuleNode;
import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbContext;
@ -25,13 +27,13 @@ import org.thingsboard.rule.engine.api.util.TbNodeUtils;
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.EntityIdFactory;
import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.EntitySearchDirection; import org.thingsboard.server.common.data.relation.EntitySearchDirection;
import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
import javax.management.relation.RelationType; import java.util.List;
import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS;
import static org.thingsboard.rule.engine.api.util.DonAsynchron.withCallback; import static org.thingsboard.rule.engine.api.util.DonAsynchron.withCallback;
/** /**
@ -43,8 +45,10 @@ import static org.thingsboard.rule.engine.api.util.DonAsynchron.withCallback;
name = "check relation", name = "check relation",
configClazz = TbCheckRelationNodeConfiguration.class, configClazz = TbCheckRelationNodeConfiguration.class,
relationTypes = {"True", "False"}, relationTypes = {"True", "False"},
nodeDescription = "Checks the relation from the selected entity to originator of the message by type and direction", nodeDescription = "Checks the relation from the selected entity to the originator of the message by type and direction" +
nodeDetails = "If relation exists - send Message via <b>True</b> chain, otherwise <b>False</b> chain is used.", " if 'Check for single entity' is set to true, otherwise rule node will check if exist" +
" any relation to the originator of the message by type and direction.",
nodeDetails = "If at least one relation exists - send Message via <b>True</b> chain, otherwise <b>False</b> chain is used.",
uiResources = {"static/rulenode/rulenode-core-config.js"}, uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbFilterNodeCheckRelationConfig") configDirective = "tbFilterNodeCheckRelationConfig")
public class TbCheckRelationNode implements TbNode { public class TbCheckRelationNode implements TbNode {
@ -58,6 +62,16 @@ public class TbCheckRelationNode implements TbNode {
@Override @Override
public void onMsg(TbContext ctx, TbMsg msg) throws TbNodeException { public void onMsg(TbContext ctx, TbMsg msg) throws TbNodeException {
ListenableFuture<Boolean> checkRelationFuture;
if (config.isCheckForSingleEntity()) {
checkRelationFuture = processSingle(ctx, msg);
} else {
checkRelationFuture = processList(ctx, msg);
}
withCallback(checkRelationFuture, filterResult -> ctx.tellNext(msg, filterResult ? "True" : "False"), t -> ctx.tellFailure(msg, t), ctx.getDbCallbackExecutor());
}
private ListenableFuture<Boolean> processSingle(TbContext ctx, TbMsg msg) {
EntityId from; EntityId from;
EntityId to; EntityId to;
if (EntitySearchDirection.FROM.name().equals(config.getDirection())) { if (EntitySearchDirection.FROM.name().equals(config.getDirection())) {
@ -67,8 +81,25 @@ public class TbCheckRelationNode implements TbNode {
to = EntityIdFactory.getByTypeAndId(config.getEntityType(), config.getEntityId()); to = EntityIdFactory.getByTypeAndId(config.getEntityType(), config.getEntityId());
from = msg.getOriginator(); from = msg.getOriginator();
} }
withCallback(ctx.getRelationService().checkRelation(ctx.getTenantId(), from, to, config.getRelationType(), RelationTypeGroup.COMMON), return ctx.getRelationService().checkRelation(ctx.getTenantId(), from, to, config.getRelationType(), RelationTypeGroup.COMMON);
filterResult -> ctx.tellNext(msg, filterResult ? "True" : "False"), t -> ctx.tellFailure(msg, t), ctx.getDbCallbackExecutor()); }
private ListenableFuture<Boolean> processList(TbContext ctx, TbMsg msg) {
if (EntitySearchDirection.FROM.name().equals(config.getDirection())) {
return Futures.transformAsync(ctx.getRelationService()
.findByToAndTypeAsync(ctx.getTenantId(), msg.getOriginator(), config.getRelationType(), RelationTypeGroup.COMMON), this::isEmptyList);
} else {
return Futures.transformAsync(ctx.getRelationService()
.findByFromAndTypeAsync(ctx.getTenantId(), msg.getOriginator(), config.getRelationType(), RelationTypeGroup.COMMON), this::isEmptyList);
}
}
private ListenableFuture<Boolean> isEmptyList(List<EntityRelation> entityRelations) {
if (entityRelations.isEmpty()) {
return Futures.immediateFuture(false);
} else {
return Futures.immediateFuture(true);
}
} }
@Override @Override

View File

@ -18,10 +18,6 @@ package org.thingsboard.rule.engine.filter;
import lombok.Data; import lombok.Data;
import org.thingsboard.rule.engine.api.NodeConfiguration; import org.thingsboard.rule.engine.api.NodeConfiguration;
import org.thingsboard.server.common.data.relation.EntitySearchDirection; import org.thingsboard.server.common.data.relation.EntitySearchDirection;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import java.util.Arrays;
import java.util.List;
/** /**
* Created by ashvayka on 19.01.18. * Created by ashvayka on 19.01.18.
@ -33,12 +29,14 @@ public class TbCheckRelationNodeConfiguration implements NodeConfiguration<TbChe
private String entityId; private String entityId;
private String entityType; private String entityType;
private String relationType; private String relationType;
private boolean checkForSingleEntity;
@Override @Override
public TbCheckRelationNodeConfiguration defaultConfiguration() { public TbCheckRelationNodeConfiguration defaultConfiguration() {
TbCheckRelationNodeConfiguration configuration = new TbCheckRelationNodeConfiguration(); TbCheckRelationNodeConfiguration configuration = new TbCheckRelationNodeConfiguration();
configuration.setDirection(EntitySearchDirection.FROM.name()); configuration.setDirection(EntitySearchDirection.FROM.name());
configuration.setRelationType("Contains"); configuration.setRelationType("Contains");
configuration.setCheckForSingleEntity(true);
return configuration; return configuration;
} }
} }