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 ffd876575c..efcc38304a 100644 --- a/application/src/test/java/org/thingsboard/server/cf/CalculatedFieldIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/cf/CalculatedFieldIntegrationTest.java @@ -36,7 +36,7 @@ import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfig import org.thingsboard.server.common.data.cf.configuration.Output; import org.thingsboard.server.common.data.cf.configuration.OutputType; import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; -import org.thingsboard.server.common.data.cf.configuration.RelationQueryDynamicSourceConfiguration; +import org.thingsboard.server.common.data.cf.configuration.RelationPathQueryDynamicSourceConfiguration; import org.thingsboard.server.common.data.cf.configuration.ScriptCalculatedFieldConfiguration; import org.thingsboard.server.common.data.cf.configuration.SimpleCalculatedFieldConfiguration; import org.thingsboard.server.common.data.cf.configuration.geofencing.EntityCoordinates; @@ -47,10 +47,12 @@ import org.thingsboard.server.common.data.id.AssetProfileId; import org.thingsboard.server.common.data.id.EntityId; 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.controller.CalculatedFieldControllerTest; import org.thingsboard.server.dao.service.DaoSqlTest; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -760,19 +762,13 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes // Zone groups: ATTRIBUTE on specific assets (one zone per group) ZoneGroupConfiguration allowedZonesGroup = new ZoneGroupConfiguration("zone", REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS, false); - var allowedZoneDynamicSourceConfiguration = new RelationQueryDynamicSourceConfiguration(); - allowedZoneDynamicSourceConfiguration.setDirection(EntitySearchDirection.FROM); - allowedZoneDynamicSourceConfiguration.setRelationType("AllowedZone"); - allowedZoneDynamicSourceConfiguration.setMaxLevel(1); - allowedZoneDynamicSourceConfiguration.setFetchLastLevelOnly(true); + var allowedZoneDynamicSourceConfiguration = new RelationPathQueryDynamicSourceConfiguration(); + allowedZoneDynamicSourceConfiguration.setLevels(List.of(new RelationPathLevel(EntitySearchDirection.FROM, "AllowedZone"))); allowedZonesGroup.setRefDynamicSourceConfiguration(allowedZoneDynamicSourceConfiguration); ZoneGroupConfiguration restrictedZonesGroup = new ZoneGroupConfiguration("zone", REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS, false); - var restrictedZoneDynamicSourceConfiguration = new RelationQueryDynamicSourceConfiguration(); - restrictedZoneDynamicSourceConfiguration.setDirection(EntitySearchDirection.FROM); - restrictedZoneDynamicSourceConfiguration.setRelationType("RestrictedZone"); - restrictedZoneDynamicSourceConfiguration.setMaxLevel(1); - restrictedZoneDynamicSourceConfiguration.setFetchLastLevelOnly(true); + var restrictedZoneDynamicSourceConfiguration = new RelationPathQueryDynamicSourceConfiguration(); + restrictedZoneDynamicSourceConfiguration.setLevels(List.of(new RelationPathLevel(EntitySearchDirection.FROM, "RestrictedZone"))); restrictedZonesGroup.setRefDynamicSourceConfiguration(restrictedZoneDynamicSourceConfiguration); cfg.setZoneGroups(Map.of("allowedZones", allowedZonesGroup, "restrictedZones", restrictedZonesGroup)); @@ -870,11 +866,8 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes cfg.setEntityCoordinates(new EntityCoordinates(ENTITY_ID_LATITUDE_ARGUMENT_KEY, ENTITY_ID_LONGITUDE_ARGUMENT_KEY)); var allowedZonesGroup = new ZoneGroupConfiguration("zone", REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS, false); - var allowedZoneDynamicSourceConfiguration = new RelationQueryDynamicSourceConfiguration(); - allowedZoneDynamicSourceConfiguration.setDirection(EntitySearchDirection.FROM); - allowedZoneDynamicSourceConfiguration.setRelationType("AllowedZone"); - allowedZoneDynamicSourceConfiguration.setMaxLevel(1); - allowedZoneDynamicSourceConfiguration.setFetchLastLevelOnly(true); + var allowedZoneDynamicSourceConfiguration = new RelationPathQueryDynamicSourceConfiguration(); + allowedZoneDynamicSourceConfiguration.setLevels(List.of(new RelationPathLevel(EntitySearchDirection.FROM, "AllowedZone"))); allowedZonesGroup.setRefDynamicSourceConfiguration(allowedZoneDynamicSourceConfiguration); cfg.setZoneGroups(Map.of("allowedZones", allowedZonesGroup)); diff --git a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/GeofencingCalculatedFieldStateTest.java b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/GeofencingCalculatedFieldStateTest.java index a86af77555..1f1ee32df2 100644 --- a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/GeofencingCalculatedFieldStateTest.java +++ b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/GeofencingCalculatedFieldStateTest.java @@ -28,7 +28,7 @@ import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration; import org.thingsboard.server.common.data.cf.configuration.Output; import org.thingsboard.server.common.data.cf.configuration.OutputType; -import org.thingsboard.server.common.data.cf.configuration.RelationQueryDynamicSourceConfiguration; +import org.thingsboard.server.common.data.cf.configuration.RelationPathQueryDynamicSourceConfiguration; import org.thingsboard.server.common.data.cf.configuration.geofencing.EntityCoordinates; import org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingCalculatedFieldConfiguration; import org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingReportStrategy; @@ -41,6 +41,7 @@ import org.thingsboard.server.common.data.kv.DoubleDataEntry; import org.thingsboard.server.common.data.kv.JsonDataEntry; 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.dao.relation.RelationService; import org.thingsboard.server.dao.usagerecord.ApiLimitService; import org.thingsboard.server.service.cf.CalculatedFieldResult; @@ -453,21 +454,15 @@ public class GeofencingCalculatedFieldStateTest { config.setEntityCoordinates(entityCoordinates); ZoneGroupConfiguration allowedZonesGroup = new ZoneGroupConfiguration("zone", reportStrategy, true); - var allowedZoneDynamicSourceConfiguration = new RelationQueryDynamicSourceConfiguration(); - allowedZoneDynamicSourceConfiguration.setDirection(EntitySearchDirection.TO); - allowedZoneDynamicSourceConfiguration.setRelationType("AllowedZone"); - allowedZoneDynamicSourceConfiguration.setMaxLevel(1); - allowedZoneDynamicSourceConfiguration.setFetchLastLevelOnly(true); + var allowedZoneDynamicSourceConfiguration = new RelationPathQueryDynamicSourceConfiguration(); + allowedZoneDynamicSourceConfiguration.setLevels(List.of(new RelationPathLevel(EntitySearchDirection.TO, "AllowedZone"))); allowedZonesGroup.setRefDynamicSourceConfiguration(allowedZoneDynamicSourceConfiguration); allowedZonesGroup.setRelationType("CurrentZone"); allowedZonesGroup.setDirection(EntitySearchDirection.TO); ZoneGroupConfiguration restrictedZonesGroup = new ZoneGroupConfiguration("zone", reportStrategy, true); - var restrictedZoneDynamicSourceConfiguration = new RelationQueryDynamicSourceConfiguration(); - restrictedZoneDynamicSourceConfiguration.setDirection(EntitySearchDirection.TO); - restrictedZoneDynamicSourceConfiguration.setRelationType("RestrictedZone"); - restrictedZoneDynamicSourceConfiguration.setMaxLevel(1); - restrictedZoneDynamicSourceConfiguration.setFetchLastLevelOnly(true); + var restrictedZoneDynamicSourceConfiguration = new RelationPathQueryDynamicSourceConfiguration(); + restrictedZoneDynamicSourceConfiguration.setLevels(List.of(new RelationPathLevel(EntitySearchDirection.TO, "RestrictedZone"))); restrictedZonesGroup.setRefDynamicSourceConfiguration(restrictedZoneDynamicSourceConfiguration); restrictedZonesGroup.setRelationType("CurrentZone"); restrictedZonesGroup.setDirection(EntitySearchDirection.TO); diff --git a/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/ArgumentTest.java b/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/ArgumentTest.java index fd59317649..d1108e9eed 100644 --- a/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/ArgumentTest.java +++ b/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/ArgumentTest.java @@ -31,7 +31,7 @@ public class ArgumentTest { @Test void validateShouldReturnTrueIfDynamicSourceConfigurationIsNotNull() { var argument = new Argument(); - argument.setRefDynamicSourceConfiguration(new RelationQueryDynamicSourceConfiguration()); + argument.setRefDynamicSourceConfiguration(new RelationPathQueryDynamicSourceConfiguration()); assertThat(argument.hasDynamicSource()).isTrue(); } diff --git a/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/geofencing/ZoneGroupConfigurationTest.java b/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/geofencing/ZoneGroupConfigurationTest.java index 4eb822d93c..beb4639a31 100644 --- a/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/geofencing/ZoneGroupConfigurationTest.java +++ b/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/geofencing/ZoneGroupConfigurationTest.java @@ -23,7 +23,7 @@ import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.cf.configuration.Argument; import org.thingsboard.server.common.data.cf.configuration.ArgumentType; import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; -import org.thingsboard.server.common.data.cf.configuration.RelationQueryDynamicSourceConfiguration; +import org.thingsboard.server.common.data.cf.configuration.RelationPathQueryDynamicSourceConfiguration; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntitySearchDirection; @@ -100,7 +100,7 @@ public class ZoneGroupConfigurationTest { @Test void whenHasDynamicSourceCalled_shouldReturnTrueIfDynamicSourceConfigurationIsNotNull() { var zoneGroupConfiguration = new ZoneGroupConfiguration("perimeter", REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS, false); - zoneGroupConfiguration.setRefDynamicSourceConfiguration(new RelationQueryDynamicSourceConfiguration()); + zoneGroupConfiguration.setRefDynamicSourceConfiguration(new RelationPathQueryDynamicSourceConfiguration()); assertThat(zoneGroupConfiguration.hasDynamicSource()).isTrue(); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/relation/JpaRelationDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/relation/JpaRelationDao.java index 782c9b9d53..b2871313ed 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/relation/JpaRelationDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/relation/JpaRelationDao.java @@ -307,7 +307,7 @@ public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService imple String sql = buildRelationPathSql(query); Object[] params = buildRelationPathParams(query); - log.info("[{}] relation path query: {}", tenantId, sql); + log.trace("[{}] relation path query: {}", tenantId, sql); return jdbcTemplate.queryForList(sql, params).stream() .map(row -> { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/CalculatedFieldServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/CalculatedFieldServiceTest.java index 7f563ea436..4b830835fa 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/CalculatedFieldServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/CalculatedFieldServiceTest.java @@ -31,7 +31,7 @@ import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfig import org.thingsboard.server.common.data.cf.configuration.Output; import org.thingsboard.server.common.data.cf.configuration.OutputType; import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; -import org.thingsboard.server.common.data.cf.configuration.RelationQueryDynamicSourceConfiguration; +import org.thingsboard.server.common.data.cf.configuration.RelationPathQueryDynamicSourceConfiguration; import org.thingsboard.server.common.data.cf.configuration.SimpleCalculatedFieldConfiguration; import org.thingsboard.server.common.data.cf.configuration.geofencing.EntityCoordinates; import org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingCalculatedFieldConfiguration; @@ -39,15 +39,19 @@ import org.thingsboard.server.common.data.cf.configuration.geofencing.ZoneGroupC import org.thingsboard.server.common.data.id.EntityId; 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.dao.cf.CalculatedFieldService; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.mock; import static org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingReportStrategy.REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS; @DaoSqlTest @@ -112,10 +116,8 @@ public class CalculatedFieldServiceTest extends AbstractServiceTest { // Zone-group argument (ATTRIBUTE) — make it DYNAMIC so scheduling is enabled ZoneGroupConfiguration zoneGroupConfiguration = new ZoneGroupConfiguration("allowed", REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS, false); - var dynamicSourceConfiguration = new RelationQueryDynamicSourceConfiguration(); - dynamicSourceConfiguration.setDirection(EntitySearchDirection.FROM); - dynamicSourceConfiguration.setMaxLevel(1); - dynamicSourceConfiguration.setRelationType(EntityRelation.CONTAINS_TYPE); + var dynamicSourceConfiguration = new RelationPathQueryDynamicSourceConfiguration(); + dynamicSourceConfiguration.setLevels(List.of(new RelationPathLevel(EntitySearchDirection.FROM, EntityRelation.CONTAINS_TYPE))); zoneGroupConfiguration.setRefDynamicSourceConfiguration(dynamicSourceConfiguration); cfg.setZoneGroups(Map.of("allowed", zoneGroupConfiguration)); @@ -150,19 +152,26 @@ public class CalculatedFieldServiceTest extends AbstractServiceTest { // Arrange a device Device device = createTestDevice(); - // Build a valid Geofencing configuration GeofencingCalculatedFieldConfiguration cfg = new GeofencingCalculatedFieldConfiguration(); // Coordinates: TS_LATEST, no dynamic source EntityCoordinates entityCoordinates = new EntityCoordinates("latitude", "longitude"); cfg.setEntityCoordinates(entityCoordinates); - // Zone-group argument (ATTRIBUTE) — make it DYNAMIC so scheduling is enabled + int maxRelationLevel = tbTenantProfileCache.get(tenantId) + .getDefaultProfileConfiguration() + .getMaxRelationLevelPerCfArgument(); + + // Zone-group argument (ATTRIBUTE) ZoneGroupConfiguration zoneGroupConfiguration = new ZoneGroupConfiguration( "allowed", REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS, false); - var dynamicSourceConfiguration = new RelationQueryDynamicSourceConfiguration(); - dynamicSourceConfiguration.setDirection(EntitySearchDirection.FROM); - dynamicSourceConfiguration.setMaxLevel(Integer.MAX_VALUE); - dynamicSourceConfiguration.setRelationType(EntityRelation.CONTAINS_TYPE); + var dynamicSourceConfiguration = new RelationPathQueryDynamicSourceConfiguration(); + + List levels = new ArrayList<>(); + for (int i = 0; i < maxRelationLevel + 1; i++) { + levels.add(mock(RelationPathLevel.class)); + } + + dynamicSourceConfiguration.setLevels(levels); zoneGroupConfiguration.setRefDynamicSourceConfiguration(dynamicSourceConfiguration); cfg.setZoneGroups(Map.of("allowed", zoneGroupConfiguration)); @@ -195,10 +204,8 @@ public class CalculatedFieldServiceTest extends AbstractServiceTest { // Zone-group argument (ATTRIBUTE) — make it DYNAMIC so scheduling is enabled ZoneGroupConfiguration zoneGroupConfiguration = new ZoneGroupConfiguration( "allowed", REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS, false); - var dynamicSourceConfiguration = new RelationQueryDynamicSourceConfiguration(); - dynamicSourceConfiguration.setDirection(EntitySearchDirection.FROM); - dynamicSourceConfiguration.setMaxLevel(1); - dynamicSourceConfiguration.setRelationType(EntityRelation.CONTAINS_TYPE); + var dynamicSourceConfiguration = new RelationPathQueryDynamicSourceConfiguration(); + dynamicSourceConfiguration.setLevels(List.of(new RelationPathLevel(EntitySearchDirection.FROM, EntityRelation.CONTAINS_TYPE))); zoneGroupConfiguration.setRefDynamicSourceConfiguration(dynamicSourceConfiguration); cfg.setZoneGroups(Map.of("allowed", zoneGroupConfiguration)); diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/cf/CalculatedFieldTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/cf/CalculatedFieldTest.java index 7f2dfba937..5e8d367538 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/cf/CalculatedFieldTest.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/cf/CalculatedFieldTest.java @@ -33,7 +33,7 @@ import org.thingsboard.server.common.data.cf.configuration.ArgumentType; import org.thingsboard.server.common.data.cf.configuration.Output; import org.thingsboard.server.common.data.cf.configuration.OutputType; import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; -import org.thingsboard.server.common.data.cf.configuration.RelationQueryDynamicSourceConfiguration; +import org.thingsboard.server.common.data.cf.configuration.RelationPathQueryDynamicSourceConfiguration; import org.thingsboard.server.common.data.cf.configuration.ScriptCalculatedFieldConfiguration; import org.thingsboard.server.common.data.cf.configuration.SimpleCalculatedFieldConfiguration; import org.thingsboard.server.common.data.cf.configuration.geofencing.EntityCoordinates; @@ -50,10 +50,12 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; 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.msa.AbstractContainerTest; import org.thingsboard.server.msa.ui.utils.EntityPrototypes; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -366,19 +368,13 @@ public class CalculatedFieldTest extends AbstractContainerTest { // Dynamic groups via relations ZoneGroupConfiguration allowedZoneGroupConfiguration = new ZoneGroupConfiguration("zone", REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS, false); - var allowedDynamicSourceConfiguration = new RelationQueryDynamicSourceConfiguration(); - allowedDynamicSourceConfiguration.setDirection(EntitySearchDirection.FROM); - allowedDynamicSourceConfiguration.setMaxLevel(1); - allowedDynamicSourceConfiguration.setFetchLastLevelOnly(true); - allowedDynamicSourceConfiguration.setRelationType("AllowedZone"); + var allowedDynamicSourceConfiguration = new RelationPathQueryDynamicSourceConfiguration(); + allowedDynamicSourceConfiguration.setLevels(List.of(new RelationPathLevel(EntitySearchDirection.FROM, "AllowedZone"))); allowedZoneGroupConfiguration.setRefDynamicSourceConfiguration(allowedDynamicSourceConfiguration); ZoneGroupConfiguration restrictedZoneGroupConfiguration = new ZoneGroupConfiguration("zone", REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS, false); - var restrictedDynamicSourceConfiguration = new RelationQueryDynamicSourceConfiguration(); - restrictedDynamicSourceConfiguration.setDirection(EntitySearchDirection.FROM); - restrictedDynamicSourceConfiguration.setMaxLevel(1); - restrictedDynamicSourceConfiguration.setFetchLastLevelOnly(true); - restrictedDynamicSourceConfiguration.setRelationType("RestrictedZone"); + var restrictedDynamicSourceConfiguration = new RelationPathQueryDynamicSourceConfiguration(); + restrictedDynamicSourceConfiguration.setLevels(List.of(new RelationPathLevel(EntitySearchDirection.FROM, "RestrictedZone"))); restrictedZoneGroupConfiguration.setRefDynamicSourceConfiguration(restrictedDynamicSourceConfiguration); cfg.setZoneGroups(Map.of("allowedZones", allowedZoneGroupConfiguration, "restrictedZones", restrictedZoneGroupConfiguration));