Updated validation logic for existing and geofencing CF
This commit is contained in:
		
							parent
							
								
									ac3f81195d
								
							
						
					
					
						commit
						67f08da7a0
					
				@ -35,7 +35,6 @@ import org.thingsboard.server.common.data.StringUtils;
 | 
				
			|||||||
import org.thingsboard.server.common.data.cf.CalculatedFieldType;
 | 
					import org.thingsboard.server.common.data.cf.CalculatedFieldType;
 | 
				
			||||||
import org.thingsboard.server.common.data.cf.configuration.Argument;
 | 
					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.ArgumentType;
 | 
				
			||||||
import org.thingsboard.server.common.data.cf.configuration.CFArgumentDynamicSourceType;
 | 
					 | 
				
			||||||
import org.thingsboard.server.common.data.cf.configuration.OutputType;
 | 
					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.RelationQueryDynamicSourceConfiguration;
 | 
				
			||||||
import org.thingsboard.server.common.data.id.CalculatedFieldId;
 | 
					import org.thingsboard.server.common.data.id.CalculatedFieldId;
 | 
				
			||||||
@ -162,7 +161,7 @@ public class DefaultCalculatedFieldProcessingService implements CalculatedFieldP
 | 
				
			|||||||
        Set<Entry<String, Argument>> entries = ctx.getArguments().entrySet();
 | 
					        Set<Entry<String, Argument>> entries = ctx.getArguments().entrySet();
 | 
				
			||||||
        if (dynamicArgumentsOnly) {
 | 
					        if (dynamicArgumentsOnly) {
 | 
				
			||||||
            entries = entries.stream()
 | 
					            entries = entries.stream()
 | 
				
			||||||
                    .filter(entry -> CFArgumentDynamicSourceType.RELATION_QUERY.equals(entry.getValue().getRefDynamicSource()))
 | 
					                    .filter(entry -> entry.getValue().hasDynamicSource())
 | 
				
			||||||
                    .collect(Collectors.toSet());
 | 
					                    .collect(Collectors.toSet());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        for (var entry : entries) {
 | 
					        for (var entry : entries) {
 | 
				
			||||||
@ -293,13 +292,13 @@ public class DefaultCalculatedFieldProcessingService implements CalculatedFieldP
 | 
				
			|||||||
        if (value.getRefEntityId() != null) {
 | 
					        if (value.getRefEntityId() != null) {
 | 
				
			||||||
            return Futures.immediateFuture(List.of(value.getRefEntityId()));
 | 
					            return Futures.immediateFuture(List.of(value.getRefEntityId()));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        var refDynamicSource = value.getRefDynamicSource();
 | 
					        if (!value.hasDynamicSource()) {
 | 
				
			||||||
        if (refDynamicSource == null) {
 | 
					 | 
				
			||||||
            return Futures.immediateFuture(List.of(entityId));
 | 
					            return Futures.immediateFuture(List.of(entityId));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return switch (value.getRefDynamicSource()) {
 | 
					        var refDynamicSourceConfiguration = value.getRefDynamicSourceConfiguration();
 | 
				
			||||||
 | 
					        return switch (refDynamicSourceConfiguration.getType()) {
 | 
				
			||||||
            case RELATION_QUERY -> {
 | 
					            case RELATION_QUERY -> {
 | 
				
			||||||
                var configuration = (RelationQueryDynamicSourceConfiguration) value.getRefDynamicSourceConfiguration();
 | 
					                var configuration = (RelationQueryDynamicSourceConfiguration) refDynamicSourceConfiguration;
 | 
				
			||||||
                if (configuration.isSimpleRelation()) {
 | 
					                if (configuration.isSimpleRelation()) {
 | 
				
			||||||
                    yield switch (configuration.getDirection()) {
 | 
					                    yield switch (configuration.getDirection()) {
 | 
				
			||||||
                        case FROM ->
 | 
					                        case FROM ->
 | 
				
			||||||
 | 
				
			|||||||
@ -90,7 +90,7 @@ public class CalculatedFieldCtx {
 | 
				
			|||||||
        for (Map.Entry<String, Argument> entry : arguments.entrySet()) {
 | 
					        for (Map.Entry<String, Argument> entry : arguments.entrySet()) {
 | 
				
			||||||
            var refId = entry.getValue().getRefEntityId();
 | 
					            var refId = entry.getValue().getRefEntityId();
 | 
				
			||||||
            var refKey = entry.getValue().getRefEntityKey();
 | 
					            var refKey = entry.getValue().getRefEntityKey();
 | 
				
			||||||
            if (refId == null && entry.getValue().getRefDynamicSource() != null) {
 | 
					            if (refId == null && entry.getValue().hasDynamicSource()) {
 | 
				
			||||||
                continue;
 | 
					                continue;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if (refId == null || refId.equals(calculatedField.getEntityId())) {
 | 
					            if (refId == null || refId.equals(calculatedField.getEntityId())) {
 | 
				
			||||||
 | 
				
			|||||||
@ -30,7 +30,6 @@ import org.thingsboard.server.common.data.cf.CalculatedField;
 | 
				
			|||||||
import org.thingsboard.server.common.data.cf.CalculatedFieldType;
 | 
					import org.thingsboard.server.common.data.cf.CalculatedFieldType;
 | 
				
			||||||
import org.thingsboard.server.common.data.cf.configuration.Argument;
 | 
					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.ArgumentType;
 | 
				
			||||||
import org.thingsboard.server.common.data.cf.configuration.CFArgumentDynamicSourceType;
 | 
					 | 
				
			||||||
import org.thingsboard.server.common.data.cf.configuration.GeofencingCalculatedFieldConfiguration;
 | 
					import org.thingsboard.server.common.data.cf.configuration.GeofencingCalculatedFieldConfiguration;
 | 
				
			||||||
import org.thingsboard.server.common.data.cf.configuration.GeofencingEvent;
 | 
					import org.thingsboard.server.common.data.cf.configuration.GeofencingEvent;
 | 
				
			||||||
import org.thingsboard.server.common.data.cf.configuration.GeofencingZoneGroupConfiguration;
 | 
					import org.thingsboard.server.common.data.cf.configuration.GeofencingZoneGroupConfiguration;
 | 
				
			||||||
@ -681,7 +680,6 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes
 | 
				
			|||||||
        allowedZonesRefDynamicSourceConfiguration.setMaxLevel(1);
 | 
					        allowedZonesRefDynamicSourceConfiguration.setMaxLevel(1);
 | 
				
			||||||
        allowedZonesRefDynamicSourceConfiguration.setFetchLastLevelOnly(true);
 | 
					        allowedZonesRefDynamicSourceConfiguration.setFetchLastLevelOnly(true);
 | 
				
			||||||
        allowedZones.setRefEntityKey(new ReferencedEntityKey("zone", ArgumentType.ATTRIBUTE, AttributeScope.SERVER_SCOPE));
 | 
					        allowedZones.setRefEntityKey(new ReferencedEntityKey("zone", ArgumentType.ATTRIBUTE, AttributeScope.SERVER_SCOPE));
 | 
				
			||||||
        allowedZones.setRefDynamicSource(CFArgumentDynamicSourceType.RELATION_QUERY);
 | 
					 | 
				
			||||||
        allowedZones.setRefDynamicSourceConfiguration(allowedZonesRefDynamicSourceConfiguration);
 | 
					        allowedZones.setRefDynamicSourceConfiguration(allowedZonesRefDynamicSourceConfiguration);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Argument restrictedZones = new Argument();
 | 
					        Argument restrictedZones = new Argument();
 | 
				
			||||||
@ -691,7 +689,6 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes
 | 
				
			|||||||
        restrictedZonesRefDynamicSourceConfiguration.setMaxLevel(1);
 | 
					        restrictedZonesRefDynamicSourceConfiguration.setMaxLevel(1);
 | 
				
			||||||
        restrictedZonesRefDynamicSourceConfiguration.setFetchLastLevelOnly(true);
 | 
					        restrictedZonesRefDynamicSourceConfiguration.setFetchLastLevelOnly(true);
 | 
				
			||||||
        restrictedZones.setRefEntityKey(new ReferencedEntityKey("zone", ArgumentType.ATTRIBUTE, AttributeScope.SERVER_SCOPE));
 | 
					        restrictedZones.setRefEntityKey(new ReferencedEntityKey("zone", ArgumentType.ATTRIBUTE, AttributeScope.SERVER_SCOPE));
 | 
				
			||||||
        restrictedZones.setRefDynamicSource(CFArgumentDynamicSourceType.RELATION_QUERY);
 | 
					 | 
				
			||||||
        restrictedZones.setRefDynamicSourceConfiguration(restrictedZonesRefDynamicSourceConfiguration);
 | 
					        restrictedZones.setRefDynamicSourceConfiguration(restrictedZonesRefDynamicSourceConfiguration);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        cfg.setArguments(Map.of(
 | 
					        cfg.setArguments(Map.of(
 | 
				
			||||||
 | 
				
			|||||||
@ -27,7 +27,6 @@ import org.thingsboard.server.common.data.cf.CalculatedField;
 | 
				
			|||||||
import org.thingsboard.server.common.data.cf.CalculatedFieldType;
 | 
					import org.thingsboard.server.common.data.cf.CalculatedFieldType;
 | 
				
			||||||
import org.thingsboard.server.common.data.cf.configuration.Argument;
 | 
					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.ArgumentType;
 | 
				
			||||||
import org.thingsboard.server.common.data.cf.configuration.CFArgumentDynamicSourceType;
 | 
					 | 
				
			||||||
import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration;
 | 
					import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration;
 | 
				
			||||||
import org.thingsboard.server.common.data.cf.configuration.GeofencingCalculatedFieldConfiguration;
 | 
					import org.thingsboard.server.common.data.cf.configuration.GeofencingCalculatedFieldConfiguration;
 | 
				
			||||||
import org.thingsboard.server.common.data.cf.configuration.GeofencingEvent;
 | 
					import org.thingsboard.server.common.data.cf.configuration.GeofencingEvent;
 | 
				
			||||||
@ -323,7 +322,6 @@ public class GeofencingCalculatedFieldStateTest {
 | 
				
			|||||||
        refDynamicSourceConfiguration3.setMaxLevel(1);
 | 
					        refDynamicSourceConfiguration3.setMaxLevel(1);
 | 
				
			||||||
        refDynamicSourceConfiguration3.setFetchLastLevelOnly(true);
 | 
					        refDynamicSourceConfiguration3.setFetchLastLevelOnly(true);
 | 
				
			||||||
        argument3.setRefEntityKey(refEntityKey3);
 | 
					        argument3.setRefEntityKey(refEntityKey3);
 | 
				
			||||||
        argument3.setRefDynamicSource(CFArgumentDynamicSourceType.RELATION_QUERY);
 | 
					 | 
				
			||||||
        argument3.setRefDynamicSourceConfiguration(refDynamicSourceConfiguration3);
 | 
					        argument3.setRefDynamicSourceConfiguration(refDynamicSourceConfiguration3);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Argument argument4 = new Argument();
 | 
					        Argument argument4 = new Argument();
 | 
				
			||||||
@ -334,7 +332,6 @@ public class GeofencingCalculatedFieldStateTest {
 | 
				
			|||||||
        refDynamicSourceConfiguration4.setMaxLevel(1);
 | 
					        refDynamicSourceConfiguration4.setMaxLevel(1);
 | 
				
			||||||
        refDynamicSourceConfiguration4.setFetchLastLevelOnly(true);
 | 
					        refDynamicSourceConfiguration4.setFetchLastLevelOnly(true);
 | 
				
			||||||
        argument4.setRefEntityKey(refEntityKey4);
 | 
					        argument4.setRefEntityKey(refEntityKey4);
 | 
				
			||||||
        argument4.setRefDynamicSource(CFArgumentDynamicSourceType.RELATION_QUERY);
 | 
					 | 
				
			||||||
        argument4.setRefDynamicSourceConfiguration(refDynamicSourceConfiguration4);
 | 
					        argument4.setRefDynamicSourceConfiguration(refDynamicSourceConfiguration4);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        config.setArguments(Map.of("latitude", argument1, "longitude", argument2, "allowedZones", argument3, "restrictedZones", argument4));
 | 
					        config.setArguments(Map.of("latitude", argument1, "longitude", argument2, "allowedZones", argument3, "restrictedZones", argument4));
 | 
				
			||||||
 | 
				
			|||||||
@ -26,7 +26,7 @@ public class Argument {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @Nullable
 | 
					    @Nullable
 | 
				
			||||||
    private EntityId refEntityId;
 | 
					    private EntityId refEntityId;
 | 
				
			||||||
    private CFArgumentDynamicSourceType refDynamicSource;
 | 
					    // TODO: add upgrade in PE version -> CFArgumentDynamicSourceType to CFArgumentDynamicSourceConfiguration
 | 
				
			||||||
    private CfArgumentDynamicSourceConfiguration refDynamicSourceConfiguration;
 | 
					    private CfArgumentDynamicSourceConfiguration refDynamicSourceConfiguration;
 | 
				
			||||||
    private ReferencedEntityKey refEntityKey;
 | 
					    private ReferencedEntityKey refEntityKey;
 | 
				
			||||||
    private String defaultValue;
 | 
					    private String defaultValue;
 | 
				
			||||||
@ -34,4 +34,8 @@ public class Argument {
 | 
				
			|||||||
    private Integer limit;
 | 
					    private Integer limit;
 | 
				
			||||||
    private Long timeWindow;
 | 
					    private Long timeWindow;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean hasDynamicSource() {
 | 
				
			||||||
 | 
					        return refDynamicSourceConfiguration != null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -58,14 +58,10 @@ public abstract class BaseCalculatedFieldConfiguration implements CalculatedFiel
 | 
				
			|||||||
        return link;
 | 
					        return link;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // TODO: update validate method in PE version.
 | 
					 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void validate() {
 | 
					    public void validate() {
 | 
				
			||||||
        boolean hasDynamicSourceRelationQuery = arguments.values()
 | 
					        if (arguments.values().stream().anyMatch(Argument::hasDynamicSource)) {
 | 
				
			||||||
                .stream()
 | 
					            throw new IllegalArgumentException("Calculated field with type: '" + getType() + "' doesn't support dynamic source configuration!");
 | 
				
			||||||
                .anyMatch(arg -> CFArgumentDynamicSourceType.RELATION_QUERY.equals(arg.getRefDynamicSource()));
 | 
					 | 
				
			||||||
        if (hasDynamicSourceRelationQuery) {
 | 
					 | 
				
			||||||
            throw new IllegalArgumentException("Calculated field with type: '" + getType() + "' doesn't support arguments with 'RELATION_QUERY' dynamic source type!");
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -27,8 +27,6 @@ import java.util.Map;
 | 
				
			|||||||
import java.util.Set;
 | 
					import java.util.Set;
 | 
				
			||||||
import java.util.stream.Collectors;
 | 
					import java.util.stream.Collectors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static org.thingsboard.server.common.data.cf.configuration.CFArgumentDynamicSourceType.RELATION_QUERY;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@Data
 | 
					@Data
 | 
				
			||||||
@EqualsAndHashCode(callSuper = true)
 | 
					@EqualsAndHashCode(callSuper = true)
 | 
				
			||||||
public class GeofencingCalculatedFieldConfiguration extends BaseCalculatedFieldConfiguration implements CalculatedFieldConfiguration {
 | 
					public class GeofencingCalculatedFieldConfiguration extends BaseCalculatedFieldConfiguration implements CalculatedFieldConfiguration {
 | 
				
			||||||
@ -55,7 +53,7 @@ public class GeofencingCalculatedFieldConfiguration extends BaseCalculatedFieldC
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public boolean isScheduledUpdateEnabled() {
 | 
					    public boolean isScheduledUpdateEnabled() {
 | 
				
			||||||
        return scheduledUpdateIntervalSec > 0 && arguments.values().stream().anyMatch(arg -> arg.getRefDynamicSource() != null);
 | 
					        return scheduledUpdateIntervalSec > 0 && arguments.values().stream().anyMatch(Argument::hasDynamicSource);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // TODO: update validate method in PE version.
 | 
					    // TODO: update validate method in PE version.
 | 
				
			||||||
@ -67,9 +65,6 @@ public class GeofencingCalculatedFieldConfiguration extends BaseCalculatedFieldC
 | 
				
			|||||||
        if (arguments.size() < 3) {
 | 
					        if (arguments.size() < 3) {
 | 
				
			||||||
            throw new IllegalArgumentException("Geofencing calculated field must contain at least 3 arguments!");
 | 
					            throw new IllegalArgumentException("Geofencing calculated field must contain at least 3 arguments!");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (arguments.size() > 5) {
 | 
					 | 
				
			||||||
            throw new IllegalArgumentException("Geofencing calculated field size exceeds limit of 5 arguments!");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        validateCoordinateArguments();
 | 
					        validateCoordinateArguments();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Map<String, Argument> zoneGroupsArguments = getZoneGroupArguments();
 | 
					        Map<String, Argument> zoneGroupsArguments = getZoneGroupArguments();
 | 
				
			||||||
@ -129,7 +124,7 @@ public class GeofencingCalculatedFieldConfiguration extends BaseCalculatedFieldC
 | 
				
			|||||||
            if (!ArgumentType.TS_LATEST.equals(refEntityKey.getType())) {
 | 
					            if (!ArgumentType.TS_LATEST.equals(refEntityKey.getType())) {
 | 
				
			||||||
                throw new IllegalArgumentException("Argument '" + coordinateKey + "' must be of type TS_LATEST.");
 | 
					                throw new IllegalArgumentException("Argument '" + coordinateKey + "' must be of type TS_LATEST.");
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if (argument.getRefDynamicSource() != null) {
 | 
					            if (argument.hasDynamicSource()) {
 | 
				
			||||||
                throw new IllegalArgumentException("Dynamic source is not allowed for argument: '" + coordinateKey + "'.");
 | 
					                throw new IllegalArgumentException("Dynamic source is not allowed for argument: '" + coordinateKey + "'.");
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -144,17 +139,9 @@ public class GeofencingCalculatedFieldConfiguration extends BaseCalculatedFieldC
 | 
				
			|||||||
            if (!ArgumentType.ATTRIBUTE.equals(refEntityKey.getType())) {
 | 
					            if (!ArgumentType.ATTRIBUTE.equals(refEntityKey.getType())) {
 | 
				
			||||||
                throw new IllegalArgumentException("Argument '" + argumentKey + "' must be of type ATTRIBUTE.");
 | 
					                throw new IllegalArgumentException("Argument '" + argumentKey + "' must be of type ATTRIBUTE.");
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            var dynamicSource = argument.getRefDynamicSource();
 | 
					            if (argument.hasDynamicSource()) {
 | 
				
			||||||
            if (dynamicSource == null) {
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            if (!RELATION_QUERY.equals(dynamicSource)) {
 | 
					 | 
				
			||||||
                throw new IllegalArgumentException("Only relation query dynamic source is supported for argument: '" + argumentKey + "'.");
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            if (argument.getRefDynamicSourceConfiguration() == null) {
 | 
					 | 
				
			||||||
                throw new IllegalArgumentException("Missing dynamic source configuration for argument: '" + argumentKey + "'.");
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
                argument.getRefDynamicSourceConfiguration().validate();
 | 
					                argument.getRefDynamicSourceConfiguration().validate();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user