Browse Source

Refactor deleteAndNotify and deleteAndNotifyInternal for attributes

pull/12297/head
ViacheslavKlimov 2 years ago
parent
commit
eb7bc8695b
  1. 45
      application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
  2. 8
      application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java
  3. 22
      application/src/main/java/org/thingsboard/server/service/device/ClaimDevicesServiceImpl.java
  4. 8
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java
  5. 2
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java
  6. 77
      application/src/main/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewService.java
  7. 18
      application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java
  8. 4
      application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java
  9. 4
      application/src/main/java/org/thingsboard/server/service/stats/DefaultRuleEngineStatisticsService.java
  10. 4
      application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java
  11. 2
      application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/BaseEntityImportService.java
  12. 2
      application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java
  13. 114
      application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java
  14. 14
      application/src/main/java/org/thingsboard/server/service/telemetry/InternalTelemetryService.java
  15. 4
      application/src/test/java/org/thingsboard/server/controller/WebsocketApiTest.java
  16. 39
      application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java
  17. 6
      common/dao-api/src/main/java/org/thingsboard/server/dao/attributes/AttributesService.java
  18. 14
      dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java
  19. 10
      dao/src/main/java/org/thingsboard/server/dao/attributes/CachedAttributesService.java
  20. 117
      rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/AttributesDeleteRequest.java
  21. 12
      rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineTelemetryService.java
  22. 10
      rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequest.java
  23. 12
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java
  24. 4
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNode.java
  25. 2
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java
  26. 19
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNode.java
  27. 2
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java
  28. 30
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNodeTest.java
  29. 12
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java
  30. 4
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeTest.java
  31. 16
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNodeTest.java
  32. 10
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java

45
application/src/main/java/org/thingsboard/server/controller/TelemetryController.java

@ -47,6 +47,7 @@ import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult; import org.springframework.web.context.request.async.DeferredResult;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.rule.engine.api.AttributesDeleteRequest;
import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.AttributesSaveRequest;
import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.rule.engine.api.TimeseriesSaveRequest;
import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.common.adaptor.JsonConverter;
@ -589,24 +590,30 @@ public class TelemetryController extends BaseController {
SecurityUser user = getCurrentUser(); SecurityUser user = getCurrentUser();
return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.WRITE_ATTRIBUTES, entityIdSrc, (result, tenantId, entityId) -> { return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.WRITE_ATTRIBUTES, entityIdSrc, (result, tenantId, entityId) -> {
tsSubService.deleteAndNotify(tenantId, entityId, scope, keys, new FutureCallback<Void>() { tsSubService.deleteAttributes(AttributesDeleteRequest.builder()
@Override .tenantId(tenantId)
public void onSuccess(@Nullable Void tmp) { .entityId(entityId)
logAttributesDeleted(user, entityId, scope, keys, null); .scope(scope)
if (entityIdSrc.getEntityType().equals(EntityType.DEVICE)) { .keys(keys)
DeviceId deviceId = new DeviceId(entityId.getId()); .callback(new FutureCallback<>() {
tbClusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onDelete( @Override
user.getTenantId(), deviceId, scope.name(), keys), null); public void onSuccess(@Nullable Void tmp) {
} logAttributesDeleted(user, entityId, scope, keys, null);
result.setResult(new ResponseEntity<>(HttpStatus.OK)); if (entityIdSrc.getEntityType().equals(EntityType.DEVICE)) {
} DeviceId deviceId = new DeviceId(entityId.getId());
tbClusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onDelete(
user.getTenantId(), deviceId, scope.name(), keys), null);
}
result.setResult(new ResponseEntity<>(HttpStatus.OK));
}
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable t) {
logAttributesDeleted(user, entityId, scope, keys, t); logAttributesDeleted(user, entityId, scope, keys, t);
result.setResult(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR)); result.setResult(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
} }
}); })
.build());
}); });
} }
@ -626,7 +633,7 @@ public class TelemetryController extends BaseController {
} }
SecurityUser user = getCurrentUser(); SecurityUser user = getCurrentUser();
return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.WRITE_ATTRIBUTES, entityIdSrc, (result, tenantId, entityId) -> { return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.WRITE_ATTRIBUTES, entityIdSrc, (result, tenantId, entityId) -> {
tsSubService.save(AttributesSaveRequest.builder() tsSubService.saveAttributes(AttributesSaveRequest.builder()
.tenantId(tenantId) .tenantId(tenantId)
.entityId(entityId) .entityId(entityId)
.scope(scope) .scope(scope)
@ -680,7 +687,7 @@ public class TelemetryController extends BaseController {
TenantProfile tenantProfile = tenantProfileCache.get(tenantId); TenantProfile tenantProfile = tenantProfileCache.get(tenantId);
tenantTtl = TimeUnit.DAYS.toSeconds(((DefaultTenantProfileConfiguration) tenantProfile.getProfileData().getConfiguration()).getDefaultStorageTtlDays()); tenantTtl = TimeUnit.DAYS.toSeconds(((DefaultTenantProfileConfiguration) tenantProfile.getProfileData().getConfiguration()).getDefaultStorageTtlDays());
} }
tsSubService.save(TimeseriesSaveRequest.builder() tsSubService.saveTimeseries(TimeseriesSaveRequest.builder()
.tenantId(tenantId) .tenantId(tenantId)
.customerId(user.getCustomerId()) .customerId(user.getCustomerId())
.entityId(entityId) .entityId(entityId)

8
application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java

@ -215,7 +215,7 @@ public class DefaultTbApiUsageStateService extends AbstractPartitionBasedService
updateLock.unlock(); updateLock.unlock();
} }
log.trace("[{}][{}] Saving new stats: {}", tenantId, ownerId, updatedEntries); log.trace("[{}][{}] Saving new stats: {}", tenantId, ownerId, updatedEntries);
tsWsService.saveInternal(TimeseriesSaveRequest.builder() tsWsService.saveTimeseriesInternal(TimeseriesSaveRequest.builder()
.tenantId(tenantId) .tenantId(tenantId)
.entityId(usageState.getApiUsageState().getId()) .entityId(usageState.getApiUsageState().getId())
.entries(updatedEntries) .entries(updatedEntries)
@ -327,7 +327,7 @@ public class DefaultTbApiUsageStateService extends AbstractPartitionBasedService
} }
} }
if (!profileThresholds.isEmpty()) { if (!profileThresholds.isEmpty()) {
tsWsService.saveInternal(TimeseriesSaveRequest.builder() tsWsService.saveTimeseriesInternal(TimeseriesSaveRequest.builder()
.tenantId(tenantId) .tenantId(tenantId)
.entityId(id) .entityId(id)
.entries(profileThresholds) .entries(profileThresholds)
@ -359,7 +359,7 @@ public class DefaultTbApiUsageStateService extends AbstractPartitionBasedService
long ts = System.currentTimeMillis(); long ts = System.currentTimeMillis();
List<TsKvEntry> stateTelemetry = new ArrayList<>(); List<TsKvEntry> stateTelemetry = new ArrayList<>();
result.forEach((apiFeature, aState) -> stateTelemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(apiFeature.getApiStateKey(), aState.name())))); result.forEach((apiFeature, aState) -> stateTelemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(apiFeature.getApiStateKey(), aState.name()))));
tsWsService.saveInternal(TimeseriesSaveRequest.builder() tsWsService.saveTimeseriesInternal(TimeseriesSaveRequest.builder()
.tenantId(state.getTenantId()) .tenantId(state.getTenantId())
.entityId(state.getApiUsageState().getId()) .entityId(state.getApiUsageState().getId())
.entries(stateTelemetry) .entries(stateTelemetry)
@ -452,7 +452,7 @@ public class DefaultTbApiUsageStateService extends AbstractPartitionBasedService
.map(key -> new BasicTsKvEntry(state.getCurrentCycleTs(), new LongDataEntry(key.getApiCountKey(), 0L))) .map(key -> new BasicTsKvEntry(state.getCurrentCycleTs(), new LongDataEntry(key.getApiCountKey(), 0L)))
.collect(Collectors.toList()); .collect(Collectors.toList());
tsWsService.saveInternal(TimeseriesSaveRequest.builder() tsWsService.saveTimeseriesInternal(TimeseriesSaveRequest.builder()
.tenantId(state.getTenantId()) .tenantId(state.getTenantId())
.entityId(state.getApiUsageState().getId()) .entityId(state.getApiUsageState().getId())
.entries(counts) .entries(counts)

22
application/src/main/java/org/thingsboard/server/service/device/ClaimDevicesServiceImpl.java

@ -28,6 +28,7 @@ import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager; import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.api.AttributesDeleteRequest;
import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.AttributesSaveRequest;
import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;
import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.AttributeScope;
@ -178,7 +179,7 @@ public class ClaimDevicesServiceImpl implements ClaimDevicesService {
return Futures.immediateFuture(new ReclaimResult(unassignedCustomer)); return Futures.immediateFuture(new ReclaimResult(unassignedCustomer));
} }
SettableFuture<ReclaimResult> result = SettableFuture.create(); SettableFuture<ReclaimResult> result = SettableFuture.create();
telemetryService.save(AttributesSaveRequest.builder() telemetryService.saveAttributes(AttributesSaveRequest.builder()
.tenantId(tenantId) .tenantId(tenantId)
.entityId(savedDevice.getId()) .entityId(savedDevice.getId())
.scope(AttributeScope.SERVER_SCOPE) .scope(AttributeScope.SERVER_SCOPE)
@ -223,18 +224,13 @@ public class ClaimDevicesServiceImpl implements ClaimDevicesService {
cache.evict(data.getKey()); cache.evict(data.getKey());
} }
SettableFuture<Void> result = SettableFuture.create(); SettableFuture<Void> result = SettableFuture.create();
telemetryService.deleteAndNotify(device.getTenantId(), telemetryService.deleteAttributes(AttributesDeleteRequest.builder()
device.getId(), AttributeScope.SERVER_SCOPE, Arrays.asList(CLAIM_ATTRIBUTE_NAME, CLAIM_DATA_ATTRIBUTE_NAME), new FutureCallback<>() { .tenantId(device.getTenantId())
@Override .entityId(device.getId())
public void onSuccess(@Nullable Void tmp) { .scope(AttributeScope.SERVER_SCOPE)
result.set(tmp); .keys(Arrays.asList(CLAIM_ATTRIBUTE_NAME, CLAIM_DATA_ATTRIBUTE_NAME))
} .future(result)
.build());
@Override
public void onFailure(Throwable t) {
result.setException(t);
}
});
return result; return result;
} }

8
application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java

@ -503,14 +503,14 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
private void save(TenantId tenantId, EdgeId edgeId, String key, long value) { private void save(TenantId tenantId, EdgeId edgeId, String key, long value) {
log.debug("[{}][{}] Updating long edge telemetry [{}] [{}]", tenantId, edgeId, key, value); log.debug("[{}][{}] Updating long edge telemetry [{}] [{}]", tenantId, edgeId, key, value);
if (persistToTelemetry) { if (persistToTelemetry) {
tsSubService.save(TimeseriesSaveRequest.builder() tsSubService.saveTimeseries(TimeseriesSaveRequest.builder()
.tenantId(tenantId) .tenantId(tenantId)
.entityId(edgeId) .entityId(edgeId)
.entry(new LongDataEntry(key, value)) .entry(new LongDataEntry(key, value))
.callback(new AttributeSaveCallback(tenantId, edgeId, key, value)) .callback(new AttributeSaveCallback(tenantId, edgeId, key, value))
.build()); .build());
} else { } else {
tsSubService.save(AttributesSaveRequest.builder() tsSubService.saveAttributes(AttributesSaveRequest.builder()
.tenantId(tenantId) .tenantId(tenantId)
.entityId(edgeId) .entityId(edgeId)
.scope(AttributeScope.SERVER_SCOPE) .scope(AttributeScope.SERVER_SCOPE)
@ -523,14 +523,14 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
private void save(TenantId tenantId, EdgeId edgeId, String key, boolean value) { private void save(TenantId tenantId, EdgeId edgeId, String key, boolean value) {
log.debug("[{}][{}] Updating boolean edge telemetry [{}] [{}]", tenantId, edgeId, key, value); log.debug("[{}][{}] Updating boolean edge telemetry [{}] [{}]", tenantId, edgeId, key, value);
if (persistToTelemetry) { if (persistToTelemetry) {
tsSubService.save(TimeseriesSaveRequest.builder() tsSubService.saveTimeseries(TimeseriesSaveRequest.builder()
.tenantId(tenantId) .tenantId(tenantId)
.entityId(edgeId) .entityId(edgeId)
.entry(new BooleanDataEntry(key, value)) .entry(new BooleanDataEntry(key, value))
.callback(new AttributeSaveCallback(tenantId, edgeId, key, value)) .callback(new AttributeSaveCallback(tenantId, edgeId, key, value))
.build()); .build());
} else { } else {
tsSubService.save(AttributesSaveRequest.builder() tsSubService.saveAttributes(AttributesSaveRequest.builder()
.tenantId(tenantId) .tenantId(tenantId)
.entityId(edgeId) .entityId(edgeId)
.scope(AttributeScope.SERVER_SCOPE) .scope(AttributeScope.SERVER_SCOPE)

2
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java

@ -278,7 +278,7 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor {
JsonObject json = JsonUtils.getJsonObject(msg.getKvList()); JsonObject json = JsonUtils.getJsonObject(msg.getKvList());
List<AttributeKvEntry> attributes = new ArrayList<>(JsonConverter.convertToAttributes(json)); List<AttributeKvEntry> attributes = new ArrayList<>(JsonConverter.convertToAttributes(json));
String scope = metaData.getValue("scope"); String scope = metaData.getValue("scope");
tsSubService.save(AttributesSaveRequest.builder() tsSubService.saveAttributes(AttributesSaveRequest.builder()
.tenantId(tenantId) .tenantId(tenantId)
.entityId(entityId) .entityId(entityId)
.scope(AttributeScope.valueOf(scope)) .scope(AttributeScope.valueOf(scope))

77
application/src/main/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewService.java

@ -25,7 +25,9 @@ import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.ConcurrentReferenceHashMap; import org.springframework.util.ConcurrentReferenceHashMap;
import org.thingsboard.rule.engine.api.AttributesDeleteRequest;
import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.AttributesSaveRequest;
import org.thingsboard.rule.engine.api.TimeseriesSaveRequest;
import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
@ -284,7 +286,7 @@ public class DefaultTbEntityViewService extends AbstractTbEntityService implemen
(startTime == 0 && endTime > lastUpdateTs) || (startTime == 0 && endTime > lastUpdateTs) ||
(startTime < lastUpdateTs && endTime > lastUpdateTs); (startTime < lastUpdateTs && endTime > lastUpdateTs);
}).collect(Collectors.toList()); }).collect(Collectors.toList());
tsSubService.save(AttributesSaveRequest.builder() tsSubService.saveAttributes(AttributesSaveRequest.builder()
.tenantId(entityView.getTenantId()) .tenantId(entityView.getTenantId())
.entityId(entityId) .entityId(entityId)
.scope(scope) .scope(scope)
@ -340,15 +342,22 @@ public class DefaultTbEntityViewService extends AbstractTbEntityService implemen
}, MoreExecutors.directExecutor()); }, MoreExecutors.directExecutor());
return Futures.transform(latestFuture, latestValues -> { return Futures.transform(latestFuture, latestValues -> {
if (latestValues != null && !latestValues.isEmpty()) { if (latestValues != null && !latestValues.isEmpty()) {
tsSubService.saveLatestAndNotify(entityView.getTenantId(), entityId, latestValues, new FutureCallback<Void>() { tsSubService.saveTimeseries(TimeseriesSaveRequest.builder()
@Override .tenantId(entityView.getTenantId())
public void onSuccess(@Nullable Void tmp) { .entityId(entityId)
} .entries(latestValues)
.onlyLatest(true)
@Override .callback(new FutureCallback<Void>() {
public void onFailure(Throwable t) { @Override
} public void onSuccess(@Nullable Void tmp) {
}); }
@Override
public void onFailure(Throwable t) {
log.error("[{}][{}] Failed to save entity view latest timeseries: {}", tenantId, entityView.getId(), latestValues, t);
}
})
.build());
} }
return null; return null;
}, MoreExecutors.directExecutor()); }, MoreExecutors.directExecutor());
@ -358,27 +367,33 @@ public class DefaultTbEntityViewService extends AbstractTbEntityService implemen
EntityViewId entityId = entityView.getId(); EntityViewId entityId = entityView.getId();
SettableFuture<Void> resultFuture = SettableFuture.create(); SettableFuture<Void> resultFuture = SettableFuture.create();
if (keys != null && !keys.isEmpty()) { if (keys != null && !keys.isEmpty()) {
tsSubService.deleteAndNotify(entityView.getTenantId(), entityId, scope, keys, new FutureCallback<Void>() { tsSubService.deleteAttributes(AttributesDeleteRequest.builder()
@Override .tenantId(entityView.getTenantId())
public void onSuccess(@Nullable Void tmp) { .entityId(entityId)
try { .scope(scope)
logAttributesDeleted(entityView.getTenantId(), user, entityId, scope, keys, null); .keys(keys)
} catch (ThingsboardException e) { .callback(new FutureCallback<>() {
log.error("Failed to log attribute delete", e); @Override
} public void onSuccess(@Nullable Void tmp) {
resultFuture.set(tmp); try {
} logAttributesDeleted(entityView.getTenantId(), user, entityId, scope, keys, null);
} catch (ThingsboardException e) {
@Override log.error("Failed to log attribute delete", e);
public void onFailure(Throwable t) { }
try { resultFuture.set(tmp);
logAttributesDeleted(entityView.getTenantId(), user, entityId, scope, keys, t); }
} catch (ThingsboardException e) {
log.error("Failed to log attribute delete", e); @Override
} public void onFailure(Throwable t) {
resultFuture.setException(t); try {
} logAttributesDeleted(entityView.getTenantId(), user, entityId, scope, keys, t);
}); } catch (ThingsboardException e) {
log.error("Failed to log attribute delete", e);
}
resultFuture.setException(t);
}
})
.build());
} else { } else {
resultFuture.set(null); resultFuture.set(null);
} }

18
application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java

@ -20,6 +20,7 @@ import jakarta.annotation.Nullable;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.rule.engine.api.AttributesDeleteRequest;
import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.AttributesSaveRequest;
import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;
import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.rule.engine.api.TimeseriesSaveRequest;
@ -262,7 +263,7 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService {
telemetry.add(new BasicTsKvEntry(ts, new LongDataEntry(getTargetTelemetryKey(firmware.getType(), TS), ts))); telemetry.add(new BasicTsKvEntry(ts, new LongDataEntry(getTargetTelemetryKey(firmware.getType(), TS), ts)));
telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTelemetryKey(firmware.getType(), STATE), OtaPackageUpdateStatus.QUEUED.name()))); telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTelemetryKey(firmware.getType(), STATE), OtaPackageUpdateStatus.QUEUED.name())));
telemetryService.save(TimeseriesSaveRequest.builder() telemetryService.saveTimeseries(TimeseriesSaveRequest.builder()
.tenantId(tenantId) .tenantId(tenantId)
.entityId(deviceId) .entityId(deviceId)
.entries(telemetry) .entries(telemetry)
@ -288,7 +289,7 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService {
BasicTsKvEntry status = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(getTelemetryKey(otaPackageType, STATE), OtaPackageUpdateStatus.INITIATED.name())); BasicTsKvEntry status = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(getTelemetryKey(otaPackageType, STATE), OtaPackageUpdateStatus.INITIATED.name()));
telemetryService.save(TimeseriesSaveRequest.builder() telemetryService.saveTimeseries(TimeseriesSaveRequest.builder()
.tenantId(tenantId) .tenantId(tenantId)
.entityId(deviceId) .entityId(deviceId)
.entry(status) .entry(status)
@ -347,7 +348,7 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService {
remove(device, otaPackageType, attrToRemove); remove(device, otaPackageType, attrToRemove);
telemetryService.save(AttributesSaveRequest.builder() telemetryService.saveAttributes(AttributesSaveRequest.builder()
.tenantId(tenantId) .tenantId(tenantId)
.entityId(deviceId) .entityId(deviceId)
.scope(AttributeScope.SHARED_SCOPE) .scope(AttributeScope.SHARED_SCOPE)
@ -371,8 +372,12 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService {
} }
private void remove(Device device, OtaPackageType otaPackageType, List<String> attributesKeys) { private void remove(Device device, OtaPackageType otaPackageType, List<String> attributesKeys) {
telemetryService.deleteAndNotify(device.getTenantId(), device.getId(), AttributeScope.SHARED_SCOPE, attributesKeys, telemetryService.deleteAttributes(AttributesDeleteRequest.builder()
new FutureCallback<>() { .tenantId(device.getTenantId())
.entityId(device.getId())
.scope(AttributeScope.SHARED_SCOPE)
.keys(attributesKeys)
.callback(new FutureCallback<>() {
@Override @Override
public void onSuccess(@Nullable Void tmp) { public void onSuccess(@Nullable Void tmp) {
log.trace("[{}] Success remove target {} attributes!", device.getId(), otaPackageType); log.trace("[{}] Success remove target {} attributes!", device.getId(), otaPackageType);
@ -383,7 +388,8 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService {
public void onFailure(Throwable t) { public void onFailure(Throwable t) {
log.error("[{}] Failed to remove target {} attributes!", device.getId(), otaPackageType, t); log.error("[{}] Failed to remove target {} attributes!", device.getId(), otaPackageType, t);
} }
}); })
.build());
} }
} }

4
application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java

@ -877,7 +877,7 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService<Dev
private void save(DeviceId deviceId, KvEntry kvEntry, long ts) { private void save(DeviceId deviceId, KvEntry kvEntry, long ts) {
if (persistToTelemetry) { if (persistToTelemetry) {
tsSubService.saveInternal(TimeseriesSaveRequest.builder() tsSubService.saveTimeseriesInternal(TimeseriesSaveRequest.builder()
.tenantId(TenantId.SYS_TENANT_ID) .tenantId(TenantId.SYS_TENANT_ID)
.entityId(deviceId) .entityId(deviceId)
.entry(new BasicTsKvEntry(ts, kvEntry)) .entry(new BasicTsKvEntry(ts, kvEntry))
@ -885,7 +885,7 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService<Dev
.callback(new TelemetrySaveCallback<>(deviceId, kvEntry)) .callback(new TelemetrySaveCallback<>(deviceId, kvEntry))
.build()); .build());
} else { } else {
tsSubService.save(AttributesSaveRequest.builder() tsSubService.saveAttributes(AttributesSaveRequest.builder()
.tenantId(TenantId.SYS_TENANT_ID) .tenantId(TenantId.SYS_TENANT_ID)
.entityId(deviceId) .entityId(deviceId)
.scope(AttributeScope.SERVER_SCOPE) .scope(AttributeScope.SERVER_SCOPE)

4
application/src/main/java/org/thingsboard/server/service/stats/DefaultRuleEngineStatisticsService.java

@ -89,7 +89,7 @@ public class DefaultRuleEngineStatisticsService implements RuleEngineStatisticsS
if (!tsList.isEmpty()) { if (!tsList.isEmpty()) {
long ttl = apiLimitService.getLimit(tenantId, DefaultTenantProfileConfiguration::getQueueStatsTtlDays); long ttl = apiLimitService.getLimit(tenantId, DefaultTenantProfileConfiguration::getQueueStatsTtlDays);
ttl = TimeUnit.DAYS.toSeconds(ttl); ttl = TimeUnit.DAYS.toSeconds(ttl);
tsService.saveInternal(TimeseriesSaveRequest.builder() tsService.saveTimeseriesInternal(TimeseriesSaveRequest.builder()
.tenantId(tenantId) .tenantId(tenantId)
.entityId(queueStatsId) .entityId(queueStatsId)
.entries(tsList) .entries(tsList)
@ -109,7 +109,7 @@ public class DefaultRuleEngineStatisticsService implements RuleEngineStatisticsS
TsKvEntry tsKv = new BasicTsKvEntry(e.getTs(), new JsonDataEntry(RULE_ENGINE_EXCEPTION, e.toJsonString(maxErrorMessageLength))); TsKvEntry tsKv = new BasicTsKvEntry(e.getTs(), new JsonDataEntry(RULE_ENGINE_EXCEPTION, e.toJsonString(maxErrorMessageLength)));
long ttl = apiLimitService.getLimit(tenantId, DefaultTenantProfileConfiguration::getRuleEngineExceptionsTtlDays); long ttl = apiLimitService.getLimit(tenantId, DefaultTenantProfileConfiguration::getRuleEngineExceptionsTtlDays);
ttl = TimeUnit.DAYS.toSeconds(ttl); ttl = TimeUnit.DAYS.toSeconds(ttl);
tsService.saveInternal(TimeseriesSaveRequest.builder() tsService.saveTimeseriesInternal(TimeseriesSaveRequest.builder()
.tenantId(tenantId) .tenantId(tenantId)
.entityId(getQueueStatsId(tenantId, queueName)) .entityId(getQueueStatsId(tenantId, queueName))
.entry(tsKv) .entry(tsKv)

4
application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java

@ -208,7 +208,7 @@ public abstract class AbstractBulkImportService<E extends HasId<? extends Entity
accessValidator.validateEntityAndCallback(user, Operation.WRITE_TELEMETRY, entity.getId(), (result, tenantId, entityId) -> { accessValidator.validateEntityAndCallback(user, Operation.WRITE_TELEMETRY, entity.getId(), (result, tenantId, entityId) -> {
TenantProfile tenantProfile = tenantProfileCache.get(tenantId); TenantProfile tenantProfile = tenantProfileCache.get(tenantId);
long tenantTtl = TimeUnit.DAYS.toSeconds(((DefaultTenantProfileConfiguration) tenantProfile.getProfileData().getConfiguration()).getDefaultStorageTtlDays()); long tenantTtl = TimeUnit.DAYS.toSeconds(((DefaultTenantProfileConfiguration) tenantProfile.getProfileData().getConfiguration()).getDefaultStorageTtlDays());
tsSubscriptionService.save(TimeseriesSaveRequest.builder() tsSubscriptionService.saveTimeseries(TimeseriesSaveRequest.builder()
.tenantId(tenantId) .tenantId(tenantId)
.customerId(user.getCustomerId()) .customerId(user.getCustomerId())
.entityId(entityId) .entityId(entityId)
@ -238,7 +238,7 @@ public abstract class AbstractBulkImportService<E extends HasId<? extends Entity
List<AttributeKvEntry> attributes = new ArrayList<>(JsonConverter.convertToAttributes(kvsEntry.getValue())); List<AttributeKvEntry> attributes = new ArrayList<>(JsonConverter.convertToAttributes(kvsEntry.getValue()));
accessValidator.validateEntityAndCallback(user, Operation.WRITE_ATTRIBUTES, entity.getId(), (result, tenantId, entityId) -> { accessValidator.validateEntityAndCallback(user, Operation.WRITE_ATTRIBUTES, entity.getId(), (result, tenantId, entityId) -> {
tsSubscriptionService.save(AttributesSaveRequest.builder() tsSubscriptionService.saveAttributes(AttributesSaveRequest.builder()
.tenantId(tenantId) .tenantId(tenantId)
.entityId(entityId) .entityId(entityId)
.scope(AttributeScope.valueOf(scope)) .scope(AttributeScope.valueOf(scope))

2
application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/BaseEntityImportService.java

@ -258,7 +258,7 @@ public abstract class BaseEntityImportService<I extends EntityId, E extends Expo
}) })
.collect(Collectors.toList()); .collect(Collectors.toList());
// fixme: attributes are saved outside the transaction // fixme: attributes are saved outside the transaction
tsSubService.save(AttributesSaveRequest.builder() tsSubService.saveAttributes(AttributesSaveRequest.builder()
.tenantId(user.getTenantId()) .tenantId(user.getTenantId())
.entityId(entity.getId()) .entityId(entity.getId())
.scope(scope) .scope(scope)

2
application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java

@ -201,7 +201,7 @@ public class DefaultSystemInfoService extends TbApplicationEventListener<Partiti
private void doSave(List<TsKvEntry> telemetry) { private void doSave(List<TsKvEntry> telemetry) {
ApiUsageState apiUsageState = apiUsageStateClient.getApiUsageState(TenantId.SYS_TENANT_ID); ApiUsageState apiUsageState = apiUsageStateClient.getApiUsageState(TenantId.SYS_TENANT_ID);
telemetryService.saveInternal(TimeseriesSaveRequest.builder() telemetryService.saveTimeseriesInternal(TimeseriesSaveRequest.builder()
.tenantId(TenantId.SYS_TENANT_ID) .tenantId(TenantId.SYS_TENANT_ID)
.entityId(apiUsageState.getId()) .entityId(apiUsageState.getId())
.entries(telemetry) .entries(telemetry)

114
application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java

@ -27,11 +27,11 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.rule.engine.api.AttributesDeleteRequest;
import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.AttributesSaveRequest;
import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;
import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.rule.engine.api.TimeseriesSaveRequest;
import org.thingsboard.server.common.data.ApiUsageRecordKey; import org.thingsboard.server.common.data.ApiUsageRecordKey;
import org.thingsboard.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
@ -111,27 +111,31 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer
} }
@Override @Override
public void save(TimeseriesSaveRequest request) { public void saveTimeseries(TimeseriesSaveRequest request) {
TenantId tenantId = request.getTenantId(); TenantId tenantId = request.getTenantId();
EntityId entityId = request.getEntityId(); EntityId entityId = request.getEntityId();
checkInternalEntity(entityId); checkInternalEntity(entityId);
boolean sysTenant = TenantId.SYS_TENANT_ID.equals(tenantId) || tenantId == null; boolean sysTenant = TenantId.SYS_TENANT_ID.equals(tenantId) || tenantId == null;
if (sysTenant || apiUsageStateService.getApiUsageState(tenantId).isDbStorageEnabled()) { if (sysTenant || request.isOnlyLatest() || apiUsageStateService.getApiUsageState(tenantId).isDbStorageEnabled()) {
KvUtils.validate(request.getEntries(), valueNoXssValidation); KvUtils.validate(request.getEntries(), valueNoXssValidation);
FutureCallback<Integer> callback = getApiUsageCallback(tenantId, request.getCustomerId(), sysTenant, request.getCallback()); ListenableFuture<Integer> future = saveTimeseriesInternal(request);
ListenableFuture<Integer> future = saveInternal(request); if (!request.isOnlyLatest()) {
Futures.addCallback(future, callback, tsCallBackExecutor); FutureCallback<Integer> callback = getApiUsageCallback(tenantId, request.getCustomerId(), sysTenant, request.getCallback());
Futures.addCallback(future, callback, tsCallBackExecutor);
}
} else { } else {
request.getCallback().onFailure(new RuntimeException("DB storage writes are disabled due to API limits!")); request.getCallback().onFailure(new RuntimeException("DB storage writes are disabled due to API limits!"));
} }
} }
@Override @Override
public ListenableFuture<Integer> saveInternal(TimeseriesSaveRequest request) { public ListenableFuture<Integer> saveTimeseriesInternal(TimeseriesSaveRequest request) {
TenantId tenantId = request.getTenantId(); TenantId tenantId = request.getTenantId();
EntityId entityId = request.getEntityId(); EntityId entityId = request.getEntityId();
ListenableFuture<Integer> saveFuture; ListenableFuture<Integer> saveFuture;
if (request.isSaveLatest()) { if (request.isOnlyLatest()) {
saveFuture = Futures.transform(tsService.saveLatest(tenantId, entityId, request.getEntries()), result -> 0, MoreExecutors.directExecutor());
} else if (request.isSaveLatest()) {
saveFuture = tsService.save(tenantId, entityId, request.getEntries(), request.getTtl()); saveFuture = tsService.save(tenantId, entityId, request.getEntries(), request.getTtl());
} else { } else {
saveFuture = tsService.saveWithoutLatest(tenantId, entityId, request.getEntries(), request.getTtl()); saveFuture = tsService.saveWithoutLatest(tenantId, entityId, request.getEntries(), request.getTtl());
@ -139,63 +143,37 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer
addMainCallback(saveFuture, request.getCallback()); addMainCallback(saveFuture, request.getCallback());
addWsCallback(saveFuture, success -> onTimeSeriesUpdate(tenantId, entityId, request.getEntries())); addWsCallback(saveFuture, success -> onTimeSeriesUpdate(tenantId, entityId, request.getEntries()));
if (request.isSaveLatest()) { if (request.isSaveLatest() && !request.isOnlyLatest()) {
addEntityViewCallback(tenantId, entityId, request.getEntries()); addEntityViewCallback(tenantId, entityId, request.getEntries());
} }
return saveFuture; return saveFuture;
} }
@Override @Override
public void save(AttributesSaveRequest request) { public void saveAttributes(AttributesSaveRequest request) {
checkInternalEntity(request.getEntityId()); checkInternalEntity(request.getEntityId());
saveInternal(request); saveAttributesInternal(request);
} }
@Override @Override
public void saveInternal(AttributesSaveRequest request) { public void saveAttributesInternal(AttributesSaveRequest request) {
log.trace("Executing saveInternal [{}]", request); log.trace("Executing saveInternal [{}]", request);
ListenableFuture<List<Long>> saveFuture = attrService.save(request.getTenantId(), request.getEntityId(), request.getScope(), request.getEntries()); ListenableFuture<List<Long>> saveFuture = attrService.save(request.getTenantId(), request.getEntityId(), request.getScope(), request.getEntries());
addVoidCallback(saveFuture, request.getCallback()); addMainCallback(saveFuture, request.getCallback());
addWsCallback(saveFuture, success -> onAttributesUpdate(request.getTenantId(), request.getEntityId(), request.getScope().name(), request.getEntries(), request.isNotifyDevice())); addWsCallback(saveFuture, success -> onAttributesUpdate(request.getTenantId(), request.getEntityId(), request.getScope().name(), request.getEntries(), request.isNotifyDevice()));
} }
@Override @Override
public void saveLatestAndNotify(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts, FutureCallback<Void> callback) { public void deleteAttributes(AttributesDeleteRequest request) {
checkInternalEntity(entityId); checkInternalEntity(request.getEntityId());
saveLatestAndNotifyInternal(tenantId, entityId, ts, callback); deleteAttributesInternal(request);
}
@Override
public void saveLatestAndNotifyInternal(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts, FutureCallback<Void> callback) {
ListenableFuture<List<Long>> saveFuture = tsService.saveLatest(tenantId, entityId, ts);
addVoidCallback(saveFuture, callback);
addWsCallback(saveFuture, success -> onTimeSeriesUpdate(tenantId, entityId, ts));
}
@Override
public void deleteAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, List<String> keys, FutureCallback<Void> callback) {
checkInternalEntity(entityId);
deleteAndNotifyInternal(tenantId, entityId, scope, keys, false, callback);
}
@Override
public void deleteAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, List<String> keys, boolean notifyDevice, FutureCallback<Void> callback) {
checkInternalEntity(entityId);
deleteAndNotifyInternal(tenantId, entityId, scope, keys, notifyDevice, callback);
}
@Override
public void deleteAndNotifyInternal(TenantId tenantId, EntityId entityId, String scope, List<String> keys, boolean notifyDevice, FutureCallback<Void> callback) {
ListenableFuture<List<String>> deleteFuture = attrService.removeAll(tenantId, entityId, scope, keys);
addVoidCallback(deleteFuture, callback);
addWsCallback(deleteFuture, success -> onAttributesDelete(tenantId, entityId, scope, keys, notifyDevice));
} }
@Override @Override
public void deleteAndNotifyInternal(TenantId tenantId, EntityId entityId, AttributeScope scope, List<String> keys, boolean notifyDevice, FutureCallback<Void> callback) { public void deleteAttributesInternal(AttributesDeleteRequest request) {
ListenableFuture<List<String>> deleteFuture = attrService.removeAll(tenantId, entityId, scope, keys); ListenableFuture<List<String>> deleteFuture = attrService.removeAll(request.getTenantId(), request.getEntityId(), request.getScope(), request.getKeys());
addVoidCallback(deleteFuture, callback); addMainCallback(deleteFuture, request.getCallback());
addWsCallback(deleteFuture, success -> onAttributesDelete(tenantId, entityId, scope.name(), keys, notifyDevice)); addWsCallback(deleteFuture, success -> onAttributesDelete(request.getTenantId(), request.getEntityId(), request.getScope().name(), request.getKeys(), request.isNotifyDevice()));
} }
@Override @Override
@ -207,7 +185,7 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer
@Override @Override
public void deleteLatestInternal(TenantId tenantId, EntityId entityId, List<String> keys, FutureCallback<Void> callback) { public void deleteLatestInternal(TenantId tenantId, EntityId entityId, List<String> keys, FutureCallback<Void> callback) {
ListenableFuture<List<TsKvLatestRemovingResult>> deleteFuture = tsService.removeLatest(tenantId, entityId, keys); ListenableFuture<List<TsKvLatestRemovingResult>> deleteFuture = tsService.removeLatest(tenantId, entityId, keys);
addVoidCallback(deleteFuture, callback); addMainCallback(deleteFuture, callback);
} }
@Override @Override
@ -229,7 +207,7 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer
@Override @Override
public void deleteTimeseriesAndNotify(TenantId tenantId, EntityId entityId, List<String> keys, List<DeleteTsKvQuery> deleteTsKvQueries, FutureCallback<Void> callback) { public void deleteTimeseriesAndNotify(TenantId tenantId, EntityId entityId, List<String> keys, List<DeleteTsKvQuery> deleteTsKvQueries, FutureCallback<Void> callback) {
ListenableFuture<List<TsKvLatestRemovingResult>> deleteFuture = tsService.remove(tenantId, entityId, deleteTsKvQueries); ListenableFuture<List<TsKvLatestRemovingResult>> deleteFuture = tsService.remove(tenantId, entityId, deleteTsKvQueries);
addVoidCallback(deleteFuture, callback); addMainCallback(deleteFuture, callback);
addWsCallback(deleteFuture, list -> onTimeSeriesDelete(tenantId, entityId, keys, list)); addWsCallback(deleteFuture, list -> onTimeSeriesDelete(tenantId, entityId, keys, list));
} }
@ -260,15 +238,21 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer
} }
} }
if (!entityViewLatest.isEmpty()) { if (!entityViewLatest.isEmpty()) {
saveLatestAndNotify(tenantId, entityView.getId(), entityViewLatest, new FutureCallback<>() { saveTimeseries(TimeseriesSaveRequest.builder()
@Override .tenantId(tenantId)
public void onSuccess(@Nullable Void tmp) { .entityId(entityView.getId())
} .entries(entityViewLatest)
.onlyLatest(true)
@Override .callback(new FutureCallback<>() {
public void onFailure(Throwable t) { @Override
} public void onSuccess(@Nullable Void tmp) {}
});
@Override
public void onFailure(Throwable t) {
log.error("[{}][{}] Failed to save entity view latest timeseries: {}", tenantId, entityView.getId(), entityViewLatest, t);
}
})
.build());
} }
} }
} }
@ -328,22 +312,8 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer
}); });
} }
private <S> void addVoidCallback(ListenableFuture<S> saveFuture, final FutureCallback<Void> callback) {
if (callback == null) return;
Futures.addCallback(saveFuture, new FutureCallback<S>() {
@Override
public void onSuccess(@Nullable S result) {
callback.onSuccess(null);
}
@Override
public void onFailure(Throwable t) {
callback.onFailure(t);
}
}, tsCallBackExecutor);
}
private <S> void addMainCallback(ListenableFuture<S> saveFuture, final FutureCallback<Void> callback) { private <S> void addMainCallback(ListenableFuture<S> saveFuture, final FutureCallback<Void> callback) {
if (callback == null) return;
Futures.addCallback(saveFuture, new FutureCallback<S>() { Futures.addCallback(saveFuture, new FutureCallback<S>() {
@Override @Override
public void onSuccess(@Nullable S result) { public void onSuccess(@Nullable S result) {

14
application/src/main/java/org/thingsboard/server/service/telemetry/InternalTelemetryService.java

@ -17,13 +17,12 @@ package org.thingsboard.server.service.telemetry;
import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.rule.engine.api.AttributesDeleteRequest;
import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.AttributesSaveRequest;
import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;
import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.rule.engine.api.TimeseriesSaveRequest;
import org.thingsboard.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.TsKvEntry;
import java.util.List; import java.util.List;
@ -32,16 +31,11 @@ import java.util.List;
*/ */
public interface InternalTelemetryService extends RuleEngineTelemetryService { public interface InternalTelemetryService extends RuleEngineTelemetryService {
ListenableFuture<Integer> saveInternal(TimeseriesSaveRequest request); ListenableFuture<Integer> saveTimeseriesInternal(TimeseriesSaveRequest request);
void saveInternal(AttributesSaveRequest request); void saveAttributesInternal(AttributesSaveRequest request);
void saveLatestAndNotifyInternal(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts, FutureCallback<Void> callback); void deleteAttributesInternal(AttributesDeleteRequest request);
@Deprecated(since = "3.7.0")
void deleteAndNotifyInternal(TenantId tenantId, EntityId entityId, String scope, List<String> keys, boolean notifyDevice, FutureCallback<Void> callback);
void deleteAndNotifyInternal(TenantId tenantId, EntityId entityId, AttributeScope scope, List<String> keys, boolean notifyDevice, FutureCallback<Void> callback);
void deleteLatestInternal(TenantId tenantId, EntityId entityId, List<String> keys, FutureCallback<Void> callback); void deleteLatestInternal(TenantId tenantId, EntityId entityId, List<String> keys, FutureCallback<Void> callback);

4
application/src/test/java/org/thingsboard/server/controller/WebsocketApiTest.java

@ -806,7 +806,7 @@ public class WebsocketApiTest extends AbstractControllerTest {
private void sendTelemetry(Device device, List<TsKvEntry> tsData) throws InterruptedException { private void sendTelemetry(Device device, List<TsKvEntry> tsData) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
tsService.save(TimeseriesSaveRequest.builder() tsService.saveTimeseries(TimeseriesSaveRequest.builder()
.tenantId(device.getTenantId()) .tenantId(device.getTenantId())
.entityId(device.getId()) .entityId(device.getId())
.entries(tsData) .entries(tsData)
@ -833,7 +833,7 @@ public class WebsocketApiTest extends AbstractControllerTest {
private void sendAttributes(TenantId tenantId, EntityId entityId, TbAttributeSubscriptionScope scope, List<AttributeKvEntry> attrData) throws InterruptedException { private void sendAttributes(TenantId tenantId, EntityId entityId, TbAttributeSubscriptionScope scope, List<AttributeKvEntry> attrData) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
tsService.save(AttributesSaveRequest.builder() tsService.saveAttributes(AttributesSaveRequest.builder()
.tenantId(tenantId) .tenantId(tenantId)
.entityId(entityId) .entityId(entityId)
.scope(scope.getAttributeScope()) .scope(scope.getAttributeScope())

39
application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java

@ -24,7 +24,6 @@ import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource; import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatcher;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
@ -211,7 +210,7 @@ public class DefaultDeviceStateServiceTest {
service.onDeviceConnect(tenantId, deviceId, lastConnectTime); service.onDeviceConnect(tenantId, deviceId, lastConnectTime);
// THEN // THEN
then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher<AttributesSaveRequest>) request -> then(telemetrySubscriptionService).should().saveAttributes(argThat(request ->
request.getTenantId().equals(TenantId.SYS_TENANT_ID) && request.getEntityId().equals(deviceId) && request.getTenantId().equals(TenantId.SYS_TENANT_ID) && request.getEntityId().equals(deviceId) &&
request.getScope().equals(AttributeScope.SERVER_SCOPE) && request.getScope().equals(AttributeScope.SERVER_SCOPE) &&
request.getEntries().get(0).getKey().equals(LAST_CONNECT_TIME) && request.getEntries().get(0).getKey().equals(LAST_CONNECT_TIME) &&
@ -298,7 +297,7 @@ public class DefaultDeviceStateServiceTest {
service.onDeviceDisconnect(tenantId, deviceId, lastDisconnectTime); service.onDeviceDisconnect(tenantId, deviceId, lastDisconnectTime);
// THEN // THEN
then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher<AttributesSaveRequest>) request -> then(telemetrySubscriptionService).should().saveAttributes(argThat(request ->
request.getTenantId().equals(TenantId.SYS_TENANT_ID) && request.getEntityId().equals(deviceId) && request.getTenantId().equals(TenantId.SYS_TENANT_ID) && request.getEntityId().equals(deviceId) &&
request.getScope().equals(AttributeScope.SERVER_SCOPE) && request.getScope().equals(AttributeScope.SERVER_SCOPE) &&
request.getEntries().get(0).getKey().equals(LAST_DISCONNECT_TIME) && request.getEntries().get(0).getKey().equals(LAST_DISCONNECT_TIME) &&
@ -421,13 +420,13 @@ public class DefaultDeviceStateServiceTest {
service.onDeviceInactivity(tenantId, deviceId, lastInactivityTime); service.onDeviceInactivity(tenantId, deviceId, lastInactivityTime);
// THEN // THEN
then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher<AttributesSaveRequest>) request -> then(telemetrySubscriptionService).should().saveAttributes(argThat(request ->
request.getTenantId().equals(TenantId.SYS_TENANT_ID) && request.getEntityId().equals(deviceId) && request.getTenantId().equals(TenantId.SYS_TENANT_ID) && request.getEntityId().equals(deviceId) &&
request.getScope().equals(AttributeScope.SERVER_SCOPE) && request.getScope().equals(AttributeScope.SERVER_SCOPE) &&
request.getEntries().get(0).getKey().equals(INACTIVITY_ALARM_TIME) && request.getEntries().get(0).getKey().equals(INACTIVITY_ALARM_TIME) &&
request.getEntries().get(0).getValue().equals(lastInactivityTime) request.getEntries().get(0).getValue().equals(lastInactivityTime)
)); ));
then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher<AttributesSaveRequest>) request -> then(telemetrySubscriptionService).should().saveAttributes(argThat(request ->
request.getTenantId().equals(TenantId.SYS_TENANT_ID) && request.getEntityId().equals(deviceId) && request.getTenantId().equals(TenantId.SYS_TENANT_ID) && request.getEntityId().equals(deviceId) &&
request.getScope().equals(AttributeScope.SERVER_SCOPE) && request.getScope().equals(AttributeScope.SERVER_SCOPE) &&
request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) &&
@ -465,12 +464,12 @@ public class DefaultDeviceStateServiceTest {
service.updateInactivityStateIfExpired(System.currentTimeMillis(), deviceId, deviceStateData); service.updateInactivityStateIfExpired(System.currentTimeMillis(), deviceId, deviceStateData);
// THEN // THEN
then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher<AttributesSaveRequest>) request -> then(telemetrySubscriptionService).should().saveAttributes(argThat(request ->
request.getTenantId().equals(TenantId.SYS_TENANT_ID) && request.getEntityId().equals(deviceId) && request.getTenantId().equals(TenantId.SYS_TENANT_ID) && request.getEntityId().equals(deviceId) &&
request.getScope().equals(AttributeScope.SERVER_SCOPE) && request.getScope().equals(AttributeScope.SERVER_SCOPE) &&
request.getEntries().get(0).getKey().equals(INACTIVITY_ALARM_TIME) request.getEntries().get(0).getKey().equals(INACTIVITY_ALARM_TIME)
)); ));
then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher<AttributesSaveRequest>) request -> then(telemetrySubscriptionService).should().saveAttributes(argThat(request ->
request.getTenantId().equals(TenantId.SYS_TENANT_ID) && request.getEntityId().equals(deviceId) && request.getTenantId().equals(TenantId.SYS_TENANT_ID) && request.getEntityId().equals(deviceId) &&
request.getScope().equals(AttributeScope.SERVER_SCOPE) && request.getScope().equals(AttributeScope.SERVER_SCOPE) &&
request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) &&
@ -627,7 +626,7 @@ public class DefaultDeviceStateServiceTest {
long newTimeout = System.currentTimeMillis() - deviceState.getLastActivityTime() + increase; long newTimeout = System.currentTimeMillis() - deviceState.getLastActivityTime() + increase;
service.onDeviceInactivityTimeoutUpdate(tenantId, deviceId, newTimeout); service.onDeviceInactivityTimeoutUpdate(tenantId, deviceId, newTimeout);
verify(telemetrySubscriptionService, never()).save(argThat((ArgumentMatcher<AttributesSaveRequest>) request -> verify(telemetrySubscriptionService, never()).saveAttributes(argThat(request ->
request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE)
)); ));
Thread.sleep(defaultTimeout + increase); Thread.sleep(defaultTimeout + increase);
@ -668,7 +667,7 @@ public class DefaultDeviceStateServiceTest {
long newTimeout = 1; long newTimeout = 1;
Thread.sleep(newTimeout); Thread.sleep(newTimeout);
verify(telemetrySubscriptionService, never()).save(argThat((ArgumentMatcher<AttributesSaveRequest>) request -> verify(telemetrySubscriptionService, never()).saveAttributes(argThat(request ->
request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE)
)); ));
} }
@ -730,13 +729,13 @@ public class DefaultDeviceStateServiceTest {
long newTimeout = 1; long newTimeout = 1;
service.onDeviceInactivityTimeoutUpdate(tenantId, deviceId, newTimeout); service.onDeviceInactivityTimeoutUpdate(tenantId, deviceId, newTimeout);
verify(telemetrySubscriptionService, never()).save(argThat((ArgumentMatcher<AttributesSaveRequest>) request -> verify(telemetrySubscriptionService, never()).saveAttributes(argThat(request ->
request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE)
)); ));
} }
private void activityVerify(boolean isActive) { private void activityVerify(boolean isActive) {
verify(telemetrySubscriptionService).save(argThat((ArgumentMatcher<AttributesSaveRequest>) request -> verify(telemetrySubscriptionService).saveAttributes(argThat(request ->
request.getEntityId().equals(deviceId) && request.getEntityId().equals(deviceId) &&
request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) &&
request.getEntries().get(0).getValue().equals(isActive) request.getEntries().get(0).getValue().equals(isActive)
@ -786,7 +785,7 @@ public class DefaultDeviceStateServiceTest {
// THEN // THEN
assertThat(deviceState.isActive()).isEqualTo(true); assertThat(deviceState.isActive()).isEqualTo(true);
assertThat(deviceState.getLastActivityTime()).isEqualTo(lastReportedActivity); assertThat(deviceState.getLastActivityTime()).isEqualTo(lastReportedActivity);
then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher<AttributesSaveRequest>) request -> then(telemetrySubscriptionService).should().saveAttributes(argThat(request ->
request.getEntityId().equals(deviceId) && request.getEntityId().equals(deviceId) &&
request.getEntries().get(0).getKey().equals(LAST_ACTIVITY_TIME) && request.getEntries().get(0).getKey().equals(LAST_ACTIVITY_TIME) &&
request.getEntries().get(0).getValue().equals(lastReportedActivity) request.getEntries().get(0).getValue().equals(lastReportedActivity)
@ -794,7 +793,7 @@ public class DefaultDeviceStateServiceTest {
assertThat(deviceState.getLastInactivityAlarmTime()).isEqualTo(expectedInactivityAlarmTime); assertThat(deviceState.getLastInactivityAlarmTime()).isEqualTo(expectedInactivityAlarmTime);
if (shouldSetInactivityAlarmTimeToZero) { if (shouldSetInactivityAlarmTimeToZero) {
then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher<AttributesSaveRequest>) request -> then(telemetrySubscriptionService).should().saveAttributes(argThat(request ->
request.getEntityId().equals(deviceId) && request.getEntityId().equals(deviceId) &&
request.getEntries().get(0).getKey().equals(INACTIVITY_ALARM_TIME) && request.getEntries().get(0).getKey().equals(INACTIVITY_ALARM_TIME) &&
request.getEntries().get(0).getValue().equals(0L) request.getEntries().get(0).getValue().equals(0L)
@ -802,7 +801,7 @@ public class DefaultDeviceStateServiceTest {
} }
if (shouldUpdateActivityStateToActive) { if (shouldUpdateActivityStateToActive) {
then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher<AttributesSaveRequest>) request -> then(telemetrySubscriptionService).should().saveAttributes(argThat(request ->
request.getEntityId().equals(deviceId) && request.getEntityId().equals(deviceId) &&
request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) &&
request.getEntries().get(0).getValue().equals(true) request.getEntries().get(0).getValue().equals(true)
@ -886,7 +885,7 @@ public class DefaultDeviceStateServiceTest {
assertThat(deviceState.getInactivityTimeout()).isEqualTo(newInactivityTimeout); assertThat(deviceState.getInactivityTimeout()).isEqualTo(newInactivityTimeout);
assertThat(deviceState.isActive()).isEqualTo(expectedActivityState); assertThat(deviceState.isActive()).isEqualTo(expectedActivityState);
if (activityState && !expectedActivityState) { if (activityState && !expectedActivityState) {
then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher<AttributesSaveRequest>) request -> then(telemetrySubscriptionService).should().saveAttributes(argThat(request ->
request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) &&
request.getEntries().get(0).getValue().equals(false) request.getEntries().get(0).getValue().equals(false)
)); ));
@ -984,7 +983,7 @@ public class DefaultDeviceStateServiceTest {
assertThat(state.getLastInactivityAlarmTime()).isEqualTo(expectedLastInactivityAlarmTime); assertThat(state.getLastInactivityAlarmTime()).isEqualTo(expectedLastInactivityAlarmTime);
if (shouldUpdateActivityStateToInactive) { if (shouldUpdateActivityStateToInactive) {
then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher<AttributesSaveRequest>) request -> then(telemetrySubscriptionService).should().saveAttributes(argThat(request ->
request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) &&
request.getEntries().get(0).getValue().equals(false) request.getEntries().get(0).getValue().equals(false)
)); ));
@ -1002,7 +1001,7 @@ public class DefaultDeviceStateServiceTest {
assertThat(actualNotification.getDeviceId()).isEqualTo(deviceId); assertThat(actualNotification.getDeviceId()).isEqualTo(deviceId);
assertThat(actualNotification.isActive()).isFalse(); assertThat(actualNotification.isActive()).isFalse();
then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher<AttributesSaveRequest>) request -> then(telemetrySubscriptionService).should().saveAttributes(argThat(request ->
request.getTenantId().equals(TenantId.SYS_TENANT_ID) && request.getEntityId().equals(deviceId) && request.getTenantId().equals(TenantId.SYS_TENANT_ID) && request.getEntityId().equals(deviceId) &&
request.getScope().equals(AttributeScope.SERVER_SCOPE) && request.getScope().equals(AttributeScope.SERVER_SCOPE) &&
request.getEntries().get(0).getKey().equals(INACTIVITY_ALARM_TIME) && request.getEntries().get(0).getKey().equals(INACTIVITY_ALARM_TIME) &&
@ -1133,7 +1132,7 @@ public class DefaultDeviceStateServiceTest {
// THEN // THEN
await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> { await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> {
assertThat(service.deviceStates.get(deviceId).getState().isActive()).isEqualTo(false); assertThat(service.deviceStates.get(deviceId).getState().isActive()).isEqualTo(false);
then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher<AttributesSaveRequest>) request -> then(telemetrySubscriptionService).should().saveAttributes(argThat(request ->
request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) &&
request.getEntries().get(0).getValue().equals(false) request.getEntries().get(0).getValue().equals(false)
)); ));
@ -1164,7 +1163,7 @@ public class DefaultDeviceStateServiceTest {
// THEN // THEN
ArgumentCaptor<AttributesSaveRequest> attributeRequestCaptor = ArgumentCaptor.forClass(AttributesSaveRequest.class); ArgumentCaptor<AttributesSaveRequest> attributeRequestCaptor = ArgumentCaptor.forClass(AttributesSaveRequest.class);
then(telemetrySubscriptionService).should(times(2)).save(attributeRequestCaptor.capture()); then(telemetrySubscriptionService).should(times(2)).saveAttributes(attributeRequestCaptor.capture());
await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> { await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> {
assertThat(service.deviceStates.get(deviceId).getState().isActive()).isEqualTo(true); assertThat(service.deviceStates.get(deviceId).getState().isActive()).isEqualTo(true);
@ -1231,7 +1230,7 @@ public class DefaultDeviceStateServiceTest {
// THEN // THEN
await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> { await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> {
assertThat(service.deviceStates.get(deviceId).getState().isActive()).isEqualTo(true); assertThat(service.deviceStates.get(deviceId).getState().isActive()).isEqualTo(true);
then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher<AttributesSaveRequest>) request -> then(telemetrySubscriptionService).should().saveAttributes(argThat(request ->
request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) &&
request.getEntries().get(0).getValue().equals(true) request.getEntries().get(0).getValue().equals(true)
)); ));

6
common/dao-api/src/main/java/org/thingsboard/server/dao/attributes/AttributesService.java

@ -37,16 +37,10 @@ public interface AttributesService {
ListenableFuture<List<AttributeKvEntry>> findAll(TenantId tenantId, EntityId entityId, AttributeScope scope); ListenableFuture<List<AttributeKvEntry>> findAll(TenantId tenantId, EntityId entityId, AttributeScope scope);
@Deprecated(since = "3.7.0")
ListenableFuture<List<Long>> save(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes);
ListenableFuture<List<Long>> save(TenantId tenantId, EntityId entityId, AttributeScope scope, List<AttributeKvEntry> attributes); ListenableFuture<List<Long>> save(TenantId tenantId, EntityId entityId, AttributeScope scope, List<AttributeKvEntry> attributes);
ListenableFuture<Long> save(TenantId tenantId, EntityId entityId, AttributeScope scope, AttributeKvEntry attribute); ListenableFuture<Long> save(TenantId tenantId, EntityId entityId, AttributeScope scope, AttributeKvEntry attribute);
@Deprecated(since = "3.7.0")
ListenableFuture<List<String>> removeAll(TenantId tenantId, EntityId entityId, String scope, List<String> attributeKeys);
ListenableFuture<List<String>> removeAll(TenantId tenantId, EntityId entityId, AttributeScope scope, List<String> attributeKeys); ListenableFuture<List<String>> removeAll(TenantId tenantId, EntityId entityId, AttributeScope scope, List<String> attributeKeys);
List<String> findAllKeysByDeviceProfileId(TenantId tenantId, DeviceProfileId deviceProfileId); List<String> findAllKeysByDeviceProfileId(TenantId tenantId, DeviceProfileId deviceProfileId);

14
dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java

@ -101,14 +101,6 @@ public class BaseAttributesService implements AttributesService {
return attributesDao.save(tenantId, entityId, scope, attribute); return attributesDao.save(tenantId, entityId, scope, attribute);
} }
@Override
public ListenableFuture<List<Long>> save(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes) {
validate(entityId, scope);
AttributeUtils.validate(attributes, valueNoXssValidation);
List<ListenableFuture<Long>> saveFutures = attributes.stream().map(attribute -> attributesDao.save(tenantId, entityId, AttributeScope.valueOf(scope), attribute)).collect(Collectors.toList());
return Futures.allAsList(saveFutures);
}
@Override @Override
public ListenableFuture<List<Long>> save(TenantId tenantId, EntityId entityId, AttributeScope scope, List<AttributeKvEntry> attributes) { public ListenableFuture<List<Long>> save(TenantId tenantId, EntityId entityId, AttributeScope scope, List<AttributeKvEntry> attributes) {
validate(entityId, scope); validate(entityId, scope);
@ -117,12 +109,6 @@ public class BaseAttributesService implements AttributesService {
return Futures.allAsList(saveFutures); return Futures.allAsList(saveFutures);
} }
@Override
public ListenableFuture<List<String>> removeAll(TenantId tenantId, EntityId entityId, String scope, List<String> attributeKeys) {
validate(entityId, scope);
return Futures.allAsList(attributesDao.removeAll(tenantId, entityId, AttributeScope.valueOf(scope), attributeKeys));
}
@Override @Override
public ListenableFuture<List<String>> removeAll(TenantId tenantId, EntityId entityId, AttributeScope scope, List<String> attributeKeys) { public ListenableFuture<List<String>> removeAll(TenantId tenantId, EntityId entityId, AttributeScope scope, List<String> attributeKeys) {
validate(entityId, scope); validate(entityId, scope);

10
dao/src/main/java/org/thingsboard/server/dao/attributes/CachedAttributesService.java

@ -222,11 +222,6 @@ public class CachedAttributesService implements AttributesService {
return doSave(tenantId, entityId, scope, attribute); return doSave(tenantId, entityId, scope, attribute);
} }
@Override
public ListenableFuture<List<Long>> save(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes) {
return save(tenantId, entityId, AttributeScope.valueOf(scope), attributes);
}
@Override @Override
public ListenableFuture<List<Long>> save(TenantId tenantId, EntityId entityId, AttributeScope scope, List<AttributeKvEntry> attributes) { public ListenableFuture<List<Long>> save(TenantId tenantId, EntityId entityId, AttributeScope scope, List<AttributeKvEntry> attributes) {
validate(entityId, scope); validate(entityId, scope);
@ -255,11 +250,6 @@ public class CachedAttributesService implements AttributesService {
log.trace("[{}][{}][{}] after cache put.", entityId, scope, key); log.trace("[{}][{}][{}] after cache put.", entityId, scope, key);
} }
@Override
public ListenableFuture<List<String>> removeAll(TenantId tenantId, EntityId entityId, String scope, List<String> attributeKeys) {
return removeAll(tenantId, entityId, AttributeScope.valueOf(scope), attributeKeys);
}
@Override @Override
public ListenableFuture<List<String>> removeAll(TenantId tenantId, EntityId entityId, AttributeScope scope, List<String> attributeKeys) { public ListenableFuture<List<String>> removeAll(TenantId tenantId, EntityId entityId, AttributeScope scope, List<String> attributeKeys) {
validate(entityId, scope); validate(entityId, scope);

117
rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/AttributesDeleteRequest.java

@ -0,0 +1,117 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.rule.engine.api;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.SettableFuture;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;
import org.thingsboard.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import java.util.List;
@Getter
@ToString
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class AttributesDeleteRequest {
private final TenantId tenantId;
private final EntityId entityId;
private final AttributeScope scope;
private final List<String> keys;
private final boolean notifyDevice;
private final FutureCallback<Void> callback;
public static Builder builder() {
return new Builder();
}
public static class Builder {
private TenantId tenantId;
private EntityId entityId;
private AttributeScope scope;
private List<String> keys;
private boolean notifyDevice;
private FutureCallback<Void> callback;
Builder() {}
public Builder tenantId(TenantId tenantId) {
this.tenantId = tenantId;
return this;
}
public Builder entityId(EntityId entityId) {
this.entityId = entityId;
return this;
}
public Builder scope(AttributeScope scope) {
this.scope = scope;
return this;
}
@Deprecated
public Builder scope(String scope) {
try {
this.scope = AttributeScope.valueOf(scope);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Invalid attribute scope '" + scope + "'");
}
return this;
}
public Builder keys(List<String> keys) {
this.keys = keys;
return this;
}
public Builder notifyDevice(boolean notifyDevice) {
this.notifyDevice = notifyDevice;
return this;
}
public Builder callback(FutureCallback<Void> callback) {
this.callback = callback;
return this;
}
public Builder future(SettableFuture<Void> future) {
return callback(new FutureCallback<>() {
@Override
public void onSuccess(Void result) {
future.set(result);
}
@Override
public void onFailure(Throwable t) {
future.setException(t);
}
});
}
public AttributesDeleteRequest build() {
return new AttributesDeleteRequest(tenantId, entityId, scope, keys, notifyDevice, callback);
}
}
}

12
rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineTelemetryService.java

@ -16,11 +16,9 @@
package org.thingsboard.rule.engine.api; package org.thingsboard.rule.engine.api;
import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.FutureCallback;
import org.thingsboard.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.DeleteTsKvQuery; import org.thingsboard.server.common.data.kv.DeleteTsKvQuery;
import org.thingsboard.server.common.data.kv.TsKvEntry;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@ -30,15 +28,11 @@ import java.util.List;
*/ */
public interface RuleEngineTelemetryService { public interface RuleEngineTelemetryService {
void save(TimeseriesSaveRequest request); void saveTimeseries(TimeseriesSaveRequest request);
void save(AttributesSaveRequest request); void saveAttributes(AttributesSaveRequest request);
void saveLatestAndNotify(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts, FutureCallback<Void> callback); void deleteAttributes(AttributesDeleteRequest request);
void deleteAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, List<String> keys, FutureCallback<Void> callback);
void deleteAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, List<String> keys, boolean notifyDevice, FutureCallback<Void> callback);
void deleteLatest(TenantId tenantId, EntityId entityId, List<String> keys, FutureCallback<Void> callback); void deleteLatest(TenantId tenantId, EntityId entityId, List<String> keys, FutureCallback<Void> callback);

10
rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequest.java

@ -39,6 +39,7 @@ public class TimeseriesSaveRequest {
private final List<TsKvEntry> entries; private final List<TsKvEntry> entries;
private final long ttl; private final long ttl;
private final boolean saveLatest; private final boolean saveLatest;
private final boolean onlyLatest;
private final FutureCallback<Void> callback; private final FutureCallback<Void> callback;
public static Builder builder() { public static Builder builder() {
@ -54,6 +55,7 @@ public class TimeseriesSaveRequest {
private long ttl; private long ttl;
private FutureCallback<Void> callback; private FutureCallback<Void> callback;
private boolean saveLatest = true; private boolean saveLatest = true;
private boolean onlyLatest;
Builder() {} Builder() {}
@ -95,6 +97,12 @@ public class TimeseriesSaveRequest {
return this; return this;
} }
public Builder onlyLatest(boolean onlyLatest) {
this.onlyLatest = onlyLatest;
this.saveLatest = true;
return this;
}
public Builder callback(FutureCallback<Void> callback) { public Builder callback(FutureCallback<Void> callback) {
this.callback = callback; this.callback = callback;
return this; return this;
@ -115,7 +123,7 @@ public class TimeseriesSaveRequest {
} }
public TimeseriesSaveRequest build() { public TimeseriesSaveRequest build() {
return new TimeseriesSaveRequest(tenantId, customerId, entityId, entries, ttl, saveLatest, callback); return new TimeseriesSaveRequest(tenantId, customerId, entityId, entries, ttl, saveLatest, onlyLatest, callback);
} }
} }

12
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java

@ -23,6 +23,7 @@ import com.google.gson.JsonPrimitive;
import jakarta.annotation.Nullable; import jakarta.annotation.Nullable;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.thingsboard.common.util.DonAsynchron; import org.thingsboard.common.util.DonAsynchron;
import org.thingsboard.rule.engine.api.AttributesDeleteRequest;
import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.AttributesSaveRequest;
import org.thingsboard.rule.engine.api.EmptyNodeConfiguration; import org.thingsboard.rule.engine.api.EmptyNodeConfiguration;
import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.RuleNode;
@ -106,14 +107,19 @@ public class TbCopyAttributesToEntityViewNode implements TbNode {
List<String> filteredAttributes = List<String> filteredAttributes =
attributes.stream().filter(attr -> attributeContainsInEntityView(scope, attr, entityView)).collect(Collectors.toList()); attributes.stream().filter(attr -> attributeContainsInEntityView(scope, attr, entityView)).collect(Collectors.toList());
if (!filteredAttributes.isEmpty()) { if (!filteredAttributes.isEmpty()) {
ctx.getTelemetryService().deleteAndNotify(ctx.getTenantId(), entityView.getId(), scope, filteredAttributes, ctx.getTelemetryService().deleteAttributes(AttributesDeleteRequest.builder()
getFutureCallback(ctx, msg, entityView)); .tenantId(ctx.getTenantId())
.entityId(entityView.getId())
.scope(scope)
.keys(filteredAttributes)
.callback(getFutureCallback(ctx, msg, entityView))
.build());
} }
} else { } else {
Set<AttributeKvEntry> attributes = JsonConverter.convertToAttributes(JsonParser.parseString(msg.getData())); Set<AttributeKvEntry> attributes = JsonConverter.convertToAttributes(JsonParser.parseString(msg.getData()));
List<AttributeKvEntry> filteredAttributes = List<AttributeKvEntry> filteredAttributes =
attributes.stream().filter(attr -> attributeContainsInEntityView(scope, attr.getKey(), entityView)).collect(Collectors.toList()); attributes.stream().filter(attr -> attributeContainsInEntityView(scope, attr.getKey(), entityView)).collect(Collectors.toList());
ctx.getTelemetryService().save(AttributesSaveRequest.builder() ctx.getTelemetryService().saveAttributes(AttributesSaveRequest.builder()
.tenantId(ctx.getTenantId()) .tenantId(ctx.getTenantId())
.entityId(entityView.getId()) .entityId(entityView.getId())
.scope(scope) .scope(scope)

4
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNode.java

@ -145,7 +145,7 @@ public class TbMathNode implements TbNode {
private ListenableFuture<Void> saveTimeSeries(TbContext ctx, TbMsg msg, double result, TbMathResult mathResultDef) { private ListenableFuture<Void> saveTimeSeries(TbContext ctx, TbMsg msg, double result, TbMathResult mathResultDef) {
final BasicTsKvEntry basicTsKvEntry = new BasicTsKvEntry(System.currentTimeMillis(), new DoubleDataEntry(mathResultDef.getKey(), result)); final BasicTsKvEntry basicTsKvEntry = new BasicTsKvEntry(System.currentTimeMillis(), new DoubleDataEntry(mathResultDef.getKey(), result));
SettableFuture<Void> future = SettableFuture.create(); SettableFuture<Void> future = SettableFuture.create();
ctx.getTelemetryService().save(TimeseriesSaveRequest.builder() ctx.getTelemetryService().saveTimeseries(TimeseriesSaveRequest.builder()
.tenantId(ctx.getTenantId()) .tenantId(ctx.getTenantId())
.entityId(msg.getOriginator()) .entityId(msg.getOriginator())
.entry(basicTsKvEntry) .entry(basicTsKvEntry)
@ -165,7 +165,7 @@ public class TbMathNode implements TbNode {
kvEntry = new DoubleDataEntry(mathResultDef.getKey(), value); kvEntry = new DoubleDataEntry(mathResultDef.getKey(), value);
} }
SettableFuture<Void> future = SettableFuture.create(); SettableFuture<Void> future = SettableFuture.create();
ctx.getTelemetryService().save(AttributesSaveRequest.builder() ctx.getTelemetryService().saveAttributes(AttributesSaveRequest.builder()
.tenantId(ctx.getTenantId()) .tenantId(ctx.getTenantId())
.entityId(msg.getOriginator()) .entityId(msg.getOriginator())
.scope(attributeScope) .scope(attributeScope)

2
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java

@ -119,7 +119,7 @@ public class TbMsgAttributesNode implements TbNode {
FutureCallback<Void> callback = sendAttributesUpdateNotification ? FutureCallback<Void> callback = sendAttributesUpdateNotification ?
new AttributesUpdateNodeCallback(ctx, msg, scope.name(), attributes) : new AttributesUpdateNodeCallback(ctx, msg, scope.name(), attributes) :
new TelemetryNodeCallback(ctx, msg); new TelemetryNodeCallback(ctx, msg);
ctx.getTelemetryService().save(AttributesSaveRequest.builder() ctx.getTelemetryService().saveAttributes(AttributesSaveRequest.builder()
.tenantId(ctx.getTenantId()) .tenantId(ctx.getTenantId())
.entityId(msg.getOriginator()) .entityId(msg.getOriginator())
.scope(scope) .scope(scope)

19
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNode.java

@ -16,6 +16,7 @@
package org.thingsboard.rule.engine.telemetry; package org.thingsboard.rule.engine.telemetry;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.thingsboard.rule.engine.api.AttributesDeleteRequest;
import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.RuleNode;
import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNode;
@ -70,16 +71,16 @@ public class TbMsgDeleteAttributesNode implements TbNode {
ctx.tellSuccess(msg); ctx.tellSuccess(msg);
} else { } else {
AttributeScope scope = getScope(msg.getMetaData().getValue(SCOPE)); AttributeScope scope = getScope(msg.getMetaData().getValue(SCOPE));
ctx.getTelemetryService().deleteAndNotify( ctx.getTelemetryService().deleteAttributes(AttributesDeleteRequest.builder()
ctx.getTenantId(), .tenantId(ctx.getTenantId())
msg.getOriginator(), .entityId(msg.getOriginator())
scope, .scope(scope)
keysToDelete, .keys(keysToDelete)
checkNotifyDevice(msg.getMetaData().getValue(NOTIFY_DEVICE_METADATA_KEY), scope), .notifyDevice(checkNotifyDevice(msg.getMetaData().getValue(NOTIFY_DEVICE_METADATA_KEY), scope))
config.isSendAttributesDeletedNotification() ? .callback(config.isSendAttributesDeletedNotification() ?
new AttributesDeleteNodeCallback(ctx, msg, scope.name(), keysToDelete) : new AttributesDeleteNodeCallback(ctx, msg, scope.name(), keysToDelete) :
new TelemetryNodeCallback(ctx, msg) new TelemetryNodeCallback(ctx, msg))
); .build());
} }
} }

2
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java

@ -105,7 +105,7 @@ public class TbMsgTimeseriesNode implements TbNode {
if (ttl == 0L) { if (ttl == 0L) {
ttl = tenantProfileDefaultStorageTtl; ttl = tenantProfileDefaultStorageTtl;
} }
ctx.getTelemetryService().save(TimeseriesSaveRequest.builder() ctx.getTelemetryService().saveTimeseries(TimeseriesSaveRequest.builder()
.tenantId(ctx.getTenantId()) .tenantId(ctx.getTenantId())
.customerId(msg.getCustomerId()) .customerId(msg.getCustomerId())
.entityId(msg.getOriginator()) .entityId(msg.getOriginator())

30
rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNodeTest.java

@ -15,7 +15,6 @@
*/ */
package org.thingsboard.rule.engine.action; package org.thingsboard.rule.engine.action;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -24,9 +23,9 @@ import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.EnumSource;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.ThrowingConsumer;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.api.AttributesDeleteRequest;
import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.AttributesSaveRequest;
import org.thingsboard.rule.engine.api.EmptyNodeConfiguration; import org.thingsboard.rule.engine.api.EmptyNodeConfiguration;
import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;
@ -56,7 +55,6 @@ import java.util.UUID;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.assertArg; import static org.mockito.ArgumentMatchers.assertArg;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doAnswer;
@ -118,7 +116,7 @@ public class TbCopyAttributesToEntityViewNodeTest {
AttributesSaveRequest request = invocation.getArgument(0); AttributesSaveRequest request = invocation.getArgument(0);
request.getCallback().onSuccess(null); request.getCallback().onSuccess(null);
return null; return null;
}).when(telemetryServiceMock).save(any(AttributesSaveRequest.class)); }).when(telemetryServiceMock).saveAttributes(any(AttributesSaveRequest.class));
TbMsg newMsg = TbMsg.newMsg(msg, msg.getQueueName(), msg.getRuleChainId(), msg.getRuleNodeId()); TbMsg newMsg = TbMsg.newMsg(msg, msg.getQueueName(), msg.getRuleChainId(), msg.getRuleNodeId());
// TODO: use newMsg() with any(TbMsgType.class), replace in other tests as well. // TODO: use newMsg() with any(TbMsgType.class), replace in other tests as well.
doAnswer(invocation -> newMsg).when(ctxMock).newMsg(any(), any(String.class), any(), any(), any(), any()); doAnswer(invocation -> newMsg).when(ctxMock).newMsg(any(), any(String.class), any(), any(), any(), any());
@ -126,7 +124,7 @@ public class TbCopyAttributesToEntityViewNodeTest {
node.onMsg(ctxMock, msg); node.onMsg(ctxMock, msg);
verify(entityViewServiceMock).findEntityViewsByTenantIdAndEntityIdAsync(eq(TENANT_ID), eq(DEVICE_ID)); verify(entityViewServiceMock).findEntityViewsByTenantIdAndEntityIdAsync(eq(TENANT_ID), eq(DEVICE_ID));
verify(telemetryServiceMock).save(assertArg((ThrowingConsumer<AttributesSaveRequest>) request -> { verify(telemetryServiceMock).saveAttributes(assertArg(request -> {
assertThat(request.getTenantId()).isEqualTo(TENANT_ID); assertThat(request.getTenantId()).isEqualTo(TENANT_ID);
assertThat(request.getEntityId()).isEqualTo(ENTITY_VIEW_ID); assertThat(request.getEntityId()).isEqualTo(ENTITY_VIEW_ID);
assertThat(request.getScope()).isEqualTo(AttributeScope.CLIENT_SCOPE); assertThat(request.getScope()).isEqualTo(AttributeScope.CLIENT_SCOPE);
@ -151,21 +149,23 @@ public class TbCopyAttributesToEntityViewNodeTest {
mockEntityViewLookup(entityView); mockEntityViewLookup(entityView);
when(ctxMock.getTelemetryService()).thenReturn(telemetryServiceMock); when(ctxMock.getTelemetryService()).thenReturn(telemetryServiceMock);
doAnswer(invocation -> { doAnswer(invocation -> {
FutureCallback<Void> callback = invocation.getArgument(4); AttributesDeleteRequest request = invocation.getArgument(0);
callback.onSuccess(null); request.getCallback().onSuccess(null);
return null; return null;
}).when(telemetryServiceMock).deleteAndNotify(any(), any(), any(AttributeScope.class), anyList(), any(FutureCallback.class)); }).when(telemetryServiceMock).deleteAttributes(any());
TbMsg newMsg = TbMsg.newMsg(msg, msg.getQueueName(), msg.getRuleChainId(), msg.getRuleNodeId()); TbMsg newMsg = TbMsg.newMsg(msg, msg.getQueueName(), msg.getRuleChainId(), msg.getRuleNodeId());
doAnswer(invocation -> newMsg).when(ctxMock).newMsg(any(), any(String.class), any(), any(), any(), any()); doAnswer(invocation -> newMsg).when(ctxMock).newMsg(any(), any(String.class), any(), any(), any(), any());
node.onMsg(ctxMock, msg); node.onMsg(ctxMock, msg);
verify(entityViewServiceMock).findEntityViewsByTenantIdAndEntityIdAsync(eq(TENANT_ID), eq(DEVICE_ID)); verify(entityViewServiceMock).findEntityViewsByTenantIdAndEntityIdAsync(eq(TENANT_ID), eq(DEVICE_ID));
ArgumentCaptor<List<String>> filteredAttributesCaptor = ArgumentCaptor.forClass(List.class); verify(telemetryServiceMock).deleteAttributes(assertArg(request -> {
verify(telemetryServiceMock).deleteAndNotify(eq(TENANT_ID), eq(ENTITY_VIEW_ID), eq(AttributeScope.SERVER_SCOPE), filteredAttributesCaptor.capture(), any(FutureCallback.class)); assertThat(request.getTenantId()).isEqualTo(TENANT_ID);
List<String> filteredAttributesCaptorValue = filteredAttributesCaptor.getValue(); assertThat(request.getEntityId()).isEqualTo(ENTITY_VIEW_ID);
assertThat(filteredAttributesCaptorValue.size()).isEqualTo(1); assertThat(request.getScope()).isEqualTo(AttributeScope.SERVER_SCOPE);
assertThat(filteredAttributesCaptorValue.get(0)).isEqualTo("serverAttribute1"); assertThat(request.getKeys().size()).isEqualTo(1);
assertThat(request.getKeys().get(0)).isEqualTo("serverAttribute1");
}));
verify(ctxMock).ack(eq(msg)); verify(ctxMock).ack(eq(msg));
verify(ctxMock).enqueueForTellNext(eq(newMsg), eq(TbNodeConnectionType.SUCCESS)); verify(ctxMock).enqueueForTellNext(eq(newMsg), eq(TbNodeConnectionType.SUCCESS));
verifyNoMoreInteractions(ctxMock, entityViewServiceMock, telemetryServiceMock); verifyNoMoreInteractions(ctxMock, entityViewServiceMock, telemetryServiceMock);
@ -202,14 +202,14 @@ public class TbCopyAttributesToEntityViewNodeTest {
AttributesSaveRequest request = invocation.getArgument(0); AttributesSaveRequest request = invocation.getArgument(0);
request.getCallback().onSuccess(null); request.getCallback().onSuccess(null);
return null; return null;
}).when(telemetryServiceMock).save(any(AttributesSaveRequest.class)); }).when(telemetryServiceMock).saveAttributes(any(AttributesSaveRequest.class));
TbMsg newMsg = TbMsg.newMsg(msg, msg.getQueueName(), msg.getRuleChainId(), msg.getRuleNodeId()); TbMsg newMsg = TbMsg.newMsg(msg, msg.getQueueName(), msg.getRuleChainId(), msg.getRuleNodeId());
doAnswer(invocation -> newMsg).when(ctxMock).newMsg(any(), any(String.class), any(), any(), any(), any()); doAnswer(invocation -> newMsg).when(ctxMock).newMsg(any(), any(String.class), any(), any(), any(), any());
node.onMsg(ctxMock, msg); node.onMsg(ctxMock, msg);
verify(entityViewServiceMock).findEntityViewsByTenantIdAndEntityIdAsync(eq(TENANT_ID), eq(DEVICE_ID)); verify(entityViewServiceMock).findEntityViewsByTenantIdAndEntityIdAsync(eq(TENANT_ID), eq(DEVICE_ID));
verify(telemetryServiceMock).save(assertArg((ThrowingConsumer<AttributesSaveRequest>) request -> { verify(telemetryServiceMock).saveAttributes(assertArg(request -> {
assertThat(request.getTenantId()).isEqualTo(TENANT_ID); assertThat(request.getTenantId()).isEqualTo(TENANT_ID);
assertThat(request.getEntityId()).isEqualTo(ENTITY_VIEW_ID); assertThat(request.getEntityId()).isEqualTo(ENTITY_VIEW_ID);
assertThat(request.getScope()).isEqualTo(AttributeScope.CLIENT_SCOPE); assertThat(request.getScope()).isEqualTo(AttributeScope.CLIENT_SCOPE);

12
rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java

@ -441,13 +441,13 @@ public class TbMathNodeTest {
AttributesSaveRequest request = invocation.getArgument(0); AttributesSaveRequest request = invocation.getArgument(0);
request.getCallback().onSuccess(null); request.getCallback().onSuccess(null);
return null; return null;
}).when(telemetryService).save(any(AttributesSaveRequest.class)); }).when(telemetryService).saveAttributes(any(AttributesSaveRequest.class));
node.onMsg(ctx, msg); node.onMsg(ctx, msg);
ArgumentCaptor<TbMsg> msgCaptor = ArgumentCaptor.forClass(TbMsg.class); ArgumentCaptor<TbMsg> msgCaptor = ArgumentCaptor.forClass(TbMsg.class);
verify(ctx, timeout(TIMEOUT)).tellSuccess(msgCaptor.capture()); verify(ctx, timeout(TIMEOUT)).tellSuccess(msgCaptor.capture());
verify(telemetryService, times(1)).save(assertArg((ThrowingConsumer<AttributesSaveRequest>) request -> { verify(telemetryService, times(1)).saveAttributes(assertArg(request -> {
assertThat(request.getEntries()).singleElement().extracting(KvEntry::getValue).isInstanceOf(Double.class); assertThat(request.getEntries()).singleElement().extracting(KvEntry::getValue).isInstanceOf(Double.class);
})); }));
@ -471,13 +471,13 @@ public class TbMathNodeTest {
TimeseriesSaveRequest request = invocation.getArgument(0); TimeseriesSaveRequest request = invocation.getArgument(0);
request.getCallback().onSuccess(null); request.getCallback().onSuccess(null);
return null; return null;
}).when(telemetryService).save(any(TimeseriesSaveRequest.class)); }).when(telemetryService).saveTimeseries(any(TimeseriesSaveRequest.class));
node.onMsg(ctx, msg); node.onMsg(ctx, msg);
ArgumentCaptor<TbMsg> msgCaptor = ArgumentCaptor.forClass(TbMsg.class); ArgumentCaptor<TbMsg> msgCaptor = ArgumentCaptor.forClass(TbMsg.class);
verify(ctx, timeout(TIMEOUT)).tellSuccess(msgCaptor.capture()); verify(ctx, timeout(TIMEOUT)).tellSuccess(msgCaptor.capture());
verify(telemetryService, times(1)).save(assertArg((ThrowingConsumer<TimeseriesSaveRequest>) request -> { verify(telemetryService, times(1)).saveTimeseries(assertArg(request -> {
assertThat(request.getEntries()).size().isOne(); assertThat(request.getEntries()).size().isOne();
assertThat(request.isSaveLatest()).isTrue(); assertThat(request.isSaveLatest()).isTrue();
})); }));
@ -502,13 +502,13 @@ public class TbMathNodeTest {
TimeseriesSaveRequest request = invocation.getArgument(0); TimeseriesSaveRequest request = invocation.getArgument(0);
request.getCallback().onSuccess(null); request.getCallback().onSuccess(null);
return null; return null;
}).when(telemetryService).save(any(TimeseriesSaveRequest.class)); }).when(telemetryService).saveTimeseries(any(TimeseriesSaveRequest.class));
node.onMsg(ctx, msg); node.onMsg(ctx, msg);
ArgumentCaptor<TbMsg> msgCaptor = ArgumentCaptor.forClass(TbMsg.class); ArgumentCaptor<TbMsg> msgCaptor = ArgumentCaptor.forClass(TbMsg.class);
verify(ctx, timeout(TIMEOUT)).tellSuccess(msgCaptor.capture()); verify(ctx, timeout(TIMEOUT)).tellSuccess(msgCaptor.capture());
verify(telemetryService, times(1)).save(assertArg((ThrowingConsumer<TimeseriesSaveRequest>) request -> { verify(telemetryService, times(1)).saveTimeseries(assertArg(request -> {
assertThat(request.getEntries()).size().isOne(); assertThat(request.getEntries()).size().isOne();
assertThat(request.isSaveLatest()).isTrue(); assertThat(request.isSaveLatest()).isTrue();
})); }));

4
rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeTest.java

@ -22,10 +22,8 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.ThrowingConsumer;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.AbstractRuleNodeUpgradeTest; import org.thingsboard.rule.engine.AbstractRuleNodeUpgradeTest;
import org.thingsboard.rule.engine.api.AttributesSaveRequest;
import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;
import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNode;
@ -171,7 +169,7 @@ class TbMsgAttributesNodeTest extends AbstractRuleNodeUpgradeTest {
node.saveAttr(testAttrList, ctxMock, testTbMsg, AttributeScope.SHARED_SCOPE, false); node.saveAttr(testAttrList, ctxMock, testTbMsg, AttributeScope.SHARED_SCOPE, false);
verify(telemetryServiceMock, times(1)).save(assertArg((ThrowingConsumer<AttributesSaveRequest>) request -> { verify(telemetryServiceMock, times(1)).saveAttributes(assertArg(request -> {
assertThat(request.getTenantId()).isEqualTo(tenantId); assertThat(request.getTenantId()).isEqualTo(tenantId);
assertThat(request.getEntityId()).isEqualTo(deviceId); assertThat(request.getEntityId()).isEqualTo(deviceId);
assertThat(request.getScope()).isEqualTo(AttributeScope.SHARED_SCOPE); assertThat(request.getScope()).isEqualTo(AttributeScope.SHARED_SCOPE);

16
rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNodeTest.java

@ -21,11 +21,11 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.api.AttributesDeleteRequest;
import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;
import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeConfiguration;
import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.msg.TbMsgType;
@ -41,10 +41,9 @@ import java.util.function.Consumer;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.assertArg;
import static org.mockito.BDDMockito.willAnswer; import static org.mockito.BDDMockito.willAnswer;
import static org.mockito.BDDMockito.willReturn; import static org.mockito.BDDMockito.willReturn;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
@ -78,11 +77,10 @@ public class TbMsgDeleteAttributesNodeTest {
willReturn(telemetryService).given(ctx).getTelemetryService(); willReturn(telemetryService).given(ctx).getTelemetryService();
willAnswer(invocation -> { willAnswer(invocation -> {
TelemetryNodeCallback callBack = invocation.getArgument(5); AttributesDeleteRequest request = invocation.getArgument(0);
callBack.onSuccess(null); request.getCallback().onSuccess(null);
return null; return null;
}).given(telemetryService).deleteAndNotify( }).given(telemetryService).deleteAttributes(any());
any(), any(), any(AttributeScope.class), anyList(), anyBoolean(), any());
} }
@AfterEach @AfterEach
@ -153,6 +151,8 @@ public class TbMsgDeleteAttributesNodeTest {
} }
verify(ctx, times(1)).tellSuccess(newMsgCaptor.capture()); verify(ctx, times(1)).tellSuccess(newMsgCaptor.capture());
verify(ctx, never()).tellFailure(any(), any()); verify(ctx, never()).tellFailure(any(), any());
verify(telemetryService, times(1)).deleteAndNotify(any(), any(), any(AttributeScope.class), anyList(), eq(notifyDevice || notifyDeviceMetadata), any()); verify(telemetryService, times(1)).deleteAttributes(assertArg(request -> {
assertThat(request.isNotifyDevice()).isEqualTo(notifyDevice || notifyDeviceMetadata);
}));
} }
} }

10
rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java

@ -130,12 +130,12 @@ public class TbMsgTimeseriesNodeTest {
TimeseriesSaveRequest request = invocation.getArgument(0); TimeseriesSaveRequest request = invocation.getArgument(0);
request.getCallback().onSuccess(null); request.getCallback().onSuccess(null);
return null; return null;
}).when(telemetryServiceMock).save(any(TimeseriesSaveRequest.class)); }).when(telemetryServiceMock).saveTimeseries(any(TimeseriesSaveRequest.class));
node.onMsg(ctxMock, msg); node.onMsg(ctxMock, msg);
List<TsKvEntry> expectedList = getTsKvEntriesListWithTs(data, System.currentTimeMillis()); List<TsKvEntry> expectedList = getTsKvEntriesListWithTs(data, System.currentTimeMillis());
verify(telemetryServiceMock).save(assertArg((ThrowingConsumer<TimeseriesSaveRequest>) request -> { verify(telemetryServiceMock).saveTimeseries(assertArg(request -> {
assertThat(request.getTenantId()).isEqualTo(TENANT_ID); assertThat(request.getTenantId()).isEqualTo(TENANT_ID);
assertThat(request.getCustomerId()).isNull(); assertThat(request.getCustomerId()).isNull();
assertThat(request.getEntityId()).isEqualTo(DEVICE_ID); assertThat(request.getEntityId()).isEqualTo(DEVICE_ID);
@ -171,12 +171,12 @@ public class TbMsgTimeseriesNodeTest {
TimeseriesSaveRequest request = invocation.getArgument(0); TimeseriesSaveRequest request = invocation.getArgument(0);
request.getCallback().onSuccess(null); request.getCallback().onSuccess(null);
return null; return null;
}).when(telemetryServiceMock).save(any(TimeseriesSaveRequest.class)); }).when(telemetryServiceMock).saveTimeseries(any(TimeseriesSaveRequest.class));
node.onMsg(ctxMock, msg); node.onMsg(ctxMock, msg);
List<TsKvEntry> expectedList = getTsKvEntriesListWithTs(data, ts); List<TsKvEntry> expectedList = getTsKvEntriesListWithTs(data, ts);
verify(telemetryServiceMock).save(assertArg((ThrowingConsumer<TimeseriesSaveRequest>) request -> { verify(telemetryServiceMock).saveTimeseries(assertArg(request -> {
assertThat(request.getTenantId()).isEqualTo(TENANT_ID); assertThat(request.getTenantId()).isEqualTo(TENANT_ID);
assertThat(request.getCustomerId()).isNull(); assertThat(request.getCustomerId()).isNull();
assertThat(request.getEntityId()).isEqualTo(DEVICE_ID); assertThat(request.getEntityId()).isEqualTo(DEVICE_ID);
@ -209,7 +209,7 @@ public class TbMsgTimeseriesNodeTest {
TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metadata, data); TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metadata, data);
node.onMsg(ctxMock, msg); node.onMsg(ctxMock, msg);
verify(telemetryServiceMock).save(assertArg((ThrowingConsumer<TimeseriesSaveRequest>) request -> { verify(telemetryServiceMock).saveTimeseries(assertArg(request -> {
assertThat(request.getTenantId()).isEqualTo(TENANT_ID); assertThat(request.getTenantId()).isEqualTo(TENANT_ID);
assertThat(request.getCustomerId()).isNull(); assertThat(request.getCustomerId()).isNull();
assertThat(request.getEntityId()).isEqualTo(DEVICE_ID); assertThat(request.getEntityId()).isEqualTo(DEVICE_ID);

Loading…
Cancel
Save