Commit 5752ba81a43a75f542a405467081ff449c0575e4

Authored by Andrew Shvayka
1 parent fd1aaba6

Implementation of Flows in Rule Engine

Showing 18 changed files with 361 additions and 253 deletions
... ... @@ -15,6 +15,8 @@
15 15 */
16 16 package org.thingsboard.server.actors.ruleChain;
17 17
  18 +import akka.actor.OneForOneStrategy;
  19 +import akka.actor.SupervisorStrategy;
18 20 import org.thingsboard.server.actors.ActorSystemContext;
19 21 import org.thingsboard.server.actors.service.ComponentActor;
20 22 import org.thingsboard.server.actors.service.ContextBasedCreator;
... ... @@ -23,6 +25,7 @@ import org.thingsboard.server.common.data.id.TenantId;
23 25 import org.thingsboard.server.common.msg.TbActorMsg;
24 26 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
25 27 import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
  28 +import scala.concurrent.duration.Duration;
26 29
27 30 public class RuleChainActor extends ComponentActor<RuleChainId, RuleChainActorMessageProcessor> {
28 31
... ... @@ -73,5 +76,13 @@ public class RuleChainActor extends ComponentActor<RuleChainId, RuleChainActorMe
73 76 return systemContext.getRuleChainErrorPersistFrequency();
74 77 }
75 78
76   - //TODO: failover strategy
  79 + @Override
  80 + public SupervisorStrategy supervisorStrategy() {
  81 + return strategy;
  82 + }
  83 +
  84 + private final SupervisorStrategy strategy = new OneForOneStrategy(3, Duration.create("1 minute"), t -> {
  85 + logAndPersist("Unknown Failure", ActorSystemContext.toException(t));
  86 + return SupervisorStrategy.resume();
  87 + });
77 88 }
... ...
... ... @@ -27,12 +27,14 @@ import org.thingsboard.server.common.data.id.EntityId;
27 27 import org.thingsboard.server.common.data.id.RuleChainId;
28 28 import org.thingsboard.server.common.data.id.RuleNodeId;
29 29 import org.thingsboard.server.common.data.id.TenantId;
  30 +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
30 31 import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
31 32 import org.thingsboard.server.common.data.relation.EntityRelation;
32 33 import org.thingsboard.server.common.data.rule.RuleChain;
33 34 import org.thingsboard.server.common.data.rule.RuleNode;
34 35 import org.thingsboard.server.common.msg.TbMsg;
35 36 import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
  37 +import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
36 38 import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
37 39 import org.thingsboard.server.dao.rule.RuleChainService;
38 40
... ... @@ -40,6 +42,8 @@ import java.util.ArrayList;
40 42 import java.util.HashMap;
41 43 import java.util.List;
42 44 import java.util.Map;
  45 +import java.util.Set;
  46 +import java.util.stream.Collectors;
43 47
44 48 /**
45 49 * @author Andrew Shvayka
... ... @@ -54,7 +58,6 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
54 58
55 59 private RuleNodeId firstId;
56 60 private RuleNodeCtx firstNode;
57   - private ComponentLifecycleState state;
58 61
59 62 RuleChainActorMessageProcessor(TenantId tenantId, RuleChainId ruleChainId, ActorSystemContext systemContext
60 63 , LoggingAdapter logger, ActorRef parent, ActorRef self) {
... ... @@ -68,20 +71,64 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
68 71
69 72 @Override
70 73 public void start(ActorContext context) throws Exception {
71   - if (state == ComponentLifecycleState.ACTIVE) {
72   - return;
73   - }
74 74 RuleChain ruleChain = service.findRuleChainById(entityId);
75 75 List<RuleNode> ruleNodeList = service.getRuleChainNodes(entityId);
76 76 // Creating and starting the actors;
77 77 for (RuleNode ruleNode : ruleNodeList) {
78   - String dispatcherName = tenantId.getId().equals(EntityId.NULL_UUID) ?
79   - DefaultActorService.SYSTEM_RULE_DISPATCHER_NAME : DefaultActorService.TENANT_RULE_DISPATCHER_NAME;
80   - ActorRef ruleNodeActor = context.actorOf(
81   - Props.create(new RuleNodeActor.ActorCreator(systemContext, tenantId, entityId, ruleNode.getId()))
82   - .withDispatcher(dispatcherName), ruleNode.getId().toString());
  78 + ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode);
83 79 nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode));
84 80 }
  81 + initRoutes(ruleChain, ruleNodeList);
  82 + }
  83 +
  84 + @Override
  85 + public void onUpdate(ActorContext context) throws Exception {
  86 + RuleChain ruleChain = service.findRuleChainById(entityId);
  87 + List<RuleNode> ruleNodeList = service.getRuleChainNodes(entityId);
  88 +
  89 + for (RuleNode ruleNode : ruleNodeList) {
  90 + RuleNodeCtx existing = nodeActors.get(ruleNode.getId());
  91 + if (existing == null) {
  92 + ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode);
  93 + nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode));
  94 + } else {
  95 + existing.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, existing.getSelf().getId(), ComponentLifecycleEvent.UPDATED), self);
  96 + }
  97 + }
  98 +
  99 + Set<RuleNodeId> existingNodes = ruleNodeList.stream().map(RuleNode::getId).collect(Collectors.toSet());
  100 + List<RuleNodeId> removedRules = nodeActors.keySet().stream().filter(node -> !existingNodes.contains(node)).collect(Collectors.toList());
  101 + removedRules.forEach(ruleNodeId -> {
  102 + RuleNodeCtx removed = nodeActors.remove(ruleNodeId);
  103 + removed.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, removed.getSelf().getId(), ComponentLifecycleEvent.DELETED), self);
  104 + });
  105 +
  106 + initRoutes(ruleChain, ruleNodeList);
  107 + }
  108 +
  109 + @Override
  110 + public void stop(ActorContext context) throws Exception {
  111 + nodeActors.values().stream().map(RuleNodeCtx::getSelfActor).forEach(context::stop);
  112 + nodeActors.clear();
  113 + nodeRoutes.clear();
  114 + context.stop(self);
  115 + }
  116 +
  117 + @Override
  118 + public void onClusterEventMsg(ClusterEventMsg msg) throws Exception {
  119 +
  120 + }
  121 +
  122 + private ActorRef createRuleNodeActor(ActorContext context, RuleNode ruleNode) {
  123 + String dispatcherName = tenantId.getId().equals(EntityId.NULL_UUID) ?
  124 + DefaultActorService.SYSTEM_RULE_DISPATCHER_NAME : DefaultActorService.TENANT_RULE_DISPATCHER_NAME;
  125 + return context.actorOf(
  126 + Props.create(new RuleNodeActor.ActorCreator(systemContext, tenantId, entityId, ruleNode.getId()))
  127 + .withDispatcher(dispatcherName), ruleNode.getId().toString());
  128 + }
  129 +
  130 + private void initRoutes(RuleChain ruleChain, List<RuleNode> ruleNodeList) {
  131 + nodeRoutes.clear();
85 132 // Populating the routes map;
86 133 for (RuleNode ruleNode : ruleNodeList) {
87 134 List<EntityRelation> relations = service.getRuleNodeRelations(ruleNode.getId());
... ... @@ -102,19 +149,6 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
102 149 state = ComponentLifecycleState.ACTIVE;
103 150 }
104 151
105   - @Override
106   - public void stop(ActorContext context) throws Exception {
107   - nodeActors.values().stream().map(RuleNodeCtx::getSelfActor).forEach(context::stop);
108   - nodeActors.clear();
109   - nodeRoutes.clear();
110   - state = ComponentLifecycleState.SUSPENDED;
111   - }
112   -
113   - @Override
114   - public void onClusterEventMsg(ClusterEventMsg msg) throws Exception {
115   -
116   - }
117   -
118 152 void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg envelope) {
119 153 checkActive();
120 154 TbMsg tbMsg = envelope.getTbMsg();
... ... @@ -126,7 +160,6 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
126 160 checkActive();
127 161 RuleNodeId originator = envelope.getOriginator();
128 162 String targetRelationType = envelope.getRelationType();
129   - //TODO: log debug output
130 163 List<RuleNodeRelation> relations = nodeRoutes.get(originator);
131 164 if (relations == null) {
132 165 return;
... ... @@ -153,12 +186,8 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
153 186 }
154 187
155 188 private void pushMstToNode(RuleNodeCtx nodeCtx, TbMsg msg) {
156   - firstNode.getSelfActor().tell(new RuleChainToRuleNodeMsg(new DefaultTbContext(systemContext, nodeCtx), msg), self);
157   - }
158   -
159   - private void checkActive() {
160   - if (state != ComponentLifecycleState.ACTIVE) {
161   - throw new IllegalStateException("Rule chain is not active!");
  189 + if (nodeCtx != null) {
  190 + nodeCtx.getSelfActor().tell(new RuleChainToRuleNodeMsg(new DefaultTbContext(systemContext, nodeCtx), msg), self);
162 191 }
163 192 }
164 193
... ...
... ... @@ -17,7 +17,6 @@ package org.thingsboard.server.actors.ruleChain;
17 17
18 18 import lombok.Data;
19 19 import org.thingsboard.rule.engine.api.TbContext;
20   -import org.thingsboard.server.common.data.id.RuleNodeId;
21 20 import org.thingsboard.server.common.msg.MsgType;
22 21 import org.thingsboard.server.common.msg.TbActorMsg;
23 22 import org.thingsboard.server.common.msg.TbMsg;
... ...
... ... @@ -22,6 +22,7 @@ import org.thingsboard.server.common.data.id.RuleChainId;
22 22 import org.thingsboard.server.common.data.id.RuleNodeId;
23 23 import org.thingsboard.server.common.data.id.TenantId;
24 24 import org.thingsboard.server.common.msg.TbActorMsg;
  25 +import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
25 26
26 27 public class RuleNodeActor extends ComponentActor<RuleNodeId, RuleNodeActorMessageProcessor> {
27 28
... ... @@ -37,6 +38,9 @@ public class RuleNodeActor extends ComponentActor<RuleNodeId, RuleNodeActorMessa
37 38 @Override
38 39 protected boolean process(TbActorMsg msg) {
39 40 switch (msg.getMsgType()) {
  41 + case COMPONENT_LIFE_CYCLE_MSG:
  42 + onComponentLifecycleMsg((ComponentLifecycleMsg) msg);
  43 + break;
40 44 case RULE_CHAIN_TO_RULE_MSG:
41 45 onRuleChainToRuleNodeMsg((RuleChainToRuleNodeMsg) msg);
42 46 break;
... ... @@ -89,6 +93,4 @@ public class RuleNodeActor extends ComponentActor<RuleNodeId, RuleNodeActorMessa
89 93 return systemContext.getRuleNodeErrorPersistFrequency();
90 94 }
91 95
92   - //TODO: failover strategy
93   -
94 96 }
... ...
... ... @@ -18,28 +18,19 @@ package org.thingsboard.server.actors.ruleChain;
18 18 import akka.actor.ActorContext;
19 19 import akka.actor.ActorRef;
20 20 import akka.event.LoggingAdapter;
21   -import com.fasterxml.jackson.databind.JsonNode;
22   -import com.fasterxml.jackson.databind.node.ObjectNode;
23   -import org.springframework.util.Base64Utils;
24 21 import org.thingsboard.rule.engine.api.TbNode;
25 22 import org.thingsboard.rule.engine.api.TbNodeConfiguration;
26 23 import org.thingsboard.rule.engine.api.TbNodeState;
27 24 import org.thingsboard.server.actors.ActorSystemContext;
28 25 import org.thingsboard.server.actors.shared.ComponentMsgProcessor;
29   -import org.thingsboard.server.common.data.DataConstants;
30   -import org.thingsboard.server.common.data.Event;
31 26 import org.thingsboard.server.common.data.id.RuleChainId;
32 27 import org.thingsboard.server.common.data.id.RuleNodeId;
33 28 import org.thingsboard.server.common.data.id.TenantId;
  29 +import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
34 30 import org.thingsboard.server.common.data.rule.RuleNode;
35   -import org.thingsboard.server.common.msg.TbMsg;
36 31 import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
37 32 import org.thingsboard.server.dao.rule.RuleChainService;
38 33
39   -import java.io.PrintWriter;
40   -import java.io.StringWriter;
41   -import java.nio.charset.StandardCharsets;
42   -
43 34 /**
44 35 * @author Andrew Shvayka
45 36 */
... ... @@ -63,11 +54,25 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
63 54 @Override
64 55 public void start(ActorContext context) throws Exception {
65 56 tbNode = initComponent(ruleNode);
  57 + state = ComponentLifecycleState.ACTIVE;
  58 + }
  59 +
  60 + @Override
  61 + public void onUpdate(ActorContext context) throws Exception {
  62 + RuleNode newRuleNode = systemContext.getRuleChainService().findRuleNodeById(entityId);
  63 + boolean restartRequired = !(ruleNode.getType().equals(newRuleNode.getType())
  64 + && ruleNode.getConfiguration().equals(newRuleNode.getConfiguration()));
  65 + this.ruleNode = newRuleNode;
  66 + if (restartRequired) {
  67 + tbNode.destroy();
  68 + start(context);
  69 + }
66 70 }
67 71
68 72 @Override
69 73 public void stop(ActorContext context) throws Exception {
70 74 tbNode.destroy();
  75 + context.stop(self);
71 76 }
72 77
73 78 @Override
... ... @@ -76,6 +81,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
76 81 }
77 82
78 83 void onRuleChainToRuleNodeMsg(RuleChainToRuleNodeMsg msg) throws Exception {
  84 + checkActive();
79 85 if (ruleNode.isDebugMode()) {
80 86 systemContext.persistDebugInput(tenantId, entityId, msg.getMsg());
81 87 }
... ... @@ -89,4 +95,5 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
89 95 return tbNode;
90 96 }
91 97
  98 +
92 99 }
... ...
... ... @@ -17,7 +17,6 @@ package org.thingsboard.server.actors.ruleChain;
17 17
18 18 import akka.actor.ActorRef;
19 19 import lombok.Data;
20   -import org.thingsboard.server.common.data.id.RuleNodeId;
21 20 import org.thingsboard.server.common.data.id.TenantId;
22 21 import org.thingsboard.server.common.data.rule.RuleNode;
23 22
... ...
... ... @@ -16,7 +16,6 @@
16 16 package org.thingsboard.server.actors.ruleChain;
17 17
18 18 import lombok.Data;
19   -import org.thingsboard.server.common.data.id.RuleNodeId;
20 19 import org.thingsboard.server.common.msg.MsgType;
21 20 import org.thingsboard.server.common.msg.TbActorMsg;
22 21 import org.thingsboard.server.common.msg.TbMsg;
... ...
... ... @@ -20,12 +20,14 @@ import akka.event.LoggingAdapter;
20 20 import org.thingsboard.server.actors.ActorSystemContext;
21 21 import org.thingsboard.server.actors.stats.StatsPersistTick;
22 22 import org.thingsboard.server.common.data.id.TenantId;
  23 +import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
23 24 import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
24 25
25 26 public abstract class ComponentMsgProcessor<T> extends AbstractContextAwareMsgProcessor {
26 27
27 28 protected final TenantId tenantId;
28 29 protected final T entityId;
  30 + protected ComponentLifecycleState state;
29 31
30 32 protected ComponentMsgProcessor(ActorSystemContext systemContext, LoggingAdapter logger, TenantId tenantId, T id) {
31 33 super(systemContext, logger);
... ... @@ -67,4 +69,10 @@ public abstract class ComponentMsgProcessor<T> extends AbstractContextAwareMsgPr
67 69 public void scheduleStatsPersistTick(ActorContext context, long statsPersistFrequency) {
68 70 schedulePeriodicMsgWithDelay(context, new StatsPersistTick(), statsPersistFrequency, statsPersistFrequency);
69 71 }
  72 +
  73 + protected void checkActive() {
  74 + if (state != ComponentLifecycleState.ACTIVE) {
  75 + throw new IllegalStateException("Rule chain is not active!");
  76 + }
  77 + }
70 78 }
... ...
... ... @@ -29,7 +29,7 @@ import org.thingsboard.server.common.data.rule.RuleChainMetaData;
29 29 /**
30 30 * Created by ashvayka on 20.03.18.
31 31 */
32   -public class AbstractRuleEngineControllerTest extends AbstractControllerTest{
  32 +public class AbstractRuleEngineControllerTest extends AbstractControllerTest {
33 33
34 34 protected RuleChain saveRuleChain(RuleChain ruleChain) throws Exception {
35 35 return doPost("/api/ruleChain", ruleChain, RuleChain.class);
... ...
... ... @@ -24,7 +24,7 @@ import java.util.Arrays;
24 24
25 25 @RunWith(ClasspathSuite.class)
26 26 @ClasspathSuite.ClassnameFilters({
27   - "org.thingsboard.server.rules.flow.*Test", "org.thingsboard.server.rules.lifecycle.*Test"})
  27 + "org.thingsboard.server.rules.flow.*Test"})
28 28 public class RuleEngineSqlTestSuite {
29 29
30 30 @ClassRule
... ...
... ... @@ -38,7 +38,9 @@ import org.thingsboard.server.common.msg.TbMsgMetaData;
38 38 import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
39 39 import org.thingsboard.server.controller.AbstractRuleEngineControllerTest;
40 40 import org.thingsboard.server.dao.attributes.AttributesService;
  41 +import org.thingsboard.server.dao.rule.RuleChainService;
41 42
  43 +import java.util.Arrays;
42 44 import java.util.Collections;
43 45
44 46 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
... ... @@ -49,17 +51,17 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
49 51 @Slf4j
50 52 public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRuleEngineControllerTest {
51 53
52   - private static final String MQTT_URL = "tcp://localhost:1883";
53   - private static final Long TIME_TO_HANDLE_REQUEST = 500L;
  54 + protected Tenant savedTenant;
  55 + protected User tenantAdmin;
54 56
55   - private Tenant savedTenant;
56   - private User tenantAdmin;
  57 + @Autowired
  58 + protected ActorService actorService;
57 59
58 60 @Autowired
59   - private ActorService actorService;
  61 + protected AttributesService attributesService;
60 62
61 63 @Autowired
62   - private AttributesService attributesService;
  64 + protected RuleChainService ruleChainService;
63 65
64 66 @Before
65 67 public void beforeTest() throws Exception {
... ... @@ -89,7 +91,7 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule
89 91 }
90 92
91 93 @Test
92   - public void testSimpleRuleChainCreation() throws Exception {
  94 + public void testRuleChainWithTwoRules() throws Exception {
93 95 // Creating Rule Chain
94 96 RuleChain ruleChain = new RuleChain();
95 97 ruleChain.setName("Simple Rule Chain");
... ... @@ -102,17 +104,26 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule
102 104 RuleChainMetaData metaData = new RuleChainMetaData();
103 105 metaData.setRuleChainId(ruleChain.getId());
104 106
105   - RuleNode ruleNode = new RuleNode();
106   - ruleNode.setName("Simple Rule Node");
107   - ruleNode.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName());
108   - ruleNode.setDebugMode(true);
109   - TbGetAttributesNodeConfiguration configuration = new TbGetAttributesNodeConfiguration();
110   - configuration.setServerAttributeNames(Collections.singletonList("serverAttributeKey"));
111   - ruleNode.setConfiguration(mapper.valueToTree(configuration));
  107 + RuleNode ruleNode1 = new RuleNode();
  108 + ruleNode1.setName("Simple Rule Node 1");
  109 + ruleNode1.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName());
  110 + ruleNode1.setDebugMode(true);
  111 + TbGetAttributesNodeConfiguration configuration1 = new TbGetAttributesNodeConfiguration();
  112 + configuration1.setServerAttributeNames(Collections.singletonList("serverAttributeKey1"));
  113 + ruleNode1.setConfiguration(mapper.valueToTree(configuration1));
112 114
113   - metaData.setNodes(Collections.singletonList(ruleNode));
114   - metaData.setFirstNodeIndex(0);
  115 + RuleNode ruleNode2 = new RuleNode();
  116 + ruleNode2.setName("Simple Rule Node 2");
  117 + ruleNode2.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName());
  118 + ruleNode2.setDebugMode(true);
  119 + TbGetAttributesNodeConfiguration configuration2 = new TbGetAttributesNodeConfiguration();
  120 + configuration2.setServerAttributeNames(Collections.singletonList("serverAttributeKey2"));
  121 + ruleNode2.setConfiguration(mapper.valueToTree(configuration2));
115 122
  123 +
  124 + metaData.setNodes(Arrays.asList(ruleNode1, ruleNode2));
  125 + metaData.setFirstNodeIndex(0);
  126 + metaData.addConnectionInfo(0, 1, "Success");
116 127 metaData = saveRuleChainMetaData(metaData);
117 128 Assert.assertNotNull(metaData);
118 129
... ... @@ -126,7 +137,12 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule
126 137 device = doPost("/api/device", device, Device.class);
127 138
128 139 attributesService.save(device.getId(), DataConstants.SERVER_SCOPE,
129   - Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry("serverAttributeKey", "serverAttributeValue"), System.currentTimeMillis())));
  140 + Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry("serverAttributeKey1", "serverAttributeValue1"), System.currentTimeMillis())));
  141 + attributesService.save(device.getId(), DataConstants.SERVER_SCOPE,
  142 + Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry("serverAttributeKey2", "serverAttributeValue2"), System.currentTimeMillis())));
  143 +
  144 +
  145 + Thread.sleep(1000);
130 146
131 147 // Pushing Message to the system
132 148 TbMsg tbMsg = new TbMsg(UUIDs.timeBased(),
... ... @@ -150,7 +166,25 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule
150 166 Assert.assertEquals(ruleChain.getFirstRuleNodeId(), outEvent.getEntityId());
151 167 Assert.assertEquals(device.getId().getId().toString(), outEvent.getBody().get("entityId").asText());
152 168
153   - Assert.assertEquals("serverAttributeValue", outEvent.getBody().get("metadata").get("ss.serverAttributeKey").asText());
  169 + Assert.assertEquals("serverAttributeValue1", outEvent.getBody().get("metadata").get("ss.serverAttributeKey1").asText());
  170 +
  171 + RuleChain finalRuleChain = ruleChain;
  172 + RuleNode lastRuleNode = metaData.getNodes().stream().filter(node -> !node.getId().equals(finalRuleChain.getFirstRuleNodeId())).findFirst().get();
  173 +
  174 + events = getDebugEvents(savedTenant.getId(), lastRuleNode.getId(), 1000);
  175 +
  176 + Assert.assertEquals(2, events.getData().size());
  177 +
  178 + inEvent = events.getData().stream().filter(e -> e.getBody().get("type").asText().equals(DataConstants.IN)).findFirst().get();
  179 + Assert.assertEquals(lastRuleNode.getId(), inEvent.getEntityId());
  180 + Assert.assertEquals(device.getId().getId().toString(), inEvent.getBody().get("entityId").asText());
  181 +
  182 + outEvent = events.getData().stream().filter(e -> e.getBody().get("type").asText().equals(DataConstants.OUT)).findFirst().get();
  183 + Assert.assertEquals(lastRuleNode.getId(), outEvent.getEntityId());
  184 + Assert.assertEquals(device.getId().getId().toString(), outEvent.getBody().get("entityId").asText());
  185 +
  186 + Assert.assertEquals("serverAttributeValue1", outEvent.getBody().get("metadata").get("ss.serverAttributeKey1").asText());
  187 + Assert.assertEquals("serverAttributeValue2", outEvent.getBody().get("metadata").get("ss.serverAttributeKey2").asText());
154 188 }
155 189
156 190 }
... ...
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.rules.lifecycle;
  17 +
  18 +import com.datastax.driver.core.utils.UUIDs;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.junit.After;
  21 +import org.junit.Assert;
  22 +import org.junit.Before;
  23 +import org.junit.Test;
  24 +import org.springframework.beans.factory.annotation.Autowired;
  25 +import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration;
  26 +import org.thingsboard.server.actors.service.ActorService;
  27 +import org.thingsboard.server.common.data.DataConstants;
  28 +import org.thingsboard.server.common.data.Device;
  29 +import org.thingsboard.server.common.data.Event;
  30 +import org.thingsboard.server.common.data.Tenant;
  31 +import org.thingsboard.server.common.data.User;
  32 +import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
  33 +import org.thingsboard.server.common.data.kv.StringDataEntry;
  34 +import org.thingsboard.server.common.data.page.TimePageData;
  35 +import org.thingsboard.server.common.data.rule.RuleChain;
  36 +import org.thingsboard.server.common.data.rule.RuleChainMetaData;
  37 +import org.thingsboard.server.common.data.rule.RuleNode;
  38 +import org.thingsboard.server.common.data.security.Authority;
  39 +import org.thingsboard.server.common.msg.TbMsg;
  40 +import org.thingsboard.server.common.msg.TbMsgMetaData;
  41 +import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
  42 +import org.thingsboard.server.controller.AbstractRuleEngineControllerTest;
  43 +import org.thingsboard.server.dao.attributes.AttributesService;
  44 +
  45 +import java.util.Collections;
  46 +
  47 +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
  48 +
  49 +/**
  50 + * @author Valerii Sosliuk
  51 + */
  52 +@Slf4j
  53 +public abstract class AbstractRuleEngineLifecycleIntegrationTest extends AbstractRuleEngineControllerTest {
  54 +
  55 + protected Tenant savedTenant;
  56 + protected User tenantAdmin;
  57 +
  58 + @Autowired
  59 + protected ActorService actorService;
  60 +
  61 + @Autowired
  62 + protected AttributesService attributesService;
  63 +
  64 + @Before
  65 + public void beforeTest() throws Exception {
  66 + loginSysAdmin();
  67 +
  68 + Tenant tenant = new Tenant();
  69 + tenant.setTitle("My tenant");
  70 + savedTenant = doPost("/api/tenant", tenant, Tenant.class);
  71 + Assert.assertNotNull(savedTenant);
  72 +
  73 + tenantAdmin = new User();
  74 + tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
  75 + tenantAdmin.setTenantId(savedTenant.getId());
  76 + tenantAdmin.setEmail("tenant2@thingsboard.org");
  77 + tenantAdmin.setFirstName("Joe");
  78 + tenantAdmin.setLastName("Downs");
  79 +
  80 + createUserAndLogin(tenantAdmin, "testPassword1");
  81 + }
  82 +
  83 + @After
  84 + public void afterTest() throws Exception {
  85 + loginSysAdmin();
  86 + if (savedTenant != null) {
  87 + doDelete("/api/tenant/" + savedTenant.getId().getId().toString()).andExpect(status().isOk());
  88 + }
  89 + }
  90 +
  91 + @Test
  92 + public void testRuleChainWithOneRule() throws Exception {
  93 + // Creating Rule Chain
  94 + RuleChain ruleChain = new RuleChain();
  95 + ruleChain.setName("Simple Rule Chain");
  96 + ruleChain.setTenantId(savedTenant.getId());
  97 + ruleChain.setRoot(true);
  98 + ruleChain.setDebugMode(true);
  99 + ruleChain = saveRuleChain(ruleChain);
  100 + Assert.assertNull(ruleChain.getFirstRuleNodeId());
  101 +
  102 + RuleChainMetaData metaData = new RuleChainMetaData();
  103 + metaData.setRuleChainId(ruleChain.getId());
  104 +
  105 + RuleNode ruleNode = new RuleNode();
  106 + ruleNode.setName("Simple Rule Node");
  107 + ruleNode.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName());
  108 + ruleNode.setDebugMode(true);
  109 + TbGetAttributesNodeConfiguration configuration = new TbGetAttributesNodeConfiguration();
  110 + configuration.setServerAttributeNames(Collections.singletonList("serverAttributeKey"));
  111 + ruleNode.setConfiguration(mapper.valueToTree(configuration));
  112 +
  113 + metaData.setNodes(Collections.singletonList(ruleNode));
  114 + metaData.setFirstNodeIndex(0);
  115 +
  116 + metaData = saveRuleChainMetaData(metaData);
  117 + Assert.assertNotNull(metaData);
  118 +
  119 + ruleChain = getRuleChain(ruleChain.getId());
  120 + Assert.assertNotNull(ruleChain.getFirstRuleNodeId());
  121 +
  122 + // Saving the device
  123 + Device device = new Device();
  124 + device.setName("My device");
  125 + device.setType("default");
  126 + device = doPost("/api/device", device, Device.class);
  127 +
  128 + attributesService.save(device.getId(), DataConstants.SERVER_SCOPE,
  129 + Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry("serverAttributeKey", "serverAttributeValue"), System.currentTimeMillis())));
  130 +
  131 + Thread.sleep(1000);
  132 +
  133 + // Pushing Message to the system
  134 + TbMsg tbMsg = new TbMsg(UUIDs.timeBased(),
  135 + "CUSTOM",
  136 + device.getId(),
  137 + new TbMsgMetaData(),
  138 + new byte[]{});
  139 + actorService.onMsg(new ServiceToRuleEngineMsg(savedTenant.getId(), tbMsg));
  140 +
  141 + Thread.sleep(3000);
  142 +
  143 + TimePageData<Event> events = getDebugEvents(savedTenant.getId(), ruleChain.getFirstRuleNodeId(), 1000);
  144 +
  145 + Assert.assertEquals(2, events.getData().size());
  146 +
  147 + Event inEvent = events.getData().stream().filter(e -> e.getBody().get("type").asText().equals(DataConstants.IN)).findFirst().get();
  148 + Assert.assertEquals(ruleChain.getFirstRuleNodeId(), inEvent.getEntityId());
  149 + Assert.assertEquals(device.getId().getId().toString(), inEvent.getBody().get("entityId").asText());
  150 +
  151 + Event outEvent = events.getData().stream().filter(e -> e.getBody().get("type").asText().equals(DataConstants.OUT)).findFirst().get();
  152 + Assert.assertEquals(ruleChain.getFirstRuleNodeId(), outEvent.getEntityId());
  153 + Assert.assertEquals(device.getId().getId().toString(), outEvent.getBody().get("entityId").asText());
  154 +
  155 + Assert.assertEquals("serverAttributeValue", outEvent.getBody().get("metadata").get("ss.serverAttributeKey").asText());
  156 + }
  157 +
  158 +}
... ...
application/src/test/java/org/thingsboard/server/rules/lifecycle/RuleEngineLifecycleSqlIntegrationTest.java renamed from dao/src/test/java/org/thingsboard/server/dao/service/rule/sql/RuleServiceSqlTest.java
... ... @@ -13,11 +13,14 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -package org.thingsboard.server.dao.service.rule.sql;
  16 +package org.thingsboard.server.rules.lifecycle;
17 17
18 18 import org.thingsboard.server.dao.service.DaoSqlTest;
19   -import org.thingsboard.server.dao.service.rule.BaseRuleServiceTest;
  19 +import org.thingsboard.server.rules.flow.AbstractRuleEngineFlowIntegrationTest;
20 20
  21 +/**
  22 + * Created by Valerii Sosliuk on 8/22/2017.
  23 + */
21 24 @DaoSqlTest
22   -public class RuleServiceSqlTest extends BaseRuleServiceTest {
  25 +public class RuleEngineLifecycleSqlIntegrationTest extends AbstractRuleEngineLifecycleIntegrationTest {
23 26 }
... ...
common/data/src/main/java/org/thingsboard/server/common/data/rule/NodeConnectionInfo.java renamed from dao/src/test/java/org/thingsboard/server/dao/service/rule/nosql/RuleServiceNoSqlTest.java
... ... @@ -13,11 +13,16 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -package org.thingsboard.server.dao.service.rule.nosql;
  16 +package org.thingsboard.server.common.data.rule;
17 17
18   -import org.thingsboard.server.dao.service.DaoNoSqlTest;
19   -import org.thingsboard.server.dao.service.rule.BaseRuleServiceTest;
  18 +import lombok.Data;
20 19
21   -@DaoNoSqlTest
22   -public class RuleServiceNoSqlTest extends BaseRuleServiceTest {
  20 +/**
  21 + * Created by ashvayka on 21.03.18.
  22 + */
  23 +@Data
  24 +public class NodeConnectionInfo {
  25 + private int fromIndex;
  26 + private int toIndex;
  27 + private String type;
23 28 }
... ...
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.rule;
  17 +
  18 +import lombok.Data;
  19 +import org.thingsboard.server.common.data.id.RuleChainId;
  20 +
  21 +/**
  22 + * Created by ashvayka on 21.03.18.
  23 + */
  24 +@Data
  25 +public class RuleChainConnectionInfo {
  26 + private int fromIndex;
  27 + private RuleChainId targetRuleChainId;
  28 + private String type;
  29 +}
... ...
... ... @@ -58,18 +58,4 @@ public class RuleChainMetaData {
58 58 ruleChainConnections.add(connectionInfo);
59 59 }
60 60
61   - @Data
62   - public class NodeConnectionInfo {
63   - private int fromIndex;
64   - private int toIndex;
65   - private String type;
66   - }
67   -
68   - @Data
69   - public class RuleChainConnectionInfo {
70   - private int fromIndex;
71   - private RuleChainId targetRuleChainId;
72   - private String type;
73   - }
74   -
75 61 }
... ...
... ... @@ -31,7 +31,9 @@ import org.thingsboard.server.common.data.page.TextPageData;
31 31 import org.thingsboard.server.common.data.page.TextPageLink;
32 32 import org.thingsboard.server.common.data.relation.EntityRelation;
33 33 import org.thingsboard.server.common.data.relation.RelationTypeGroup;
  34 +import org.thingsboard.server.common.data.rule.NodeConnectionInfo;
34 35 import org.thingsboard.server.common.data.rule.RuleChain;
  36 +import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo;
35 37 import org.thingsboard.server.common.data.rule.RuleChainMetaData;
36 38 import org.thingsboard.server.common.data.rule.RuleNode;
37 39 import org.thingsboard.server.dao.entity.AbstractEntityService;
... ... @@ -148,7 +150,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
148 150 ruleChainDao.save(ruleChain);
149 151 }
150 152 if (ruleChainMetaData.getConnections() != null) {
151   - for (RuleChainMetaData.NodeConnectionInfo nodeConnection : ruleChainMetaData.getConnections()) {
  153 + for (NodeConnectionInfo nodeConnection : ruleChainMetaData.getConnections()) {
152 154 EntityId from = nodes.get(nodeConnection.getFromIndex()).getId();
153 155 EntityId to = nodes.get(nodeConnection.getToIndex()).getId();
154 156 String type = nodeConnection.getType();
... ... @@ -161,7 +163,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
161 163 }
162 164 }
163 165 if (ruleChainMetaData.getRuleChainConnections() != null) {
164   - for (RuleChainMetaData.RuleChainConnectionInfo nodeToRuleChainConnection : ruleChainMetaData.getRuleChainConnections()) {
  166 + for (RuleChainConnectionInfo nodeToRuleChainConnection : ruleChainMetaData.getRuleChainConnections()) {
165 167 EntityId from = nodes.get(nodeToRuleChainConnection.getFromIndex()).getId();
166 168 EntityId to = nodeToRuleChainConnection.getTargetRuleChainId();
167 169 String type = nodeToRuleChainConnection.getType();
... ... @@ -314,7 +316,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
314 316
315 317 private void createRelation(EntityRelation relation) throws ExecutionException, InterruptedException {
316 318 log.debug("Creating relation: {}", relation);
317   - relationService.saveRelationAsync(relation).get();
  319 + relationService.saveRelation(relation);
318 320 }
319 321
320 322 private DataValidator<RuleChain> ruleChainValidator =
... ...
1   -/**
2   - * Copyright © 2016-2018 The Thingsboard Authors
3   - *
4   - * Licensed under the Apache License, Version 2.0 (the "License");
5   - * you may not use this file except in compliance with the License.
6   - * You may obtain a copy of the License at
7   - *
8   - * http://www.apache.org/licenses/LICENSE-2.0
9   - *
10   - * Unless required by applicable law or agreed to in writing, software
11   - * distributed under the License is distributed on an "AS IS" BASIS,
12   - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   - * See the License for the specific language governing permissions and
14   - * limitations under the License.
15   - */
16   -package org.thingsboard.server.dao.service.rule;
17   -
18   -import com.datastax.driver.core.utils.UUIDs;
19   -import org.junit.Assert;
20   -import org.junit.Test;
21   -import org.thingsboard.server.common.data.id.TenantId;
22   -import org.thingsboard.server.common.data.page.TextPageData;
23   -import org.thingsboard.server.common.data.page.TextPageLink;
24   -import org.thingsboard.server.common.data.plugin.PluginMetaData;
25   -import org.thingsboard.server.common.data.rule.RuleMetaData;
26   -import org.thingsboard.server.dao.model.ModelConstants;
27   -import org.thingsboard.server.dao.service.AbstractServiceTest;
28   -
29   -import java.util.List;
30   -import java.util.concurrent.ThreadLocalRandom;
31   -
32   -public abstract class BaseRuleServiceTest extends AbstractServiceTest {
33   -
34   - @Test
35   - public void saveRule() throws Exception {
36   - PluginMetaData plugin = generatePlugin(null, "testPluginToken" + ThreadLocalRandom.current().nextInt());
37   - pluginService.savePlugin(plugin);
38   - RuleMetaData ruleMetaData = ruleService.saveRule(generateRule(plugin.getTenantId(), null, plugin.getApiToken()));
39   - Assert.assertNotNull(ruleMetaData.getId());
40   - Assert.assertNotNull(ruleMetaData.getAdditionalInfo());
41   - ruleMetaData.setAdditionalInfo(mapper.readTree("{\"description\":\"test\"}"));
42   - RuleMetaData newRuleMetaData = ruleService.saveRule(ruleMetaData);
43   - Assert.assertEquals(ruleMetaData.getAdditionalInfo(), newRuleMetaData.getAdditionalInfo());
44   - }
45   -
46   - @Test
47   - public void findRuleById() throws Exception {
48   - PluginMetaData plugin = generatePlugin(null, "testPluginToken" + ThreadLocalRandom.current().nextInt());
49   - pluginService.savePlugin(plugin);
50   -
51   - RuleMetaData expected = ruleService.saveRule(generateRule(plugin.getTenantId(), null, plugin.getApiToken()));
52   - Assert.assertNotNull(expected.getId());
53   - RuleMetaData found = ruleService.findRuleById(expected.getId());
54   - Assert.assertEquals(expected, found);
55   - }
56   -
57   - @Test
58   - public void findPluginRules() throws Exception {
59   - TenantId tenantIdA = new TenantId(UUIDs.timeBased());
60   - TenantId tenantIdB = new TenantId(UUIDs.timeBased());
61   -
62   - PluginMetaData pluginA = generatePlugin(tenantIdA, "testPluginToken" + ThreadLocalRandom.current().nextInt());
63   - PluginMetaData pluginB = generatePlugin(tenantIdB, "testPluginToken" + ThreadLocalRandom.current().nextInt());
64   - pluginService.savePlugin(pluginA);
65   - pluginService.savePlugin(pluginB);
66   -
67   - ruleService.saveRule(generateRule(tenantIdA, null, pluginA.getApiToken()));
68   - ruleService.saveRule(generateRule(tenantIdA, null, pluginA.getApiToken()));
69   - ruleService.saveRule(generateRule(tenantIdA, null, pluginA.getApiToken()));
70   -
71   - ruleService.saveRule(generateRule(tenantIdB, null, pluginB.getApiToken()));
72   - ruleService.saveRule(generateRule(tenantIdB, null, pluginB.getApiToken()));
73   -
74   - List<RuleMetaData> foundA = ruleService.findPluginRules(pluginA.getApiToken());
75   - Assert.assertEquals(3, foundA.size());
76   -
77   - List<RuleMetaData> foundB = ruleService.findPluginRules(pluginB.getApiToken());
78   - Assert.assertEquals(2, foundB.size());
79   - }
80   -
81   - @Test
82   - public void findSystemRules() throws Exception {
83   - TenantId systemTenant = new TenantId(ModelConstants.NULL_UUID); // system tenant id
84   -
85   - PluginMetaData plugin = generatePlugin(systemTenant, "testPluginToken" + ThreadLocalRandom.current().nextInt());
86   - pluginService.savePlugin(plugin);
87   - ruleService.saveRule(generateRule(systemTenant, null, plugin.getApiToken()));
88   - ruleService.saveRule(generateRule(systemTenant, null, plugin.getApiToken()));
89   - ruleService.saveRule(generateRule(systemTenant, null, plugin.getApiToken()));
90   - TextPageData<RuleMetaData> found = ruleService.findSystemRules(new TextPageLink(100));
91   - Assert.assertEquals(3, found.getData().size());
92   - }
93   -
94   - @Test
95   - public void findTenantRules() throws Exception {
96   - TenantId tenantIdA = new TenantId(UUIDs.timeBased());
97   - TenantId tenantIdB = new TenantId(UUIDs.timeBased());
98   -
99   - PluginMetaData pluginA = generatePlugin(tenantIdA, "testPluginToken" + ThreadLocalRandom.current().nextInt());
100   - PluginMetaData pluginB = generatePlugin(tenantIdB, "testPluginToken" + ThreadLocalRandom.current().nextInt());
101   - pluginService.savePlugin(pluginA);
102   - pluginService.savePlugin(pluginB);
103   -
104   - ruleService.saveRule(generateRule(tenantIdA, null, pluginA.getApiToken()));
105   - ruleService.saveRule(generateRule(tenantIdA, null, pluginA.getApiToken()));
106   - ruleService.saveRule(generateRule(tenantIdA, null, pluginA.getApiToken()));
107   -
108   - ruleService.saveRule(generateRule(tenantIdB, null, pluginB.getApiToken()));
109   - ruleService.saveRule(generateRule(tenantIdB, null, pluginB.getApiToken()));
110   -
111   - TextPageData<RuleMetaData> foundA = ruleService.findTenantRules(tenantIdA, new TextPageLink(100));
112   - Assert.assertEquals(3, foundA.getData().size());
113   -
114   - TextPageData<RuleMetaData> foundB = ruleService.findTenantRules(tenantIdB, new TextPageLink(100));
115   - Assert.assertEquals(2, foundB.getData().size());
116   - }
117   -
118   - @Test
119   - public void deleteRuleById() throws Exception {
120   - PluginMetaData plugin = generatePlugin(null, "testPluginToken" + ThreadLocalRandom.current().nextInt());
121   - pluginService.savePlugin(plugin);
122   -
123   - RuleMetaData expected = ruleService.saveRule(generateRule(plugin.getTenantId(), null, plugin.getApiToken()));
124   - Assert.assertNotNull(expected.getId());
125   - RuleMetaData found = ruleService.findRuleById(expected.getId());
126   - Assert.assertEquals(expected, found);
127   - ruleService.deleteRuleById(expected.getId());
128   - found = ruleService.findRuleById(expected.getId());
129   - Assert.assertNull(found);
130   - }
131   -
132   - @Test
133   - public void deleteRulesByTenantId() throws Exception {
134   - TenantId tenantIdA = new TenantId(UUIDs.timeBased());
135   - TenantId tenantIdB = new TenantId(UUIDs.timeBased());
136   -
137   - PluginMetaData pluginA = generatePlugin(tenantIdA, "testPluginToken" + ThreadLocalRandom.current().nextInt());
138   - PluginMetaData pluginB = generatePlugin(tenantIdB, "testPluginToken" + ThreadLocalRandom.current().nextInt());
139   - pluginService.savePlugin(pluginA);
140   - pluginService.savePlugin(pluginB);
141   -
142   - ruleService.saveRule(generateRule(tenantIdA, null, pluginA.getApiToken()));
143   - ruleService.saveRule(generateRule(tenantIdA, null, pluginA.getApiToken()));
144   - ruleService.saveRule(generateRule(tenantIdA, null, pluginA.getApiToken()));
145   -
146   - ruleService.saveRule(generateRule(tenantIdB, null, pluginB.getApiToken()));
147   - ruleService.saveRule(generateRule(tenantIdB, null, pluginB.getApiToken()));
148   -
149   - TextPageData<RuleMetaData> foundA = ruleService.findTenantRules(tenantIdA, new TextPageLink(100));
150   - Assert.assertEquals(3, foundA.getData().size());
151   -
152   - TextPageData<RuleMetaData> foundB = ruleService.findTenantRules(tenantIdB, new TextPageLink(100));
153   - Assert.assertEquals(2, foundB.getData().size());
154   -
155   - ruleService.deleteRulesByTenantId(tenantIdA);
156   -
157   - foundA = ruleService.findTenantRules(tenantIdA, new TextPageLink(100));
158   - Assert.assertEquals(0, foundA.getData().size());
159   -
160   - foundB = ruleService.findTenantRules(tenantIdB, new TextPageLink(100));
161   - Assert.assertEquals(2, foundB.getData().size());
162   - }
163   -}
\ No newline at end of file