Browse Source

fixed NPE on reference entities check + added basic controller tests for Propagation CF

pull/14107/head
dshvaika 12 months ago
parent
commit
265b63dc06
  1. 95
      application/src/test/java/org/thingsboard/server/controller/CalculatedFieldControllerTest.java
  2. 2
      common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/AlarmCalculatedFieldConfiguration.java
  3. 7
      common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/ArgumentsBasedCalculatedFieldConfiguration.java
  4. 3
      common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/CalculatedFieldConfiguration.java
  5. 2
      common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/geofencing/GeofencingCalculatedFieldConfiguration.java
  6. 3
      dao/src/main/java/org/thingsboard/server/dao/cf/BaseCalculatedFieldService.java

95
application/src/test/java/org/thingsboard/server/controller/CalculatedFieldControllerTest.java

@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.cf.configuration.ArgumentType;
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.PropagationCalculatedFieldConfiguration;
import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey;
import org.thingsboard.server.common.data.cf.configuration.RelationPathQueryDynamicSourceConfiguration;
import org.thingsboard.server.common.data.cf.configuration.SimpleCalculatedFieldConfiguration;
@ -35,6 +36,7 @@ import org.thingsboard.server.common.data.cf.configuration.geofencing.EntityCoor
import org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingCalculatedFieldConfiguration;
import org.thingsboard.server.common.data.cf.configuration.geofencing.ZoneGroupConfiguration;
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 org.thingsboard.server.common.data.security.Authority;
@ -44,6 +46,7 @@ import java.util.List;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingReportStrategy.REPORT_TRANSITION_EVENTS_AND_PRESENCE_STATUS;
@ -81,7 +84,7 @@ public class CalculatedFieldControllerTest extends AbstractControllerTest {
@Test
public void testSaveCalculatedField() throws Exception {
Device testDevice = createDevice("Test device", "1234567890");
CalculatedField calculatedField = getCalculatedField(testDevice.getId());
CalculatedField calculatedField = getSimpleCalculatedField(testDevice.getId());
CalculatedField savedCalculatedField = doPost("/api/calculatedField", calculatedField, CalculatedField.class);
@ -109,7 +112,7 @@ public class CalculatedFieldControllerTest extends AbstractControllerTest {
@Test
public void testSaveGeofencingCalculatedField() throws Exception {
Device testDevice = createDevice("Test device", "1234567890");
CalculatedField calculatedField = getCalculatedField(testDevice.getId(), getGeofencingCalculatedFieldConfig());
CalculatedField calculatedField = getCalculatedField(testDevice.getId(), CalculatedFieldType.GEOFENCING);
CalculatedField savedCalculatedField = doPost("/api/calculatedField", calculatedField, CalculatedField.class);
@ -134,10 +137,48 @@ public class CalculatedFieldControllerTest extends AbstractControllerTest {
.andExpect(status().isOk());
}
@Test
public void testSavePropagationCalculatedField() throws Exception {
Device testDevice = createDevice("Test device", "1234567890");
CalculatedField calculatedField = getCalculatedField(testDevice.getId(), CalculatedFieldType.PROPAGATION);
CalculatedField savedCalculatedField = doPost("/api/calculatedField", calculatedField, CalculatedField.class);
assertThat(savedCalculatedField).isNotNull();
assertThat(savedCalculatedField.getId()).isNotNull();
assertThat(savedCalculatedField.getCreatedTime()).isGreaterThan(0);
assertThat(savedCalculatedField.getTenantId()).isEqualTo(savedTenant.getId());
assertThat(savedCalculatedField.getEntityId()).isEqualTo(calculatedField.getEntityId());
assertThat(savedCalculatedField.getType()).isEqualTo(calculatedField.getType());
assertThat(savedCalculatedField.getName()).isEqualTo(calculatedField.getName());
assertThat(savedCalculatedField.getConfiguration()).isEqualTo(getPropagationCalculatedFieldConfig());
assertThat(savedCalculatedField.getVersion()).isEqualTo(1L);
savedCalculatedField.setName("Test CF");
CalculatedField updatedCalculatedField = doPost("/api/calculatedField", savedCalculatedField, CalculatedField.class);
assertThat(updatedCalculatedField.getName()).isEqualTo(savedCalculatedField.getName());
assertThat(updatedCalculatedField.getVersion()).isEqualTo(savedCalculatedField.getVersion() + 1);
doDelete("/api/calculatedField/" + savedCalculatedField.getId().getId().toString())
.andExpect(status().isOk());
}
@Test
public void testSavePropagationCalculatedFieldWithNullArguments() throws Exception {
Device testDevice = createDevice("Test device", "1234567890");
CalculatedField calculatedField = getCalculatedField(testDevice.getId(), CalculatedFieldType.PROPAGATION, getPropagationCalculatedFieldConfig(null));
doPost("/api/calculatedField", calculatedField)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("arguments must not be empty")));
}
@Test
public void testGetCalculatedFieldById() throws Exception {
Device testDevice = createDevice("Test device", "1234567890");
CalculatedField calculatedField = getCalculatedField(testDevice.getId());
CalculatedField calculatedField = getSimpleCalculatedField(testDevice.getId());
CalculatedField savedCalculatedField = doPost("/api/calculatedField", calculatedField, CalculatedField.class);
CalculatedField fetchedCalculatedField = doGet("/api/calculatedField/" + savedCalculatedField.getId().getId(), CalculatedField.class);
@ -152,7 +193,7 @@ public class CalculatedFieldControllerTest extends AbstractControllerTest {
@Test
public void testDeleteCalculatedField() throws Exception {
Device testDevice = createDevice("Test device", "1234567890");
CalculatedField calculatedField = getCalculatedField(testDevice.getId());
CalculatedField calculatedField = getSimpleCalculatedField(testDevice.getId());
CalculatedField savedCalculatedField = doPost("/api/calculatedField", calculatedField, CalculatedField.class);
@ -163,17 +204,27 @@ public class CalculatedFieldControllerTest extends AbstractControllerTest {
doGet("/api/calculatedField/" + savedCalculatedField.getId().getId()).andExpect(status().isNotFound());
}
private CalculatedField getCalculatedField(DeviceId deviceId) {
return getCalculatedField(deviceId, getSimpleCalculatedFieldConfig());
private CalculatedField getSimpleCalculatedField(DeviceId deviceId) {
return getCalculatedField(deviceId, CalculatedFieldType.SIMPLE);
}
private CalculatedField getCalculatedField(DeviceId deviceId, CalculatedFieldType cfType) {
return getCalculatedField(deviceId, cfType, null);
}
private CalculatedField getCalculatedField(DeviceId deviceId, CalculatedFieldConfiguration configuration) {
private CalculatedField getCalculatedField(DeviceId deviceId, CalculatedFieldType cfType, CalculatedFieldConfiguration customConfiguration) {
CalculatedField calculatedField = new CalculatedField();
calculatedField.setEntityId(deviceId);
calculatedField.setType(CalculatedFieldType.SIMPLE);
calculatedField.setType(cfType);
calculatedField.setName("Test Calculated Field");
calculatedField.setConfigurationVersion(1);
calculatedField.setConfiguration(configuration);
if (customConfiguration != null) {
calculatedField.setConfiguration(customConfiguration);
} else switch (cfType) {
case SIMPLE -> calculatedField.setConfiguration(getSimpleCalculatedFieldConfig());
case GEOFENCING -> calculatedField.setConfiguration(getGeofencingCalculatedFieldConfig());
case PROPAGATION -> calculatedField.setConfiguration(getPropagationCalculatedFieldConfig());
}
calculatedField.setVersion(1L);
return calculatedField;
}
@ -198,6 +249,32 @@ public class CalculatedFieldControllerTest extends AbstractControllerTest {
return config;
}
private CalculatedFieldConfiguration getPropagationCalculatedFieldConfig() {
Argument arg = new Argument();
arg.setRefEntityKey(new ReferencedEntityKey("temperature", ArgumentType.TS_LATEST, null));
return getPropagationCalculatedFieldConfig(Map.of("t", arg));
}
private CalculatedFieldConfiguration getPropagationCalculatedFieldConfig(Map<String, Argument> arguments) {
var config = new PropagationCalculatedFieldConfiguration();
config.setRelationType(EntityRelation.CONTAINS_TYPE);
config.setDirection(EntitySearchDirection.TO);
config.setApplyExpressionToResolvedArguments(false);
config.setExpression(null);
Output output = new Output();
output.setType(OutputType.TIME_SERIES);
config.setOutput(output);
Argument arg = new Argument();
arg.setRefEntityKey(new ReferencedEntityKey("temperature", ArgumentType.TS_LATEST, null));
config.setArguments(arguments);
return config;
}
private CalculatedFieldConfiguration getSimpleCalculatedFieldConfig() {
SimpleCalculatedFieldConfiguration config = new SimpleCalculatedFieldConfiguration();

2
common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/AlarmCalculatedFieldConfiguration.java

@ -29,8 +29,6 @@ import java.util.Map;
@Data
public class AlarmCalculatedFieldConfiguration implements ArgumentsBasedCalculatedFieldConfiguration {
@Valid
@NotEmpty
private Map<String, Argument> arguments;
@Valid

7
common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/ArgumentsBasedCalculatedFieldConfiguration.java

@ -15,6 +15,8 @@
*/
package org.thingsboard.server.common.data.cf.configuration;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import org.thingsboard.server.common.data.id.EntityId;
import java.util.List;
@ -24,9 +26,14 @@ import java.util.stream.Collectors;
public interface ArgumentsBasedCalculatedFieldConfiguration extends CalculatedFieldConfiguration {
@Valid
@NotEmpty
Map<String, Argument> getArguments();
default List<EntityId> getReferencedEntities() {
if (getArguments() == null) {
return List.of();
}
return getArguments().values().stream()
.map(Argument::getRefEntityId)
.filter(Objects::nonNull)

3
common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/CalculatedFieldConfiguration.java

@ -27,7 +27,6 @@ import org.thingsboard.server.common.data.id.CalculatedFieldId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@ -55,7 +54,7 @@ public interface CalculatedFieldConfiguration {
@JsonIgnore
default List<EntityId> getReferencedEntities() {
return Collections.emptyList();
return List.of();
}
default CalculatedFieldLink buildCalculatedFieldLink(TenantId tenantId, EntityId referencedEntityId, CalculatedFieldId calculatedFieldId) {

2
common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/geofencing/GeofencingCalculatedFieldConfiguration.java

@ -56,7 +56,7 @@ public class GeofencingCalculatedFieldConfiguration implements ArgumentsBasedCal
@Override
public List<EntityId> getReferencedEntities() {
return zoneGroups.values().stream().map(ZoneGroupConfiguration::getRefEntityId).filter(Objects::nonNull).toList();
return zoneGroups == null ? List.of() : zoneGroups.values().stream().map(ZoneGroupConfiguration::getRefEntityId).filter(Objects::nonNull).toList();
}
@Override

3
dao/src/main/java/org/thingsboard/server/dao/cf/BaseCalculatedFieldService.java

@ -57,8 +57,7 @@ public class BaseCalculatedFieldService extends AbstractEntityService implements
@Override
public CalculatedField save(CalculatedField calculatedField) {
CalculatedField oldCalculatedField = calculatedFieldDataValidator.validate(calculatedField, CalculatedField::getTenantId);
return doSave(calculatedField, oldCalculatedField);
return save(calculatedField, true);
}
@Override

Loading…
Cancel
Save