From 0c68754160075282b455deb1c48f54290988fb44 Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Fri, 24 Oct 2025 17:12:05 +0300 Subject: [PATCH 1/2] changed propagation cf config to use relationPathLevel --- .../server/cf/CalculatedFieldIntegrationTest.java | 6 ++---- .../controller/CalculatedFieldControllerTest.java | 3 +-- .../state/PropagationCalculatedFieldStateTest.java | 4 ++-- .../PropagationCalculatedFieldConfiguration.java | 9 +++------ .../PropagationCalculatedFieldConfigurationTest.java | 12 ++++++++++-- 5 files changed, 18 insertions(+), 16 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/cf/CalculatedFieldIntegrationTest.java b/application/src/test/java/org/thingsboard/server/cf/CalculatedFieldIntegrationTest.java index 209a2da6f1..8cf9c307a6 100644 --- a/application/src/test/java/org/thingsboard/server/cf/CalculatedFieldIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/cf/CalculatedFieldIntegrationTest.java @@ -1025,8 +1025,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes cf.setConfigurationVersion(1); PropagationCalculatedFieldConfiguration cfg = new PropagationCalculatedFieldConfiguration(); - cfg.setDirection(EntitySearchDirection.TO); - cfg.setRelationType(EntityRelation.CONTAINS_TYPE); + cfg.setRelation(new RelationPathLevel(EntitySearchDirection.TO, EntityRelation.CONTAINS_TYPE)); cfg.setApplyExpressionToResolvedArguments(true); Argument arg = new Argument(); @@ -1105,8 +1104,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes cf.setConfigurationVersion(1); PropagationCalculatedFieldConfiguration cfg = new PropagationCalculatedFieldConfiguration(); - cfg.setDirection(EntitySearchDirection.TO); - cfg.setRelationType(EntityRelation.CONTAINS_TYPE); + cfg.setRelation(new RelationPathLevel(EntitySearchDirection.TO, EntityRelation.CONTAINS_TYPE)); cfg.setApplyExpressionToResolvedArguments(false); // arguments-only mode Argument arg = new Argument(); diff --git a/application/src/test/java/org/thingsboard/server/controller/CalculatedFieldControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/CalculatedFieldControllerTest.java index aa3e802f70..4ebace6ae7 100644 --- a/application/src/test/java/org/thingsboard/server/controller/CalculatedFieldControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/CalculatedFieldControllerTest.java @@ -271,8 +271,7 @@ public class CalculatedFieldControllerTest extends AbstractControllerTest { private CalculatedFieldConfiguration getPropagationCalculatedFieldConfig(Map arguments) { var config = new PropagationCalculatedFieldConfiguration(); - config.setRelationType(EntityRelation.CONTAINS_TYPE); - config.setDirection(EntitySearchDirection.TO); + config.setRelation(new RelationPathLevel(EntitySearchDirection.TO, EntityRelation.CONTAINS_TYPE)); config.setApplyExpressionToResolvedArguments(false); config.setExpression(null); diff --git a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/PropagationCalculatedFieldStateTest.java b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/PropagationCalculatedFieldStateTest.java index 04a7ab5203..4f8b14f9b5 100644 --- a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/PropagationCalculatedFieldStateTest.java +++ b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/PropagationCalculatedFieldStateTest.java @@ -42,6 +42,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.DoubleDataEntry; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntitySearchDirection; +import org.thingsboard.server.common.data.relation.RelationPathLevel; import org.thingsboard.server.common.stats.DefaultStatsFactory; import org.thingsboard.server.dao.usagerecord.ApiLimitService; import org.thingsboard.server.service.cf.PropagationCalculatedFieldResult; @@ -222,8 +223,7 @@ public class PropagationCalculatedFieldStateTest { private CalculatedFieldConfiguration getCalculatedFieldConfig(boolean applyExpressionToResolvedArguments) { var config = new PropagationCalculatedFieldConfiguration(); - config.setDirection(EntitySearchDirection.TO); - config.setRelationType(EntityRelation.CONTAINS_TYPE); + config.setRelation(new RelationPathLevel(EntitySearchDirection.TO, EntityRelation.CONTAINS_TYPE)); config.setApplyExpressionToResolvedArguments(applyExpressionToResolvedArguments); Argument temperatureArg = new Argument(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/PropagationCalculatedFieldConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/PropagationCalculatedFieldConfiguration.java index 5e8c822d78..1dfa9d3cb7 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/PropagationCalculatedFieldConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/PropagationCalculatedFieldConfiguration.java @@ -15,13 +15,11 @@ */ package org.thingsboard.server.common.data.cf.configuration; -import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.Data; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.cf.CalculatedFieldType; -import org.thingsboard.server.common.data.relation.EntitySearchDirection; import org.thingsboard.server.common.data.relation.RelationPathLevel; import java.util.List; @@ -33,9 +31,7 @@ public class PropagationCalculatedFieldConfiguration extends BaseCalculatedField public static final String PROPAGATION_CONFIG_ARGUMENT = "propagationCtx"; @NotNull - private EntitySearchDirection direction; - @NotBlank - private String relationType; + private RelationPathLevel relation; private boolean applyExpressionToResolvedArguments; @@ -46,6 +42,7 @@ public class PropagationCalculatedFieldConfiguration extends BaseCalculatedField @Override public void validate() { + relation.validate(); baseCalculatedFieldRestriction(); propagationRestriction(); if (!applyExpressionToResolvedArguments) { @@ -77,7 +74,7 @@ public class PropagationCalculatedFieldConfiguration extends BaseCalculatedField public Argument toPropagationArgument() { var refDynamicSourceConfiguration = new RelationPathQueryDynamicSourceConfiguration(); - refDynamicSourceConfiguration.setLevels(List.of(new RelationPathLevel(direction, relationType))); + refDynamicSourceConfiguration.setLevels(List.of(relation)); var propagationArgument = new Argument(); propagationArgument.setRefDynamicSourceConfiguration(refDynamicSourceConfiguration); return propagationArgument; diff --git a/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/PropagationCalculatedFieldConfigurationTest.java b/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/PropagationCalculatedFieldConfigurationTest.java index 36f63feed7..a3140ee63a 100644 --- a/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/PropagationCalculatedFieldConfigurationTest.java +++ b/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/PropagationCalculatedFieldConfigurationTest.java @@ -22,6 +22,7 @@ import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntitySearchDirection; +import org.thingsboard.server.common.data.relation.RelationPathLevel; import java.util.Map; import java.util.UUID; @@ -42,6 +43,7 @@ public class PropagationCalculatedFieldConfigurationTest { @Test void validateShouldThrowWhenConfigurationDisallowArgumentsWithReferencedEntity() { var cfg = new PropagationCalculatedFieldConfiguration(); + cfg.setRelation(new RelationPathLevel(EntitySearchDirection.TO, EntityRelation.CONTAINS_TYPE)); Argument argumentWithRefEntityIdSet = new Argument(); argumentWithRefEntityIdSet.setRefEntityId(new DeviceId(UUID.fromString("bda14084-f40e-4acc-9b85-9d1dd209bb64"))); cfg.setArguments(Map.of("argumentWithRefEntityIdSet", argumentWithRefEntityIdSet)); @@ -53,6 +55,7 @@ public class PropagationCalculatedFieldConfigurationTest { @Test void validateShouldThrowWhenConfigurationDisallowArgumentsWithDynamicReferenceConfiguration() { var cfg = new PropagationCalculatedFieldConfiguration(); + cfg.setRelation(new RelationPathLevel(EntitySearchDirection.TO, EntityRelation.CONTAINS_TYPE)); Argument argumentWithDynamicRefEntitySource = new Argument(); argumentWithDynamicRefEntitySource.setRefDynamicSourceConfiguration(new CurrentOwnerDynamicSourceConfiguration()); cfg.setArguments(Map.of("argumentWithDynamicRefEntitySource", argumentWithDynamicRefEntitySource)); @@ -64,6 +67,7 @@ public class PropagationCalculatedFieldConfigurationTest { @Test void validateShouldThrowWhenConfigurationHasNoArgumentsWithCurrentEntitySource() { var cfg = new PropagationCalculatedFieldConfiguration(); + cfg.setRelation(new RelationPathLevel(EntitySearchDirection.TO, EntityRelation.CONTAINS_TYPE)); Argument argumentWithRefEntityIdSet = new Argument(); argumentWithRefEntityIdSet.setRefEntityId(new DeviceId(UUID.fromString("3703e895-3f9b-4b75-a715-b68f1ad51944"))); cfg.setArguments(Map.of("argumentWithRefEntityIdSet", argumentWithRefEntityIdSet)); @@ -77,6 +81,7 @@ public class PropagationCalculatedFieldConfigurationTest { @Test void validateShouldThrowWhenUsedReservedPropagationArgumentName() { var cfg = new PropagationCalculatedFieldConfiguration(); + cfg.setRelation(new RelationPathLevel(EntitySearchDirection.TO, EntityRelation.CONTAINS_TYPE)); cfg.setArguments(Map.of(PROPAGATION_CONFIG_ARGUMENT, new Argument())); assertThatThrownBy(cfg::validate) .isInstanceOf(IllegalArgumentException.class) @@ -86,6 +91,7 @@ public class PropagationCalculatedFieldConfigurationTest { @Test void validateShouldThrowWhenUsedReservedCtxArgumentName() { var cfg = new PropagationCalculatedFieldConfiguration(); + cfg.setRelation(new RelationPathLevel(EntitySearchDirection.TO, EntityRelation.CONTAINS_TYPE)); cfg.setArguments(Map.of("ctx", new Argument())); assertThatThrownBy(cfg::validate) .isInstanceOf(IllegalArgumentException.class) @@ -95,6 +101,7 @@ public class PropagationCalculatedFieldConfigurationTest { @Test void validateShouldThrowWhenReferencedEntityKeyIsNotSet() { var cfg = new PropagationCalculatedFieldConfiguration(); + cfg.setRelation(new RelationPathLevel(EntitySearchDirection.TO, EntityRelation.CONTAINS_TYPE)); Argument argument = new Argument(); cfg.setArguments(Map.of("someArgumentName", argument)); assertThatThrownBy(cfg::validate) @@ -105,6 +112,7 @@ public class PropagationCalculatedFieldConfigurationTest { @Test void validateShouldThrowWhenReferencedEntityKeyTypeIsTsRolling() { var cfg = new PropagationCalculatedFieldConfiguration(); + cfg.setRelation(new RelationPathLevel(EntitySearchDirection.TO, EntityRelation.CONTAINS_TYPE)); ReferencedEntityKey referencedEntityKey = new ReferencedEntityKey("someKey", ArgumentType.TS_ROLLING, null); Argument argument = new Argument(); argument.setRefEntityKey(referencedEntityKey); @@ -118,6 +126,7 @@ public class PropagationCalculatedFieldConfigurationTest { @Test void validateShouldThrowWhenExpressionIsNotSet() { var cfg = new PropagationCalculatedFieldConfiguration(); + cfg.setRelation(new RelationPathLevel(EntitySearchDirection.TO, EntityRelation.CONTAINS_TYPE)); cfg.setArguments(Map.of("someArgumentName", new Argument())); cfg.setApplyExpressionToResolvedArguments(true); assertThatThrownBy(cfg::validate) @@ -128,8 +137,7 @@ public class PropagationCalculatedFieldConfigurationTest { @Test void validateToPropagationArgumentMethodCallReturnCorrectArgument() { var cfg = new PropagationCalculatedFieldConfiguration(); - cfg.setDirection(EntitySearchDirection.TO); - cfg.setRelationType(EntityRelation.CONTAINS_TYPE); + cfg.setRelation(new RelationPathLevel(EntitySearchDirection.TO, EntityRelation.CONTAINS_TYPE)); Argument propagationArgument = cfg.toPropagationArgument(); assertThat(propagationArgument).isNotNull(); From cb93f47138d5231fef14eeaca1c750a1f872e693 Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Mon, 27 Oct 2025 08:52:15 +0200 Subject: [PATCH 2/2] added jakarte validation --- .../PropagationCalculatedFieldConfiguration.java | 3 ++- .../server/common/data/relation/RelationPathLevel.java | 4 +++- .../PropagationCalculatedFieldConfigurationTest.java | 9 +-------- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/PropagationCalculatedFieldConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/PropagationCalculatedFieldConfiguration.java index 1dfa9d3cb7..61d4542eb9 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/PropagationCalculatedFieldConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/PropagationCalculatedFieldConfiguration.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.cf.configuration; +import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import lombok.Data; import lombok.EqualsAndHashCode; @@ -30,6 +31,7 @@ public class PropagationCalculatedFieldConfiguration extends BaseCalculatedField public static final String PROPAGATION_CONFIG_ARGUMENT = "propagationCtx"; + @Valid @NotNull private RelationPathLevel relation; @@ -42,7 +44,6 @@ public class PropagationCalculatedFieldConfiguration extends BaseCalculatedField @Override public void validate() { - relation.validate(); baseCalculatedFieldRestriction(); propagationRestriction(); if (!applyExpressionToResolvedArguments) { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationPathLevel.java b/common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationPathLevel.java index c28135204f..c04ca09e79 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationPathLevel.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationPathLevel.java @@ -15,9 +15,11 @@ */ package org.thingsboard.server.common.data.relation; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import org.thingsboard.server.common.data.StringUtils; -public record RelationPathLevel(EntitySearchDirection direction, String relationType) { +public record RelationPathLevel(@NotNull EntitySearchDirection direction, @NotBlank String relationType) { public void validate() { if (direction == null) { diff --git a/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/PropagationCalculatedFieldConfigurationTest.java b/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/PropagationCalculatedFieldConfigurationTest.java index a3140ee63a..9c77f1bd21 100644 --- a/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/PropagationCalculatedFieldConfigurationTest.java +++ b/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/PropagationCalculatedFieldConfigurationTest.java @@ -43,7 +43,6 @@ public class PropagationCalculatedFieldConfigurationTest { @Test void validateShouldThrowWhenConfigurationDisallowArgumentsWithReferencedEntity() { var cfg = new PropagationCalculatedFieldConfiguration(); - cfg.setRelation(new RelationPathLevel(EntitySearchDirection.TO, EntityRelation.CONTAINS_TYPE)); Argument argumentWithRefEntityIdSet = new Argument(); argumentWithRefEntityIdSet.setRefEntityId(new DeviceId(UUID.fromString("bda14084-f40e-4acc-9b85-9d1dd209bb64"))); cfg.setArguments(Map.of("argumentWithRefEntityIdSet", argumentWithRefEntityIdSet)); @@ -55,7 +54,6 @@ public class PropagationCalculatedFieldConfigurationTest { @Test void validateShouldThrowWhenConfigurationDisallowArgumentsWithDynamicReferenceConfiguration() { var cfg = new PropagationCalculatedFieldConfiguration(); - cfg.setRelation(new RelationPathLevel(EntitySearchDirection.TO, EntityRelation.CONTAINS_TYPE)); Argument argumentWithDynamicRefEntitySource = new Argument(); argumentWithDynamicRefEntitySource.setRefDynamicSourceConfiguration(new CurrentOwnerDynamicSourceConfiguration()); cfg.setArguments(Map.of("argumentWithDynamicRefEntitySource", argumentWithDynamicRefEntitySource)); @@ -67,7 +65,6 @@ public class PropagationCalculatedFieldConfigurationTest { @Test void validateShouldThrowWhenConfigurationHasNoArgumentsWithCurrentEntitySource() { var cfg = new PropagationCalculatedFieldConfiguration(); - cfg.setRelation(new RelationPathLevel(EntitySearchDirection.TO, EntityRelation.CONTAINS_TYPE)); Argument argumentWithRefEntityIdSet = new Argument(); argumentWithRefEntityIdSet.setRefEntityId(new DeviceId(UUID.fromString("3703e895-3f9b-4b75-a715-b68f1ad51944"))); cfg.setArguments(Map.of("argumentWithRefEntityIdSet", argumentWithRefEntityIdSet)); @@ -75,13 +72,12 @@ public class PropagationCalculatedFieldConfigurationTest { assertThatThrownBy(cfg::validate) .isInstanceOf(IllegalArgumentException.class) .hasMessage("At least one argument must be configured with the 'Current entity' " + - "source entity type for 'Expression result' propagation mode!"); + "source entity type for 'Expression result' propagation mode!"); } @Test void validateShouldThrowWhenUsedReservedPropagationArgumentName() { var cfg = new PropagationCalculatedFieldConfiguration(); - cfg.setRelation(new RelationPathLevel(EntitySearchDirection.TO, EntityRelation.CONTAINS_TYPE)); cfg.setArguments(Map.of(PROPAGATION_CONFIG_ARGUMENT, new Argument())); assertThatThrownBy(cfg::validate) .isInstanceOf(IllegalArgumentException.class) @@ -91,7 +87,6 @@ public class PropagationCalculatedFieldConfigurationTest { @Test void validateShouldThrowWhenUsedReservedCtxArgumentName() { var cfg = new PropagationCalculatedFieldConfiguration(); - cfg.setRelation(new RelationPathLevel(EntitySearchDirection.TO, EntityRelation.CONTAINS_TYPE)); cfg.setArguments(Map.of("ctx", new Argument())); assertThatThrownBy(cfg::validate) .isInstanceOf(IllegalArgumentException.class) @@ -112,7 +107,6 @@ public class PropagationCalculatedFieldConfigurationTest { @Test void validateShouldThrowWhenReferencedEntityKeyTypeIsTsRolling() { var cfg = new PropagationCalculatedFieldConfiguration(); - cfg.setRelation(new RelationPathLevel(EntitySearchDirection.TO, EntityRelation.CONTAINS_TYPE)); ReferencedEntityKey referencedEntityKey = new ReferencedEntityKey("someKey", ArgumentType.TS_ROLLING, null); Argument argument = new Argument(); argument.setRefEntityKey(referencedEntityKey); @@ -126,7 +120,6 @@ public class PropagationCalculatedFieldConfigurationTest { @Test void validateShouldThrowWhenExpressionIsNotSet() { var cfg = new PropagationCalculatedFieldConfiguration(); - cfg.setRelation(new RelationPathLevel(EntitySearchDirection.TO, EntityRelation.CONTAINS_TYPE)); cfg.setArguments(Map.of("someArgumentName", new Argument())); cfg.setApplyExpressionToResolvedArguments(true); assertThatThrownBy(cfg::validate)