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:
		
							parent
							
								
									94827253e4
								
							
						
					
					
						commit
						b966abb3e6
					
				@ -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.page.TextPageData;
 | 
			
		||||
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.RelationTypeGroup;
 | 
			
		||||
import org.thingsboard.server.common.msg.TbMsg;
 | 
			
		||||
import org.thingsboard.server.dao.asset.AssetService;
 | 
			
		||||
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.entityview.EntityViewService;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Optional;
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
 | 
			
		||||
@ -84,7 +87,7 @@ public abstract class TbAbstractRelationActionNode<C extends TbAbstractRelationA
 | 
			
		||||
    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));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -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())) {
 | 
			
		||||
            fromId = EntityIdFactory.getByTypeAndId(entityContainer.getEntityType().name(), entityContainer.getEntityId().toString());
 | 
			
		||||
            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
 | 
			
		||||
    @AllArgsConstructor
 | 
			
		||||
    private static class Entitykey {
 | 
			
		||||
 | 
			
		||||
@ -65,7 +65,7 @@ public class TbCreateRelationNode extends TbAbstractRelationActionNode<TbCreateR
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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),
 | 
			
		||||
                result -> {
 | 
			
		||||
                    if (!result) {
 | 
			
		||||
 | 
			
		||||
@ -25,18 +25,24 @@ import org.thingsboard.rule.engine.api.TbNodeException;
 | 
			
		||||
import org.thingsboard.rule.engine.api.util.TbNodeUtils;
 | 
			
		||||
import org.thingsboard.rule.engine.util.EntityContainer;
 | 
			
		||||
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.msg.TbMsg;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@Slf4j
 | 
			
		||||
@RuleNode(
 | 
			
		||||
        type = ComponentType.ACTION,
 | 
			
		||||
        name = "delete relation",
 | 
			
		||||
        configClazz = TbDeleteRelationNodeConfiguration.class,
 | 
			
		||||
        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.",
 | 
			
		||||
        nodeDescription = "Finds target Entity by entity name pattern and then delete a relation to Originator Entity by type and direction" +
 | 
			
		||||
                " 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"},
 | 
			
		||||
        configDirective = "tbActionNodeDeleteRelationConfig",
 | 
			
		||||
        configDirective ="tbActionNodeDeleteRelationConfig",
 | 
			
		||||
        icon = "remove_circle"
 | 
			
		||||
)
 | 
			
		||||
public class TbDeleteRelationNode extends TbAbstractRelationActionNode<TbDeleteRelationNodeConfiguration> {
 | 
			
		||||
@ -52,22 +58,52 @@ public class TbDeleteRelationNode extends TbAbstractRelationActionNode<TbDeleteR
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected ListenableFuture<Boolean> doProcessEntityRelationAction(TbContext ctx, TbMsg msg, EntityContainer entityContainer) {
 | 
			
		||||
        return deleteIfExist(ctx, msg, entityContainer);
 | 
			
		||||
    protected ListenableFuture<Boolean> processEntityRelationAction(TbContext ctx, TbMsg msg) {
 | 
			
		||||
        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) {
 | 
			
		||||
        processSearchDirection(msg, entityContainer);
 | 
			
		||||
    @Override
 | 
			
		||||
    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),
 | 
			
		||||
                result -> {
 | 
			
		||||
                    if (result) {
 | 
			
		||||
                        return processDeleteRelation(ctx);
 | 
			
		||||
                        return processSingleDeleteRelation(ctx);
 | 
			
		||||
                    }
 | 
			
		||||
                    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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -22,10 +22,12 @@ import org.thingsboard.server.common.data.relation.EntitySearchDirection;
 | 
			
		||||
@Data
 | 
			
		||||
public class TbDeleteRelationNodeConfiguration extends TbAbstractRelationActionNodeConfiguration implements NodeConfiguration<TbDeleteRelationNodeConfiguration> {
 | 
			
		||||
 | 
			
		||||
    private boolean deleteForSingleEntity;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public TbDeleteRelationNodeConfiguration defaultConfiguration() {
 | 
			
		||||
        TbDeleteRelationNodeConfiguration configuration = new TbDeleteRelationNodeConfiguration();
 | 
			
		||||
        configuration.setDeleteForSingleEntity(true);
 | 
			
		||||
        configuration.setDirection(EntitySearchDirection.FROM.name());
 | 
			
		||||
        configuration.setRelationType("Contains");
 | 
			
		||||
        configuration.setEntityNamePattern("");
 | 
			
		||||
 | 
			
		||||
@ -15,6 +15,8 @@
 | 
			
		||||
 */
 | 
			
		||||
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 org.thingsboard.rule.engine.api.RuleNode;
 | 
			
		||||
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.EntityIdFactory;
 | 
			
		||||
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.RelationTypeGroup;
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@ -43,8 +45,10 @@ import static org.thingsboard.rule.engine.api.util.DonAsynchron.withCallback;
 | 
			
		||||
        name = "check relation",
 | 
			
		||||
        configClazz = TbCheckRelationNodeConfiguration.class,
 | 
			
		||||
        relationTypes = {"True", "False"},
 | 
			
		||||
        nodeDescription = "Checks the relation from the selected entity to 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.",
 | 
			
		||||
        nodeDescription = "Checks the relation from the selected entity to the originator of the message by type and direction" +
 | 
			
		||||
                " 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"},
 | 
			
		||||
        configDirective = "tbFilterNodeCheckRelationConfig")
 | 
			
		||||
public class TbCheckRelationNode implements TbNode {
 | 
			
		||||
@ -58,6 +62,16 @@ public class TbCheckRelationNode implements TbNode {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    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 to;
 | 
			
		||||
        if (EntitySearchDirection.FROM.name().equals(config.getDirection())) {
 | 
			
		||||
@ -67,8 +81,25 @@ public class TbCheckRelationNode implements TbNode {
 | 
			
		||||
            to = EntityIdFactory.getByTypeAndId(config.getEntityType(), config.getEntityId());
 | 
			
		||||
            from = msg.getOriginator();
 | 
			
		||||
        }
 | 
			
		||||
        withCallback(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());
 | 
			
		||||
        return ctx.getRelationService().checkRelation(ctx.getTenantId(), from, to, config.getRelationType(), RelationTypeGroup.COMMON);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
@ -18,10 +18,6 @@ package org.thingsboard.rule.engine.filter;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import org.thingsboard.rule.engine.api.NodeConfiguration;
 | 
			
		||||
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.
 | 
			
		||||
@ -33,12 +29,14 @@ public class TbCheckRelationNodeConfiguration implements NodeConfiguration<TbChe
 | 
			
		||||
    private String entityId;
 | 
			
		||||
    private String entityType;
 | 
			
		||||
    private String relationType;
 | 
			
		||||
    private boolean checkForSingleEntity;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public TbCheckRelationNodeConfiguration defaultConfiguration() {
 | 
			
		||||
        TbCheckRelationNodeConfiguration configuration = new TbCheckRelationNodeConfiguration();
 | 
			
		||||
        configuration.setDirection(EntitySearchDirection.FROM.name());
 | 
			
		||||
        configuration.setRelationType("Contains");
 | 
			
		||||
        configuration.setCheckForSingleEntity(true);
 | 
			
		||||
        return configuration;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user