Browse Source

added upgrade script for check field presence node && PROD-2217

pull/8786/head
ShvaykaD 3 years ago
parent
commit
e32dc47ea5
  1. 44
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckRelationNode.java
  2. 2
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckRelationNodeConfiguration.java
  3. 55
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckRelationNodeTest.java
  4. 2
      ui-ngx/src/assets/help/en_US/rulenode/originator_fields_node_fields_templatization.md

44
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckRelationNode.java

@ -15,23 +15,26 @@
*/
package org.thingsboard.rule.engine.filter;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
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;
import org.thingsboard.rule.engine.api.TbNode;
import org.thingsboard.rule.engine.api.TbNodeConfiguration;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.msg.TbNodeConnectionType;
import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.rule.engine.api.TbVersionedNode;
import org.thingsboard.rule.engine.api.util.TbNodeUtils;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.msg.TbNodeConnectionType;
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.data.util.TbPair;
import org.thingsboard.server.common.msg.TbMsg;
import java.util.List;
@ -54,7 +57,9 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback;
"Output connections: <code>True</code>, <code>False</code>, <code>Failure</code>",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbFilterNodeCheckRelationConfig")
public class TbCheckRelationNode implements TbNode {
public class TbCheckRelationNode implements TbVersionedNode {
private static final String DIRECTION_PROPERTY_NAME = "direction";
private TbCheckRelationNodeConfiguration config;
private EntityId singleEntityId;
@ -84,19 +89,19 @@ public class TbCheckRelationNode implements TbNode {
EntityId from;
EntityId to;
if (EntitySearchDirection.FROM.name().equals(config.getDirection())) {
from = singleEntityId;
to = msg.getOriginator();
} else {
to = singleEntityId;
from = msg.getOriginator();
} else {
from = singleEntityId;
to = msg.getOriginator();
}
return ctx.getRelationService().checkRelationAsync(ctx.getTenantId(), from, to, config.getRelationType(), RelationTypeGroup.COMMON);
}
private ListenableFuture<Boolean> processList(TbContext ctx, TbMsg msg) {
ListenableFuture<List<EntityRelation>> relationListFuture = EntitySearchDirection.FROM.name().equals(config.getDirection()) ?
ctx.getRelationService().findByToAndTypeAsync(ctx.getTenantId(), msg.getOriginator(), config.getRelationType(), RelationTypeGroup.COMMON) :
ctx.getRelationService().findByFromAndTypeAsync(ctx.getTenantId(), msg.getOriginator(), config.getRelationType(), RelationTypeGroup.COMMON);
ctx.getRelationService().findByFromAndTypeAsync(ctx.getTenantId(), msg.getOriginator(), config.getRelationType(), RelationTypeGroup.COMMON) :
ctx.getRelationService().findByToAndTypeAsync(ctx.getTenantId(), msg.getOriginator(), config.getRelationType(), RelationTypeGroup.COMMON);
return Futures.transformAsync(relationListFuture, this::isEmptyList, ctx.getDbCallbackExecutor());
}
@ -104,4 +109,25 @@ public class TbCheckRelationNode implements TbNode {
return entityRelations.isEmpty() ? Futures.immediateFuture(false) : Futures.immediateFuture(true);
}
@Override
public TbPair<Boolean, JsonNode> upgrade(int fromVersion, JsonNode oldConfiguration) throws TbNodeException {
if (fromVersion == 0) {
var newConfigObjectNode = (ObjectNode) oldConfiguration;
if (!newConfigObjectNode.has(DIRECTION_PROPERTY_NAME)) {
throw new TbNodeException("property to update: '" + DIRECTION_PROPERTY_NAME + "' doesn't exists in configuration!");
}
String direction = newConfigObjectNode.get(DIRECTION_PROPERTY_NAME).asText();
if ("TO".equals(direction)) {
newConfigObjectNode.put(DIRECTION_PROPERTY_NAME, EntitySearchDirection.FROM.name());
return new TbPair<>(true, newConfigObjectNode);
}
if ("FROM".equals(direction)) {
newConfigObjectNode.put(DIRECTION_PROPERTY_NAME, EntitySearchDirection.TO.name());
return new TbPair<>(true, newConfigObjectNode);
}
throw new TbNodeException("property to update: '" + DIRECTION_PROPERTY_NAME + "' has invalid value!");
}
return new TbPair<>(false, oldConfiguration);
}
}

2
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckRelationNodeConfiguration.java

@ -34,7 +34,7 @@ public class TbCheckRelationNodeConfiguration implements NodeConfiguration<TbChe
@Override
public TbCheckRelationNodeConfiguration defaultConfiguration() {
TbCheckRelationNodeConfiguration configuration = new TbCheckRelationNodeConfiguration();
var configuration = new TbCheckRelationNodeConfiguration();
configuration.setDirection(EntitySearchDirection.FROM.name());
configuration.setRelationType(EntityRelation.CONTAINS_TYPE);
configuration.setCheckForSingleEntity(true);

55
rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckRelationNodeTest.java

@ -15,6 +15,7 @@
*/
package org.thingsboard.rule.engine.filter;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.util.concurrent.Futures;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@ -25,6 +26,7 @@ import org.thingsboard.rule.engine.TestDbCallbackExecutor;
import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.rule.engine.api.TbNodeConfiguration;
import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
@ -32,6 +34,7 @@ import org.thingsboard.server.common.data.msg.TbNodeConnectionType;
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.data.util.TbPair;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.dao.relation.RelationService;
@ -41,7 +44,9 @@ import java.util.List;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@ -55,11 +60,11 @@ import static org.thingsboard.server.common.data.msg.TbMsgType.POST_ATTRIBUTES_R
class TbCheckRelationNodeTest {
private static final TenantId TENANT_ID = new TenantId(UUID.randomUUID());
private static final DeviceId DEVICE_ID = new DeviceId(UUID.randomUUID());
private static final DeviceId ORIGINATOR_ID = new DeviceId(UUID.randomUUID());
private static final TestDbCallbackExecutor DB_EXECUTOR = new TestDbCallbackExecutor();
private static final TbMsgMetaData EMPTY_METADATA = new TbMsgMetaData();
private static final String EMPTY_DATA = "{}";
private static final TbMsg EMPTY_POST_ATTRIBUTES_MSG = TbMsg.newMsg(POST_ATTRIBUTES_REQUEST.name(), DEVICE_ID, EMPTY_METADATA, EMPTY_DATA);
private static final TbMsg EMPTY_POST_ATTRIBUTES_MSG = TbMsg.newMsg(POST_ATTRIBUTES_REQUEST.name(), ORIGINATOR_ID, EMPTY_METADATA, EMPTY_DATA);
private TbCheckRelationNode node;
@ -104,7 +109,7 @@ class TbCheckRelationNodeTest {
config.setEntityType(assetId.getEntityType().name());
config.setEntityId(assetId.getId().toString());
when(relationService.checkRelationAsync(TENANT_ID, assetId, DEVICE_ID, config.getRelationType(), RelationTypeGroup.COMMON)).thenReturn(Futures.immediateFuture(true));
when(relationService.checkRelationAsync(TENANT_ID, ORIGINATOR_ID, assetId, config.getRelationType(), RelationTypeGroup.COMMON)).thenReturn(Futures.immediateFuture(true));
node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config)));
// WHEN
@ -128,7 +133,7 @@ class TbCheckRelationNodeTest {
config.setEntityType(assetId.getEntityType().name());
config.setEntityId(assetId.getId().toString());
when(relationService.checkRelationAsync(TENANT_ID, assetId, DEVICE_ID, config.getRelationType(), RelationTypeGroup.COMMON)).thenReturn(Futures.immediateFuture(false));
when(relationService.checkRelationAsync(TENANT_ID, ORIGINATOR_ID, assetId, config.getRelationType(), RelationTypeGroup.COMMON)).thenReturn(Futures.immediateFuture(false));
node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config)));
// WHEN
@ -153,7 +158,7 @@ class TbCheckRelationNodeTest {
config.setEntityId(assetId.getId().toString());
config.setDirection(EntitySearchDirection.TO.name());
when(relationService.checkRelationAsync(TENANT_ID, DEVICE_ID, assetId, config.getRelationType(), RelationTypeGroup.COMMON)).thenReturn(Futures.immediateFuture(true));
when(relationService.checkRelationAsync(TENANT_ID, assetId, ORIGINATOR_ID, config.getRelationType(), RelationTypeGroup.COMMON)).thenReturn(Futures.immediateFuture(true));
node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config)));
// WHEN
@ -163,7 +168,6 @@ class TbCheckRelationNodeTest {
ArgumentCaptor<TbMsg> newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class);
verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.TRUE));
verify(ctx, never()).tellFailure(any(), any());
verify(relationService, never()).findByFromAndTypeAsync(any(), any(), anyString(), any());
TbMsg newMsg = newMsgCaptor.getValue();
assertThat(newMsg).isNotNull();
assertThat(newMsg).isSameAs(EMPTY_POST_ATTRIBUTES_MSG);
@ -179,7 +183,7 @@ class TbCheckRelationNodeTest {
config.setEntityId(assetId.getId().toString());
config.setDirection(EntitySearchDirection.TO.name());
when(relationService.checkRelationAsync(TENANT_ID, DEVICE_ID, assetId, config.getRelationType(), RelationTypeGroup.COMMON)).thenReturn(Futures.immediateFuture(false));
when(relationService.checkRelationAsync(TENANT_ID, assetId, ORIGINATOR_ID, config.getRelationType(), RelationTypeGroup.COMMON)).thenReturn(Futures.immediateFuture(false));
node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config)));
// WHEN
@ -200,12 +204,12 @@ class TbCheckRelationNodeTest {
var config = new TbCheckRelationNodeConfiguration().defaultConfiguration();
config.setCheckForSingleEntity(false);
var entityRelation = new EntityRelation();
entityRelation.setTo(DEVICE_ID);
entityRelation.setFrom(new AssetId(UUID.randomUUID()));
entityRelation.setFrom(ORIGINATOR_ID);
entityRelation.setTo(new AssetId(UUID.randomUUID()));
entityRelation.setType(EntityRelation.CONTAINS_TYPE);
entityRelation.setTypeGroup(RelationTypeGroup.COMMON);
when(relationService.findByToAndTypeAsync(TENANT_ID, DEVICE_ID, config.getRelationType(), RelationTypeGroup.COMMON)).thenReturn(Futures.immediateFuture(List.of(entityRelation)));
when(relationService.findByFromAndTypeAsync(TENANT_ID, ORIGINATOR_ID, config.getRelationType(), RelationTypeGroup.COMMON)).thenReturn(Futures.immediateFuture(List.of(entityRelation)));
node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config)));
// WHEN
@ -215,7 +219,7 @@ class TbCheckRelationNodeTest {
ArgumentCaptor<TbMsg> newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class);
verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.TRUE));
verify(ctx, never()).tellFailure(any(), any());
verify(relationService, never()).findByFromAndTypeAsync(any(), any(), anyString(), any());
verify(relationService, never()).findByToAndTypeAsync(any(), any(), anyString(), any());
TbMsg newMsg = newMsgCaptor.getValue();
assertThat(newMsg).isNotNull();
assertThat(newMsg).isSameAs(EMPTY_POST_ATTRIBUTES_MSG);
@ -227,7 +231,7 @@ class TbCheckRelationNodeTest {
var config = new TbCheckRelationNodeConfiguration().defaultConfiguration();
config.setCheckForSingleEntity(false);
when(relationService.findByToAndTypeAsync(TENANT_ID, DEVICE_ID, config.getRelationType(), RelationTypeGroup.COMMON)).thenReturn(Futures.immediateFuture(Collections.emptyList()));
when(relationService.findByFromAndTypeAsync(TENANT_ID, ORIGINATOR_ID, config.getRelationType(), RelationTypeGroup.COMMON)).thenReturn(Futures.immediateFuture(Collections.emptyList()));
node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config)));
// WHEN
@ -237,7 +241,7 @@ class TbCheckRelationNodeTest {
ArgumentCaptor<TbMsg> newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class);
verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.FALSE));
verify(ctx, never()).tellFailure(any(), any());
verify(relationService, never()).findByFromAndTypeAsync(any(), any(), anyString(), any());
verify(relationService, never()).findByToAndTypeAsync(any(), any(), anyString(), any());
TbMsg newMsg = newMsgCaptor.getValue();
assertThat(newMsg).isNotNull();
assertThat(newMsg).isSameAs(EMPTY_POST_ATTRIBUTES_MSG);
@ -251,11 +255,11 @@ class TbCheckRelationNodeTest {
config.setDirection(EntitySearchDirection.TO.name());
var entityRelation = new EntityRelation();
entityRelation.setFrom(new AssetId(UUID.randomUUID()));
entityRelation.setTo(DEVICE_ID);
entityRelation.setTo(ORIGINATOR_ID);
entityRelation.setType(EntityRelation.CONTAINS_TYPE);
entityRelation.setTypeGroup(RelationTypeGroup.COMMON);
when(relationService.findByFromAndTypeAsync(TENANT_ID, DEVICE_ID, config.getRelationType(), RelationTypeGroup.COMMON)).thenReturn(Futures.immediateFuture(List.of(entityRelation)));
when(relationService.findByToAndTypeAsync(TENANT_ID, ORIGINATOR_ID, config.getRelationType(), RelationTypeGroup.COMMON)).thenReturn(Futures.immediateFuture(List.of(entityRelation)));
node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config)));
// WHEN
@ -265,7 +269,7 @@ class TbCheckRelationNodeTest {
ArgumentCaptor<TbMsg> newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class);
verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.TRUE));
verify(ctx, never()).tellFailure(any(), any());
verify(relationService, never()).findByToAndTypeAsync(any(), any(), anyString(), any());
verify(relationService, never()).findByFromAndTypeAsync(any(), any(), anyString(), any());
TbMsg newMsg = newMsgCaptor.getValue();
assertThat(newMsg).isNotNull();
assertThat(newMsg).isSameAs(EMPTY_POST_ATTRIBUTES_MSG);
@ -278,7 +282,7 @@ class TbCheckRelationNodeTest {
config.setCheckForSingleEntity(false);
config.setDirection(EntitySearchDirection.TO.name());
when(relationService.findByFromAndTypeAsync(TENANT_ID, DEVICE_ID, config.getRelationType(), RelationTypeGroup.COMMON)).thenReturn(Futures.immediateFuture(Collections.emptyList()));
when(relationService.findByToAndTypeAsync(TENANT_ID, ORIGINATOR_ID, config.getRelationType(), RelationTypeGroup.COMMON)).thenReturn(Futures.immediateFuture(Collections.emptyList()));
node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config)));
// WHEN
@ -288,10 +292,25 @@ class TbCheckRelationNodeTest {
ArgumentCaptor<TbMsg> newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class);
verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.FALSE));
verify(ctx, never()).tellFailure(any(), any());
verify(relationService, never()).findByToAndTypeAsync(any(), any(), anyString(), any());
verify(relationService, never()).findByFromAndTypeAsync(any(), any(), anyString(), any());
TbMsg newMsg = newMsgCaptor.getValue();
assertThat(newMsg).isNotNull();
assertThat(newMsg).isSameAs(EMPTY_POST_ATTRIBUTES_MSG);
}
@Test
void givenOldConfig_whenUpgrade_thenShouldReturnTrueResultWithNewConfig() throws Exception {
// GIVEN
var config = new TbCheckRelationNodeConfiguration().defaultConfiguration();
config.setEntityType(ORIGINATOR_ID.getEntityType().name());
config.setEntityId(ORIGINATOR_ID.getId().toString());
String oldConfig = "{\"checkForSingleEntity\":true,\"direction\":\"TO\",\"entityType\":\"" + config.getEntityType() + "\",\"entityId\":\"" + config.getEntityId() + "\",\"relationType\":\"Contains\"}";
JsonNode configJson = JacksonUtil.toJsonNode(oldConfig);
// WHEN
TbPair<Boolean, JsonNode> upgrade = node.upgrade(0, configJson);
// THEN
assertTrue(upgrade.getFirst());
assertEquals(config, JacksonUtil.treeToValue(upgrade.getSecond(), config.getClass()));
}
}

2
ui-ngx/src/assets/help/en_US/rulenode/originator_fields_node_fields_templatization.md

@ -12,7 +12,7 @@ Let's assume that we have two device types in our use case:
- `smart_door_lock`
- `motion_detector`
Let's assume that device of type `dock_lock_sensor` and name `SDL-001` publish next type of messages to the system:
Let's assume that device of type `smart_door_lock` and name `SDL-001` publish next type of messages to the system:
```json
{

Loading…
Cancel
Save