Browse Source

Merge with master

pull/11063/head
Igor Kulikov 2 years ago
parent
commit
d5c8f82b36
  1. 26
      application/src/main/data/upgrade/3.7.0/schema_update.sql
  2. 2
      application/src/main/java/org/thingsboard/server/actors/shared/ComponentMsgProcessor.java
  3. 4
      application/src/main/java/org/thingsboard/server/actors/stats/StatsPersistTick.java
  4. 3
      application/src/main/java/org/thingsboard/server/controller/BaseController.java
  5. 26
      application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java
  6. 4
      application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java
  7. 46
      application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
  8. 1
      application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java
  9. 4
      application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java
  10. 2
      application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java
  11. 2
      application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java
  12. 4
      application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/DefaultEntityExportService.java
  13. 4
      application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java
  14. 4
      application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/BaseEntityImportService.java
  15. 2
      application/src/main/resources/thingsboard.yml
  16. 2
      application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java
  17. 2
      application/src/test/java/org/thingsboard/server/controller/AuditLogControllerTest.java
  18. 30
      application/src/test/java/org/thingsboard/server/controller/DeviceControllerTest.java
  19. 2
      application/src/test/java/org/thingsboard/server/controller/EntityViewControllerTest.java
  20. 2
      application/src/test/java/org/thingsboard/server/controller/RuleChainControllerTest.java
  21. 8
      application/src/test/java/org/thingsboard/server/controller/TenantControllerTest.java
  22. 6
      application/src/test/java/org/thingsboard/server/edge/DeviceEdgeTest.java
  23. 8
      application/src/test/java/org/thingsboard/server/edge/UserEdgeTest.java
  24. 10
      application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/OtaLwM2MIntegrationTest.java
  25. 10
      common/cache/src/main/java/org/thingsboard/server/cache/VersionedCaffeineTbCache.java
  26. 23
      common/cache/src/main/java/org/thingsboard/server/cache/VersionedRedisTbCache.java
  27. 10
      common/cache/src/main/java/org/thingsboard/server/cache/VersionedTbCache.java
  28. 2
      common/cache/src/main/java/org/thingsboard/server/cache/device/DeviceCacheEvictEvent.java
  29. 4
      common/cache/src/main/java/org/thingsboard/server/cache/device/DeviceCaffeineCache.java
  30. 4
      common/cache/src/main/java/org/thingsboard/server/cache/device/DeviceRedisCache.java
  31. 5
      common/data/src/main/java/org/thingsboard/server/common/data/Customer.java
  32. 8
      common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java
  33. 37
      common/data/src/main/java/org/thingsboard/server/common/data/Device.java
  34. 2
      common/data/src/main/java/org/thingsboard/server/common/data/DeviceInfo.java
  35. 4
      common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java
  36. 8
      common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java
  37. 5
      common/data/src/main/java/org/thingsboard/server/common/data/HasVersion.java
  38. 12
      common/data/src/main/java/org/thingsboard/server/common/data/Tenant.java
  39. 9
      common/data/src/main/java/org/thingsboard/server/common/data/User.java
  40. 7
      common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java
  41. 5
      common/data/src/main/java/org/thingsboard/server/common/data/asset/AssetProfile.java
  42. 9
      common/data/src/main/java/org/thingsboard/server/common/data/edge/Edge.java
  43. 24
      common/data/src/main/java/org/thingsboard/server/common/data/exception/EntityVersionMismatchException.java
  44. 1
      common/data/src/main/java/org/thingsboard/server/common/data/exception/ThingsboardErrorCode.java
  45. 7
      common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java
  46. 12
      common/data/src/main/java/org/thingsboard/server/common/data/security/DeviceCredentials.java
  47. 2
      common/data/src/main/java/org/thingsboard/server/common/data/sync/JsonTbEntity.java
  48. 2
      common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/DeviceExportData.java
  49. 1
      common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/EntityExportData.java
  50. 11
      common/data/src/main/java/org/thingsboard/server/common/data/widget/BaseWidgetType.java
  51. 9
      common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetTypeDetails.java
  52. 7
      common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetsBundle.java
  53. 1
      common/message/src/main/java/org/thingsboard/server/common/msg/tools/MaxPayloadSizeExceededException.java
  54. 24
      common/proto/src/main/java/org/thingsboard/server/common/util/ProtoUtils.java
  55. 4
      common/proto/src/main/proto/queue.proto
  56. 209
      common/transport/coap/src/test/java/org/thingsboard/server/transport/coap/efento/CoapEfentTransportResourceTest.java
  57. 4
      common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java
  58. 2
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/TbLwM2MDtlsBootstrapCertificateVerifier.java
  59. 2
      dao/src/main/java/org/thingsboard/server/dao/Dao.java
  60. 2
      dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmCommentService.java
  61. 6
      dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileCacheKey.java
  62. 4
      dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileCaffeineCache.java
  63. 6
      dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileEvictEvent.java
  64. 5
      dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileRedisCache.java
  65. 31
      dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java
  66. 2
      dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java
  67. 12
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java
  68. 8
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileCacheKey.java
  69. 4
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileCaffeineCache.java
  70. 6
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileEvictEvent.java
  71. 5
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileRedisCache.java
  72. 51
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java
  73. 70
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
  74. 2
      dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java
  75. 29
      dao/src/main/java/org/thingsboard/server/dao/entity/CachedVersionedEntityService.java
  76. 8
      dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewCacheValue.java
  77. 4
      dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewCaffeineCache.java
  78. 6
      dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewEvictEvent.java
  79. 4
      dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewRedisCache.java
  80. 40
      dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java
  81. 14
      dao/src/main/java/org/thingsboard/server/dao/model/BaseSqlEntity.java
  82. 45
      dao/src/main/java/org/thingsboard/server/dao/model/BaseVersionedEntity.java
  83. 1
      dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
  84. 4
      dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractAlarmCommentEntity.java
  85. 15
      dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractAssetEntity.java
  86. 17
      dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractDeviceEntity.java
  87. 16
      dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractEdgeEntity.java
  88. 16
      dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractEntityViewEntity.java
  89. 15
      dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractTenantEntity.java
  90. 15
      dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractWidgetTypeEntity.java
  91. 1
      dao/src/main/java/org/thingsboard/server/dao/model/sql/AssetInfoEntity.java
  92. 10
      dao/src/main/java/org/thingsboard/server/dao/model/sql/AssetProfileEntity.java
  93. 10
      dao/src/main/java/org/thingsboard/server/dao/model/sql/CustomerEntity.java
  94. 11
      dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardEntity.java
  95. 10
      dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardInfoEntity.java
  96. 11
      dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceCredentialsEntity.java
  97. 11
      dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java
  98. 11
      dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleChainEntity.java
  99. 10
      dao/src/main/java/org/thingsboard/server/dao/model/sql/UserEntity.java
  100. 11
      dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetsBundleEntity.java

26
application/src/main/data/upgrade/3.7.0/schema_update.sql

@ -68,14 +68,34 @@ $$;
CREATE SEQUENCE IF NOT EXISTS attribute_kv_version_seq cache 1;
CREATE SEQUENCE IF NOT EXISTS ts_kv_latest_version_seq cache 1;
ALTER TABLE attribute_kv ADD COLUMN version bigint default 0;
ALTER TABLE ts_kv_latest ADD COLUMN version bigint default 0;
ALTER TABLE attribute_kv ADD COLUMN IF NOT EXISTS version bigint default 0;
ALTER TABLE ts_kv_latest ADD COLUMN IF NOT EXISTS version bigint default 0;
-- KV VERSIONING UPDATE END
-- RELATION VERSIONING UPDATE START
CREATE SEQUENCE IF NOT EXISTS relation_version_seq cache 1;
ALTER TABLE relation ADD COLUMN version bigint default 0;
ALTER TABLE relation ADD COLUMN IF NOT EXISTS version bigint default 0;
-- RELATION VERSIONING UPDATE END
-- ENTITIES VERSIONING UPDATE START
ALTER TABLE device ADD COLUMN IF NOT EXISTS version BIGINT DEFAULT 1;
ALTER TABLE device_profile ADD COLUMN IF NOT EXISTS version BIGINT DEFAULT 1;
ALTER TABLE device_credentials ADD COLUMN IF NOT EXISTS version BIGINT DEFAULT 1;
ALTER TABLE asset ADD COLUMN IF NOT EXISTS version BIGINT DEFAULT 1;
ALTER TABLE asset_profile ADD COLUMN IF NOT EXISTS version BIGINT DEFAULT 1;
ALTER TABLE entity_view ADD COLUMN IF NOT EXISTS version BIGINT DEFAULT 1;
ALTER TABLE tb_user ADD COLUMN IF NOT EXISTS version BIGINT DEFAULT 1;
ALTER TABLE customer ADD COLUMN IF NOT EXISTS version BIGINT DEFAULT 1;
ALTER TABLE edge ADD COLUMN IF NOT EXISTS version BIGINT DEFAULT 1;
ALTER TABLE rule_chain ADD COLUMN IF NOT EXISTS version BIGINT DEFAULT 1;
ALTER TABLE dashboard ADD COLUMN IF NOT EXISTS version BIGINT DEFAULT 1;
ALTER TABLE widget_type ADD COLUMN IF NOT EXISTS version BIGINT DEFAULT 1;
ALTER TABLE widgets_bundle ADD COLUMN IF NOT EXISTS version BIGINT DEFAULT 1;
ALTER TABLE tenant ADD COLUMN IF NOT EXISTS version BIGINT DEFAULT 1;
-- ENTITIES VERSIONING UPDATE END

2
application/src/main/java/org/thingsboard/server/actors/shared/ComponentMsgProcessor.java

@ -78,7 +78,7 @@ public abstract class ComponentMsgProcessor<T extends EntityId> extends Abstract
}
public void scheduleStatsPersistTick(TbActorCtx context, long statsPersistFrequency) {
schedulePeriodicMsgWithDelay(context, new StatsPersistTick(), statsPersistFrequency, statsPersistFrequency);
schedulePeriodicMsgWithDelay(context, StatsPersistTick.INSTANCE, statsPersistFrequency, statsPersistFrequency);
}
protected boolean checkMsgValid(TbMsg tbMsg) {

4
application/src/main/java/org/thingsboard/server/actors/stats/StatsPersistTick.java

@ -18,7 +18,9 @@ package org.thingsboard.server.actors.stats;
import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.TbActorMsg;
public final class StatsPersistTick implements TbActorMsg {
public enum StatsPersistTick implements TbActorMsg {
INSTANCE;
@Override
public MsgType getMsgType() {
return MsgType.STATS_PERSIST_TICK_MSG;

3
application/src/main/java/org/thingsboard/server/controller/BaseController.java

@ -65,6 +65,7 @@ import org.thingsboard.server.common.data.asset.AssetProfile;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeInfo;
import org.thingsboard.server.common.data.exception.EntityVersionMismatchException;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.AlarmCommentId;
@ -390,6 +391,8 @@ public abstract class BaseController {
} else {
return new ThingsboardException("Database error", ThingsboardErrorCode.GENERAL);
}
} else if (exception instanceof EntityVersionMismatchException) {
return new ThingsboardException(exception.getMessage(), exception, ThingsboardErrorCode.VERSION_CONFLICT);
}
return new ThingsboardException(exception.getMessage(), exception, ThingsboardErrorCode.GENERAL);
}

26
application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java

@ -793,7 +793,7 @@ public class ControllerConstants {
" * 'SHARED_ATTRIBUTE' - used for shared attributes; \n" +
" * 'SERVER_ATTRIBUTE' - used for server attributes; \n" +
" * 'ATTRIBUTE' - used for any of the above; \n" +
" * 'TIME_SERIES' - used for time-series values; \n" +
" * 'TIME_SERIES' - used for time series values; \n" +
" * 'ENTITY_FIELD' - used for accessing entity fields like 'name', 'label', etc. The list of available fields depends on the entity type; \n" +
" * 'ALARM_FIELD' - similar to entity field, but is used in alarm queries only; \n" +
"\n\n Let's review the example:\n\n" +
@ -904,7 +904,7 @@ public class ControllerConstants {
protected static final String KEY_FILTERS =
"\n\n # Key Filters" +
"\nKey Filter allows you to define complex logical expressions over entity field, attribute or latest time-series value. The filter is defined using 'key', 'valueType' and 'predicate' objects. " +
"\nKey Filter allows you to define complex logical expressions over entity field, attribute or latest time series value. The filter is defined using 'key', 'valueType' and 'predicate' objects. " +
"Single Entity Query may have zero, one or multiple predicates. If multiple filters are defined, they are evaluated using logical 'AND'. " +
"The example below checks that temperature of the entity is above 20 degrees:" +
"\n\n" + MARKDOWN_CODE_BLOCK_START +
@ -935,7 +935,7 @@ public class ControllerConstants {
"For example, \"find all devices with profile 'Moisture Sensor'\" or \"Find all devices related to asset 'Building A'\"" +
"\n\nOptional **key filters** allow to filter results of the entity filter by complex criteria against " +
"main entity fields (name, label, type, etc), attributes and telemetry. " +
"For example, \"temperature > 20 or temperature< 10\" or \"name starts with 'T', and attribute 'model' is 'T1000', and timeseries field 'batteryLevel' > 40\"." +
"For example, \"temperature > 20 or temperature< 10\" or \"name starts with 'T', and attribute 'model' is 'T1000', and time series field 'batteryLevel' > 40\"." +
"\n\nLet's review the example:" +
"\n\n" + MARKDOWN_CODE_BLOCK_START +
"{\n" +
@ -970,13 +970,13 @@ public class ControllerConstants {
protected static final String ENTITY_DATA_QUERY_DESCRIPTION =
"Allows to run complex queries over platform entities (devices, assets, customers, etc) " +
"based on the combination of main entity filter and multiple key filters. " +
"Returns the paginated result of the query that contains requested entity fields and latest values of requested attributes and time-series data.\n\n" +
"Returns the paginated result of the query that contains requested entity fields and latest values of requested attributes and time series data.\n\n" +
"# Query Definition\n\n" +
"\n\nMain **entity filter** is mandatory and defines generic search criteria. " +
"For example, \"find all devices with profile 'Moisture Sensor'\" or \"Find all devices related to asset 'Building A'\"" +
"\n\nOptional **key filters** allow to filter results of the **entity filter** by complex criteria against " +
"main entity fields (name, label, type, etc), attributes and telemetry. " +
"For example, \"temperature > 20 or temperature< 10\" or \"name starts with 'T', and attribute 'model' is 'T1000', and timeseries field 'batteryLevel' > 40\"." +
"For example, \"temperature > 20 or temperature< 10\" or \"name starts with 'T', and attribute 'model' is 'T1000', and time series field 'batteryLevel' > 40\"." +
"\n\nThe **entity fields** and **latest values** contains list of entity fields and latest attribute/telemetry fields to fetch for each entity." +
"\n\nThe **page link** contains information about the page to fetch and the sort ordering." +
"\n\nLet's review the example:" +
@ -1054,7 +1054,7 @@ public class ControllerConstants {
protected static final String ALARM_DATA_QUERY_DESCRIPTION = "This method description defines how Alarm Data Query extends the Entity Data Query. " +
"See method 'Find Entity Data by Query' first to get the info about 'Entity Data Query'." +
"\n\n The platform will first search the entities that match the entity and key filters. Then, the platform will use 'Alarm Page Link' to filter the alarms related to those entities. " +
"Finally, platform fetch the properties of alarm that are defined in the **'alarmFields'** and combine them with the other entity, attribute and latest time-series fields to return the result. " +
"Finally, platform fetch the properties of alarm that are defined in the **'alarmFields'** and combine them with the other entity, attribute and latest time series fields to return the result. " +
"\n\n See example of the alarm query below. The query will search first 100 active alarms with type 'Temperature Alarm' or 'Fire Alarm' for any device with current temperature > 0. " +
"The query will return combination of the entity fields: name of the device, device model and latest temperature reading and alarms fields: createdTime, type, severity and status: " +
"\n\n" + MARKDOWN_CODE_BLOCK_START +
@ -1175,7 +1175,7 @@ public class ControllerConstants {
protected static final String ALARM_FILTER_KEY = "## Alarm Filter Key" + NEW_LINE +
"Filter Key defines either entity field, attribute, telemetry or constant. It is a JSON object that consists the key name and type. The following filter key types are supported:\n" +
" * 'ATTRIBUTE' - used for attributes values;\n" +
" * 'TIME_SERIES' - used for time-series values;\n" +
" * 'TIME_SERIES' - used for time series values;\n" +
" * 'ENTITY_FIELD' - used for accessing entity fields like 'name', 'label', etc. The list of available fields depends on the entity type;\n" +
" * 'CONSTANT' - constant value specified." + NEW_LINE + "Let's review the example:" + NEW_LINE +
MARKDOWN_CODE_BLOCK_START +
@ -1293,7 +1293,7 @@ public class ControllerConstants {
protected static final String KEY_FILTERS_DESCRIPTION = "# Key Filters" + NEW_LINE +
"Key filter objects are created under the **'condition'** array. They allow you to define complex logical expressions over entity field, " +
"attribute, latest time-series value or constant. The filter is defined using 'key', 'valueType', " +
"attribute, latest time series value or constant. The filter is defined using 'key', 'valueType', " +
"'value' (refers to the value of the 'CONSTANT' alarm filter key type) and 'predicate' objects. Let's review each object:" + NEW_LINE +
ALARM_FILTER_KEY + FILTER_VALUE_TYPE + NEW_LINE + DEVICE_PROFILE_FILTER_PREDICATE + NEW_LINE;
@ -1606,7 +1606,7 @@ public class ControllerConstants {
protected static final String ATTRIBUTES_JSON_REQUEST_DESCRIPTION = "A string value representing the json object. For example, '{\"key\":\"value\"}'. See API call description for more details.";
protected static final String TELEMETRY_KEYS_BASE_DESCRIPTION = "A string value representing the comma-separated list of telemetry keys.";
protected static final String TELEMETRY_KEYS_DESCRIPTION = TELEMETRY_KEYS_BASE_DESCRIPTION + " If keys are not selected, the result will return all latest timeseries. For example, 'temperature,humidity'.";
protected static final String TELEMETRY_KEYS_DESCRIPTION = TELEMETRY_KEYS_BASE_DESCRIPTION + " If keys are not selected, the result will return all latest time series. For example, 'temperature,humidity'.";
protected static final String TELEMETRY_SCOPE_DESCRIPTION = "Value is deprecated, reserved for backward compatibility and not used in the API call implementation. Specify any scope for compatibility";
protected static final String TELEMETRY_JSON_REQUEST_DESCRIPTION = "A JSON with the telemetry values. See API call description for more details.";
@ -1622,11 +1622,11 @@ public class ControllerConstants {
protected static final String SAVE_ENTITY_ATTRIBUTES_STATUS_UNAUTHORIZED = "User is not authorized to save entity attributes for selected entity. Most likely, User belongs to different Customer or Tenant.";
protected static final String SAVE_ENTITY_ATTRIBUTES_STATUS_INTERNAL_SERVER_ERROR = "The exception was thrown during processing the request. " +
"Platform creates an audit log event about entity attributes updates with action type 'ATTRIBUTES_UPDATED' that includes an error stacktrace.";
protected static final String SAVE_ENTITY_TIMESERIES_STATUS_OK = "Timeseries from the request was created or updated. " +
"Platform creates an audit log event about entity timeseries updates with action type 'TIMESERIES_UPDATED'.";
protected static final String SAVE_ENTITY_TIMESERIES_STATUS_UNAUTHORIZED = "User is not authorized to save entity timeseries for selected entity. Most likely, User belongs to different Customer or Tenant.";
protected static final String SAVE_ENTITY_TIMESERIES_STATUS_OK = "Time series from the request was created or updated. " +
"Platform creates an audit log event about entity time series updates with action type 'TIMESERIES_UPDATED'.";
protected static final String SAVE_ENTITY_TIMESERIES_STATUS_UNAUTHORIZED = "User is not authorized to save entity time series for selected entity. Most likely, User belongs to different Customer or Tenant.";
protected static final String SAVE_ENTITY_TIMESERIES_STATUS_INTERNAL_SERVER_ERROR = "The exception was thrown during processing the request. " +
"Platform creates an audit log event about entity timeseries updates with action type 'TIMESERIES_UPDATED' that includes an error stacktrace.";
"Platform creates an audit log event about entity time series updates with action type 'TIMESERIES_UPDATED' that includes an error stacktrace.";
protected static final String ENTITY_ATTRIBUTE_SCOPES_TEMPLATE = " List of possible attribute scopes depends on the entity type: " +
"\n\n * SERVER_SCOPE - supported for all entity types;" +

4
application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java

@ -125,8 +125,8 @@ public class DeviceProfileController extends BaseController {
return checkNotNull(deviceProfileService.findDefaultDeviceProfileInfo(getTenantId()));
}
@ApiOperation(value = "Get time-series keys (getTimeseriesKeys)",
notes = "Get a set of unique time-series keys used by devices that belong to specified profile. " +
@ApiOperation(value = "Get time series keys (getTimeseriesKeys)",
notes = "Get a set of unique time series keys used by devices that belong to specified profile. " +
"If profile is not set returns a list of unique keys among all profiles. " +
"The call is used for auto-complete in the UI forms. " +
"The implementation limits the number of devices that participate in search to 100 as a trade of between accurate results and time-consuming queries. " +

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

@ -243,8 +243,8 @@ public class TelemetryController extends BaseController {
(result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, scope, keysStr));
}
@ApiOperation(value = "Get time-series keys (getTimeseriesKeys)",
notes = "Returns a set of unique time-series key names for the selected entity. " +
@ApiOperation(value = "Get time series keys (getTimeseriesKeys)",
notes = "Returns a set of unique time series key names for the selected entity. " +
"\n\n" + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/{entityType}/{entityId}/keys/timeseries", method = RequestMethod.GET)
@ -256,10 +256,10 @@ public class TelemetryController extends BaseController {
(result, tenantId, entityId) -> Futures.addCallback(tsService.findAllLatest(tenantId, entityId), getTsKeysToResponseCallback(result), MoreExecutors.directExecutor()));
}
@ApiOperation(value = "Get latest time-series value (getLatestTimeseries)",
notes = "Returns all time-series that belong to specified entity. Use optional 'keys' parameter to return specific time-series." +
@ApiOperation(value = "Get latest time series value (getLatestTimeseries)",
notes = "Returns all time series that belong to specified entity. Use optional 'keys' parameter to return specific time series." +
" The result is a JSON object. The format of the values depends on the 'useStrictDataTypes' parameter." +
" By default, all time-series values are converted to strings: \n\n"
" By default, all time series values are converted to strings: \n\n"
+ MARKDOWN_CODE_BLOCK_START
+ LATEST_TS_NON_STRICT_DATA_EXAMPLE
+ MARKDOWN_CODE_BLOCK_END
@ -282,8 +282,8 @@ public class TelemetryController extends BaseController {
(result, tenantId, entityId) -> getLatestTimeseriesValuesCallback(result, user, entityId, keysStr, useStrictDataTypes));
}
@ApiOperation(value = "Get time-series data (getTimeseries)",
notes = "Returns a range of time-series values for specified entity. " +
@ApiOperation(value = "Get time series data (getTimeseries)",
notes = "Returns a range of time series values for specified entity. " +
"Returns not aggregated data by default. " +
"Use aggregation function ('agg') and aggregation interval ('interval') to enable aggregation of the results on the database / server side. " +
"The aggregation is generally more efficient then fetching all records. \n\n"
@ -308,7 +308,7 @@ public class TelemetryController extends BaseController {
@RequestParam(name = "interval", defaultValue = "0") Long interval,
@Parameter(description = "A string value representing the timezone that will be used to calculate exact timestamps for 'WEEK', 'WEEK_ISO', 'MONTH' and 'QUARTER' interval types.")
@RequestParam(name = "timeZone", required = false) String timeZone,
@Parameter(description = "An integer value that represents a max number of timeseries data points to fetch." +
@Parameter(description = "An integer value that represents a max number of time series data points to fetch." +
" This parameter is used only in the case if 'agg' parameter is set to 'NONE'.", schema = @Schema(defaultValue = "100"))
@RequestParam(name = "limit", defaultValue = "100") Integer limit,
@Parameter(description = "A string value representing the aggregation function. " +
@ -406,8 +406,8 @@ public class TelemetryController extends BaseController {
}
@ApiOperation(value = "Save or update time-series data (saveEntityTelemetry)",
notes = "Creates or updates the entity time-series data based on the Entity Id and request payload." +
@ApiOperation(value = "Save or update time series data (saveEntityTelemetry)",
notes = "Creates or updates the entity time series data based on the Entity Id and request payload." +
SAVE_TIMESERIES_REQUEST_PAYLOAD +
"\n\n The scope parameter is not used in the API call implementation but should be specified whatever value because it is used as a path variable. "
+ INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@ -429,8 +429,8 @@ public class TelemetryController extends BaseController {
return saveTelemetry(getTenantId(), entityId, requestBody, 0L);
}
@ApiOperation(value = "Save or update time-series data with TTL (saveEntityTelemetryWithTTL)",
notes = "Creates or updates the entity time-series data based on the Entity Id and request payload." +
@ApiOperation(value = "Save or update time series data with TTL (saveEntityTelemetryWithTTL)",
notes = "Creates or updates the entity time series data based on the Entity Id and request payload." +
SAVE_TIMESERIES_REQUEST_PAYLOAD +
"\n\n The scope parameter is not used in the API call implementation but should be specified whatever value because it is used as a path variable. "
+ "\n\nThe ttl parameter takes affect only in case of Cassandra DB."
@ -454,21 +454,21 @@ public class TelemetryController extends BaseController {
return saveTelemetry(getTenantId(), entityId, requestBody, ttl);
}
@ApiOperation(value = "Delete entity time-series data (deleteEntityTimeseries)",
notes = "Delete time-series for selected entity based on entity id, entity type and keys." +
" Use 'deleteAllDataForKeys' to delete all time-series data." +
@ApiOperation(value = "Delete entity time series data (deleteEntityTimeseries)",
notes = "Delete time series for selected entity based on entity id, entity type and keys." +
" Use 'deleteAllDataForKeys' to delete all time series data." +
" Use 'startTs' and 'endTs' to specify time-range instead. " +
" Use 'deleteLatest' to delete latest value (stored in separate table for performance) if the value's timestamp matches the time-range. " +
" Use 'rewriteLatestIfDeleted' to rewrite latest value (stored in separate table for performance) if the value's timestamp matches the time-range and 'deleteLatest' param is true." +
" The replacement value will be fetched from the 'time-series' table, and its timestamp will be the most recent one before the defined time-range. " +
" The replacement value will be fetched from the 'time series' table, and its timestamp will be the most recent one before the defined time-range. " +
TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Timeseries for the selected keys in the request was removed. " +
"Platform creates an audit log event about entity timeseries removal with action type 'TIMESERIES_DELETED'."),
@ApiResponse(responseCode = "200", description = "Time series for the selected keys in the request was removed. " +
"Platform creates an audit log event about entity time series removal with action type 'TIMESERIES_DELETED'."),
@ApiResponse(responseCode = "400", description = "Platform returns a bad request in case if keys list is empty or start and end timestamp values is empty when deleteAllDataForKeys is set to false."),
@ApiResponse(responseCode = "401", description = "User is not authorized to delete entity timeseries for selected entity. Most likely, User belongs to different Customer or Tenant."),
@ApiResponse(responseCode = "401", description = "User is not authorized to delete entity time series for selected entity. Most likely, User belongs to different Customer or Tenant."),
@ApiResponse(responseCode = "500", description = "The exception was thrown during processing the request. " +
"Platform creates an audit log event about entity timeseries removal with action type 'TIMESERIES_DELETED' that includes an error stacktrace."),
"Platform creates an audit log event about entity time series removal with action type 'TIMESERIES_DELETED' that includes an error stacktrace."),
})
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/{entityType}/{entityId}/timeseries/delete", method = RequestMethod.DELETE)
@ -649,12 +649,12 @@ public class TelemetryController extends BaseController {
try {
telemetryJson = JsonParser.parseString(requestBody);
} catch (Exception e) {
return getImmediateDeferredResult("Unable to parse timeseries payload: Invalid JSON body!", HttpStatus.BAD_REQUEST);
return getImmediateDeferredResult("Unable to parse time series payload: Invalid JSON body!", HttpStatus.BAD_REQUEST);
}
try {
telemetryRequest = JsonConverter.convertToTelemetry(telemetryJson, System.currentTimeMillis());
} catch (Exception e) {
return getImmediateDeferredResult("Unable to parse timeseries payload. Invalid JSON body: " + e.getMessage(), HttpStatus.BAD_REQUEST);
return getImmediateDeferredResult("Unable to parse time series payload. Invalid JSON body: " + e.getMessage(), HttpStatus.BAD_REQUEST);
}
List<TsKvEntry> entries = new ArrayList<>();
for (Map.Entry<Long, List<KvEntry>> entry : telemetryRequest.entrySet()) {
@ -663,7 +663,7 @@ public class TelemetryController extends BaseController {
}
}
if (entries.isEmpty()) {
return getImmediateDeferredResult("No timeseries data found in request body!", HttpStatus.BAD_REQUEST);
return getImmediateDeferredResult("No time series data found in request body!", HttpStatus.BAD_REQUEST);
}
SecurityUser user = getCurrentUser();
return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.WRITE_TELEMETRY, entityIdSrc, (result, tenantId, entityId) -> {

1
application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java

@ -92,6 +92,7 @@ public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHand
errorCodeToStatusMap.put(ThingsboardErrorCode.TOO_MANY_REQUESTS, HttpStatus.TOO_MANY_REQUESTS);
errorCodeToStatusMap.put(ThingsboardErrorCode.TOO_MANY_UPDATES, HttpStatus.TOO_MANY_REQUESTS);
errorCodeToStatusMap.put(ThingsboardErrorCode.SUBSCRIPTION_VIOLATION, HttpStatus.FORBIDDEN);
errorCodeToStatusMap.put(ThingsboardErrorCode.VERSION_CONFLICT, HttpStatus.CONFLICT);
}
private static ThingsboardErrorCode statusToErrorCode(HttpStatus status) {

4
application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java

@ -182,7 +182,8 @@ public class EdgeEventSourcingListener {
return false;
}
if (oldEntity != null) {
User oldUser = (User) oldEntity;
user = JacksonUtil.clone(user);
User oldUser = JacksonUtil.clone((User) oldEntity);
cleanUpUserAdditionalInfo(oldUser);
cleanUpUserAdditionalInfo(user);
return !user.equals(oldUser);
@ -226,6 +227,7 @@ public class EdgeEventSourcingListener {
user.setAdditionalInfo(additionalInfo);
}
}
user.setVersion(null);
}
private EdgeEventType getEdgeEventTypeForEntityEvent(Object entity) {

2
application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java

@ -183,7 +183,7 @@ public class DefaultTbDeviceService extends AbstractTbEntityService implements T
try {
DeviceCredentials result = checkNotNull(deviceCredentialsService.updateDeviceCredentials(tenantId, deviceCredentials));
logEntityActionService.logEntityAction(tenantId, deviceId, device, device.getCustomerId(),
actionType, user, deviceCredentials);
actionType, user, result);
return result;
} catch (Exception e) {
logEntityActionService.logEntityAction(tenantId, emptyId(EntityType.DEVICE),

2
application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java

@ -147,7 +147,7 @@ public class ImagesUpdater {
try {
entity = dao.findById(TenantId.SYS_TENANT_ID, id.getId());
} catch (Exception e) {
log.error("Failed to update {} images: error fetching {} by id [{}]: {}", type, type, id.getId(), StringUtils.abbreviate(e.toString(), 1000));
log.error("Failed to update {} images: error fetching entity by id [{}]: {}", type, id.getId(), StringUtils.abbreviate(e.toString(), 1000));
continue;
}
try {

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

@ -22,6 +22,7 @@ import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.ExportableEntity;
import org.thingsboard.server.common.data.HasVersion;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
@ -74,6 +75,9 @@ public class DefaultEntityExportService<I extends EntityId, E extends Exportable
exportData.setEntity(entity);
exportData.setEntityType(entityId.getEntityType());
setAdditionalExportData(ctx, entity, exportData);
if (entity instanceof HasVersion hasVersion) {
hasVersion.setVersion(null);
}
var externalId = entity.getExternalId() != null ? entity.getExternalId() : entity.getId();
ctx.putExternalId(entityId, externalId);

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

@ -37,6 +37,7 @@ import org.thingsboard.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.HasAdditionalInfo;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.HasVersion;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.audit.ActionType;
@ -147,6 +148,9 @@ public abstract class AbstractBulkImportService<E extends HasId<? extends Entity
if (entity.getId() != null) {
importedEntityInfo.setOldEntity((E) entity.getClass().getConstructor(entity.getClass()).newInstance(entity));
importedEntityInfo.setUpdated(true);
if (entity instanceof HasVersion versionedEntity) {
versionedEntity.setVersion(null); // to overwrite the entity regardless of concurrent changes
}
} else {
setOwners(entity, user);
}

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

@ -28,6 +28,7 @@ import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.ExportableEntity;
import org.thingsboard.server.common.data.HasDefaultOption;
import org.thingsboard.server.common.data.HasVersion;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardException;
@ -161,6 +162,9 @@ public abstract class BaseEntityImportService<I extends EntityId, E extends Expo
protected void cleanupForComparison(E e) {
e.setTenantId(null);
e.setCreatedTime(0);
if (e instanceof HasVersion hasVersion) {
hasVersion.setVersion(null);
}
}
protected abstract E saveOrUpdate(EntitiesImportCtx ctx, E entity, D exportData, IdProvider idProvider);

2
application/src/main/resources/thingsboard.yml

@ -490,7 +490,7 @@ actors:
# Cache settings parameters
cache:
# caffeine or redis
# caffeine or redis(7.2 - latest compatible version)
type: "${CACHE_TYPE:caffeine}"
maximumPoolSize: "${CACHE_MAXIMUM_POOL_SIZE:16}" # max pool size to process futures that call the external cache
attributes:

2
application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java

@ -526,7 +526,7 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
JsonNode activateRequest = getActivateRequest(password);
ResultActions resultActions = doPost("/api/noauth/activate", activateRequest);
resultActions.andExpect(status().isOk());
return savedUser;
return doGet("/api/user/" + savedUser.getId(), User.class);
}
private JsonNode getActivateRequest(String password) throws Exception {

2
application/src/test/java/org/thingsboard/server/controller/AuditLogControllerTest.java

@ -161,7 +161,7 @@ public class AuditLogControllerTest extends AbstractControllerTest {
Device savedDevice = doPost("/api/device", device, Device.class);
for (int i = 0; i < 11; i++) {
savedDevice.setName("Device name" + i);
doPost("/api/device", savedDevice, Device.class);
savedDevice = doPost("/api/device", savedDevice, Device.class);
}
List<AuditLog> loadedAuditLogs = new ArrayList<>();

30
application/src/test/java/org/thingsboard/server/controller/DeviceControllerTest.java

@ -192,7 +192,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService);
savedDevice.setName("My new device");
doPost("/api/device", savedDevice, Device.class);
savedDevice = doPost("/api/device", savedDevice, Device.class);
testNotifyEntityAllOneTime(savedDevice, savedDevice.getId(), savedDevice.getId(), savedTenant.getId(),
tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.UPDATED);
@ -247,7 +247,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService);
savedDevice.setName("My new device");
doPost("/api/device", savedDevice, Device.class);
savedDevice = doPost("/api/device", savedDevice, Device.class);
testNotifyEntityAllOneTime(savedDevice, savedDevice.getId(), savedDevice.getId(), savedTenant.getId(),
tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.UPDATED);
@ -749,8 +749,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService);
doPost("/api/device/credentials", deviceCredentials)
.andExpect(status().isOk());
deviceCredentials = doPost("/api/device/credentials", deviceCredentials, DeviceCredentials.class);
testNotifyEntityMsgToEdgePushMsgToCoreOneTime(savedDevice, savedDevice.getId(), savedDevice.getId(), savedTenant.getId(),
tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.CREDENTIALS_UPDATED, deviceCredentials);
@ -1586,10 +1585,33 @@ public class DeviceControllerTest extends AbstractControllerTest {
Assert.assertEquals(newAttributeValue, actualAttribute.get("value"));
}
@Test
public void testSaveDeviceWithOutdatedVersion() throws Exception {
Device device = createDevice("Device v1.0");
assertThat(device.getVersion()).isOne();
device.setName("Device v2.0");
device = doPost("/api/device", device, Device.class);
assertThat(device.getVersion()).isEqualTo(2);
device.setName("Device v1.1");
device.setVersion(1L);
String response = doPost("/api/device", device).andExpect(status().isConflict())
.andReturn().getResponse().getContentAsString();
assertThat(JacksonUtil.toJsonNode(response).get("message").asText())
.containsIgnoringCase("already changed by someone else");
device.setVersion(null); // overriding entity
device = doPost("/api/device", device, Device.class);
assertThat(device.getName()).isEqualTo("Device v1.1");
assertThat(device.getVersion()).isEqualTo(3);
}
private Device createDevice(String name) {
Device device = new Device();
device.setName(name);
device.setType("default");
return doPost("/api/device", device, Device.class);
}
}

2
application/src/test/java/org/thingsboard/server/controller/EntityViewControllerTest.java

@ -176,7 +176,7 @@ public class EntityViewControllerTest extends AbstractControllerTest {
savedView.setName("New test entity view");
doPost("/api/entityView", savedView, EntityView.class);
savedView = doPost("/api/entityView", savedView, EntityView.class);
foundEntityView = doGet("/api/entityView/" + savedView.getId().getId().toString(), EntityView.class);
assertEquals(savedView, foundEntityView);

2
application/src/test/java/org/thingsboard/server/controller/RuleChainControllerTest.java

@ -120,7 +120,7 @@ public class RuleChainControllerTest extends AbstractControllerTest {
ActionType.ADDED);
savedRuleChain.setName("New RuleChain");
doPost("/api/ruleChain", savedRuleChain, RuleChain.class);
savedRuleChain = doPost("/api/ruleChain", savedRuleChain, RuleChain.class);
RuleChain foundRuleChain = doGet("/api/ruleChain/" + savedRuleChain.getId().getId().toString(), RuleChain.class);
Assert.assertEquals(savedRuleChain.getName(), foundRuleChain.getName());

8
application/src/test/java/org/thingsboard/server/controller/TenantControllerTest.java

@ -149,7 +149,7 @@ public class TenantControllerTest extends AbstractControllerTest {
testBroadcastEntityStateChangeEventTimeManyTimeTenant(savedTenant, ComponentLifecycleEvent.CREATED, 1);
savedTenant.setTitle("My new tenant");
saveTenant(savedTenant);
savedTenant = saveTenant(savedTenant);
Tenant foundTenant = doGet("/api/tenant/" + savedTenant.getId().getId().toString(), Tenant.class);
Assert.assertEquals(foundTenant.getTitle(), savedTenant.getTitle());
@ -470,7 +470,7 @@ public class TenantControllerTest extends AbstractControllerTest {
tenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class);
tenant.setTenantProfileId(tenantProfile.getId());
saveTenant(tenant);
tenant = saveTenant(tenant);
login(username, password);
@ -500,7 +500,7 @@ public class TenantControllerTest extends AbstractControllerTest {
tenantProfile2 = doPost("/api/tenantProfile", tenantProfile2, TenantProfile.class);
tenant.setTenantProfileId(tenantProfile2.getId());
saveTenant(tenant);
tenant = saveTenant(tenant);
login(username, password);
@ -542,7 +542,7 @@ public class TenantControllerTest extends AbstractControllerTest {
loginSysAdmin();
tenant.setTenantProfileId(null);
saveTenant(tenant);
tenant = saveTenant(tenant);
login(username, password);
for (Queue queue : foundTenantQueues) {

6
application/src/test/java/org/thingsboard/server/edge/DeviceEdgeTest.java

@ -204,8 +204,7 @@ public class DeviceEdgeTest extends AbstractEdgeTest {
Assert.assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId());
deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN);
deviceCredentials.setCredentialsId("access_token");
doPost("/api/device/credentials", deviceCredentials)
.andExpect(status().isOk());
deviceCredentials = doPost("/api/device/credentials", deviceCredentials, DeviceCredentials.class);
Assert.assertTrue(edgeImitator.waitForMessages());
AbstractMessage latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof DeviceCredentialsUpdateMsg);
@ -218,8 +217,7 @@ public class DeviceEdgeTest extends AbstractEdgeTest {
deviceCredentials.setCredentialsType(DeviceCredentialsType.X509_CERTIFICATE);
deviceCredentials.setCredentialsId(null);
deviceCredentials.setCredentialsValue("-----BEGIN RSA PRIVATE KEY-----");
doPost("/api/device/credentials", deviceCredentials)
.andExpect(status().isOk());
deviceCredentials = doPost("/api/device/credentials", deviceCredentials, DeviceCredentials.class);
Assert.assertTrue(edgeImitator.waitForMessages());
latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof DeviceCredentialsUpdateMsg);

8
application/src/test/java/org/thingsboard/server/edge/UserEdgeTest.java

@ -47,7 +47,7 @@ public class UserEdgeTest extends AbstractEdgeTest {
@Test
public void testCreateUpdateDeleteTenantUser() throws Exception {
// create user
edgeImitator.expectMessageAmount(3);
edgeImitator.expectMessageAmount(4);
User newTenantAdmin = new User();
newTenantAdmin.setAuthority(Authority.TENANT_ADMIN);
newTenantAdmin.setTenantId(tenantId);
@ -55,7 +55,7 @@ public class UserEdgeTest extends AbstractEdgeTest {
newTenantAdmin.setFirstName("Boris");
newTenantAdmin.setLastName("Johnson");
User savedTenantAdmin = createUser(newTenantAdmin, "tenant");
Assert.assertTrue(edgeImitator.waitForMessages()); // wait 3 messages - user update msg and x2 user credentials update msgs
Assert.assertTrue(edgeImitator.waitForMessages()); // wait 4 messages - x2 user update msg and x2 user credentials update msgs
Optional<UserUpdateMsg> userUpdateMsgOpt = edgeImitator.findMessageByType(UserUpdateMsg.class);
Assert.assertTrue(userUpdateMsgOpt.isPresent());
UserUpdateMsg userUpdateMsg = userUpdateMsgOpt.get();
@ -131,7 +131,7 @@ public class UserEdgeTest extends AbstractEdgeTest {
Assert.assertTrue(edgeImitator.waitForMessages());
// create user
edgeImitator.expectMessageAmount(3);
edgeImitator.expectMessageAmount(4);
User customerUser = new User();
customerUser.setAuthority(Authority.CUSTOMER_USER);
customerUser.setTenantId(tenantId);
@ -140,7 +140,7 @@ public class UserEdgeTest extends AbstractEdgeTest {
customerUser.setFirstName("John");
customerUser.setLastName("Edwards");
User savedCustomerUser = createUser(customerUser, "customer");
Assert.assertTrue(edgeImitator.waitForMessages()); // wait 3 messages - user update msg and x2 user credentials update msgs
Assert.assertTrue(edgeImitator.waitForMessages()); // wait 4 messages - x2 user update msg and x2 user credentials update msgs
Optional<UserUpdateMsg> userUpdateMsgOpt = edgeImitator.findMessageByType(UserUpdateMsg.class);
Assert.assertTrue(userUpdateMsgOpt.isPresent());
UserUpdateMsg userUpdateMsg = userUpdateMsgOpt.get();

10
application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/OtaLwM2MIntegrationTest.java

@ -59,7 +59,7 @@ public class OtaLwM2MIntegrationTest extends AbstractOtaLwM2MIntegrationTest {
createDeviceProfile(transportConfiguration);
LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(this.CLIENT_ENDPOINT_WITHOUT_FW_INFO));
final Device device = createDevice(deviceCredentials, this.CLIENT_ENDPOINT_WITHOUT_FW_INFO);
createNewClient(SECURITY_NO_SEC, null, false, this.CLIENT_ENDPOINT_WITHOUT_FW_INFO);
createNewClient(SECURITY_NO_SEC, null, false, this.CLIENT_ENDPOINT_WITHOUT_FW_INFO);
awaitObserveReadAll(0, device.getId().getId().toString());
device.setFirmwareId(createFirmware().getId());
@ -145,12 +145,14 @@ public class OtaLwM2MIntegrationTest extends AbstractOtaLwM2MIntegrationTest {
return tsKvEntries;
}
private boolean predicateForStatuses (List<TsKvEntry> ts) {
List<OtaPackageUpdateStatus> statuses = ts.stream().sorted(Comparator
.comparingLong(TsKvEntry::getTs)).map(KvEntry::getValueAsString)
private boolean predicateForStatuses(List<TsKvEntry> ts) {
List<OtaPackageUpdateStatus> statuses = ts.stream()
.sorted(Comparator.comparingLong(TsKvEntry::getTs))
.map(KvEntry::getValueAsString)
.map(OtaPackageUpdateStatus::valueOf)
.collect(Collectors.toList());
log.warn("{}", statuses);
return statuses.containsAll(expectedStatuses);
}
}

10
common/cache/src/main/java/org/thingsboard/server/cache/VersionedCaffeineTbCache.java

@ -39,14 +39,14 @@ public abstract class VersionedCaffeineTbCache<K extends Serializable, V extends
@Override
public void put(K key, V value) {
Long version = value != null ? value.getVersion() : 0;
Long version = getVersion(value);
if (version == null) {
return;
}
doPut(key, value, version);
}
private void doPut(K key, V value, Long version) {
if (version == null) {
return;
}
lock.lock();
try {
TbPair<Long, V> versionValuePair = doGet(key);
@ -85,7 +85,7 @@ public abstract class VersionedCaffeineTbCache<K extends Serializable, V extends
@Override
void doPutIfAbsent(K key, V value) {
cache.putIfAbsent(key, wrapValue(value, value != null ? value.getVersion() : 0));
cache.putIfAbsent(key, wrapValue(value, getVersion(value)));
}
private TbPair<Long, V> wrapValue(V value, Long version) {

23
common/cache/src/main/java/org/thingsboard/server/cache/VersionedRedisTbCache.java

@ -46,20 +46,11 @@ public abstract class VersionedRedisTbCache<K extends Serializable, V extends Se
redis.call('SET', key, newValueWithVersion, 'EX', expiration)
end
local function bytes_to_number(bytes)
local n = 0
for i = 1, 8 do
n = n * 256 + string.byte(bytes, i)
end
return n
end
-- Get the current version (first 8 bytes) of the current value
local currentVersionBytes = redis.call('GETRANGE', key, 0, 7)
if currentVersionBytes and #currentVersionBytes == 8 then
local currentVersion = bytes_to_number(currentVersionBytes)
local currentVersion = struct.unpack(">I8", currentVersionBytes)
if newVersion > currentVersion then
setNewValue()
end
@ -68,7 +59,7 @@ public abstract class VersionedRedisTbCache<K extends Serializable, V extends Se
setNewValue()
end
""");
static final byte[] SET_VERSIONED_VALUE_SHA = StringRedisSerializer.UTF_8.serialize("80e56cbbbb4bd9cb150d6537f1e7d8df4fddb252");
static final byte[] SET_VERSIONED_VALUE_SHA = StringRedisSerializer.UTF_8.serialize("0453cb1814135b706b4198b09a09f43c9f67bbfe");
public VersionedRedisTbCache(String cacheName, CacheSpecsMap cacheSpecsMap, RedisConnectionFactory connectionFactory, TBRedisCacheConfiguration configuration, TbRedisSerializer<K, V> valueSerializer) {
super(cacheName, cacheSpecsMap, connectionFactory, configuration, valueSerializer);
@ -168,14 +159,4 @@ public abstract class VersionedRedisTbCache<K extends Serializable, V extends Se
throw new NotImplementedException("evictOrPut is not supported by versioned cache");
}
private Long getVersion(V value) {
if (value == null) {
return 0L;
} else if (value.getVersion() != null) {
return value.getVersion();
} else {
return null;
}
}
}

10
common/cache/src/main/java/org/thingsboard/server/cache/VersionedTbCache.java

@ -50,4 +50,14 @@ public interface VersionedTbCache<K extends Serializable, V extends Serializable
void evict(K key, Long version);
default Long getVersion(V value) {
if (value == null) {
return 0L;
} else if (value.getVersion() != null) {
return value.getVersion();
} else {
return null;
}
}
}

2
common/cache/src/main/java/org/thingsboard/server/cache/device/DeviceCacheEvictEvent.java

@ -16,6 +16,7 @@
package org.thingsboard.server.cache.device;
import lombok.Data;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
@ -26,5 +27,6 @@ public class DeviceCacheEvictEvent {
private final DeviceId deviceId;
private final String newName;
private final String oldName;
private Device savedDevice;
}

4
common/cache/src/main/java/org/thingsboard/server/cache/device/DeviceCaffeineCache.java

@ -18,13 +18,13 @@ package org.thingsboard.server.cache.device;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;
import org.thingsboard.server.cache.CaffeineTbTransactionalCache;
import org.thingsboard.server.cache.VersionedCaffeineTbCache;
import org.thingsboard.server.common.data.CacheConstants;
import org.thingsboard.server.common.data.Device;
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true)
@Service("DeviceCache")
public class DeviceCaffeineCache extends CaffeineTbTransactionalCache<DeviceCacheKey, Device> {
public class DeviceCaffeineCache extends VersionedCaffeineTbCache<DeviceCacheKey, Device> {
public DeviceCaffeineCache(CacheManager cacheManager) {
super(cacheManager, CacheConstants.DEVICE_CACHE);

4
common/cache/src/main/java/org/thingsboard/server/cache/device/DeviceRedisCache.java

@ -21,9 +21,9 @@ import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.SerializationException;
import org.springframework.stereotype.Service;
import org.thingsboard.server.cache.CacheSpecsMap;
import org.thingsboard.server.cache.RedisTbTransactionalCache;
import org.thingsboard.server.cache.TBRedisCacheConfiguration;
import org.thingsboard.server.cache.TbRedisSerializer;
import org.thingsboard.server.cache.VersionedRedisTbCache;
import org.thingsboard.server.common.data.CacheConstants;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.util.ProtoUtils;
@ -31,7 +31,7 @@ import org.thingsboard.server.gen.transport.TransportProtos;
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis")
@Service("DeviceCache")
public class DeviceRedisCache extends RedisTbTransactionalCache<DeviceCacheKey, Device> {
public class DeviceRedisCache extends VersionedRedisTbCache<DeviceCacheKey, Device> {
public DeviceRedisCache(TBRedisCacheConfiguration configuration, CacheSpecsMap cacheSpecsMap, RedisConnectionFactory connectionFactory) {
super(CacheConstants.DEVICE_CACHE, cacheSpecsMap, connectionFactory, configuration, new TbRedisSerializer<>() {

5
common/data/src/main/java/org/thingsboard/server/common/data/Customer.java

@ -29,7 +29,7 @@ import org.thingsboard.server.common.data.validation.Length;
import org.thingsboard.server.common.data.validation.NoXss;
@EqualsAndHashCode(callSuper = true)
public class Customer extends ContactBased<CustomerId> implements HasTenantId, ExportableEntity<CustomerId>, HasTitle {
public class Customer extends ContactBased<CustomerId> implements HasTenantId, ExportableEntity<CustomerId>, HasTitle, HasVersion {
private static final long serialVersionUID = -1599722990298929275L;
@ -42,6 +42,8 @@ public class Customer extends ContactBased<CustomerId> implements HasTenantId, E
@Getter @Setter
private CustomerId externalId;
@Getter @Setter
private Long version;
public Customer() {
super();
@ -56,6 +58,7 @@ public class Customer extends ContactBased<CustomerId> implements HasTenantId, E
this.tenantId = customer.getTenantId();
this.title = customer.getTitle();
this.externalId = customer.getExternalId();
this.version = customer.getVersion();
}
public TenantId getTenantId() {

8
common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java

@ -18,6 +18,8 @@ package org.thingsboard.server.common.data;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import lombok.Getter;
import lombok.Setter;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.TenantId;
@ -29,7 +31,7 @@ import java.util.Objects;
import java.util.Set;
@Schema
public class DashboardInfo extends BaseData<DashboardId> implements HasName, HasTenantId, HasTitle, HasImage {
public class DashboardInfo extends BaseData<DashboardId> implements HasName, HasTenantId, HasTitle, HasImage, HasVersion {
private static final long serialVersionUID = -9080404114760433799L;
@ -43,6 +45,9 @@ public class DashboardInfo extends BaseData<DashboardId> implements HasName, Has
private boolean mobileHide;
private Integer mobileOrder;
@Getter @Setter
private Long version;
public DashboardInfo() {
super();
}
@ -59,6 +64,7 @@ public class DashboardInfo extends BaseData<DashboardId> implements HasName, Has
this.assignedCustomers = dashboardInfo.getAssignedCustomers();
this.mobileHide = dashboardInfo.isMobileHide();
this.mobileOrder = dashboardInfo.getMobileOrder();
this.version = dashboardInfo.getVersion();
}
@Schema(description = "JSON object with the dashboard Id. " +

37
common/data/src/main/java/org/thingsboard/server/common/data/Device.java

@ -22,6 +22,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.device.data.DeviceData;
import org.thingsboard.server.common.data.id.CustomerId;
@ -38,8 +39,9 @@ import java.util.Optional;
@Schema
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Slf4j
public class Device extends BaseDataWithAdditionalInfo<DeviceId> implements HasLabel, HasTenantId, HasCustomerId, HasOtaPackage, ExportableEntity<DeviceId> {
public class Device extends BaseDataWithAdditionalInfo<DeviceId> implements HasLabel, HasTenantId, HasCustomerId, HasOtaPackage, HasVersion, ExportableEntity<DeviceId> {
private static final long serialVersionUID = 2807343040519543363L;
@ -65,6 +67,8 @@ public class Device extends BaseDataWithAdditionalInfo<DeviceId> implements HasL
@Getter @Setter
private DeviceId externalId;
@Getter @Setter
private Long version;
public Device() {
super();
@ -86,6 +90,7 @@ public class Device extends BaseDataWithAdditionalInfo<DeviceId> implements HasL
this.firmwareId = device.getFirmwareId();
this.softwareId = device.getSoftwareId();
this.externalId = device.getExternalId();
this.version = device.getVersion();
}
public Device updateDevice(Device device) {
@ -100,6 +105,7 @@ public class Device extends BaseDataWithAdditionalInfo<DeviceId> implements HasL
this.setSoftwareId(device.getSoftwareId());
Optional.ofNullable(device.getAdditionalInfo()).ifPresent(this::setAdditionalInfo);
this.setExternalId(device.getExternalId());
this.setVersion(device.getVersion());
return this;
}
@ -225,33 +231,4 @@ public class Device extends BaseDataWithAdditionalInfo<DeviceId> implements HasL
return super.getAdditionalInfo();
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Device [tenantId=");
builder.append(tenantId);
builder.append(", customerId=");
builder.append(customerId);
builder.append(", name=");
builder.append(name);
builder.append(", type=");
builder.append(type);
builder.append(", label=");
builder.append(label);
builder.append(", deviceProfileId=");
builder.append(deviceProfileId);
builder.append(", deviceData=");
builder.append(firmwareId);
builder.append(", firmwareId=");
builder.append(deviceData);
builder.append(", additionalInfo=");
builder.append(getAdditionalInfo());
builder.append(", createdTime=");
builder.append(createdTime);
builder.append(", id=");
builder.append(id);
builder.append("]");
return builder.toString();
}
}

2
common/data/src/main/java/org/thingsboard/server/common/data/DeviceInfo.java

@ -18,11 +18,13 @@ package org.thingsboard.server.common.data;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.thingsboard.server.common.data.id.DeviceId;
@Schema
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class DeviceInfo extends Device {
private static final long serialVersionUID = -3004579925090663691L;

4
common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java

@ -42,7 +42,7 @@ import java.io.IOException;
@ToString(exclude = {"image", "profileDataBytes"})
@EqualsAndHashCode(callSuper = true)
@Slf4j
public class DeviceProfile extends BaseData<DeviceProfileId> implements HasName, HasTenantId, HasOtaPackage, HasRuleEngineProfile, ExportableEntity<DeviceProfileId>, HasImage, HasDefaultOption {
public class DeviceProfile extends BaseData<DeviceProfileId> implements HasName, HasTenantId, HasOtaPackage, HasRuleEngineProfile, ExportableEntity<DeviceProfileId>, HasImage, HasDefaultOption, HasVersion {
private static final long serialVersionUID = 6998485460273302018L;
@ -97,6 +97,7 @@ public class DeviceProfile extends BaseData<DeviceProfileId> implements HasName,
private RuleChainId defaultEdgeRuleChainId;
private DeviceProfileId externalId;
private Long version;
public DeviceProfile() {
super();
@ -122,6 +123,7 @@ public class DeviceProfile extends BaseData<DeviceProfileId> implements HasName,
this.softwareId = deviceProfile.getSoftwareId();
this.defaultEdgeRuleChainId = deviceProfile.getDefaultEdgeRuleChainId();
this.externalId = deviceProfile.getExternalId();
this.version = deviceProfile.getVersion();
}
@Schema(description = "JSON object with the device profile Id. " +

8
common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java

@ -36,7 +36,7 @@ import org.thingsboard.server.common.data.validation.NoXss;
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class EntityView extends BaseDataWithAdditionalInfo<EntityViewId>
implements HasName, HasTenantId, HasCustomerId, ExportableEntity<EntityViewId> {
implements HasName, HasTenantId, HasCustomerId, HasVersion, ExportableEntity<EntityViewId> {
private static final long serialVersionUID = 5582010124562018986L;
@ -60,6 +60,7 @@ public class EntityView extends BaseDataWithAdditionalInfo<EntityViewId>
private long endTimeMs;
private EntityViewId externalId;
private Long version;
public EntityView() {
super();
@ -80,6 +81,7 @@ public class EntityView extends BaseDataWithAdditionalInfo<EntityViewId>
this.startTimeMs = entityView.getStartTimeMs();
this.endTimeMs = entityView.getEndTimeMs();
this.externalId = entityView.getExternalId();
this.version = entityView.getVersion();
}
@Schema(description = "JSON object with Customer Id. Use 'assignEntityViewToCustomer' to change the Customer Id.", accessMode = Schema.AccessMode.READ_ONLY)
@ -102,7 +104,7 @@ public class EntityView extends BaseDataWithAdditionalInfo<EntityViewId>
@Schema(description = "JSON object with the Entity View Id. " +
"Specify this field to update the Entity View. " +
"Referencing non-existing Entity View Id will cause error. " +
"Omit this field to create new Entity View." )
"Omit this field to create new Entity View.")
@Override
public EntityViewId getId() {
return super.getId();
@ -114,7 +116,7 @@ public class EntityView extends BaseDataWithAdditionalInfo<EntityViewId>
return super.getCreatedTime();
}
@Schema(description = "Additional parameters of the device",implementation = com.fasterxml.jackson.databind.JsonNode.class)
@Schema(description = "Additional parameters of the device", implementation = com.fasterxml.jackson.databind.JsonNode.class)
@Override
public JsonNode getAdditionalInfo() {
return super.getAdditionalInfo();

5
common/data/src/main/java/org/thingsboard/server/common/data/HasVersion.java

@ -16,5 +16,10 @@
package org.thingsboard.server.common.data;
public interface HasVersion {
Long getVersion();
default void setVersion(Long version) {
}
}

12
common/data/src/main/java/org/thingsboard/server/common/data/Tenant.java

@ -20,6 +20,8 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.TenantProfileId;
import org.thingsboard.server.common.data.validation.Length;
@ -27,7 +29,7 @@ import org.thingsboard.server.common.data.validation.NoXss;
@Schema
@EqualsAndHashCode(callSuper = true)
public class Tenant extends ContactBased<TenantId> implements HasTenantId, HasTitle {
public class Tenant extends ContactBased<TenantId> implements HasTenantId, HasTitle, HasVersion {
private static final long serialVersionUID = 8057243243859922101L;
@ -43,6 +45,9 @@ public class Tenant extends ContactBased<TenantId> implements HasTenantId, HasTi
@Schema(description = "JSON object with Tenant Profile Id")
private TenantProfileId tenantProfileId;
@Getter @Setter
private Long version;
public Tenant() {
super();
}
@ -56,6 +61,7 @@ public class Tenant extends ContactBased<TenantId> implements HasTenantId, HasTi
this.title = tenant.getTitle();
this.region = tenant.getRegion();
this.tenantProfileId = tenant.getTenantProfileId();
this.version = tenant.getVersion();
}
public String getTitle() {
@ -98,7 +104,7 @@ public class Tenant extends ContactBased<TenantId> implements HasTenantId, HasTi
@Schema(description = "JSON object with the tenant Id. " +
"Specify this field to update the tenant. " +
"Referencing non-existing tenant Id will cause error. " +
"Omit this field to create new tenant." )
"Omit this field to create new tenant.")
@Override
public TenantId getId() {
return super.getId();
@ -158,7 +164,7 @@ public class Tenant extends ContactBased<TenantId> implements HasTenantId, HasTi
return super.getEmail();
}
@Schema(description = "Additional parameters of the device",implementation = com.fasterxml.jackson.databind.JsonNode.class)
@Schema(description = "Additional parameters of the device", implementation = com.fasterxml.jackson.databind.JsonNode.class)
@Override
public JsonNode getAdditionalInfo() {
return super.getAdditionalInfo();

9
common/data/src/main/java/org/thingsboard/server/common/data/User.java

@ -20,6 +20,8 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
@ -33,7 +35,7 @@ import static org.apache.commons.lang3.StringUtils.isNotEmpty;
@Schema
@EqualsAndHashCode(callSuper = true)
public class User extends BaseDataWithAdditionalInfo<UserId> implements HasName, HasTenantId, HasCustomerId, NotificationRecipient {
public class User extends BaseDataWithAdditionalInfo<UserId> implements HasName, HasTenantId, HasCustomerId, NotificationRecipient, HasVersion {
private static final long serialVersionUID = 8250339805336035966L;
@ -50,6 +52,9 @@ public class User extends BaseDataWithAdditionalInfo<UserId> implements HasName,
@NoXss
private String phone;
@Getter @Setter
private Long version;
public User() {
super();
}
@ -67,6 +72,7 @@ public class User extends BaseDataWithAdditionalInfo<UserId> implements HasName,
this.firstName = user.getFirstName();
this.lastName = user.getLastName();
this.phone = user.getPhone();
this.version = user.getVersion();
}
@ -222,4 +228,5 @@ public class User extends BaseDataWithAdditionalInfo<UserId> implements HasName,
public boolean isCustomerUser() {
return !isSystemAdmin() && !isTenantAdmin();
}
}

7
common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java

@ -25,6 +25,7 @@ import org.thingsboard.server.common.data.ExportableEntity;
import org.thingsboard.server.common.data.HasCustomerId;
import org.thingsboard.server.common.data.HasLabel;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.HasVersion;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.AssetProfileId;
import org.thingsboard.server.common.data.id.CustomerId;
@ -36,7 +37,7 @@ import java.util.Optional;
@Schema
@EqualsAndHashCode(callSuper = true)
public class Asset extends BaseDataWithAdditionalInfo<AssetId> implements HasLabel, HasTenantId, HasCustomerId, ExportableEntity<AssetId> {
public class Asset extends BaseDataWithAdditionalInfo<AssetId> implements HasLabel, HasTenantId, HasCustomerId, HasVersion, ExportableEntity<AssetId> {
private static final long serialVersionUID = 2807343040519543363L;
@ -56,6 +57,8 @@ public class Asset extends BaseDataWithAdditionalInfo<AssetId> implements HasLab
@Getter @Setter
private AssetId externalId;
@Getter @Setter
private Long version;
public Asset() {
super();
@ -74,6 +77,7 @@ public class Asset extends BaseDataWithAdditionalInfo<AssetId> implements HasLab
this.label = asset.getLabel();
this.assetProfileId = asset.getAssetProfileId();
this.externalId = asset.getExternalId();
this.version = asset.getVersion();
}
public void update(Asset asset) {
@ -85,6 +89,7 @@ public class Asset extends BaseDataWithAdditionalInfo<AssetId> implements HasLab
this.assetProfileId = asset.getAssetProfileId();
Optional.ofNullable(asset.getAdditionalInfo()).ifPresent(this::setAdditionalInfo);
this.externalId = asset.getExternalId();
this.version = asset.getVersion();
}
@Schema(description = "JSON object with the asset Id. " +

5
common/data/src/main/java/org/thingsboard/server/common/data/asset/AssetProfile.java

@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.HasImage;
import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.HasRuleEngineProfile;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.HasVersion;
import org.thingsboard.server.common.data.id.AssetProfileId;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.RuleChainId;
@ -39,7 +40,7 @@ import org.thingsboard.server.common.data.validation.NoXss;
@ToString(exclude = {"image"})
@EqualsAndHashCode(callSuper = true)
@Slf4j
public class AssetProfile extends BaseData<AssetProfileId> implements HasName, HasTenantId, HasRuleEngineProfile, ExportableEntity<AssetProfileId>, HasImage, HasDefaultOption {
public class AssetProfile extends BaseData<AssetProfileId> implements HasName, HasTenantId, HasRuleEngineProfile, ExportableEntity<AssetProfileId>, HasImage, HasDefaultOption, HasVersion {
private static final long serialVersionUID = 6998485460273302018L;
@ -74,6 +75,7 @@ public class AssetProfile extends BaseData<AssetProfileId> implements HasName, H
private RuleChainId defaultEdgeRuleChainId;
private AssetProfileId externalId;
private Long version;
public AssetProfile() {
super();
@ -95,6 +97,7 @@ public class AssetProfile extends BaseData<AssetProfileId> implements HasName, H
this.defaultQueueName = assetProfile.getDefaultQueueName();
this.defaultEdgeRuleChainId = assetProfile.getDefaultEdgeRuleChainId();
this.externalId = assetProfile.getExternalId();
this.version = assetProfile.getVersion();
}
@Schema(description = "JSON object with the asset profile Id. " +

9
common/data/src/main/java/org/thingsboard/server/common/data/edge/Edge.java

@ -17,12 +17,14 @@ package org.thingsboard.server.common.data.edge;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.thingsboard.server.common.data.BaseDataWithAdditionalInfo;
import org.thingsboard.server.common.data.HasCustomerId;
import org.thingsboard.server.common.data.HasLabel;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.HasVersion;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.RuleChainId;
@ -34,7 +36,7 @@ import org.thingsboard.server.common.data.validation.NoXss;
@EqualsAndHashCode(callSuper = true)
@ToString
@Setter
public class Edge extends BaseDataWithAdditionalInfo<EdgeId> implements HasLabel, HasTenantId, HasCustomerId {
public class Edge extends BaseDataWithAdditionalInfo<EdgeId> implements HasLabel, HasTenantId, HasCustomerId, HasVersion {
private static final long serialVersionUID = 4934987555236873728L;
@ -57,6 +59,9 @@ public class Edge extends BaseDataWithAdditionalInfo<EdgeId> implements HasLabel
@Length(fieldName = "secret")
private String secret;
@Getter
private Long version;
public Edge() {
super();
}
@ -75,6 +80,7 @@ public class Edge extends BaseDataWithAdditionalInfo<EdgeId> implements HasLabel
this.name = edge.getName();
this.routingKey = edge.getRoutingKey();
this.secret = edge.getSecret();
this.version = edge.getVersion();
}
public void update(Edge edge) {
@ -86,6 +92,7 @@ public class Edge extends BaseDataWithAdditionalInfo<EdgeId> implements HasLabel
this.name = edge.getName();
this.routingKey = edge.getRoutingKey();
this.secret = edge.getSecret();
this.version = edge.getVersion();
}
@Schema(description = "JSON object with the Edge Id. " +

24
common/data/src/main/java/org/thingsboard/server/common/data/exception/EntityVersionMismatchException.java

@ -0,0 +1,24 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.exception;
public class EntityVersionMismatchException extends RuntimeException {
public EntityVersionMismatchException(String message, Throwable cause) {
super(message, cause);
}
}

1
common/data/src/main/java/org/thingsboard/server/common/data/exception/ThingsboardErrorCode.java

@ -29,6 +29,7 @@ public enum ThingsboardErrorCode {
ITEM_NOT_FOUND(32),
TOO_MANY_REQUESTS(33),
TOO_MANY_UPDATES(34),
VERSION_CONFLICT(35),
SUBSCRIPTION_VIOLATION(40),
PASSWORD_VIOLATION(45);

7
common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java

@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.ExportableEntity;
import org.thingsboard.server.common.data.HasDefaultOption;
import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.HasVersion;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.id.TenantId;
@ -36,7 +37,7 @@ import org.thingsboard.server.common.data.validation.NoXss;
@Data
@EqualsAndHashCode(callSuper = true)
@Slf4j
public class RuleChain extends BaseDataWithAdditionalInfo<RuleChainId> implements HasName, HasTenantId, ExportableEntity<RuleChainId>, HasDefaultOption {
public class RuleChain extends BaseDataWithAdditionalInfo<RuleChainId> implements HasName, HasTenantId, ExportableEntity<RuleChainId>, HasDefaultOption, HasVersion {
private static final long serialVersionUID = -5656679015121935465L;
@ -58,6 +59,7 @@ public class RuleChain extends BaseDataWithAdditionalInfo<RuleChainId> implement
private transient JsonNode configuration;
private RuleChainId externalId;
private Long version;
@JsonIgnore
private byte[] configurationBytes;
@ -79,6 +81,7 @@ public class RuleChain extends BaseDataWithAdditionalInfo<RuleChainId> implement
this.root = ruleChain.isRoot();
this.setConfiguration(ruleChain.getConfiguration());
this.setExternalId(ruleChain.getExternalId());
this.version = ruleChain.getVersion();
}
@Override
@ -89,7 +92,7 @@ public class RuleChain extends BaseDataWithAdditionalInfo<RuleChainId> implement
@Schema(description = "JSON object with the Rule Chain Id. " +
"Specify this field to update the Rule Chain. " +
"Referencing non-existing Rule Chain Id will cause error. " +
"Omit this field to create new rule chain." )
"Omit this field to create new rule chain.")
@Override
public RuleChainId getId() {
return super.getId();

12
common/data/src/main/java/org/thingsboard/server/common/data/security/DeviceCredentials.java

@ -17,20 +17,26 @@ package org.thingsboard.server.common.data.security;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import org.thingsboard.server.common.data.BaseData;
import org.thingsboard.server.common.data.HasVersion;
import org.thingsboard.server.common.data.id.DeviceCredentialsId;
import org.thingsboard.server.common.data.id.DeviceId;
@Schema
@EqualsAndHashCode(callSuper = true)
public class DeviceCredentials extends BaseData<DeviceCredentialsId> implements DeviceCredentialsFilter {
public class DeviceCredentials extends BaseData<DeviceCredentialsId> implements DeviceCredentialsFilter, HasVersion {
private static final long serialVersionUID = -7869261127032877765L;
private DeviceId deviceId;
private DeviceCredentialsType credentialsType;
private String credentialsId;
private String credentialsValue;
@Getter @Setter
private Long version;
public DeviceCredentials() {
super();
}
@ -45,6 +51,7 @@ public class DeviceCredentials extends BaseData<DeviceCredentialsId> implements
this.credentialsType = deviceCredentials.getCredentialsType();
this.credentialsId = deviceCredentials.getCredentialsId();
this.credentialsValue = deviceCredentials.getCredentialsValue();
this.version = deviceCredentials.getVersion();
}
@Schema(requiredMode = Schema.RequiredMode.REQUIRED, accessMode = Schema.AccessMode.READ_ONLY, description = "The Id is automatically generated during device creation. " +
@ -111,4 +118,5 @@ public class DeviceCredentials extends BaseData<DeviceCredentialsId> implements
+ credentialsId + ", credentialsValue=" + credentialsValue + ", createdTime=" + createdTime + ", id="
+ id + "]";
}
}

2
common/data/src/main/java/org/thingsboard/server/common/data/sync/JsonTbEntity.java

@ -60,6 +60,6 @@ import java.lang.annotation.Target;
@Type(name = "NOTIFICATION_RULE", value = NotificationRule.class),
@Type(name = "TB_RESOURCE", value = TbResource.class)
})
@JsonIgnoreProperties(value = {"tenantId", "createdTime"}, ignoreUnknown = true)
@JsonIgnoreProperties(value = {"tenantId", "createdTime", "version"}, ignoreUnknown = true)
public @interface JsonTbEntity {
}

2
common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/DeviceExportData.java

@ -30,7 +30,7 @@ import org.thingsboard.server.common.data.security.DeviceCredentials;
public class DeviceExportData extends EntityExportData<Device> {
@JsonProperty(index = 3)
@JsonIgnoreProperties({"id", "deviceId", "createdTime"})
@JsonIgnoreProperties({"id", "deviceId", "createdTime", "version"})
private DeviceCredentials credentials;
@JsonIgnore

1
common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/EntityExportData.java

@ -95,4 +95,5 @@ public class EntityExportData<E extends ExportableEntity<? extends EntityId>> {
public boolean hasRelations() {
return relations != null;
}
}

11
common/data/src/main/java/org/thingsboard/server/common/data/widget/BaseWidgetType.java

@ -17,16 +17,19 @@ package org.thingsboard.server.common.data.widget;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.BaseData;
import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.HasVersion;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.WidgetTypeId;
import org.thingsboard.server.common.data.validation.Length;
import org.thingsboard.server.common.data.validation.NoXss;
@Data
public class BaseWidgetType extends BaseData<WidgetTypeId> implements HasName, HasTenantId {
@EqualsAndHashCode(callSuper = true)
public class BaseWidgetType extends BaseData<WidgetTypeId> implements HasName, HasTenantId, HasVersion {
private static final long serialVersionUID = 8388684344603660756L;
@ -47,6 +50,8 @@ public class BaseWidgetType extends BaseData<WidgetTypeId> implements HasName, H
@Schema(description = "Whether widget type is SCADA symbol.", example = "true")
private boolean scada;
private Long version;
public BaseWidgetType() {
super();
}
@ -62,12 +67,13 @@ public class BaseWidgetType extends BaseData<WidgetTypeId> implements HasName, H
this.name = widgetType.getName();
this.deprecated = widgetType.isDeprecated();
this.scada = widgetType.isScada();
this.version = widgetType.getVersion();
}
@Schema(description = "JSON object with the Widget Type Id. " +
"Specify this field to update the Widget Type. " +
"Referencing non-existing Widget Type Id will cause error. " +
"Omit this field to create new Widget Type." )
"Omit this field to create new Widget Type.")
@Override
public WidgetTypeId getId() {
return super.getId();
@ -78,4 +84,5 @@ public class BaseWidgetType extends BaseData<WidgetTypeId> implements HasName, H
public long getCreatedTime() {
return super.getCreatedTime();
}
}

9
common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetTypeDetails.java

@ -18,8 +18,7 @@ package org.thingsboard.server.common.data.widget;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.ExportableEntity;
import org.thingsboard.server.common.data.HasImage;
import org.thingsboard.server.common.data.HasName;
@ -29,7 +28,8 @@ import org.thingsboard.server.common.data.validation.Length;
import org.thingsboard.server.common.data.validation.NoXss;
@Data
@JsonPropertyOrder({ "fqn", "name", "deprecated", "image", "description", "descriptor", "externalId" })
@EqualsAndHashCode(callSuper = true)
@JsonPropertyOrder({"fqn", "name", "deprecated", "image", "description", "descriptor", "externalId"})
public class WidgetTypeDetails extends WidgetType implements HasName, HasTenantId, HasImage, ExportableEntity<WidgetTypeId> {
@Schema(description = "Relative or external image URL. Replaced with image data URL (Base64) in case of relative URL and 'inlineImages' option enabled.")
@ -42,8 +42,6 @@ public class WidgetTypeDetails extends WidgetType implements HasName, HasTenantI
@Schema(description = "Tags of the widget type")
private String[] tags;
@Getter
@Setter
private WidgetTypeId externalId;
public WidgetTypeDetails() {
@ -65,4 +63,5 @@ public class WidgetTypeDetails extends WidgetType implements HasName, HasTenantI
this.tags = widgetTypeDetails.getTags();
this.externalId = widgetTypeDetails.getExternalId();
}
}

7
common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetsBundle.java

@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.HasImage;
import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.HasTitle;
import org.thingsboard.server.common.data.HasVersion;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.WidgetsBundleId;
import org.thingsboard.server.common.data.validation.Length;
@ -33,7 +34,7 @@ import org.thingsboard.server.common.data.validation.NoXss;
@Schema
@EqualsAndHashCode(callSuper = true)
public class WidgetsBundle extends BaseData<WidgetsBundleId> implements HasName, HasTenantId, ExportableEntity<WidgetsBundleId>, HasTitle, HasImage {
public class WidgetsBundle extends BaseData<WidgetsBundleId> implements HasName, HasTenantId, ExportableEntity<WidgetsBundleId>, HasTitle, HasImage, HasVersion {
private static final long serialVersionUID = -7627368878362410489L;
@ -81,6 +82,9 @@ public class WidgetsBundle extends BaseData<WidgetsBundleId> implements HasName,
@Getter
@Setter
private WidgetsBundleId externalId;
@Getter
@Setter
private Long version;
public WidgetsBundle() {
super();
@ -100,6 +104,7 @@ public class WidgetsBundle extends BaseData<WidgetsBundleId> implements HasName,
this.description = widgetsBundle.getDescription();
this.order = widgetsBundle.getOrder();
this.externalId = widgetsBundle.getExternalId();
this.version = widgetsBundle.getVersion();
}
@Schema(description = "JSON object with the Widget Bundle Id. " +

1
common/message/src/main/java/org/thingsboard/server/common/msg/tools/MaxPayloadSizeExceededException.java

@ -20,4 +20,5 @@ public class MaxPayloadSizeExceededException extends RuntimeException {
public MaxPayloadSizeExceededException() {
super("Payload size exceeds the limit");
}
}

24
common/proto/src/main/java/org/thingsboard/server/common/util/ProtoUtils.java

@ -17,9 +17,7 @@ package org.thingsboard.server.common.util;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.Nullable;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.ApiUsageState;
import org.thingsboard.server.common.data.ApiUsageStateValue;
@ -570,6 +568,9 @@ public class ProtoUtils {
if (isNotNull(device.getDeviceDataBytes())) {
builder.setDeviceData(ByteString.copyFrom(device.getDeviceDataBytes()));
}
if (isNotNull(device.getVersion())) {
builder.setVersion(device.getVersion());
}
return builder.build();
}
@ -601,6 +602,9 @@ public class ProtoUtils {
if (proto.hasDeviceData()) {
device.setDeviceDataBytes(proto.getDeviceData().toByteArray());
}
if (proto.hasVersion()) {
device.setVersion(proto.getVersion());
}
return device;
}
@ -656,6 +660,9 @@ public class ProtoUtils {
builder.setDefaultEdgeRuleChainIdMSB(getMsb(deviceProfile.getDefaultEdgeRuleChainId()))
.setDefaultEdgeRuleChainIdLSB(getLsb(deviceProfile.getDefaultEdgeRuleChainId()));
}
if (isNotNull(deviceProfile.getVersion())) {
builder.setVersion(deviceProfile.getVersion());
}
return builder.build();
}
@ -701,6 +708,9 @@ public class ProtoUtils {
if (proto.hasDefaultEdgeRuleChainIdMSB() && proto.hasDefaultEdgeRuleChainIdLSB()) {
deviceProfile.setDefaultEdgeRuleChainId(getEntityId(proto.getDefaultEdgeRuleChainIdMSB(), proto.getDefaultEdgeRuleChainIdLSB(), RuleChainId::new));
}
if (proto.hasVersion()) {
deviceProfile.setVersion(proto.getVersion());
}
return deviceProfile;
}
@ -743,6 +753,9 @@ public class ProtoUtils {
if (isNotNull(tenant.getAdditionalInfo())) {
builder.setAdditionalInfo(JacksonUtil.toString(tenant.getAdditionalInfo()));
}
if (isNotNull(tenant.getVersion())) {
builder.setVersion(tenant.getVersion());
}
return builder.build();
}
@ -782,6 +795,9 @@ public class ProtoUtils {
if (proto.hasAdditionalInfo()) {
tenant.setAdditionalInfo(JacksonUtil.toJsonNode(proto.getAdditionalInfo()));
}
if (proto.hasVersion()) {
tenant.setVersion(proto.getVersion());
}
return tenant;
}
@ -996,6 +1012,9 @@ public class ProtoUtils {
if (deviceCredentials.getCredentialsValue() != null) {
builder.setCredentialsValue(deviceCredentials.getCredentialsValue());
}
if (deviceCredentials.getVersion() != null) {
builder.setVersion(deviceCredentials.getVersion());
}
return builder.build();
}
@ -1007,6 +1026,7 @@ public class ProtoUtils {
deviceCredentials.setCredentialsId(proto.getCredentialsId());
deviceCredentials.setCredentialsType(DeviceCredentialsType.valueOf(proto.getCredentialsType().name()));
deviceCredentials.setCredentialsValue(proto.hasCredentialsValue() ? proto.getCredentialsValue() : null);
deviceCredentials.setVersion(proto.hasVersion() ? proto.getVersion() : null);
return deviceCredentials;
}

4
common/proto/src/main/proto/queue.proto

@ -217,6 +217,7 @@ message DeviceProto {
optional int64 softwareIdLSB = 18;
optional int64 externalIdMSB = 19;
optional int64 externalIdLSB = 20;
optional int64 version = 21;
}
message DeviceProfileProto {
@ -247,6 +248,7 @@ message DeviceProfileProto {
optional int64 defaultEdgeRuleChainIdLSB = 25;
optional int64 externalIdMSB = 26;
optional int64 externalIdLSB = 27;
optional int64 version = 28;
}
message TenantProto {
@ -266,6 +268,7 @@ message TenantProto {
optional string phone = 14;
optional string email = 15;
optional string additionalInfo = 16;
optional int64 version = 17;
}
message TenantProfileProto {
@ -677,6 +680,7 @@ message DeviceCredentialsProto {
CredentialsType credentialsType = 6;
string credentialsId = 7;
optional string credentialsValue = 8;
optional int64 version = 9;
}
message CredentialsDataProto {

209
common/transport/coap/src/test/java/org/thingsboard/server/transport/coap/efento/CoapEfentTransportResourceTest.java

@ -18,17 +18,57 @@ package org.thingsboard.server.transport.coap.efento;
import com.google.protobuf.ByteString;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType;
import org.thingsboard.server.gen.transport.coap.MeasurementsProtos;
import org.thingsboard.server.gen.transport.coap.MeasurementsProtos.ProtoMeasurements;
import org.thingsboard.server.transport.coap.CoapTransportContext;
import org.thingsboard.server.transport.coap.efento.utils.CoapEfentoUtils;
import java.nio.ByteBuffer;
import java.time.Instant;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.mock;
import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_AMBIENT_LIGHT;
import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_ATMOSPHERIC_PRESSURE;
import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_BREATH_VOC;
import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_CO2_EQUIVALENT;
import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_CURRENT;
import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_CURRENT_PRECISE;
import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_DIFFERENTIAL_PRESSURE;
import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_DISTANCE_MM;
import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_ELECTRICITY_METER;
import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_ELEC_METER_ACC_MAJOR;
import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_ELEC_METER_ACC_MINOR;
import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_FLOODING;
import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_HIGH_PRESSURE;
import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_HUMIDITY;
import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_HUMIDITY_ACCURATE;
import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_IAQ;
import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_OK_ALARM;
import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_OUTPUT_CONTROL;
import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_PERCENTAGE;
import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_PULSE_CNT;
import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_PULSE_CNT_ACC_MAJOR;
import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_PULSE_CNT_ACC_MINOR;
import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_PULSE_CNT_ACC_WIDE_MAJOR;
import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_PULSE_CNT_ACC_WIDE_MINOR;
import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_SOIL_MOISTURE;
import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_STATIC_IAQ;
import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE;
import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_VOLTAGE;
import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_WATER_METER;
import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_WATER_METER_ACC_MAJOR;
import static org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_WATER_METER_ACC_MINOR;
import static org.thingsboard.server.transport.coap.efento.utils.CoapEfentoUtils.convertTimestampToUtcString;
class CoapEfentTransportResourceTest {
@ -41,25 +81,25 @@ class CoapEfentTransportResourceTest {
}
@Test
void checkContinuousSensor() {
void checkContinuousSensorWithSomeMeasurements() {
long tsInSec = Instant.now().getEpochSecond();
MeasurementsProtos.ProtoMeasurements measurements = MeasurementsProtos.ProtoMeasurements.newBuilder()
ProtoMeasurements measurements = ProtoMeasurements.newBuilder()
.setSerialNum(integerToByteString(1234))
.setCloudToken("test_token")
.setMeasurementPeriodBase(180)
.setMeasurementPeriodFactor(1)
.setMeasurementPeriodFactor(5)
.setBatteryStatus(true)
.setSignal(0)
.setNextTransmissionAt(1000)
.setTransferReason(0)
.setHash(0)
.addAllChannels(List.of(MeasurementsProtos.ProtoChannel.newBuilder()
.setType(MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE)
.setType(MeasurementType.MEASUREMENT_TYPE_TEMPERATURE)
.setTimestamp(Math.toIntExact(tsInSec))
.addAllSampleOffsets(List.of(223, 224))
.build(),
MeasurementsProtos.ProtoChannel.newBuilder()
.setType(MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_HUMIDITY)
.setType(MeasurementType.MEASUREMENT_TYPE_HUMIDITY)
.setTimestamp(Math.toIntExact(tsInSec))
.addAllSampleOffsets(List.of(20, 30))
.build()
@ -70,26 +110,88 @@ class CoapEfentTransportResourceTest {
assertThat(efentoMeasurements.get(0).getTs()).isEqualTo(tsInSec * 1000);
assertThat(efentoMeasurements.get(0).getValues().getAsJsonObject().get("temperature_1").getAsDouble()).isEqualTo(22.3);
assertThat(efentoMeasurements.get(0).getValues().getAsJsonObject().get("humidity_2").getAsDouble()).isEqualTo(20);
assertThat(efentoMeasurements.get(1).getTs()).isEqualTo((tsInSec + 180) * 1000);
assertThat(efentoMeasurements.get(1).getTs()).isEqualTo((tsInSec + 180 * 5) * 1000);
assertThat(efentoMeasurements.get(1).getValues().getAsJsonObject().get("temperature_1").getAsDouble()).isEqualTo(22.4);
assertThat(efentoMeasurements.get(1).getValues().getAsJsonObject().get("humidity_2").getAsDouble()).isEqualTo(30);
checkDefaultMeasurements(measurements, efentoMeasurements, 180 * 5, false);
}
@ParameterizedTest
@MethodSource
void checkContinuousSensor(MeasurementType measurementType, List<Integer> sampleOffsets, String property, double expectedValue) {
long tsInSec = Instant.now().getEpochSecond();
ProtoMeasurements measurements = ProtoMeasurements.newBuilder()
.setSerialNum(integerToByteString(1234))
.setCloudToken("test_token")
.setMeasurementPeriodBase(180)
.setMeasurementPeriodFactor(0)
.setBatteryStatus(true)
.setSignal(0)
.setNextTransmissionAt(1000)
.setTransferReason(0)
.setHash(0)
.addAllChannels(List.of(MeasurementsProtos.ProtoChannel.newBuilder()
.setType(measurementType)
.setTimestamp(Math.toIntExact(tsInSec))
.addAllSampleOffsets(sampleOffsets)
.build()
))
.build();
List<CoapEfentoTransportResource.EfentoTelemetry> efentoMeasurements = coapEfentoTransportResource.getEfentoMeasurements(measurements, UUID.randomUUID());
assertThat(efentoMeasurements).hasSize(1);
assertThat(efentoMeasurements.get(0).getTs()).isEqualTo(tsInSec * 1000);
assertThat(efentoMeasurements.get(0).getValues().getAsJsonObject().get(property).getAsDouble()).isEqualTo(expectedValue);
checkDefaultMeasurements(measurements, efentoMeasurements, 180, false);
}
private static Stream<Arguments> checkContinuousSensor() {
return Stream.of(
Arguments.of(MEASUREMENT_TYPE_TEMPERATURE, List.of(223), "temperature_1", 22.3),
Arguments.of(MEASUREMENT_TYPE_WATER_METER, List.of(1050), "pulse_counter_water_1", 1050),
Arguments.of(MEASUREMENT_TYPE_HUMIDITY, List.of(20), "humidity_1", 20),
Arguments.of(MEASUREMENT_TYPE_ATMOSPHERIC_PRESSURE, List.of(1013), "pressure_1", 101.3),
Arguments.of(MEASUREMENT_TYPE_DIFFERENTIAL_PRESSURE, List.of(500), "pressure_diff_1", 500),
Arguments.of(MEASUREMENT_TYPE_PULSE_CNT, List.of(300), "pulse_cnt_1", 300),
Arguments.of(MEASUREMENT_TYPE_IAQ, List.of(150), "iaq_1", 150),
Arguments.of(MEASUREMENT_TYPE_ELECTRICITY_METER, List.of(1200), "watt_hour_1", 1200),
Arguments.of(MEASUREMENT_TYPE_SOIL_MOISTURE, List.of(35), "soil_moisture_1", 35),
Arguments.of(MEASUREMENT_TYPE_AMBIENT_LIGHT, List.of(500), "ambient_light_1", 50),
Arguments.of(MEASUREMENT_TYPE_HIGH_PRESSURE, List.of(200000), "high_pressure_1", 200000),
Arguments.of(MEASUREMENT_TYPE_DISTANCE_MM, List.of(1500), "distance_mm_1", 1500),
Arguments.of(MEASUREMENT_TYPE_WATER_METER_ACC_MINOR, List.of(125), "acc_counter_water_minor_1", 125),
Arguments.of(MEASUREMENT_TYPE_WATER_METER_ACC_MAJOR, List.of(2500), "acc_counter_water_major_1", 2500),
Arguments.of(MEASUREMENT_TYPE_HUMIDITY_ACCURATE, List.of(525), "humidity_relative_1", 52.5),
Arguments.of(MEASUREMENT_TYPE_STATIC_IAQ, List.of(110), "static_iaq_1", 110),
Arguments.of(MEASUREMENT_TYPE_CO2_EQUIVALENT, List.of(450), "co2_ppm_1", 450),
Arguments.of(MEASUREMENT_TYPE_BREATH_VOC, List.of(220), "breath_voc_ppm_1", 220),
Arguments.of(MEASUREMENT_TYPE_PERCENTAGE, List.of(80), "percentage_1", 0.80),
Arguments.of(MEASUREMENT_TYPE_VOLTAGE, List.of(2400), "voltage_1", 240),
Arguments.of(MEASUREMENT_TYPE_CURRENT, List.of(550), "current_1", 5.5),
Arguments.of(MEASUREMENT_TYPE_PULSE_CNT_ACC_MINOR, List.of(180), "pulse_cnt_minor_1", 180),
Arguments.of(MEASUREMENT_TYPE_PULSE_CNT_ACC_MAJOR, List.of(1200), "pulse_cnt_major_1", 1200),
Arguments.of(MEASUREMENT_TYPE_ELEC_METER_ACC_MINOR, List.of(550), "elec_meter_minor_1", 550),
Arguments.of(MEASUREMENT_TYPE_ELEC_METER_ACC_MAJOR, List.of(5500), "elec_meter_major_1", 5500),
Arguments.of(MEASUREMENT_TYPE_PULSE_CNT_ACC_WIDE_MINOR, List.of(230), "pulse_cnt_wide_minor_1", 230),
Arguments.of(MEASUREMENT_TYPE_PULSE_CNT_ACC_WIDE_MAJOR, List.of(1700), "pulse_cnt_wide_major_1", 1700),
Arguments.of(MEASUREMENT_TYPE_CURRENT_PRECISE, List.of(275), "current_precise_1", 0.275)
);
}
@Test
void checkBinarySensor() {
long tsInSec = Instant.now().getEpochSecond();
MeasurementsProtos.ProtoMeasurements measurements = MeasurementsProtos.ProtoMeasurements.newBuilder()
ProtoMeasurements measurements = ProtoMeasurements.newBuilder()
.setSerialNum(integerToByteString(1234))
.setCloudToken("test_token")
.setMeasurementPeriodBase(180)
.setMeasurementPeriodFactor(1)
.setMeasurementPeriodFactor(0)
.setBatteryStatus(true)
.setSignal(0)
.setNextTransmissionAt(1000)
.setTransferReason(0)
.setHash(0)
.addChannels(MeasurementsProtos.ProtoChannel.newBuilder()
.setType(MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_OK_ALARM)
.setType(MEASUREMENT_TYPE_OK_ALARM)
.setTimestamp(Math.toIntExact(tsInSec))
.addAllSampleOffsets(List.of(1, 1))
.build())
@ -98,12 +200,14 @@ class CoapEfentTransportResourceTest {
assertThat(efentoMeasurements).hasSize(1);
assertThat(efentoMeasurements.get(0).getTs()).isEqualTo(tsInSec * 1000);
assertThat(efentoMeasurements.get(0).getValues().getAsJsonObject().get("ok_alarm_1").getAsString()).isEqualTo("ALARM");
checkDefaultMeasurements(measurements, efentoMeasurements, 180 * 14, true);
}
@Test
void checkBinarySensorWhenValueIsVarying() {
@ParameterizedTest
@MethodSource
void checkBinarySensorWhenValueIsVarying(MeasurementType measurementType, String property, String expectedValueWhenOffsetNotOk, String expectedValueWhenOffsetOk) {
long tsInSec = Instant.now().getEpochSecond();
MeasurementsProtos.ProtoMeasurements measurements = MeasurementsProtos.ProtoMeasurements.newBuilder()
ProtoMeasurements measurements = ProtoMeasurements.newBuilder()
.setSerialNum(integerToByteString(1234))
.setCloudToken("test_token")
.setMeasurementPeriodBase(180)
@ -114,7 +218,7 @@ class CoapEfentTransportResourceTest {
.setTransferReason(0)
.setHash(0)
.addChannels(MeasurementsProtos.ProtoChannel.newBuilder()
.setType(MeasurementTypeProtos.MeasurementType.MEASUREMENT_TYPE_OK_ALARM)
.setType(measurementType)
.setTimestamp(Math.toIntExact(tsInSec))
.addAllSampleOffsets(List.of(1, -10))
.build())
@ -122,9 +226,63 @@ class CoapEfentTransportResourceTest {
List<CoapEfentoTransportResource.EfentoTelemetry> efentoMeasurements = coapEfentoTransportResource.getEfentoMeasurements(measurements, UUID.randomUUID());
assertThat(efentoMeasurements).hasSize(2);
assertThat(efentoMeasurements.get(0).getTs()).isEqualTo(tsInSec * 1000);
assertThat(efentoMeasurements.get(0).getValues().getAsJsonObject().get("ok_alarm_1").getAsString()).isEqualTo("ALARM");
assertThat(efentoMeasurements.get(0).getValues().getAsJsonObject().get(property).getAsString()).isEqualTo(expectedValueWhenOffsetNotOk);
assertThat(efentoMeasurements.get(1).getTs()).isEqualTo((tsInSec + 9) * 1000);
assertThat(efentoMeasurements.get(1).getValues().getAsJsonObject().get("ok_alarm_1").getAsString()).isEqualTo("OK");
assertThat(efentoMeasurements.get(1).getValues().getAsJsonObject().get(property).getAsString()).isEqualTo(expectedValueWhenOffsetOk);
checkDefaultMeasurements(measurements, efentoMeasurements, 180, true);
}
private static Stream<Arguments> checkBinarySensorWhenValueIsVarying() {
return Stream.of(
Arguments.of(MEASUREMENT_TYPE_OK_ALARM, "ok_alarm_1", "ALARM", "OK"),
Arguments.of(MEASUREMENT_TYPE_FLOODING, "flooding_1", "WATER_DETECTED", "OK"),
Arguments.of(MEASUREMENT_TYPE_OUTPUT_CONTROL, "output_control_1", "ON", "OFF")
);
}
@Test
void checkExceptionWhenChannelsListIsEmpty() {
ProtoMeasurements measurements = ProtoMeasurements.newBuilder()
.setSerialNum(integerToByteString(1234))
.setCloudToken("test_token")
.setMeasurementPeriodBase(180)
.setMeasurementPeriodFactor(1)
.setBatteryStatus(true)
.setSignal(0)
.setNextTransmissionAt(1000)
.setTransferReason(0)
.setHash(0)
.build();
UUID sessionId = UUID.randomUUID();
assertThatThrownBy(() -> coapEfentoTransportResource.getEfentoMeasurements(measurements, sessionId))
.isInstanceOf(IllegalStateException.class)
.hasMessage("[" + sessionId + "]: Failed to get Efento measurements, reason: channels list is empty!");
}
@Test
void checkExceptionWhenValuesMapIsEmpty() {
long tsInSec = Instant.now().getEpochSecond();
ProtoMeasurements measurements = ProtoMeasurements.newBuilder()
.setSerialNum(integerToByteString(1234))
.setCloudToken("test_token")
.setMeasurementPeriodBase(180)
.setMeasurementPeriodFactor(1)
.setBatteryStatus(true)
.setSignal(0)
.setNextTransmissionAt(1000)
.setTransferReason(0)
.setHash(0)
.addChannels(MeasurementsProtos.ProtoChannel.newBuilder()
.setType(MEASUREMENT_TYPE_TEMPERATURE)
.setTimestamp(Math.toIntExact(tsInSec))
.build())
.build();
UUID sessionId = UUID.randomUUID();
assertThatThrownBy(() -> coapEfentoTransportResource.getEfentoMeasurements(measurements, sessionId))
.isInstanceOf(IllegalStateException.class)
.hasMessage("[" + sessionId + "]: Failed to collect Efento measurements, reason, values map is empty!");
}
public static ByteString integerToByteString(Integer intValue) {
@ -141,4 +299,23 @@ class CoapEfentTransportResourceTest {
return ByteString.copyFrom(byteArray);
}
private void checkDefaultMeasurements(ProtoMeasurements incomingMeasurements,
List<CoapEfentoTransportResource.EfentoTelemetry> actualEfentoMeasurements,
long expectedMeasurementInterval,
boolean isBinarySensor) {
for (int i = 0; i < actualEfentoMeasurements.size(); i++) {
CoapEfentoTransportResource.EfentoTelemetry actualEfentoMeasurement = actualEfentoMeasurements.get(i);
assertThat(actualEfentoMeasurement.getValues().getAsJsonObject().get("serial").getAsString()).isEqualTo(CoapEfentoUtils.convertByteArrayToString(incomingMeasurements.getSerialNum().toByteArray()));
assertThat(actualEfentoMeasurement.getValues().getAsJsonObject().get("battery").getAsString()).isEqualTo(incomingMeasurements.getBatteryStatus() ? "ok" : "low");
MeasurementsProtos.ProtoChannel protoChannel = incomingMeasurements.getChannelsList().get(0);
long measuredAt = isBinarySensor ?
TimeUnit.SECONDS.toMillis(protoChannel.getTimestamp()) + Math.abs(TimeUnit.SECONDS.toMillis(protoChannel.getSampleOffsetsList().get(i))) - 1000 :
TimeUnit.SECONDS.toMillis(protoChannel.getTimestamp() + i * expectedMeasurementInterval);
assertThat(actualEfentoMeasurement.getValues().getAsJsonObject().get("measured_at").getAsString()).isEqualTo(convertTimestampToUtcString(measuredAt));
assertThat(actualEfentoMeasurement.getValues().getAsJsonObject().get("next_transmission_at").getAsString()).isEqualTo(convertTimestampToUtcString(TimeUnit.SECONDS.toMillis(incomingMeasurements.getNextTransmissionAt())));
assertThat(actualEfentoMeasurement.getValues().getAsJsonObject().get("signal").getAsLong()).isEqualTo(incomingMeasurements.getSignal());
assertThat(actualEfentoMeasurement.getValues().getAsJsonObject().get("measurement_interval").getAsDouble()).isEqualTo(expectedMeasurementInterval);
}
}
}

4
common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java

@ -202,8 +202,8 @@ public class DeviceApiController implements TbTransportService {
return responseWriter;
}
@Operation(summary = "Post time-series data (postTelemetry)",
description = "Post time-series data on behalf of device. "
@Operation(summary = "Post time series data (postTelemetry)",
description = "Post time series data on behalf of device. "
+ "\n Example of the request: "
+ TS_PAYLOAD
+ REQUIRE_ACCESS_TOKEN)

2
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/TbLwM2MDtlsBootstrapCertificateVerifier.java

@ -82,7 +82,7 @@ public class TbLwM2MDtlsBootstrapCertificateVerifier implements NewAdvancedCerti
staticCertificateVerifier = new StaticNewAdvancedCertificateVerifier(trustedCertificates, new RawPublicKeyIdentity[0], null);
}
} catch (Exception e) {
log.warn("ailed to initialize the LwM2M certificate verifier", e);
log.warn("Failed to initialize the LwM2M certificate verifier", e);
}
}

2
dao/src/main/java/org/thingsboard/server/dao/Dao.java

@ -39,7 +39,7 @@ public interface Dao<T> {
T saveAndFlush(TenantId tenantId, T t);
boolean removeById(TenantId tenantId, UUID id);
void removeById(TenantId tenantId, UUID id);
void removeAllByIds(Collection<UUID> ids);

2
dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmCommentService.java

@ -65,7 +65,7 @@ public class BaseAlarmCommentService extends AbstractEntityService implements Al
@Override
public AlarmComment saveAlarmComment(TenantId tenantId, AlarmComment alarmComment) {
log.debug("Deleting Alarm Comment: {}", alarmComment);
log.debug("Saving Alarm Comment: {}", alarmComment);
alarmCommentDataValidator.validate(alarmComment, c -> tenantId);
AlarmComment result = alarmCommentDao.save(tenantId, alarmComment);
eventPublisher.publishEvent(DeleteEntityEvent.builder().tenantId(tenantId).entity(result)

6
dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileCacheKey.java

@ -38,15 +38,15 @@ public class AssetProfileCacheKey implements Serializable {
this.defaultProfile = defaultProfile;
}
public static AssetProfileCacheKey fromName(TenantId tenantId, String name) {
public static AssetProfileCacheKey forName(TenantId tenantId, String name) {
return new AssetProfileCacheKey(tenantId, name, null, false);
}
public static AssetProfileCacheKey fromId(AssetProfileId id) {
public static AssetProfileCacheKey forId(AssetProfileId id) {
return new AssetProfileCacheKey(null, null, id, false);
}
public static AssetProfileCacheKey defaultProfile(TenantId tenantId) {
public static AssetProfileCacheKey forDefaultProfile(TenantId tenantId) {
return new AssetProfileCacheKey(tenantId, null, null, true);
}

4
dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileCaffeineCache.java

@ -18,13 +18,13 @@ package org.thingsboard.server.dao.asset;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;
import org.thingsboard.server.cache.CaffeineTbTransactionalCache;
import org.thingsboard.server.cache.VersionedCaffeineTbCache;
import org.thingsboard.server.common.data.CacheConstants;
import org.thingsboard.server.common.data.asset.AssetProfile;
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true)
@Service("AssetProfileCache")
public class AssetProfileCaffeineCache extends CaffeineTbTransactionalCache<AssetProfileCacheKey, AssetProfile> {
public class AssetProfileCaffeineCache extends VersionedCaffeineTbCache<AssetProfileCacheKey, AssetProfile> {
public AssetProfileCaffeineCache(CacheManager cacheManager) {
super(cacheManager, CacheConstants.ASSET_PROFILE_CACHE);

6
dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileEvictEvent.java

@ -15,11 +15,16 @@
*/
package org.thingsboard.server.dao.asset;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.thingsboard.server.common.data.asset.AssetProfile;
import org.thingsboard.server.common.data.id.AssetProfileId;
import org.thingsboard.server.common.data.id.TenantId;
@Data
@RequiredArgsConstructor
@AllArgsConstructor
public class AssetProfileEvictEvent {
private final TenantId tenantId;
@ -27,5 +32,6 @@ public class AssetProfileEvictEvent {
private final String oldName;
private final AssetProfileId assetProfileId;
private final boolean defaultProfile;
private AssetProfile savedAssetProfile;
}

5
dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileRedisCache.java

@ -19,17 +19,18 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.stereotype.Service;
import org.thingsboard.server.cache.CacheSpecsMap;
import org.thingsboard.server.cache.RedisTbTransactionalCache;
import org.thingsboard.server.cache.TBRedisCacheConfiguration;
import org.thingsboard.server.cache.TbJsonRedisSerializer;
import org.thingsboard.server.cache.VersionedRedisTbCache;
import org.thingsboard.server.common.data.CacheConstants;
import org.thingsboard.server.common.data.asset.AssetProfile;
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis")
@Service("AssetProfileCache")
public class AssetProfileRedisCache extends RedisTbTransactionalCache<AssetProfileCacheKey, AssetProfile> {
public class AssetProfileRedisCache extends VersionedRedisTbCache<AssetProfileCacheKey, AssetProfile> {
public AssetProfileRedisCache(TBRedisCacheConfiguration configuration, CacheSpecsMap cacheSpecsMap, RedisConnectionFactory connectionFactory) {
super(CacheConstants.ASSET_PROFILE_CACHE, cacheSpecsMap, connectionFactory, configuration, new TbJsonRedisSerializer<>(AssetProfile.class));
}
}

31
dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java

@ -33,7 +33,7 @@ import org.thingsboard.server.common.data.id.HasId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.entity.AbstractCachedEntityService;
import org.thingsboard.server.dao.entity.CachedVersionedEntityService;
import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent;
import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent;
import org.thingsboard.server.dao.exception.DataValidationException;
@ -53,7 +53,7 @@ import static org.thingsboard.server.dao.service.Validator.validateId;
@Service("AssetProfileDaoService")
@Slf4j
public class AssetProfileServiceImpl extends AbstractCachedEntityService<AssetProfileCacheKey, AssetProfile, AssetProfileEvictEvent> implements AssetProfileService {
public class AssetProfileServiceImpl extends CachedVersionedEntityService<AssetProfileCacheKey, AssetProfile, AssetProfileEvictEvent> implements AssetProfileService {
private static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
@ -81,18 +81,20 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService<AssetPr
@TransactionalEventListener(classes = AssetProfileEvictEvent.class)
@Override
public void handleEvictEvent(AssetProfileEvictEvent event) {
List<AssetProfileCacheKey> keys = new ArrayList<>(2);
keys.add(AssetProfileCacheKey.fromName(event.getTenantId(), event.getNewName()));
if (event.getAssetProfileId() != null) {
keys.add(AssetProfileCacheKey.fromId(event.getAssetProfileId()));
List<AssetProfileCacheKey> toEvict = new ArrayList<>(2);
toEvict.add(AssetProfileCacheKey.forName(event.getTenantId(), event.getNewName()));
if (event.getSavedAssetProfile() != null) {
cache.put(AssetProfileCacheKey.forId(event.getSavedAssetProfile().getId()), event.getSavedAssetProfile());
} else if (event.getAssetProfileId() != null) {
toEvict.add(AssetProfileCacheKey.forId(event.getAssetProfileId()));
}
if (event.isDefaultProfile()) {
keys.add(AssetProfileCacheKey.defaultProfile(event.getTenantId()));
toEvict.add(AssetProfileCacheKey.forDefaultProfile(event.getTenantId()));
}
if (StringUtils.isNotEmpty(event.getOldName()) && !event.getOldName().equals(event.getNewName())) {
keys.add(AssetProfileCacheKey.fromName(event.getTenantId(), event.getOldName()));
toEvict.add(AssetProfileCacheKey.forName(event.getTenantId(), event.getOldName()));
}
cache.evict(keys);
cache.evict(toEvict);
}
@Override
@ -104,8 +106,8 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService<AssetPr
public AssetProfile findAssetProfileById(TenantId tenantId, AssetProfileId assetProfileId, boolean putInCache) {
log.trace("Executing findAssetProfileById [{}]", assetProfileId);
Validator.validateId(assetProfileId, id -> INCORRECT_ASSET_PROFILE_ID + id);
return cache.getOrFetchFromDB(AssetProfileCacheKey.fromId(assetProfileId),
() -> assetProfileDao.findById(tenantId, assetProfileId.getId()), true, putInCache);
return cache.get(AssetProfileCacheKey.forId(assetProfileId),
() -> assetProfileDao.findById(tenantId, assetProfileId.getId()), putInCache);
}
@Override
@ -117,7 +119,7 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService<AssetPr
public AssetProfile findAssetProfileByName(TenantId tenantId, String profileName, boolean putInCache) {
log.trace("Executing findAssetProfileByName [{}][{}]", tenantId, profileName);
Validator.validateString(profileName, s -> INCORRECT_ASSET_PROFILE_NAME + s);
return cache.getOrFetchFromDB(AssetProfileCacheKey.fromName(tenantId, profileName),
return cache.getOrFetchFromDB(AssetProfileCacheKey.forName(tenantId, profileName),
() -> assetProfileDao.findByName(tenantId, profileName), false, putInCache);
}
@ -147,7 +149,7 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService<AssetPr
imageService.replaceBase64WithImageUrl(assetProfile, "asset profile");
savedAssetProfile = assetProfileDao.saveAndFlush(assetProfile.getTenantId(), assetProfile);
publishEvictEvent(new AssetProfileEvictEvent(savedAssetProfile.getTenantId(), savedAssetProfile.getName(),
oldAssetProfile != null ? oldAssetProfile.getName() : null, savedAssetProfile.getId(), savedAssetProfile.isDefault()));
oldAssetProfile != null ? oldAssetProfile.getName() : null, savedAssetProfile.getId(), savedAssetProfile.isDefault(), savedAssetProfile));
if (publishSaveEvent) {
eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(savedAssetProfile.getTenantId()).entity(savedAssetProfile)
.entityId(savedAssetProfile.getId()).created(oldAssetProfile == null).build());
@ -267,7 +269,7 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService<AssetPr
public AssetProfile findDefaultAssetProfile(TenantId tenantId) {
log.trace("Executing findDefaultAssetProfile tenantId [{}]", tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
return cache.getAndPutInTransaction(AssetProfileCacheKey.defaultProfile(tenantId),
return cache.getAndPutInTransaction(AssetProfileCacheKey.forDefaultProfile(tenantId),
() -> assetProfileDao.findDefaultAssetProfile(tenantId), true);
}
@ -353,4 +355,5 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService<AssetPr
return profile == null ? null : new AssetProfileInfo(profile.getId(), profile.getTenantId(), profile.getName(), profile.getImage(),
profile.getDefaultDashboardId());
}
}

2
dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java

@ -201,9 +201,9 @@ public class CustomerServiceImpl extends AbstractCachedEntityService<CustomerCac
userService.deleteCustomerUsers(customer.getTenantId(), customerId);
apiUsageStateService.deleteApiUsageStateByEntityId(customerId);
customerDao.removeById(tenantId, customerId.getId());
publishEvictEvent(new CustomerCacheEvictEvent(customer.getTenantId(), customer.getTitle(), null));
countService.publishCountEntityEvictEvent(tenantId, EntityType.CUSTOMER);
eventPublisher.publishEvent(DeleteEntityEvent.builder().tenantId(tenantId).entityId(customerId).build());
publishEvictEvent(new CustomerCacheEvictEvent(customer.getTenantId(), customer.getTitle(), null));
}
@Override

12
dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java

@ -16,11 +16,11 @@
package org.thingsboard.server.dao.device;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.SecurityMode;
import org.eclipse.leshan.core.util.SecurityUtil;
import org.hibernate.exception.ConstraintViolationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.event.TransactionalEventListener;
import org.thingsboard.common.util.JacksonUtil;
@ -45,20 +45,18 @@ import org.thingsboard.server.dao.entity.AbstractCachedEntityService;
import org.thingsboard.server.dao.eventsourcing.ActionEntityEvent;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.exception.DeviceCredentialsValidationException;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.validator.DeviceCredentialsDataValidator;
import static org.thingsboard.server.dao.service.Validator.validateId;
import static org.thingsboard.server.dao.service.Validator.validateString;
@Service
@Slf4j
@RequiredArgsConstructor
public class DeviceCredentialsServiceImpl extends AbstractCachedEntityService<String, DeviceCredentials, DeviceCredentialsEvictEvent> implements DeviceCredentialsService {
@Autowired
private DeviceCredentialsDao deviceCredentialsDao;
@Autowired
private DataValidator<DeviceCredentials> credentialsValidator;
private final DeviceCredentialsDao deviceCredentialsDao;
private final DeviceCredentialsDataValidator credentialsValidator;
@TransactionalEventListener(classes = DeviceCredentialsEvictEvent.class)
@Override

8
dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileCacheKey.java

@ -41,19 +41,19 @@ public class DeviceProfileCacheKey implements Serializable {
this.provisionDeviceKey = provisionDeviceKey;
}
public static DeviceProfileCacheKey fromName(TenantId tenantId, String name) {
public static DeviceProfileCacheKey forName(TenantId tenantId, String name) {
return new DeviceProfileCacheKey(tenantId, name, null, false, null);
}
public static DeviceProfileCacheKey fromId(DeviceProfileId id) {
public static DeviceProfileCacheKey forId(DeviceProfileId id) {
return new DeviceProfileCacheKey(null, null, id, false, null);
}
public static DeviceProfileCacheKey defaultProfile(TenantId tenantId) {
public static DeviceProfileCacheKey forDefaultProfile(TenantId tenantId) {
return new DeviceProfileCacheKey(tenantId, null, null, true, null);
}
public static DeviceProfileCacheKey fromProvisionDeviceKey(String provisionDeviceKey) {
public static DeviceProfileCacheKey forProvisionKey(String provisionDeviceKey) {
return new DeviceProfileCacheKey(null, null, null, false, provisionDeviceKey);
}

4
dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileCaffeineCache.java

@ -18,13 +18,13 @@ package org.thingsboard.server.dao.device;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;
import org.thingsboard.server.cache.CaffeineTbTransactionalCache;
import org.thingsboard.server.cache.VersionedCaffeineTbCache;
import org.thingsboard.server.common.data.CacheConstants;
import org.thingsboard.server.common.data.DeviceProfile;
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true)
@Service("DeviceProfileCache")
public class DeviceProfileCaffeineCache extends CaffeineTbTransactionalCache<DeviceProfileCacheKey, DeviceProfile> {
public class DeviceProfileCaffeineCache extends VersionedCaffeineTbCache<DeviceProfileCacheKey, DeviceProfile> {
public DeviceProfileCaffeineCache(CacheManager cacheManager) {
super(cacheManager, CacheConstants.DEVICE_PROFILE_CACHE);

6
dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileEvictEvent.java

@ -15,11 +15,16 @@
*/
package org.thingsboard.server.dao.device;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.TenantId;
@Data
@RequiredArgsConstructor
@AllArgsConstructor
public class DeviceProfileEvictEvent {
private final TenantId tenantId;
@ -28,5 +33,6 @@ public class DeviceProfileEvictEvent {
private final DeviceProfileId deviceProfileId;
private final boolean defaultProfile;
private final String provisionDeviceKey;
private DeviceProfile savedDeviceProfile;
}

5
dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileRedisCache.java

@ -21,9 +21,9 @@ import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.SerializationException;
import org.springframework.stereotype.Service;
import org.thingsboard.server.cache.CacheSpecsMap;
import org.thingsboard.server.cache.RedisTbTransactionalCache;
import org.thingsboard.server.cache.TBRedisCacheConfiguration;
import org.thingsboard.server.cache.TbRedisSerializer;
import org.thingsboard.server.cache.VersionedRedisTbCache;
import org.thingsboard.server.common.data.CacheConstants;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.util.ProtoUtils;
@ -31,7 +31,7 @@ import org.thingsboard.server.gen.transport.TransportProtos;
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis")
@Service("DeviceProfileCache")
public class DeviceProfileRedisCache extends RedisTbTransactionalCache<DeviceProfileCacheKey, DeviceProfile> {
public class DeviceProfileRedisCache extends VersionedRedisTbCache<DeviceProfileCacheKey, DeviceProfile> {
public DeviceProfileRedisCache(TBRedisCacheConfiguration configuration, CacheSpecsMap cacheSpecsMap, RedisConnectionFactory connectionFactory) {
super(CacheConstants.DEVICE_PROFILE_CACHE, cacheSpecsMap, connectionFactory, configuration, new TbRedisSerializer<DeviceProfileCacheKey, DeviceProfile>() {
@ -50,4 +50,5 @@ public class DeviceProfileRedisCache extends RedisTbTransactionalCache<DevicePro
}
});
}
}

51
dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java

@ -15,8 +15,8 @@
*/
package org.thingsboard.server.dao.device;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.exception.ConstraintViolationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -42,14 +42,14 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.msg.EncryptionUtil;
import org.thingsboard.server.dao.entity.AbstractCachedEntityService;
import org.thingsboard.server.dao.entity.CachedVersionedEntityService;
import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent;
import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.resource.ImageService;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.PaginatedRemover;
import org.thingsboard.server.dao.service.Validator;
import org.thingsboard.server.dao.service.validator.DeviceProfileDataValidator;
import java.io.ByteArrayInputStream;
import java.security.cert.Certificate;
@ -69,7 +69,8 @@ import static org.thingsboard.server.dao.service.Validator.validateString;
@Service("DeviceProfileDaoService")
@Slf4j
public class DeviceProfileServiceImpl extends AbstractCachedEntityService<DeviceProfileCacheKey, DeviceProfile, DeviceProfileEvictEvent> implements DeviceProfileService {
@RequiredArgsConstructor
public class DeviceProfileServiceImpl extends CachedVersionedEntityService<DeviceProfileCacheKey, DeviceProfile, DeviceProfileEvictEvent> implements DeviceProfileService {
private static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
private static final String INCORRECT_DEVICE_PROFILE_ID = "Incorrect deviceProfileId ";
@ -87,7 +88,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
private DeviceService deviceService;
@Autowired
private DataValidator<DeviceProfile> deviceProfileValidator;
private DeviceProfileDataValidator deviceProfileValidator;
@Autowired
private ImageService imageService;
@ -95,21 +96,23 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
@TransactionalEventListener(classes = DeviceProfileEvictEvent.class)
@Override
public void handleEvictEvent(DeviceProfileEvictEvent event) {
List<DeviceProfileCacheKey> keys = new ArrayList<>(2);
keys.add(DeviceProfileCacheKey.fromName(event.getTenantId(), event.getNewName()));
if (event.getDeviceProfileId() != null) {
keys.add(DeviceProfileCacheKey.fromId(event.getDeviceProfileId()));
List<DeviceProfileCacheKey> toEvict = new ArrayList<>(2);
toEvict.add(DeviceProfileCacheKey.forName(event.getTenantId(), event.getNewName()));
if (event.getSavedDeviceProfile() != null) {
cache.put(DeviceProfileCacheKey.forId(event.getSavedDeviceProfile().getId()), event.getSavedDeviceProfile());
} else if (event.getDeviceProfileId() != null) {
toEvict.add(DeviceProfileCacheKey.forId(event.getDeviceProfileId()));
}
if (event.isDefaultProfile()) {
keys.add(DeviceProfileCacheKey.defaultProfile(event.getTenantId()));
toEvict.add(DeviceProfileCacheKey.forDefaultProfile(event.getTenantId()));
}
if (StringUtils.isNotEmpty(event.getOldName()) && !event.getOldName().equals(event.getNewName())) {
keys.add(DeviceProfileCacheKey.fromName(event.getTenantId(), event.getOldName()));
toEvict.add(DeviceProfileCacheKey.forName(event.getTenantId(), event.getOldName()));
}
if (StringUtils.isNotEmpty(event.getProvisionDeviceKey())) {
keys.add(DeviceProfileCacheKey.fromProvisionDeviceKey(event.getProvisionDeviceKey()));
toEvict.add(DeviceProfileCacheKey.forProvisionKey(event.getProvisionDeviceKey()));
}
cache.evict(keys);
cache.evict(toEvict);
}
@Override
@ -121,8 +124,8 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
public DeviceProfile findDeviceProfileById(TenantId tenantId, DeviceProfileId deviceProfileId, boolean putInCache) {
log.trace("Executing findDeviceProfileById [{}]", deviceProfileId);
validateId(deviceProfileId, id -> INCORRECT_DEVICE_PROFILE_ID + id);
return cache.getOrFetchFromDB(DeviceProfileCacheKey.fromId(deviceProfileId),
() -> deviceProfileDao.findById(tenantId, deviceProfileId.getId()), true, putInCache);
return cache.get(DeviceProfileCacheKey.forId(deviceProfileId),
() -> deviceProfileDao.findById(tenantId, deviceProfileId.getId()), putInCache);
}
@Override
@ -134,7 +137,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
public DeviceProfile findDeviceProfileByName(TenantId tenantId, String profileName, boolean putInCache) {
log.trace("Executing findDeviceProfileByName [{}][{}]", tenantId, profileName);
validateString(profileName, pn -> INCORRECT_DEVICE_PROFILE_NAME + pn);
return cache.getOrFetchFromDB(DeviceProfileCacheKey.fromName(tenantId, profileName),
return cache.getOrFetchFromDB(DeviceProfileCacheKey.forName(tenantId, profileName),
() -> deviceProfileDao.findByName(tenantId, profileName), true, putInCache);
}
@ -142,7 +145,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
public DeviceProfile findDeviceProfileByProvisionDeviceKey(String provisionDeviceKey) {
log.trace("Executing findDeviceProfileByProvisionDeviceKey provisionKey [{}]", provisionDeviceKey);
validateString(provisionDeviceKey, dk -> INCORRECT_PROVISION_DEVICE_KEY + dk);
return cache.getAndPutInTransaction(DeviceProfileCacheKey.fromProvisionDeviceKey(provisionDeviceKey),
return cache.getAndPutInTransaction(DeviceProfileCacheKey.forProvisionKey(provisionDeviceKey),
() -> deviceProfileDao.findByProvisionDeviceKey(provisionDeviceKey), false);
}
@ -179,7 +182,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
savedDeviceProfile = deviceProfileDao.saveAndFlush(deviceProfile.getTenantId(), deviceProfile);
publishEvictEvent(new DeviceProfileEvictEvent(savedDeviceProfile.getTenantId(), savedDeviceProfile.getName(),
oldDeviceProfile != null ? oldDeviceProfile.getName() : null, savedDeviceProfile.getId(), savedDeviceProfile.isDefault(),
oldDeviceProfile != null ? oldDeviceProfile.getProvisionDeviceKey() : null));
oldDeviceProfile != null ? oldDeviceProfile.getProvisionDeviceKey() : null, savedDeviceProfile));
if (publishSaveEvent) {
eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(savedDeviceProfile.getTenantId()).entityId(savedDeviceProfile.getId())
.entity(savedDeviceProfile).oldEntity(oldDeviceProfile).created(oldDeviceProfile == null).build());
@ -241,13 +244,9 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
null, deviceProfile.getId(), deviceProfile.isDefault(),
deviceProfile.getProvisionDeviceKey()));
eventPublisher.publishEvent(DeleteEntityEvent.builder().tenantId(tenantId).entityId(deviceProfileId).entity(deviceProfile).build());
} catch (Exception t) {
ConstraintViolationException e = extractConstraintViolationException(t).orElse(null);
if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_device_profile")) {
throw new DataValidationException("The device profile referenced by the devices cannot be deleted!");
} else {
throw t;
}
} catch (Exception e) {
checkConstraintViolation(e, "fk_device_profile", "The device profile referenced by the devices cannot be deleted!");
throw e;
}
}
@ -316,7 +315,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
public DeviceProfile findDefaultDeviceProfile(TenantId tenantId) {
log.trace("Executing findDefaultDeviceProfile tenantId [{}]", tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
return cache.getAndPutInTransaction(DeviceProfileCacheKey.defaultProfile(tenantId),
return cache.getAndPutInTransaction(DeviceProfileCacheKey.forDefaultProfile(tenantId),
() -> deviceProfileDao.findDefaultDeviceProfile(tenantId), true);
}

70
dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java

@ -18,8 +18,8 @@ package org.thingsboard.server.dao.device;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.event.TransactionalEventListener;
@ -68,7 +68,7 @@ import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.dao.device.provision.ProvisionFailedException;
import org.thingsboard.server.dao.device.provision.ProvisionRequest;
import org.thingsboard.server.dao.device.provision.ProvisionResponseStatus;
import org.thingsboard.server.dao.entity.AbstractCachedEntityService;
import org.thingsboard.server.dao.entity.CachedVersionedEntityService;
import org.thingsboard.server.dao.entity.EntityCountService;
import org.thingsboard.server.dao.event.EventService;
import org.thingsboard.server.dao.eventsourcing.ActionEntityEvent;
@ -76,8 +76,8 @@ import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent;
import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.PaginatedRemover;
import org.thingsboard.server.dao.service.validator.DeviceDataValidator;
import org.thingsboard.server.dao.sql.JpaExecutorService;
import org.thingsboard.server.dao.tenant.TenantService;
@ -94,38 +94,23 @@ import static org.thingsboard.server.dao.service.Validator.validateString;
@Service("DeviceDaoService")
@Slf4j
public class DeviceServiceImpl extends AbstractCachedEntityService<DeviceCacheKey, Device, DeviceCacheEvictEvent> implements DeviceService {
@RequiredArgsConstructor
public class DeviceServiceImpl extends CachedVersionedEntityService<DeviceCacheKey, Device, DeviceCacheEvictEvent> implements DeviceService {
public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
public static final String INCORRECT_DEVICE_PROFILE_ID = "Incorrect deviceProfileId ";
public static final String INCORRECT_PAGE_LINK = "Incorrect page link ";
public static final String INCORRECT_CUSTOMER_ID = "Incorrect customerId ";
public static final String INCORRECT_DEVICE_ID = "Incorrect deviceId ";
public static final String INCORRECT_EDGE_ID = "Incorrect edgeId ";
@Autowired
private DeviceDao deviceDao;
@Autowired
private DeviceCredentialsService deviceCredentialsService;
@Autowired
private DeviceProfileService deviceProfileService;
@Autowired
private EventService eventService;
@Autowired
private TenantService tenantService;
@Autowired
private DataValidator<Device> deviceValidator;
@Autowired
private EntityCountService countService;
@Autowired
private JpaExecutorService executor;
private final DeviceDao deviceDao;
private final DeviceCredentialsService deviceCredentialsService;
private final DeviceProfileService deviceProfileService;
private final EventService eventService;
private final TenantService tenantService;
private final DeviceDataValidator deviceValidator;
private final EntityCountService countService;
private final JpaExecutorService executor;
@Override
public DeviceInfo findDeviceInfoById(TenantId tenantId, DeviceId deviceId) {
@ -139,11 +124,11 @@ public class DeviceServiceImpl extends AbstractCachedEntityService<DeviceCacheKe
log.trace("Executing findDeviceById [{}]", deviceId);
validateId(deviceId, id -> INCORRECT_DEVICE_ID + id);
if (TenantId.SYS_TENANT_ID.equals(tenantId)) {
return cache.getAndPutInTransaction(new DeviceCacheKey(deviceId),
() -> deviceDao.findById(tenantId, deviceId.getId()), true);
return cache.get(new DeviceCacheKey(deviceId),
() -> deviceDao.findById(tenantId, deviceId.getId()));
} else {
return cache.getAndPutInTransaction(new DeviceCacheKey(tenantId, deviceId),
() -> deviceDao.findDeviceByTenantIdAndId(tenantId, deviceId.getId()), true);
return cache.get(new DeviceCacheKey(tenantId, deviceId),
() -> deviceDao.findDeviceByTenantIdAndId(tenantId, deviceId.getId()));
}
}
@ -251,6 +236,7 @@ public class DeviceServiceImpl extends AbstractCachedEntityService<DeviceCacheKe
device.setType(deviceProfile.getName());
device.setDeviceData(syncDeviceData(deviceProfile, device.getDeviceData()));
Device savedDevice = deviceDao.saveAndFlush(device.getTenantId(), device);
deviceCacheEvictEvent.setSavedDevice(savedDevice);
publishEvictEvent(deviceCacheEvictEvent);
if (device.getId() == null) {
countService.publishCountEntityEvictEvent(savedDevice.getTenantId(), EntityType.DEVICE);
@ -270,16 +256,20 @@ public class DeviceServiceImpl extends AbstractCachedEntityService<DeviceCacheKe
@TransactionalEventListener(classes = DeviceCacheEvictEvent.class)
@Override
public void handleEvictEvent(DeviceCacheEvictEvent event) {
List<DeviceCacheKey> keys = new ArrayList<>(3);
keys.add(new DeviceCacheKey(event.getTenantId(), event.getNewName()));
if (event.getDeviceId() != null) {
keys.add(new DeviceCacheKey(event.getDeviceId()));
keys.add(new DeviceCacheKey(event.getTenantId(), event.getDeviceId()));
}
List<DeviceCacheKey> toEvict = new ArrayList<>(3);
toEvict.add(new DeviceCacheKey(event.getTenantId(), event.getNewName()));
if (StringUtils.isNotEmpty(event.getOldName()) && !event.getOldName().equals(event.getNewName())) {
keys.add(new DeviceCacheKey(event.getTenantId(), event.getOldName()));
toEvict.add(new DeviceCacheKey(event.getTenantId(), event.getOldName()));
}
Device savedDevice = event.getSavedDevice();
if (savedDevice != null) {
cache.put(new DeviceCacheKey(event.getDeviceId()), savedDevice);
cache.put(new DeviceCacheKey(event.getTenantId(), event.getDeviceId()), savedDevice);
} else {
toEvict.add(new DeviceCacheKey(event.getDeviceId()));
toEvict.add(new DeviceCacheKey(event.getTenantId(), event.getDeviceId()));
}
cache.evict(keys);
cache.evict(toEvict);
}
private DeviceData syncDeviceData(DeviceProfile deviceProfile, DeviceData deviceData) {

2
dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java

@ -246,8 +246,8 @@ public class EdgeServiceImpl extends AbstractCachedEntityService<EdgeCacheKey, E
}
edgeDao.removeById(tenantId, edgeId.getId());
eventPublisher.publishEvent(DeleteEntityEvent.builder().tenantId(tenantId).entityId(edgeId).build());
publishEvictEvent(new EdgeCacheEvictEvent(edge.getTenantId(), edge.getName(), null));
eventPublisher.publishEvent(DeleteEntityEvent.builder().tenantId(tenantId).entityId(edgeId).build());
}
@Override

29
dao/src/main/java/org/thingsboard/server/dao/entity/CachedVersionedEntityService.java

@ -0,0 +1,29 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.entity;
import org.springframework.beans.factory.annotation.Autowired;
import org.thingsboard.server.cache.VersionedTbCache;
import org.thingsboard.server.common.data.HasVersion;
import java.io.Serializable;
public abstract class CachedVersionedEntityService<K extends Serializable, V extends Serializable & HasVersion, E> extends AbstractCachedEntityService<K, V, E> {
@Autowired
protected VersionedTbCache<K, V> cache;
}

8
dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewCacheValue.java

@ -19,6 +19,7 @@ import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.HasVersion;
import java.io.Serializable;
import java.util.List;
@ -26,11 +27,16 @@ import java.util.List;
@Getter
@EqualsAndHashCode
@Builder
public class EntityViewCacheValue implements Serializable {
public class EntityViewCacheValue implements Serializable, HasVersion {
private static final long serialVersionUID = 1959004642076413174L;
private final EntityView entityView;
private final List<EntityView> entityViews;
@Override
public Long getVersion() {
return entityView != null ? entityView.getVersion() : 0;
}
}

4
dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewCaffeineCache.java

@ -18,12 +18,12 @@ package org.thingsboard.server.dao.entityview;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;
import org.thingsboard.server.cache.CaffeineTbTransactionalCache;
import org.thingsboard.server.cache.VersionedCaffeineTbCache;
import org.thingsboard.server.common.data.CacheConstants;
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true)
@Service("EntityViewCache")
public class EntityViewCaffeineCache extends CaffeineTbTransactionalCache<EntityViewCacheKey, EntityViewCacheValue> {
public class EntityViewCaffeineCache extends VersionedCaffeineTbCache<EntityViewCacheKey, EntityViewCacheValue> {
public EntityViewCaffeineCache(CacheManager cacheManager) {
super(cacheManager, CacheConstants.ENTITY_VIEW_CACHE);

6
dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewEvictEvent.java

@ -15,21 +15,25 @@
*/
package org.thingsboard.server.dao.entityview;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.TenantId;
@Data
@RequiredArgsConstructor
@AllArgsConstructor
class EntityViewEvictEvent {
private final TenantId tenantId;
private final EntityViewId id;
private final EntityViewId entityViewId;
private final EntityId newEntityId;
private final EntityId oldEntityId;
private final String newName;
private final String oldName;
private EntityView savedEntityView;
}

4
dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewRedisCache.java

@ -19,14 +19,14 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.stereotype.Service;
import org.thingsboard.server.cache.CacheSpecsMap;
import org.thingsboard.server.cache.RedisTbTransactionalCache;
import org.thingsboard.server.cache.TBRedisCacheConfiguration;
import org.thingsboard.server.cache.TbJsonRedisSerializer;
import org.thingsboard.server.cache.VersionedRedisTbCache;
import org.thingsboard.server.common.data.CacheConstants;
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis")
@Service("EntityViewCache")
public class EntityViewRedisCache extends RedisTbTransactionalCache<EntityViewCacheKey, EntityViewCacheValue> {
public class EntityViewRedisCache extends VersionedRedisTbCache<EntityViewCacheKey, EntityViewCacheValue> {
public EntityViewRedisCache(TBRedisCacheConfiguration configuration, CacheSpecsMap cacheSpecsMap, RedisConnectionFactory connectionFactory) {
super(CacheConstants.ENTITY_VIEW_CACHE, cacheSpecsMap, connectionFactory, configuration, new TbJsonRedisSerializer<>(EntityViewCacheValue.class));

40
dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java

@ -44,13 +44,13 @@ import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.EntitySearchDirection;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.dao.entity.AbstractCachedEntityService;
import org.thingsboard.server.dao.entity.CachedVersionedEntityService;
import org.thingsboard.server.dao.eventsourcing.ActionEntityEvent;
import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent;
import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.PaginatedRemover;
import org.thingsboard.server.dao.service.validator.EntityViewDataValidator;
import org.thingsboard.server.dao.sql.JpaExecutorService;
import java.util.ArrayList;
@ -69,7 +69,7 @@ import static org.thingsboard.server.dao.service.Validator.validateString;
*/
@Service("EntityViewDaoService")
@Slf4j
public class EntityViewServiceImpl extends AbstractCachedEntityService<EntityViewCacheKey, EntityViewCacheValue, EntityViewEvictEvent> implements EntityViewService {
public class EntityViewServiceImpl extends CachedVersionedEntityService<EntityViewCacheKey, EntityViewCacheValue, EntityViewEvictEvent> implements EntityViewService {
public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
public static final String INCORRECT_CUSTOMER_ID = "Incorrect customerId ";
@ -80,7 +80,7 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService<EntityVie
private EntityViewDao entityViewDao;
@Autowired
private DataValidator<EntityView> entityViewValidator;
private EntityViewDataValidator entityViewValidator;
@Autowired
protected JpaExecutorService service;
@ -88,17 +88,21 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService<EntityVie
@TransactionalEventListener(classes = EntityViewEvictEvent.class)
@Override
public void handleEvictEvent(EntityViewEvictEvent event) {
List<EntityViewCacheKey> keys = new ArrayList<>(5);
keys.add(EntityViewCacheKey.byName(event.getTenantId(), event.getNewName()));
keys.add(EntityViewCacheKey.byId(event.getId()));
keys.add(EntityViewCacheKey.byEntityId(event.getTenantId(), event.getNewEntityId()));
List<EntityViewCacheKey> toEvict = new ArrayList<>(5);
toEvict.add(EntityViewCacheKey.byName(event.getTenantId(), event.getNewName()));
if (event.getSavedEntityView() != null) {
cache.put(EntityViewCacheKey.byId(event.getSavedEntityView().getId()), new EntityViewCacheValue(event.getSavedEntityView(), null));
} else if (event.getEntityViewId() != null) {
toEvict.add(EntityViewCacheKey.byId(event.getEntityViewId()));
}
toEvict.add(EntityViewCacheKey.byEntityId(event.getTenantId(), event.getNewEntityId()));
if (event.getOldEntityId() != null && !event.getOldEntityId().equals(event.getNewEntityId())) {
keys.add(EntityViewCacheKey.byEntityId(event.getTenantId(), event.getOldEntityId()));
toEvict.add(EntityViewCacheKey.byEntityId(event.getTenantId(), event.getOldEntityId()));
}
if (StringUtils.isNotEmpty(event.getOldName()) && !event.getOldName().equals(event.getNewName())) {
keys.add(EntityViewCacheKey.byName(event.getTenantId(), event.getOldName()));
toEvict.add(EntityViewCacheKey.byName(event.getTenantId(), event.getOldName()));
}
cache.evict(keys);
cache.evict(toEvict);
}
@Override
@ -113,11 +117,11 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService<EntityVie
if (doValidate) {
old = entityViewValidator.validate(entityView, EntityView::getTenantId);
} else if (entityView.getId() != null) {
old = findEntityViewById(entityView.getTenantId(), entityView.getId());
old = findEntityViewById(entityView.getTenantId(), entityView.getId(), false);
}
try {
EntityView saved = entityViewDao.save(entityView.getTenantId(), entityView);
publishEvictEvent(new EntityViewEvictEvent(saved.getTenantId(), saved.getId(), saved.getEntityId(), old != null ? old.getEntityId() : null, saved.getName(), old != null ? old.getName() : null));
publishEvictEvent(new EntityViewEvictEvent(saved.getTenantId(), saved.getId(), saved.getEntityId(), old != null ? old.getEntityId() : null, saved.getName(), old != null ? old.getName() : null, saved));
eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(saved.getTenantId())
.entityId(saved.getId()).created(entityView.getId() == null).build());
return saved;
@ -172,9 +176,11 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService<EntityVie
public EntityView findEntityViewById(TenantId tenantId, EntityViewId entityViewId, boolean putInCache) {
log.trace("Executing findEntityViewById [{}]", entityViewId);
validateId(entityViewId, id -> INCORRECT_ENTITY_VIEW_ID + id);
return cache.getOrFetchFromDB(EntityViewCacheKey.byId(entityViewId),
() -> entityViewDao.findById(tenantId, entityViewId.getId())
, EntityViewCacheValue::getEntityView, v -> new EntityViewCacheValue(v, null), true, putInCache);
EntityViewCacheValue value = cache.get(EntityViewCacheKey.byId(entityViewId), () -> {
EntityView entityView = entityViewDao.findById(tenantId, entityViewId.getId());
return new EntityViewCacheValue(entityView, null);
}, putInCache);
return value != null ? value.getEntityView() : null;
}
@Override
@ -233,7 +239,7 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService<EntityVie
PageLink pageLink) {
log.trace("Executing findEntityViewByTenantIdAndCustomerId, tenantId [{}], customerId [{}]," +
" pageLink [{}]", tenantId, customerId, pageLink);
validateId(tenantId, id ->INCORRECT_TENANT_ID + id);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id);
validatePageLink(pageLink);
return entityViewDao.findEntityViewsByTenantIdAndCustomerId(tenantId.getId(),

14
dao/src/main/java/org/thingsboard/server/dao/model/BaseSqlEntity.java

@ -22,6 +22,7 @@ import jakarta.persistence.MappedSuperclass;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.BaseData;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UUIDBased;
@ -48,6 +49,19 @@ public abstract class BaseSqlEntity<D> implements BaseEntity<D> {
@Column(name = ModelConstants.CREATED_TIME_PROPERTY, updatable = false)
protected long createdTime;
public BaseSqlEntity() {
}
public BaseSqlEntity(BaseData<?> domain) {
this.id = domain.getUuidId();
this.createdTime = domain.getCreatedTime();
}
public BaseSqlEntity(BaseSqlEntity<?> entity) {
this.id = entity.id;
this.createdTime = entity.createdTime;
}
@Override
public UUID getUuid() {
return id;

45
dao/src/main/java/org/thingsboard/server/dao/model/BaseVersionedEntity.java

@ -15,6 +15,47 @@
*/
package org.thingsboard.server.dao.model;
public interface BaseVersionedEntity {
long getVersion();
import jakarta.persistence.Column;
import jakarta.persistence.MappedSuperclass;
import jakarta.persistence.Version;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import org.thingsboard.server.common.data.BaseData;
import org.thingsboard.server.common.data.HasVersion;
@Data
@EqualsAndHashCode(callSuper = true)
@MappedSuperclass
public abstract class BaseVersionedEntity<D extends BaseData & HasVersion> extends BaseSqlEntity<D> implements HasVersion {
@Getter @Setter
@Version
@Column(name = ModelConstants.VERSION_PROPERTY)
protected Long version;
public BaseVersionedEntity() {
super();
}
public BaseVersionedEntity(D domain) {
super(domain);
this.version = domain.getVersion();
}
public BaseVersionedEntity(BaseVersionedEntity<?> entity) {
super(entity);
this.version = entity.version;
}
@Override
public String toString() {
return "BaseVersionedEntity{" +
"id=" + id +
", createdTime=" + createdTime +
", version=" + version +
'}';
}
}

1
dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java

@ -49,6 +49,7 @@ public class ModelConstants {
public static final String SEARCH_TEXT_PROPERTY = "search_text";
public static final String ADDITIONAL_INFO_PROPERTY = "additional_info";
public static final String ENTITY_TYPE_PROPERTY = "entity_type";
public static final String VERSION_PROPERTY = "version";
public static final String ENTITY_TYPE_COLUMN = ENTITY_TYPE_PROPERTY;
public static final String TENANT_ID_COLUMN = "tenant_id";

4
dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractAlarmCommentEntity.java

@ -26,7 +26,6 @@ import org.thingsboard.server.common.data.alarm.AlarmCommentType;
import org.thingsboard.server.common.data.id.AlarmCommentId;
import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.dao.model.BaseEntity;
import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.util.mapping.JsonConverter;
@ -40,7 +39,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.ALARM_COMMENT_TYPE
@Data
@EqualsAndHashCode(callSuper = true)
@MappedSuperclass
public abstract class AbstractAlarmCommentEntity<T extends AlarmComment> extends BaseSqlEntity<T> implements BaseEntity<T> {
public abstract class AbstractAlarmCommentEntity<T extends AlarmComment> extends BaseSqlEntity<T> {
@Column(name = ALARM_COMMENT_ALARM_ID, columnDefinition = "uuid")
private UUID alarmId;
@ -94,4 +93,5 @@ public abstract class AbstractAlarmCommentEntity<T extends AlarmComment> extends
alarmComment.setComment(comment);
return alarmComment;
}
}

15
dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractAssetEntity.java

@ -26,7 +26,7 @@ import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.AssetProfileId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.BaseVersionedEntity;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.util.mapping.JsonConverter;
@ -42,7 +42,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.EXTERNAL_ID_PROPER
@Data
@EqualsAndHashCode(callSuper = true)
@MappedSuperclass
public abstract class AbstractAssetEntity<T extends Asset> extends BaseSqlEntity<T> {
public abstract class AbstractAssetEntity<T extends Asset> extends BaseVersionedEntity<T> {
@Column(name = ASSET_TENANT_ID_PROPERTY)
private UUID tenantId;
@ -73,11 +73,8 @@ public abstract class AbstractAssetEntity<T extends Asset> extends BaseSqlEntity
super();
}
public AbstractAssetEntity(Asset asset) {
if (asset.getId() != null) {
this.setUuid(asset.getId().getId());
}
this.setCreatedTime(asset.getCreatedTime());
public AbstractAssetEntity(T asset) {
super(asset);
if (asset.getTenantId() != null) {
this.tenantId = asset.getTenantId().getId();
}
@ -97,8 +94,7 @@ public abstract class AbstractAssetEntity<T extends Asset> extends BaseSqlEntity
}
public AbstractAssetEntity(AssetEntity assetEntity) {
this.setId(assetEntity.getId());
this.setCreatedTime(assetEntity.getCreatedTime());
super(assetEntity);
this.tenantId = assetEntity.getTenantId();
this.customerId = assetEntity.getCustomerId();
this.assetProfileId = assetEntity.getAssetProfileId();
@ -112,6 +108,7 @@ public abstract class AbstractAssetEntity<T extends Asset> extends BaseSqlEntity
protected Asset toAsset() {
Asset asset = new Asset(new AssetId(id));
asset.setCreatedTime(createdTime);
asset.setVersion(version);
if (tenantId != null) {
asset.setTenantId(TenantId.fromUUID(tenantId));
}

17
dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractDeviceEntity.java

@ -32,7 +32,7 @@ import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.OtaPackageId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.BaseVersionedEntity;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.util.mapping.JsonConverter;
@ -41,7 +41,7 @@ import java.util.UUID;
@Data
@EqualsAndHashCode(callSuper = true)
@MappedSuperclass
public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEntity<T> {
public abstract class AbstractDeviceEntity<T extends Device> extends BaseVersionedEntity<T> {
@Column(name = ModelConstants.DEVICE_TENANT_ID_PROPERTY, columnDefinition = "uuid")
private UUID tenantId;
@ -83,11 +83,8 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti
super();
}
public AbstractDeviceEntity(Device device) {
if (device.getId() != null) {
this.setUuid(device.getUuidId());
}
this.setCreatedTime(device.getCreatedTime());
public AbstractDeviceEntity(T device) {
super(device);
if (device.getTenantId() != null) {
this.tenantId = device.getTenantId().getId();
}
@ -113,9 +110,8 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti
}
}
public AbstractDeviceEntity(DeviceEntity deviceEntity) {
this.setId(deviceEntity.getId());
this.setCreatedTime(deviceEntity.getCreatedTime());
public AbstractDeviceEntity(AbstractDeviceEntity<T> deviceEntity) {
super(deviceEntity);
this.tenantId = deviceEntity.getTenantId();
this.customerId = deviceEntity.getCustomerId();
this.deviceProfileId = deviceEntity.getDeviceProfileId();
@ -132,6 +128,7 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti
protected Device toDevice() {
Device device = new Device(new DeviceId(getUuid()));
device.setCreatedTime(createdTime);
device.setVersion(version);
if (tenantId != null) {
device.setTenantId(TenantId.fromUUID(tenantId));
}

16
dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractEdgeEntity.java

@ -26,7 +26,7 @@ import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.BaseVersionedEntity;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.util.mapping.JsonConverter;
@ -44,7 +44,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.EDGE_TYPE_PROPERTY
@Data
@EqualsAndHashCode(callSuper = true)
@MappedSuperclass
public abstract class AbstractEdgeEntity<T extends Edge> extends BaseSqlEntity<T> {
public abstract class AbstractEdgeEntity<T extends Edge> extends BaseVersionedEntity<T> {
@Column(name = EDGE_TENANT_ID_PROPERTY, columnDefinition = "uuid")
private UUID tenantId;
@ -78,11 +78,8 @@ public abstract class AbstractEdgeEntity<T extends Edge> extends BaseSqlEntity<T
super();
}
public AbstractEdgeEntity(Edge edge) {
if (edge.getId() != null) {
this.setUuid(edge.getId().getId());
}
this.setCreatedTime(edge.getCreatedTime());
public AbstractEdgeEntity(T edge) {
super(edge);
if (edge.getTenantId() != null) {
this.tenantId = edge.getTenantId().getId();
}
@ -101,8 +98,7 @@ public abstract class AbstractEdgeEntity<T extends Edge> extends BaseSqlEntity<T
}
public AbstractEdgeEntity(EdgeEntity edgeEntity) {
this.setId(edgeEntity.getId());
this.setCreatedTime(edgeEntity.getCreatedTime());
super(edgeEntity);
this.tenantId = edgeEntity.getTenantId();
this.customerId = edgeEntity.getCustomerId();
this.rootRuleChainId = edgeEntity.getRootRuleChainId();
@ -117,6 +113,7 @@ public abstract class AbstractEdgeEntity<T extends Edge> extends BaseSqlEntity<T
protected Edge toEdge() {
Edge edge = new Edge(new EdgeId(getUuid()));
edge.setCreatedTime(createdTime);
edge.setVersion(version);
if (tenantId != null) {
edge.setTenantId(TenantId.fromUUID(tenantId));
}
@ -134,4 +131,5 @@ public abstract class AbstractEdgeEntity<T extends Edge> extends BaseSqlEntity<T
edge.setAdditionalInfo(additionalInfo);
return edge;
}
}

16
dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractEntityViewEntity.java

@ -32,7 +32,7 @@ import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.objects.TelemetryEntityView;
import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.BaseVersionedEntity;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.util.mapping.JsonConverter;
@ -48,7 +48,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_TYPE_PROPER
@EqualsAndHashCode(callSuper = true)
@MappedSuperclass
@Slf4j
public abstract class AbstractEntityViewEntity<T extends EntityView> extends BaseSqlEntity<T> {
public abstract class AbstractEntityViewEntity<T extends EntityView> extends BaseVersionedEntity<T> {
@Column(name = ModelConstants.ENTITY_VIEW_ENTITY_ID_PROPERTY)
private UUID entityId;
@ -89,11 +89,8 @@ public abstract class AbstractEntityViewEntity<T extends EntityView> extends Bas
super();
}
public AbstractEntityViewEntity(EntityView entityView) {
if (entityView.getId() != null) {
this.setUuid(entityView.getId().getId());
}
this.setCreatedTime(entityView.getCreatedTime());
public AbstractEntityViewEntity(T entityView) {
super(entityView);
if (entityView.getEntityId() != null) {
this.entityId = entityView.getEntityId().getId();
this.entityType = entityView.getEntityId().getEntityType();
@ -120,8 +117,7 @@ public abstract class AbstractEntityViewEntity<T extends EntityView> extends Bas
}
public AbstractEntityViewEntity(EntityViewEntity entityViewEntity) {
this.setId(entityViewEntity.getId());
this.setCreatedTime(entityViewEntity.getCreatedTime());
super(entityViewEntity);
this.entityId = entityViewEntity.getEntityId();
this.entityType = entityViewEntity.getEntityType();
this.tenantId = entityViewEntity.getTenantId();
@ -138,6 +134,7 @@ public abstract class AbstractEntityViewEntity<T extends EntityView> extends Bas
protected EntityView toEntityView() {
EntityView entityView = new EntityView(new EntityViewId(getUuid()));
entityView.setCreatedTime(createdTime);
entityView.setVersion(version);
if (entityId != null) {
entityView.setEntityId(EntityIdFactory.getByTypeAndUuid(entityType.name(), entityId));
@ -163,4 +160,5 @@ public abstract class AbstractEntityViewEntity<T extends EntityView> extends Bas
}
return entityView;
}
}

15
dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractTenantEntity.java

@ -24,7 +24,7 @@ import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.TenantProfileId;
import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.BaseVersionedEntity;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.util.mapping.JsonConverter;
@ -33,7 +33,7 @@ import java.util.UUID;
@Data
@EqualsAndHashCode(callSuper = true)
@MappedSuperclass
public abstract class AbstractTenantEntity<T extends Tenant> extends BaseSqlEntity<T> {
public abstract class AbstractTenantEntity<T extends Tenant> extends BaseVersionedEntity<T> {
@Column(name = ModelConstants.TENANT_TITLE_PROPERTY)
private String title;
@ -76,11 +76,8 @@ public abstract class AbstractTenantEntity<T extends Tenant> extends BaseSqlEnti
super();
}
public AbstractTenantEntity(Tenant tenant) {
if (tenant.getId() != null) {
this.setUuid(tenant.getId().getId());
}
this.setCreatedTime(tenant.getCreatedTime());
public AbstractTenantEntity(T tenant) {
super(tenant);
this.title = tenant.getTitle();
this.region = tenant.getRegion();
this.country = tenant.getCountry();
@ -98,8 +95,7 @@ public abstract class AbstractTenantEntity<T extends Tenant> extends BaseSqlEnti
}
public AbstractTenantEntity(TenantEntity tenantEntity) {
this.setId(tenantEntity.getId());
this.setCreatedTime(tenantEntity.getCreatedTime());
super(tenantEntity);
this.title = tenantEntity.getTitle();
this.region = tenantEntity.getRegion();
this.country = tenantEntity.getCountry();
@ -117,6 +113,7 @@ public abstract class AbstractTenantEntity<T extends Tenant> extends BaseSqlEnti
protected Tenant toTenant() {
Tenant tenant = new Tenant(TenantId.fromUUID(this.getUuid()));
tenant.setCreatedTime(createdTime);
tenant.setVersion(version);
tenant.setTitle(title);
tenant.setRegion(region);
tenant.setCountry(country);

15
dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractWidgetTypeEntity.java

@ -22,7 +22,7 @@ import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.WidgetTypeId;
import org.thingsboard.server.common.data.widget.BaseWidgetType;
import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.BaseVersionedEntity;
import org.thingsboard.server.dao.model.ModelConstants;
import java.util.UUID;
@ -30,7 +30,7 @@ import java.util.UUID;
@Data
@EqualsAndHashCode(callSuper = true)
@MappedSuperclass
public abstract class AbstractWidgetTypeEntity<T extends BaseWidgetType> extends BaseSqlEntity<T> {
public abstract class AbstractWidgetTypeEntity<T extends BaseWidgetType> extends BaseVersionedEntity<T> {
@Column(name = ModelConstants.WIDGET_TYPE_TENANT_ID_PROPERTY)
private UUID tenantId;
@ -51,11 +51,8 @@ public abstract class AbstractWidgetTypeEntity<T extends BaseWidgetType> extends
super();
}
public AbstractWidgetTypeEntity(BaseWidgetType widgetType) {
if (widgetType.getId() != null) {
this.setUuid(widgetType.getId().getId());
}
this.setCreatedTime(widgetType.getCreatedTime());
public AbstractWidgetTypeEntity(T widgetType) {
super(widgetType);
if (widgetType.getTenantId() != null) {
this.tenantId = widgetType.getTenantId().getId();
}
@ -66,8 +63,7 @@ public abstract class AbstractWidgetTypeEntity<T extends BaseWidgetType> extends
}
public AbstractWidgetTypeEntity(AbstractWidgetTypeEntity widgetTypeEntity) {
this.setId(widgetTypeEntity.getId());
this.setCreatedTime(widgetTypeEntity.getCreatedTime());
super(widgetTypeEntity);
this.tenantId = widgetTypeEntity.getTenantId();
this.fqn = widgetTypeEntity.getFqn();
this.name = widgetTypeEntity.getName();
@ -78,6 +74,7 @@ public abstract class AbstractWidgetTypeEntity<T extends BaseWidgetType> extends
protected BaseWidgetType toBaseWidgetType() {
BaseWidgetType widgetType = new BaseWidgetType(new WidgetTypeId(getUuid()));
widgetType.setCreatedTime(createdTime);
widgetType.setVersion(version);
if (tenantId != null) {
widgetType.setTenantId(TenantId.fromUUID(tenantId));
}

1
dao/src/main/java/org/thingsboard/server/dao/model/sql/AssetInfoEntity.java

@ -18,6 +18,7 @@ package org.thingsboard.server.dao.model.sql;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.asset.AssetInfo;
import java.util.HashMap;

10
dao/src/main/java/org/thingsboard/server/dao/model/sql/AssetProfileEntity.java

@ -25,7 +25,7 @@ import org.thingsboard.server.common.data.id.AssetProfileId;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.BaseVersionedEntity;
import org.thingsboard.server.dao.model.ModelConstants;
import java.util.UUID;
@ -34,7 +34,7 @@ import java.util.UUID;
@EqualsAndHashCode(callSuper = true)
@Entity
@Table(name = ModelConstants.ASSET_PROFILE_TABLE_NAME)
public final class AssetProfileEntity extends BaseSqlEntity<AssetProfile> {
public final class AssetProfileEntity extends BaseVersionedEntity<AssetProfile> {
@Column(name = ModelConstants.ASSET_PROFILE_TENANT_ID_PROPERTY)
private UUID tenantId;
@ -71,13 +71,10 @@ public final class AssetProfileEntity extends BaseSqlEntity<AssetProfile> {
}
public AssetProfileEntity(AssetProfile assetProfile) {
if (assetProfile.getId() != null) {
this.setUuid(assetProfile.getId().getId());
}
super(assetProfile);
if (assetProfile.getTenantId() != null) {
this.tenantId = assetProfile.getTenantId().getId();
}
this.setCreatedTime(assetProfile.getCreatedTime());
this.name = assetProfile.getName();
this.image = assetProfile.getImage();
this.description = assetProfile.getDescription();
@ -101,6 +98,7 @@ public final class AssetProfileEntity extends BaseSqlEntity<AssetProfile> {
public AssetProfile toData() {
AssetProfile assetProfile = new AssetProfile(new AssetProfileId(this.getUuid()));
assetProfile.setCreatedTime(createdTime);
assetProfile.setVersion(version);
if (tenantId != null) {
assetProfile.setTenantId(TenantId.fromUUID(tenantId));
}

10
dao/src/main/java/org/thingsboard/server/dao/model/sql/CustomerEntity.java

@ -25,7 +25,7 @@ import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.BaseVersionedEntity;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.util.mapping.JsonConverter;
@ -35,7 +35,7 @@ import java.util.UUID;
@EqualsAndHashCode(callSuper = true)
@Entity
@Table(name = ModelConstants.CUSTOMER_TABLE_NAME)
public final class CustomerEntity extends BaseSqlEntity<Customer> {
public final class CustomerEntity extends BaseVersionedEntity<Customer> {
@Column(name = ModelConstants.CUSTOMER_TENANT_ID_PROPERTY)
private UUID tenantId;
@ -82,10 +82,7 @@ public final class CustomerEntity extends BaseSqlEntity<Customer> {
}
public CustomerEntity(Customer customer) {
if (customer.getId() != null) {
this.setUuid(customer.getId().getId());
}
this.setCreatedTime(customer.getCreatedTime());
super(customer);
this.tenantId = customer.getTenantId().getId();
this.title = customer.getTitle();
this.country = customer.getCountry();
@ -107,6 +104,7 @@ public final class CustomerEntity extends BaseSqlEntity<Customer> {
public Customer toData() {
Customer customer = new Customer(new CustomerId(this.getUuid()));
customer.setCreatedTime(createdTime);
customer.setVersion(version);
customer.setTenantId(TenantId.fromUUID(tenantId));
customer.setTitle(title);
customer.setCountry(country);

11
dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardEntity.java

@ -30,7 +30,7 @@ import org.thingsboard.server.common.data.ShortCustomerInfo;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.BaseVersionedEntity;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.util.mapping.JsonConverter;
@ -42,7 +42,7 @@ import java.util.UUID;
@EqualsAndHashCode(callSuper = true)
@Entity
@Table(name = ModelConstants.DASHBOARD_TABLE_NAME)
public final class DashboardEntity extends BaseSqlEntity<Dashboard> {
public final class DashboardEntity extends BaseVersionedEntity<Dashboard> {
private static final JavaType assignedCustomersType =
JacksonUtil.constructCollectionType(HashSet.class, ShortCustomerInfo.class);
@ -77,10 +77,7 @@ public final class DashboardEntity extends BaseSqlEntity<Dashboard> {
}
public DashboardEntity(Dashboard dashboard) {
if (dashboard.getId() != null) {
this.setUuid(dashboard.getId().getId());
}
this.setCreatedTime(dashboard.getCreatedTime());
super(dashboard);
if (dashboard.getTenantId() != null) {
this.tenantId = dashboard.getTenantId().getId();
}
@ -105,6 +102,7 @@ public final class DashboardEntity extends BaseSqlEntity<Dashboard> {
public Dashboard toData() {
Dashboard dashboard = new Dashboard(new DashboardId(this.getUuid()));
dashboard.setCreatedTime(this.getCreatedTime());
dashboard.setVersion(version);
if (tenantId != null) {
dashboard.setTenantId(TenantId.fromUUID(tenantId));
}
@ -125,4 +123,5 @@ public final class DashboardEntity extends BaseSqlEntity<Dashboard> {
}
return dashboard;
}
}

10
dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardInfoEntity.java

@ -28,7 +28,7 @@ import org.thingsboard.server.common.data.ShortCustomerInfo;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.BaseVersionedEntity;
import org.thingsboard.server.dao.model.ModelConstants;
import java.util.HashSet;
@ -39,7 +39,7 @@ import java.util.UUID;
@EqualsAndHashCode(callSuper = true)
@Entity
@Table(name = ModelConstants.DASHBOARD_TABLE_NAME)
public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> {
public class DashboardInfoEntity extends BaseVersionedEntity<DashboardInfo> {
private static final JavaType assignedCustomersType =
JacksonUtil.constructCollectionType(HashSet.class, ShortCustomerInfo.class);
@ -67,10 +67,7 @@ public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> {
}
public DashboardInfoEntity(DashboardInfo dashboardInfo) {
if (dashboardInfo.getId() != null) {
this.setUuid(dashboardInfo.getId().getId());
}
this.setCreatedTime(dashboardInfo.getCreatedTime());
super(dashboardInfo);
if (dashboardInfo.getTenantId() != null) {
this.tenantId = dashboardInfo.getTenantId().getId();
}
@ -91,6 +88,7 @@ public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> {
public DashboardInfo toData() {
DashboardInfo dashboardInfo = new DashboardInfo(new DashboardId(this.getUuid()));
dashboardInfo.setCreatedTime(createdTime);
dashboardInfo.setVersion(version);
if (tenantId != null) {
dashboardInfo.setTenantId(TenantId.fromUUID(tenantId));
}

11
dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceCredentialsEntity.java

@ -26,8 +26,7 @@ import org.thingsboard.server.common.data.id.DeviceCredentialsId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.dao.model.BaseEntity;
import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.BaseVersionedEntity;
import org.thingsboard.server.dao.model.ModelConstants;
import java.util.UUID;
@ -36,7 +35,7 @@ import java.util.UUID;
@EqualsAndHashCode(callSuper = true)
@Entity
@Table(name = ModelConstants.DEVICE_CREDENTIALS_TABLE_NAME)
public final class DeviceCredentialsEntity extends BaseSqlEntity<DeviceCredentials> implements BaseEntity<DeviceCredentials> {
public final class DeviceCredentialsEntity extends BaseVersionedEntity<DeviceCredentials> {
@Column(name = ModelConstants.DEVICE_CREDENTIALS_DEVICE_ID_PROPERTY)
private UUID deviceId;
@ -56,10 +55,7 @@ public final class DeviceCredentialsEntity extends BaseSqlEntity<DeviceCredentia
}
public DeviceCredentialsEntity(DeviceCredentials deviceCredentials) {
if (deviceCredentials.getId() != null) {
this.setUuid(deviceCredentials.getId().getId());
}
this.setCreatedTime(deviceCredentials.getCreatedTime());
super(deviceCredentials);
if (deviceCredentials.getDeviceId() != null) {
this.deviceId = deviceCredentials.getDeviceId().getId();
}
@ -72,6 +68,7 @@ public final class DeviceCredentialsEntity extends BaseSqlEntity<DeviceCredentia
public DeviceCredentials toData() {
DeviceCredentials deviceCredentials = new DeviceCredentials(new DeviceCredentialsId(this.getUuid()));
deviceCredentials.setCreatedTime(createdTime);
deviceCredentials.setVersion(version);
if (deviceId != null) {
deviceCredentials.setDeviceId(new DeviceId(deviceId));
}

11
dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java

@ -38,7 +38,7 @@ import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.OtaPackageId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.BaseVersionedEntity;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.util.mapping.JsonConverter;
@ -48,7 +48,7 @@ import java.util.UUID;
@EqualsAndHashCode(callSuper = true)
@Entity
@Table(name = ModelConstants.DEVICE_PROFILE_TABLE_NAME)
public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> {
public final class DeviceProfileEntity extends BaseVersionedEntity<DeviceProfile> {
@Column(name = ModelConstants.DEVICE_PROFILE_TENANT_ID_PROPERTY)
private UUID tenantId;
@ -111,13 +111,10 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> {
}
public DeviceProfileEntity(DeviceProfile deviceProfile) {
if (deviceProfile.getId() != null) {
this.setUuid(deviceProfile.getId().getId());
}
super(deviceProfile);
if (deviceProfile.getTenantId() != null) {
this.tenantId = deviceProfile.getTenantId().getId();
}
this.setCreatedTime(deviceProfile.getCreatedTime());
this.name = deviceProfile.getName();
this.type = deviceProfile.getType();
this.image = deviceProfile.getImage();
@ -152,6 +149,7 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> {
public DeviceProfile toData() {
DeviceProfile deviceProfile = new DeviceProfile(new DeviceProfileId(this.getUuid()));
deviceProfile.setCreatedTime(createdTime);
deviceProfile.setVersion(version);
if (tenantId != null) {
deviceProfile.setTenantId(TenantId.fromUUID(tenantId));
}
@ -187,4 +185,5 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> {
return deviceProfile;
}
}

11
dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleChainEntity.java

@ -30,7 +30,7 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.BaseVersionedEntity;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.util.mapping.JsonConverter;
@ -40,7 +40,7 @@ import java.util.UUID;
@EqualsAndHashCode(callSuper = true)
@Entity
@Table(name = ModelConstants.RULE_CHAIN_TABLE_NAME)
public class RuleChainEntity extends BaseSqlEntity<RuleChain> {
public class RuleChainEntity extends BaseVersionedEntity<RuleChain> {
@Column(name = ModelConstants.RULE_CHAIN_TENANT_ID_PROPERTY)
private UUID tenantId;
@ -76,10 +76,7 @@ public class RuleChainEntity extends BaseSqlEntity<RuleChain> {
}
public RuleChainEntity(RuleChain ruleChain) {
if (ruleChain.getId() != null) {
this.setUuid(ruleChain.getUuidId());
}
this.setCreatedTime(ruleChain.getCreatedTime());
super(ruleChain);
this.tenantId = DaoUtil.getId(ruleChain.getTenantId());
this.name = ruleChain.getName();
this.type = ruleChain.getType();
@ -99,6 +96,7 @@ public class RuleChainEntity extends BaseSqlEntity<RuleChain> {
public RuleChain toData() {
RuleChain ruleChain = new RuleChain(new RuleChainId(this.getUuid()));
ruleChain.setCreatedTime(createdTime);
ruleChain.setVersion(version);
ruleChain.setTenantId(TenantId.fromUUID(tenantId));
ruleChain.setName(name);
ruleChain.setType(type);
@ -114,4 +112,5 @@ public class RuleChainEntity extends BaseSqlEntity<RuleChain> {
}
return ruleChain;
}
}

10
dao/src/main/java/org/thingsboard/server/dao/model/sql/UserEntity.java

@ -29,7 +29,7 @@ import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.BaseVersionedEntity;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.util.mapping.JsonConverter;
@ -42,7 +42,7 @@ import java.util.UUID;
@EqualsAndHashCode(callSuper = true)
@Entity
@Table(name = ModelConstants.USER_PG_HIBERNATE_TABLE_NAME)
public class UserEntity extends BaseSqlEntity<User> {
public class UserEntity extends BaseVersionedEntity<User> {
@Column(name = ModelConstants.USER_TENANT_ID_PROPERTY)
private UUID tenantId;
@ -74,10 +74,7 @@ public class UserEntity extends BaseSqlEntity<User> {
}
public UserEntity(User user) {
if (user.getId() != null) {
this.setUuid(user.getId().getId());
}
this.setCreatedTime(user.getCreatedTime());
super(user);
this.authority = user.getAuthority();
if (user.getTenantId() != null) {
this.tenantId = user.getTenantId().getId();
@ -96,6 +93,7 @@ public class UserEntity extends BaseSqlEntity<User> {
public User toData() {
User user = new User(new UserId(this.getUuid()));
user.setCreatedTime(createdTime);
user.setVersion(version);
user.setAuthority(authority);
if (tenantId != null) {
user.setTenantId(TenantId.fromUUID(tenantId));

11
dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetsBundleEntity.java

@ -24,7 +24,7 @@ import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.WidgetsBundleId;
import org.thingsboard.server.common.data.widget.WidgetsBundle;
import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.BaseVersionedEntity;
import org.thingsboard.server.dao.model.ModelConstants;
import java.util.UUID;
@ -33,7 +33,7 @@ import java.util.UUID;
@EqualsAndHashCode(callSuper = true)
@Entity
@Table(name = ModelConstants.WIDGETS_BUNDLE_TABLE_NAME)
public final class WidgetsBundleEntity extends BaseSqlEntity<WidgetsBundle> {
public final class WidgetsBundleEntity extends BaseVersionedEntity<WidgetsBundle> {
@Column(name = ModelConstants.WIDGETS_BUNDLE_TENANT_ID_PROPERTY)
private UUID tenantId;
@ -64,10 +64,7 @@ public final class WidgetsBundleEntity extends BaseSqlEntity<WidgetsBundle> {
}
public WidgetsBundleEntity(WidgetsBundle widgetsBundle) {
if (widgetsBundle.getId() != null) {
this.setUuid(widgetsBundle.getId().getId());
}
this.setCreatedTime(widgetsBundle.getCreatedTime());
super(widgetsBundle);
if (widgetsBundle.getTenantId() != null) {
this.tenantId = widgetsBundle.getTenantId().getId();
}
@ -86,6 +83,7 @@ public final class WidgetsBundleEntity extends BaseSqlEntity<WidgetsBundle> {
public WidgetsBundle toData() {
WidgetsBundle widgetsBundle = new WidgetsBundle(new WidgetsBundleId(id));
widgetsBundle.setCreatedTime(createdTime);
widgetsBundle.setVersion(version);
if (tenantId != null) {
widgetsBundle.setTenantId(TenantId.fromUUID(tenantId));
}
@ -100,4 +98,5 @@ public final class WidgetsBundleEntity extends BaseSqlEntity<WidgetsBundle> {
}
return widgetsBundle;
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save