Browse Source

fixed delete attributes

pull/14141/head
IrynaMatveieva 12 months ago
parent
commit
55fb64ef13
  1. 49
      application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldEntityMessageProcessor.java
  2. 30
      application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldManagerMessageProcessor.java
  3. 75
      application/src/test/java/org/thingsboard/server/cf/LatestValuesAggregationCalculatedFieldTest.java

49
application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldEntityMessageProcessor.java

@ -551,19 +551,19 @@ public class CalculatedFieldEntityMessageProcessor extends AbstractContextAwareM
}
private Map<String, ArgumentEntry> mapToArguments(CalculatedFieldCtx ctx, List<TsKvProto> data) {
return mapToArguments(entityId, ctx.getMainEntityArguments(), ctx.getRelatedEntityArguments(), data);
return mapToArguments(entityId, ctx.getMainEntityArguments(), Collections.emptyMap(), data);
}
private Map<String, ArgumentEntry> mapToArguments(CalculatedFieldCtx ctx, EntityId entityId, List<TsKvProto> data) {
return mapToArguments(entityId, ctx.getLinkedAndDynamicArgs(entityId), ctx.getRelatedEntityArguments(), data);
}
private Map<String, ArgumentEntry> mapToArguments(EntityId originator, Map<ReferencedEntityKey, String> argNames, Map<ReferencedEntityKey, String> aggArgNames, List<TsKvProto> data) {
private Map<String, ArgumentEntry> mapToArguments(EntityId originator, Map<ReferencedEntityKey, String> argNames, Map<ReferencedEntityKey, String> relatedEntityArgs, List<TsKvProto> data) {
Map<String, ArgumentEntry> arguments = new HashMap<>();
if (!aggArgNames.isEmpty()) {
if (!relatedEntityArgs.isEmpty()) {
for (TsKvProto item : data) {
ReferencedEntityKey key = new ReferencedEntityKey(item.getKv().getKey(), ArgumentType.TS_LATEST, null);
String argName = aggArgNames.get(key);
String argName = relatedEntityArgs.get(key);
if (argName != null) {
arguments.put(argName, new AggSingleEntityArgumentEntry(originator, item));
}
@ -587,17 +587,17 @@ public class CalculatedFieldEntityMessageProcessor extends AbstractContextAwareM
}
private Map<String, ArgumentEntry> mapToArguments(CalculatedFieldCtx ctx, AttributeScopeProto scope, List<AttributeValueProto> attrDataList) {
return mapToArguments(entityId, ctx.getMainEntityArguments(), ctx.getMainEntityGeofencingArgumentNames(), ctx.getRelatedEntityArguments(), scope, attrDataList);
return mapToArguments(entityId, ctx.getMainEntityArguments(), ctx.getMainEntityGeofencingArgumentNames(), Collections.emptyMap(), scope, attrDataList);
}
private Map<String, ArgumentEntry> mapToArguments(CalculatedFieldCtx ctx, EntityId entityId, AttributeScopeProto scope, List<AttributeValueProto> attrDataList) {
var argNames = ctx.getLinkedAndDynamicArgs(entityId);
List<String> geofencingArgumentNames = ctx.getLinkedEntityAndCurrentOwnerGeofencingArgumentNames();
Map<ReferencedEntityKey, String> aggregationInputs = ctx.getRelatedEntityArguments();
return mapToArguments(entityId, argNames, geofencingArgumentNames, aggregationInputs, scope, attrDataList);
Map<ReferencedEntityKey, String> relatedEntityArgs = ctx.getRelatedEntityArguments();
return mapToArguments(entityId, argNames, geofencingArgumentNames, relatedEntityArgs, scope, attrDataList);
}
private Map<String, ArgumentEntry> mapToArguments(EntityId entityId, Map<ReferencedEntityKey, String> argNames, List<String> geofencingArgNames, Map<ReferencedEntityKey, String> aggArgNames, AttributeScopeProto scope, List<AttributeValueProto> attrDataList) {
private Map<String, ArgumentEntry> mapToArguments(EntityId entityId, Map<ReferencedEntityKey, String> argNames, List<String> geofencingArgNames, Map<ReferencedEntityKey, String> relatedEntityArgs, AttributeScopeProto scope, List<AttributeValueProto> attrDataList) {
Map<String, ArgumentEntry> arguments = new HashMap<>();
if (!argNames.isEmpty()) {
for (AttributeValueProto item : attrDataList) {
@ -613,10 +613,10 @@ public class CalculatedFieldEntityMessageProcessor extends AbstractContextAwareM
arguments.put(argName, new SingleValueArgumentEntry(item));
}
}
if (!aggArgNames.isEmpty()) {
if (!relatedEntityArgs.isEmpty()) {
for (AttributeValueProto item : attrDataList) {
ReferencedEntityKey key = new ReferencedEntityKey(item.getKey(), ArgumentType.ATTRIBUTE, AttributeScope.valueOf(scope.name()));
String argName = aggArgNames.get(key);
String argName = relatedEntityArgs.get(key);
if (argName != null) {
arguments.put(argName, new AggSingleEntityArgumentEntry(entityId, item));
}
@ -627,26 +627,40 @@ public class CalculatedFieldEntityMessageProcessor extends AbstractContextAwareM
private Map<String, ArgumentEntry> mapToArgumentsWithDefaultValue(CalculatedFieldCtx ctx, EntityId entityId, AttributeScopeProto scope, List<String> removedAttrKeys) {
var argNames = ctx.getLinkedAndDynamicArgs(entityId);
if (argNames.isEmpty()) {
Map<ReferencedEntityKey, String> relatedEntityArguments = ctx.getRelatedEntityArguments();
if (argNames.isEmpty() && relatedEntityArguments.isEmpty()) {
return Collections.emptyMap();
}
List<String> geofencingArgumentNames = ctx.getLinkedEntityAndCurrentOwnerGeofencingArgumentNames();
List<String> relatedArgumentNames = ctx.getRelatedEntityArgumentNames();
return mapToArgumentsWithDefaultValue(entityId, argNames, ctx.getArguments(), geofencingArgumentNames, relatedArgumentNames, scope, removedAttrKeys);
return mapToArgumentsWithDefaultValue(entityId, argNames, ctx.getArguments(), geofencingArgumentNames, relatedEntityArguments, scope, removedAttrKeys);
}
private Map<String, ArgumentEntry> mapToArgumentsWithDefaultValue(CalculatedFieldCtx ctx, AttributeScopeProto scope, List<String> removedAttrKeys) {
return mapToArgumentsWithDefaultValue(null, ctx.getMainEntityArguments(), ctx.getArguments(), ctx.getMainEntityGeofencingArgumentNames(), new ArrayList<>(), scope, removedAttrKeys);
return mapToArgumentsWithDefaultValue(null, ctx.getMainEntityArguments(), ctx.getArguments(), ctx.getMainEntityGeofencingArgumentNames(), Collections.emptyMap(), scope, removedAttrKeys);
}
private Map<String, ArgumentEntry> mapToArgumentsWithDefaultValue(EntityId msgEntityId,
Map<ReferencedEntityKey, String> argNames,
Map<String, Argument> configArguments,
List<String> geofencingArgNames,
List<String> relatedEntityArgNames,
Map<ReferencedEntityKey, String> relatedEntityArgs,
AttributeScopeProto scope,
List<String> removedAttrKeys) {
Map<String, ArgumentEntry> arguments = new HashMap<>();
if (!relatedEntityArgs.isEmpty()) {
for (String removedKey : removedAttrKeys) {
ReferencedEntityKey key = new ReferencedEntityKey(removedKey, ArgumentType.ATTRIBUTE, AttributeScope.valueOf(scope.name()));
if (relatedEntityArgs.containsKey(key)) {
String argName = relatedEntityArgs.get(key);
Argument argument = configArguments.get(argName);
String defaultValue = (argument != null) ? argument.getDefaultValue() : null;
SingleValueArgumentEntry argumentEntry = StringUtils.isNotEmpty(defaultValue)
? new SingleValueArgumentEntry(System.currentTimeMillis(), new StringDataEntry(removedKey, defaultValue), null)
: new SingleValueArgumentEntry();
arguments.put(argName, new AggSingleEntityArgumentEntry(msgEntityId, argumentEntry));
}
}
}
for (String removedKey : removedAttrKeys) {
ReferencedEntityKey key = new ReferencedEntityKey(removedKey, ArgumentType.ATTRIBUTE, AttributeScope.valueOf(scope.name()));
String argName = argNames.get(key);
@ -662,12 +676,7 @@ public class CalculatedFieldEntityMessageProcessor extends AbstractContextAwareM
SingleValueArgumentEntry argumentEntry = StringUtils.isNotEmpty(defaultValue)
? new SingleValueArgumentEntry(System.currentTimeMillis(), new StringDataEntry(removedKey, defaultValue), null)
: new SingleValueArgumentEntry();
if (relatedEntityArgNames.contains(argName)) {
arguments.put(argName, new AggSingleEntityArgumentEntry(msgEntityId, argumentEntry));
continue;
}
arguments.put(argName, argumentEntry);
}
return arguments;
}

30
application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldManagerMessageProcessor.java

@ -41,9 +41,9 @@ import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageDataIterable;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.EntityRelationPathQuery;
import org.thingsboard.server.common.data.relation.EntitySearchDirection;
import org.thingsboard.server.common.data.relation.RelationPathLevel;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.common.msg.CalculatedFieldStatePartitionRestoreMsg;
import org.thingsboard.server.common.msg.cf.CalculatedFieldCacheInitMsg;
import org.thingsboard.server.common.msg.cf.CalculatedFieldEntityLifecycleMsg;
@ -520,22 +520,22 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware
List<CalculatedFieldEntityCtxId> result = new ArrayList<>();
if (cf.getCalculatedField().getConfiguration() instanceof LatestValuesAggregationCalculatedFieldConfiguration configuration) {
RelationPathLevel relation = configuration.getRelation();
switch (relation.direction()) {
case FROM -> {
List<EntityRelation> byToAndType = relationService.findByToAndType(tenantId, entityId, relation.relationType(), RelationTypeGroup.COMMON);
if (byToAndType != null && !byToAndType.isEmpty()) {
EntityRelation entityRelation = byToAndType.get(0); // only one supported
EntitySearchDirection inverseDirection = switch (relation.direction()) {
case FROM -> EntitySearchDirection.TO;
case TO -> EntitySearchDirection.FROM;
};
RelationPathLevel inverseRelation = new RelationPathLevel(inverseDirection, relation.relationType());
List<EntityRelation> byRelationPathQuery = relationService.findByRelationPathQuery(tenantId, new EntityRelationPathQuery(entityId, List.of(inverseRelation)));
if (byRelationPathQuery != null && !byRelationPathQuery.isEmpty()) {
switch (relation.direction()) {
case FROM -> {
EntityRelation entityRelation = byRelationPathQuery.get(0); // only one supported
result.add(new CalculatedFieldEntityCtxId(tenantId, cf.getCfId(), entityRelation.getFrom()));
}
}
case TO -> {
List<EntityRelation> byFromAndType = relationService.findByFromAndType(tenantId, entityId, relation.relationType(), RelationTypeGroup.COMMON);
if (byFromAndType != null && !byFromAndType.isEmpty()) {
for (EntityRelation entityRelation : byFromAndType) {
if (entityRelation.getTo().equals(cf.getEntityId())) {
result.add(new CalculatedFieldEntityCtxId(tenantId, cf.getCfId(), entityRelation.getTo()));
}
}
case TO -> {
byRelationPathQuery.stream()
.filter(entityRelation -> entityRelation.getTo().equals(cf.getEntityId()))
.forEach(entityRelation -> result.add(new CalculatedFieldEntityCtxId(tenantId, cf.getCfId(), entityRelation.getTo())));
}
}
}

75
application/src/test/java/org/thingsboard/server/cf/LatestValuesAggregationCalculatedFieldTest.java

@ -383,6 +383,44 @@ public class LatestValuesAggregationCalculatedFieldTest extends AbstractControll
});
}
@Test
public void testDeleteAttr_checkAggregationWithDefault() throws Exception {
Asset asset2 = createAsset("Asset 2", assetProfile.getId());
Device device3 = createDevice("Device 3", "1234567890333");
Device device4 = createDevice("Device 4", "1234567890444");
createEntityRelation(asset2.getId(), device3.getId(), "Contains");
createEntityRelation(asset2.getId(), device4.getId(), "Contains");
postAttributes(device3.getId(), AttributeScope.SERVER_SCOPE, "{\"occupied\":true}");
postAttributes(device4.getId(), AttributeScope.SERVER_SCOPE, "{\"occupied\":true}");
createOccupancyCFWithAttr(asset2.getId());
await().alias("create CF and perform aggregation").atMost(deduplicationInterval, TimeUnit.SECONDS)
.pollInterval(POLL_INTERVAL, TimeUnit.SECONDS)
.untilAsserted(() -> {
verifyTelemetry(asset2.getId(), Map.of(
"freeSpaces", "0",
"occupiedSpaces", "2",
"totalSpaces", "2"
));
});
doDelete("/api/plugins/telemetry/DEVICE/" + device3.getUuidId() + "/SERVER_SCOPE?keys=occupied", String.class);
doDelete("/api/plugins/telemetry/DEVICE/" + device4.getUuidId() + "/SERVER_SCOPE?keys=occupied", String.class);
await().alias("delete attribute and perform aggregation with default values").atMost(deduplicationInterval * 2, TimeUnit.SECONDS)
.pollInterval(POLL_INTERVAL, TimeUnit.SECONDS)
.untilAsserted(() -> {
verifyTelemetry(asset2.getId(), Map.of(
"freeSpaces", "2",
"occupiedSpaces", "0",
"totalSpaces", "2"
));
});
}
@Test
public void testCreateRelation_checkAggregation() throws Exception {
createOccupancyCF(asset.getId());
@ -646,6 +684,43 @@ public class LatestValuesAggregationCalculatedFieldTest extends AbstractControll
output);
}
private CalculatedField createOccupancyCFWithAttr(EntityId entityId) {
Map<String, Argument> arguments = new HashMap<>();
Argument argument = new Argument();
argument.setRefEntityKey(new ReferencedEntityKey("occupied", ArgumentType.ATTRIBUTE, AttributeScope.SERVER_SCOPE));
argument.setDefaultValue("false");
arguments.put("oc", argument);
Map<String, AggMetric> aggMetrics = new HashMap<>();
AggMetric freeSpaces = new AggMetric();
freeSpaces.setFunction(AggFunction.COUNT);
freeSpaces.setFilter("return oc == false;");
freeSpaces.setInput(new AggKeyInput("oc"));
aggMetrics.put("freeSpaces", freeSpaces);
AggMetric occupiedSpaces = new AggMetric();
occupiedSpaces.setFunction(AggFunction.COUNT);
occupiedSpaces.setFilter("return oc == true;");
occupiedSpaces.setInput(new AggKeyInput("oc"));
aggMetrics.put("occupiedSpaces", occupiedSpaces);
AggMetric totalSpaces = new AggMetric();
totalSpaces.setFunction(AggFunction.COUNT);
totalSpaces.setInput(new AggFunctionInput("return 1;"));
aggMetrics.put("totalSpaces", totalSpaces);
Output output = new Output();
output.setType(OutputType.TIME_SERIES);
output.setDecimalsByDefault(0);
return createAggCf("Occupied spaces", entityId,
new RelationPathLevel(EntitySearchDirection.FROM, "Contains"),
arguments,
aggMetrics,
output);
}
private CalculatedField createAggCf(String name,
EntityId entityId,
RelationPathLevel relation,

Loading…
Cancel
Save