Browse Source

changed AttributeService interface to accept AttributeScope instead of String

pull/9850/head
dashevchenko 3 years ago
parent
commit
e779c06ec1
  1. 26
      application/src/main/data/upgrade/3.6.2/schema_update_attribute_kv.sql
  2. 87
      application/src/main/data/upgrade/3.6.2/schema_update_ttl.sql
  3. 19
      application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
  4. 86
      application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
  5. 3
      application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java
  6. 5
      application/src/main/java/org/thingsboard/server/service/device/ClaimDevicesServiceImpl.java
  7. 5
      application/src/main/java/org/thingsboard/server/service/device/DeviceProvisionServiceImpl.java
  8. 5
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java
  9. 3
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java
  10. 3
      application/src/main/java/org/thingsboard/server/service/edge/rpc/sync/DefaultEdgeRequestsService.java
  11. 25
      application/src/main/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewService.java
  12. 1
      application/src/main/java/org/thingsboard/server/service/install/CassandraTsDatabaseUpgradeService.java
  13. 7
      application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
  14. 11
      application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java
  15. 14
      application/src/main/java/org/thingsboard/server/service/query/DefaultEntityQueryService.java
  16. 5
      application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java
  17. 3
      application/src/main/java/org/thingsboard/server/service/subscription/DefaultSubscriptionManagerService.java
  18. 3
      application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractSubCtx.java
  19. 10
      application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/DefaultEntityExportService.java
  20. 5
      application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java
  21. 10
      application/src/main/java/org/thingsboard/server/service/ws/DefaultWebSocketService.java
  22. 9
      application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java
  23. 3
      application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java
  24. 13
      common/dao-api/src/main/java/org/thingsboard/server/dao/attributes/AttributesService.java
  25. 6
      common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/importing/csv/BulkImportColumnType.java
  26. 3
      dao/src/main/java/org/thingsboard/server/dao/attributes/AttributeCacheKey.java
  27. 4
      dao/src/main/java/org/thingsboard/server/dao/attributes/AttributeUtils.java
  28. 24
      dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java
  29. 28
      dao/src/main/java/org/thingsboard/server/dao/attributes/CachedAttributesService.java
  30. 2
      dao/src/main/java/org/thingsboard/server/dao/model/sql/AttributeKvDictionaryEntry.java
  31. 14
      dao/src/main/java/org/thingsboard/server/dao/service/Validator.java
  32. 8
      dao/src/main/java/org/thingsboard/server/dao/sql/attributes/AttributeKvDictionaryRepository.java
  33. 16
      dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java
  34. 72
      dao/src/main/resources/sql/schema-views-and-functions.sql
  35. 17
      dao/src/test/java/org/thingsboard/server/dao/service/EntityServiceTest.java
  36. 40
      dao/src/test/java/org/thingsboard/server/dao/service/attributes/BaseAttributesServiceTest.java
  37. 4
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/BaseTbMsgPushNodeConfiguration.java
  38. 5
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNode.java
  39. 13
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNode.java
  40. 13
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetAttributesNode.java
  41. 4
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetMappedDataNode.java
  42. 7
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DeviceState.java
  43. 3
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DynamicPredicateValueCtxImpl.java
  44. 3
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java
  45. 3
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java
  46. 7
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNodeTest.java
  47. 5
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNodeTest.java
  48. 5
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNodeTest.java
  49. 5
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNodeTest.java
  50. 54
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java

26
application/src/main/data/upgrade/3.6.2/schema_update_attribute_kv.sql

@ -136,32 +136,6 @@ EXCEPTION
END END
$$; $$;
CREATE OR REPLACE PROCEDURE recreate_device_info_active_attribute_view()
LANGUAGE plpgsql AS
$$
BEGIN
DROP VIEW IF EXISTS device_info_active_attribute_view CASCADE;
CREATE OR REPLACE VIEW device_info_active_attribute_view AS
SELECT d.*
, c.title as customer_title
, COALESCE((c.additional_info::json->>'isPublic')::bool, FALSE) as customer_is_public
, d.type as device_profile_name
, COALESCE(da.bool_v, FALSE) as active
FROM device d
LEFT JOIN customer c ON c.id = d.customer_id
LEFT JOIN attribute_kv da ON da.entity_id = d.id AND da.attribute_type = 2 AND da.attribute_key = (select key_id from attribute_kv_dictionary where key = 'active');
END;
$$;
CREATE OR REPLACE PROCEDURE recreate_device_info_view()
LANGUAGE plpgsql AS
$$
BEGIN
DROP VIEW IF EXISTS device_info_view CASCADE;
CREATE OR REPLACE VIEW device_info_view AS SELECT * FROM device_info_active_attribute_view;
END;
$$;
CREATE OR REPLACE PROCEDURE drop_attribute_kv_old_table() CREATE OR REPLACE PROCEDURE drop_attribute_kv_old_table()
LANGUAGE plpgsql AS LANGUAGE plpgsql AS
$$ $$

87
application/src/main/data/upgrade/3.6.2/schema_update_ttl.sql

@ -1,87 +0,0 @@
--
-- Copyright © 2016-2023 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.
--
CREATE OR REPLACE PROCEDURE cleanup_timeseries_by_ttl(IN null_uuid uuid,
IN system_ttl bigint, INOUT deleted bigint)
LANGUAGE plpgsql AS
$$
DECLARE
tenant_cursor CURSOR FOR select tenant.id as tenant_id
from tenant;
tenant_id_record uuid;
customer_id_record uuid;
tenant_ttl bigint;
customer_ttl bigint;
deleted_for_entities bigint;
tenant_ttl_ts bigint;
customer_ttl_ts bigint;
BEGIN
OPEN tenant_cursor;
FETCH tenant_cursor INTO tenant_id_record;
WHILE FOUND
LOOP
EXECUTE format(
'select attribute_kv.long_v from attribute_kv where attribute_kv.entity_id = %L and attribute_kv.attribute_key = (select key_id from attribute_kv_dictionary where key = %L)',
tenant_id_record, 'TTL') INTO tenant_ttl;
if tenant_ttl IS NULL THEN
tenant_ttl := system_ttl;
END IF;
IF tenant_ttl > 0 THEN
tenant_ttl_ts := (EXTRACT(EPOCH FROM current_timestamp) * 1000 - tenant_ttl::bigint * 1000)::bigint;
deleted_for_entities := delete_device_records_from_ts_kv(tenant_id_record, null_uuid, tenant_ttl_ts);
deleted := deleted + deleted_for_entities;
RAISE NOTICE '% telemetry removed for devices where tenant_id = %', deleted_for_entities, tenant_id_record;
deleted_for_entities := delete_asset_records_from_ts_kv(tenant_id_record, null_uuid, tenant_ttl_ts);
deleted := deleted + deleted_for_entities;
RAISE NOTICE '% telemetry removed for assets where tenant_id = %', deleted_for_entities, tenant_id_record;
END IF;
FOR customer_id_record IN
SELECT customer.id AS customer_id FROM customer WHERE customer.tenant_id = tenant_id_record
LOOP
EXECUTE format(
'select attribute_kv.long_v from attribute_kv where attribute_kv.entity_id = %L and attribute_kv.attribute_key = (select key_id from attribute_kv_dictionary where key = %L)',
customer_id_record, 'TTL') INTO customer_ttl;
IF customer_ttl IS NULL THEN
customer_ttl_ts := tenant_ttl_ts;
ELSE
IF customer_ttl > 0 THEN
customer_ttl_ts :=
(EXTRACT(EPOCH FROM current_timestamp) * 1000 -
customer_ttl::bigint * 1000)::bigint;
END IF;
END IF;
IF customer_ttl_ts IS NOT NULL AND customer_ttl_ts > 0 THEN
deleted_for_entities :=
delete_customer_records_from_ts_kv(tenant_id_record, customer_id_record,
customer_ttl_ts);
deleted := deleted + deleted_for_entities;
RAISE NOTICE '% telemetry removed for customer with id = % where tenant_id = %', deleted_for_entities, customer_id_record, tenant_id_record;
deleted_for_entities :=
delete_device_records_from_ts_kv(tenant_id_record, customer_id_record,
customer_ttl_ts);
deleted := deleted + deleted_for_entities;
RAISE NOTICE '% telemetry removed for devices where tenant_id = % and customer_id = %', deleted_for_entities, tenant_id_record, customer_id_record;
deleted_for_entities := delete_asset_records_from_ts_kv(tenant_id_record,
customer_id_record,
customer_ttl_ts);
deleted := deleted + deleted_for_entities;
RAISE NOTICE '% telemetry removed for assets where tenant_id = % and customer_id = %', deleted_for_entities, tenant_id_record, customer_id_record;
END IF;
END LOOP;
FETCH tenant_cursor INTO tenant_id_record;
END LOOP;
END
$$;

19
application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java

@ -32,6 +32,7 @@ import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.TbActorCtx; import org.thingsboard.server.actors.TbActorCtx;
import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor; import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor;
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.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.EdgeUtils;
@ -501,7 +502,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
private void handleGetAttributesRequest(SessionInfoProto sessionInfo, GetAttributeRequestMsg request) { private void handleGetAttributesRequest(SessionInfoProto sessionInfo, GetAttributeRequestMsg request) {
int requestId = request.getRequestId(); int requestId = request.getRequestId();
if (request.getOnlyShared()) { if (request.getOnlyShared()) {
Futures.addCallback(findAllAttributesByScope(DataConstants.SHARED_SCOPE), new FutureCallback<>() { Futures.addCallback(findAllAttributesByScope(AttributeScope.SHARED_SCOPE), new FutureCallback<>() {
@Override @Override
public void onSuccess(@Nullable List<AttributeKvEntry> result) { public void onSuccess(@Nullable List<AttributeKvEntry> result) {
GetAttributeResponseMsg responseMsg = GetAttributeResponseMsg.newBuilder() GetAttributeResponseMsg responseMsg = GetAttributeResponseMsg.newBuilder()
@ -551,26 +552,26 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
ListenableFuture<List<AttributeKvEntry>> clientAttributesFuture; ListenableFuture<List<AttributeKvEntry>> clientAttributesFuture;
ListenableFuture<List<AttributeKvEntry>> sharedAttributesFuture; ListenableFuture<List<AttributeKvEntry>> sharedAttributesFuture;
if (CollectionUtils.isEmpty(request.getClientAttributeNamesList()) && CollectionUtils.isEmpty(request.getSharedAttributeNamesList())) { if (CollectionUtils.isEmpty(request.getClientAttributeNamesList()) && CollectionUtils.isEmpty(request.getSharedAttributeNamesList())) {
clientAttributesFuture = findAllAttributesByScope(DataConstants.CLIENT_SCOPE); clientAttributesFuture = findAllAttributesByScope(AttributeScope.CLIENT_SCOPE);
sharedAttributesFuture = findAllAttributesByScope(DataConstants.SHARED_SCOPE); sharedAttributesFuture = findAllAttributesByScope(AttributeScope.SHARED_SCOPE);
} else if (!CollectionUtils.isEmpty(request.getClientAttributeNamesList()) && !CollectionUtils.isEmpty(request.getSharedAttributeNamesList())) { } else if (!CollectionUtils.isEmpty(request.getClientAttributeNamesList()) && !CollectionUtils.isEmpty(request.getSharedAttributeNamesList())) {
clientAttributesFuture = findAttributesByScope(toSet(request.getClientAttributeNamesList()), DataConstants.CLIENT_SCOPE); clientAttributesFuture = findAttributesByScope(toSet(request.getClientAttributeNamesList()), AttributeScope.CLIENT_SCOPE);
sharedAttributesFuture = findAttributesByScope(toSet(request.getSharedAttributeNamesList()), DataConstants.SHARED_SCOPE); sharedAttributesFuture = findAttributesByScope(toSet(request.getSharedAttributeNamesList()), AttributeScope.SHARED_SCOPE);
} else if (CollectionUtils.isEmpty(request.getClientAttributeNamesList()) && !CollectionUtils.isEmpty(request.getSharedAttributeNamesList())) { } else if (CollectionUtils.isEmpty(request.getClientAttributeNamesList()) && !CollectionUtils.isEmpty(request.getSharedAttributeNamesList())) {
clientAttributesFuture = Futures.immediateFuture(Collections.emptyList()); clientAttributesFuture = Futures.immediateFuture(Collections.emptyList());
sharedAttributesFuture = findAttributesByScope(toSet(request.getSharedAttributeNamesList()), DataConstants.SHARED_SCOPE); sharedAttributesFuture = findAttributesByScope(toSet(request.getSharedAttributeNamesList()), AttributeScope.SHARED_SCOPE);
} else { } else {
sharedAttributesFuture = Futures.immediateFuture(Collections.emptyList()); sharedAttributesFuture = Futures.immediateFuture(Collections.emptyList());
clientAttributesFuture = findAttributesByScope(toSet(request.getClientAttributeNamesList()), DataConstants.CLIENT_SCOPE); clientAttributesFuture = findAttributesByScope(toSet(request.getClientAttributeNamesList()), AttributeScope.CLIENT_SCOPE);
} }
return Futures.allAsList(Arrays.asList(clientAttributesFuture, sharedAttributesFuture)); return Futures.allAsList(Arrays.asList(clientAttributesFuture, sharedAttributesFuture));
} }
private ListenableFuture<List<AttributeKvEntry>> findAllAttributesByScope(String scope) { private ListenableFuture<List<AttributeKvEntry>> findAllAttributesByScope(AttributeScope scope) {
return systemContext.getAttributesService().findAll(tenantId, deviceId, scope); return systemContext.getAttributesService().findAll(tenantId, deviceId, scope);
} }
private ListenableFuture<List<AttributeKvEntry>> findAttributesByScope(Set<String> attributesSet, String scope) { private ListenableFuture<List<AttributeKvEntry>> findAttributesByScope(Set<String> attributesSet, AttributeScope scope) {
return systemContext.getAttributesService().find(tenantId, deviceId, scope, attributesSet); return systemContext.getAttributesService().find(tenantId, deviceId, scope, attributesSet);
} }

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

@ -51,7 +51,7 @@ import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.common.adaptor.JsonConverter;
import org.thingsboard.server.common.data.DataConstants; 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.StringUtils; import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.TenantProfile;
@ -198,7 +198,7 @@ public class TelemetryController extends BaseController {
public DeferredResult<ResponseEntity> getAttributeKeysByScope( public DeferredResult<ResponseEntity> getAttributeKeysByScope(
@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType, @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType,
@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
@Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, required = true, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE", "CLIENT_SCOPE"})) @PathVariable("scope") String scope) throws ThingsboardException { @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, required = true, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE", "CLIENT_SCOPE"})) @PathVariable("scope") AttributeScope scope) throws ThingsboardException {
return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr,
(result, tenantId, entityId) -> getAttributeKeysCallback(result, tenantId, entityId, scope)); (result, tenantId, entityId) -> getAttributeKeysCallback(result, tenantId, entityId, scope));
} }
@ -240,7 +240,7 @@ public class TelemetryController extends BaseController {
public DeferredResult<ResponseEntity> getAttributesByScope( public DeferredResult<ResponseEntity> getAttributesByScope(
@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType, @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType,
@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
@Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE", "CLIENT_SCOPE"}, required = true)) @PathVariable("scope") String scope, @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE", "CLIENT_SCOPE"}, required = true)) @PathVariable("scope") AttributeScope scope,
@Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException { @Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException {
SecurityUser user = getCurrentUser(); SecurityUser user = getCurrentUser();
return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr,
@ -352,7 +352,7 @@ public class TelemetryController extends BaseController {
@ResponseBody @ResponseBody
public DeferredResult<ResponseEntity> saveDeviceAttributes( public DeferredResult<ResponseEntity> saveDeviceAttributes(
@Parameter(description = DEVICE_ID_PARAM_DESCRIPTION, required = true) @PathVariable("deviceId") String deviceIdStr, @Parameter(description = DEVICE_ID_PARAM_DESCRIPTION, required = true) @PathVariable("deviceId") String deviceIdStr,
@Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE"}, required = true)) @PathVariable("scope") String scope, @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE"}, required = true)) @PathVariable("scope") AttributeScope scope,
@Parameter(description = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody JsonNode request) throws ThingsboardException { @Parameter(description = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody JsonNode request) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr); EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr);
return saveAttributes(getTenantId(), entityId, scope, request); return saveAttributes(getTenantId(), entityId, scope, request);
@ -376,7 +376,7 @@ public class TelemetryController extends BaseController {
public DeferredResult<ResponseEntity> saveEntityAttributesV1( public DeferredResult<ResponseEntity> saveEntityAttributesV1(
@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType, @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType,
@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
@Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE"})) @PathVariable("scope")String scope, @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE"})) @PathVariable("scope")AttributeScope scope,
@Parameter(description = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true)@RequestBody JsonNode request) throws ThingsboardException { @Parameter(description = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true)@RequestBody JsonNode request) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
return saveAttributes(getTenantId(), entityId, scope, request); return saveAttributes(getTenantId(), entityId, scope, request);
@ -400,7 +400,7 @@ public class TelemetryController extends BaseController {
public DeferredResult<ResponseEntity> saveEntityAttributesV2( public DeferredResult<ResponseEntity> saveEntityAttributesV2(
@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType, @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType,
@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
@Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE"}, required = true)) @PathVariable("scope")String scope, @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE"}, required = true)) @PathVariable("scope")AttributeScope scope,
@Parameter(description = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true)@RequestBody JsonNode request) throws ThingsboardException { @Parameter(description = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true)@RequestBody JsonNode request) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
return saveAttributes(getTenantId(), entityId, scope, request); return saveAttributes(getTenantId(), entityId, scope, request);
@ -425,7 +425,7 @@ public class TelemetryController extends BaseController {
public DeferredResult<ResponseEntity> saveEntityTelemetry( public DeferredResult<ResponseEntity> saveEntityTelemetry(
@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType, @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType,
@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
@Parameter(description = TELEMETRY_SCOPE_DESCRIPTION, required = true, schema = @Schema(allowableValues = "ANY")) @PathVariable("scope")String scope, @Parameter(description = TELEMETRY_SCOPE_DESCRIPTION, required = true, schema = @Schema(allowableValues = "ANY")) @PathVariable("scope")AttributeScope scope,
@Parameter(description = TELEMETRY_JSON_REQUEST_DESCRIPTION, required = true)@RequestBody String requestBody) throws ThingsboardException { @Parameter(description = TELEMETRY_JSON_REQUEST_DESCRIPTION, required = true)@RequestBody String requestBody) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
return saveTelemetry(getTenantId(), entityId, requestBody, 0L); return saveTelemetry(getTenantId(), entityId, requestBody, 0L);
@ -450,7 +450,7 @@ public class TelemetryController extends BaseController {
public DeferredResult<ResponseEntity> saveEntityTelemetryWithTTL( public DeferredResult<ResponseEntity> saveEntityTelemetryWithTTL(
@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType, @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType,
@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
@Parameter(description = TELEMETRY_SCOPE_DESCRIPTION, required = true, schema = @Schema(allowableValues = "ANY")) @PathVariable("scope")String scope, @Parameter(description = TELEMETRY_SCOPE_DESCRIPTION, required = true, schema = @Schema(allowableValues = "ANY")) @PathVariable("scope")AttributeScope scope,
@Parameter(description = "A long value representing TTL (Time to Live) parameter.", required = true)@PathVariable("ttl")Long ttl, @Parameter(description = "A long value representing TTL (Time to Live) parameter.", required = true)@PathVariable("ttl")Long ttl,
@Parameter(description = TELEMETRY_JSON_REQUEST_DESCRIPTION, required = true)@RequestBody String requestBody) throws ThingsboardException { @Parameter(description = TELEMETRY_JSON_REQUEST_DESCRIPTION, required = true)@RequestBody String requestBody) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
@ -555,7 +555,7 @@ public class TelemetryController extends BaseController {
@ResponseBody @ResponseBody
public DeferredResult<ResponseEntity> deleteDeviceAttributes( public DeferredResult<ResponseEntity> deleteDeviceAttributes(
@Parameter(description = DEVICE_ID_PARAM_DESCRIPTION, required = true) @PathVariable(DEVICE_ID) String deviceIdStr, @Parameter(description = DEVICE_ID_PARAM_DESCRIPTION, required = true) @PathVariable(DEVICE_ID) String deviceIdStr,
@Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE", "CLIENT_SCOPE"}, required = true)) @PathVariable("scope")String scope, @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE", "CLIENT_SCOPE"}, required = true)) @PathVariable("scope")AttributeScope scope,
@Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION, required = true)@RequestParam(name = "keys")String keysStr) throws ThingsboardException { @Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION, required = true)@RequestParam(name = "keys")String keysStr) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr); EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr);
return deleteAttributes(entityId, scope, keysStr); return deleteAttributes(entityId, scope, keysStr);
@ -579,49 +579,43 @@ public class TelemetryController extends BaseController {
public DeferredResult<ResponseEntity> deleteEntityAttributes( public DeferredResult<ResponseEntity> deleteEntityAttributes(
@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType, @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType,
@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
@Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, required = true, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE", "CLIENT_SCOPE"})) @PathVariable("scope")String scope, @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, required = true, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE", "CLIENT_SCOPE"})) @PathVariable("scope")AttributeScope scope,
@Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION, required = true)@RequestParam(name = "keys")String keysStr) throws ThingsboardException { @Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION, required = true)@RequestParam(name = "keys")String keysStr) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
return deleteAttributes(entityId, scope, keysStr); return deleteAttributes(entityId, scope, keysStr);
} }
private DeferredResult<ResponseEntity> deleteAttributes(EntityId entityIdSrc, String scope, String keysStr) throws ThingsboardException { private DeferredResult<ResponseEntity> deleteAttributes(EntityId entityIdSrc, AttributeScope scope, String keysStr) throws ThingsboardException {
List<String> keys = toKeysList(keysStr); List<String> keys = toKeysList(keysStr);
if (keys.isEmpty()) { if (keys.isEmpty()) {
return getImmediateDeferredResult("Empty keys: " + keysStr, HttpStatus.BAD_REQUEST); return getImmediateDeferredResult("Empty keys: " + keysStr, HttpStatus.BAD_REQUEST);
} }
SecurityUser user = getCurrentUser(); SecurityUser user = getCurrentUser();
if (DataConstants.SERVER_SCOPE.equals(scope) || return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.WRITE_ATTRIBUTES, entityIdSrc, (result, tenantId, entityId) -> {
DataConstants.SHARED_SCOPE.equals(scope) || tsSubService.deleteAndNotify(tenantId, entityId, scope.name(), keys, new FutureCallback<Void>() {
DataConstants.CLIENT_SCOPE.equals(scope)) { @Override
return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.WRITE_ATTRIBUTES, entityIdSrc, (result, tenantId, entityId) -> { public void onSuccess(@Nullable Void tmp) {
tsSubService.deleteAndNotify(tenantId, entityId, scope, keys, new FutureCallback<Void>() { logAttributesDeleted(user, entityId, scope, keys, null);
@Override if (entityIdSrc.getEntityType().equals(EntityType.DEVICE)) {
public void onSuccess(@Nullable Void tmp) { DeviceId deviceId = new DeviceId(entityId.getId());
logAttributesDeleted(user, entityId, scope, keys, null); tbClusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onDelete(
if (entityIdSrc.getEntityType().equals(EntityType.DEVICE)) { user.getTenantId(), deviceId, scope.name(), keys), null);
DeviceId deviceId = new DeviceId(entityId.getId());
tbClusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onDelete(
user.getTenantId(), deviceId, scope, keys), null);
}
result.setResult(new ResponseEntity<>(HttpStatus.OK));
} }
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));
} }
});
}); });
} else { });
return getImmediateDeferredResult("Invalid attribute scope: " + scope, HttpStatus.BAD_REQUEST);
}
} }
private DeferredResult<ResponseEntity> saveAttributes(TenantId srcTenantId, EntityId entityIdSrc, String scope, JsonNode json) throws ThingsboardException { private DeferredResult<ResponseEntity> saveAttributes(TenantId srcTenantId, EntityId entityIdSrc, AttributeScope scope, JsonNode json) throws ThingsboardException {
if (!DataConstants.SERVER_SCOPE.equals(scope) && !DataConstants.SHARED_SCOPE.equals(scope)) { if (AttributeScope.SERVER_SCOPE != scope && AttributeScope.SHARED_SCOPE != scope) {
return getImmediateDeferredResult("Invalid scope: " + scope, HttpStatus.BAD_REQUEST); return getImmediateDeferredResult("Invalid scope: " + scope, HttpStatus.BAD_REQUEST);
} }
if (json.isObject()) { if (json.isObject()) {
@ -636,7 +630,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.saveAndNotify(tenantId, entityId, scope, attributes, new FutureCallback<Void>() { tsSubService.saveAndNotify(tenantId, entityId, scope.name(), attributes, new FutureCallback<Void>() {
@Override @Override
public void onSuccess(@Nullable Void tmp) { public void onSuccess(@Nullable Void tmp) {
logAttributesUpdated(user, entityId, scope, attributes, null); logAttributesUpdated(user, entityId, scope, attributes, null);
@ -710,10 +704,10 @@ public class TelemetryController extends BaseController {
Futures.addCallback(future, getTsKvListCallback(result, useStrictDataTypes), MoreExecutors.directExecutor()); Futures.addCallback(future, getTsKvListCallback(result, useStrictDataTypes), MoreExecutors.directExecutor());
} }
private void getAttributeValuesCallback(@Nullable DeferredResult<ResponseEntity> result, SecurityUser user, EntityId entityId, String scope, String keys) { private void getAttributeValuesCallback(@Nullable DeferredResult<ResponseEntity> result, SecurityUser user, EntityId entityId, AttributeScope scope, String keys) {
List<String> keyList = toKeysList(keys); List<String> keyList = toKeysList(keys);
FutureCallback<List<AttributeKvEntry>> callback = getAttributeValuesToResponseCallback(result, user, scope, entityId, keyList); FutureCallback<List<AttributeKvEntry>> callback = getAttributeValuesToResponseCallback(result, user, scope, entityId, keyList);
if (!StringUtils.isEmpty(scope)) { if (scope != null) {
if (keyList != null && !keyList.isEmpty()) { if (keyList != null && !keyList.isEmpty()) {
Futures.addCallback(attributesService.find(user.getTenantId(), entityId, scope, keyList), callback, MoreExecutors.directExecutor()); Futures.addCallback(attributesService.find(user.getTenantId(), entityId, scope, keyList), callback, MoreExecutors.directExecutor());
} else { } else {
@ -721,7 +715,7 @@ public class TelemetryController extends BaseController {
} }
} else { } else {
List<ListenableFuture<List<AttributeKvEntry>>> futures = new ArrayList<>(); List<ListenableFuture<List<AttributeKvEntry>>> futures = new ArrayList<>();
for (String tmpScope : DataConstants.allScopes()) { for (AttributeScope tmpScope : AttributeScope.values()) {
if (keyList != null && !keyList.isEmpty()) { if (keyList != null && !keyList.isEmpty()) {
futures.add(attributesService.find(user.getTenantId(), entityId, tmpScope, keyList)); futures.add(attributesService.find(user.getTenantId(), entityId, tmpScope, keyList));
} else { } else {
@ -735,13 +729,13 @@ public class TelemetryController extends BaseController {
} }
} }
private void getAttributeKeysCallback(@Nullable DeferredResult<ResponseEntity> result, TenantId tenantId, EntityId entityId, String scope) { private void getAttributeKeysCallback(@Nullable DeferredResult<ResponseEntity> result, TenantId tenantId, EntityId entityId, AttributeScope scope) {
Futures.addCallback(attributesService.findAll(tenantId, entityId, scope), getAttributeKeysToResponseCallback(result), MoreExecutors.directExecutor()); Futures.addCallback(attributesService.findAll(tenantId, entityId, scope), getAttributeKeysToResponseCallback(result), MoreExecutors.directExecutor());
} }
private void getAttributeKeysCallback(@Nullable DeferredResult<ResponseEntity> result, TenantId tenantId, EntityId entityId) { private void getAttributeKeysCallback(@Nullable DeferredResult<ResponseEntity> result, TenantId tenantId, EntityId entityId) {
List<ListenableFuture<List<AttributeKvEntry>>> futures = new ArrayList<>(); List<ListenableFuture<List<AttributeKvEntry>>> futures = new ArrayList<>();
for (String scope : DataConstants.allScopes()) { for (AttributeScope scope : AttributeScope.values()) {
futures.add(attributesService.findAll(tenantId, entityId, scope)); futures.add(attributesService.findAll(tenantId, entityId, scope));
} }
@ -784,7 +778,7 @@ public class TelemetryController extends BaseController {
} }
private FutureCallback<List<AttributeKvEntry>> getAttributeValuesToResponseCallback(final DeferredResult<ResponseEntity> response, private FutureCallback<List<AttributeKvEntry>> getAttributeValuesToResponseCallback(final DeferredResult<ResponseEntity> response,
final SecurityUser user, final String scope, final SecurityUser user, final AttributeScope scope,
final EntityId entityId, final List<String> keyList) { final EntityId entityId, final List<String> keyList) {
return new FutureCallback<>() { return new FutureCallback<>() {
@Override @Override
@ -835,18 +829,18 @@ public class TelemetryController extends BaseController {
toException(e), telemetry); toException(e), telemetry);
} }
private void logAttributesDeleted(SecurityUser user, EntityId entityId, String scope, List<String> keys, Throwable e) { private void logAttributesDeleted(SecurityUser user, EntityId entityId, AttributeScope scope, List<String> keys, Throwable e) {
notificationEntityService.logEntityAction(user.getTenantId(), (UUIDBased & EntityId) entityId, notificationEntityService.logEntityAction(user.getTenantId(), (UUIDBased & EntityId) entityId,
ActionType.ATTRIBUTES_DELETED, user, toException(e), scope, keys); ActionType.ATTRIBUTES_DELETED, user, toException(e), scope, keys);
} }
private void logAttributesUpdated(SecurityUser user, EntityId entityId, String scope, List<AttributeKvEntry> attributes, Throwable e) { private void logAttributesUpdated(SecurityUser user, EntityId entityId, AttributeScope scope, List<AttributeKvEntry> attributes, Throwable e) {
notificationEntityService.logEntityAction(user.getTenantId(), entityId, ActionType.ATTRIBUTES_UPDATED, user, notificationEntityService.logEntityAction(user.getTenantId(), entityId, ActionType.ATTRIBUTES_UPDATED, user,
toException(e), scope, attributes); toException(e), scope, attributes);
} }
private void logAttributesRead(SecurityUser user, EntityId entityId, String scope, List<String> keys, Throwable e) { private void logAttributesRead(SecurityUser user, EntityId entityId, AttributeScope scope, List<String> keys, Throwable e) {
notificationEntityService.logEntityAction(user.getTenantId(), entityId, ActionType.ATTRIBUTES_READ, user, notificationEntityService.logEntityAction(user.getTenantId(), entityId, ActionType.ATTRIBUTES_READ, user,
toException(e), scope, keys); toException(e), scope, keys);
} }

3
application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java

@ -267,9 +267,6 @@ public class ThingsboardInstallService {
databaseEntitiesUpgradeService.upgradeDatabase("3.6.0"); databaseEntitiesUpgradeService.upgradeDatabase("3.6.0");
case "3.6.2": case "3.6.2":
log.info("Upgrading ThingsBoard from version 3.6.2 to 3.7.0 ..."); log.info("Upgrading ThingsBoard from version 3.6.2 to 3.7.0 ...");
if (databaseTsUpgradeService != null) {
databaseTsUpgradeService.upgradeDatabase("3.6.2");
}
databaseEntitiesUpgradeService.upgradeDatabase("3.6.2"); databaseEntitiesUpgradeService.upgradeDatabase("3.6.2");
//TODO DON'T FORGET to update switch statement in the CacheCleanupService if you need to clear the cache //TODO DON'T FORGET to update switch statement in the CacheCleanupService if you need to clear the cache
break; break;

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

@ -29,6 +29,7 @@ import org.springframework.stereotype.Service;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;
import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.cluster.TbClusterService;
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.DataConstants; import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
@ -100,7 +101,7 @@ public class ClaimDevicesServiceImpl implements ClaimDevicesService {
return Futures.immediateFailedFuture(new IllegalArgumentException()); return Futures.immediateFailedFuture(new IllegalArgumentException());
} else { } else {
ListenableFuture<List<AttributeKvEntry>> claimingAllowedFuture = attributesService.find(tenantId, device.getId(), ListenableFuture<List<AttributeKvEntry>> claimingAllowedFuture = attributesService.find(tenantId, device.getId(),
DataConstants.SERVER_SCOPE, Collections.singletonList(CLAIM_ATTRIBUTE_NAME)); AttributeScope.SERVER_SCOPE, Collections.singletonList(CLAIM_ATTRIBUTE_NAME));
return Futures.transform(claimingAllowedFuture, list -> { return Futures.transform(claimingAllowedFuture, list -> {
if (list != null && !list.isEmpty()) { if (list != null && !list.isEmpty()) {
Optional<Boolean> claimingAllowedOptional = list.get(0).getBooleanValue(); Optional<Boolean> claimingAllowedOptional = list.get(0).getBooleanValue();
@ -123,7 +124,7 @@ public class ClaimDevicesServiceImpl implements ClaimDevicesService {
return Futures.immediateFuture(new ClaimDataInfo(true, key, claimDataFromCache)); return Futures.immediateFuture(new ClaimDataInfo(true, key, claimDataFromCache));
} else { } else {
ListenableFuture<Optional<AttributeKvEntry>> claimDataAttrFuture = attributesService.find(device.getTenantId(), device.getId(), ListenableFuture<Optional<AttributeKvEntry>> claimDataAttrFuture = attributesService.find(device.getTenantId(), device.getId(),
DataConstants.SERVER_SCOPE, CLAIM_DATA_ATTRIBUTE_NAME); AttributeScope.SERVER_SCOPE, CLAIM_DATA_ATTRIBUTE_NAME);
return Futures.transform(claimDataAttrFuture, claimDataAttr -> { return Futures.transform(claimDataAttrFuture, claimDataAttr -> {
if (claimDataAttr.isPresent()) { if (claimDataAttr.isPresent()) {

5
application/src/main/java/org/thingsboard/server/service/device/DeviceProvisionServiceImpl.java

@ -23,6 +23,7 @@ import lombok.extern.slf4j.Slf4j;
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.server.cluster.TbClusterService; import org.thingsboard.server.cluster.TbClusterService;
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.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
@ -189,7 +190,7 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService {
private ProvisionResponse processProvision(Device device, ProvisionRequest provisionRequest) { private ProvisionResponse processProvision(Device device, ProvisionRequest provisionRequest) {
try { try {
Optional<AttributeKvEntry> provisionState = attributesService.find(device.getTenantId(), device.getId(), Optional<AttributeKvEntry> provisionState = attributesService.find(device.getTenantId(), device.getId(),
DataConstants.SERVER_SCOPE, DEVICE_PROVISION_STATE).get(); AttributeScope.SERVER_SCOPE, DEVICE_PROVISION_STATE).get();
if (provisionState != null && provisionState.isPresent() && !provisionState.get().getValueAsString().equals(PROVISIONED_STATE)) { if (provisionState != null && provisionState.isPresent() && !provisionState.get().getValueAsString().equals(PROVISIONED_STATE)) {
notify(device, provisionRequest, TbMsgType.PROVISION_FAILURE, false); notify(device, provisionRequest, TbMsgType.PROVISION_FAILURE, false);
throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name()); throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name());
@ -245,7 +246,7 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService {
} }
private ListenableFuture<List<String>> saveProvisionStateAttribute(Device device) { private ListenableFuture<List<String>> saveProvisionStateAttribute(Device device) {
return attributesService.save(device.getTenantId(), device.getId(), DataConstants.SERVER_SCOPE, return attributesService.save(device.getTenantId(), device.getId(), AttributeScope.SERVER_SCOPE,
Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry(DEVICE_PROVISION_STATE, PROVISIONED_STATE), Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry(DEVICE_PROVISION_STATE, PROVISIONED_STATE),
System.currentTimeMillis()))); System.currentTimeMillis())));
} }

5
application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java

@ -25,6 +25,7 @@ import lombok.Data;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.data.util.Pair; import org.springframework.data.util.Pair;
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.EdgeUtils; import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.Edge;
@ -543,7 +544,7 @@ public final class EdgeGrpcSession implements Closeable {
private ListenableFuture<Pair<Long, Long>> getQueueStartTsAndSeqId() { private ListenableFuture<Pair<Long, Long>> getQueueStartTsAndSeqId() {
ListenableFuture<List<AttributeKvEntry>> future = ListenableFuture<List<AttributeKvEntry>> future =
ctx.getAttributesService().find(edge.getTenantId(), edge.getId(), DataConstants.SERVER_SCOPE, Arrays.asList(QUEUE_START_TS_ATTR_KEY, QUEUE_START_SEQ_ID_ATTR_KEY)); ctx.getAttributesService().find(edge.getTenantId(), edge.getId(), AttributeScope.SERVER_SCOPE, Arrays.asList(QUEUE_START_TS_ATTR_KEY, QUEUE_START_SEQ_ID_ATTR_KEY));
return Futures.transform(future, attributeKvEntries -> { return Futures.transform(future, attributeKvEntries -> {
long startTs = 0L; long startTs = 0L;
long startSeqId = 0L; long startSeqId = 0L;
@ -605,7 +606,7 @@ public final class EdgeGrpcSession implements Closeable {
List<AttributeKvEntry> attributes = Arrays.asList( List<AttributeKvEntry> attributes = Arrays.asList(
new BaseAttributeKvEntry(new LongDataEntry(QUEUE_START_TS_ATTR_KEY, this.newStartTs), System.currentTimeMillis()), new BaseAttributeKvEntry(new LongDataEntry(QUEUE_START_TS_ATTR_KEY, this.newStartTs), System.currentTimeMillis()),
new BaseAttributeKvEntry(new LongDataEntry(QUEUE_START_SEQ_ID_ATTR_KEY, this.newStartSeqId), System.currentTimeMillis())); new BaseAttributeKvEntry(new LongDataEntry(QUEUE_START_SEQ_ID_ATTR_KEY, this.newStartSeqId), System.currentTimeMillis()));
return ctx.getAttributesService().save(edge.getTenantId(), edge.getId(), DataConstants.SERVER_SCOPE, attributes); return ctx.getAttributesService().save(edge.getTenantId(), edge.getId(), AttributeScope.SERVER_SCOPE, attributes);
} }
private DownlinkMsg convertEntityEventToDownlink(EdgeEvent edgeEvent) { private DownlinkMsg convertEntityEventToDownlink(EdgeEvent edgeEvent) {

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

@ -30,6 +30,7 @@ import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
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.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
@ -287,7 +288,7 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor {
String scope = attributeDeleteMsg.getScope(); String scope = attributeDeleteMsg.getScope();
List<String> attributeKeys = attributeDeleteMsg.getAttributeNamesList(); List<String> attributeKeys = attributeDeleteMsg.getAttributeNamesList();
ListenableFuture<List<String>> removeAllFuture = attributesService.removeAll(tenantId, entityId, scope, attributeKeys); ListenableFuture<List<String>> removeAllFuture = attributesService.removeAll(tenantId, entityId, AttributeScope.valueOf(scope), attributeKeys);
return Futures.transformAsync(removeAllFuture, removeAttributes -> { return Futures.transformAsync(removeAllFuture, removeAttributes -> {
if (EntityType.DEVICE.name().equals(entityType)) { if (EntityType.DEVICE.name().equals(entityType)) {
SettableFuture<Void> futureToSet = SettableFuture.create(); SettableFuture<Void> futureToSet = SettableFuture.create();

3
application/src/main/java/org/thingsboard/server/service/edge/rpc/sync/DefaultEdgeRequestsService.java

@ -28,6 +28,7 @@ import org.springframework.context.annotation.Lazy;
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.server.cluster.TbClusterService; import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.EdgeUtils;
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;
@ -136,7 +137,7 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
return Futures.immediateFuture(null); return Futures.immediateFuture(null);
} }
String scope = attributesRequestMsg.getScope(); String scope = attributesRequestMsg.getScope();
ListenableFuture<List<AttributeKvEntry>> findAttrFuture = attributesService.findAll(tenantId, entityId, scope); ListenableFuture<List<AttributeKvEntry>> findAttrFuture = attributesService.findAll(tenantId, entityId, AttributeScope.valueOf(scope));
return Futures.transformAsync(findAttrFuture, ssAttributes return Futures.transformAsync(findAttrFuture, ssAttributes
-> processEntityAttributesAndAddToEdgeQueue(tenantId, entityId, edge, entityType, scope, ssAttributes, attributesRequestMsg), -> processEntityAttributesAndAddToEdgeQueue(tenantId, entityId, edge, entityType, scope, ssAttributes, attributesRequestMsg),
dbCallbackExecutorService); dbCallbackExecutorService);

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

@ -24,6 +24,7 @@ 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.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
@ -99,9 +100,9 @@ public class DefaultTbEntityViewService extends AbstractTbEntityService implemen
if (oldEntityView != null) { if (oldEntityView != null) {
if (oldEntityView.getKeys() != null && oldEntityView.getKeys().getAttributes() != null) { if (oldEntityView.getKeys() != null && oldEntityView.getKeys().getAttributes() != null) {
futures.add(deleteAttributesFromEntityView(oldEntityView, DataConstants.CLIENT_SCOPE, oldEntityView.getKeys().getAttributes().getCs(), user)); futures.add(deleteAttributesFromEntityView(oldEntityView, AttributeScope.CLIENT_SCOPE, oldEntityView.getKeys().getAttributes().getCs(), user));
futures.add(deleteAttributesFromEntityView(oldEntityView, DataConstants.SERVER_SCOPE, oldEntityView.getKeys().getAttributes().getSs(), user)); futures.add(deleteAttributesFromEntityView(oldEntityView, AttributeScope.SERVER_SCOPE, oldEntityView.getKeys().getAttributes().getSs(), user));
futures.add(deleteAttributesFromEntityView(oldEntityView, DataConstants.SHARED_SCOPE, oldEntityView.getKeys().getAttributes().getSh(), user)); futures.add(deleteAttributesFromEntityView(oldEntityView, AttributeScope.SHARED_SCOPE, oldEntityView.getKeys().getAttributes().getSh(), user));
} }
List<String> tsKeys = oldEntityView.getKeys() != null && oldEntityView.getKeys().getTimeseries() != null ? List<String> tsKeys = oldEntityView.getKeys() != null && oldEntityView.getKeys().getTimeseries() != null ?
oldEntityView.getKeys().getTimeseries() : Collections.emptyList(); oldEntityView.getKeys().getTimeseries() : Collections.emptyList();
@ -109,9 +110,9 @@ public class DefaultTbEntityViewService extends AbstractTbEntityService implemen
} }
if (savedEntityView.getKeys() != null) { if (savedEntityView.getKeys() != null) {
if (savedEntityView.getKeys().getAttributes() != null) { if (savedEntityView.getKeys().getAttributes() != null) {
futures.add(copyAttributesFromEntityToEntityView(savedEntityView, DataConstants.CLIENT_SCOPE, savedEntityView.getKeys().getAttributes().getCs(), user)); futures.add(copyAttributesFromEntityToEntityView(savedEntityView, AttributeScope.CLIENT_SCOPE, savedEntityView.getKeys().getAttributes().getCs(), user));
futures.add(copyAttributesFromEntityToEntityView(savedEntityView, DataConstants.SERVER_SCOPE, savedEntityView.getKeys().getAttributes().getSs(), user)); futures.add(copyAttributesFromEntityToEntityView(savedEntityView, AttributeScope.SERVER_SCOPE, savedEntityView.getKeys().getAttributes().getSs(), user));
futures.add(copyAttributesFromEntityToEntityView(savedEntityView, DataConstants.SHARED_SCOPE, savedEntityView.getKeys().getAttributes().getSh(), user)); futures.add(copyAttributesFromEntityToEntityView(savedEntityView, AttributeScope.SHARED_SCOPE, savedEntityView.getKeys().getAttributes().getSh(), user));
} }
futures.add(copyLatestFromEntityToEntityView(tenantId, savedEntityView)); futures.add(copyLatestFromEntityToEntityView(tenantId, savedEntityView));
} }
@ -269,7 +270,7 @@ public class DefaultTbEntityViewService extends AbstractTbEntityService implemen
} }
} }
private ListenableFuture<List<Void>> copyAttributesFromEntityToEntityView(EntityView entityView, String scope, Collection<String> keys, User user) throws ThingsboardException { private ListenableFuture<List<Void>> copyAttributesFromEntityToEntityView(EntityView entityView, AttributeScope scope, Collection<String> keys, User user) throws ThingsboardException {
EntityViewId entityId = entityView.getId(); EntityViewId entityId = entityView.getId();
if (keys != null && !keys.isEmpty()) { if (keys != null && !keys.isEmpty()) {
ListenableFuture<List<AttributeKvEntry>> getAttrFuture = attributesService.find(entityView.getTenantId(), entityView.getEntityId(), scope, keys); ListenableFuture<List<AttributeKvEntry>> getAttrFuture = attributesService.find(entityView.getTenantId(), entityView.getEntityId(), scope, keys);
@ -287,7 +288,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.saveAndNotify(entityView.getTenantId(), entityId, scope, attributes, new FutureCallback<Void>() { tsSubService.saveAndNotify(entityView.getTenantId(), entityId, scope.name(), attributes, new FutureCallback<Void>() {
@Override @Override
public void onSuccess(@Nullable Void tmp) { public void onSuccess(@Nullable Void tmp) {
try { try {
@ -351,11 +352,11 @@ public class DefaultTbEntityViewService extends AbstractTbEntityService implemen
}, MoreExecutors.directExecutor()); }, MoreExecutors.directExecutor());
} }
private ListenableFuture<Void> deleteAttributesFromEntityView(EntityView entityView, String scope, List<String> keys, User user) { private ListenableFuture<Void> deleteAttributesFromEntityView(EntityView entityView, AttributeScope scope, List<String> keys, User user) {
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.deleteAndNotify(entityView.getTenantId(), entityId, scope.name(), keys, new FutureCallback<Void>() {
@Override @Override
public void onSuccess(@Nullable Void tmp) { public void onSuccess(@Nullable Void tmp) {
try { try {
@ -433,11 +434,11 @@ public class DefaultTbEntityViewService extends AbstractTbEntityService implemen
return resultFuture; return resultFuture;
} }
private void logAttributesUpdated(TenantId tenantId, User user, EntityId entityId, String scope, List<AttributeKvEntry> attributes, Throwable e) throws ThingsboardException { private void logAttributesUpdated(TenantId tenantId, User user, EntityId entityId, AttributeScope scope, List<AttributeKvEntry> attributes, Throwable e) throws ThingsboardException {
notificationEntityService.logEntityAction(tenantId, entityId, ActionType.ATTRIBUTES_UPDATED, user, toException(e), scope, attributes); notificationEntityService.logEntityAction(tenantId, entityId, ActionType.ATTRIBUTES_UPDATED, user, toException(e), scope, attributes);
} }
private void logAttributesDeleted(TenantId tenantId, User user, EntityId entityId, String scope, List<String> keys, Throwable e) throws ThingsboardException { private void logAttributesDeleted(TenantId tenantId, User user, EntityId entityId, AttributeScope scope, List<String> keys, Throwable e) throws ThingsboardException {
notificationEntityService.logEntityAction(tenantId, entityId, ActionType.ATTRIBUTES_DELETED, user, toException(e), scope, keys); notificationEntityService.logEntityAction(tenantId, entityId, ActionType.ATTRIBUTES_DELETED, user, toException(e), scope, keys);
} }

1
application/src/main/java/org/thingsboard/server/service/install/CassandraTsDatabaseUpgradeService.java

@ -52,7 +52,6 @@ public class CassandraTsDatabaseUpgradeService extends AbstractCassandraDatabase
case "3.1.1": case "3.1.1":
case "3.2.1": case "3.2.1":
case "3.2.2": case "3.2.2":
case "3.6.2":
break; break;
default: default:
throw new RuntimeException("Unable to upgrade Cassandra database, unsupported fromVersion: " + fromVersion); throw new RuntimeException("Unable to upgrade Cassandra database, unsupported fromVersion: " + fromVersion);

7
application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java

@ -32,6 +32,7 @@ import org.springframework.stereotype.Service;
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.server.common.data.AdminSettings; import org.thingsboard.server.common.data.AdminSettings;
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.DataConstants; import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
@ -469,7 +470,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
DeviceId t1Id = createDevice(demoTenant.getId(), null, savedThermostatDeviceProfile.getId(), "Thermostat T1", "T1_TEST_TOKEN", "Demo device for Thermostats dashboard").getId(); DeviceId t1Id = createDevice(demoTenant.getId(), null, savedThermostatDeviceProfile.getId(), "Thermostat T1", "T1_TEST_TOKEN", "Demo device for Thermostats dashboard").getId();
DeviceId t2Id = createDevice(demoTenant.getId(), null, savedThermostatDeviceProfile.getId(), "Thermostat T2", "T2_TEST_TOKEN", "Demo device for Thermostats dashboard").getId(); DeviceId t2Id = createDevice(demoTenant.getId(), null, savedThermostatDeviceProfile.getId(), "Thermostat T2", "T2_TEST_TOKEN", "Demo device for Thermostats dashboard").getId();
attributesService.save(demoTenant.getId(), t1Id, DataConstants.SERVER_SCOPE, attributesService.save(demoTenant.getId(), t1Id, AttributeScope.SERVER_SCOPE,
Arrays.asList(new BaseAttributeKvEntry(System.currentTimeMillis(), new DoubleDataEntry("latitude", 37.3948)), Arrays.asList(new BaseAttributeKvEntry(System.currentTimeMillis(), new DoubleDataEntry("latitude", 37.3948)),
new BaseAttributeKvEntry(System.currentTimeMillis(), new DoubleDataEntry("longitude", -122.1503)), new BaseAttributeKvEntry(System.currentTimeMillis(), new DoubleDataEntry("longitude", -122.1503)),
new BaseAttributeKvEntry(System.currentTimeMillis(), new BooleanDataEntry("temperatureAlarmFlag", true)), new BaseAttributeKvEntry(System.currentTimeMillis(), new BooleanDataEntry("temperatureAlarmFlag", true)),
@ -477,7 +478,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
new BaseAttributeKvEntry(System.currentTimeMillis(), new LongDataEntry("temperatureAlarmThreshold", (long) 20)), new BaseAttributeKvEntry(System.currentTimeMillis(), new LongDataEntry("temperatureAlarmThreshold", (long) 20)),
new BaseAttributeKvEntry(System.currentTimeMillis(), new LongDataEntry("humidityAlarmThreshold", (long) 50)))); new BaseAttributeKvEntry(System.currentTimeMillis(), new LongDataEntry("humidityAlarmThreshold", (long) 50))));
attributesService.save(demoTenant.getId(), t2Id, DataConstants.SERVER_SCOPE, attributesService.save(demoTenant.getId(), t2Id, AttributeScope.SERVER_SCOPE,
Arrays.asList(new BaseAttributeKvEntry(System.currentTimeMillis(), new DoubleDataEntry("latitude", 37.493801)), Arrays.asList(new BaseAttributeKvEntry(System.currentTimeMillis(), new DoubleDataEntry("latitude", 37.493801)),
new BaseAttributeKvEntry(System.currentTimeMillis(), new DoubleDataEntry("longitude", -121.948769)), new BaseAttributeKvEntry(System.currentTimeMillis(), new DoubleDataEntry("longitude", -121.948769)),
new BaseAttributeKvEntry(System.currentTimeMillis(), new BooleanDataEntry("temperatureAlarmFlag", true)), new BaseAttributeKvEntry(System.currentTimeMillis(), new BooleanDataEntry("temperatureAlarmFlag", true)),
@ -586,7 +587,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
Collections.singletonList(new BasicTsKvEntry(System.currentTimeMillis(), new BooleanDataEntry(key, value))), 0L); Collections.singletonList(new BasicTsKvEntry(System.currentTimeMillis(), new BooleanDataEntry(key, value))), 0L);
addTsCallback(saveFuture, new TelemetrySaveCallback<>(deviceId, key, value)); addTsCallback(saveFuture, new TelemetrySaveCallback<>(deviceId, key, value));
} else { } else {
ListenableFuture<List<String>> saveFuture = attributesService.save(TenantId.SYS_TENANT_ID, deviceId, DataConstants.SERVER_SCOPE, ListenableFuture<List<String>> saveFuture = attributesService.save(TenantId.SYS_TENANT_ID, deviceId, AttributeScope.SERVER_SCOPE,
Collections.singletonList(new BaseAttributeKvEntry(new BooleanDataEntry(key, value) Collections.singletonList(new BaseAttributeKvEntry(new BooleanDataEntry(key, value)
, System.currentTimeMillis()))); , System.currentTimeMillis())));
addTsCallback(saveFuture, new TelemetrySaveCallback<>(deviceId, key, value)); addTsCallback(saveFuture, new TelemetrySaveCallback<>(deviceId, key, value));

11
application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java

@ -826,11 +826,9 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
executeQuery(conn, "CREATE INDEX IF NOT EXISTS idx_attribute_kv_by_key_and_last_update_ts ON attribute_kv(entity_id, attribute_key, last_update_ts desc);"); executeQuery(conn, "CREATE INDEX IF NOT EXISTS idx_attribute_kv_by_key_and_last_update_ts ON attribute_kv(entity_id, attribute_key, last_update_ts desc);");
// remove temp files // remove temp files
if (pathToTempAttributeKvFile.toFile().exists()) { boolean deleteTsKvFile = Files.deleteIfExists(pathToTempAttributeKvFile);
boolean deleteTsKvFile = pathToTempAttributeKvFile.toFile().delete(); if (deleteTsKvFile) {
if (deleteTsKvFile) { log.info("Successfully deleted the temp file for attribute_kv table upgrade!");
log.info("Successfully deleted the temp file for attribute_kv table upgrade!");
}
} }
executeQuery(conn, "UPDATE tb_schema_settings SET schema_version = 3007000;"); executeQuery(conn, "UPDATE tb_schema_settings SET schema_version = 3007000;");
@ -900,9 +898,8 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
Statement statement = conn.createStatement(); Statement statement = conn.createStatement();
statement.execute(query); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script statement.execute(query); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script
printWarnings(statement); printWarnings(statement);
Thread.sleep(2000);
log.info("Successfully executed query: {}", query); log.info("Successfully executed query: {}", query);
} catch (InterruptedException | SQLException e) { } catch (SQLException e) {
log.error("Failed to execute query: {} due to: {}", query, e.getMessage()); log.error("Failed to execute query: {} due to: {}", query, e.getMessage());
throw new RuntimeException("Failed to execute query:" + query + " due to: ", e); throw new RuntimeException("Failed to execute query:" + query + " due to: ", e);
} }

14
application/src/main/java/org/thingsboard/server/service/query/DefaultEntityQueryService.java

@ -31,6 +31,7 @@ import org.springframework.util.CollectionUtils;
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.KvUtil; import org.thingsboard.common.util.KvUtil;
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.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;
@ -154,7 +155,7 @@ public class DefaultEntityQueryService implements EntityQueryService {
try { try {
Optional<AttributeKvEntry> valueOpt = attributesService.find(user.getTenantId(), entityId, Optional<AttributeKvEntry> valueOpt = attributesService.find(user.getTenantId(), entityId,
TbAttributeSubscriptionScope.SERVER_SCOPE.name(), dynamicValue.getSourceAttribute()).get(); AttributeScope.SERVER_SCOPE, dynamicValue.getSourceAttribute()).get();
if (valueOpt.isPresent()) { if (valueOpt.isPresent()) {
AttributeKvEntry entry = valueOpt.get(); AttributeKvEntry entry = valueOpt.get();
@ -251,13 +252,14 @@ public class DefaultEntityQueryService implements EntityQueryService {
} }
if (isAttributes) { if (isAttributes) {
ListenableFuture<List<String>> future; Map<EntityType, List<EntityId>> typesMap = ids.stream().collect(Collectors.groupingBy(EntityId::getEntityType));
future = dbCallbackExecutor.submit(() -> attributesService.findAllKeysByEntityIds(tenantId, ids)); List<ListenableFuture<List<String>>> futures = new ArrayList<>(typesMap.size());
attributesKeysFuture = Futures.transform(future, list -> { typesMap.forEach((type, entityIds) -> futures.add(dbCallbackExecutor.submit(() -> attributesService.findAllKeysByEntityIds(tenantId, entityIds))));
if (CollectionUtils.isEmpty(list)) { attributesKeysFuture = Futures.transform(Futures.allAsList(futures), lists -> {
if (CollectionUtils.isEmpty(lists)) {
return Collections.emptyList(); return Collections.emptyList();
} }
return list.stream().distinct().sorted().collect(Collectors.toList()); return lists.stream().flatMap(List::stream).distinct().sorted().collect(Collectors.toList());
}, dbCallbackExecutor); }, dbCallbackExecutor);
} else { } else {
attributesKeysFuture = null; attributesKeysFuture = null;

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

@ -36,6 +36,7 @@ import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.common.util.ThingsBoardExecutors;
import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.cluster.TbClusterService;
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.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceIdInfo; import org.thingsboard.server.common.data.DeviceIdInfo;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
@ -585,7 +586,7 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService<Dev
ListenableFuture<List<TsKvEntry>> tsData = tsService.findLatest(TenantId.SYS_TENANT_ID, device.getId(), PERSISTENT_ATTRIBUTES); ListenableFuture<List<TsKvEntry>> tsData = tsService.findLatest(TenantId.SYS_TENANT_ID, device.getId(), PERSISTENT_ATTRIBUTES);
future = Futures.transform(tsData, extractDeviceStateData(device), deviceStateExecutor); future = Futures.transform(tsData, extractDeviceStateData(device), deviceStateExecutor);
} else { } else {
ListenableFuture<List<AttributeKvEntry>> attrData = attributesService.find(TenantId.SYS_TENANT_ID, device.getId(), SERVER_SCOPE, PERSISTENT_ATTRIBUTES); ListenableFuture<List<AttributeKvEntry>> attrData = attributesService.find(TenantId.SYS_TENANT_ID, device.getId(), AttributeScope.SERVER_SCOPE, PERSISTENT_ATTRIBUTES);
future = Futures.transform(attrData, extractDeviceStateData(device), deviceStateExecutor); future = Futures.transform(attrData, extractDeviceStateData(device), deviceStateExecutor);
} }
return transformInactivityTimeout(future); return transformInactivityTimeout(future);
@ -596,7 +597,7 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService<Dev
if (!persistToTelemetry || deviceStateData.getState().getInactivityTimeout() != defaultInactivityTimeoutMs) { if (!persistToTelemetry || deviceStateData.getState().getInactivityTimeout() != defaultInactivityTimeoutMs) {
return future; //fail fast return future; //fail fast
} }
var attributesFuture = attributesService.find(TenantId.SYS_TENANT_ID, deviceStateData.getDeviceId(), SERVER_SCOPE, INACTIVITY_TIMEOUT); var attributesFuture = attributesService.find(TenantId.SYS_TENANT_ID, deviceStateData.getDeviceId(), AttributeScope.SERVER_SCOPE, INACTIVITY_TIMEOUT);
return Futures.transform(attributesFuture, attributes -> { return Futures.transform(attributesFuture, attributes -> {
attributes.flatMap(KvEntry::getLongValue).ifPresent((inactivityTimeout) -> { attributes.flatMap(KvEntry::getLongValue).ifPresent((inactivityTimeout) -> {
if (inactivityTimeout > 0) { if (inactivityTimeout > 0) {

3
application/src/main/java/org/thingsboard/server/service/subscription/DefaultSubscriptionManagerService.java

@ -23,6 +23,7 @@ import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.cluster.TbClusterService;
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.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmInfo;
@ -496,7 +497,7 @@ public class DefaultSubscriptionManagerService extends TbApplicationEventListene
serviceId, subscription.getSessionId(), subscription.getSubscriptionId(), subscription.getEntityId()); serviceId, subscription.getSessionId(), subscription.getSubscriptionId(), subscription.getEntityId());
final Map<String, Long> keyStates = subscription.getKeyStates(); final Map<String, Long> keyStates = subscription.getKeyStates();
DonAsynchron.withCallback(attrService.find(subscription.getTenantId(), subscription.getEntityId(), DataConstants.CLIENT_SCOPE, keyStates.keySet()), values -> { DonAsynchron.withCallback(attrService.find(subscription.getTenantId(), subscription.getEntityId(), AttributeScope.CLIENT_SCOPE, keyStates.keySet()), values -> {
List<TsKvEntry> missedUpdates = new ArrayList<>(); List<TsKvEntry> missedUpdates = new ArrayList<>();
values.forEach(latestEntry -> { values.forEach(latestEntry -> {
if (latestEntry.getLastUpdateTs() > keyStates.get(latestEntry.getKey())) { if (latestEntry.getLastUpdateTs() > keyStates.get(latestEntry.getKey())) {

3
application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractSubCtx.java

@ -22,6 +22,7 @@ import lombok.Data;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
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;
@ -215,7 +216,7 @@ public abstract class TbAbstractSubCtx<T extends EntityCountQuery> {
private ListenableFuture<DynamicValueKeySub> resolveEntityValue(TenantId tenantId, EntityId entityId, DynamicValueKey key) { private ListenableFuture<DynamicValueKeySub> resolveEntityValue(TenantId tenantId, EntityId entityId, DynamicValueKey key) {
ListenableFuture<Optional<AttributeKvEntry>> entry = attributesService.find(tenantId, entityId, ListenableFuture<Optional<AttributeKvEntry>> entry = attributesService.find(tenantId, entityId,
TbAttributeSubscriptionScope.SERVER_SCOPE.name(), key.getSourceAttribute()); AttributeScope.SERVER_SCOPE, key.getSourceAttribute());
return Futures.transform(entry, attributeOpt -> { return Futures.transform(entry, attributeOpt -> {
DynamicValueKeySub sub = new DynamicValueKeySub(key, entityId); DynamicValueKeySub sub = new DynamicValueKeySub(key, entityId);
if (attributeOpt.isPresent()) { if (attributeOpt.isPresent()) {

10
application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/DefaultEntityExportService.java

@ -19,7 +19,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.DataConstants; 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.ExportableEntity; import org.thingsboard.server.common.data.ExportableEntity;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
@ -108,16 +108,16 @@ public class DefaultEntityExportService<I extends EntityId, E extends Exportable
} }
private Map<String, List<AttributeExportData>> exportAttributes(EntitiesExportCtx<?> ctx, E entity) throws ThingsboardException { private Map<String, List<AttributeExportData>> exportAttributes(EntitiesExportCtx<?> ctx, E entity) throws ThingsboardException {
List<String> scopes; List<AttributeScope> scopes;
if (entity.getId().getEntityType() == EntityType.DEVICE) { if (entity.getId().getEntityType() == EntityType.DEVICE) {
scopes = List.of(DataConstants.SERVER_SCOPE, DataConstants.SHARED_SCOPE); scopes = List.of(AttributeScope.SERVER_SCOPE, AttributeScope.SHARED_SCOPE);
} else { } else {
scopes = Collections.singletonList(DataConstants.SERVER_SCOPE); scopes = Collections.singletonList(AttributeScope.SERVER_SCOPE);
} }
Map<String, List<AttributeExportData>> attributes = new LinkedHashMap<>(); Map<String, List<AttributeExportData>> attributes = new LinkedHashMap<>();
scopes.forEach(scope -> { scopes.forEach(scope -> {
try { try {
attributes.put(scope, attributesService.findAll(ctx.getTenantId(), entity.getId(), scope).get().stream() attributes.put(scope.name(), attributesService.findAll(ctx.getTenantId(), entity.getId(), scope).get().stream()
.map(attribute -> { .map(attribute -> {
AttributeExportData attributeExportData = new AttributeExportData(); AttributeExportData attributeExportData = new AttributeExportData();
attributeExportData.setKey(attribute.getKey()); attributeExportData.setKey(attribute.getKey());

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

@ -26,6 +26,7 @@ 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.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;
@ -248,7 +249,7 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer
@Override @Override
public void saveAndNotifyInternal(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes, boolean notifyDevice, FutureCallback<Void> callback) { public void saveAndNotifyInternal(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes, boolean notifyDevice, FutureCallback<Void> callback) {
ListenableFuture<List<String>> saveFuture = attrService.save(tenantId, entityId, scope, attributes); ListenableFuture<List<String>> saveFuture = attrService.save(tenantId, entityId, AttributeScope.valueOf(scope), attributes);
addVoidCallback(saveFuture, callback); addVoidCallback(saveFuture, callback);
addWsCallback(saveFuture, success -> onAttributesUpdate(tenantId, entityId, scope, attributes, notifyDevice)); addWsCallback(saveFuture, success -> onAttributesUpdate(tenantId, entityId, scope, attributes, notifyDevice));
} }
@ -280,7 +281,7 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer
@Override @Override
public void deleteAndNotifyInternal(TenantId tenantId, EntityId entityId, String scope, List<String> keys, boolean notifyDevice, FutureCallback<Void> callback) { 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); ListenableFuture<List<String>> deleteFuture = attrService.removeAll(tenantId, entityId, AttributeScope.valueOf(scope), keys);
addVoidCallback(deleteFuture, callback); addVoidCallback(deleteFuture, callback);
addWsCallback(deleteFuture, success -> onAttributesDelete(tenantId, entityId, scope, keys, notifyDevice)); addWsCallback(deleteFuture, success -> onAttributesDelete(tenantId, entityId, scope, keys, notifyDevice));
} }

10
application/src/main/java/org/thingsboard/server/service/ws/DefaultWebSocketService.java

@ -29,7 +29,7 @@ import org.springframework.web.socket.CloseStatus;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.common.util.ThingsBoardExecutors;
import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
@ -949,7 +949,7 @@ public class DefaultWebSocketService implements WebSocketService {
@Override @Override
public void onSuccess(@Nullable ValidationResult result) { public void onSuccess(@Nullable ValidationResult result) {
List<ListenableFuture<List<AttributeKvEntry>>> futures = new ArrayList<>(); List<ListenableFuture<List<AttributeKvEntry>>> futures = new ArrayList<>();
for (String scope : DataConstants.allScopes()) { for (AttributeScope scope : AttributeScope.values()) {
futures.add(attributesService.find(tenantId, entityId, scope, keys)); futures.add(attributesService.find(tenantId, entityId, scope, keys));
} }
@ -968,7 +968,7 @@ public class DefaultWebSocketService implements WebSocketService {
return new FutureCallback<ValidationResult>() { return new FutureCallback<ValidationResult>() {
@Override @Override
public void onSuccess(@Nullable ValidationResult result) { public void onSuccess(@Nullable ValidationResult result) {
Futures.addCallback(attributesService.find(tenantId, entityId, scope, keys), callback, MoreExecutors.directExecutor()); Futures.addCallback(attributesService.find(tenantId, entityId, AttributeScope.valueOf(scope), keys), callback, MoreExecutors.directExecutor());
} }
@Override @Override
@ -983,7 +983,7 @@ public class DefaultWebSocketService implements WebSocketService {
@Override @Override
public void onSuccess(@Nullable ValidationResult result) { public void onSuccess(@Nullable ValidationResult result) {
List<ListenableFuture<List<AttributeKvEntry>>> futures = new ArrayList<>(); List<ListenableFuture<List<AttributeKvEntry>>> futures = new ArrayList<>();
for (String scope : DataConstants.allScopes()) { for (AttributeScope scope : AttributeScope.values()) {
futures.add(attributesService.findAll(tenantId, entityId, scope)); futures.add(attributesService.findAll(tenantId, entityId, scope));
} }
@ -1002,7 +1002,7 @@ public class DefaultWebSocketService implements WebSocketService {
return new FutureCallback<ValidationResult>() { return new FutureCallback<ValidationResult>() {
@Override @Override
public void onSuccess(@Nullable ValidationResult result) { public void onSuccess(@Nullable ValidationResult result) {
Futures.addCallback(attributesService.findAll(tenantId, entityId, scope), callback, MoreExecutors.directExecutor()); Futures.addCallback(attributesService.findAll(tenantId, entityId, AttributeScope.valueOf(scope)), callback, MoreExecutors.directExecutor());
} }
@Override @Override

9
application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java

@ -31,6 +31,7 @@ import org.thingsboard.rule.engine.util.TbMsgSource;
import org.thingsboard.rule.engine.metadata.TbGetAttributesNode; import org.thingsboard.rule.engine.metadata.TbGetAttributesNode;
import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration; import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration;
import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.ActorSystemContext;
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.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EventInfo; import org.thingsboard.server.common.data.EventInfo;
@ -174,9 +175,9 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule
device.setType("default"); device.setType("default");
device = doPost("/api/device", device, Device.class); device = doPost("/api/device", device, Device.class);
attributesService.save(device.getTenantId(), device.getId(), DataConstants.SERVER_SCOPE, attributesService.save(device.getTenantId(), device.getId(), AttributeScope.SERVER_SCOPE,
Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry("serverAttributeKey1", "serverAttributeValue1"), System.currentTimeMillis()))).get(); Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry("serverAttributeKey1", "serverAttributeValue1"), System.currentTimeMillis()))).get();
attributesService.save(device.getTenantId(), device.getId(), DataConstants.SERVER_SCOPE, attributesService.save(device.getTenantId(), device.getId(), AttributeScope.SERVER_SCOPE,
Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry("serverAttributeKey2", "serverAttributeValue2"), System.currentTimeMillis()))).get(); Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry("serverAttributeKey2", "serverAttributeValue2"), System.currentTimeMillis()))).get();
TbMsgCallback tbMsgCallback = Mockito.mock(TbMsgCallback.class); TbMsgCallback tbMsgCallback = Mockito.mock(TbMsgCallback.class);
@ -299,9 +300,9 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule
device.setType("default"); device.setType("default");
device = doPost("/api/device", device, Device.class); device = doPost("/api/device", device, Device.class);
attributesService.save(device.getTenantId(), device.getId(), DataConstants.SERVER_SCOPE, attributesService.save(device.getTenantId(), device.getId(), AttributeScope.SERVER_SCOPE,
Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry("serverAttributeKey1", "serverAttributeValue1"), System.currentTimeMillis()))).get(); Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry("serverAttributeKey1", "serverAttributeValue1"), System.currentTimeMillis()))).get();
attributesService.save(device.getTenantId(), device.getId(), DataConstants.SERVER_SCOPE, attributesService.save(device.getTenantId(), device.getId(), AttributeScope.SERVER_SCOPE,
Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry("serverAttributeKey2", "serverAttributeValue2"), System.currentTimeMillis()))).get(); Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry("serverAttributeKey2", "serverAttributeValue2"), System.currentTimeMillis()))).get();
TbMsgCallback tbMsgCallback = Mockito.mock(TbMsgCallback.class); TbMsgCallback tbMsgCallback = Mockito.mock(TbMsgCallback.class);

3
application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java

@ -28,6 +28,7 @@ import org.thingsboard.rule.engine.util.TbMsgSource;
import org.thingsboard.rule.engine.metadata.TbGetAttributesNode; import org.thingsboard.rule.engine.metadata.TbGetAttributesNode;
import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration; import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration;
import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.ActorSystemContext;
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.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EventInfo; import org.thingsboard.server.common.data.EventInfo;
@ -134,7 +135,7 @@ public abstract class AbstractRuleEngineLifecycleIntegrationTest extends Abstrac
device = doPost("/api/device", device, Device.class); device = doPost("/api/device", device, Device.class);
log.warn("before update attr"); log.warn("before update attr");
attributesService.save(device.getTenantId(), device.getId(), DataConstants.SERVER_SCOPE, attributesService.save(device.getTenantId(), device.getId(), AttributeScope.SERVER_SCOPE,
Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry("serverAttributeKey", "serverAttributeValue"), System.currentTimeMillis()))) Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry("serverAttributeKey", "serverAttributeValue"), System.currentTimeMillis())))
.get(TIMEOUT, TimeUnit.SECONDS); .get(TIMEOUT, TimeUnit.SECONDS);
log.warn("attr updated"); log.warn("attr updated");

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

@ -16,6 +16,7 @@
package org.thingsboard.server.dao.attributes; package org.thingsboard.server.dao.attributes;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.DeviceProfileId;
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;
@ -30,17 +31,17 @@ import java.util.Optional;
*/ */
public interface AttributesService { public interface AttributesService {
ListenableFuture<Optional<AttributeKvEntry>> find(TenantId tenantId, EntityId entityId, String scope, String attributeKey); ListenableFuture<Optional<AttributeKvEntry>> find(TenantId tenantId, EntityId entityId, AttributeScope scope, String attributeKey);
ListenableFuture<List<AttributeKvEntry>> find(TenantId tenantId, EntityId entityId, String scope, Collection<String> attributeKeys); ListenableFuture<List<AttributeKvEntry>> find(TenantId tenantId, EntityId entityId, AttributeScope scope, Collection<String> attributeKeys);
ListenableFuture<List<AttributeKvEntry>> findAll(TenantId tenantId, EntityId entityId, String scope); ListenableFuture<List<AttributeKvEntry>> findAll(TenantId tenantId, EntityId entityId, AttributeScope scope);
ListenableFuture<List<String>> save(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes); ListenableFuture<List<String>> save(TenantId tenantId, EntityId entityId, AttributeScope scope, List<AttributeKvEntry> attributes);
ListenableFuture<String> save(TenantId tenantId, EntityId entityId, String scope, AttributeKvEntry attribute); ListenableFuture<String> save(TenantId tenantId, EntityId entityId, AttributeScope scope, AttributeKvEntry attribute);
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);
List<String> findAllKeysByDeviceProfileId(TenantId tenantId, DeviceProfileId deviceProfileId); List<String> findAllKeysByDeviceProfileId(TenantId tenantId, DeviceProfileId deviceProfileId);

6
common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/importing/csv/BulkImportColumnType.java

@ -16,7 +16,7 @@
package org.thingsboard.server.common.data.sync.ie.importing.csv; package org.thingsboard.server.common.data.sync.ie.importing.csv;
import lombok.Getter; import lombok.Getter;
import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode; import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode;
@Getter @Getter
@ -24,8 +24,8 @@ public enum BulkImportColumnType {
NAME, NAME,
TYPE, TYPE,
LABEL, LABEL,
SHARED_ATTRIBUTE(DataConstants.SHARED_SCOPE, true), SHARED_ATTRIBUTE(AttributeScope.SHARED_SCOPE.name(), true),
SERVER_ATTRIBUTE(DataConstants.SERVER_SCOPE, true), SERVER_ATTRIBUTE(AttributeScope.SERVER_SCOPE.name(), true),
TIMESERIES(true), TIMESERIES(true),
ACCESS_TOKEN, ACCESS_TOKEN,
X509, X509,

3
dao/src/main/java/org/thingsboard/server/dao/attributes/AttributeCacheKey.java

@ -18,6 +18,7 @@ package org.thingsboard.server.dao.attributes;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.Getter; import lombok.Getter;
import org.thingsboard.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import java.io.Serializable; import java.io.Serializable;
@ -28,7 +29,7 @@ import java.io.Serializable;
public class AttributeCacheKey implements Serializable { public class AttributeCacheKey implements Serializable {
private static final long serialVersionUID = 2013369077925351881L; private static final long serialVersionUID = 2013369077925351881L;
private final String scope; private final AttributeScope scope;
private final EntityId entityId; private final EntityId entityId;
private final String key; private final String key;

4
dao/src/main/java/org/thingsboard/server/dao/attributes/AttributeUtils.java

@ -26,9 +26,9 @@ import java.util.List;
public class AttributeUtils { public class AttributeUtils {
public static void validate(EntityId id, String scope) { public static void validate(EntityId id, AttributeScope scope) {
Validator.validateId(id.getId(), "Incorrect id " + id); Validator.validateId(id.getId(), "Incorrect id " + id);
Validator.validateEnum(AttributeScope.class, scope, "Incorrect scope " + scope); Validator.checkNotNull(scope, "Incorrect scope " + scope);
} }
public static void validate(List<AttributeKvEntry> kvEntries, boolean valueNoXssValidation) { public static void validate(List<AttributeKvEntry> kvEntries, boolean valueNoXssValidation) {

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

@ -54,23 +54,23 @@ public class BaseAttributesService implements AttributesService {
} }
@Override @Override
public ListenableFuture<Optional<AttributeKvEntry>> find(TenantId tenantId, EntityId entityId, String scope, String attributeKey) { public ListenableFuture<Optional<AttributeKvEntry>> find(TenantId tenantId, EntityId entityId, AttributeScope scope, String attributeKey) {
validate(entityId, scope); validate(entityId, scope);
Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey); Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey);
return Futures.immediateFuture(attributesDao.find(tenantId, entityId, AttributeScope.valueOf(scope), attributeKey)); return Futures.immediateFuture(attributesDao.find(tenantId, entityId, scope, attributeKey));
} }
@Override @Override
public ListenableFuture<List<AttributeKvEntry>> find(TenantId tenantId, EntityId entityId, String scope, Collection<String> attributeKeys) { public ListenableFuture<List<AttributeKvEntry>> find(TenantId tenantId, EntityId entityId, AttributeScope scope, Collection<String> attributeKeys) {
validate(entityId, scope); validate(entityId, scope);
attributeKeys.forEach(attributeKey -> Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey)); attributeKeys.forEach(attributeKey -> Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey));
return Futures.immediateFuture(attributesDao.find(tenantId, entityId, AttributeScope.valueOf(scope), attributeKeys)); return Futures.immediateFuture(attributesDao.find(tenantId, entityId, scope, attributeKeys));
} }
@Override @Override
public ListenableFuture<List<AttributeKvEntry>> findAll(TenantId tenantId, EntityId entityId, String scope) { public ListenableFuture<List<AttributeKvEntry>> findAll(TenantId tenantId, EntityId entityId, AttributeScope scope) {
validate(entityId, scope); validate(entityId, scope);
return Futures.immediateFuture(attributesDao.findAll(tenantId, entityId, AttributeScope.valueOf(scope))); return Futures.immediateFuture(attributesDao.findAll(tenantId, entityId, scope));
} }
@Override @Override
@ -84,23 +84,23 @@ public class BaseAttributesService implements AttributesService {
} }
@Override @Override
public ListenableFuture<String> save(TenantId tenantId, EntityId entityId, String scope, AttributeKvEntry attribute) { public ListenableFuture<String> save(TenantId tenantId, EntityId entityId, AttributeScope scope, AttributeKvEntry attribute) {
validate(entityId, scope); validate(entityId, scope);
AttributeUtils.validate(attribute, valueNoXssValidation); AttributeUtils.validate(attribute, valueNoXssValidation);
return attributesDao.save(tenantId, entityId, AttributeScope.valueOf(scope), attribute); return attributesDao.save(tenantId, entityId, scope, attribute);
} }
@Override @Override
public ListenableFuture<List<String>> save(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes) { public ListenableFuture<List<String>> save(TenantId tenantId, EntityId entityId, AttributeScope scope, List<AttributeKvEntry> attributes) {
validate(entityId, scope); validate(entityId, scope);
AttributeUtils.validate(attributes, valueNoXssValidation); AttributeUtils.validate(attributes, valueNoXssValidation);
List<ListenableFuture<String>> saveFutures = attributes.stream().map(attribute -> attributesDao.save(tenantId, entityId, AttributeScope.valueOf(scope), attribute)).collect(Collectors.toList()); List<ListenableFuture<String>> saveFutures = attributes.stream().map(attribute -> attributesDao.save(tenantId, entityId, scope, attribute)).collect(Collectors.toList());
return Futures.allAsList(saveFutures); return Futures.allAsList(saveFutures);
} }
@Override @Override
public ListenableFuture<List<String>> removeAll(TenantId tenantId, EntityId entityId, String scope, List<String> attributeKeys) { public ListenableFuture<List<String>> removeAll(TenantId tenantId, EntityId entityId, AttributeScope scope, List<String> attributeKeys) {
validate(entityId, scope); validate(entityId, scope);
return Futures.allAsList(attributesDao.removeAll(tenantId, entityId, AttributeScope.valueOf(scope), attributeKeys)); return Futures.allAsList(attributesDao.removeAll(tenantId, entityId, scope, attributeKeys));
} }
} }

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

@ -105,7 +105,7 @@ public class CachedAttributesService implements AttributesService {
@Override @Override
public ListenableFuture<Optional<AttributeKvEntry>> find(TenantId tenantId, EntityId entityId, String scope, String attributeKey) { public ListenableFuture<Optional<AttributeKvEntry>> find(TenantId tenantId, EntityId entityId, AttributeScope scope, String attributeKey) {
validate(entityId, scope); validate(entityId, scope);
Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey); Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey);
@ -120,7 +120,7 @@ public class CachedAttributesService implements AttributesService {
return cacheExecutor.submit(() -> { return cacheExecutor.submit(() -> {
var cacheTransaction = cache.newTransactionForKey(attributeCacheKey); var cacheTransaction = cache.newTransactionForKey(attributeCacheKey);
try { try {
Optional<AttributeKvEntry> result = attributesDao.find(tenantId, entityId, AttributeScope.valueOf(scope), attributeKey); Optional<AttributeKvEntry> result = attributesDao.find(tenantId, entityId, scope, attributeKey);
cacheTransaction.putIfAbsent(attributeCacheKey, result.orElse(null)); cacheTransaction.putIfAbsent(attributeCacheKey, result.orElse(null));
cacheTransaction.commit(); cacheTransaction.commit();
return result; return result;
@ -134,7 +134,7 @@ public class CachedAttributesService implements AttributesService {
} }
@Override @Override
public ListenableFuture<List<AttributeKvEntry>> find(TenantId tenantId, EntityId entityId, String scope, Collection<String> attributeKeys) { public ListenableFuture<List<AttributeKvEntry>> find(TenantId tenantId, EntityId entityId, AttributeScope scope, Collection<String> attributeKeys) {
validate(entityId, scope); validate(entityId, scope);
attributeKeys = new LinkedHashSet<>(attributeKeys); // deduplicate the attributes attributeKeys = new LinkedHashSet<>(attributeKeys); // deduplicate the attributes
attributeKeys.forEach(attributeKey -> Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey)); attributeKeys.forEach(attributeKey -> Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey));
@ -159,7 +159,7 @@ public class CachedAttributesService implements AttributesService {
var cacheTransaction = cache.newTransactionForKeys(notFoundKeys); var cacheTransaction = cache.newTransactionForKeys(notFoundKeys);
try { try {
log.trace("[{}][{}] Lookup attributes from db: {}", entityId, scope, notFoundAttributeKeys); log.trace("[{}][{}] Lookup attributes from db: {}", entityId, scope, notFoundAttributeKeys);
List<AttributeKvEntry> result = attributesDao.find(tenantId, entityId, AttributeScope.valueOf(scope), notFoundAttributeKeys); List<AttributeKvEntry> result = attributesDao.find(tenantId, entityId, scope, notFoundAttributeKeys);
for (AttributeKvEntry foundInDbAttribute : result) { for (AttributeKvEntry foundInDbAttribute : result) {
AttributeCacheKey attributeCacheKey = new AttributeCacheKey(scope, entityId, foundInDbAttribute.getKey()); AttributeCacheKey attributeCacheKey = new AttributeCacheKey(scope, entityId, foundInDbAttribute.getKey());
cacheTransaction.putIfAbsent(attributeCacheKey, foundInDbAttribute); cacheTransaction.putIfAbsent(attributeCacheKey, foundInDbAttribute);
@ -181,7 +181,7 @@ public class CachedAttributesService implements AttributesService {
}); });
} }
private Map<String, TbCacheValueWrapper<AttributeKvEntry>> findCachedAttributes(EntityId entityId, String scope, Collection<String> attributeKeys) { private Map<String, TbCacheValueWrapper<AttributeKvEntry>> findCachedAttributes(EntityId entityId, AttributeScope scope, Collection<String> attributeKeys) {
Map<String, TbCacheValueWrapper<AttributeKvEntry>> cachedAttributes = new HashMap<>(); Map<String, TbCacheValueWrapper<AttributeKvEntry>> cachedAttributes = new HashMap<>();
for (String attributeKey : attributeKeys) { for (String attributeKey : attributeKeys) {
var cachedAttributeValue = cache.get(new AttributeCacheKey(scope, entityId, attributeKey)); var cachedAttributeValue = cache.get(new AttributeCacheKey(scope, entityId, attributeKey));
@ -196,9 +196,9 @@ public class CachedAttributesService implements AttributesService {
} }
@Override @Override
public ListenableFuture<List<AttributeKvEntry>> findAll(TenantId tenantId, EntityId entityId, String scope) { public ListenableFuture<List<AttributeKvEntry>> findAll(TenantId tenantId, EntityId entityId, AttributeScope scope) {
validate(entityId, scope); validate(entityId, scope);
return Futures.immediateFuture(attributesDao.findAll(tenantId, entityId, AttributeScope.valueOf(scope))); return Futures.immediateFuture(attributesDao.findAll(tenantId, entityId, scope));
} }
@Override @Override
@ -212,28 +212,28 @@ public class CachedAttributesService implements AttributesService {
} }
@Override @Override
public ListenableFuture<String> save(TenantId tenantId, EntityId entityId, String scope, AttributeKvEntry attribute) { public ListenableFuture<String> save(TenantId tenantId, EntityId entityId, AttributeScope scope, AttributeKvEntry attribute) {
validate(entityId, scope); validate(entityId, scope);
AttributeUtils.validate(attribute, valueNoXssValidation); AttributeUtils.validate(attribute, valueNoXssValidation);
ListenableFuture<String> future = attributesDao.save(tenantId, entityId, AttributeScope.valueOf(scope), attribute); ListenableFuture<String> future = attributesDao.save(tenantId, entityId, scope, attribute);
return Futures.transform(future, key -> evict(entityId, scope, attribute, key), cacheExecutor); return Futures.transform(future, key -> evict(entityId, scope, attribute, key), cacheExecutor);
} }
@Override @Override
public ListenableFuture<List<String>> save(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes) { public ListenableFuture<List<String>> save(TenantId tenantId, EntityId entityId, AttributeScope scope, List<AttributeKvEntry> attributes) {
validate(entityId, scope); validate(entityId, scope);
AttributeUtils.validate(attributes, valueNoXssValidation); AttributeUtils.validate(attributes, valueNoXssValidation);
List<ListenableFuture<String>> futures = new ArrayList<>(attributes.size()); List<ListenableFuture<String>> futures = new ArrayList<>(attributes.size());
for (var attribute : attributes) { for (var attribute : attributes) {
ListenableFuture<String> future = attributesDao.save(tenantId, entityId, AttributeScope.valueOf(scope), attribute); ListenableFuture<String> future = attributesDao.save(tenantId, entityId, scope, attribute);
futures.add(Futures.transform(future, key -> evict(entityId, scope, attribute, key), cacheExecutor)); futures.add(Futures.transform(future, key -> evict(entityId, scope, attribute, key), cacheExecutor));
} }
return Futures.allAsList(futures); return Futures.allAsList(futures);
} }
private String evict(EntityId entityId, String scope, AttributeKvEntry attribute, String key) { private String evict(EntityId entityId, AttributeScope scope, AttributeKvEntry attribute, String key) {
log.trace("[{}][{}][{}] Before cache evict: {}", entityId, scope, key, attribute); log.trace("[{}][{}][{}] Before cache evict: {}", entityId, scope, key, attribute);
cache.evictOrPut(new AttributeCacheKey(scope, entityId, key), attribute); cache.evictOrPut(new AttributeCacheKey(scope, entityId, key), attribute);
log.trace("[{}][{}][{}] after cache evict.", entityId, scope, key); log.trace("[{}][{}][{}] after cache evict.", entityId, scope, key);
@ -241,9 +241,9 @@ public class CachedAttributesService implements AttributesService {
} }
@Override @Override
public ListenableFuture<List<String>> removeAll(TenantId tenantId, EntityId entityId, String scope, List<String> attributeKeys) { public ListenableFuture<List<String>> removeAll(TenantId tenantId, EntityId entityId, AttributeScope scope, List<String> attributeKeys) {
validate(entityId, scope); validate(entityId, scope);
List<ListenableFuture<String>> futures = attributesDao.removeAll(tenantId, entityId, AttributeScope.valueOf(scope), attributeKeys); List<ListenableFuture<String>> futures = attributesDao.removeAll(tenantId, entityId, scope, attributeKeys);
return Futures.allAsList(futures.stream().map(future -> Futures.transform(future, key -> { return Futures.allAsList(futures.stream().map(future -> Futures.transform(future, key -> {
cache.evict(new AttributeCacheKey(scope, entityId, key)); cache.evict(new AttributeCacheKey(scope, entityId, key));
return key; return key;

2
dao/src/main/java/org/thingsboard/server/dao/model/sql/AttributeKvDictionary.java → dao/src/main/java/org/thingsboard/server/dao/model/sql/AttributeKvDictionaryEntry.java

@ -28,7 +28,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.KEY_ID_COLUMN;
@Data @Data
@Entity @Entity
@Table(name = "attribute_kv_dictionary") @Table(name = "attribute_kv_dictionary")
public final class AttributeKvDictionary { public final class AttributeKvDictionaryEntry {
@Id @Id
@Column(name = KEY_COLUMN) @Column(name = KEY_COLUMN)

14
dao/src/main/java/org/thingsboard/server/dao/service/Validator.java

@ -15,7 +15,6 @@
*/ */
package org.thingsboard.server.dao.service; package org.thingsboard.server.dao.service;
import org.apache.commons.lang3.EnumUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.thingsboard.common.util.RegexUtils; import org.thingsboard.common.util.RegexUtils;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
@ -60,19 +59,6 @@ public class Validator {
} }
} }
/**
* This method validate <code>String</code> string. If string is null or can not be cast to enum than throw
* <code>IncorrectParameterException</code> exception
*
* @param val the val
* @param errorMessage the error message for exception
*/
public static <E extends Enum<E>> void validateEnum(Class<E> enumClass, String val, String errorMessage) {
if (val == null || !EnumUtils.isValidEnum(enumClass, val)) {
throw new IncorrectParameterException(errorMessage);
}
}
/** /**
* This method validate <code>long</code> value. If value isn't possitive than throw * This method validate <code>long</code> value. If value isn't possitive than throw

8
dao/src/main/java/org/thingsboard/server/dao/sql/attributes/AttributeKvDictionaryRepository.java

@ -16,14 +16,12 @@
package org.thingsboard.server.dao.sql.attributes; package org.thingsboard.server.dao.sql.attributes;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.thingsboard.server.dao.model.sql.AttributeKvDictionary; import org.thingsboard.server.dao.model.sql.AttributeKvDictionaryEntry;
import org.thingsboard.server.dao.util.SqlTsOrTsLatestAnyDao;
import java.util.Optional; import java.util.Optional;
@SqlTsOrTsLatestAnyDao public interface AttributeKvDictionaryRepository extends JpaRepository<AttributeKvDictionaryEntry, String> {
public interface AttributeKvDictionaryRepository extends JpaRepository<AttributeKvDictionary, String> {
Optional<AttributeKvDictionary> findByKeyId(int keyId); Optional<AttributeKvDictionaryEntry> findByKeyId(int keyId);
} }

16
dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java

@ -36,7 +36,7 @@ import org.thingsboard.server.common.stats.StatsFactory;
import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.attributes.AttributesDao; import org.thingsboard.server.dao.attributes.AttributesDao;
import org.thingsboard.server.dao.model.sql.AttributeKvCompositeKey; import org.thingsboard.server.dao.model.sql.AttributeKvCompositeKey;
import org.thingsboard.server.dao.model.sql.AttributeKvDictionary; import org.thingsboard.server.dao.model.sql.AttributeKvDictionaryEntry;
import org.thingsboard.server.dao.model.sql.AttributeKvEntity; import org.thingsboard.server.dao.model.sql.AttributeKvEntity;
import org.thingsboard.server.dao.sql.JpaAbstractDaoListeningExecutorService; import org.thingsboard.server.dao.sql.JpaAbstractDaoListeningExecutorService;
import org.thingsboard.server.dao.sql.ScheduledLogExecutorComponent; import org.thingsboard.server.dao.sql.ScheduledLogExecutorComponent;
@ -215,21 +215,21 @@ public class JpaAttributeDao extends JpaAbstractDaoListeningExecutorService impl
private Integer getOrSaveKeyId(String attributeKey) { private Integer getOrSaveKeyId(String attributeKey) {
Integer keyId = attributeDictionaryMap.get(attributeKey); Integer keyId = attributeDictionaryMap.get(attributeKey);
if (keyId == null) { if (keyId == null) {
Optional<AttributeKvDictionary> byIdOptional = dictionaryRepository.findById(attributeKey); Optional<AttributeKvDictionaryEntry> byIdOptional = dictionaryRepository.findById(attributeKey);
if (byIdOptional.isEmpty()) { if (byIdOptional.isEmpty()) {
attributeCreationLock.lock(); attributeCreationLock.lock();
try { try {
byIdOptional = dictionaryRepository.findById(attributeKey); byIdOptional = dictionaryRepository.findById(attributeKey);
if (byIdOptional.isEmpty()) { if (byIdOptional.isEmpty()) {
AttributeKvDictionary attributeKvDictionary = new AttributeKvDictionary(); AttributeKvDictionaryEntry attributeKvDictionaryEntry = new AttributeKvDictionaryEntry();
attributeKvDictionary.setKey(attributeKey); attributeKvDictionaryEntry.setKey(attributeKey);
try { try {
AttributeKvDictionary saved = dictionaryRepository.save(attributeKvDictionary); AttributeKvDictionaryEntry saved = dictionaryRepository.save(attributeKvDictionaryEntry);
attributeDictionaryMap.put(saved.getKey(), saved.getKeyId()); attributeDictionaryMap.put(saved.getKey(), saved.getKeyId());
keyId = saved.getKeyId(); keyId = saved.getKeyId();
} catch (DataIntegrityViolationException | ConstraintViolationException e) { } catch (DataIntegrityViolationException | ConstraintViolationException e) {
byIdOptional = dictionaryRepository.findById(attributeKey); byIdOptional = dictionaryRepository.findById(attributeKey);
AttributeKvDictionary dictionary = byIdOptional.orElseThrow(() -> new RuntimeException("Failed to get AttributeKvDictionary entity from DB!")); AttributeKvDictionaryEntry dictionary = byIdOptional.orElseThrow(() -> new RuntimeException("Failed to get AttributeKvDictionary entity from DB!"));
attributeDictionaryMap.put(dictionary.getKey(), dictionary.getKeyId()); attributeDictionaryMap.put(dictionary.getKey(), dictionary.getKeyId());
keyId = dictionary.getKeyId(); keyId = dictionary.getKeyId();
} }
@ -247,7 +247,7 @@ public class JpaAttributeDao extends JpaAbstractDaoListeningExecutorService impl
return keyId; return keyId;
} }
private String getKey(Integer attributeKey) { private String getKey(Integer attributeKey) {
Optional<AttributeKvDictionary> byKeyId = dictionaryRepository.findByKeyId(attributeKey); Optional<AttributeKvDictionaryEntry> byKeyId = dictionaryRepository.findByKeyId(attributeKey);
return byKeyId.map(AttributeKvDictionary::getKey).orElse(null); return byKeyId.map(AttributeKvDictionaryEntry::getKey).orElse(null);
} }
} }

72
dao/src/main/resources/sql/schema-views-and-functions.sql

@ -291,3 +291,75 @@ CREATE OR REPLACE VIEW widget_type_info_view AS
SELECT t.* SELECT t.*
, COALESCE((t.descriptor::json->>'type')::text, '') as widget_type , COALESCE((t.descriptor::json->>'type')::text, '') as widget_type
FROM widget_type t; FROM widget_type t;
CREATE OR REPLACE PROCEDURE cleanup_timeseries_by_ttl(IN null_uuid uuid,
IN system_ttl bigint, INOUT deleted bigint)
LANGUAGE plpgsql AS
$$
DECLARE
tenant_cursor CURSOR FOR select tenant.id as tenant_id
from tenant;
tenant_id_record uuid;
customer_id_record uuid;
tenant_ttl bigint;
customer_ttl bigint;
deleted_for_entities bigint;
tenant_ttl_ts bigint;
customer_ttl_ts bigint;
BEGIN
OPEN tenant_cursor;
FETCH tenant_cursor INTO tenant_id_record;
WHILE FOUND
LOOP
EXECUTE format(
'select attribute_kv.long_v from attribute_kv where attribute_kv.entity_id = %L and attribute_kv.attribute_key = (select key_id from attribute_kv_dictionary where key = %L)',
tenant_id_record, 'TTL') INTO tenant_ttl;
if tenant_ttl IS NULL THEN
tenant_ttl := system_ttl;
END IF;
IF tenant_ttl > 0 THEN
tenant_ttl_ts := (EXTRACT(EPOCH FROM current_timestamp) * 1000 - tenant_ttl::bigint * 1000)::bigint;
deleted_for_entities := delete_device_records_from_ts_kv(tenant_id_record, null_uuid, tenant_ttl_ts);
deleted := deleted + deleted_for_entities;
RAISE NOTICE '% telemetry removed for devices where tenant_id = %', deleted_for_entities, tenant_id_record;
deleted_for_entities := delete_asset_records_from_ts_kv(tenant_id_record, null_uuid, tenant_ttl_ts);
deleted := deleted + deleted_for_entities;
RAISE NOTICE '% telemetry removed for assets where tenant_id = %', deleted_for_entities, tenant_id_record;
END IF;
FOR customer_id_record IN
SELECT customer.id AS customer_id FROM customer WHERE customer.tenant_id = tenant_id_record
LOOP
EXECUTE format(
'select attribute_kv.long_v from attribute_kv where attribute_kv.entity_id = %L and attribute_kv.attribute_key = (select key_id from attribute_kv_dictionary where key = %L)',
customer_id_record, 'TTL') INTO customer_ttl;
IF customer_ttl IS NULL THEN
customer_ttl_ts := tenant_ttl_ts;
ELSE
IF customer_ttl > 0 THEN
customer_ttl_ts :=
(EXTRACT(EPOCH FROM current_timestamp) * 1000 -
customer_ttl::bigint * 1000)::bigint;
END IF;
END IF;
IF customer_ttl_ts IS NOT NULL AND customer_ttl_ts > 0 THEN
deleted_for_entities :=
delete_customer_records_from_ts_kv(tenant_id_record, customer_id_record,
customer_ttl_ts);
deleted := deleted + deleted_for_entities;
RAISE NOTICE '% telemetry removed for customer with id = % where tenant_id = %', deleted_for_entities, customer_id_record, tenant_id_record;
deleted_for_entities :=
delete_device_records_from_ts_kv(tenant_id_record, customer_id_record,
customer_ttl_ts);
deleted := deleted + deleted_for_entities;
RAISE NOTICE '% telemetry removed for devices where tenant_id = % and customer_id = %', deleted_for_entities, tenant_id_record, customer_id_record;
deleted_for_entities := delete_asset_records_from_ts_kv(tenant_id_record,
customer_id_record,
customer_ttl_ts);
deleted := deleted + deleted_for_entities;
RAISE NOTICE '% telemetry removed for assets where tenant_id = % and customer_id = %', deleted_for_entities, tenant_id_record, customer_id_record;
END IF;
END LOOP;
FETCH tenant_cursor INTO tenant_id_record;
END LOOP;
END
$$;

17
dao/src/test/java/org/thingsboard/server/dao/service/EntityServiceTest.java

@ -25,6 +25,7 @@ import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.ResultSetExtractor; import org.springframework.jdbc.core.ResultSetExtractor;
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.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
@ -373,7 +374,7 @@ public class EntityServiceTest extends AbstractServiceTest {
List<ListenableFuture<List<String>>> attributeFutures = new ArrayList<>(); List<ListenableFuture<List<String>>> attributeFutures = new ArrayList<>();
for (int i = 0; i < devices.size(); i++) { for (int i = 0; i < devices.size(); i++) {
Device device = devices.get(i); Device device = devices.get(i);
attributeFutures.add(saveLongAttribute(device.getId(), "temperature", temperatures.get(i), DataConstants.CLIENT_SCOPE)); attributeFutures.add(saveLongAttribute(device.getId(), "temperature", temperatures.get(i), AttributeScope.CLIENT_SCOPE));
} }
Futures.allAsList(attributeFutures).get(); Futures.allAsList(attributeFutures).get();
@ -552,7 +553,7 @@ public class EntityServiceTest extends AbstractServiceTest {
List<ListenableFuture<List<String>>> attributeFutures = new ArrayList<>(); List<ListenableFuture<List<String>>> attributeFutures = new ArrayList<>();
for (int i = 0; i < devices.size(); i++) { for (int i = 0; i < devices.size(); i++) {
Device device = devices.get(i); Device device = devices.get(i);
attributeFutures.add(saveLongAttribute(device.getId(), "temperature", temperatures.get(i), DataConstants.CLIENT_SCOPE)); attributeFutures.add(saveLongAttribute(device.getId(), "temperature", temperatures.get(i), AttributeScope.CLIENT_SCOPE));
} }
Futures.allAsList(attributeFutures).get(); Futures.allAsList(attributeFutures).get();
@ -627,7 +628,7 @@ public class EntityServiceTest extends AbstractServiceTest {
List<ListenableFuture<List<String>>> attributeFutures = new ArrayList<>(); List<ListenableFuture<List<String>>> attributeFutures = new ArrayList<>();
for (int i = 0; i < assets.size(); i++) { for (int i = 0; i < assets.size(); i++) {
Asset asset = assets.get(i); Asset asset = assets.get(i);
attributeFutures.add(saveLongAttribute(asset.getId(), "consumption", consumptions.get(i), DataConstants.SERVER_SCOPE)); attributeFutures.add(saveLongAttribute(asset.getId(), "consumption", consumptions.get(i), AttributeScope.SERVER_SCOPE));
} }
Futures.allAsList(attributeFutures).get(); Futures.allAsList(attributeFutures).get();
@ -1436,7 +1437,7 @@ public class EntityServiceTest extends AbstractServiceTest {
List<ListenableFuture<List<String>>> attributeFutures = new ArrayList<>(); List<ListenableFuture<List<String>>> attributeFutures = new ArrayList<>();
for (int i = 0; i < devices.size(); i++) { for (int i = 0; i < devices.size(); i++) {
Device device = devices.get(i); Device device = devices.get(i);
for (String currentScope : DataConstants.allScopes()) { for (AttributeScope currentScope : AttributeScope.values()) {
attributeFutures.add(saveLongAttribute(device.getId(), "temperature", temperatures.get(i), currentScope)); attributeFutures.add(saveLongAttribute(device.getId(), "temperature", temperatures.get(i), currentScope));
} }
} }
@ -1538,7 +1539,7 @@ public class EntityServiceTest extends AbstractServiceTest {
List<ListenableFuture<List<String>>> attributeFutures = new ArrayList<>(); List<ListenableFuture<List<String>>> attributeFutures = new ArrayList<>();
for (int i = 0; i < devices.size(); i++) { for (int i = 0; i < devices.size(); i++) {
Device device = devices.get(i); Device device = devices.get(i);
attributeFutures.add(saveLongAttribute(device.getId(), "temperature", temperatures.get(i), DataConstants.CLIENT_SCOPE)); attributeFutures.add(saveLongAttribute(device.getId(), "temperature", temperatures.get(i), AttributeScope.CLIENT_SCOPE));
} }
Futures.allAsList(attributeFutures).get(); Futures.allAsList(attributeFutures).get();
@ -1816,7 +1817,7 @@ public class EntityServiceTest extends AbstractServiceTest {
List<ListenableFuture<List<String>>> attributeFutures = new ArrayList<>(); List<ListenableFuture<List<String>>> attributeFutures = new ArrayList<>();
for (int i = 0; i < devices.size(); i++) { for (int i = 0; i < devices.size(); i++) {
Device device = devices.get(i); Device device = devices.get(i);
attributeFutures.add(saveStringAttribute(device.getId(), "attributeString", attributeStrings.get(i), DataConstants.CLIENT_SCOPE)); attributeFutures.add(saveStringAttribute(device.getId(), "attributeString", attributeStrings.get(i), AttributeScope.CLIENT_SCOPE));
} }
Futures.allAsList(attributeFutures).get(); Futures.allAsList(attributeFutures).get();
@ -2149,13 +2150,13 @@ public class EntityServiceTest extends AbstractServiceTest {
return filter; return filter;
} }
private ListenableFuture<List<String>> saveLongAttribute(EntityId entityId, String key, long value, String scope) { private ListenableFuture<List<String>> saveLongAttribute(EntityId entityId, String key, long value, AttributeScope scope) {
KvEntry attrValue = new LongDataEntry(key, value); KvEntry attrValue = new LongDataEntry(key, value);
AttributeKvEntry attr = new BaseAttributeKvEntry(attrValue, 42L); AttributeKvEntry attr = new BaseAttributeKvEntry(attrValue, 42L);
return attributesService.save(SYSTEM_TENANT_ID, entityId, scope, Collections.singletonList(attr)); return attributesService.save(SYSTEM_TENANT_ID, entityId, scope, Collections.singletonList(attr));
} }
private ListenableFuture<List<String>> saveStringAttribute(EntityId entityId, String key, String value, String scope) { private ListenableFuture<List<String>> saveStringAttribute(EntityId entityId, String key, String value, AttributeScope scope) {
KvEntry attrValue = new StringDataEntry(key, value); KvEntry attrValue = new StringDataEntry(key, value);
AttributeKvEntry attr = new BaseAttributeKvEntry(attrValue, 42L); AttributeKvEntry attr = new BaseAttributeKvEntry(attrValue, 42L);
return attributesService.save(SYSTEM_TENANT_ID, entityId, scope, Collections.singletonList(attr)); return attributesService.save(SYSTEM_TENANT_ID, entityId, scope, Collections.singletonList(attr));

40
dao/src/test/java/org/thingsboard/server/dao/service/attributes/BaseAttributesServiceTest.java

@ -26,7 +26,7 @@ import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.thingsboard.server.cache.TbTransactionalCache; import org.thingsboard.server.cache.TbTransactionalCache;
import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.AttributeKvEntry;
@ -71,8 +71,8 @@ public abstract class BaseAttributesServiceTest extends AbstractServiceTest {
DeviceId deviceId = new DeviceId(Uuids.timeBased()); DeviceId deviceId = new DeviceId(Uuids.timeBased());
KvEntry attrValue = new StringDataEntry("attribute1", "value1"); KvEntry attrValue = new StringDataEntry("attribute1", "value1");
AttributeKvEntry attr = new BaseAttributeKvEntry(attrValue, 42L); AttributeKvEntry attr = new BaseAttributeKvEntry(attrValue, 42L);
attributesService.save(SYSTEM_TENANT_ID, deviceId, DataConstants.CLIENT_SCOPE, Collections.singletonList(attr)).get(); attributesService.save(SYSTEM_TENANT_ID, deviceId, AttributeScope.CLIENT_SCOPE, Collections.singletonList(attr)).get();
Optional<AttributeKvEntry> saved = attributesService.find(SYSTEM_TENANT_ID, deviceId, DataConstants.CLIENT_SCOPE, attr.getKey()).get(); Optional<AttributeKvEntry> saved = attributesService.find(SYSTEM_TENANT_ID, deviceId, AttributeScope.CLIENT_SCOPE, attr.getKey()).get();
Assert.assertTrue(saved.isPresent()); Assert.assertTrue(saved.isPresent());
Assert.assertEquals(attr, saved.get()); Assert.assertEquals(attr, saved.get());
} }
@ -83,17 +83,17 @@ public abstract class BaseAttributesServiceTest extends AbstractServiceTest {
KvEntry attrOldValue = new StringDataEntry("attribute1", "value1"); KvEntry attrOldValue = new StringDataEntry("attribute1", "value1");
AttributeKvEntry attrOld = new BaseAttributeKvEntry(attrOldValue, 42L); AttributeKvEntry attrOld = new BaseAttributeKvEntry(attrOldValue, 42L);
attributesService.save(SYSTEM_TENANT_ID, deviceId, DataConstants.CLIENT_SCOPE, Collections.singletonList(attrOld)).get(); attributesService.save(SYSTEM_TENANT_ID, deviceId, AttributeScope.CLIENT_SCOPE, Collections.singletonList(attrOld)).get();
Optional<AttributeKvEntry> saved = attributesService.find(SYSTEM_TENANT_ID, deviceId, DataConstants.CLIENT_SCOPE, attrOld.getKey()).get(); Optional<AttributeKvEntry> saved = attributesService.find(SYSTEM_TENANT_ID, deviceId, AttributeScope.CLIENT_SCOPE, attrOld.getKey()).get();
Assert.assertTrue(saved.isPresent()); Assert.assertTrue(saved.isPresent());
Assert.assertEquals(attrOld, saved.get()); Assert.assertEquals(attrOld, saved.get());
KvEntry attrNewValue = new StringDataEntry("attribute1", "value2"); KvEntry attrNewValue = new StringDataEntry("attribute1", "value2");
AttributeKvEntry attrNew = new BaseAttributeKvEntry(attrNewValue, 73L); AttributeKvEntry attrNew = new BaseAttributeKvEntry(attrNewValue, 73L);
attributesService.save(SYSTEM_TENANT_ID, deviceId, DataConstants.CLIENT_SCOPE, Collections.singletonList(attrNew)).get(); attributesService.save(SYSTEM_TENANT_ID, deviceId, AttributeScope.CLIENT_SCOPE, Collections.singletonList(attrNew)).get();
saved = attributesService.find(SYSTEM_TENANT_ID, deviceId, DataConstants.CLIENT_SCOPE, attrOld.getKey()).get(); saved = attributesService.find(SYSTEM_TENANT_ID, deviceId, AttributeScope.CLIENT_SCOPE, attrOld.getKey()).get();
Assert.assertEquals(attrNew, saved.get()); Assert.assertEquals(attrNew, saved.get());
} }
@ -108,11 +108,11 @@ public abstract class BaseAttributesServiceTest extends AbstractServiceTest {
KvEntry attrBNewValue = new StringDataEntry("B", "value3"); KvEntry attrBNewValue = new StringDataEntry("B", "value3");
AttributeKvEntry attrBNew = new BaseAttributeKvEntry(attrBNewValue, 73L); AttributeKvEntry attrBNew = new BaseAttributeKvEntry(attrBNewValue, 73L);
attributesService.save(SYSTEM_TENANT_ID, deviceId, DataConstants.CLIENT_SCOPE, Collections.singletonList(attrAOld)).get(); attributesService.save(SYSTEM_TENANT_ID, deviceId, AttributeScope.CLIENT_SCOPE, Collections.singletonList(attrAOld)).get();
attributesService.save(SYSTEM_TENANT_ID, deviceId, DataConstants.CLIENT_SCOPE, Collections.singletonList(attrANew)).get(); attributesService.save(SYSTEM_TENANT_ID, deviceId, AttributeScope.CLIENT_SCOPE, Collections.singletonList(attrANew)).get();
attributesService.save(SYSTEM_TENANT_ID, deviceId, DataConstants.CLIENT_SCOPE, Collections.singletonList(attrBNew)).get(); attributesService.save(SYSTEM_TENANT_ID, deviceId, AttributeScope.CLIENT_SCOPE, Collections.singletonList(attrBNew)).get();
List<AttributeKvEntry> saved = attributesService.findAll(SYSTEM_TENANT_ID, deviceId, DataConstants.CLIENT_SCOPE).get(); List<AttributeKvEntry> saved = attributesService.findAll(SYSTEM_TENANT_ID, deviceId, AttributeScope.CLIENT_SCOPE).get();
Assert.assertNotNull(saved); Assert.assertNotNull(saved);
Assert.assertEquals(2, saved.size()); Assert.assertEquals(2, saved.size());
@ -123,7 +123,7 @@ public abstract class BaseAttributesServiceTest extends AbstractServiceTest {
@Test @Test
public void testDummyRequestWithEmptyResult() throws Exception { public void testDummyRequestWithEmptyResult() throws Exception {
var future = attributesService.find(new TenantId(UUID.randomUUID()), new DeviceId(UUID.randomUUID()), DataConstants.SERVER_SCOPE, "TEST"); var future = attributesService.find(new TenantId(UUID.randomUUID()), new DeviceId(UUID.randomUUID()), AttributeScope.SERVER_SCOPE, "TEST");
Assert.assertNotNull(future); Assert.assertNotNull(future);
var result = future.get(10, TimeUnit.SECONDS); var result = future.get(10, TimeUnit.SECONDS);
Assert.assertTrue(result.isEmpty()); Assert.assertTrue(result.isEmpty());
@ -133,7 +133,7 @@ public abstract class BaseAttributesServiceTest extends AbstractServiceTest {
public void testConcurrentTransaction() throws Exception { public void testConcurrentTransaction() throws Exception {
var tenantId = new TenantId(UUID.randomUUID()); var tenantId = new TenantId(UUID.randomUUID());
var deviceId = new DeviceId(UUID.randomUUID()); var deviceId = new DeviceId(UUID.randomUUID());
var scope = DataConstants.SERVER_SCOPE; var scope = AttributeScope.SERVER_SCOPE;
var key = "TEST"; var key = "TEST";
var attrKey = new AttributeCacheKey(scope, deviceId, "TEST"); var attrKey = new AttributeCacheKey(scope, deviceId, "TEST");
@ -179,7 +179,7 @@ public abstract class BaseAttributesServiceTest extends AbstractServiceTest {
public void testFetchAndUpdateEmpty() throws Exception { public void testFetchAndUpdateEmpty() throws Exception {
var tenantId = new TenantId(UUID.randomUUID()); var tenantId = new TenantId(UUID.randomUUID());
var deviceId = new DeviceId(UUID.randomUUID()); var deviceId = new DeviceId(UUID.randomUUID());
var scope = DataConstants.SERVER_SCOPE; var scope = AttributeScope.SERVER_SCOPE;
var key = "TEST"; var key = "TEST";
Optional<AttributeKvEntry> emptyValue = attributesService.find(tenantId, deviceId, scope, key).get(10, TimeUnit.SECONDS); Optional<AttributeKvEntry> emptyValue = attributesService.find(tenantId, deviceId, scope, key).get(10, TimeUnit.SECONDS);
@ -193,7 +193,7 @@ public abstract class BaseAttributesServiceTest extends AbstractServiceTest {
public void testFetchAndUpdateMulti() throws Exception { public void testFetchAndUpdateMulti() throws Exception {
var tenantId = new TenantId(UUID.randomUUID()); var tenantId = new TenantId(UUID.randomUUID());
var deviceId = new DeviceId(UUID.randomUUID()); var deviceId = new DeviceId(UUID.randomUUID());
var scope = DataConstants.SERVER_SCOPE; var scope = AttributeScope.SERVER_SCOPE;
var key1 = "TEST1"; var key1 = "TEST1";
var key2 = "TEST2"; var key2 = "TEST2";
@ -222,7 +222,7 @@ public abstract class BaseAttributesServiceTest extends AbstractServiceTest {
} }
private void testConcurrentFetchAndUpdate(TenantId tenantId, DeviceId deviceId, ListeningExecutorService pool) throws Exception { private void testConcurrentFetchAndUpdate(TenantId tenantId, DeviceId deviceId, ListeningExecutorService pool) throws Exception {
var scope = DataConstants.SERVER_SCOPE; var scope = AttributeScope.SERVER_SCOPE;
var key = "TEST"; var key = "TEST";
saveAttribute(tenantId, deviceId, scope, key, OLD_VALUE); saveAttribute(tenantId, deviceId, scope, key, OLD_VALUE);
List<ListenableFuture<?>> futures = new ArrayList<>(); List<ListenableFuture<?>> futures = new ArrayList<>();
@ -236,7 +236,7 @@ public abstract class BaseAttributesServiceTest extends AbstractServiceTest {
} }
private void testConcurrentFetchAndUpdateMulti(TenantId tenantId, DeviceId deviceId, ListeningExecutorService pool) throws Exception { private void testConcurrentFetchAndUpdateMulti(TenantId tenantId, DeviceId deviceId, ListeningExecutorService pool) throws Exception {
var scope = DataConstants.SERVER_SCOPE; var scope = AttributeScope.SERVER_SCOPE;
var key1 = "TEST1"; var key1 = "TEST1";
var key2 = "TEST2"; var key2 = "TEST2";
saveAttribute(tenantId, deviceId, scope, key1, OLD_VALUE); saveAttribute(tenantId, deviceId, scope, key1, OLD_VALUE);
@ -258,7 +258,7 @@ public abstract class BaseAttributesServiceTest extends AbstractServiceTest {
Assert.assertEquals(NEW_VALUE, newResult.get(1)); Assert.assertEquals(NEW_VALUE, newResult.get(1));
} }
private String getAttributeValue(TenantId tenantId, DeviceId deviceId, String scope, String key) { private String getAttributeValue(TenantId tenantId, DeviceId deviceId, AttributeScope scope, String key) {
try { try {
Optional<AttributeKvEntry> entry = attributesService.find(tenantId, deviceId, scope, key).get(10, TimeUnit.SECONDS); Optional<AttributeKvEntry> entry = attributesService.find(tenantId, deviceId, scope, key).get(10, TimeUnit.SECONDS);
return entry.orElseThrow(RuntimeException::new).getStrValue().orElse("Unknown"); return entry.orElseThrow(RuntimeException::new).getStrValue().orElse("Unknown");
@ -268,7 +268,7 @@ public abstract class BaseAttributesServiceTest extends AbstractServiceTest {
} }
} }
private List<String> getAttributeValues(TenantId tenantId, DeviceId deviceId, String scope, List<String> keys) { private List<String> getAttributeValues(TenantId tenantId, DeviceId deviceId, AttributeScope scope, List<String> keys) {
try { try {
List<AttributeKvEntry> entry = attributesService.find(tenantId, deviceId, scope, keys).get(10, TimeUnit.SECONDS); List<AttributeKvEntry> entry = attributesService.find(tenantId, deviceId, scope, keys).get(10, TimeUnit.SECONDS);
return entry.stream().map(e -> e.getStrValue().orElse(null)).collect(Collectors.toList()); return entry.stream().map(e -> e.getStrValue().orElse(null)).collect(Collectors.toList());
@ -278,7 +278,7 @@ public abstract class BaseAttributesServiceTest extends AbstractServiceTest {
} }
} }
private void saveAttribute(TenantId tenantId, DeviceId deviceId, String scope, String key, String s) { private void saveAttribute(TenantId tenantId, DeviceId deviceId, AttributeScope scope, String key, String s) {
try { try {
AttributeKvEntry newEntry = new BaseAttributeKvEntry(System.currentTimeMillis(), new StringDataEntry(key, s)); AttributeKvEntry newEntry = new BaseAttributeKvEntry(System.currentTimeMillis(), new StringDataEntry(key, s));
attributesService.save(tenantId, deviceId, scope, Collections.singletonList(newEntry)).get(10, TimeUnit.SECONDS); attributesService.save(tenantId, deviceId, scope, Collections.singletonList(newEntry)).get(10, TimeUnit.SECONDS);

4
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/BaseTbMsgPushNodeConfiguration.java

@ -17,7 +17,7 @@ package org.thingsboard.rule.engine.edge;
import lombok.Data; import lombok.Data;
import org.thingsboard.rule.engine.api.NodeConfiguration; import org.thingsboard.rule.engine.api.NodeConfiguration;
import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.AttributeScope;
@Data @Data
public class BaseTbMsgPushNodeConfiguration implements NodeConfiguration<BaseTbMsgPushNodeConfiguration> { public class BaseTbMsgPushNodeConfiguration implements NodeConfiguration<BaseTbMsgPushNodeConfiguration> {
@ -27,7 +27,7 @@ public class BaseTbMsgPushNodeConfiguration implements NodeConfiguration<BaseTbM
@Override @Override
public BaseTbMsgPushNodeConfiguration defaultConfiguration() { public BaseTbMsgPushNodeConfiguration defaultConfiguration() {
BaseTbMsgPushNodeConfiguration configuration = new BaseTbMsgPushNodeConfiguration(); BaseTbMsgPushNodeConfiguration configuration = new BaseTbMsgPushNodeConfiguration();
configuration.setScope(DataConstants.SERVER_SCOPE); configuration.setScope(AttributeScope.SERVER_SCOPE.name());
return configuration; return configuration;
} }
} }

5
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNode.java

@ -22,6 +22,7 @@ import lombok.extern.slf4j.Slf4j;
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.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.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.AttributeKvEntry;
@ -67,7 +68,7 @@ public class TbGpsGeofencingActionNode extends AbstractGeofencingNode<TbGpsGeofe
EntityGeofencingState entityState = entityStates.computeIfAbsent(msg.getOriginator(), key -> { EntityGeofencingState entityState = entityStates.computeIfAbsent(msg.getOriginator(), key -> {
try { try {
Optional<AttributeKvEntry> entry = ctx.getAttributesService() Optional<AttributeKvEntry> entry = ctx.getAttributesService()
.find(ctx.getTenantId(), msg.getOriginator(), DataConstants.SERVER_SCOPE, ctx.getServiceId()) .find(ctx.getTenantId(), msg.getOriginator(), AttributeScope.SERVER_SCOPE, ctx.getServiceId())
.get(1, TimeUnit.MINUTES); .get(1, TimeUnit.MINUTES);
if (entry.isPresent()) { if (entry.isPresent()) {
JsonObject element = parser.parse(entry.get().getValueAsString()).getAsJsonObject(); JsonObject element = parser.parse(entry.get().getValueAsString()).getAsJsonObject();
@ -120,7 +121,7 @@ public class TbGpsGeofencingActionNode extends AbstractGeofencingNode<TbGpsGeofe
object.addProperty("stayed", entityState.isStayed()); object.addProperty("stayed", entityState.isStayed());
AttributeKvEntry entry = new BaseAttributeKvEntry(new StringDataEntry(ctx.getServiceId(), gson.toJson(object)), System.currentTimeMillis()); AttributeKvEntry entry = new BaseAttributeKvEntry(new StringDataEntry(ctx.getServiceId(), gson.toJson(object)), System.currentTimeMillis());
List<AttributeKvEntry> attributeKvEntryList = Collections.singletonList(entry); List<AttributeKvEntry> attributeKvEntryList = Collections.singletonList(entry);
ctx.getAttributesService().save(ctx.getTenantId(), entityId, DataConstants.SERVER_SCOPE, attributeKvEntryList); ctx.getAttributesService().save(ctx.getTenantId(), entityId, AttributeScope.SERVER_SCOPE, attributeKvEntryList);
} }
@Override @Override

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

@ -33,6 +33,7 @@ import org.thingsboard.rule.engine.api.TbNode;
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.rule.engine.api.util.TbNodeUtils; import org.thingsboard.rule.engine.api.util.TbNodeUtils;
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.StringUtils; import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
@ -208,15 +209,15 @@ public class TbMathNode implements TbNode {
} }
private ListenableFuture<Void> saveAttribute(TbContext ctx, TbMsg msg, double result, TbMathResult mathResultDef) { private ListenableFuture<Void> saveAttribute(TbContext ctx, TbMsg msg, double result, TbMathResult mathResultDef) {
String attributeScope = getAttributeScope(mathResultDef.getAttributeScope()); AttributeScope attributeScope = getAttributeScope(mathResultDef.getAttributeScope());
if (isIntegerResult(mathResultDef, config.getOperation())) { if (isIntegerResult(mathResultDef, config.getOperation())) {
var value = toIntValue(result); var value = toIntValue(result);
return ctx.getTelemetryService().saveAttrAndNotify( return ctx.getTelemetryService().saveAttrAndNotify(
ctx.getTenantId(), msg.getOriginator(), attributeScope, mathResultDef.getKey(), value); ctx.getTenantId(), msg.getOriginator(), attributeScope.name(), mathResultDef.getKey(), value);
} else { } else {
var value = toDoubleValue(mathResultDef, result); var value = toDoubleValue(mathResultDef, result);
return ctx.getTelemetryService().saveAttrAndNotify( return ctx.getTelemetryService().saveAttrAndNotify(
ctx.getTenantId(), msg.getOriginator(), attributeScope, mathResultDef.getKey(), value); ctx.getTenantId(), msg.getOriginator(), attributeScope.name(), mathResultDef.getKey(), value);
} }
} }
@ -384,7 +385,7 @@ public class TbMathNode implements TbNode {
case MESSAGE_METADATA: case MESSAGE_METADATA:
return Futures.immediateFuture(TbMathArgumentValue.fromMessageMetadata(arg, argKey, msg.getMetaData())); return Futures.immediateFuture(TbMathArgumentValue.fromMessageMetadata(arg, argKey, msg.getMetaData()));
case ATTRIBUTE: case ATTRIBUTE:
String scope = getAttributeScope(arg.getAttributeScope()); AttributeScope scope = getAttributeScope(arg.getAttributeScope());
return Futures.transform(ctx.getAttributesService().find(ctx.getTenantId(), msg.getOriginator(), scope, argKey), return Futures.transform(ctx.getAttributesService().find(ctx.getTenantId(), msg.getOriginator(), scope, argKey),
opt -> getTbMathArgumentValue(arg, opt, "Attribute: " + argKey + " with scope: " + scope + " not found for entity: " + msg.getOriginator()) opt -> getTbMathArgumentValue(arg, opt, "Attribute: " + argKey + " with scope: " + scope + " not found for entity: " + msg.getOriginator())
, MoreExecutors.directExecutor()); , MoreExecutors.directExecutor());
@ -402,8 +403,8 @@ public class TbMathNode implements TbNode {
return CONSTANT.equals(type) ? keyPattern : TbNodeUtils.processPattern(keyPattern, msg); return CONSTANT.equals(type) ? keyPattern : TbNodeUtils.processPattern(keyPattern, msg);
} }
private String getAttributeScope(String attrScope) { private AttributeScope getAttributeScope(String attrScope) {
return StringUtils.isEmpty(attrScope) ? DataConstants.SERVER_SCOPE : attrScope; return StringUtils.isEmpty(attrScope) ? AttributeScope.SERVER_SCOPE : AttributeScope.valueOf(attrScope);
} }
private TbMathArgumentValue getTbMathArgumentValue(TbMathArgument arg, Optional<? extends KvEntry> kvOpt, String error) { private TbMathArgumentValue getTbMathArgumentValue(TbMathArgument arg, Optional<? extends KvEntry> kvOpt, String error) {

13
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetAttributesNode.java

@ -29,6 +29,7 @@ import org.thingsboard.rule.engine.api.TbNodeConfiguration;
import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.rule.engine.api.util.TbNodeUtils;
import org.thingsboard.rule.engine.util.TbMsgSource; import org.thingsboard.rule.engine.util.TbMsgSource;
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.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
@ -100,9 +101,9 @@ public abstract class TbAbstractGetAttributesNode<C extends TbGetAttributesNodeC
Set<TbPair<String, List<String>>> failuresPairSet = ConcurrentHashMap.newKeySet(); Set<TbPair<String, List<String>>> failuresPairSet = ConcurrentHashMap.newKeySet();
var getKvEntryPairFutures = Futures.allAsList( var getKvEntryPairFutures = Futures.allAsList(
getLatestTelemetry(ctx, entityId, TbNodeUtils.processPatterns(config.getLatestTsKeyNames(), msg), failuresPairSet), getLatestTelemetry(ctx, entityId, TbNodeUtils.processPatterns(config.getLatestTsKeyNames(), msg), failuresPairSet),
getAttrAsync(ctx, entityId, CLIENT_SCOPE, TbNodeUtils.processPatterns(config.getClientAttributeNames(), msg), failuresPairSet), getAttrAsync(ctx, entityId, AttributeScope.CLIENT_SCOPE, TbNodeUtils.processPatterns(config.getClientAttributeNames(), msg), failuresPairSet),
getAttrAsync(ctx, entityId, SHARED_SCOPE, TbNodeUtils.processPatterns(config.getSharedAttributeNames(), msg), failuresPairSet), getAttrAsync(ctx, entityId, AttributeScope.SHARED_SCOPE, TbNodeUtils.processPatterns(config.getSharedAttributeNames(), msg), failuresPairSet),
getAttrAsync(ctx, entityId, SERVER_SCOPE, TbNodeUtils.processPatterns(config.getServerAttributeNames(), msg), failuresPairSet) getAttrAsync(ctx, entityId, AttributeScope.SERVER_SCOPE, TbNodeUtils.processPatterns(config.getServerAttributeNames(), msg), failuresPairSet)
); );
withCallback(getKvEntryPairFutures, futuresList -> { withCallback(getKvEntryPairFutures, futuresList -> {
var msgMetaData = msg.getMetaData().copy(); var msgMetaData = msg.getMetaData().copy();
@ -127,7 +128,7 @@ public abstract class TbAbstractGetAttributesNode<C extends TbGetAttributesNodeC
private ListenableFuture<TbPair<String, List<AttributeKvEntry>>> getAttrAsync( private ListenableFuture<TbPair<String, List<AttributeKvEntry>>> getAttrAsync(
TbContext ctx, TbContext ctx,
EntityId entityId, EntityId entityId,
String scope, AttributeScope scope,
List<String> keys, List<String> keys,
Set<TbPair<String, List<String>>> failuresPairSet Set<TbPair<String, List<String>>> failuresPairSet
) { ) {
@ -138,9 +139,9 @@ public abstract class TbAbstractGetAttributesNode<C extends TbGetAttributesNodeC
return Futures.transform(attributeKvEntryListFuture, attributeKvEntryList -> { return Futures.transform(attributeKvEntryListFuture, attributeKvEntryList -> {
if (isTellFailureIfAbsent && attributeKvEntryList.size() != keys.size()) { if (isTellFailureIfAbsent && attributeKvEntryList.size() != keys.size()) {
List<String> nonExistentKeys = getNonExistentKeys(attributeKvEntryList, keys); List<String> nonExistentKeys = getNonExistentKeys(attributeKvEntryList, keys);
failuresPairSet.add(new TbPair<>(scope, nonExistentKeys)); failuresPairSet.add(new TbPair<>(scope.name(), nonExistentKeys));
} }
return new TbPair<>(scope, attributeKvEntryList); return new TbPair<>(scope.name(), attributeKvEntryList);
}, ctx.getDbCallbackExecutor()); }, ctx.getDbCallbackExecutor());
} }

4
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetMappedDataNode.java

@ -25,6 +25,7 @@ import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.rule.engine.api.util.TbNodeUtils;
import org.thingsboard.rule.engine.util.EntitiesFieldsAsyncLoader; import org.thingsboard.rule.engine.util.EntitiesFieldsAsyncLoader;
import org.thingsboard.rule.engine.util.TbMsgSource; import org.thingsboard.rule.engine.util.TbMsgSource;
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.kv.KvEntry; import org.thingsboard.server.common.data.kv.KvEntry;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
@ -36,7 +37,6 @@ import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.thingsboard.common.util.DonAsynchron.withCallback; import static org.thingsboard.common.util.DonAsynchron.withCallback;
import static org.thingsboard.server.common.data.DataConstants.SERVER_SCOPE;
@Slf4j @Slf4j
public abstract class TbAbstractGetMappedDataNode<T extends EntityId, C extends TbGetMappedDataNodeConfiguration> extends TbAbstractNodeWithFetchTo<C> { public abstract class TbAbstractGetMappedDataNode<T extends EntityId, C extends TbGetMappedDataNodeConfiguration> extends TbAbstractNodeWithFetchTo<C> {
@ -134,7 +134,7 @@ public abstract class TbAbstractGetMappedDataNode<T extends EntityId, C extends
} }
private ListenableFuture<List<KvEntry>> getAttributesAsync(TbContext ctx, EntityId entityId, List<String> attrKeys) { private ListenableFuture<List<KvEntry>> getAttributesAsync(TbContext ctx, EntityId entityId, List<String> attrKeys) {
var latest = ctx.getAttributesService().find(ctx.getTenantId(), entityId, SERVER_SCOPE, attrKeys); var latest = ctx.getAttributesService().find(ctx.getTenantId(), entityId, AttributeScope.SERVER_SCOPE, attrKeys);
return Futures.transform(latest, l -> return Futures.transform(latest, l ->
l.stream() l.stream()
.map(i -> (KvEntry) i) .map(i -> (KvEntry) i)

7
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DeviceState.java

@ -21,6 +21,7 @@ import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.rule.engine.profile.state.PersistedAlarmState; import org.thingsboard.rule.engine.profile.state.PersistedAlarmState;
import org.thingsboard.rule.engine.profile.state.PersistedDeviceState; import org.thingsboard.rule.engine.profile.state.PersistedDeviceState;
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.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
@ -382,9 +383,9 @@ class DeviceState {
} }
} }
if (!attributeKeys.isEmpty()) { if (!attributeKeys.isEmpty()) {
addToSnapshot(result, ctx.getAttributesService().find(ctx.getTenantId(), originator, DataConstants.CLIENT_SCOPE, attributeKeys).get()); addToSnapshot(result, ctx.getAttributesService().find(ctx.getTenantId(), originator, AttributeScope.CLIENT_SCOPE, attributeKeys).get());
addToSnapshot(result, ctx.getAttributesService().find(ctx.getTenantId(), originator, DataConstants.SHARED_SCOPE, attributeKeys).get()); addToSnapshot(result, ctx.getAttributesService().find(ctx.getTenantId(), originator, AttributeScope.SHARED_SCOPE, attributeKeys).get());
addToSnapshot(result, ctx.getAttributesService().find(ctx.getTenantId(), originator, DataConstants.SERVER_SCOPE, attributeKeys).get()); addToSnapshot(result, ctx.getAttributesService().find(ctx.getTenantId(), originator, AttributeScope.SERVER_SCOPE, attributeKeys).get());
} }
} }

3
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DynamicPredicateValueCtxImpl.java

@ -17,6 +17,7 @@ package org.thingsboard.rule.engine.profile;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbContext;
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.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
@ -62,7 +63,7 @@ public class DynamicPredicateValueCtxImpl implements DynamicPredicateValueCtx {
private EntityKeyValue getValue(EntityId entityId, String key) { private EntityKeyValue getValue(EntityId entityId, String key) {
try { try {
Optional<AttributeKvEntry> entry = ctx.getAttributesService().find(tenantId, entityId, DataConstants.SERVER_SCOPE, key).get(); Optional<AttributeKvEntry> entry = ctx.getAttributesService().find(tenantId, entityId, AttributeScope.SERVER_SCOPE, key).get();
if (entry.isPresent()) { if (entry.isPresent()) {
return DeviceState.toEntityValue(entry.get()); return DeviceState.toEntityValue(entry.get());
} }

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

@ -28,6 +28,7 @@ import org.thingsboard.rule.engine.api.TbNode;
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.rule.engine.api.util.TbNodeUtils; import org.thingsboard.rule.engine.api.util.TbNodeUtils;
import org.thingsboard.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.data.kv.KvEntry;
@ -96,7 +97,7 @@ public class TbMsgAttributesNode implements TbNode {
} }
List<String> keys = newAttributes.stream().map(KvEntry::getKey).collect(Collectors.toList()); List<String> keys = newAttributes.stream().map(KvEntry::getKey).collect(Collectors.toList());
ListenableFuture<List<AttributeKvEntry>> findFuture = ctx.getAttributesService().find(ctx.getTenantId(), msg.getOriginator(), scope, keys); ListenableFuture<List<AttributeKvEntry>> findFuture = ctx.getAttributesService().find(ctx.getTenantId(), msg.getOriginator(), AttributeScope.valueOf(scope), keys);
DonAsynchron.withCallback(findFuture, DonAsynchron.withCallback(findFuture,
currentAttributes -> { currentAttributes -> {

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

@ -39,6 +39,7 @@ 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.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
@ -365,7 +366,7 @@ public class TbMathNodeTest {
TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().toString()); TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().toString());
when(attributesService.find(tenantId, originator, DataConstants.SERVER_SCOPE, "a")) when(attributesService.find(tenantId, originator, AttributeScope.SERVER_SCOPE, "a"))
.thenReturn(Futures.immediateFuture(Optional.of(new BaseAttributeKvEntry(System.currentTimeMillis(), new DoubleDataEntry("a", 2.0))))); .thenReturn(Futures.immediateFuture(Optional.of(new BaseAttributeKvEntry(System.currentTimeMillis(), new DoubleDataEntry("a", 2.0)))));
when(tsService.findLatest(tenantId, originator, "b")) when(tsService.findLatest(tenantId, originator, "b"))

7
rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNodeTest.java

@ -32,6 +32,7 @@ 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.rule.engine.util.TbMsgSource; import org.thingsboard.rule.engine.util.TbMsgSource;
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.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
@ -106,13 +107,13 @@ public class TbGetAttributesNodeTest {
tsKeys = List.of("temperature", "humidity", "unknown"); tsKeys = List.of("temperature", "humidity", "unknown");
ts = System.currentTimeMillis(); ts = System.currentTimeMillis();
when(attributesServiceMock.find(TENANT_ID, ORIGINATOR, DataConstants.CLIENT_SCOPE, clientAttributes)) when(attributesServiceMock.find(TENANT_ID, ORIGINATOR, AttributeScope.CLIENT_SCOPE, clientAttributes))
.thenReturn(Futures.immediateFuture(getListAttributeKvEntry(clientAttributes, ts))); .thenReturn(Futures.immediateFuture(getListAttributeKvEntry(clientAttributes, ts)));
when(attributesServiceMock.find(TENANT_ID, ORIGINATOR, DataConstants.SERVER_SCOPE, serverAttributes)) when(attributesServiceMock.find(TENANT_ID, ORIGINATOR, AttributeScope.SERVER_SCOPE, serverAttributes))
.thenReturn(Futures.immediateFuture(getListAttributeKvEntry(serverAttributes, ts))); .thenReturn(Futures.immediateFuture(getListAttributeKvEntry(serverAttributes, ts)));
when(attributesServiceMock.find(TENANT_ID, ORIGINATOR, DataConstants.SHARED_SCOPE, sharedAttributes)) when(attributesServiceMock.find(TENANT_ID, ORIGINATOR, AttributeScope.SHARED_SCOPE, sharedAttributes))
.thenReturn(Futures.immediateFuture(getListAttributeKvEntry(sharedAttributes, ts))); .thenReturn(Futures.immediateFuture(getListAttributeKvEntry(sharedAttributes, ts)));
when(timeseriesServiceMock.findLatest(TENANT_ID, ORIGINATOR, tsKeys)) when(timeseriesServiceMock.findLatest(TENANT_ID, ORIGINATOR, tsKeys))

5
rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNodeTest.java

@ -33,6 +33,7 @@ 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.rule.engine.util.TbMsgSource; import org.thingsboard.rule.engine.util.TbMsgSource;
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.DataConstants; import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
@ -277,7 +278,7 @@ public class TbGetCustomerAttributeNodeTest {
doReturn(device).when(deviceServiceMock).findDeviceById(eq(TENANT_ID), eq(device.getId())); doReturn(device).when(deviceServiceMock).findDeviceById(eq(TENANT_ID), eq(device.getId()));
when(ctxMock.getAttributesService()).thenReturn(attributesServiceMock); when(ctxMock.getAttributesService()).thenReturn(attributesServiceMock);
when(attributesServiceMock.find(eq(TENANT_ID), eq(CUSTOMER_ID), eq(DataConstants.SERVER_SCOPE), argThat(new ListMatcher<>(expectedPatternProcessedKeysList)))) when(attributesServiceMock.find(eq(TENANT_ID), eq(CUSTOMER_ID), eq(AttributeScope.SERVER_SCOPE), argThat(new ListMatcher<>(expectedPatternProcessedKeysList))))
.thenReturn(Futures.immediateFuture(attributesList)); .thenReturn(Futures.immediateFuture(attributesList));
when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR);
@ -324,7 +325,7 @@ public class TbGetCustomerAttributeNodeTest {
doReturn(Futures.immediateFuture(user)).when(userServiceMock).findUserByIdAsync(eq(TENANT_ID), eq(user.getId())); doReturn(Futures.immediateFuture(user)).when(userServiceMock).findUserByIdAsync(eq(TENANT_ID), eq(user.getId()));
when(ctxMock.getAttributesService()).thenReturn(attributesServiceMock); when(ctxMock.getAttributesService()).thenReturn(attributesServiceMock);
when(attributesServiceMock.find(eq(TENANT_ID), eq(CUSTOMER_ID), eq(DataConstants.SERVER_SCOPE), argThat(new ListMatcher<>(expectedPatternProcessedKeysList)))) when(attributesServiceMock.find(eq(TENANT_ID), eq(CUSTOMER_ID), eq(AttributeScope.SERVER_SCOPE), argThat(new ListMatcher<>(expectedPatternProcessedKeysList))))
.thenReturn(Futures.immediateFuture(attributesList)); .thenReturn(Futures.immediateFuture(attributesList));
when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR);

5
rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNodeTest.java

@ -34,6 +34,7 @@ import org.thingsboard.rule.engine.api.TbNodeConfiguration;
import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.rule.engine.data.RelationsQuery; import org.thingsboard.rule.engine.data.RelationsQuery;
import org.thingsboard.rule.engine.util.TbMsgSource; import org.thingsboard.rule.engine.util.TbMsgSource;
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.Dashboard; import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.DataConstants;
@ -291,7 +292,7 @@ public class TbGetRelatedAttributeNodeTest {
doReturn(Futures.immediateFuture(List.of(entityRelation))).when(relationServiceMock).findByQuery(eq(TENANT_ID), any()); doReturn(Futures.immediateFuture(List.of(entityRelation))).when(relationServiceMock).findByQuery(eq(TENANT_ID), any());
when(ctxMock.getAttributesService()).thenReturn(attributesServiceMock); when(ctxMock.getAttributesService()).thenReturn(attributesServiceMock);
when(attributesServiceMock.find(eq(TENANT_ID), eq(user.getId()), eq(DataConstants.SERVER_SCOPE), argThat(new ListMatcher<>(expectedPatternProcessedKeysList)))) when(attributesServiceMock.find(eq(TENANT_ID), eq(user.getId()), eq(AttributeScope.SERVER_SCOPE), argThat(new ListMatcher<>(expectedPatternProcessedKeysList))))
.thenReturn(Futures.immediateFuture(attributes)); .thenReturn(Futures.immediateFuture(attributes));
when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR);
@ -342,7 +343,7 @@ public class TbGetRelatedAttributeNodeTest {
doReturn(Futures.immediateFuture(List.of(entityRelation))).when(relationServiceMock).findByQuery(eq(TENANT_ID), any()); doReturn(Futures.immediateFuture(List.of(entityRelation))).when(relationServiceMock).findByQuery(eq(TENANT_ID), any());
when(ctxMock.getAttributesService()).thenReturn(attributesServiceMock); when(ctxMock.getAttributesService()).thenReturn(attributesServiceMock);
when(attributesServiceMock.find(eq(TENANT_ID), eq(secondCustomer.getId()), eq(DataConstants.SERVER_SCOPE), argThat(new ListMatcher<>(expectedPatternProcessedKeysList)))) when(attributesServiceMock.find(eq(TENANT_ID), eq(secondCustomer.getId()), eq(AttributeScope.SERVER_SCOPE), argThat(new ListMatcher<>(expectedPatternProcessedKeysList))))
.thenReturn(Futures.immediateFuture(attributes)); .thenReturn(Futures.immediateFuture(attributes));
when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR);

5
rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNodeTest.java

@ -32,6 +32,7 @@ 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.rule.engine.util.TbMsgSource; import org.thingsboard.rule.engine.util.TbMsgSource;
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.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
@ -216,7 +217,7 @@ public class TbGetTenantAttributeNodeTest {
when(ctxMock.getTenantId()).thenReturn(TENANT_ID); when(ctxMock.getTenantId()).thenReturn(TENANT_ID);
when(ctxMock.getAttributesService()).thenReturn(attributesServiceMock); when(ctxMock.getAttributesService()).thenReturn(attributesServiceMock);
when(attributesServiceMock.find(eq(TENANT_ID), eq(TENANT_ID), eq(DataConstants.SERVER_SCOPE), argThat(new ListMatcher<>(expectedPatternProcessedKeysList)))) when(attributesServiceMock.find(eq(TENANT_ID), eq(TENANT_ID), eq(AttributeScope.SERVER_SCOPE), argThat(new ListMatcher<>(expectedPatternProcessedKeysList))))
.thenReturn(Futures.immediateFuture(attributesList)); .thenReturn(Futures.immediateFuture(attributesList));
when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR);
@ -257,7 +258,7 @@ public class TbGetTenantAttributeNodeTest {
when(ctxMock.getTenantId()).thenReturn(TENANT_ID); when(ctxMock.getTenantId()).thenReturn(TENANT_ID);
when(ctxMock.getAttributesService()).thenReturn(attributesServiceMock); when(ctxMock.getAttributesService()).thenReturn(attributesServiceMock);
when(attributesServiceMock.find(eq(TENANT_ID), eq(TENANT_ID), eq(DataConstants.SERVER_SCOPE), argThat(new ListMatcher<>(expectedPatternProcessedKeysList)))) when(attributesServiceMock.find(eq(TENANT_ID), eq(TENANT_ID), eq(AttributeScope.SERVER_SCOPE), argThat(new ListMatcher<>(expectedPatternProcessedKeysList))))
.thenReturn(Futures.immediateFuture(attributesList)); .thenReturn(Futures.immediateFuture(attributesList));
when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR);

54
rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java

@ -374,7 +374,7 @@ public class TbDeviceProfileNodeTest {
.thenReturn(null); .thenReturn(null);
registerCreateAlarmMock(alarmService.createAlarm(any()), true); registerCreateAlarmMock(alarmService.createAlarm(any()), true);
Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); Mockito.when(ctx.getAttributesService()).thenReturn(attributesService);
Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet()))
.thenReturn(attrListListenableFuture); .thenReturn(attrListListenableFuture);
TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING);
@ -458,11 +458,11 @@ public class TbDeviceProfileNodeTest {
.thenReturn(null); .thenReturn(null);
registerCreateAlarmMock(alarmService.createAlarm(any()), true); registerCreateAlarmMock(alarmService.createAlarm(any()), true);
Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); Mockito.when(ctx.getAttributesService()).thenReturn(attributesService);
Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet()))
.thenReturn(Futures.immediateFuture(Collections.emptyList())); .thenReturn(Futures.immediateFuture(Collections.emptyList()));
Mockito.when(attributesService.find(eq(tenantId), eq(customerId), Mockito.anyString(), Mockito.anyString())) Mockito.when(attributesService.find(eq(tenantId), eq(customerId), Mockito.any(AttributeScope.class), Mockito.anyString()))
.thenReturn(Futures.immediateFuture(Optional.empty())); .thenReturn(Futures.immediateFuture(Optional.empty()));
Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), Mockito.anyString(), Mockito.anyString())) Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), Mockito.any(AttributeScope.class), Mockito.anyString()))
.thenReturn(attrListListenableFuture); .thenReturn(attrListListenableFuture);
TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING);
@ -532,7 +532,7 @@ public class TbDeviceProfileNodeTest {
.thenReturn(null); .thenReturn(null);
registerCreateAlarmMock(alarmService.createAlarm(any()), true); registerCreateAlarmMock(alarmService.createAlarm(any()), true);
Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); Mockito.when(ctx.getAttributesService()).thenReturn(attributesService);
Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet()))
.thenReturn(listListenableFutureWithLess); .thenReturn(listListenableFutureWithLess);
TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING);
@ -628,7 +628,7 @@ public class TbDeviceProfileNodeTest {
.thenReturn(null); .thenReturn(null);
registerCreateAlarmMock(alarmService.createAlarm(any()), true); registerCreateAlarmMock(alarmService.createAlarm(any()), true);
Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); Mockito.when(ctx.getAttributesService()).thenReturn(attributesService);
Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet()))
.thenReturn(listListenableFuture); .thenReturn(listListenableFuture);
TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING);
@ -748,13 +748,13 @@ public class TbDeviceProfileNodeTest {
.thenReturn(null); .thenReturn(null);
registerCreateAlarmMock(alarmService.createAlarm(any()), true); registerCreateAlarmMock(alarmService.createAlarm(any()), true);
Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); Mockito.when(ctx.getAttributesService()).thenReturn(attributesService);
Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), Mockito.anyString(), Mockito.anyString())) Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), Mockito.any(AttributeScope.class), Mockito.anyString()))
.thenReturn(optionalDurationAttribute); .thenReturn(optionalDurationAttribute);
Mockito.when(ctx.getDeviceService().findDeviceById(tenantId, deviceId)) Mockito.when(ctx.getDeviceService().findDeviceById(tenantId, deviceId))
.thenReturn(device); .thenReturn(device);
Mockito.when(attributesService.find(eq(tenantId), eq(customerId), eq(DataConstants.SERVER_SCOPE), Mockito.anyString())) Mockito.when(attributesService.find(eq(tenantId), eq(customerId), eq(AttributeScope.SERVER_SCOPE), Mockito.anyString()))
.thenReturn(emptyOptional); .thenReturn(emptyOptional);
Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet()))
.thenReturn(listNoDurationAttribute); .thenReturn(listNoDurationAttribute);
TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING);
@ -865,7 +865,7 @@ public class TbDeviceProfileNodeTest {
.thenReturn(null); .thenReturn(null);
registerCreateAlarmMock(alarmService.createAlarm(any()), true); registerCreateAlarmMock(alarmService.createAlarm(any()), true);
Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); Mockito.when(ctx.getAttributesService()).thenReturn(attributesService);
Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet()))
.thenReturn(listListenableFuture); .thenReturn(listListenableFuture);
TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING);
@ -977,13 +977,13 @@ public class TbDeviceProfileNodeTest {
.thenReturn(null); .thenReturn(null);
registerCreateAlarmMock(alarmService.createAlarm(any()), true); registerCreateAlarmMock(alarmService.createAlarm(any()), true);
Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); Mockito.when(ctx.getAttributesService()).thenReturn(attributesService);
Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), Mockito.anyString(), Mockito.anyString())) Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), Mockito.any(AttributeScope.class), Mockito.anyString()))
.thenReturn(optionalDurationAttribute); .thenReturn(optionalDurationAttribute);
Mockito.when(ctx.getDeviceService().findDeviceById(tenantId, deviceId)) Mockito.when(ctx.getDeviceService().findDeviceById(tenantId, deviceId))
.thenReturn(device); .thenReturn(device);
Mockito.when(attributesService.find(eq(tenantId), eq(customerId), eq(DataConstants.SERVER_SCOPE), Mockito.anyString())) Mockito.when(attributesService.find(eq(tenantId), eq(customerId), eq(AttributeScope.SERVER_SCOPE), Mockito.anyString()))
.thenReturn(emptyOptional); .thenReturn(emptyOptional);
Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet()))
.thenReturn(listNoDurationAttribute); .thenReturn(listNoDurationAttribute);
TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING);
@ -1080,7 +1080,7 @@ public class TbDeviceProfileNodeTest {
.thenReturn(null); .thenReturn(null);
registerCreateAlarmMock(alarmService.createAlarm(any()), true); registerCreateAlarmMock(alarmService.createAlarm(any()), true);
Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); Mockito.when(ctx.getAttributesService()).thenReturn(attributesService);
Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet()))
.thenReturn(listListenableFuture); .thenReturn(listListenableFuture);
TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING);
@ -1179,7 +1179,7 @@ public class TbDeviceProfileNodeTest {
.thenReturn(null); .thenReturn(null);
registerCreateAlarmMock(alarmService.createAlarm(any()), true); registerCreateAlarmMock(alarmService.createAlarm(any()), true);
Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); Mockito.when(ctx.getAttributesService()).thenReturn(attributesService);
Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet()))
.thenReturn(listListenableFuture); .thenReturn(listListenableFuture);
TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING);
@ -1262,7 +1262,7 @@ public class TbDeviceProfileNodeTest {
.thenReturn(null); .thenReturn(null);
registerCreateAlarmMock(alarmService.createAlarm(any()), true); registerCreateAlarmMock(alarmService.createAlarm(any()), true);
Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); Mockito.when(ctx.getAttributesService()).thenReturn(attributesService);
Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet()))
.thenReturn(listListenableFutureActiveSchedule); .thenReturn(listListenableFutureActiveSchedule);
TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING);
@ -1359,7 +1359,7 @@ public class TbDeviceProfileNodeTest {
Mockito.when(alarmService.findLatestActiveByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm")) Mockito.when(alarmService.findLatestActiveByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm"))
.thenReturn(null); .thenReturn(null);
Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); Mockito.when(ctx.getAttributesService()).thenReturn(attributesService);
Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet()))
.thenReturn(listListenableFutureInactiveSchedule); .thenReturn(listListenableFutureInactiveSchedule);
TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING);
@ -1434,11 +1434,11 @@ public class TbDeviceProfileNodeTest {
.thenReturn(null); .thenReturn(null);
registerCreateAlarmMock(alarmService.createAlarm(any()), true); registerCreateAlarmMock(alarmService.createAlarm(any()), true);
Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); Mockito.when(ctx.getAttributesService()).thenReturn(attributesService);
Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet()))
.thenReturn(listListenableFutureWithLess); .thenReturn(listListenableFutureWithLess);
Mockito.when(ctx.getDeviceService().findDeviceById(tenantId, deviceId)) Mockito.when(ctx.getDeviceService().findDeviceById(tenantId, deviceId))
.thenReturn(device); .thenReturn(device);
Mockito.when(attributesService.find(eq(tenantId), eq(customerId), eq(DataConstants.SERVER_SCOPE), Mockito.anyString())) Mockito.when(attributesService.find(eq(tenantId), eq(customerId), eq(AttributeScope.SERVER_SCOPE), Mockito.anyString()))
.thenReturn(optionalListenableFutureWithLess); .thenReturn(optionalListenableFutureWithLess);
TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING);
@ -1510,9 +1510,9 @@ public class TbDeviceProfileNodeTest {
.thenReturn(null); .thenReturn(null);
registerCreateAlarmMock(alarmService.createAlarm(any()), true); registerCreateAlarmMock(alarmService.createAlarm(any()), true);
Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); Mockito.when(ctx.getAttributesService()).thenReturn(attributesService);
Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet()))
.thenReturn(listListenableFutureWithLess); .thenReturn(listListenableFutureWithLess);
Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), eq(DataConstants.SERVER_SCOPE), Mockito.anyString())) Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), eq(AttributeScope.SERVER_SCOPE), Mockito.anyString()))
.thenReturn(optionalListenableFutureWithLess); .thenReturn(optionalListenableFutureWithLess);
TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING);
@ -1592,11 +1592,11 @@ public class TbDeviceProfileNodeTest {
Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); Mockito.when(ctx.getAttributesService()).thenReturn(attributesService);
Mockito.when(ctx.getDeviceService().findDeviceById(tenantId, deviceId)) Mockito.when(ctx.getDeviceService().findDeviceById(tenantId, deviceId))
.thenReturn(device); .thenReturn(device);
Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet()))
.thenReturn(listListenableFutureWithLess); .thenReturn(listListenableFutureWithLess);
Mockito.when(attributesService.find(eq(tenantId), eq(customerId), Mockito.anyString(), Mockito.anyString())) Mockito.when(attributesService.find(eq(tenantId), eq(customerId), Mockito.any(AttributeScope.class), Mockito.anyString()))
.thenReturn(emptyOptionalFuture); .thenReturn(emptyOptionalFuture);
Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), eq(DataConstants.SERVER_SCOPE), Mockito.anyString())) Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), eq(AttributeScope.SERVER_SCOPE), Mockito.anyString()))
.thenReturn(optionalListenableFutureWithLess); .thenReturn(optionalListenableFutureWithLess);
TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING);
@ -1678,11 +1678,11 @@ public class TbDeviceProfileNodeTest {
Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); Mockito.when(ctx.getAttributesService()).thenReturn(attributesService);
Mockito.when(ctx.getDeviceService().findDeviceById(tenantId, deviceId)) Mockito.when(ctx.getDeviceService().findDeviceById(tenantId, deviceId))
.thenReturn(device); .thenReturn(device);
Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet()))
.thenReturn(listListenableFutureWithLess); .thenReturn(listListenableFutureWithLess);
Mockito.when(attributesService.find(eq(tenantId), eq(customerId), Mockito.anyString(), Mockito.anyString())) Mockito.when(attributesService.find(eq(tenantId), eq(customerId), Mockito.any(AttributeScope.class), Mockito.anyString()))
.thenReturn(emptyOptionalFuture); .thenReturn(emptyOptionalFuture);
Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), eq(DataConstants.SERVER_SCOPE), Mockito.anyString())) Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), eq(AttributeScope.SERVER_SCOPE), Mockito.anyString()))
.thenReturn(optionalListenableFutureWithLess); .thenReturn(optionalListenableFutureWithLess);
TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING);

Loading…
Cancel
Save