From e32dc47ea58b3f5e4ec92e3ec7e8b7294cc041c4 Mon Sep 17 00:00:00 2001 From: ShvaykaD Date: Fri, 30 Jun 2023 17:42:53 +0300 Subject: [PATCH] added upgrade script for check field presence node && PROD-2217 --- .../engine/filter/TbCheckRelationNode.java | 44 ++++++++++++--- .../TbCheckRelationNodeConfiguration.java | 2 +- .../filter/TbCheckRelationNodeTest.java | 55 +++++++++++++------ ...nator_fields_node_fields_templatization.md | 2 +- 4 files changed, 74 insertions(+), 29 deletions(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckRelationNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckRelationNode.java index 2186378527..ecc06f6303 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckRelationNode.java +++ b/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: True, False, Failure", 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 processList(TbContext ctx, TbMsg msg) { ListenableFuture> 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 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); + } + } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckRelationNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckRelationNodeConfiguration.java index ad5574ec8c..1a8ab7068d 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckRelationNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckRelationNodeConfiguration.java @@ -34,7 +34,7 @@ public class TbCheckRelationNodeConfiguration implements NodeConfiguration 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 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 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 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 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 upgrade = node.upgrade(0, configJson); + // THEN + assertTrue(upgrade.getFirst()); + assertEquals(config, JacksonUtil.treeToValue(upgrade.getSecond(), config.getClass())); + } + } diff --git a/ui-ngx/src/assets/help/en_US/rulenode/originator_fields_node_fields_templatization.md b/ui-ngx/src/assets/help/en_US/rulenode/originator_fields_node_fields_templatization.md index b21f450ae9..42f4ea6138 100644 --- a/ui-ngx/src/assets/help/en_US/rulenode/originator_fields_node_fields_templatization.md +++ b/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 {