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.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 {
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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("");
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user