Browse Source

Merge branch 'master' into fix_bug_californium_3_11

pull/10462/head
Viacheslav Klimov 2 years ago
committed by GitHub
parent
commit
ae2533162e
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 2
      .github/ISSUE_TEMPLATE/---bug-report.md
  2. 2
      .github/ISSUE_TEMPLATE/feature_request.md
  3. 2
      .github/ISSUE_TEMPLATE/question.md
  4. 111
      application/src/main/data/json/demo/dashboards/rule_engine_statistics.json
  5. 32
      application/src/main/data/upgrade/3.6.3/schema_update.sql
  6. 6
      application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
  7. 3
      application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
  8. 6
      application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
  9. 8
      application/src/main/java/org/thingsboard/server/controller/BaseController.java
  10. 1
      application/src/main/java/org/thingsboard/server/controller/EntityQueryController.java
  11. 7
      application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java
  12. 8
      application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java
  13. 75
      application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java
  14. 41
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java
  15. 2
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeSyncCursor.java
  16. 32
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/oauth2/OAuth2MsgConstructor.java
  17. 2
      application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/CustomerEdgeEventFetcher.java
  18. 56
      application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/OAuth2EdgeEventFetcher.java
  19. 248
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java
  20. 3
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java
  21. 20
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/customer/CustomerEdgeProcessor.java
  22. 63
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/oauth2/OAuth2EdgeProcessor.java
  23. 27
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessor.java
  24. 1
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java
  25. 11
      application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java
  26. 16
      application/src/main/java/org/thingsboard/server/service/entitiy/asset/DefaultTbAssetService.java
  27. 11
      application/src/main/java/org/thingsboard/server/service/entitiy/asset/profile/DefaultTbAssetProfileService.java
  28. 2
      application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java
  29. 8
      application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java
  30. 4
      application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java
  31. 47
      application/src/main/java/org/thingsboard/server/service/stats/DefaultRuleEngineStatisticsService.java
  32. 149
      application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java
  33. 6
      application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java
  34. 76
      application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java
  35. 1
      application/src/main/java/org/thingsboard/server/service/sync/vc/data/EntitiesImportCtx.java
  36. 4
      application/src/main/resources/thingsboard.yml
  37. 53
      application/src/test/java/org/thingsboard/server/controller/AssetControllerTest.java
  38. 21
      application/src/test/java/org/thingsboard/server/controller/BaseQueueControllerTest.java
  39. 38
      application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java
  40. 51
      application/src/test/java/org/thingsboard/server/controller/EntityQueryControllerTest.java
  41. 2
      application/src/test/java/org/thingsboard/server/controller/HomePageApiTest.java
  42. 3
      application/src/test/java/org/thingsboard/server/controller/UserControllerTest.java
  43. 15
      application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java
  44. 113
      application/src/test/java/org/thingsboard/server/edge/OAuth2EdgeTest.java
  45. 6
      application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java
  46. 25
      application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessorTest.java
  47. 40
      application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessorTest.java
  48. 80
      application/src/test/java/org/thingsboard/server/service/script/NashornJsInvokeServiceTest.java
  49. 2
      application/src/test/resources/logback-test.xml
  50. 2
      common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java
  51. 1
      common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java
  52. 37
      common/dao-api/src/main/java/org/thingsboard/server/dao/queue/QueueStatsService.java
  53. 6
      common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java
  54. 3
      common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEventType.java
  55. 2
      common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java
  56. 43
      common/data/src/main/java/org/thingsboard/server/common/data/id/QueueStatsId.java
  57. 2
      common/data/src/main/java/org/thingsboard/server/common/data/id/UUIDBased.java
  58. 2
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Info.java
  59. 2
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Params.java
  60. 26
      common/data/src/main/java/org/thingsboard/server/common/data/page/PageLink.java
  61. 30
      common/data/src/main/java/org/thingsboard/server/common/data/page/TimePageLink.java
  62. 39
      common/data/src/main/java/org/thingsboard/server/common/data/queue/QueueStats.java
  63. 1
      common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/request/load/EntityTypeVersionLoadRequest.java
  64. 6
      common/edge-api/src/main/proto/edge.proto
  65. 150
      common/proto/src/main/java/org/thingsboard/server/common/util/KvProtoUtil.java
  66. 65
      common/proto/src/main/java/org/thingsboard/server/common/util/ProtoUtils.java
  67. 1
      common/proto/src/main/proto/queue.proto
  68. 118
      common/proto/src/test/java/org/thingsboard/server/common/util/KvProtoUtilTest.java
  69. 30
      common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java
  70. 24
      common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java
  71. 4
      common/script/script-api/src/main/java/org/thingsboard/script/api/AbstractScriptInvokeService.java
  72. 9
      common/script/script-api/src/main/java/org/thingsboard/script/api/js/NashornJsInvokeService.java
  73. 71
      common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java
  74. 82
      common/transport/http/src/test/java/org/thingsboard/server/transport/http/DeviceApiControllerTest.java
  75. 16
      common/transport/http/src/test/resources/logback-test.xml
  76. 18
      dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java
  77. 2
      dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java
  78. 4
      dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmCommentService.java
  79. 20
      dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java
  80. 24
      dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java
  81. 81
      dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java
  82. 6
      dao/src/main/java/org/thingsboard/server/dao/attributes/AttributeUtils.java
  83. 8
      dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java
  84. 4
      dao/src/main/java/org/thingsboard/server/dao/attributes/CachedAttributesService.java
  85. 14
      dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java
  86. 14
      dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java
  87. 32
      dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java
  88. 2
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityServiceImpl.java
  89. 4
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java
  90. 26
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java
  91. 68
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
  92. 64
      dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java
  93. 66
      dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java
  94. 68
      dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java
  95. 9
      dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
  96. 5
      dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ParamsEntity.java
  97. 69
      dao/src/main/java/org/thingsboard/server/dao/model/sql/QueueStatsEntity.java
  98. 6
      dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ConfigTemplateServiceImpl.java
  99. 12
      dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java
  100. 1
      dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java

2
.github/ISSUE_TEMPLATE/---bug-report.md

@ -3,7 +3,7 @@ name: "\U0001F41E Bug report"
about: Create a report to help us improve
title: "Your title here"
labels: ['bug', 'unconfirmed']
assignees: AndriichnekoDm
assignees: Ultrazombie
---

2
.github/ISSUE_TEMPLATE/feature_request.md

@ -3,7 +3,7 @@ name: Feature request
about: Suggest an idea for this project
title: "Your title here"
labels: ['feature']
assignees: 'AndriichnekoDm'
assignees: 'Ultrazombie'
---

2
.github/ISSUE_TEMPLATE/question.md

@ -3,7 +3,7 @@ name: Question
about: Describe your questions in detail
title: "Your title here"
labels: ['question']
assignees: 'AndriichnekoDm'
assignees: 'Ultrazombie'
---

111
application/src/main/data/json/demo/dashboards/rule_engine_statistics.json

@ -13,6 +13,7 @@
"datasources": [
{
"type": "entity",
"entityAliasId": "140f23dd-e3a0-ed98-6189-03c49d2d8018",
"dataKeys": [
{
"name": "ruleEngineException",
@ -51,7 +52,59 @@
"_hash": 0.7255162989552142
}
],
"entityAliasId": "140f23dd-e3a0-ed98-6189-03c49d2d8018"
"alarmFilterConfig": {
"statusList": [
"ACTIVE"
]
},
"latestDataKeys": [
{
"name": "queueName",
"type": "entityField",
"label": "Queue name",
"color": "#ffc107",
"settings": {
"show": false,
"order": null,
"useCellStyleFunction": false,
"cellStyleFunction": "",
"useCellContentFunction": false,
"cellContentFunction": "",
"defaultColumnVisibility": "visible",
"columnSelectionToDisplay": "enabled"
},
"_hash": 0.8104572478982748,
"aggregationType": null,
"units": null,
"decimals": null,
"funcBody": null,
"usePostProcessing": null,
"postFuncBody": null
},
{
"name": "serviceId",
"type": "entityField",
"label": "Service Id",
"color": "#607d8b",
"settings": {
"show": false,
"order": null,
"useCellStyleFunction": false,
"cellStyleFunction": "",
"useCellContentFunction": false,
"cellContentFunction": "",
"defaultColumnVisibility": "visible",
"columnSelectionToDisplay": "enabled"
},
"_hash": 0.38329217099945034,
"aggregationType": null,
"units": null,
"decimals": null,
"funcBody": null,
"usePostProcessing": null,
"postFuncBody": null
}
]
}
],
"timewindow": {
@ -71,7 +124,9 @@
"settings": {
"showTimestamp": true,
"displayPagination": true,
"defaultPageSize": 10
"defaultPageSize": 10,
"enableSearch": true,
"enableSelectColumnDisplay": true
},
"title": "Exceptions",
"dropShadow": true,
@ -89,7 +144,10 @@
"iconColor": "rgba(0, 0, 0, 0.87)",
"iconSize": "24px",
"titleTooltip": "",
"displayTimewindow": true
"displayTimewindow": true,
"configMode": "basic",
"titleFont": null,
"titleColor": null
},
"id": "5eb79712-5c24-3060-7e4f-6af36b8f842d",
"typeFullFqn": "system.cards.timeseries_table"
@ -329,7 +387,25 @@
"statusList": [
"ACTIVE"
]
}
},
"latestDataKeys": [
{
"name": "queueName",
"type": "entityField",
"label": "Queue name",
"color": "#ffc107",
"settings": {},
"_hash": 0.8012481564934415
},
{
"name": "serviceId",
"type": "entityField",
"label": "Service Id",
"color": "#607d8b",
"settings": {},
"_hash": 0.0724871638610094
}
]
}
],
"timewindow": {
@ -724,7 +800,25 @@
"statusList": [
"ACTIVE"
]
}
},
"latestDataKeys": [
{
"name": "queueName",
"type": "entityField",
"label": "Queue name",
"color": "#f44336",
"settings": {},
"_hash": 0.7242351292118758
},
{
"name": "serviceId",
"type": "entityField",
"label": "Service Id",
"color": "#ffc107",
"settings": {},
"_hash": 0.3347262075244206
}
]
}
],
"timewindow": {
@ -1004,12 +1098,9 @@
"id": "140f23dd-e3a0-ed98-6189-03c49d2d8018",
"alias": "TbServiceQueues",
"filter": {
"type": "assetType",
"type": "entityType",
"resolveMultiple": true,
"assetNameFilter": "",
"assetTypes": [
"TbServiceQueue"
]
"entityType": "QUEUE_STATS"
}
}
},

32
application/src/main/data/upgrade/3.6.3/schema_update.sql

@ -14,7 +14,6 @@
-- limitations under the License.
--
-- create new attribute_kv table schema
DO
$$
@ -105,4 +104,33 @@ EXCEPTION
ROLLBACK;
RAISE EXCEPTION 'Error during COPY: %', SQLERRM;
END
$$;
$$;
-- OAUTH2 PARAMS ALTER TABLE START
ALTER TABLE oauth2_params
ADD COLUMN IF NOT EXISTS edge_enabled boolean DEFAULT false;
-- OAUTH2 PARAMS ALTER TABLE END
-- QUEUE STATS UPDATE START
CREATE TABLE IF NOT EXISTS queue_stats (
id uuid NOT NULL CONSTRAINT queue_stats_pkey PRIMARY KEY,
created_time bigint NOT NULL,
tenant_id uuid NOT NULL,
queue_name varchar(255) NOT NULL,
service_id varchar(255) NOT NULL,
CONSTRAINT queue_stats_name_unq_key UNIQUE (tenant_id, queue_name, service_id)
);
INSERT INTO queue_stats
SELECT id, created_time, tenant_id, substring(name FROM 1 FOR position('_' IN name) - 1) AS queue_name,
substring(name FROM position('_' IN name) + 1) AS service_id
FROM asset
WHERE type = 'TbServiceQueue' and name LIKE '%\_%';
DELETE FROM asset WHERE type='TbServiceQueue';
DELETE FROM asset_profile WHERE name ='TbServiceQueue';
-- QUEUE STATS UPDATE END

6
application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java

@ -79,6 +79,7 @@ import org.thingsboard.server.dao.notification.NotificationTargetService;
import org.thingsboard.server.dao.notification.NotificationTemplateService;
import org.thingsboard.server.dao.ota.OtaPackageService;
import org.thingsboard.server.dao.queue.QueueService;
import org.thingsboard.server.dao.queue.QueueStatsService;
import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.dao.resource.ResourceService;
import org.thingsboard.server.dao.rule.RuleChainService;
@ -447,6 +448,11 @@ public class ActorSystemContext {
@Getter
private QueueService queueService;
@Lazy
@Autowired(required = false)
@Getter
private QueueStatsService queueStatsService;
@Lazy
@Autowired(required = false)
@Getter

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

@ -21,6 +21,7 @@ import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import jakarta.annotation.Nullable;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.thingsboard.common.util.JacksonUtil;
@ -68,7 +69,6 @@ import org.thingsboard.server.common.msg.rule.engine.DeviceEdgeUpdateMsg;
import org.thingsboard.server.common.msg.rule.engine.DeviceNameOrTypeUpdateMsg;
import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
import org.thingsboard.server.common.util.KvProtoUtil;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg;
import org.thingsboard.server.gen.transport.TransportProtos.DeviceSessionsCacheEntry;
@ -97,7 +97,6 @@ import org.thingsboard.server.service.rpc.RpcSubmitStrategy;
import org.thingsboard.server.service.state.DefaultDeviceStateService;
import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
import jakarta.annotation.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;

6
application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java

@ -96,6 +96,7 @@ import org.thingsboard.server.dao.notification.NotificationTargetService;
import org.thingsboard.server.dao.notification.NotificationTemplateService;
import org.thingsboard.server.dao.ota.OtaPackageService;
import org.thingsboard.server.dao.queue.QueueService;
import org.thingsboard.server.dao.queue.QueueStatsService;
import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.dao.resource.ResourceService;
import org.thingsboard.server.dao.rule.RuleChainService;
@ -768,6 +769,11 @@ class DefaultTbContext implements TbContext {
return mainCtx.getQueueService();
}
@Override
public QueueStatsService getQueueStatsService() {
return mainCtx.getQueueStatsService();
}
@Override
public EventLoopGroup getSharedEventLoop() {
return mainCtx.getSharedEventLoopGroupService().getSharedEventLoopGroup();

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

@ -506,7 +506,7 @@ public abstract class BaseController {
TenantProfile checkTenantProfileId(TenantProfileId tenantProfileId, Operation operation) throws ThingsboardException {
try {
validateId(tenantProfileId, "Incorrect tenantProfileId " + tenantProfileId);
validateId(tenantProfileId, id -> "Incorrect tenantProfileId " + id);
TenantProfile tenantProfile = tenantProfileService.findTenantProfileById(getTenantId(), tenantProfileId);
checkNotNull(tenantProfile, "Tenant profile with id [" + tenantProfileId + "] is not found");
accessControlService.checkPermission(getCurrentUser(), Resource.TENANT_PROFILE, operation);
@ -541,7 +541,7 @@ public abstract class BaseController {
if (entityId == null) {
throw new ThingsboardException("Parameter entityId can't be empty!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
}
validateId(entityId.getId(), "Incorrect entityId " + entityId);
validateId(entityId.getId(), id -> "Incorrect entityId " + id);
switch (entityId.getEntityType()) {
case ALARM:
checkAlarmId(new AlarmId(entityId.getId()), operation);
@ -668,7 +668,7 @@ public abstract class BaseController {
AlarmComment checkAlarmCommentId(AlarmCommentId alarmCommentId, AlarmId alarmId) throws ThingsboardException {
try {
validateId(alarmCommentId, "Incorrect alarmCommentId " + alarmCommentId);
validateId(alarmCommentId, id -> "Incorrect alarmCommentId " + id);
AlarmComment alarmComment = alarmCommentService.findAlarmCommentByIdAsync(getCurrentUser().getTenantId(), alarmCommentId).get();
checkNotNull(alarmComment, "Alarm comment with id [" + alarmCommentId + "] is not found");
if (!alarmId.equals(alarmComment.getAlarmId())) {
@ -736,7 +736,7 @@ public abstract class BaseController {
}
protected RuleNode checkRuleNode(RuleNodeId ruleNodeId, Operation operation) throws ThingsboardException {
validateId(ruleNodeId, "Incorrect ruleNodeId " + ruleNodeId);
validateId(ruleNodeId, id -> "Incorrect ruleNodeId " + id);
RuleNode ruleNode = ruleChainService.findRuleNodeById(getTenantId(), ruleNodeId);
checkNotNull(ruleNode, "Rule node with id [" + ruleNodeId + "] is not found");
checkRuleChain(ruleNode.getRuleChainId(), operation);

1
application/src/main/java/org/thingsboard/server/controller/EntityQueryController.java

@ -20,7 +20,6 @@ import io.swagger.v3.oas.annotations.media.Schema;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

7
application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java

@ -42,6 +42,7 @@ import org.thingsboard.server.service.edge.rpc.processor.device.DeviceEdgeProces
import org.thingsboard.server.service.edge.rpc.processor.device.profile.DeviceProfileEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.edge.EdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.entityview.EntityViewEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.oauth2.OAuth2EdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.ota.OtaPackageEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.queue.QueueEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.relation.RelationEdgeProcessor;
@ -126,6 +127,9 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
@Autowired
private ResourceEdgeProcessor resourceEdgeProcessor;
@Autowired
private OAuth2EdgeProcessor oAuth2EdgeProcessor;
@Autowired
protected ApplicationEventPublisher eventPublisher;
@ -231,6 +235,9 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
case TB_RESOURCE:
resourceEdgeProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
break;
case OAUTH2:
oAuth2EdgeProcessor.processOAuth2Notification(tenantId, edgeNotificationMsg);
break;
default:
log.warn("[{}] Edge event type [{}] is not designed to be pushed to edge", tenantId, type);
}

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

@ -32,6 +32,7 @@ import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.edge.EdgeEventService;
import org.thingsboard.server.dao.edge.EdgeService;
import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.oauth2.OAuth2Service;
import org.thingsboard.server.dao.ota.OtaPackageService;
import org.thingsboard.server.dao.queue.QueueService;
import org.thingsboard.server.dao.resource.ResourceService;
@ -61,6 +62,7 @@ import org.thingsboard.server.service.edge.rpc.processor.device.profile.DevicePr
import org.thingsboard.server.service.edge.rpc.processor.edge.EdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.entityview.EntityViewEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.entityview.EntityViewProcessorFactory;
import org.thingsboard.server.service.edge.rpc.processor.oauth2.OAuth2EdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.ota.OtaPackageEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.queue.QueueEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.relation.RelationEdgeProcessor;
@ -151,6 +153,9 @@ public class EdgeContextComponent {
@Autowired
private ResourceService resourceService;
@Autowired
private OAuth2Service oAuth2Service;
@Autowired
private RateLimitService rateLimitService;
@ -220,6 +225,9 @@ public class EdgeContextComponent {
@Autowired
private ResourceEdgeProcessor resourceEdgeProcessor;
@Autowired
private OAuth2EdgeProcessor oAuth2EdgeProcessor;
@Autowired
private EdgeMsgConstructor edgeMsgConstructor;

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

@ -33,6 +33,7 @@ import org.thingsboard.server.common.data.alarm.AlarmComment;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.oauth2.OAuth2Info;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.common.data.rule.RuleChain;
@ -159,43 +160,41 @@ public class EdgeEventSourcingListener {
private boolean isValidSaveEntityEventForEdgeProcessing(SaveEntityEvent<?> event) {
Object entity = event.getEntity();
Object oldEntity = event.getOldEntity();
switch (event.getEntityId().getEntityType()) {
case RULE_CHAIN:
if (entity instanceof RuleChain) {
RuleChain ruleChain = (RuleChain) entity;
return RuleChainType.EDGE.equals(ruleChain.getType());
}
break;
case USER:
if (entity instanceof User) {
User user = (User) entity;
if (Authority.SYS_ADMIN.equals(user.getAuthority())) {
return false;
if (event.getEntityId() != null) {
switch (event.getEntityId().getEntityType()) {
case RULE_CHAIN:
if (entity instanceof RuleChain ruleChain) {
return RuleChainType.EDGE.equals(ruleChain.getType());
}
break;
case USER:
if (entity instanceof User user) {
if (Authority.SYS_ADMIN.equals(user.getAuthority())) {
return false;
}
if (oldEntity != null) {
User oldUser = (User) oldEntity;
cleanUpUserAdditionalInfo(oldUser);
cleanUpUserAdditionalInfo(user);
return !user.equals(oldUser);
}
}
if (oldEntity != null) {
User oldUser = (User) oldEntity;
cleanUpUserAdditionalInfo(oldUser);
cleanUpUserAdditionalInfo(user);
return !user.equals(oldUser);
break;
case OTA_PACKAGE:
if (entity instanceof OtaPackageInfo otaPackageInfo) {
return otaPackageInfo.hasUrl() || otaPackageInfo.isHasData();
}
break;
case ALARM:
if (entity instanceof AlarmApiCallResult || entity instanceof Alarm) {
return false;
}
}
break;
case OTA_PACKAGE:
if (entity instanceof OtaPackageInfo) {
OtaPackageInfo otaPackageInfo = (OtaPackageInfo) entity;
return otaPackageInfo.hasUrl() || otaPackageInfo.isHasData();
}
break;
case ALARM:
if (entity instanceof AlarmApiCallResult || entity instanceof Alarm) {
break;
case TENANT:
return !event.getCreated();
case API_USAGE_STATE, EDGE:
return false;
}
break;
case TENANT:
return !event.getCreated();
case API_USAGE_STATE:
case EDGE:
return false;
}
}
// Default: If the entity doesn't match any of the conditions, consider it as valid.
return true;
@ -206,8 +205,7 @@ public class EdgeEventSourcingListener {
if (user.getAdditionalInfo() instanceof NullNode) {
user.setAdditionalInfo(null);
}
if (user.getAdditionalInfo() instanceof ObjectNode) {
ObjectNode additionalInfo = ((ObjectNode) user.getAdditionalInfo());
if (user.getAdditionalInfo() instanceof ObjectNode additionalInfo) {
additionalInfo.remove(UserServiceImpl.FAILED_LOGIN_ATTEMPTS);
additionalInfo.remove(UserServiceImpl.LAST_LOGIN_TS);
if (additionalInfo.isEmpty()) {
@ -221,6 +219,8 @@ public class EdgeEventSourcingListener {
private EdgeEventType getEdgeEventTypeForEntityEvent(Object entity) {
if (entity instanceof AlarmComment) {
return EdgeEventType.ALARM_COMMENT;
} else if (entity instanceof OAuth2Info) {
return EdgeEventType.OAUTH2;
}
return null;
}
@ -228,6 +228,8 @@ public class EdgeEventSourcingListener {
private String getBodyMsgForEntityEvent(Object entity) {
if (entity instanceof AlarmComment) {
return JacksonUtil.toString(entity);
} else if (entity instanceof OAuth2Info) {
return JacksonUtil.toString(entity);
}
return null;
}
@ -238,4 +240,5 @@ public class EdgeEventSourcingListener {
}
return isCreated ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED;
}
}

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

@ -498,9 +498,9 @@ public final class EdgeGrpcSession implements Closeable {
log.trace("[{}][{}][{}] downlink msg(s) are going to be send.", this.tenantId, this.sessionId, copy.size());
for (DownlinkMsg downlinkMsg : copy) {
if (this.clientMaxInboundMessageSize != 0 && downlinkMsg.getSerializedSize() > this.clientMaxInboundMessageSize) {
String error = String.format("Client max inbound message size [{%s}] is exceeded. Please increase value of CLOUD_RPC_MAX_INBOUND_MESSAGE_SIZE " +
String error = String.format("Client max inbound message size %s is exceeded. Please increase value of CLOUD_RPC_MAX_INBOUND_MESSAGE_SIZE " +
"env variable on the edge and restart it.", this.clientMaxInboundMessageSize);
String message = String.format("Downlink msg size [{%s}] exceeds client max inbound message size [{%s}]. " +
String message = String.format("Downlink msg size %s exceeds client max inbound message size %s. " +
"Please increase value of CLOUD_RPC_MAX_INBOUND_MESSAGE_SIZE env variable on the edge and restart it.", downlinkMsg.getSerializedSize(), this.clientMaxInboundMessageSize);
log.error("[{}][{}][{}] {} Message {}", this.tenantId, edge.getId(), this.sessionId, message, downlinkMsg);
ctx.getNotificationRuleProcessor().process(EdgeCommunicationFailureTrigger.builder().tenantId(tenantId)
@ -551,35 +551,14 @@ public final class EdgeGrpcSession implements Closeable {
DownlinkMsg downlinkMsg = null;
try {
switch (edgeEvent.getAction()) {
case UPDATED:
case ADDED:
case DELETED:
case ASSIGNED_TO_EDGE:
case UNASSIGNED_FROM_EDGE:
case ALARM_ACK:
case ALARM_CLEAR:
case ALARM_DELETE:
case CREDENTIALS_UPDATED:
case RELATION_ADD_OR_UPDATE:
case RELATION_DELETED:
case CREDENTIALS_REQUEST:
case RPC_CALL:
case ASSIGNED_TO_CUSTOMER:
case UNASSIGNED_FROM_CUSTOMER:
case ADDED_COMMENT:
case UPDATED_COMMENT:
case DELETED_COMMENT:
case UPDATED, ADDED, DELETED, ASSIGNED_TO_EDGE, UNASSIGNED_FROM_EDGE, ALARM_ACK, ALARM_CLEAR, ALARM_DELETE, CREDENTIALS_UPDATED, RELATION_ADD_OR_UPDATE, RELATION_DELETED, CREDENTIALS_REQUEST, RPC_CALL, ASSIGNED_TO_CUSTOMER, UNASSIGNED_FROM_CUSTOMER, ADDED_COMMENT, UPDATED_COMMENT, DELETED_COMMENT -> {
downlinkMsg = convertEntityEventToDownlink(edgeEvent);
log.trace("[{}][{}] entity message processed [{}]", this.tenantId, this.sessionId, downlinkMsg);
break;
case ATTRIBUTES_UPDATED:
case POST_ATTRIBUTES:
case ATTRIBUTES_DELETED:
case TIMESERIES_UPDATED:
downlinkMsg = ctx.getTelemetryProcessor().convertTelemetryEventToDownlink(edgeEvent);
break;
default:
log.warn("[{}][{}] Unsupported action type [{}]", this.tenantId, this.sessionId, edgeEvent.getAction());
}
case ATTRIBUTES_UPDATED, POST_ATTRIBUTES, ATTRIBUTES_DELETED, TIMESERIES_UPDATED ->
downlinkMsg = ctx.getTelemetryProcessor().convertTelemetryEventToDownlink(edge, edgeEvent);
default ->
log.warn("[{}][{}] Unsupported action type [{}]", this.tenantId, this.sessionId, edgeEvent.getAction());
}
} catch (Exception e) {
log.error("[{}][{}] Exception during converting edge event to downlink msg", this.tenantId, this.sessionId, e);
@ -705,6 +684,8 @@ public final class EdgeGrpcSession implements Closeable {
return ctx.getTenantEdgeProcessor().convertTenantEventToDownlink(edgeEvent, this.edgeVersion);
case TENANT_PROFILE:
return ctx.getTenantProfileEdgeProcessor().convertTenantProfileEventToDownlink(edgeEvent, this.edgeVersion);
case OAUTH2:
return ctx.getOAuth2EdgeProcessor().convertOAuth2EventToDownlink(edgeEvent);
default:
log.warn("[{}] Unsupported edge event type [{}]", this.tenantId, edgeEvent);
return null;
@ -855,7 +836,7 @@ public final class EdgeGrpcSession implements Closeable {
.build();
}
String error = "Failed to validate the edge!";
String failureMsg = String.format("{%s} Provided request secret: %s", error, request.getEdgeSecret());
String failureMsg = String.format("%s Provided request secret: %s", error, request.getEdgeSecret());
ctx.getNotificationRuleProcessor().process(EdgeCommunicationFailureTrigger.builder().tenantId(tenantId).edgeId(edge.getId())
.customerId(edge.getCustomerId()).edgeName(edge.getName()).failureMsg(failureMsg).error(error).build());
return ConnectResponseMsg.newBuilder()

2
application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeSyncCursor.java

@ -30,6 +30,7 @@ import org.thingsboard.server.service.edge.rpc.fetch.DeviceProfilesEdgeEventFetc
import org.thingsboard.server.service.edge.rpc.fetch.DevicesEdgeEventFetcher;
import org.thingsboard.server.service.edge.rpc.fetch.EdgeEventFetcher;
import org.thingsboard.server.service.edge.rpc.fetch.EntityViewsEdgeEventFetcher;
import org.thingsboard.server.service.edge.rpc.fetch.OAuth2EdgeEventFetcher;
import org.thingsboard.server.service.edge.rpc.fetch.OtaPackagesEdgeEventFetcher;
import org.thingsboard.server.service.edge.rpc.fetch.QueuesEdgeEventFetcher;
import org.thingsboard.server.service.edge.rpc.fetch.RuleChainsEdgeEventFetcher;
@ -79,6 +80,7 @@ public class EdgeSyncCursor {
fetchers.add(new TenantWidgetsBundlesEdgeEventFetcher(ctx.getWidgetsBundleService()));
fetchers.add(new OtaPackagesEdgeEventFetcher(ctx.getOtaPackageService()));
fetchers.add(new TenantResourcesEdgeEventFetcher(ctx.getResourceService()));
fetchers.add(new OAuth2EdgeEventFetcher(ctx.getOAuth2Service()));
}
}

32
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/oauth2/OAuth2MsgConstructor.java

@ -0,0 +1,32 @@
/**
* 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.service.edge.rpc.constructor.oauth2;
import org.springframework.stereotype.Component;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.oauth2.OAuth2Info;
import org.thingsboard.server.gen.edge.v1.OAuth2UpdateMsg;
import org.thingsboard.server.queue.util.TbCoreComponent;
@Component
@TbCoreComponent
public class OAuth2MsgConstructor {
public OAuth2UpdateMsg constructOAuth2UpdateMsg(OAuth2Info oAuth2Info) {
return OAuth2UpdateMsg.newBuilder().setEntity(JacksonUtil.toString(oAuth2Info)).build();
}
}

2
application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/CustomerEdgeEventFetcher.java

@ -46,7 +46,7 @@ public class CustomerEdgeEventFetcher implements EdgeEventFetcher {
List<EdgeEvent> result = new ArrayList<>();
result.add(EdgeUtils.constructEdgeEvent(edge.getTenantId(), edge.getId(),
EdgeEventType.CUSTOMER, EdgeEventActionType.ADDED, customerId, null));
// @voba - returns PageData object to be in sync with other fetchers
// returns PageData object to be in sync with other fetchers
return new PageData<>(result, 1, result.size(), false);
}
}

56
application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/OAuth2EdgeEventFetcher.java

@ -0,0 +1,56 @@
/**
* 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.service.edge.rpc.fetch;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.oauth2.OAuth2Info;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.oauth2.OAuth2Service;
import java.util.ArrayList;
import java.util.List;
@AllArgsConstructor
@Slf4j
public class OAuth2EdgeEventFetcher implements EdgeEventFetcher {
private final OAuth2Service oAuth2Service;
@Override
public PageLink getPageLink(int pageSize) {
return null;
}
@Override
public PageData<EdgeEvent> fetchEdgeEvents(TenantId tenantId, Edge edge, PageLink pageLink) {
List<EdgeEvent> result = new ArrayList<>();
OAuth2Info oAuth2Info = oAuth2Service.findOAuth2Info();
result.add(EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.OAUTH2,
EdgeEventActionType.ADDED, null, JacksonUtil.valueToTree(oAuth2Info)));
// returns PageData object to be in sync with other fetchers
return new PageData<>(result, 1, result.size(), false);
}
}

248
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java

@ -25,7 +25,6 @@ import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.EdgeUtils;
@ -51,9 +50,8 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.msg.TbMsgType;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageDataIterable;
import org.thingsboard.server.common.data.page.PageDataIterableByTenantIdEntityId;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.common.data.rule.RuleChain;
@ -75,6 +73,7 @@ import org.thingsboard.server.dao.edge.EdgeEventService;
import org.thingsboard.server.dao.edge.EdgeService;
import org.thingsboard.server.dao.edge.EdgeSynchronizationManager;
import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.oauth2.OAuth2Service;
import org.thingsboard.server.dao.ota.OtaPackageService;
import org.thingsboard.server.dao.queue.QueueService;
import org.thingsboard.server.dao.relation.RelationService;
@ -93,7 +92,6 @@ import org.thingsboard.server.queue.TbQueueCallback;
import org.thingsboard.server.queue.TbQueueMsgMetadata;
import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
import org.thingsboard.server.service.entitiy.TbLogEntityActionService;
import org.thingsboard.server.service.edge.rpc.constructor.alarm.AlarmMsgConstructorFactory;
import org.thingsboard.server.service.edge.rpc.constructor.asset.AssetMsgConstructorFactory;
import org.thingsboard.server.service.edge.rpc.constructor.customer.CustomerMsgConstructorFactory;
@ -101,6 +99,7 @@ import org.thingsboard.server.service.edge.rpc.constructor.dashboard.DashboardMs
import org.thingsboard.server.service.edge.rpc.constructor.device.DeviceMsgConstructorFactory;
import org.thingsboard.server.service.edge.rpc.constructor.edge.EdgeMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.entityview.EntityViewMsgConstructorFactory;
import org.thingsboard.server.service.edge.rpc.constructor.oauth2.OAuth2MsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.ota.OtaPackageMsgConstructorFactory;
import org.thingsboard.server.service.edge.rpc.constructor.queue.QueueMsgConstructorFactory;
import org.thingsboard.server.service.edge.rpc.constructor.relation.RelationMsgConstructorFactory;
@ -114,6 +113,7 @@ import org.thingsboard.server.service.edge.rpc.constructor.widget.WidgetMsgConst
import org.thingsboard.server.service.edge.rpc.processor.alarm.AlarmEdgeProcessorFactory;
import org.thingsboard.server.service.edge.rpc.processor.asset.AssetEdgeProcessorFactory;
import org.thingsboard.server.service.edge.rpc.processor.entityview.EntityViewProcessorFactory;
import org.thingsboard.server.service.entitiy.TbLogEntityActionService;
import org.thingsboard.server.service.executors.DbCallbackExecutorService;
import org.thingsboard.server.service.profile.TbAssetProfileCache;
import org.thingsboard.server.service.profile.TbDeviceProfileCache;
@ -226,6 +226,9 @@ public abstract class BaseEdgeProcessor {
@Autowired
protected ResourceService resourceService;
@Autowired
protected OAuth2Service oAuth2Service;
@Autowired
@Lazy
protected TbQueueProducerProvider producerProvider;
@ -257,6 +260,9 @@ public abstract class BaseEdgeProcessor {
@Autowired
protected EntityDataMsgConstructor entityDataMsgConstructor;
@Autowired
protected OAuth2MsgConstructor oAuth2MsgConstructor;
@Autowired
protected RuleChainMsgConstructorFactory ruleChainMsgConstructorFactory;
@ -349,35 +355,15 @@ public abstract class BaseEdgeProcessor {
private boolean doSaveIfEdgeIsOffline(EdgeEventType type,
EdgeEventActionType action) {
switch (action) {
case TIMESERIES_UPDATED:
case ALARM_ACK:
case ALARM_CLEAR:
case ALARM_ASSIGNED:
case ALARM_UNASSIGNED:
case CREDENTIALS_REQUEST:
case ADDED_COMMENT:
case UPDATED_COMMENT:
return true;
}
switch (type) {
case ALARM:
case ALARM_COMMENT:
case RULE_CHAIN:
case RULE_CHAIN_METADATA:
case USER:
case CUSTOMER:
case TENANT:
case TENANT_PROFILE:
case WIDGETS_BUNDLE:
case WIDGET_TYPE:
case ADMIN_SETTINGS:
case OTA_PACKAGE:
case QUEUE:
case RELATION:
return true;
}
return false;
return switch (action) {
case TIMESERIES_UPDATED, ALARM_ACK, ALARM_CLEAR, ALARM_ASSIGNED, ALARM_UNASSIGNED, CREDENTIALS_REQUEST, ADDED_COMMENT, UPDATED_COMMENT ->
true;
default -> switch (type) {
case ALARM, ALARM_COMMENT, RULE_CHAIN, RULE_CHAIN_METADATA, USER, CUSTOMER, TENANT, TENANT_PROFILE, WIDGETS_BUNDLE, WIDGET_TYPE, ADMIN_SETTINGS, OTA_PACKAGE, QUEUE, RELATION ->
true;
default -> false;
};
};
}
private ListenableFuture<Void> doSaveEdgeEvent(TenantId tenantId, EdgeId edgeId, EdgeEventType type, EdgeEventActionType action, EntityId entityId, JsonNode body) {
@ -392,18 +378,13 @@ public abstract class BaseEdgeProcessor {
protected ListenableFuture<Void> processActionForAllEdges(TenantId tenantId, EdgeEventType type,
EdgeEventActionType actionType, EntityId entityId,
EdgeId sourceEdgeId) {
JsonNode body, EdgeId sourceEdgeId) {
List<ListenableFuture<Void>> futures = new ArrayList<>();
if (TenantId.SYS_TENANT_ID.equals(tenantId)) {
PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
PageData<TenantId> tenantsIds;
do {
tenantsIds = tenantService.findTenantsIds(pageLink);
for (TenantId tenantId1 : tenantsIds.getData()) {
futures.addAll(processActionForAllEdgesByTenantId(tenantId1, type, actionType, entityId, null, sourceEdgeId));
}
pageLink = pageLink.nextPageLink();
} while (tenantsIds.hasNext());
PageDataIterable<TenantId> tenantIds = new PageDataIterable<>(link -> tenantService.findTenantsIds(link), 1024);
for (TenantId tenantId1 : tenantIds) {
futures.addAll(processActionForAllEdgesByTenantId(tenantId1, type, actionType, entityId, body, sourceEdgeId));
}
} else {
futures = processActionForAllEdgesByTenantId(tenantId, type, actionType, entityId, null, sourceEdgeId);
}
@ -416,22 +397,13 @@ public abstract class BaseEdgeProcessor {
EntityId entityId,
JsonNode body,
EdgeId sourceEdgeId) {
PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
PageData<Edge> pageData;
List<ListenableFuture<Void>> futures = new ArrayList<>();
do {
pageData = edgeService.findEdgesByTenantId(tenantId, pageLink);
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
for (Edge edge : pageData.getData()) {
if (!edge.getId().equals(sourceEdgeId)) {
futures.add(saveEdgeEvent(tenantId, edge.getId(), type, actionType, entityId, body));
}
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
PageDataIterable<Edge> edges = new PageDataIterable<>(link -> edgeService.findEdgesByTenantId(tenantId, link), 1024);
for (Edge edge : edges) {
if (!edge.getId().equals(sourceEdgeId)) {
futures.add(saveEdgeEvent(tenantId, edge.getId(), type, actionType, entityId, body));
}
} while (pageData != null && pageData.hasNext());
}
return futures;
}
@ -442,31 +414,17 @@ public abstract class BaseEdgeProcessor {
}
protected UpdateMsgType getUpdateMsgType(EdgeEventActionType actionType) {
switch (actionType) {
case UPDATED:
case CREDENTIALS_UPDATED:
case ASSIGNED_TO_CUSTOMER:
case UNASSIGNED_FROM_CUSTOMER:
case UPDATED_COMMENT:
return UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE;
case ADDED:
case ASSIGNED_TO_EDGE:
case RELATION_ADD_OR_UPDATE:
case ADDED_COMMENT:
return UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE;
case DELETED:
case UNASSIGNED_FROM_EDGE:
case RELATION_DELETED:
case DELETED_COMMENT:
case ALARM_DELETE:
return UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE;
case ALARM_ACK:
return UpdateMsgType.ALARM_ACK_RPC_MESSAGE;
case ALARM_CLEAR:
return UpdateMsgType.ALARM_CLEAR_RPC_MESSAGE;
default:
throw new RuntimeException("Unsupported actionType [" + actionType + "]");
}
return switch (actionType) {
case UPDATED, CREDENTIALS_UPDATED, ASSIGNED_TO_CUSTOMER, UNASSIGNED_FROM_CUSTOMER, UPDATED_COMMENT ->
UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE;
case ADDED, ASSIGNED_TO_EDGE, RELATION_ADD_OR_UPDATE, ADDED_COMMENT ->
UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE;
case DELETED, UNASSIGNED_FROM_EDGE, RELATION_DELETED, DELETED_COMMENT, ALARM_DELETE ->
UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE;
case ALARM_ACK -> UpdateMsgType.ALARM_ACK_RPC_MESSAGE;
case ALARM_CLEAR -> UpdateMsgType.ALARM_CLEAR_RPC_MESSAGE;
default -> throw new RuntimeException("Unsupported actionType [" + actionType + "]");
};
}
public ListenableFuture<Void> processEntityNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
@ -539,48 +497,33 @@ public abstract class BaseEdgeProcessor {
}
private ListenableFuture<Void> updateDependentRuleChains(TenantId tenantId, RuleChainId processingRuleChainId, EdgeId edgeId) {
PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
PageData<RuleChain> pageData;
List<ListenableFuture<Void>> futures = new ArrayList<>();
do {
pageData = ruleChainService.findRuleChainsByTenantIdAndEdgeId(tenantId, edgeId, pageLink);
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
for (RuleChain ruleChain : pageData.getData()) {
if (!ruleChain.getId().equals(processingRuleChainId)) {
List<RuleChainConnectionInfo> connectionInfos =
ruleChainService.loadRuleChainMetaData(ruleChain.getTenantId(), ruleChain.getId()).getRuleChainConnections();
if (connectionInfos != null && !connectionInfos.isEmpty()) {
for (RuleChainConnectionInfo connectionInfo : connectionInfos) {
if (connectionInfo.getTargetRuleChainId().equals(processingRuleChainId)) {
futures.add(saveEdgeEvent(tenantId,
edgeId,
EdgeEventType.RULE_CHAIN_METADATA,
EdgeEventActionType.UPDATED,
ruleChain.getId(),
null));
}
}
}
PageDataIterable<RuleChain> ruleChains = new PageDataIterable<>(link -> ruleChainService.findRuleChainsByTenantIdAndEdgeId(tenantId, edgeId, link), 1024);
for (RuleChain ruleChain : ruleChains) {
List<RuleChainConnectionInfo> connectionInfos =
ruleChainService.loadRuleChainMetaData(ruleChain.getTenantId(), ruleChain.getId()).getRuleChainConnections();
if (connectionInfos != null && !connectionInfos.isEmpty()) {
for (RuleChainConnectionInfo connectionInfo : connectionInfos) {
if (connectionInfo.getTargetRuleChainId().equals(processingRuleChainId)) {
futures.add(saveEdgeEvent(tenantId,
edgeId,
EdgeEventType.RULE_CHAIN_METADATA,
EdgeEventActionType.UPDATED,
ruleChain.getId(),
null));
}
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
}
} while (pageData != null && pageData.hasNext());
}
return Futures.transform(Futures.allAsList(futures), voids -> null, dbCallbackExecutorService);
}
private ListenableFuture<Void> processEntityNotificationForAllEdges(TenantId tenantId, EdgeEventType type, EdgeEventActionType actionType, EntityId entityId, EdgeId sourceEdgeId) {
switch (actionType) {
case ADDED:
case UPDATED:
case DELETED:
case CREDENTIALS_UPDATED: // used by USER entity
return processActionForAllEdges(tenantId, type, actionType, entityId, sourceEdgeId);
default:
return Futures.immediateFuture(null);
}
return switch (actionType) {
case ADDED, UPDATED, DELETED, CREDENTIALS_UPDATED -> // used by USER entity
processActionForAllEdges(tenantId, type, actionType, entityId, null, sourceEdgeId);
default -> Futures.immediateFuture(null);
};
}
protected EntityId constructEntityId(String entityTypeStr, long entityIdMSB, long entityIdLSB) {
@ -623,26 +566,18 @@ public abstract class BaseEdgeProcessor {
}
protected boolean isEntityExists(TenantId tenantId, EntityId entityId) {
switch (entityId.getEntityType()) {
case TENANT:
return tenantService.findTenantById(tenantId) != null;
case DEVICE:
return deviceService.findDeviceById(tenantId, new DeviceId(entityId.getId())) != null;
case ASSET:
return assetService.findAssetById(tenantId, new AssetId(entityId.getId())) != null;
case ENTITY_VIEW:
return entityViewService.findEntityViewById(tenantId, new EntityViewId(entityId.getId())) != null;
case CUSTOMER:
return customerService.findCustomerById(tenantId, new CustomerId(entityId.getId())) != null;
case USER:
return userService.findUserById(tenantId, new UserId(entityId.getId())) != null;
case DASHBOARD:
return dashboardService.findDashboardById(tenantId, new DashboardId(entityId.getId())) != null;
case EDGE:
return edgeService.findEdgeById(tenantId, new EdgeId(entityId.getId())) != null;
default:
return false;
}
return switch (entityId.getEntityType()) {
case TENANT -> tenantService.findTenantById(tenantId) != null;
case DEVICE -> deviceService.findDeviceById(tenantId, new DeviceId(entityId.getId())) != null;
case ASSET -> assetService.findAssetById(tenantId, new AssetId(entityId.getId())) != null;
case ENTITY_VIEW ->
entityViewService.findEntityViewById(tenantId, new EntityViewId(entityId.getId())) != null;
case CUSTOMER -> customerService.findCustomerById(tenantId, new CustomerId(entityId.getId())) != null;
case USER -> userService.findUserById(tenantId, new UserId(entityId.getId())) != null;
case DASHBOARD -> dashboardService.findDashboardById(tenantId, new DashboardId(entityId.getId())) != null;
case EDGE -> edgeService.findEdgeById(tenantId, new EdgeId(entityId.getId())) != null;
default -> false;
};
}
protected void createRelationFromEdge(TenantId tenantId, EdgeId edgeId, EntityId entityId) {
@ -681,37 +616,25 @@ public abstract class BaseEdgeProcessor {
}
protected AssetProfile checkIfAssetProfileDefaultFieldsAssignedToEdge(TenantId tenantId, EdgeId edgeId, AssetProfile assetProfile, EdgeVersion edgeVersion) {
switch (edgeVersion) {
case V_3_3_3:
case V_3_3_0:
case V_3_4_0:
if (assetProfile.getDefaultDashboardId() != null
&& isEntityNotAssignedToEdge(tenantId, assetProfile.getDefaultDashboardId(), edgeId)) {
assetProfile.setDefaultDashboardId(null);
}
if (assetProfile.getDefaultEdgeRuleChainId() != null
&& isEntityNotAssignedToEdge(tenantId, assetProfile.getDefaultEdgeRuleChainId(), edgeId)) {
assetProfile.setDefaultEdgeRuleChainId(null);
}
break;
if (EdgeVersion.V_3_3_0.equals(edgeVersion) || EdgeVersion.V_3_3_3.equals(edgeVersion) || EdgeVersion.V_3_4_0.equals(edgeVersion)) {
if (assetProfile.getDefaultDashboardId() != null && isEntityNotAssignedToEdge(tenantId, assetProfile.getDefaultDashboardId(), edgeId)) {
assetProfile.setDefaultDashboardId(null);
}
if (assetProfile.getDefaultEdgeRuleChainId() != null && isEntityNotAssignedToEdge(tenantId, assetProfile.getDefaultEdgeRuleChainId(), edgeId)) {
assetProfile.setDefaultEdgeRuleChainId(null);
}
}
return assetProfile;
}
protected DeviceProfile checkIfDeviceProfileDefaultFieldsAssignedToEdge(TenantId tenantId, EdgeId edgeId, DeviceProfile deviceProfile, EdgeVersion edgeVersion) {
switch (edgeVersion) {
case V_3_3_3:
case V_3_3_0:
case V_3_4_0:
if (deviceProfile.getDefaultDashboardId() != null
&& isEntityNotAssignedToEdge(tenantId, deviceProfile.getDefaultDashboardId(), edgeId)) {
deviceProfile.setDefaultDashboardId(null);
}
if (deviceProfile.getDefaultEdgeRuleChainId() != null
&& isEntityNotAssignedToEdge(tenantId, deviceProfile.getDefaultEdgeRuleChainId(), edgeId)) {
deviceProfile.setDefaultEdgeRuleChainId(null);
}
break;
if (EdgeVersion.V_3_3_0.equals(edgeVersion) || EdgeVersion.V_3_3_3.equals(edgeVersion) || EdgeVersion.V_3_4_0.equals(edgeVersion)) {
if (deviceProfile.getDefaultDashboardId() != null && isEntityNotAssignedToEdge(tenantId, deviceProfile.getDefaultDashboardId(), edgeId)) {
deviceProfile.setDefaultDashboardId(null);
}
if (deviceProfile.getDefaultEdgeRuleChainId() != null && isEntityNotAssignedToEdge(tenantId, deviceProfile.getDefaultEdgeRuleChainId(), edgeId)) {
deviceProfile.setDefaultEdgeRuleChainId(null);
}
}
return deviceProfile;
}
@ -726,4 +649,5 @@ public abstract class BaseEdgeProcessor {
}
return true;
}
}

3
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java

@ -32,7 +32,6 @@ import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.msg.TbMsgType;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.dao.asset.BaseAssetService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
@ -115,7 +114,7 @@ public abstract class AssetEdgeProcessor extends BaseAssetProcessor implements A
case ASSIGNED_TO_CUSTOMER:
case UNASSIGNED_FROM_CUSTOMER:
Asset asset = assetService.findAssetById(edgeEvent.getTenantId(), assetId);
if (asset != null && !BaseAssetService.TB_SERVICE_QUEUE.equals(asset.getType())) {
if (asset != null) {
UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction());
AssetUpdateMsg assetUpdateMsg = ((AssetMsgConstructor)
assetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructAssetUpdatedMsg(msgType, asset);

20
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/customer/CustomerEdgeProcessor.java

@ -29,8 +29,7 @@ import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
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.data.page.PageDataIterable;
import org.thingsboard.server.gen.edge.v1.CustomerUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.EdgeVersion;
@ -85,20 +84,11 @@ public class CustomerEdgeProcessor extends BaseEdgeProcessor {
CustomerId customerId = new CustomerId(EntityIdFactory.getByEdgeEventTypeAndUuid(type, uuid).getId());
switch (actionType) {
case UPDATED:
PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
PageData<Edge> pageData;
List<ListenableFuture<Void>> futures = new ArrayList<>();
do {
pageData = edgeService.findEdgesByTenantIdAndCustomerId(tenantId, customerId, pageLink);
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
for (Edge edge : pageData.getData()) {
futures.add(saveEdgeEvent(tenantId, edge.getId(), type, actionType, customerId, null));
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
}
} while (pageData != null && pageData.hasNext());
PageDataIterable<Edge> edges = new PageDataIterable<>(link -> edgeService.findEdgesByTenantIdAndCustomerId(tenantId, customerId, link), 1024);
for (Edge edge : edges) {
futures.add(saveEdgeEvent(tenantId, edge.getId(), type, actionType, customerId, null));
}
return Futures.transform(Futures.allAsList(futures), voids -> null, dbCallbackExecutorService);
case DELETED:
EdgeId edgeId = new EdgeId(new UUID(edgeNotificationMsg.getEdgeIdMSB(), edgeNotificationMsg.getEdgeIdLSB()));

63
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/oauth2/OAuth2EdgeProcessor.java

@ -0,0 +1,63 @@
/**
* 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.service.edge.rpc.processor.oauth2;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.oauth2.OAuth2Info;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.OAuth2UpdateMsg;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor;
@Slf4j
@Component
@TbCoreComponent
public class OAuth2EdgeProcessor extends BaseEdgeProcessor {
public DownlinkMsg convertOAuth2EventToDownlink(EdgeEvent edgeEvent) {
DownlinkMsg downlinkMsg = null;
OAuth2Info oAuth2Info = JacksonUtil.convertValue(edgeEvent.getBody(), OAuth2Info.class);
if (oAuth2Info != null) {
OAuth2UpdateMsg oAuth2UpdateMsg = oAuth2MsgConstructor.constructOAuth2UpdateMsg(oAuth2Info);
downlinkMsg = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addOAuth2UpdateMsg(oAuth2UpdateMsg)
.build();
}
return downlinkMsg;
}
public ListenableFuture<Void> processOAuth2Notification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
OAuth2Info oAuth2Info = JacksonUtil.fromString(edgeNotificationMsg.getBody(), OAuth2Info.class);
if (oAuth2Info == null) {
return Futures.immediateFuture(null);
}
EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType());
EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction());
return processActionForAllEdges(tenantId, type, actionType, null, JacksonUtil.toJsonNode(edgeNotificationMsg.getBody()), null);
}
}

27
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessor.java

@ -16,12 +16,18 @@
package org.thingsboard.server.service.edge.rpc.processor.telemetry;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.notification.rule.trigger.EdgeCommunicationFailureTrigger;
import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.EntityDataProto;
import org.thingsboard.server.queue.util.TbCoreComponent;
@ -31,18 +37,28 @@ import org.thingsboard.server.queue.util.TbCoreComponent;
@TbCoreComponent
public class TelemetryEdgeProcessor extends BaseTelemetryProcessor {
@Value("${edges.rpc.max_telemetry_message_size:0}")
private int maxTelemetryMessageSize;
@Lazy
@Autowired
private NotificationRuleProcessor notificationRuleProcessor;
@Override
protected String getMsgSourceKey() {
return DataConstants.EDGE_MSG_SOURCE;
}
public DownlinkMsg convertTelemetryEventToDownlink(EdgeEvent edgeEvent) {
public DownlinkMsg convertTelemetryEventToDownlink(Edge edge, EdgeEvent edgeEvent) {
if (edgeEvent.getBody() != null) {
String bodyStr = edgeEvent.getBody().toString();
if (bodyStr.length() > 1000) {
log.debug("[{}][{}][{}] Conversion to a DownlinkMsg telemetry event failed due to a size limit violation. " +
"Current size is {}, but the limit is 1000. {}", edgeEvent.getTenantId(), edgeEvent.getEdgeId(),
edgeEvent.getEntityId(), bodyStr.length(), StringUtils.truncate(bodyStr, 100));
if (maxTelemetryMessageSize > 0 && bodyStr.length() > maxTelemetryMessageSize) {
String error = "Conversion to a DownlinkMsg telemetry event failed due to a size limit violation.";
String message = String.format("%s Current size is %s, but the limit is %s", error, bodyStr.length(), maxTelemetryMessageSize);
log.debug("[{}][{}][{}] {}. {}", edgeEvent.getTenantId(), edgeEvent.getEdgeId(),
edgeEvent.getEntityId(), message, StringUtils.truncate(bodyStr, 100));
notificationRuleProcessor.process(EdgeCommunicationFailureTrigger.builder().tenantId(edgeEvent.getTenantId())
.edgeId(edgeEvent.getEdgeId()).customerId(edge.getCustomerId()).edgeName(edge.getName()).failureMsg(message).error(error).build());
return null;
}
}
@ -55,4 +71,5 @@ public class TelemetryEdgeProcessor extends BaseTelemetryProcessor {
.addEntityData(entityDataProto)
.build();
}
}

1
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java

@ -60,4 +60,5 @@ public class TenantEdgeProcessor extends BaseEdgeProcessor {
}
return downlinkMsg;
}
}

11
application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java

@ -25,10 +25,9 @@ import org.springframework.context.annotation.Lazy;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.alarm.AlarmInfo;
import org.thingsboard.server.common.data.alarm.AlarmQuery;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.TenantId;
@ -72,11 +71,11 @@ public abstract class AbstractTbEntityService {
@Lazy
private EntitiesVersionControlService vcService;
protected void removeAlarmsByEntityId(TenantId tenantId, EntityId entityId) {
PageData<AlarmInfo> alarms =
alarmService.findAlarms(tenantId, new AlarmQuery(entityId, new TimePageLink(Integer.MAX_VALUE), null, null, null, false));
protected void removeAlarmsByOriginatorId(TenantId tenantId, EntityId entityId) {
PageData<AlarmId> alarms =
alarmService.findAlarmIdsByOriginatorId(tenantId, entityId, new TimePageLink(Integer.MAX_VALUE));
alarms.getData().stream().map(AlarmInfo::getId).forEach(alarmId -> alarmService.delAlarm(tenantId, alarmId));
alarms.getData().forEach(alarmId -> alarmService.delAlarm(tenantId, alarmId));
}
protected <T> T checkNotNull(T reference) throws ThingsboardException {

16
application/src/main/java/org/thingsboard/server/service/entitiy/asset/DefaultTbAssetService.java

@ -22,10 +22,8 @@ import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.asset.Asset;
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.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId;
@ -33,30 +31,18 @@ import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.service.entitiy.AbstractTbEntityService;
import org.thingsboard.server.service.profile.TbAssetProfileCache;
import static org.thingsboard.server.dao.asset.BaseAssetService.TB_SERVICE_QUEUE;
@Service
@AllArgsConstructor
public class DefaultTbAssetService extends AbstractTbEntityService implements TbAssetService {
private final AssetService assetService;
private final TbAssetProfileCache assetProfileCache;
@Override
public Asset save(Asset asset, User user) throws Exception {
ActionType actionType = asset.getId() == null ? ActionType.ADDED : ActionType.UPDATED;
TenantId tenantId = asset.getTenantId();
try {
if (TB_SERVICE_QUEUE.equals(asset.getType())) {
throw new ThingsboardException("Unable to save asset with type " + TB_SERVICE_QUEUE, ThingsboardErrorCode.BAD_REQUEST_PARAMS);
} else if (asset.getAssetProfileId() != null) {
AssetProfile assetProfile = assetProfileCache.get(tenantId, asset.getAssetProfileId());
if (assetProfile != null && TB_SERVICE_QUEUE.equals(assetProfile.getName())) {
throw new ThingsboardException("Unable to save asset with profile " + TB_SERVICE_QUEUE, ThingsboardErrorCode.BAD_REQUEST_PARAMS);
}
}
Asset savedAsset = checkNotNull(assetService.saveAsset(asset));
autoCommit(user, savedAsset.getId());
logEntityActionService.logEntityAction(tenantId, savedAsset.getId(), savedAsset, asset.getCustomerId(),
@ -75,7 +61,7 @@ public class DefaultTbAssetService extends AbstractTbEntityService implements Tb
TenantId tenantId = asset.getTenantId();
AssetId assetId = asset.getId();
try {
removeAlarmsByEntityId(tenantId, assetId);
removeAlarmsByOriginatorId(tenantId, assetId);
assetService.deleteAsset(tenantId, assetId);
logEntityActionService.logEntityAction(tenantId, assetId, asset, asset.getCustomerId(), actionType, user, assetId.toString());
} catch (Exception e) {

11
application/src/main/java/org/thingsboard/server/service/entitiy/asset/profile/DefaultTbAssetProfileService.java

@ -22,7 +22,6 @@ import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.asset.AssetProfile;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.AssetProfileId;
import org.thingsboard.server.common.data.id.TenantId;
@ -30,8 +29,6 @@ import org.thingsboard.server.dao.asset.AssetProfileService;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.entitiy.AbstractTbEntityService;
import static org.thingsboard.server.dao.asset.BaseAssetService.TB_SERVICE_QUEUE;
@Service
@TbCoreComponent
@AllArgsConstructor
@ -45,14 +42,6 @@ public class DefaultTbAssetProfileService extends AbstractTbEntityService implem
ActionType actionType = assetProfile.getId() == null ? ActionType.ADDED : ActionType.UPDATED;
TenantId tenantId = assetProfile.getTenantId();
try {
if (TB_SERVICE_QUEUE.equals(assetProfile.getName())) {
throw new ThingsboardException("Unable to save asset profile with name " + TB_SERVICE_QUEUE, ThingsboardErrorCode.BAD_REQUEST_PARAMS);
} else if (assetProfile.getId() != null) {
AssetProfile foundAssetProfile = assetProfileService.findAssetProfileById(tenantId, assetProfile.getId());
if (foundAssetProfile != null && TB_SERVICE_QUEUE.equals(foundAssetProfile.getName())) {
throw new ThingsboardException("Updating asset profile with name " + TB_SERVICE_QUEUE + " is prohibited!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
}
}
AssetProfile savedAssetProfile = checkNotNull(assetProfileService.saveAssetProfile(assetProfile));
autoCommit(user, savedAssetProfile.getId());
logEntityActionService.logEntityAction(tenantId, savedAssetProfile.getId(), savedAssetProfile,

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

@ -94,7 +94,7 @@ public class DefaultTbDeviceService extends AbstractTbEntityService implements T
TenantId tenantId = device.getTenantId();
DeviceId deviceId = device.getId();
try {
removeAlarmsByEntityId(tenantId, deviceId);
removeAlarmsByOriginatorId(tenantId, deviceId);
deviceService.deleteDevice(tenantId, deviceId);
logEntityActionService.logEntityAction(tenantId, deviceId, device, device.getCustomerId(), actionType,
user, deviceId.toString());

8
application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java

@ -18,6 +18,8 @@ package org.thingsboard.server.service.queue;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
@ -50,9 +52,9 @@ import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse;
import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequestActorMsg;
import org.thingsboard.server.common.stats.StatsFactory;
import org.thingsboard.server.common.util.KvProtoUtil;
import org.thingsboard.server.common.util.ProtoUtils;
import org.thingsboard.server.dao.resource.ImageCacheKey;
import org.thingsboard.server.common.util.KvProtoUtil;
import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.DeviceStateServiceMsgProto;
@ -101,8 +103,6 @@ import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWra
import org.thingsboard.server.service.ws.notification.sub.NotificationRequestUpdate;
import org.thingsboard.server.service.ws.notification.sub.NotificationUpdate;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
@ -583,7 +583,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
subscriptionManagerService.onTimeSeriesUpdate(
toTenantId(tenantIdMSB, tenantIdLSB),
TbSubscriptionUtils.toEntityId(proto.getEntityType(), proto.getEntityIdMSB(), proto.getEntityIdLSB()),
KvProtoUtil.toTsKvEntityList(proto.getDataList()), callback);
KvProtoUtil.fromTsKvProtoList(proto.getDataList()), callback);
} else if (msg.hasAttrUpdate()) {
TbAttributeUpdateProto proto = msg.getAttrUpdate();
subscriptionManagerService.onAttributesUpdate(

4
application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java

@ -95,7 +95,7 @@ public class DefaultTbResourceService extends AbstractTbEntityService implements
@Override
public List<LwM2mObject> findLwM2mObject(TenantId tenantId, String sortOrder, String sortProperty, String[] objectIds) {
log.trace("Executing findByTenantId [{}]", tenantId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
List<TbResource> resources = resourceService.findTenantResourcesByResourceTypeAndObjectIds(tenantId, ResourceType.LWM2M_MODEL,
objectIds);
return resources.stream()
@ -107,7 +107,7 @@ public class DefaultTbResourceService extends AbstractTbEntityService implements
@Override
public List<LwM2mObject> findLwM2mObjectPage(TenantId tenantId, String sortProperty, String sortOrder, PageLink pageLink) {
log.trace("Executing findByTenantId [{}]", tenantId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
PageData<TbResource> resourcePageData = resourceService.findTenantResourcesByResourceTypeAndPageLink(tenantId, ResourceType.LWM2M_MODEL, pageLink);
return resourcePageData.getData().stream()
.flatMap(s -> Stream.ofNullable(toLwM2mObject(s, false)))

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

@ -21,15 +21,15 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.QueueStatsId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
import org.thingsboard.server.common.data.kv.JsonDataEntry;
import org.thingsboard.server.common.data.kv.LongDataEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.queue.QueueStats;
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.dao.queue.QueueStatsService;
import org.thingsboard.server.dao.usagerecord.ApiLimitService;
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
import org.thingsboard.server.queue.util.TbRuleEngineComponent;
@ -52,7 +52,6 @@ import java.util.stream.Collectors;
@RequiredArgsConstructor
public class DefaultRuleEngineStatisticsService implements RuleEngineStatisticsService {
public static final String TB_SERVICE_QUEUE = "TbServiceQueue";
public static final String RULE_ENGINE_EXCEPTION = "ruleEngineException";
public static final FutureCallback<Integer> CALLBACK = new FutureCallback<Integer>() {
@Override
@ -68,10 +67,10 @@ public class DefaultRuleEngineStatisticsService implements RuleEngineStatisticsS
private final TbServiceInfoProvider serviceInfoProvider;
private final TelemetrySubscriptionService tsService;
private final AssetService assetService;
private final QueueStatsService queueStatsService;
private final ApiLimitService apiLimitService;
private final Lock lock = new ReentrantLock();
private final ConcurrentMap<TenantQueueKey, AssetId> tenantQueueAssets = new ConcurrentHashMap<>();
private final ConcurrentMap<TenantQueueKey, QueueStatsId> tenantQueueStats = new ConcurrentHashMap<>();
@Value("${queue.rule-engine.stats.max-error-message-length:4096}")
private int maxErrorMessageLength;
@ -82,7 +81,7 @@ public class DefaultRuleEngineStatisticsService implements RuleEngineStatisticsS
ruleEngineStats.getTenantStats().forEach((id, stats) -> {
try {
TenantId tenantId = TenantId.fromUUID(id);
AssetId serviceAssetId = getServiceAssetId(tenantId, queueName);
QueueStatsId queueStatsId = getQueueStatsId(tenantId, queueName);
if (stats.getTotalMsgCounter().get() > 0) {
List<TsKvEntry> tsList = stats.getCounters().entrySet().stream()
.map(kv -> new BasicTsKvEntry(ts, new LongDataEntry(kv.getKey(), (long) kv.getValue().get())))
@ -90,7 +89,7 @@ public class DefaultRuleEngineStatisticsService implements RuleEngineStatisticsS
if (!tsList.isEmpty()) {
long ttl = apiLimitService.getLimit(tenantId, DefaultTenantProfileConfiguration::getQueueStatsTtlDays);
ttl = TimeUnit.DAYS.toSeconds(ttl);
tsService.saveAndNotifyInternal(tenantId, serviceAssetId, tsList, ttl, CALLBACK);
tsService.saveAndNotifyInternal(tenantId, queueStatsId, tsList, ttl, CALLBACK);
}
}
} catch (Exception e) {
@ -104,7 +103,7 @@ public class DefaultRuleEngineStatisticsService implements RuleEngineStatisticsS
TsKvEntry tsKv = new BasicTsKvEntry(e.getTs(), new JsonDataEntry(RULE_ENGINE_EXCEPTION, e.toJsonString(maxErrorMessageLength)));
long ttl = apiLimitService.getLimit(tenantId, DefaultTenantProfileConfiguration::getRuleEngineExceptionsTtlDays);
ttl = TimeUnit.DAYS.toSeconds(ttl);
tsService.saveAndNotifyInternal(tenantId, getServiceAssetId(tenantId, queueName), Collections.singletonList(tsKv), ttl, CALLBACK);
tsService.saveAndNotifyInternal(tenantId, getQueueStatsId(tenantId, queueName), Collections.singletonList(tsKv), ttl, CALLBACK);
} catch (Exception e2) {
if (!"Asset is referencing to non-existent tenant!".equalsIgnoreCase(e2.getMessage())) {
log.debug("[{}] Failed to store the statistics", tenantId, e2);
@ -113,30 +112,30 @@ public class DefaultRuleEngineStatisticsService implements RuleEngineStatisticsS
});
}
private AssetId getServiceAssetId(TenantId tenantId, String queueName) {
private QueueStatsId getQueueStatsId(TenantId tenantId, String queueName) {
TenantQueueKey key = new TenantQueueKey(tenantId, queueName);
AssetId assetId = tenantQueueAssets.get(key);
if (assetId == null) {
QueueStatsId queueStatsId = tenantQueueStats.get(key);
if (queueStatsId == null) {
lock.lock();
try {
assetId = tenantQueueAssets.get(key);
if (assetId == null) {
Asset asset = assetService.findAssetByTenantIdAndName(tenantId, queueName + "_" + serviceInfoProvider.getServiceId());
if (asset == null) {
asset = new Asset();
asset.setTenantId(tenantId);
asset.setName(queueName + "_" + serviceInfoProvider.getServiceId());
asset.setType(TB_SERVICE_QUEUE);
asset = assetService.saveAsset(asset);
queueStatsId = tenantQueueStats.get(key);
if (queueStatsId == null) {
QueueStats queueStats = queueStatsService.findByTenantIdAndNameAndServiceId(tenantId, queueName , serviceInfoProvider.getServiceId());
if (queueStats == null) {
queueStats = new QueueStats();
queueStats.setTenantId(tenantId);
queueStats.setQueueName(queueName);
queueStats.setServiceId(serviceInfoProvider.getServiceId());
queueStats = queueStatsService.save(tenantId, queueStats);
}
assetId = asset.getId();
tenantQueueAssets.put(key, assetId);
queueStatsId = queueStats.getId();
tenantQueueStats.put(key, queueStatsId);
}
} finally {
lock.unlock();
}
}
return assetId;
return queueStatsId;
}
@Data

149
application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java

@ -23,19 +23,9 @@ import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
import org.thingsboard.server.common.data.kv.BooleanDataEntry;
import org.thingsboard.server.common.data.kv.DataType;
import org.thingsboard.server.common.data.kv.DoubleDataEntry;
import org.thingsboard.server.common.data.kv.JsonDataEntry;
import org.thingsboard.server.common.data.kv.KvEntry;
import org.thingsboard.server.common.data.kv.LongDataEntry;
import org.thingsboard.server.common.data.kv.StringDataEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.KeyValueProto;
import org.thingsboard.server.gen.transport.TransportProtos.SubscriptionMgrMsgProto;
import org.thingsboard.server.gen.transport.TransportProtos.TbAlarmDeleteProto;
import org.thingsboard.server.gen.transport.TransportProtos.TbAlarmUpdateProto;
@ -46,29 +36,23 @@ import org.thingsboard.server.gen.transport.TransportProtos.TbTimeSeriesDeletePr
import org.thingsboard.server.gen.transport.TransportProtos.TbTimeSeriesUpdateProto;
import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg;
import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto;
import org.thingsboard.server.service.ws.notification.sub.NotificationRequestUpdate;
import org.thingsboard.server.service.ws.notification.sub.NotificationUpdate;
import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscriptionUpdate;
import org.thingsboard.server.service.ws.telemetry.sub.AlarmSubscriptionUpdate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.UUID;
public class TbSubscriptionUtils {
private static final DataType[] dataTypeByProtoNumber;
import static org.thingsboard.server.common.util.KvProtoUtil.fromTsValueProtoList;
import static org.thingsboard.server.common.util.KvProtoUtil.toTsKvProtoBuilder;
import static org.thingsboard.server.common.util.KvProtoUtil.toTsValueProto;
static {
int arraySize = Arrays.stream(DataType.values()).mapToInt(DataType::getProtoNumber).max().orElse(0);
dataTypeByProtoNumber = new DataType[arraySize + 1];
Arrays.stream(DataType.values()).forEach(dataType -> dataTypeByProtoNumber[dataType.getProtoNumber()] = dataType);
}
public class TbSubscriptionUtils {
public static ToCoreMsg toSubEventProto(String serviceId, TbEntitySubEvent event) {
SubscriptionMgrMsgProto.Builder msgBuilder = SubscriptionMgrMsgProto.newBuilder();
@ -189,7 +173,7 @@ public class TbSubscriptionUtils {
builder.setEntityIdLSB(entityId.getId().getLeastSignificantBits());
builder.setTenantIdMSB(tenantId.getId().getMostSignificantBits());
builder.setTenantIdLSB(tenantId.getId().getLeastSignificantBits());
ts.forEach(v -> builder.addData(toKeyValueProto(v.getTs(), v).build()));
ts.forEach(v -> builder.addData(toTsKvProtoBuilder(v.getTs(), v).build()));
SubscriptionMgrMsgProto.Builder msgBuilder = SubscriptionMgrMsgProto.newBuilder();
msgBuilder.setTsUpdate(builder);
return ToCoreMsg.newBuilder().setToSubscriptionMgrMsg(msgBuilder.build()).build();
@ -216,7 +200,7 @@ public class TbSubscriptionUtils {
builder.setTenantIdMSB(tenantId.getId().getMostSignificantBits());
builder.setTenantIdLSB(tenantId.getId().getLeastSignificantBits());
builder.setScope(scope);
attributes.forEach(v -> builder.addData(toKeyValueProto(v.getLastUpdateTs(), v).build()));
attributes.forEach(v -> builder.addData(toTsKvProtoBuilder(v.getLastUpdateTs(), v).build()));
SubscriptionMgrMsgProto.Builder msgBuilder = SubscriptionMgrMsgProto.newBuilder();
msgBuilder.setAttrUpdate(builder);
@ -239,121 +223,10 @@ public class TbSubscriptionUtils {
return ToCoreMsg.newBuilder().setToSubscriptionMgrMsg(msgBuilder.build()).build();
}
private static TsKvProto.Builder toKeyValueProto(long ts, KvEntry attr) {
KeyValueProto.Builder dataBuilder = KeyValueProto.newBuilder();
dataBuilder.setKey(attr.getKey());
dataBuilder.setType(toProto(attr.getDataType()));
switch (attr.getDataType()) {
case BOOLEAN:
attr.getBooleanValue().ifPresent(dataBuilder::setBoolV);
break;
case LONG:
attr.getLongValue().ifPresent(dataBuilder::setLongV);
break;
case DOUBLE:
attr.getDoubleValue().ifPresent(dataBuilder::setDoubleV);
break;
case JSON:
attr.getJsonValue().ifPresent(dataBuilder::setJsonV);
break;
case STRING:
attr.getStrValue().ifPresent(dataBuilder::setStringV);
break;
}
return TsKvProto.newBuilder().setTs(ts).setKv(dataBuilder);
}
private static TransportProtos.TsValueProto toTsValueProto(long ts, KvEntry attr) {
TransportProtos.TsValueProto.Builder dataBuilder = TransportProtos.TsValueProto.newBuilder();
dataBuilder.setTs(ts);
dataBuilder.setType(toProto(attr.getDataType()));
switch (attr.getDataType()) {
case BOOLEAN:
attr.getBooleanValue().ifPresent(dataBuilder::setBoolV);
break;
case LONG:
attr.getLongValue().ifPresent(dataBuilder::setLongV);
break;
case DOUBLE:
attr.getDoubleValue().ifPresent(dataBuilder::setDoubleV);
break;
case JSON:
attr.getJsonValue().ifPresent(dataBuilder::setJsonV);
break;
case STRING:
attr.getStrValue().ifPresent(dataBuilder::setStringV);
break;
}
return dataBuilder.build();
}
public static EntityId toEntityId(String entityType, long entityIdMSB, long entityIdLSB) {
return EntityIdFactory.getByTypeAndUuid(entityType, new UUID(entityIdMSB, entityIdLSB));
}
public static List<TsKvEntry> toTsKvEntityList(List<TsKvProto> dataList) {
List<TsKvEntry> result = new ArrayList<>(dataList.size());
dataList.forEach(proto -> result.add(new BasicTsKvEntry(proto.getTs(), getKvEntry(proto.getKv()))));
return result;
}
public static List<AttributeKvEntry> toAttributeKvList(List<TsKvProto> dataList) {
List<AttributeKvEntry> result = new ArrayList<>(dataList.size());
dataList.forEach(proto -> result.add(new BaseAttributeKvEntry(getKvEntry(proto.getKv()), proto.getTs())));
return result;
}
private static KvEntry getKvEntry(KeyValueProto proto) {
KvEntry entry = null;
switch (fromProto(proto.getType())) {
case BOOLEAN:
entry = new BooleanDataEntry(proto.getKey(), proto.getBoolV());
break;
case LONG:
entry = new LongDataEntry(proto.getKey(), proto.getLongV());
break;
case DOUBLE:
entry = new DoubleDataEntry(proto.getKey(), proto.getDoubleV());
break;
case STRING:
entry = new StringDataEntry(proto.getKey(), proto.getStringV());
break;
case JSON:
entry = new JsonDataEntry(proto.getKey(), proto.getJsonV());
break;
}
return entry;
}
public static List<TsKvEntry> toTsKvEntityList(String key, List<TransportProtos.TsValueProto> dataList) {
List<TsKvEntry> result = new ArrayList<>(dataList.size());
dataList.forEach(proto -> result.add(new BasicTsKvEntry(proto.getTs(), getKvEntry(key, proto))));
return result;
}
private static KvEntry getKvEntry(String key, TransportProtos.TsValueProto proto) {
KvEntry entry = null;
switch (fromProto(proto.getType())) {
case BOOLEAN:
entry = new BooleanDataEntry(key, proto.getBoolV());
break;
case LONG:
entry = new LongDataEntry(key, proto.getLongV());
break;
case DOUBLE:
entry = new DoubleDataEntry(key, proto.getDoubleV());
break;
case STRING:
entry = new StringDataEntry(key, proto.getStringV());
break;
case JSON:
entry = new JsonDataEntry(key, proto.getJsonV());
break;
}
return entry;
}
public static ToCoreMsg toAlarmUpdateProto(TenantId tenantId, EntityId entityId, AlarmInfo alarm) {
TbAlarmUpdateProto.Builder builder = TbAlarmUpdateProto.newBuilder();
builder.setEntityType(entityId.getEntityType().name());
@ -411,7 +284,7 @@ public class TbSubscriptionUtils {
public static List<TsKvEntry> fromProto(TransportProtos.TbSubUpdateProto proto) {
List<TsKvEntry> result = new ArrayList<>();
for (var p : proto.getDataList()) {
result.addAll(toTsKvEntityList(p.getKey(), p.getTsValueList()));
result.addAll(fromTsValueProtoList(p.getKey(), p.getTsValueList()));
}
return result;
}
@ -453,12 +326,4 @@ public class TbSubscriptionUtils {
return ToCoreNotificationMsg.newBuilder().setToLocalSubscriptionServiceMsg(result).build();
}
public static TransportProtos.KeyValueType toProto(DataType dataType) {
return TransportProtos.KeyValueType.forNumber(dataType.getProtoNumber());
}
public static DataType fromProto(TransportProtos.KeyValueType keyValueType) {
return dataTypeByProtoNumber[keyValueType.getNumber()];
}
}

6
application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java

@ -101,7 +101,11 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS
ctx.putInternalId(exportData.getExternalId(), importResult.getSavedEntity().getId());
ctx.addReferenceCallback(exportData.getExternalId(), importResult.getSaveReferencesCallback());
ctx.addEventCallback(importResult.getSendEventsCallback());
if (ctx.isRollbackOnError()) {
ctx.addEventCallback(importResult.getSendEventsCallback());
} else {
importResult.getSendEventsCallback().run();
}
return importResult;
}

76
application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java

@ -39,6 +39,7 @@ import org.thingsboard.server.common.data.id.EntityIdFactory;
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.PageDataIterable;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.sync.ie.EntityExportData;
import org.thingsboard.server.common.data.sync.ie.EntityExportSettings;
@ -89,6 +90,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.function.Function;
@ -246,16 +248,18 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont
switch (request.getType()) {
case SINGLE_ENTITY: {
SingleEntityVersionLoadRequest versionLoadRequest = (SingleEntityVersionLoadRequest) request;
ctx.setRollbackOnError(true);
VersionLoadConfig config = versionLoadRequest.getConfig();
ListenableFuture<EntityExportData> future = gitServiceQueue.getEntity(user.getTenantId(), request.getVersionId(), versionLoadRequest.getExternalEntityId());
DonAsynchron.withCallback(future,
entityData -> doInTemplate(ctx, request, c -> loadSingleEntity(c, config, entityData)),
entityData -> load(ctx, request, c -> loadSingleEntity(c, config, entityData)),
e -> processLoadError(ctx, e), executor);
break;
}
case ENTITY_TYPE: {
EntityTypeVersionLoadRequest versionLoadRequest = (EntityTypeVersionLoadRequest) request;
executor.submit(() -> doInTemplate(ctx, request, c -> loadMultipleEntities(c, versionLoadRequest)));
ctx.setRollbackOnError(versionLoadRequest.isRollbackOnError());
executor.submit(() -> load(ctx, request, c -> loadMultipleEntities(c, versionLoadRequest)));
break;
}
default:
@ -265,19 +269,24 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont
return ctx.getRequestId();
}
private <R> VersionLoadResult doInTemplate(EntitiesImportCtx ctx, VersionLoadRequest request, Function<EntitiesImportCtx, VersionLoadResult> function) {
private <R> VersionLoadResult load(EntitiesImportCtx ctx, VersionLoadRequest request, Function<EntitiesImportCtx, VersionLoadResult> loadFunction) {
try {
VersionLoadResult result = transactionTemplate.execute(status -> {
try {
return function.apply(ctx);
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e); // to prevent UndeclaredThrowableException
VersionLoadResult result;
if (ctx.isRollbackOnError()) {
result = transactionTemplate.execute(status -> {
try {
return loadFunction.apply(ctx);
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e); // to prevent UndeclaredThrowableException
}
});
for (ThrowingRunnable eventCallback : ctx.getEventCallbacks()) {
eventCallback.run();
}
});
for (ThrowingRunnable throwingRunnable : ctx.getEventCallbacks()) {
throwingRunnable.run();
} else {
result = loadFunction.apply(ctx);
}
result.setDone(true);
return cachePut(ctx.getRequestId(), result);
@ -324,7 +333,6 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont
sw.startNew("Entities " + entityType.name());
ctx.setSettings(getEntityImportSettings(request, entityType));
importEntities(ctx, entityType);
persistToCache(ctx);
}
sw.startNew("Reimport");
@ -336,7 +344,6 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont
.filter(entityType -> request.getEntityTypes().get(entityType).isRemoveOtherEntities())
.sorted(exportImportService.getEntityTypeComparatorForImport().reversed())
.forEach(entityType -> removeOtherEntities(ctx, entityType));
persistToCache(ctx);
sw.startNew("References and Relations");
exportImportService.saveReferencesAndRelations(ctx);
@ -389,6 +396,8 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont
ctx.getImportedEntities().computeIfAbsent(entityType, t -> new HashSet<>())
.add(importResult.getSavedEntity().getId());
}
persistToCache(ctx);
log.debug("Imported {} pack ({}) for tenant {}", entityType, entityDataList.size(), ctx.getTenantId());
offset += limit;
} while (entityDataList.size() == limit);
@ -413,17 +422,34 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont
}
private void removeOtherEntities(EntitiesImportCtx ctx, EntityType entityType) {
DaoUtil.processInBatches(pageLink -> {
return exportableEntitiesService.findEntitiesByTenantId(ctx.getTenantId(), entityType, pageLink);
}, 100, entity -> {
if (ctx.getImportedEntities().get(entityType) == null || !ctx.getImportedEntities().get(entityType).contains(entity.getId())) {
exportableEntitiesService.removeById(ctx.getTenantId(), entity.getId());
ctx.addEventCallback(() -> logEntityActionService.logEntityAction(ctx.getTenantId(), entity.getId(), entity, null,
ActionType.DELETED, ctx.getUser()));
ctx.registerDeleted(entityType);
var entities = new PageDataIterable<>(link -> exportableEntitiesService.findEntitiesIdsByTenantId(ctx.getTenantId(), entityType, link), 100);
Set<EntityId> toRemove = new HashSet<>();
for (EntityId entityId : entities) {
if (ctx.getImportedEntities().get(entityType) == null || !ctx.getImportedEntities().get(entityType).contains(entityId)) {
toRemove.add(entityId);
}
});
}
for (EntityId entityId : toRemove) {
ExportableEntity<EntityId> entity = exportableEntitiesService.findEntityById(entityId);
exportableEntitiesService.removeById(ctx.getTenantId(), entityId);
ThrowingRunnable callback = () -> {
logEntityActionService.logEntityAction(ctx.getTenantId(), entity.getId(), entity, null,
ActionType.DELETED, ctx.getUser());
};
if (ctx.isRollbackOnError()) {
ctx.addEventCallback(callback);
} else {
try {
callback.run();
} catch (ThingsboardException e) {
throw new RuntimeException(e);
}
}
ctx.registerDeleted(entityType);
}
persistToCache(ctx);
}
private VersionLoadResult onError(EntityId externalId, Throwable e) {

1
application/src/main/java/org/thingsboard/server/service/sync/vc/data/EntitiesImportCtx.java

@ -58,6 +58,7 @@ public class EntitiesImportCtx {
private boolean finalImportAttempt = false;
private EntityImportSettings settings;
private EntityImportResult<?> currentImportResult;
private boolean rollbackOnError;
public EntitiesImportCtx(UUID requestId, User user, String versionId) {
this(requestId, user, versionId, null);

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

@ -866,6 +866,8 @@ js:
monitor_thread_pool_size: "${LOCAL_JS_SANDBOX_MONITOR_THREAD_POOL_SIZE:4}"
# Maximum CPU time in milliseconds allowed for script execution
max_cpu_time: "${LOCAL_JS_SANDBOX_MAX_CPU_TIME:8000}"
# Maximum memory in Bytes which JS executor thread can allocate (approximate calculation). A zero memory limit in combination with a non-zero CPU limit is not recommended due to the implementation of Nashorn 0.4.2. 100MiB is effectively unlimited for most cases
max_memory: "${LOCAL_JS_SANDBOX_MAX_MEMORY:104857600}"
# Maximum allowed JavaScript execution errors before JavaScript will be blacklisted
max_errors: "${LOCAL_JS_SANDBOX_MAX_ERRORS:3}"
# JS Eval max request timeout. 0 - no timeout
@ -1304,6 +1306,8 @@ edges:
private_key: "${EDGES_RPC_SSL_PRIVATE_KEY:privateKeyFile.pem}"
# Maximum size (in bytes) of inbound messages the cloud can handle from the edge. By default, it can handle messages up to 4 Megabytes
max_inbound_message_size: "${EDGES_RPC_MAX_INBOUND_MESSAGE_SIZE:4194304}"
# Maximum length of telemetry (time-series and attributes) message the cloud sends to the edge. By default, there is no limitation.
max_telemetry_message_size: "${EDGES_RPC_MAX_TELEMETRY_MESSAGE_SIZE:0}"
storage:
# Max records of edge event to read from DB and sent to the edge
max_read_records_count: "${EDGES_STORAGE_MAX_READ_RECORDS_COUNT:50}"

53
application/src/test/java/org/thingsboard/server/controller/AssetControllerTest.java

@ -29,6 +29,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.test.context.ContextConfiguration;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.StringUtils;
@ -45,16 +46,17 @@ import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.asset.AssetDao;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.service.DaoSqlTest;
import org.thingsboard.server.service.stats.DefaultRuleEngineStatisticsService;
import java.util.ArrayList;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
@ -327,6 +329,53 @@ public class AssetControllerTest extends AbstractControllerTest {
.andExpect(statusReason(containsString(msgErrorNoFound("Alarm", alarm.getId().getId().toString()))));
}
@Test
public void testDeleteAssetWithPropagatedAlarm() throws Exception {
Device device = new Device();
device.setTenantId(savedTenant.getTenantId());
device.setName("Test device");
device.setLabel("Label");
device.setType("default");
device = doPost("/api/device", device, Device.class);
Asset asset = new Asset();
asset.setName("My asset");
asset.setType("default");
asset = doPost("/api/asset", asset, Asset.class);
EntityRelation entityRelation = new EntityRelation(asset.getId(), device.getId(), "CONTAINS");
doPost("/api/relation", entityRelation);
//create alarm
Alarm alarm = Alarm.builder()
.tenantId(savedTenant.getTenantId())
.originator(device.getId())
.severity(AlarmSeverity.CRITICAL)
.type("test_type")
.propagate(true)
.build();
alarm = doPost("/api/alarm", alarm, Alarm.class);
Assert.assertNotNull(alarm);
PageData<AlarmInfo> deviceAlarms = doGetTyped("/api/alarm/DEVICE/" + device.getUuidId() + "?page=0&pageSize=10", new TypeReference<>() {
});
assertThat(deviceAlarms.getData()).hasSize(1);
PageData<AlarmInfo> assetAlarms = doGetTyped("/api/alarm/ASSET/" + asset.getUuidId() + "?page=0&pageSize=10", new TypeReference<>() {
});
assertThat(assetAlarms.getData()).hasSize(1);
//delete asset
doDelete("/api/asset/" + asset.getId().getId().toString())
.andExpect(status().isOk());
//check device alarms
PageData<AlarmInfo> deviceAlarmsAfterAssetDeletion = doGetTyped("/api/alarm/DEVICE/" + device.getUuidId() + "?page=0&pageSize=10", new TypeReference<PageData<AlarmInfo>>() {
});
assertThat(deviceAlarmsAfterAssetDeletion.getData()).hasSize(1);
}
@Test
public void testDeleteAssetAssignedToEntityView() throws Exception {
Asset asset1 = new Asset();
@ -567,8 +616,6 @@ public class AssetControllerTest extends AbstractControllerTest {
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.ADDED, cntEntity, cntEntity, cntEntity);
loadedAssets.removeIf(asset -> asset.getType().equals(DefaultRuleEngineStatisticsService.TB_SERVICE_QUEUE));
assets.sort(idComparator);
loadedAssets.sort(idComparator);

21
application/src/test/java/org/thingsboard/server/controller/BaseQueueControllerTest.java

@ -25,7 +25,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.test.context.TestPropertySource;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.TsKvEntry;
@ -34,12 +33,13 @@ import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.queue.ProcessingStrategy;
import org.thingsboard.server.common.data.queue.ProcessingStrategyType;
import org.thingsboard.server.common.data.queue.Queue;
import org.thingsboard.server.common.data.queue.QueueStats;
import org.thingsboard.server.common.data.queue.SubmitStrategy;
import org.thingsboard.server.common.data.queue.SubmitStrategyType;
import org.thingsboard.server.common.msg.queue.RuleEngineException;
import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.stats.StatsFactory;
import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.dao.queue.QueueStatsService;
import org.thingsboard.server.dao.service.DaoSqlTest;
import org.thingsboard.server.dao.timeseries.TimeseriesDao;
import org.thingsboard.server.gen.transport.TransportProtos;
@ -50,6 +50,7 @@ import org.thingsboard.server.service.queue.processing.TbRuleEngineProcessingRes
import org.thingsboard.server.service.stats.DefaultRuleEngineStatisticsService;
import org.thingsboard.server.service.stats.RuleEngineStatisticsService;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
@ -66,7 +67,6 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.dao.asset.BaseAssetService.TB_SERVICE_QUEUE;
@DaoSqlTest
@TestPropertySource(properties = {
@ -81,7 +81,7 @@ public class BaseQueueControllerTest extends AbstractControllerTest {
@SpyBean
private TimeseriesDao timeseriesDao;
@Autowired
private AssetService assetService;
private QueueStatsService queueStatsService;
@Test
public void testQueueWithServiceTypeRE() throws Exception {
@ -176,16 +176,17 @@ public class BaseQueueControllerTest extends AbstractControllerTest {
});
ruleEngineStatisticsService.reportQueueStats(System.currentTimeMillis(), testStats);
Asset serviceAsset = assetService.findAssetsByTenantIdAndType(tenantId, TB_SERVICE_QUEUE, new PageLink(100)).getData()
.stream().filter(asset -> asset.getName().startsWith(queue.getName()))
.findFirst().get();
List<QueueStats> queueStatsList = queueStatsService.findByTenantId(tenantId);
assertThat(queueStatsList).hasSize(1);
QueueStats queueStats = queueStatsList.get(0);
assertThat(queueStats.getQueueName()).isEqualTo(queue.getName());
ArgumentCaptor<Long> ttlCaptor = ArgumentCaptor.forClass(Long.class);
verify(timeseriesDao).save(eq(tenantId), eq(serviceAsset.getId()), argThat(tsKvEntry -> {
verify(timeseriesDao).save(eq(tenantId), eq(queueStats.getId()), argThat(tsKvEntry -> {
return tsKvEntry.getKey().equals(TbRuleEngineConsumerStats.SUCCESSFUL_MSGS) &&
tsKvEntry.getLongValue().get().equals(5L);
}), ttlCaptor.capture());
verify(timeseriesDao).save(eq(tenantId), eq(serviceAsset.getId()), argThat(tsKvEntry -> {
verify(timeseriesDao).save(eq(tenantId), eq(queueStats.getId()), argThat(tsKvEntry -> {
return tsKvEntry.getKey().equals(TbRuleEngineConsumerStats.FAILED_MSGS) &&
tsKvEntry.getLongValue().get().equals(5L);
}), ttlCaptor.capture());
@ -193,7 +194,7 @@ public class BaseQueueControllerTest extends AbstractControllerTest {
assertThat(usedTtl).isEqualTo(TimeUnit.DAYS.toSeconds(queueStatsTtlDays));
});
verify(timeseriesDao).save(eq(tenantId), eq(serviceAsset.getId()), argThat(tsKvEntry -> {
verify(timeseriesDao).save(eq(tenantId), eq(queueStats.getId()), argThat(tsKvEntry -> {
return tsKvEntry.getKey().equals(DefaultRuleEngineStatisticsService.RULE_ENGINE_EXCEPTION) &&
tsKvEntry.getJsonValue().get().equals(ruleEngineException.toJsonString(0));
}), ttlCaptor.capture());

38
application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java

@ -76,6 +76,7 @@ import org.thingsboard.server.gen.edge.v1.CustomerUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg;
import org.thingsboard.server.gen.edge.v1.EdgeVersion;
import org.thingsboard.server.gen.edge.v1.OAuth2UpdateMsg;
import org.thingsboard.server.gen.edge.v1.QueueUpdateMsg;
import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg;
import org.thingsboard.server.gen.edge.v1.SyncCompletedMsg;
@ -631,7 +632,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
List<Edge> loadedEdges = new ArrayList<>();
PageLink pageLink = new PageLink(23);
PageData<Edge> pageData = null;
PageData<Edge> pageData;
do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/edges?",
new TypeReference<>() {
@ -885,6 +886,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
EdgeImitator edgeImitator = new EdgeImitator(EDGE_HOST, EDGE_PORT, edge.getRoutingKey(), edge.getSecret());
edgeImitator.ignoreType(UserCredentialsUpdateMsg.class);
edgeImitator.ignoreType(OAuth2UpdateMsg.class);
edgeImitator.expectMessageAmount(24);
edgeImitator.connect();
@ -973,8 +975,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
private boolean popQueueMsg(List<AbstractMessage> messages, UpdateMsgType msgType, String name) {
for (AbstractMessage message : messages) {
if (message instanceof QueueUpdateMsg) {
QueueUpdateMsg queueUpdateMsg = (QueueUpdateMsg) message;
if (message instanceof QueueUpdateMsg queueUpdateMsg) {
Queue queue = JacksonUtil.fromString(queueUpdateMsg.getEntity(), Queue.class, true);
Assert.assertNotNull(queue);
if (msgType.equals(queueUpdateMsg.getMsgType()) && name.equals(queue.getName())) {
@ -988,8 +989,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
private boolean popRuleChainMsg(List<AbstractMessage> messages, UpdateMsgType msgType, String name) {
for (AbstractMessage message : messages) {
if (message instanceof RuleChainUpdateMsg) {
RuleChainUpdateMsg ruleChainUpdateMsg = (RuleChainUpdateMsg) message;
if (message instanceof RuleChainUpdateMsg ruleChainUpdateMsg) {
RuleChain ruleChain = JacksonUtil.fromString(ruleChainUpdateMsg.getEntity(), RuleChain.class, true);
Assert.assertNotNull(ruleChain);
if (msgType.equals(ruleChainUpdateMsg.getMsgType())
@ -1005,8 +1005,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
private boolean popAdminSettingsMsg(List<AbstractMessage> messages, String key) {
for (AbstractMessage message : messages) {
if (message instanceof AdminSettingsUpdateMsg) {
AdminSettingsUpdateMsg adminSettingsUpdateMsg = (AdminSettingsUpdateMsg) message;
if (message instanceof AdminSettingsUpdateMsg adminSettingsUpdateMsg) {
AdminSettings adminSettings = JacksonUtil.fromString(adminSettingsUpdateMsg.getEntity(), AdminSettings.class, true);
Assert.assertNotNull(adminSettings);
if (key.equals(adminSettings.getKey())) {
@ -1020,8 +1019,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
private boolean popDeviceProfileMsg(List<AbstractMessage> messages, UpdateMsgType msgType, String name) {
for (AbstractMessage message : messages) {
if (message instanceof DeviceProfileUpdateMsg) {
DeviceProfileUpdateMsg deviceProfileUpdateMsg = (DeviceProfileUpdateMsg) message;
if (message instanceof DeviceProfileUpdateMsg deviceProfileUpdateMsg) {
DeviceProfile deviceProfile = JacksonUtil.fromString(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class, true);
Assert.assertNotNull(deviceProfile);
if (msgType.equals(deviceProfileUpdateMsg.getMsgType())
@ -1036,8 +1034,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
private boolean popDeviceMsg(List<AbstractMessage> messages, UpdateMsgType msgType, String name) {
for (AbstractMessage message : messages) {
if (message instanceof DeviceUpdateMsg) {
DeviceUpdateMsg deviceUpdateMsg = (DeviceUpdateMsg) message;
if (message instanceof DeviceUpdateMsg deviceUpdateMsg) {
Device device = JacksonUtil.fromString(deviceUpdateMsg.getEntity(), Device.class, true);
Assert.assertNotNull(device);
if (msgType.equals(deviceUpdateMsg.getMsgType())
@ -1052,8 +1049,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
private boolean popAssetProfileMsg(List<AbstractMessage> messages, UpdateMsgType msgType, String name) {
for (AbstractMessage message : messages) {
if (message instanceof AssetProfileUpdateMsg) {
AssetProfileUpdateMsg assetProfileUpdateMsg = (AssetProfileUpdateMsg) message;
if (message instanceof AssetProfileUpdateMsg assetProfileUpdateMsg) {
AssetProfile assetProfile = JacksonUtil.fromString(assetProfileUpdateMsg.getEntity(), AssetProfile.class, true);
Assert.assertNotNull(assetProfile);
if (msgType.equals(assetProfileUpdateMsg.getMsgType())
@ -1068,8 +1064,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
private boolean popAssetMsg(List<AbstractMessage> messages, UpdateMsgType msgType, String name) {
for (AbstractMessage message : messages) {
if (message instanceof AssetUpdateMsg) {
AssetUpdateMsg assetUpdateMsg = (AssetUpdateMsg) message;
if (message instanceof AssetUpdateMsg assetUpdateMsg) {
Asset asset = JacksonUtil.fromString(assetUpdateMsg.getEntity(), Asset.class, true);
Assert.assertNotNull(asset);
if (msgType.equals(assetUpdateMsg.getMsgType())
@ -1084,8 +1079,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
private boolean popUserMsg(List<AbstractMessage> messages, UpdateMsgType msgType, String email, Authority authority) {
for (AbstractMessage message : messages) {
if (message instanceof UserUpdateMsg) {
UserUpdateMsg userUpdateMsg = (UserUpdateMsg) message;
if (message instanceof UserUpdateMsg userUpdateMsg) {
User user = JacksonUtil.fromString(userUpdateMsg.getEntity(), User.class, true);
Assert.assertNotNull(user);
if (msgType.equals(userUpdateMsg.getMsgType())
@ -1101,8 +1095,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
private boolean popCustomerMsg(List<AbstractMessage> messages, UpdateMsgType msgType, String title) {
for (AbstractMessage message : messages) {
if (message instanceof CustomerUpdateMsg) {
CustomerUpdateMsg customerUpdateMsg = (CustomerUpdateMsg) message;
if (message instanceof CustomerUpdateMsg customerUpdateMsg) {
Customer customer = JacksonUtil.fromString(customerUpdateMsg.getEntity(), Customer.class, true);
Assert.assertNotNull(customer);
if (msgType.equals(customerUpdateMsg.getMsgType())
@ -1117,8 +1110,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
private boolean popTenantMsg(List<AbstractMessage> messages, TenantId tenantId1) {
for (AbstractMessage message : messages) {
if (message instanceof TenantUpdateMsg) {
TenantUpdateMsg tenantUpdateMsg = (TenantUpdateMsg) message;
if (message instanceof TenantUpdateMsg tenantUpdateMsg) {
Tenant tenant = JacksonUtil.fromString(tenantUpdateMsg.getEntity(), Tenant.class, true);
Assert.assertNotNull(tenant);
if (UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE.equals(tenantUpdateMsg.getMsgType())
@ -1133,8 +1125,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
private boolean popTenantProfileMsg(List<AbstractMessage> messages, TenantProfileId tenantProfileId) {
for (AbstractMessage message : messages) {
if (message instanceof TenantProfileUpdateMsg) {
TenantProfileUpdateMsg tenantProfileUpdateMsg = (TenantProfileUpdateMsg) message;
if (message instanceof TenantProfileUpdateMsg tenantProfileUpdateMsg) {
TenantProfile tenantProfile = JacksonUtil.fromString(tenantProfileUpdateMsg.getEntity(), TenantProfile.class, true);
Assert.assertNotNull(tenantProfile);
if (UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE.equals(tenantProfileUpdateMsg.getMsgType())
@ -1236,4 +1227,5 @@ public class EdgeControllerTest extends AbstractControllerTest {
edgeUpgradeInstructionsService.setAppVersion("3.6.2.6");
Assert.assertTrue(edgeUpgradeInstructionsService.isUpgradeAvailable(savedEdge.getTenantId(), savedEdge.getId()));
}
}

51
application/src/test/java/org/thingsboard/server/controller/EntityQueryControllerTest.java

@ -22,11 +22,13 @@ import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.web.servlet.ResultActions;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.alarm.Alarm;
@ -51,10 +53,13 @@ import org.thingsboard.server.common.data.query.FilterPredicateValue;
import org.thingsboard.server.common.data.query.KeyFilter;
import org.thingsboard.server.common.data.query.NumericFilterPredicate;
import org.thingsboard.server.common.data.query.TsValue;
import org.thingsboard.server.common.data.queue.QueueStats;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.queue.QueueStatsService;
import org.thingsboard.server.dao.service.DaoSqlTest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
@ -69,6 +74,9 @@ public class EntityQueryControllerTest extends AbstractControllerTest {
private Tenant savedTenant;
private User tenantAdmin;
@Autowired
private QueueStatsService queueStatsService;
@Before
public void beforeTest() throws Exception {
loginSysAdmin();
@ -593,4 +601,47 @@ public class EntityQueryControllerTest extends AbstractControllerTest {
assertThat(getErrorMessage(result)).contains("Invalid").contains("sort property");
}
@Test
public void testFindQueueStatsEntitiesByQuery() throws Exception {
List<QueueStats> queueStatsList = new ArrayList<>();
for (int i = 0; i < 97; i++) {
QueueStats queueStats = new QueueStats();
queueStats.setQueueName(StringUtils.randomAlphabetic(5));
queueStats.setServiceId(StringUtils.randomAlphabetic(5));
queueStats.setTenantId(savedTenant.getTenantId());
queueStatsList.add(queueStatsService.save(savedTenant.getId(), queueStats));
Thread.sleep(1);
}
EntityTypeFilter entityTypeFilter = new EntityTypeFilter();
entityTypeFilter.setEntityType(EntityType.QUEUE_STATS);
EntityDataSortOrder sortOrder = new EntityDataSortOrder(
new EntityKey(EntityKeyType.ENTITY_FIELD, "queueName"), EntityDataSortOrder.Direction.ASC
);
EntityDataPageLink pageLink = new EntityDataPageLink(10, 0, null, sortOrder);
List<EntityKey> entityFields = Arrays.asList(new EntityKey(EntityKeyType.ENTITY_FIELD, "queueName"),
new EntityKey(EntityKeyType.ENTITY_FIELD, "serviceId"));
EntityDataQuery query = new EntityDataQuery(entityTypeFilter, pageLink, entityFields, null, null);
PageData<EntityData> data =
doPostWithTypedResponse("/api/entitiesQuery/find", query, new TypeReference<PageData<EntityData>>() {
});
Assert.assertEquals(97, data.getTotalElements());
Assert.assertEquals(10, data.getTotalPages());
Assert.assertTrue(data.hasNext());
Assert.assertEquals(10, data.getData().size());
data.getData().forEach(entityData -> {
assertThat(entityData.getLatest().get(EntityKeyType.ENTITY_FIELD).get("queueName")).asString().isNotBlank();
assertThat(entityData.getLatest().get(EntityKeyType.ENTITY_FIELD).get("serviceId")).asString().isNotBlank();
});
EntityCountQuery countQuery = new EntityCountQuery(entityTypeFilter);
Long count = doPostWithResponse("/api/entitiesQuery/count", countQuery, Long.class);
Assert.assertEquals(97, count.longValue());
}
}

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

@ -494,7 +494,7 @@ public class HomePageApiTest extends AbstractControllerTest {
}
private OAuth2Info createDefaultOAuth2Info() {
return new OAuth2Info(true, Lists.newArrayList(
return new OAuth2Info(true, false, Lists.newArrayList(
OAuth2ParamsInfo.builder()
.domainInfos(Lists.newArrayList(
OAuth2DomainInfo.builder().name("domain").scheme(SchemeType.MIXED).build()

3
application/src/test/java/org/thingsboard/server/controller/UserControllerTest.java

@ -1109,13 +1109,14 @@ public class UserControllerTest extends AbstractControllerTest {
private List<UserEmailInfo> getUsersInfo(PageLink pageLink) throws Exception {
List<UserEmailInfo> loadedCustomerUsers = new ArrayList<>();
PageData<UserEmailInfo> pageData = null;
PageData<UserEmailInfo> pageData;
do {
pageData = doGetTypedWithPageLink("/api/users/info?", new TypeReference<>() {
}, pageLink);
loadedCustomerUsers.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
Assert.assertEquals(pageLink.getPageSize(), pageData.getData().size());
}
} while (pageData.hasNext());
return loadedCustomerUsers;

15
application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java

@ -63,6 +63,7 @@ import org.thingsboard.server.common.data.id.DeviceProfileId;
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.common.data.oauth2.OAuth2Info;
import org.thingsboard.server.common.data.ota.ChecksumAlgorithm;
import org.thingsboard.server.common.data.ota.OtaPackageType;
import org.thingsboard.server.common.data.page.PageData;
@ -85,6 +86,7 @@ import org.thingsboard.server.gen.edge.v1.CustomerUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg;
import org.thingsboard.server.gen.edge.v1.EdgeConfiguration;
import org.thingsboard.server.gen.edge.v1.OAuth2UpdateMsg;
import org.thingsboard.server.gen.edge.v1.QueueUpdateMsg;
import org.thingsboard.server.gen.edge.v1.RuleChainMetadataRequestMsg;
import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg;
@ -140,6 +142,7 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest {
installation();
edgeImitator = new EdgeImitator("localhost", 7070, edge.getRoutingKey(), edge.getSecret());
edgeImitator.ignoreType(OAuth2UpdateMsg.class);
edgeImitator.expectMessageAmount(21);
edgeImitator.connect();
@ -538,6 +541,18 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest {
Assert.assertTrue(customer.isPublic());
}
private void validateOAuth2() throws Exception {
Optional<OAuth2UpdateMsg> oAuth2UpdateMsgOpt = edgeImitator.findMessageByType(OAuth2UpdateMsg.class);
Assert.assertTrue(oAuth2UpdateMsgOpt.isPresent());
OAuth2UpdateMsg oAuth2UpdateMsg = oAuth2UpdateMsgOpt.get();
OAuth2Info oAuth2Info = JacksonUtil.fromString(oAuth2UpdateMsg.getEntity(), OAuth2Info.class, true);
Assert.assertNotNull(oAuth2Info);
OAuth2Info auth2Info = doGet("/api/oauth2/config", OAuth2Info.class);
Assert.assertNotNull(auth2Info);
Assert.assertEquals(oAuth2Info, auth2Info);
testAutoGeneratedCodeByProtobuf(oAuth2UpdateMsg);
}
private void validateSyncCompleted() {
Optional<SyncCompletedMsg> syncCompletedMsgOpt = edgeImitator.findMessageByType(SyncCompletedMsg.class);
Assert.assertTrue(syncCompletedMsgOpt.isPresent());

113
application/src/test/java/org/thingsboard/server/edge/OAuth2EdgeTest.java

@ -0,0 +1,113 @@
/**
* 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.edge;
import com.google.common.collect.Lists;
import com.google.protobuf.AbstractMessage;
import org.junit.Assert;
import org.junit.Test;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.oauth2.MapperType;
import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig;
import org.thingsboard.server.common.data.oauth2.OAuth2DomainInfo;
import org.thingsboard.server.common.data.oauth2.OAuth2Info;
import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig;
import org.thingsboard.server.common.data.oauth2.OAuth2ParamsInfo;
import org.thingsboard.server.common.data.oauth2.OAuth2RegistrationInfo;
import org.thingsboard.server.common.data.oauth2.SchemeType;
import org.thingsboard.server.dao.service.DaoSqlTest;
import org.thingsboard.server.gen.edge.v1.OAuth2UpdateMsg;
import java.util.Arrays;
import java.util.Collections;
import java.util.UUID;
@DaoSqlTest
public class OAuth2EdgeTest extends AbstractEdgeTest {
@Test
public void testOAuth2Support() throws Exception {
loginSysAdmin();
// enable oauth
edgeImitator.allowIgnoredTypes();
edgeImitator.expectMessageAmount(1);
OAuth2Info oAuth2Info = createDefaultOAuth2Info();
oAuth2Info = doPost("/api/oauth2/config", oAuth2Info, OAuth2Info.class);
Assert.assertTrue(edgeImitator.waitForMessages());
AbstractMessage latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof OAuth2UpdateMsg);
OAuth2UpdateMsg oAuth2UpdateMsg = (OAuth2UpdateMsg) latestMessage;
OAuth2Info result = JacksonUtil.fromString(oAuth2UpdateMsg.getEntity(), OAuth2Info.class, true);
Assert.assertEquals(oAuth2Info, result);
// disable oauth support
edgeImitator.expectMessageAmount(1);
oAuth2Info.setEnabled(false);
oAuth2Info.setEdgeEnabled(false);
doPost("/api/oauth2/config", oAuth2Info, OAuth2Info.class);
Assert.assertTrue(edgeImitator.waitForMessages());
latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof OAuth2UpdateMsg);
oAuth2UpdateMsg = (OAuth2UpdateMsg) latestMessage;
result = JacksonUtil.fromString(oAuth2UpdateMsg.getEntity(), OAuth2Info.class, true);
Assert.assertEquals(oAuth2Info, result);
edgeImitator.ignoreType(OAuth2UpdateMsg.class);
loginTenantAdmin();
}
private OAuth2Info createDefaultOAuth2Info() {
return new OAuth2Info(true, true, Lists.newArrayList(
OAuth2ParamsInfo.builder()
.domainInfos(Lists.newArrayList(
OAuth2DomainInfo.builder().name("domain").scheme(SchemeType.MIXED).build()
))
.mobileInfos(Collections.emptyList())
.clientRegistrations(Lists.newArrayList(
validRegistrationInfo()
))
.build()
));
}
private OAuth2RegistrationInfo validRegistrationInfo() {
return OAuth2RegistrationInfo.builder()
.clientId(UUID.randomUUID().toString())
.clientSecret(UUID.randomUUID().toString())
.authorizationUri(UUID.randomUUID().toString())
.accessTokenUri(UUID.randomUUID().toString())
.scope(Arrays.asList(UUID.randomUUID().toString(), UUID.randomUUID().toString()))
.platforms(Collections.emptyList())
.userInfoUri(UUID.randomUUID().toString())
.userNameAttributeName(UUID.randomUUID().toString())
.jwkSetUri(UUID.randomUUID().toString())
.clientAuthenticationMethod(UUID.randomUUID().toString())
.loginButtonLabel(UUID.randomUUID().toString())
.mapperConfig(
OAuth2MapperConfig.builder()
.type(MapperType.CUSTOM)
.custom(
OAuth2CustomMapperConfig.builder()
.url(UUID.randomUUID().toString())
.build()
)
.build()
)
.build();
}
}

6
application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java

@ -44,6 +44,7 @@ import org.thingsboard.server.gen.edge.v1.DownlinkResponseMsg;
import org.thingsboard.server.gen.edge.v1.EdgeConfiguration;
import org.thingsboard.server.gen.edge.v1.EntityDataProto;
import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg;
import org.thingsboard.server.gen.edge.v1.OAuth2UpdateMsg;
import org.thingsboard.server.gen.edge.v1.OtaPackageUpdateMsg;
import org.thingsboard.server.gen.edge.v1.QueueUpdateMsg;
import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg;
@ -314,6 +315,11 @@ public class EdgeImitator {
result.add(saveDownlinkMsg(resourceUpdateMsg));
}
}
if (downlinkMsg.getOAuth2UpdateMsgCount() > 0) {
for (OAuth2UpdateMsg oAuth2UpdateMsg : downlinkMsg.getOAuth2UpdateMsgList()) {
result.add(saveDownlinkMsg(oAuth2UpdateMsg));
}
}
if (downlinkMsg.hasEdgeConfiguration()) {
result.add(saveDownlinkMsg(downlinkMsg.getEdgeConfiguration()));
}

25
application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessorTest.java

@ -46,6 +46,7 @@ import org.thingsboard.server.dao.edge.EdgeEventService;
import org.thingsboard.server.dao.edge.EdgeService;
import org.thingsboard.server.dao.edge.EdgeSynchronizationManager;
import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.oauth2.OAuth2Service;
import org.thingsboard.server.dao.ota.OtaPackageService;
import org.thingsboard.server.dao.queue.QueueService;
import org.thingsboard.server.dao.relation.RelationService;
@ -81,6 +82,7 @@ import org.thingsboard.server.service.edge.rpc.constructor.edge.EdgeMsgConstruct
import org.thingsboard.server.service.edge.rpc.constructor.entityview.EntityViewMsgConstructorFactory;
import org.thingsboard.server.service.edge.rpc.constructor.entityview.EntityViewMsgConstructorV1;
import org.thingsboard.server.service.edge.rpc.constructor.entityview.EntityViewMsgConstructorV2;
import org.thingsboard.server.service.edge.rpc.constructor.oauth2.OAuth2MsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.ota.OtaPackageMsgConstructorFactory;
import org.thingsboard.server.service.edge.rpc.constructor.ota.OtaPackageMsgConstructorV1;
import org.thingsboard.server.service.edge.rpc.constructor.ota.OtaPackageMsgConstructorV2;
@ -128,6 +130,7 @@ import org.thingsboard.server.service.edge.rpc.processor.device.profile.DevicePr
import org.thingsboard.server.service.edge.rpc.processor.entityview.EntityViewProcessorFactory;
import org.thingsboard.server.service.edge.rpc.processor.entityview.EntityViewProcessorV1;
import org.thingsboard.server.service.edge.rpc.processor.entityview.EntityViewProcessorV2;
import org.thingsboard.server.service.edge.rpc.processor.oauth2.OAuth2EdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.relation.RelationEdgeProcessorFactory;
import org.thingsboard.server.service.edge.rpc.processor.relation.RelationEdgeProcessorV1;
import org.thingsboard.server.service.edge.rpc.processor.relation.RelationEdgeProcessorV2;
@ -239,6 +242,9 @@ public abstract class BaseEdgeProcessorTest {
@MockBean
protected ResourceService resourceService;
@MockBean
protected OAuth2Service oAuth2Service;
@MockBean
@Lazy
protected TbQueueProducerProvider producerProvider;
@ -360,6 +366,9 @@ public abstract class BaseEdgeProcessorTest {
@MockBean
protected WidgetMsgConstructorV2 widgetMsgConstructorV2;
@MockBean
protected OAuth2MsgConstructor oAuth2MsgConstructor;
@MockBean
protected AlarmEdgeProcessorV1 alarmProcessorV1;
@ -417,6 +426,9 @@ public abstract class BaseEdgeProcessorTest {
@MockBean
protected RelationEdgeProcessorV2 relationEdgeProcessorV2;
@MockBean
protected OAuth2EdgeProcessor oAuth2EdgeProcessor;
@SpyBean
protected RuleChainMsgConstructorFactory ruleChainMsgConstructorFactory;
@ -514,17 +526,18 @@ public abstract class BaseEdgeProcessorTest {
}
protected static Stream<Arguments> provideParameters() {
UUID dashoboardUUID = UUID.randomUUID();
UUID ruleChaindUUID = UUID.randomUUID();
UUID dashboardUUID = UUID.randomUUID();
UUID ruleChainUUID = UUID.randomUUID();
return Stream.of(
Arguments.of(EdgeVersion.V_3_3_0, 0, 0, 0, 0),
Arguments.of(EdgeVersion.V_3_3_3, 0, 0, 0, 0),
Arguments.of(EdgeVersion.V_3_4_0, 0, 0, 0, 0),
Arguments.of(EdgeVersion.V_3_6_0,
dashoboardUUID.getMostSignificantBits(),
dashoboardUUID.getLeastSignificantBits(),
ruleChaindUUID.getMostSignificantBits(),
ruleChaindUUID.getLeastSignificantBits())
dashboardUUID.getMostSignificantBits(),
dashboardUUID.getLeastSignificantBits(),
ruleChainUUID.getMostSignificantBits(),
ruleChainUUID.getLeastSignificantBits())
);
}
}

40
application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessorTest.java

@ -16,27 +16,51 @@
package org.thingsboard.server.service.edge.rpc.processor.telemetry;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.Mockito;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessorTest;
@Slf4j
@RunWith(MockitoJUnitRunner.class)
public class TelemetryEdgeProcessorTest {
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {TelemetryEdgeProcessor.class})
@TestPropertySource(properties = {
"edges.rpc.max_telemetry_message_size=1000"
})
public class TelemetryEdgeProcessorTest extends BaseEdgeProcessorTest {
@SpyBean
private TelemetryEdgeProcessor telemetryEdgeProcessor;
@MockBean
private NotificationRuleProcessor notificationRuleProcessor;
@Test
public void testConvert_maxSizeLimit() throws Exception {
public void testConvert_maxSizeLimit() {
Edge edge = new Edge();
EdgeEvent edgeEvent = new EdgeEvent();
ObjectNode body = JacksonUtil.newObjectNode();
body.put("value", StringUtils.randomAlphanumeric(10000));
body.put("value", StringUtils.randomAlphanumeric(1000));
edgeEvent.setBody(body);
DownlinkMsg downlinkMsg = new TelemetryEdgeProcessor().convertTelemetryEventToDownlink(edgeEvent);
DownlinkMsg downlinkMsg = telemetryEdgeProcessor.convertTelemetryEventToDownlink(edge, edgeEvent);
Assert.assertNull(downlinkMsg);
verify(notificationRuleProcessor, Mockito.times(1)).process(any());
}
}

80
application/src/test/java/org/thingsboard/server/service/script/NashornJsInvokeServiceTest.java

@ -15,23 +15,29 @@
*/
package org.thingsboard.server.service.script;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.junit.Assert;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.test.context.TestPropertySource;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.common.util.TbStopWatch;
import org.thingsboard.script.api.ScriptType;
import org.thingsboard.script.api.js.NashornJsInvokeService;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.controller.AbstractControllerTest;
import org.thingsboard.server.dao.service.DaoSqlTest;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.thingsboard.server.common.data.msg.TbMsgType.POST_TELEMETRY_REQUEST;
@ -41,8 +47,9 @@ import static org.thingsboard.server.common.data.msg.TbMsgType.POST_TELEMETRY_RE
"js.max_script_body_size=50",
"js.max_total_args_size=50",
"js.max_result_size=50",
"js.local.max_errors=2"
"js.local.max_errors=2",
})
@Slf4j
class NashornJsInvokeServiceTest extends AbstractControllerTest {
@Autowired
@ -56,23 +63,64 @@ class NashornJsInvokeServiceTest extends AbstractControllerTest {
int iterations = 1000;
UUID scriptId = evalScript("return msg.temperature > 20");
// warmup
ObjectNode msg = JacksonUtil.newObjectNode();
for (int i = 0; i < 100; i++) {
msg.put("temperature", i);
log.info("Warming up 1000 times...");
var warmupWatch = TbStopWatch.create();
for (int i = 0; i < 1000; i++) {
boolean expected = i > 20;
boolean result = Boolean.valueOf(invokeScript(scriptId, JacksonUtil.toString(msg)));
boolean result = Boolean.parseBoolean(invokeScript(scriptId, "{\"temperature\":" + i + "}"));
Assert.assertEquals(expected, result);
}
long startTs = System.currentTimeMillis();
log.info("Warming up finished in {} ms", warmupWatch.stopAndGetTotalTimeMillis());
log.info("Starting performance test...");
var watch = TbStopWatch.create();
for (int i = 0; i < iterations; i++) {
msg.put("temperature", i);
boolean expected = i > 20;
boolean result = Boolean.valueOf(invokeScript(scriptId, JacksonUtil.toString(msg)));
boolean result = Boolean.parseBoolean(invokeScript(scriptId, "{\"temperature\":" + i + "}"));
log.debug("asserting result");
Assert.assertEquals(expected, result);
}
long duration = System.currentTimeMillis() - startTs;
System.out.println(iterations + " invocations took: " + duration + "ms");
Assert.assertTrue(duration < TimeUnit.MINUTES.toMillis(4));
long duration = watch.stopAndGetTotalTimeMillis();
log.info("Performance test with {} invocations took: {} ms", iterations, duration);
assertThat(duration).as("duration ms")
.isLessThan(TimeUnit.MINUTES.toMillis(1)); // effective exec time is about 500ms
}
@Test
void givenSimpleScriptMultiThreadTestPerformance() throws ExecutionException, InterruptedException, TimeoutException {
int iterations = 1000*4;
List<ListenableFuture<Object>> futures = new ArrayList<>(iterations);
UUID scriptId = evalScript("return msg.temperature > 20 ;");
// warmup
log.info("Warming up 1000 times...");
var warmupWatch = TbStopWatch.create();
for (int i = 0; i < 1000; i++) {
futures.add(invokeScriptAsync(scriptId, "{\"temperature\":" + i + "}"));
}
List<Object> results = Futures.allAsList(futures).get(1, TimeUnit.MINUTES);
for (int i = 0; i < 1000; i++) {
boolean expected = i > 20;
boolean result = Boolean.parseBoolean(results.get(i).toString());
Assert.assertEquals(expected, result);
}
log.info("Warming up finished in {} ms", warmupWatch.stopAndGetTotalTimeMillis());
futures.clear();
log.info("Starting performance test...");
var watch = TbStopWatch.create();
for (int i = 0; i < iterations; i++) {
futures.add(invokeScriptAsync(scriptId, "{\"temperature\":" + i + "}"));
}
results = Futures.allAsList(futures).get(1, TimeUnit.MINUTES);
for (int i = 0; i < iterations; i++) {
boolean expected = i > 20;
boolean result = Boolean.parseBoolean(results.get(i).toString());
Assert.assertEquals(expected, result);
}
long duration = watch.stopAndGetTotalTimeMillis();
log.info("Performance test with {} invocations took: {} ms", iterations, duration);
assertThat(duration).as("duration ms")
.isLessThan(TimeUnit.MINUTES.toMillis(1)); // effective exec time is about 500ms
}
@Test
@ -122,7 +170,11 @@ class NashornJsInvokeServiceTest extends AbstractControllerTest {
}
private String invokeScript(UUID scriptId, String msg) throws ExecutionException, InterruptedException {
return invokeService.invokeScript(TenantId.SYS_TENANT_ID, null, scriptId, msg, "{}", POST_TELEMETRY_REQUEST.name()).get().toString();
return invokeScriptAsync(scriptId, msg).get().toString();
}
private ListenableFuture<Object> invokeScriptAsync(UUID scriptId, String msg) {
return invokeService.invokeScript(TenantId.SYS_TENANT_ID, null, scriptId, msg, "{}", POST_TELEMETRY_REQUEST.name());
}
}

2
application/src/test/resources/logback-test.xml

@ -16,7 +16,7 @@
<logger name="org.testcontainers" level="INFO" />
<logger name="org.eclipse.leshan" level="INFO"/>
<logger name="org.thingsboard.server.controller.AbstractWebTest" level="INFO"/>
<logger name="org.thingsboard.server.service.script" level="INFO"/>
<!-- mute TelemetryEdgeSqlTest that causes a lot of randomly generated errors -->
<logger name="org.thingsboard.server.service.edge.rpc.EdgeGrpcSession" level="OFF"/>

2
common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java

@ -106,6 +106,8 @@ public interface AlarmService extends EntityDaoService {
PageData<AlarmId> findAlarmIdsByAssigneeId(TenantId tenantId, UserId userId, PageLink pageLink);
PageData<AlarmId> findAlarmIdsByOriginatorId(TenantId tenantId, EntityId originatorId, PageLink pageLink);
void deleteEntityAlarmRelations(TenantId tenantId, EntityId entityId);
void deleteEntityAlarmRecordsByTenantId(TenantId tenantId);

1
common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java

@ -24,6 +24,7 @@ import java.util.List;
import java.util.UUID;
public interface OAuth2Service {
List<OAuth2ClientInfo> getOAuth2Clients(String domainScheme, String domainName, String pkgName, PlatformType platformType);
void saveOAuth2Info(OAuth2Info oauth2Info);

37
common/dao-api/src/main/java/org/thingsboard/server/dao/queue/QueueStatsService.java

@ -0,0 +1,37 @@
/**
* 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.queue;
import org.thingsboard.server.common.data.id.QueueStatsId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.queue.QueueStats;
import org.thingsboard.server.dao.entity.EntityDaoService;
import java.util.List;
public interface QueueStatsService extends EntityDaoService {
QueueStats save(TenantId tenantId, QueueStats queueStats);
QueueStats findQueueStatsById(TenantId tenantId, QueueStatsId queueStatsId);
QueueStats findByTenantIdAndNameAndServiceId(TenantId tenantId, String queueName, String serviceId);
List<QueueStats> findByTenantId(TenantId tenantId);
void deleteByTenantId(TenantId tenantId);
}

6
common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java

@ -26,7 +26,6 @@ import java.util.stream.Collectors;
* @author Andrew Shvayka
*/
public enum EntityType {
TENANT(1),
CUSTOMER(2),
USER(3),
@ -59,12 +58,13 @@ public enum EntityType {
NOTIFICATION_TEMPLATE (30),
NOTIFICATION_REQUEST (31),
NOTIFICATION (32),
NOTIFICATION_RULE (33);
NOTIFICATION_RULE (33),
QUEUE_STATS(34);
@Getter
private final int protoNumber; // Corresponds to EntityTypeProto
EntityType(int protoNumber) {
private EntityType(int protoNumber) {
this.protoNumber = protoNumber;
}

3
common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEventType.java

@ -41,7 +41,8 @@ public enum EdgeEventType {
ADMIN_SETTINGS(true, null),
OTA_PACKAGE(true, EntityType.OTA_PACKAGE),
QUEUE(true, EntityType.QUEUE),
TB_RESOURCE(true, EntityType.TB_RESOURCE);
TB_RESOURCE(true, EntityType.TB_RESOURCE),
OAUTH2(true, null);
private final boolean allEdgesRelated;

2
common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java

@ -103,6 +103,8 @@ public class EntityIdFactory {
return new NotificationTemplateId(uuid);
case NOTIFICATION:
return new NotificationId(uuid);
case QUEUE_STATS:
return new QueueStatsId(uuid);
}
throw new IllegalArgumentException("EntityType " + type + " is not supported!");
}

43
common/data/src/main/java/org/thingsboard/server/common/data/id/QueueStatsId.java

@ -0,0 +1,43 @@
/**
* 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.id;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import org.thingsboard.server.common.data.EntityType;
import java.util.UUID;
public class QueueStatsId extends UUIDBased implements EntityId {
private static final long serialVersionUID = 1L;
@JsonCreator
public QueueStatsId(@JsonProperty("id") UUID id) {
super(id);
}
public static QueueStatsId fromString(String queueId) {
return new QueueStatsId(UUID.fromString(queueId));
}
@Schema(required = true, description = "string", example = "QUEUE_STATS", allowableValues = "QUEUE_STATS")
@Override
public EntityType getEntityType() {
return EntityType.QUEUE_STATS;
}
}

2
common/data/src/main/java/org/thingsboard/server/common/data/id/UUIDBased.java

@ -68,7 +68,7 @@ public abstract class UUIDBased implements HasUUID, Serializable {
@Override
public String toString() {
return id.toString();
return String.valueOf(id);
}
}

2
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Info.java

@ -35,6 +35,8 @@ import java.util.List;
public class OAuth2Info {
@Schema(description = "Whether OAuth2 settings are enabled or not")
private boolean enabled;
@Schema(description = "Whether OAuth2 settings are enabled on Edge or not")
private boolean edgeEnabled;
@Schema(description = "List of configured OAuth2 clients. Cannot contain null values", required = true)
private List<OAuth2ParamsInfo> oauth2ParamsInfos;
}

2
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Params.java

@ -30,11 +30,13 @@ import org.thingsboard.server.common.data.id.TenantId;
public class OAuth2Params extends BaseData<OAuth2ParamsId> {
private boolean enabled;
private boolean edgeEnabled;
private TenantId tenantId;
public OAuth2Params(OAuth2Params oauth2Params) {
super(oauth2Params);
this.enabled = oauth2Params.enabled;
this.edgeEnabled = oauth2Params.edgeEnabled;
this.tenantId = oauth2Params.tenantId;
}
}

26
common/data/src/main/java/org/thingsboard/server/common/data/page/PageLink.java

@ -19,6 +19,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import org.springframework.data.domain.Sort;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@ -65,19 +66,19 @@ public class PageLink {
return new PageLink(this.pageSize, this.page+1, this.textSearch, this.sortOrder);
}
public Sort toSort(SortOrder sortOrder, Map<String,String> columnMap) {
public Sort toSort(SortOrder sortOrder, Map<String,String> columnMap, boolean addDefaultSorting) {
if (sortOrder == null) {
return DEFAULT_SORT;
} else {
String property = sortOrder.getProperty();
if (columnMap.containsKey(property)) {
property = columnMap.get(property);
}
return Sort.by(Sort.Direction.fromString(sortOrder.getDirection().name()), property);
return toSort(List.of(sortOrder), columnMap, addDefaultSorting);
}
}
public Sort toSort(List<SortOrder> sortOrders, Map<String,String> columnMap) {
public Sort toSort(List<SortOrder> sortOrders, Map<String,String> columnMap, boolean addDefaultSorting) {
if (addDefaultSorting && !isDefaultSortOrderAvailable(sortOrders)) {
sortOrders = new ArrayList<>(sortOrders);
sortOrders.add(new SortOrder(DEFAULT_SORT_PROPERTY, SortOrder.Direction.ASC));
}
return Sort.by(sortOrders.stream().map(s -> toSortOrder(s, columnMap)).collect(Collectors.toList()));
}
@ -86,7 +87,16 @@ public class PageLink {
if (columnMap.containsKey(property)) {
property = columnMap.get(property);
}
return new Sort.Order(Sort.Direction.fromString(sortOrder.getDirection().name()), property, Sort.NullHandling.NULLS_LAST);
return new Sort.Order(Sort.Direction.fromString(sortOrder.getDirection().name()), property);
}
public boolean isDefaultSortOrderAvailable(List<SortOrder> sortOrders) {
for (SortOrder sortOrder : sortOrders) {
if (DEFAULT_SORT_PROPERTY.equals(sortOrder.getProperty())) {
return true;
}
}
return false;
}
}

30
common/data/src/main/java/org/thingsboard/server/common/data/page/TimePageLink.java

@ -19,11 +19,6 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.data.domain.Sort;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Data
@ToString(callSuper = true)
@ -67,29 +62,4 @@ public class TimePageLink extends PageLink {
this.startTime, this.endTime);
}
@Override
public Sort toSort(SortOrder sortOrder, Map<String,String> columnMap) {
if (sortOrder == null) {
return super.toSort(sortOrder, columnMap);
} else {
return toSort(new ArrayList<>(List.of(sortOrder)), columnMap);
}
}
@Override
public Sort toSort(List<SortOrder> sortOrders, Map<String,String> columnMap) {
if (!isDefaultSortOrderAvailable(sortOrders)) {
sortOrders.add(new SortOrder(DEFAULT_SORT_PROPERTY, SortOrder.Direction.ASC));
}
return super.toSort(sortOrders, columnMap);
}
private boolean isDefaultSortOrderAvailable(List<SortOrder> sortOrders) {
for (SortOrder sortOrder : sortOrders) {
if (DEFAULT_SORT_PROPERTY.equals(sortOrder.getProperty())) {
return true;
}
}
return false;
}
}

39
common/data/src/main/java/org/thingsboard/server/common/data/queue/QueueStats.java

@ -0,0 +1,39 @@
/**
* 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.queue;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.BaseData;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.QueueStatsId;
@EqualsAndHashCode(callSuper = true)
@Data
public class QueueStats extends BaseData<QueueStatsId> implements HasTenantId {
private TenantId tenantId;
private String queueName;
private String serviceId;
public QueueStats() {
}
public QueueStats(QueueStatsId id) {
super(id);
}
}

1
common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/request/load/EntityTypeVersionLoadRequest.java

@ -26,6 +26,7 @@ import java.util.Map;
public class EntityTypeVersionLoadRequest extends VersionLoadRequest {
private Map<EntityType, EntityTypeVersionLoadConfig> entityTypes;
private boolean rollbackOnError;
@Override
public VersionLoadRequestType getType() {

6
common/edge-api/src/main/proto/edge.proto

@ -37,6 +37,7 @@ enum EdgeVersion {
V_3_6_0 = 3;
V_3_6_1 = 4;
V_3_6_2 = 5;
V_3_7_0 = 6;
}
/**
@ -467,6 +468,10 @@ message ResourceUpdateMsg {
string entity = 11;
}
message OAuth2UpdateMsg {
string entity = 1;
}
message RuleChainMetadataRequestMsg {
int64 ruleChainIdMSB = 1;
int64 ruleChainIdLSB = 2;
@ -668,5 +673,6 @@ message DownlinkMsg {
repeated TenantProfileUpdateMsg tenantProfileUpdateMsg = 27;
repeated ResourceUpdateMsg resourceUpdateMsg = 28;
repeated AlarmCommentUpdateMsg alarmCommentUpdateMsg = 29;
repeated OAuth2UpdateMsg oAuth2UpdateMsg = 30;
}

150
common/proto/src/main/java/org/thingsboard/server/common/util/KvProtoUtil.java

@ -29,11 +29,26 @@ import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.gen.transport.TransportProtos;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class KvProtoUtil {
private static final DataType[] dataTypeByProtoNumber;
static {
int arraySize = Arrays.stream(DataType.values()).mapToInt(DataType::getProtoNumber).max().orElse(0);
dataTypeByProtoNumber = new DataType[arraySize + 1];
Arrays.stream(DataType.values()).forEach(dataType -> dataTypeByProtoNumber[dataType.getProtoNumber()] = dataType);
}
public static List<AttributeKvEntry> toAttributeKvList(List<TransportProtos.TsKvProto> dataList) {
List<AttributeKvEntry> result = new ArrayList<>(dataList.size());
dataList.forEach(proto -> result.add(new BaseAttributeKvEntry(fromTsKvProto(proto.getKv()), proto.getTs())));
return result;
}
public static List<TransportProtos.TsKvProto> attrToTsKvProtos(List<AttributeKvEntry> result) {
List<TransportProtos.TsKvProto> clientAttributes;
if (result == null || result.isEmpty()) {
@ -47,8 +62,7 @@ public class KvProtoUtil {
return clientAttributes;
}
public static List<TransportProtos.TsKvProto> tsToTsKvProtos(List<TsKvEntry> result) {
public static List<TransportProtos.TsKvProto> toTsKvProtoList(List<TsKvEntry> result) {
List<TransportProtos.TsKvProto> ts;
if (result == null || result.isEmpty()) {
ts = Collections.emptyList();
@ -61,95 +75,85 @@ public class KvProtoUtil {
return ts;
}
public static List<TsKvEntry> fromTsKvProtoList(List<TransportProtos.TsKvProto> dataList) {
List<TsKvEntry> result = new ArrayList<>(dataList.size());
dataList.forEach(proto -> result.add(new BasicTsKvEntry(proto.getTs(), fromTsKvProto(proto.getKv()))));
return result;
}
public static TransportProtos.TsKvProto toTsKvProto(long ts, KvEntry kvEntry) {
return TransportProtos.TsKvProto.newBuilder().setTs(ts)
.setKv(KvProtoUtil.toKeyValueProto(kvEntry)).build();
.setKv(KvProtoUtil.toKeyValueTypeProto(kvEntry)).build();
}
public static TsKvEntry fromTsKvProto(TransportProtos.TsKvProto proto) {
return new BasicTsKvEntry(proto.getTs(), fromTsKvProto(proto.getKv()));
}
public static TransportProtos.KeyValueProto toKeyValueProto(KvEntry kvEntry) {
public static TransportProtos.KeyValueProto toKeyValueTypeProto(KvEntry kvEntry) {
TransportProtos.KeyValueProto.Builder builder = TransportProtos.KeyValueProto.newBuilder();
builder.setKey(kvEntry.getKey());
builder.setType(toKeyValueTypeProto(kvEntry.getDataType()));
switch (kvEntry.getDataType()) {
case BOOLEAN:
builder.setType(TransportProtos.KeyValueType.BOOLEAN_V);
builder.setBoolV(kvEntry.getBooleanValue().get());
break;
case DOUBLE:
builder.setType(TransportProtos.KeyValueType.DOUBLE_V);
builder.setDoubleV(kvEntry.getDoubleValue().get());
break;
case LONG:
builder.setType(TransportProtos.KeyValueType.LONG_V);
builder.setLongV(kvEntry.getLongValue().get());
break;
case STRING:
builder.setType(TransportProtos.KeyValueType.STRING_V);
builder.setStringV(kvEntry.getStrValue().get());
break;
case JSON:
builder.setType(TransportProtos.KeyValueType.JSON_V);
builder.setJsonV(kvEntry.getJsonValue().get());
break;
case BOOLEAN -> kvEntry.getBooleanValue().ifPresent(builder::setBoolV);
case LONG -> kvEntry.getLongValue().ifPresent(builder::setLongV);
case DOUBLE -> kvEntry.getDoubleValue().ifPresent(builder::setDoubleV);
case JSON -> kvEntry.getJsonValue().ifPresent(builder::setJsonV);
case STRING -> kvEntry.getStrValue().ifPresent(builder::setStringV);
}
return builder.build();
}
public static TransportProtos.TsKvProto.Builder toKeyValueProto(long ts, KvEntry attr) {
TransportProtos.KeyValueProto.Builder dataBuilder = TransportProtos.KeyValueProto.newBuilder();
dataBuilder.setKey(attr.getKey());
dataBuilder.setType(TransportProtos.KeyValueType.forNumber(attr.getDataType().ordinal()));
switch (attr.getDataType()) {
case BOOLEAN:
attr.getBooleanValue().ifPresent(dataBuilder::setBoolV);
break;
case LONG:
attr.getLongValue().ifPresent(dataBuilder::setLongV);
break;
case DOUBLE:
attr.getDoubleValue().ifPresent(dataBuilder::setDoubleV);
break;
case JSON:
attr.getJsonValue().ifPresent(dataBuilder::setJsonV);
break;
case STRING:
attr.getStrValue().ifPresent(dataBuilder::setStringV);
break;
}
return TransportProtos.TsKvProto.newBuilder().setTs(ts).setKv(dataBuilder);
public static KvEntry fromTsKvProto(TransportProtos.KeyValueProto proto) {
return switch (fromKeyValueTypeProto(proto.getType())) {
case BOOLEAN -> new BooleanDataEntry(proto.getKey(), proto.getBoolV());
case LONG -> new LongDataEntry(proto.getKey(), proto.getLongV());
case DOUBLE -> new DoubleDataEntry(proto.getKey(), proto.getDoubleV());
case STRING -> new StringDataEntry(proto.getKey(), proto.getStringV());
case JSON -> new JsonDataEntry(proto.getKey(), proto.getJsonV());
};
}
public static List<TsKvEntry> toTsKvEntityList(List<TransportProtos.TsKvProto> dataList) {
List<TsKvEntry> result = new ArrayList<>(dataList.size());
dataList.forEach(proto -> result.add(new BasicTsKvEntry(proto.getTs(), getKvEntry(proto.getKv()))));
return result;
public static TransportProtos.TsKvProto.Builder toTsKvProtoBuilder(long ts, KvEntry kvEntry) {
return TransportProtos.TsKvProto.newBuilder().setTs(ts).setKv(KvProtoUtil.toKeyValueTypeProto(kvEntry));
}
public static List<AttributeKvEntry> toAttributeKvList(List<TransportProtos.TsKvProto> dataList) {
List<AttributeKvEntry> result = new ArrayList<>(dataList.size());
dataList.forEach(proto -> result.add(new BaseAttributeKvEntry(getKvEntry(proto.getKv()), proto.getTs())));
public static List<TsKvEntry> fromTsValueProtoList(String key, List<TransportProtos.TsValueProto> dataList) {
List<TsKvEntry> result = new ArrayList<>(dataList.size());
dataList.forEach(proto -> result.add(new BasicTsKvEntry(proto.getTs(), fromTsValueProto(key, proto))));
return result;
}
private static KvEntry getKvEntry(TransportProtos.KeyValueProto proto) {
KvEntry entry = null;
DataType type = DataType.values()[proto.getType().getNumber()];
switch (type) {
case BOOLEAN:
entry = new BooleanDataEntry(proto.getKey(), proto.getBoolV());
break;
case LONG:
entry = new LongDataEntry(proto.getKey(), proto.getLongV());
break;
case DOUBLE:
entry = new DoubleDataEntry(proto.getKey(), proto.getDoubleV());
break;
case STRING:
entry = new StringDataEntry(proto.getKey(), proto.getStringV());
break;
case JSON:
entry = new JsonDataEntry(proto.getKey(), proto.getJsonV());
break;
public static TransportProtos.TsValueProto toTsValueProto(long ts, KvEntry attr) {
TransportProtos.TsValueProto.Builder dataBuilder = TransportProtos.TsValueProto.newBuilder();
dataBuilder.setTs(ts);
dataBuilder.setType(toKeyValueTypeProto(attr.getDataType()));
switch (attr.getDataType()) {
case BOOLEAN -> attr.getBooleanValue().ifPresent(dataBuilder::setBoolV);
case LONG -> attr.getLongValue().ifPresent(dataBuilder::setLongV);
case DOUBLE -> attr.getDoubleValue().ifPresent(dataBuilder::setDoubleV);
case JSON -> attr.getJsonValue().ifPresent(dataBuilder::setJsonV);
case STRING -> attr.getStrValue().ifPresent(dataBuilder::setStringV);
}
return entry;
return dataBuilder.build();
}
public static KvEntry fromTsValueProto(String key, TransportProtos.TsValueProto proto) {
return switch (fromKeyValueTypeProto(proto.getType())) {
case BOOLEAN -> new BooleanDataEntry(key, proto.getBoolV());
case LONG -> new LongDataEntry(key, proto.getLongV());
case DOUBLE -> new DoubleDataEntry(key, proto.getDoubleV());
case STRING -> new StringDataEntry(key, proto.getStringV());
case JSON -> new JsonDataEntry(key, proto.getJsonV());
};
}
public static TransportProtos.KeyValueType toKeyValueTypeProto(DataType dataType) {
return TransportProtos.KeyValueType.forNumber(dataType.getProtoNumber());
}
public static DataType fromKeyValueTypeProto(TransportProtos.KeyValueType keyValueType) {
return dataTypeByProtoNumber[keyValueType.getNumber()];
}
}

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

@ -434,36 +434,28 @@ public class ProtoUtils {
}
public static TransportProtos.ToDeviceActorNotificationMsgProto toProto(ToDeviceActorNotificationMsg msg) {
if (msg instanceof DeviceEdgeUpdateMsg) {
DeviceEdgeUpdateMsg updateMsg = (DeviceEdgeUpdateMsg) msg;
if (msg instanceof DeviceEdgeUpdateMsg updateMsg) {
TransportProtos.DeviceEdgeUpdateMsgProto proto = toProto(updateMsg);
return TransportProtos.ToDeviceActorNotificationMsgProto.newBuilder().setDeviceEdgeUpdateMsg(proto).build();
} else if (msg instanceof DeviceNameOrTypeUpdateMsg) {
DeviceNameOrTypeUpdateMsg updateMsg = (DeviceNameOrTypeUpdateMsg) msg;
} else if (msg instanceof DeviceNameOrTypeUpdateMsg updateMsg) {
TransportProtos.DeviceNameOrTypeUpdateMsgProto proto = toProto(updateMsg);
return TransportProtos.ToDeviceActorNotificationMsgProto.newBuilder().setDeviceNameOrTypeMsg(proto).build();
} else if (msg instanceof DeviceAttributesEventNotificationMsg) {
DeviceAttributesEventNotificationMsg updateMsg = (DeviceAttributesEventNotificationMsg) msg;
} else if (msg instanceof DeviceAttributesEventNotificationMsg updateMsg) {
TransportProtos.DeviceAttributesEventMsgProto proto = toProto(updateMsg);
return TransportProtos.ToDeviceActorNotificationMsgProto.newBuilder().setDeviceAttributesEventMsg(proto).build();
} else if (msg instanceof DeviceCredentialsUpdateNotificationMsg) {
DeviceCredentialsUpdateNotificationMsg updateMsg = (DeviceCredentialsUpdateNotificationMsg) msg;
} else if (msg instanceof DeviceCredentialsUpdateNotificationMsg updateMsg) {
TransportProtos.DeviceCredentialsUpdateMsgProto proto = toProto(updateMsg);
return TransportProtos.ToDeviceActorNotificationMsgProto.newBuilder().setDeviceCredentialsUpdateMsg(proto).build();
} else if (msg instanceof ToDeviceRpcRequestActorMsg) {
ToDeviceRpcRequestActorMsg updateMsg = (ToDeviceRpcRequestActorMsg) msg;
} else if (msg instanceof ToDeviceRpcRequestActorMsg updateMsg) {
TransportProtos.ToDeviceRpcRequestActorMsgProto proto = toProto(updateMsg);
return TransportProtos.ToDeviceActorNotificationMsgProto.newBuilder().setToDeviceRpcRequestMsg(proto).build();
} else if (msg instanceof FromDeviceRpcResponseActorMsg) {
FromDeviceRpcResponseActorMsg updateMsg = (FromDeviceRpcResponseActorMsg) msg;
} else if (msg instanceof FromDeviceRpcResponseActorMsg updateMsg) {
TransportProtos.FromDeviceRpcResponseActorMsgProto proto = toProto(updateMsg);
return TransportProtos.ToDeviceActorNotificationMsgProto.newBuilder().setFromDeviceRpcResponseMsg(proto).build();
} else if (msg instanceof RemoveRpcActorMsg) {
RemoveRpcActorMsg updateMsg = (RemoveRpcActorMsg) msg;
} else if (msg instanceof RemoveRpcActorMsg updateMsg) {
TransportProtos.RemoveRpcActorMsgProto proto = toProto(updateMsg);
return TransportProtos.ToDeviceActorNotificationMsgProto.newBuilder().setRemoveRpcActorMsg(proto).build();
} else if (msg instanceof DeviceDeleteMsg) {
DeviceDeleteMsg updateMsg = (DeviceDeleteMsg) msg;
} else if (msg instanceof DeviceDeleteMsg updateMsg) {
TransportProtos.DeviceDeleteMsgProto proto = toProto(updateMsg);
return TransportProtos.ToDeviceActorNotificationMsgProto.newBuilder().setDeviceDeleteMsg(proto).build();
}
@ -507,24 +499,14 @@ public class ProtoUtils {
List<AttributeKvEntry> result = new ArrayList<>();
for (TransportProtos.AttributeValueProto kvEntry : valuesList) {
boolean hasValue = kvEntry.getHasV();
KvEntry entry = null;
switch (kvEntry.getType()) {
case BOOLEAN_V:
entry = new BooleanDataEntry(kvEntry.getKey(), hasValue ? kvEntry.getBoolV() : null);
break;
case LONG_V:
entry = new LongDataEntry(kvEntry.getKey(), hasValue ? kvEntry.getLongV() : null);
break;
case DOUBLE_V:
entry = new DoubleDataEntry(kvEntry.getKey(), hasValue ? kvEntry.getDoubleV() : null);
break;
case STRING_V:
entry = new StringDataEntry(kvEntry.getKey(), hasValue ? kvEntry.getStringV() : null);
break;
case JSON_V:
entry = new JsonDataEntry(kvEntry.getKey(), hasValue ? kvEntry.getJsonV() : null);
break;
}
KvEntry entry = switch (kvEntry.getType()) {
case BOOLEAN_V -> new BooleanDataEntry(kvEntry.getKey(), hasValue ? kvEntry.getBoolV() : null);
case LONG_V -> new LongDataEntry(kvEntry.getKey(), hasValue ? kvEntry.getLongV() : null);
case DOUBLE_V -> new DoubleDataEntry(kvEntry.getKey(), hasValue ? kvEntry.getDoubleV() : null);
case STRING_V -> new StringDataEntry(kvEntry.getKey(), hasValue ? kvEntry.getStringV() : null);
case JSON_V -> new JsonDataEntry(kvEntry.getKey(), hasValue ? kvEntry.getJsonV() : null);
default -> null;
};
result.add(new BaseAttributeKvEntry(kvEntry.getLastUpdateTs(), entry));
}
return result;
@ -1029,15 +1011,11 @@ public class ProtoUtils {
.setDeviceProfileIdLSB(device.getDeviceProfileId().getId().getLeastSignificantBits())
.setAdditionalInfo(JacksonUtil.toString(device.getAdditionalInfo()));
PowerSavingConfiguration psmConfiguration = null;
switch (device.getDeviceData().getTransportConfiguration().getType()) {
case LWM2M:
psmConfiguration = (Lwm2mDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration();
break;
case COAP:
psmConfiguration = (CoapDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration();
break;
}
PowerSavingConfiguration psmConfiguration = switch (device.getDeviceData().getTransportConfiguration().getType()) {
case LWM2M -> (Lwm2mDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration();
case COAP -> (CoapDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration();
default -> null;
};
if (psmConfiguration != null) {
PowerMode powerMode = psmConfiguration.getPowerMode();
@ -1079,4 +1057,5 @@ public class ProtoUtils {
private static Long checkLong(Long l) {
return isNotNull(l) ? l : 0;
}
}

1
common/proto/src/main/proto/queue.proto

@ -54,6 +54,7 @@ enum EntityTypeProto {
NOTIFICATION_REQUEST = 31;
NOTIFICATION = 32;
NOTIFICATION_RULE = 33;
QUEUE_STATS = 34;
}
/**

118
common/proto/src/test/java/org/thingsboard/server/common/util/KvProtoUtilTest.java

@ -0,0 +1,118 @@
/**
* 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.util;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.thingsboard.server.common.data.kv.AggTsKvEntry;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
import org.thingsboard.server.common.data.kv.BooleanDataEntry;
import org.thingsboard.server.common.data.kv.DataType;
import org.thingsboard.server.common.data.kv.DoubleDataEntry;
import org.thingsboard.server.common.data.kv.JsonDataEntry;
import org.thingsboard.server.common.data.kv.KvEntry;
import org.thingsboard.server.common.data.kv.LongDataEntry;
import org.thingsboard.server.common.data.kv.StringDataEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.assertj.core.api.Assertions.assertThat;
class KvProtoUtilTest {
private static final long TS = System.currentTimeMillis();
private static Stream<KvEntry> kvEntryData() {
String key = "key";
return Stream.of(
new BooleanDataEntry(key, true),
new LongDataEntry(key, 23L),
new DoubleDataEntry(key, 23.0),
new StringDataEntry(key, "stringValue"),
new JsonDataEntry(key, "jsonValue")
);
}
private static Stream<KvEntry> basicTsKvEntryData() {
return kvEntryData().map(kvEntry -> new BasicTsKvEntry(TS, kvEntry));
}
private static Stream<List<BaseAttributeKvEntry>> attributeKvEntryData() {
return Stream.of(kvEntryData().map(kvEntry -> new BaseAttributeKvEntry(TS, kvEntry)).toList());
}
private static List<TsKvEntry> createTsKvEntryList(boolean withAggregation) {
return kvEntryData().map(kvEntry -> {
if (withAggregation) {
return new AggTsKvEntry(TS, kvEntry, 0);
} else {
return new BasicTsKvEntry(TS, kvEntry);
}
}).collect(Collectors.toList());
}
@ParameterizedTest
@EnumSource(DataType.class)
void protoDataTypeSerialization(DataType dataType) {
assertThat(KvProtoUtil.fromKeyValueTypeProto(KvProtoUtil.toKeyValueTypeProto(dataType)))
.as(dataType.name()).isEqualTo(dataType);
}
@ParameterizedTest
@MethodSource("kvEntryData")
void protoKeyValueProtoSerialization(KvEntry kvEntry) {
assertThat(KvProtoUtil.fromTsKvProto(KvProtoUtil.toKeyValueTypeProto(kvEntry)))
.as("deserialized").isEqualTo(kvEntry);
}
@ParameterizedTest
@MethodSource("basicTsKvEntryData")
void protoTsKvEntrySerialization(KvEntry kvEntry) {
assertThat(KvProtoUtil.fromTsKvProto(KvProtoUtil.toTsKvProto(TS, kvEntry)))
.as("deserialized").isEqualTo(kvEntry);
}
@ParameterizedTest
@MethodSource("kvEntryData")
void protoTsValueSerialization(KvEntry kvEntry) {
assertThat(KvProtoUtil.fromTsValueProto(kvEntry.getKey(), KvProtoUtil.toTsValueProto(TS, kvEntry)))
.as("deserialized").isEqualTo(kvEntry);
}
@ParameterizedTest
@ValueSource(booleans = {true, false})
void protoListTsKvEntrySerialization(boolean withAggregation) {
List<TsKvEntry> tsKvEntries = createTsKvEntryList(withAggregation);
assertThat(KvProtoUtil.fromTsKvProtoList(KvProtoUtil.toTsKvProtoList(tsKvEntries)))
.as("deserialized").isEqualTo(tsKvEntries);
}
@ParameterizedTest
@MethodSource("attributeKvEntryData")
void protoListAttributeKvSerialization(List<AttributeKvEntry> attributeKvEntries) {
assertThat(KvProtoUtil.toAttributeKvList(KvProtoUtil.attrToTsKvProtos(attributeKvEntries)))
.as("deserialized")
.isEqualTo(attributeKvEntries);
}
}

30
common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java

@ -109,40 +109,40 @@ public class PubSubMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng
@Override
public TbQueueProducer<TbProtoQueueMsg<ToTransportMsg>> createTransportNotificationsMsgProducer() {
return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, transportNotificationSettings.getNotificationsTopic());
return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic()));
}
@Override
public TbQueueProducer<TbProtoQueueMsg<ToRuleEngineMsg>> createRuleEngineMsgProducer() {
return new TbPubSubProducerTemplate<>(ruleEngineAdmin, pubSubSettings, ruleEngineSettings.getTopic());
return new TbPubSubProducerTemplate<>(ruleEngineAdmin, pubSubSettings, topicService.buildTopicName(ruleEngineSettings.getTopic()));
}
@Override
public TbQueueProducer<TbProtoQueueMsg<ToRuleEngineNotificationMsg>> createRuleEngineNotificationsMsgProducer() {
return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, ruleEngineSettings.getTopic());
return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(ruleEngineSettings.getTopic()));
}
@Override
public TbQueueProducer<TbProtoQueueMsg<ToCoreMsg>> createTbCoreMsgProducer() {
return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getTopic());
return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic()));
}
@Override
public TbQueueProducer<TbProtoQueueMsg<ToCoreNotificationMsg>> createTbCoreNotificationsMsgProducer() {
return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, coreSettings.getTopic());
return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic()));
}
@Override
public TbQueueConsumer<TbProtoQueueMsg<TransportProtos.ToVersionControlServiceMsg>> createToVersionControlMsgConsumer() {
return new TbPubSubConsumerTemplate<>(vcAdmin, pubSubSettings, vcSettings.getTopic(),
return new TbPubSubConsumerTemplate<>(vcAdmin, pubSubSettings, topicService.buildTopicName(vcSettings.getTopic()),
msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.ToVersionControlServiceMsg.parseFrom(msg.getData()), msg.getHeaders())
);
}
@Override
public TbQueueConsumer<TbProtoQueueMsg<ToRuleEngineMsg>> createToRuleEngineMsgConsumer(Queue configuration) {
return new TbPubSubConsumerTemplate<>(ruleEngineAdmin, pubSubSettings, configuration.getTopic(),
return new TbPubSubConsumerTemplate<>(ruleEngineAdmin, pubSubSettings, topicService.buildTopicName(configuration.getTopic()),
msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineMsg.parseFrom(msg.getData()), msg.getHeaders()));
}
@ -155,7 +155,7 @@ public class PubSubMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng
@Override
public TbQueueConsumer<TbProtoQueueMsg<ToCoreMsg>> createToCoreMsgConsumer() {
return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getTopic(),
return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic()),
msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreMsg.parseFrom(msg.getData()), msg.getHeaders()));
}
@ -168,13 +168,13 @@ public class PubSubMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng
@Override
public TbQueueConsumer<TbProtoQueueMsg<TransportApiRequestMsg>> createTransportApiRequestConsumer() {
return new TbPubSubConsumerTemplate<>(transportApiAdmin, pubSubSettings, transportApiSettings.getRequestsTopic(),
return new TbPubSubConsumerTemplate<>(transportApiAdmin, pubSubSettings, topicService.buildTopicName(transportApiSettings.getRequestsTopic()),
msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders()));
}
@Override
public TbQueueProducer<TbProtoQueueMsg<TransportApiResponseMsg>> createTransportApiResponseProducer() {
return new TbPubSubProducerTemplate<>(transportApiAdmin, pubSubSettings, transportApiSettings.getResponsesTopic());
return new TbPubSubProducerTemplate<>(transportApiAdmin, pubSubSettings, topicService.buildTopicName(transportApiSettings.getResponsesTopic()));
}
@Override
@ -202,29 +202,29 @@ public class PubSubMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng
@Override
public TbQueueConsumer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgConsumer() {
return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getUsageStatsTopic(),
return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic()),
msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
}
@Override
public TbQueueConsumer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgConsumer() {
return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getOtaPackageTopic(),
return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic()),
msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
}
@Override
public TbQueueProducer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgProducer() {
return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getOtaPackageTopic());
return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic()));
}
@Override
public TbQueueProducer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() {
return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getUsageStatsTopic());
return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic()));
}
@Override
public TbQueueProducer<TbProtoQueueMsg<TransportProtos.ToVersionControlServiceMsg>> createVersionControlMsgProducer() {
return new TbPubSubProducerTemplate<>(vcAdmin, pubSubSettings, vcSettings.getTopic());
return new TbPubSubProducerTemplate<>(vcAdmin, pubSubSettings, topicService.buildTopicName(vcSettings.getTopic()));
}
@PreDestroy

24
common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java

@ -100,32 +100,32 @@ public class PubSubTbCoreQueueFactory implements TbCoreQueueFactory {
@Override
public TbQueueProducer<TbProtoQueueMsg<ToTransportMsg>> createTransportNotificationsMsgProducer() {
return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, transportNotificationSettings.getNotificationsTopic());
return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic()));
}
@Override
public TbQueueProducer<TbProtoQueueMsg<ToRuleEngineMsg>> createRuleEngineMsgProducer() {
return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getTopic());
return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic()));
}
@Override
public TbQueueProducer<TbProtoQueueMsg<ToRuleEngineNotificationMsg>> createRuleEngineNotificationsMsgProducer() {
return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, ruleEngineSettings.getTopic());
return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(ruleEngineSettings.getTopic()));
}
@Override
public TbQueueProducer<TbProtoQueueMsg<ToCoreMsg>> createTbCoreMsgProducer() {
return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getTopic());
return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic()));
}
@Override
public TbQueueProducer<TbProtoQueueMsg<ToCoreNotificationMsg>> createTbCoreNotificationsMsgProducer() {
return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, coreSettings.getTopic());
return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic()));
}
@Override
public TbQueueConsumer<TbProtoQueueMsg<ToCoreMsg>> createToCoreMsgConsumer() {
return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getTopic(),
return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic()),
msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreMsg.parseFrom(msg.getData()), msg.getHeaders()));
}
@ -138,13 +138,13 @@ public class PubSubTbCoreQueueFactory implements TbCoreQueueFactory {
@Override
public TbQueueConsumer<TbProtoQueueMsg<TransportApiRequestMsg>> createTransportApiRequestConsumer() {
return new TbPubSubConsumerTemplate<>(transportApiAdmin, pubSubSettings, transportApiSettings.getRequestsTopic(),
return new TbPubSubConsumerTemplate<>(transportApiAdmin, pubSubSettings, topicService.buildTopicName(transportApiSettings.getRequestsTopic()),
msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders()));
}
@Override
public TbQueueProducer<TbProtoQueueMsg<TransportApiResponseMsg>> createTransportApiResponseProducer() {
return new TbPubSubProducerTemplate<>(transportApiAdmin, pubSubSettings, transportApiSettings.getResponsesTopic());
return new TbPubSubProducerTemplate<>(transportApiAdmin, pubSubSettings, topicService.buildTopicName(transportApiSettings.getResponsesTopic()));
}
@Override
@ -172,24 +172,24 @@ public class PubSubTbCoreQueueFactory implements TbCoreQueueFactory {
@Override
public TbQueueConsumer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgConsumer() {
return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getUsageStatsTopic(),
return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic()),
msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
}
@Override
public TbQueueConsumer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgConsumer() {
return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getOtaPackageTopic(),
return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic()),
msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
}
@Override
public TbQueueProducer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgProducer() {
return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getOtaPackageTopic());
return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic()));
}
@Override
public TbQueueProducer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() {
return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getUsageStatsTopic());
return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic()));
}
@Override

4
common/script/script-api/src/main/java/org/thingsboard/script/api/AbstractScriptInvokeService.java

@ -145,14 +145,14 @@ public abstract class AbstractScriptInvokeService implements ScriptInvokeService
log.trace("[{}] InvokeScript uuid {} with timeout {}ms", tenantId, scriptId, getMaxInvokeRequestsTimeout());
var task = doInvokeFunction(scriptId, args);
var resultFuture = Futures.transformAsync(task.getResultFuture(), output -> {
var resultFuture = Futures.transform(task.getResultFuture(), output -> {
String result = JacksonUtil.toString(output);
if (resultSizeExceeded(result)) {
throw new TbScriptException(scriptId, TbScriptException.ErrorCode.OTHER, null, new RuntimeException(
format("Script invocation result exceeds maximum allowed size of %s symbols", getMaxResultSize())
));
}
return Futures.immediateFuture(output);
return output;
}, MoreExecutors.directExecutor());
return withTimeoutAndStatsCallback(scriptId, task, resultFuture, invokeCallback, getMaxInvokeRequestsTimeout());

9
common/script/script-api/src/main/java/org/thingsboard/script/api/js/NashornJsInvokeService.java

@ -41,7 +41,6 @@ import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReentrantLock;
@Slf4j
@ -62,9 +61,12 @@ public class NashornJsInvokeService extends AbstractJsInvokeService {
@Value("${js.local.monitor_thread_pool_size}")
private int monitorThreadPoolSize;
@Value("${js.local.max_cpu_time}")
@Value("${js.local.max_cpu_time:8000}") // 8 seconds
private long maxCpuTime;
@Value("${js.local.max_memory:104857600}") // 100 MiB
private long maxMemory;
@Getter
@Value("${js.local.max_errors}")
private int maxErrors;
@ -107,12 +109,13 @@ public class NashornJsInvokeService extends AbstractJsInvokeService {
@Override
public void init() {
super.init();
jsExecutor = MoreExecutors.listeningDecorator(Executors.newWorkStealingPool(jsExecutorThreadPoolSize));
jsExecutor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(jsExecutorThreadPoolSize, "nashorn-js-executor"));
if (useJsSandbox) {
sandbox = NashornSandboxes.create();
monitorExecutorService = ThingsBoardExecutors.newWorkStealingPool(monitorThreadPoolSize, "nashorn-js-monitor");
sandbox.setExecutor(monitorExecutorService);
sandbox.setMaxCPUTime(maxCpuTime);
sandbox.setMaxMemory(maxMemory);
sandbox.allowNoBraces(false);
sandbox.allowLoadFunctions(true);
sandbox.setMaxPreparedStatements(30);

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

@ -16,12 +16,11 @@
package org.thingsboard.server.transport.http;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
@ -31,6 +30,7 @@ import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
@ -56,6 +56,7 @@ import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg;
import org.thingsboard.server.gen.transport.TransportProtos.GetOtaPackageResponseMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg;
import org.thingsboard.server.gen.transport.TransportProtos.SessionCloseNotificationProto;
import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
@ -429,22 +430,17 @@ public class DeviceApiController implements TbTransportService {
.setDeviceIdMSB(sessionInfo.getDeviceIdMSB())
.setDeviceIdLSB(sessionInfo.getDeviceIdLSB())
.setType(firmwareType.name()).build();
transportContext.getTransportService().process(sessionInfo, requestMsg, new GetOtaPackageCallback(responseWriter, title, version, size, chunk));
transportContext.getTransportService().process(sessionInfo, requestMsg, new GetOtaPackageCallback(transportContext, responseWriter, title, version, size, chunk));
}));
return responseWriter;
}
private static class DeviceAuthCallback implements TransportServiceCallback<ValidateDeviceCredentialsResponse> {
@RequiredArgsConstructor
static class DeviceAuthCallback implements TransportServiceCallback<ValidateDeviceCredentialsResponse> {
private final TransportContext transportContext;
private final DeferredResult<ResponseEntity> responseWriter;
private final Consumer<SessionInfoProto> onSuccess;
DeviceAuthCallback(TransportContext transportContext, DeferredResult<ResponseEntity> responseWriter, Consumer<SessionInfoProto> onSuccess) {
this.transportContext = transportContext;
this.responseWriter = responseWriter;
this.onSuccess = onSuccess;
}
@Override
public void onSuccess(ValidateDeviceCredentialsResponse msg) {
if (msg.hasDeviceInfo()) {
@ -456,18 +452,21 @@ public class DeviceApiController implements TbTransportService {
@Override
public void onError(Throwable e) {
log.warn("Failed to process request", e);
responseWriter.setResult(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
String body = null;
if (e instanceof HttpMessageNotReadableException || e instanceof JsonParseException) {
body = e.getMessage();
log.debug("Failed to process request in DeviceAuthCallback: {}", body);
} else {
log.warn("Failed to process request in DeviceAuthCallback", e);
}
responseWriter.setResult(new ResponseEntity<>(body, HttpStatus.INTERNAL_SERVER_ERROR));
}
}
private static class DeviceProvisionCallback implements TransportServiceCallback<ProvisionDeviceResponseMsg> {
@RequiredArgsConstructor
static class DeviceProvisionCallback implements TransportServiceCallback<ProvisionDeviceResponseMsg> {
private final DeferredResult<ResponseEntity> responseWriter;
DeviceProvisionCallback(DeferredResult<ResponseEntity> responseWriter) {
this.responseWriter = responseWriter;
}
@Override
public void onSuccess(ProvisionDeviceResponseMsg msg) {
responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg).toString(), HttpStatus.OK));
@ -475,25 +474,25 @@ public class DeviceApiController implements TbTransportService {
@Override
public void onError(Throwable e) {
log.warn("Failed to process request", e);
responseWriter.setResult(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
String body = null;
if (e instanceof HttpMessageNotReadableException || e instanceof JsonParseException) {
body = e.getMessage();
log.debug("Failed to process request in DeviceProvisionCallback: {}", body);
} else {
log.warn("Failed to process request in DeviceProvisionCallback", e);
}
responseWriter.setResult(new ResponseEntity<>(body, HttpStatus.INTERNAL_SERVER_ERROR));
}
}
private class GetOtaPackageCallback implements TransportServiceCallback<TransportProtos.GetOtaPackageResponseMsg> {
@RequiredArgsConstructor
static class GetOtaPackageCallback implements TransportServiceCallback<GetOtaPackageResponseMsg> {
private final TransportContext transportContext;
private final DeferredResult<ResponseEntity> responseWriter;
private final String title;
private final String version;
private final int chuckSize;
private final int chuck;
GetOtaPackageCallback(DeferredResult<ResponseEntity> responseWriter, String title, String version, int chuckSize, int chuck) {
this.responseWriter = responseWriter;
this.title = title;
this.version = version;
this.chuckSize = chuckSize;
this.chuck = chuck;
}
private final int chunkSize;
private final int chunk;
@Override
public void onSuccess(TransportProtos.GetOtaPackageResponseMsg otaPackageResponseMsg) {
@ -501,7 +500,7 @@ public class DeviceApiController implements TbTransportService {
responseWriter.setResult(new ResponseEntity<>(HttpStatus.NOT_FOUND));
} else if (title.equals(otaPackageResponseMsg.getTitle()) && version.equals(otaPackageResponseMsg.getVersion())) {
String otaPackageId = new UUID(otaPackageResponseMsg.getOtaPackageIdMSB(), otaPackageResponseMsg.getOtaPackageIdLSB()).toString();
ByteArrayResource resource = new ByteArrayResource(transportContext.getOtaPackageDataCache().get(otaPackageId, chuckSize, chuck));
ByteArrayResource resource = new ByteArrayResource(transportContext.getOtaPackageDataCache().get(otaPackageId, chunkSize, chunk));
ResponseEntity<ByteArrayResource> response = ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + otaPackageResponseMsg.getFileName())
.header("x-filename", otaPackageResponseMsg.getFileName())
@ -516,8 +515,14 @@ public class DeviceApiController implements TbTransportService {
@Override
public void onError(Throwable e) {
log.warn("Failed to process request", e);
responseWriter.setResult(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
String body = null;
if (e instanceof HttpMessageNotReadableException || e instanceof JsonParseException) {
body = e.getMessage();
log.debug("Failed to process request in GetOtaPackageCallback: {}", body);
} else {
log.warn("Failed to process request in GetOtaPackageCallback", e);
}
responseWriter.setResult(new ResponseEntity<>(body, HttpStatus.INTERNAL_SERVER_ERROR));
}
}

82
common/transport/http/src/test/java/org/thingsboard/server/transport/http/DeviceApiControllerTest.java

@ -0,0 +1,82 @@
/**
* 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.transport.http;
import com.google.gson.JsonParseException;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.context.request.async.DeferredResult;
import org.thingsboard.server.common.transport.TransportContext;
import org.thingsboard.server.gen.transport.TransportProtos;
import java.io.IOException;
import java.util.function.Consumer;
class DeviceApiControllerTest {
@Test
void deviceAuthCallbackTest() {
TransportContext transportContext = Mockito.mock(TransportContext.class);
DeferredResult<ResponseEntity> responseWriter = Mockito.mock(DeferredResult.class);
Consumer<TransportProtos.SessionInfoProto> onSuccess = x -> {
};
var callback = new DeviceApiController.DeviceAuthCallback(transportContext, responseWriter, onSuccess);
callback.onError(new HttpMessageNotReadableException("JSON incorrect syntax"));
callback.onError(new JsonParseException("Json ; expected"));
callback.onError(new IOException("not found"));
callback.onError(new RuntimeException("oops it is run time error"));
}
@Test
void deviceProvisionCallbackTest() {
DeferredResult<ResponseEntity> responseWriter = Mockito.mock(DeferredResult.class);
var callback = new DeviceApiController.DeviceProvisionCallback(responseWriter);
callback.onError(new HttpMessageNotReadableException("JSON incorrect syntax"));
callback.onError(new JsonParseException("Json ; expected"));
callback.onError(new IOException("not found"));
callback.onError(new RuntimeException("oops it is run time error"));
}
@Test
void getOtaPackageCallback() {
TransportContext transportContext = Mockito.mock(TransportContext.class);
DeferredResult<ResponseEntity> responseWriter = Mockito.mock(DeferredResult.class);
String title = "Title";
String version = "version";
int chunkSize = 11;
int chunk = 3;
var callback = new DeviceApiController.GetOtaPackageCallback(transportContext, responseWriter, title, version, chunkSize, chunk);
callback.onError(new HttpMessageNotReadableException("JSON incorrect syntax"));
callback.onError(new JsonParseException("Json ; expected"));
callback.onError(new IOException("not found"));
callback.onError(new RuntimeException("oops it is run time error"));
}
}

16
common/transport/http/src/test/resources/logback-test.xml

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{ISO8601} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
logger name="org.thingsboard.server.transport.http.DeviceApiController" level="DEBUG" />
<root level="INFO">
<appender-ref ref="console"/>
</root>
</configuration>

18
dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java

@ -56,11 +56,19 @@ public abstract class DaoUtil {
}
public static Pageable toPageable(PageLink pageLink) {
return toPageable(pageLink, Collections.emptyMap());
return toPageable(pageLink, true);
}
public static Pageable toPageable(PageLink pageLink, boolean addDefaultSorting) {
return toPageable(pageLink, Collections.emptyMap(), addDefaultSorting);
}
public static Pageable toPageable(PageLink pageLink, Map<String, String> columnMap) {
return PageRequest.of(pageLink.getPage(), pageLink.getPageSize(), pageLink.toSort(pageLink.getSortOrder(), columnMap));
return toPageable(pageLink, columnMap, true);
}
public static Pageable toPageable(PageLink pageLink, Map<String, String> columnMap, boolean addDefaultSorting) {
return PageRequest.of(pageLink.getPage(), pageLink.getPageSize(), pageLink.toSort(pageLink.getSortOrder(), columnMap, addDefaultSorting));
}
public static Pageable toPageable(PageLink pageLink, List<SortOrder> sortOrders) {
@ -68,7 +76,11 @@ public abstract class DaoUtil {
}
public static Pageable toPageable(PageLink pageLink, Map<String, String> columnMap, List<SortOrder> sortOrders) {
return PageRequest.of(pageLink.getPage(), pageLink.getPageSize(), pageLink.toSort(sortOrders, columnMap));
return toPageable(pageLink, columnMap, sortOrders, true);
}
public static Pageable toPageable(PageLink pageLink, Map<String, String> columnMap, List<SortOrder> sortOrders, boolean addDefaultSorting) {
return PageRequest.of(pageLink.getPage(), pageLink.getPageSize(), pageLink.toSort(sortOrders, columnMap, addDefaultSorting));
}
public static <T> List<T> convertDataList(Collection<? extends ToData<T>> toDataList) {

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

@ -80,6 +80,8 @@ public interface AlarmDao extends Dao<Alarm> {
PageData<AlarmId> findAlarmIdsByAssigneeId(TenantId tenantId, UUID userId, PageLink pageLink);
PageData<AlarmId> findAlarmIdsByOriginatorId(TenantId tenantId, EntityId originatorId, PageLink pageLink);
void createEntityAlarmRecord(EntityAlarm entityAlarm);
List<EntityAlarm> findEntityAlarmRecords(TenantId tenantId, AlarmId id);

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

@ -82,14 +82,14 @@ public class BaseAlarmCommentService extends AbstractEntityService implements Al
@Override
public ListenableFuture<AlarmComment> findAlarmCommentByIdAsync(TenantId tenantId, AlarmCommentId alarmCommentId) {
log.trace("Executing findAlarmCommentByIdAsync by alarmCommentId [{}]", alarmCommentId);
validateId(alarmCommentId, "Incorrect alarmCommentId " + alarmCommentId);
validateId(alarmCommentId, id -> "Incorrect alarmCommentId " + id);
return alarmCommentDao.findAlarmCommentByIdAsync(tenantId, alarmCommentId.getId());
}
@Override
public AlarmComment findAlarmCommentById(TenantId tenantId, AlarmCommentId alarmCommentId) {
log.trace("Executing findAlarmCommentByIdAsync by alarmCommentId [{}]", alarmCommentId);
validateId(alarmCommentId, "Incorrect alarmCommentId " + alarmCommentId);
validateId(alarmCommentId, id -> "Incorrect alarmCommentId " + id);
return alarmCommentDao.findById(tenantId, alarmCommentId.getId());
}

20
dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java

@ -169,7 +169,7 @@ public class BaseAlarmService extends AbstractCachedEntityService<TenantId, Page
@Override
public PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId,
AlarmDataQuery query, Collection<EntityId> orderedEntityIds) {
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateEntityDataPageLink(query.getPageLink());
return alarmDao.findAlarmDataByQueryForEntities(tenantId, query, orderedEntityIds);
}
@ -261,21 +261,21 @@ public class BaseAlarmService extends AbstractCachedEntityService<TenantId, Page
@Override
public Alarm findAlarmById(TenantId tenantId, AlarmId alarmId) {
log.trace("Executing findAlarmById [{}]", alarmId);
validateId(alarmId, "Incorrect alarmId " + alarmId);
validateId(alarmId, id -> "Incorrect alarmId " + id);
return alarmDao.findAlarmById(tenantId, alarmId.getId());
}
@Override
public ListenableFuture<Alarm> findAlarmByIdAsync(TenantId tenantId, AlarmId alarmId) {
log.trace("Executing findAlarmByIdAsync [{}]", alarmId);
validateId(alarmId, "Incorrect alarmId " + alarmId);
validateId(alarmId, id -> "Incorrect alarmId " + id);
return alarmDao.findAlarmByIdAsync(tenantId, alarmId.getId());
}
@Override
public AlarmInfo findAlarmInfoById(TenantId tenantId, AlarmId alarmId) {
log.trace("Executing findAlarmInfoByIdAsync [{}]", alarmId);
validateId(alarmId, "Incorrect alarmId " + alarmId);
validateId(alarmId, id -> "Incorrect alarmId " + id);
return alarmDao.findAlarmInfoById(tenantId, alarmId.getId());
}
@ -302,10 +302,16 @@ public class BaseAlarmService extends AbstractCachedEntityService<TenantId, Page
@Override
public PageData<AlarmId> findAlarmIdsByAssigneeId(TenantId tenantId, UserId userId, PageLink pageLink) {
log.trace("[{}] Executing findAlarmIdsByAssigneeId [{}]", tenantId, userId);
validateId(userId, "Incorrect userId " + userId);
validateId(userId, id -> "Incorrect userId " + id);
return alarmDao.findAlarmIdsByAssigneeId(tenantId, userId.getId(), pageLink);
}
@Override
public PageData<AlarmId> findAlarmIdsByOriginatorId(TenantId tenantId, EntityId originatorId, PageLink pageLink) {
log.trace("[{}] Executing findAlarmsByOriginatorId [{}]", tenantId, originatorId);
return alarmDao.findAlarmIdsByOriginatorId(tenantId, originatorId, pageLink);
}
@Override
public AlarmSeverity findHighestAlarmSeverity(TenantId tenantId, EntityId entityId, AlarmSearchStatus alarmSearchStatus,
AlarmStatus alarmStatus, String assigneeId) {
@ -336,14 +342,14 @@ public class BaseAlarmService extends AbstractCachedEntityService<TenantId, Page
@Override
public long countAlarmsByQuery(TenantId tenantId, CustomerId customerId, AlarmCountQuery query) {
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
return alarmDao.countAlarmsByQuery(tenantId, customerId, query);
}
@Override
public PageData<EntitySubtype> findAlarmTypesByTenantId(TenantId tenantId, PageLink pageLink) {
log.trace("Executing findAlarmTypesByTenantId, tenantId [{}]", tenantId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
if (DEFAULT_ALARM_TYPES_PAGE_LINK.equals(pageLink)) {
return cache.getAndPutInTransaction(tenantId, () ->
alarmDao.findTenantAlarmTypes(tenantId.getId(), pageLink), false);

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

@ -103,7 +103,7 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService<AssetPr
@Override
public AssetProfile findAssetProfileById(TenantId tenantId, AssetProfileId assetProfileId, boolean putInCache) {
log.trace("Executing findAssetProfileById [{}]", assetProfileId);
Validator.validateId(assetProfileId, INCORRECT_ASSET_PROFILE_ID + assetProfileId);
Validator.validateId(assetProfileId, id -> INCORRECT_ASSET_PROFILE_ID + id);
return cache.getOrFetchFromDB(AssetProfileCacheKey.fromId(assetProfileId),
() -> assetProfileDao.findById(tenantId, assetProfileId.getId()), true, putInCache);
}
@ -116,7 +116,7 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService<AssetPr
@Override
public AssetProfile findAssetProfileByName(TenantId tenantId, String profileName, boolean putInCache) {
log.trace("Executing findAssetProfileByName [{}][{}]", tenantId, profileName);
Validator.validateString(profileName, INCORRECT_ASSET_PROFILE_NAME + profileName);
Validator.validateString(profileName, s -> INCORRECT_ASSET_PROFILE_NAME + s);
return cache.getOrFetchFromDB(AssetProfileCacheKey.fromName(tenantId, profileName),
() -> assetProfileDao.findByName(tenantId, profileName), false, putInCache);
}
@ -124,7 +124,7 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService<AssetPr
@Override
public AssetProfileInfo findAssetProfileInfoById(TenantId tenantId, AssetProfileId assetProfileId) {
log.trace("Executing findAssetProfileInfoById [{}]", assetProfileId);
Validator.validateId(assetProfileId, INCORRECT_ASSET_PROFILE_ID + assetProfileId);
Validator.validateId(assetProfileId, id -> INCORRECT_ASSET_PROFILE_ID + id);
return toAssetProfileInfo(findAssetProfileById(tenantId, assetProfileId));
}
@ -179,7 +179,7 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService<AssetPr
@Transactional
public void deleteAssetProfile(TenantId tenantId, AssetProfileId assetProfileId) {
log.trace("Executing deleteAssetProfile [{}]", assetProfileId);
Validator.validateId(assetProfileId, INCORRECT_ASSET_PROFILE_ID + assetProfileId);
Validator.validateId(assetProfileId, id -> INCORRECT_ASSET_PROFILE_ID + id);
AssetProfile assetProfile = assetProfileDao.findById(tenantId, assetProfileId.getId());
if (assetProfile != null && assetProfile.isDefault()) {
throw new DataValidationException("Deletion of Default Asset Profile is prohibited!");
@ -208,7 +208,7 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService<AssetPr
@Override
public PageData<AssetProfile> findAssetProfiles(TenantId tenantId, PageLink pageLink) {
log.trace("Executing findAssetProfiles tenantId [{}], pageLink [{}]", tenantId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
Validator.validatePageLink(pageLink);
return assetProfileDao.findAssetProfiles(tenantId, pageLink);
}
@ -216,7 +216,7 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService<AssetPr
@Override
public PageData<AssetProfileInfo> findAssetProfileInfos(TenantId tenantId, PageLink pageLink) {
log.trace("Executing findAssetProfileInfos tenantId [{}], pageLink [{}]", tenantId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
Validator.validatePageLink(pageLink);
return assetProfileDao.findAssetProfileInfos(tenantId, pageLink);
}
@ -246,7 +246,7 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService<AssetPr
}
private AssetProfile doCreateDefaultAssetProfile(TenantId tenantId, String profileName, boolean defaultProfile, boolean publishSaveEvent) {
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
AssetProfile assetProfile = new AssetProfile();
assetProfile.setTenantId(tenantId);
assetProfile.setDefault(defaultProfile);
@ -258,7 +258,7 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService<AssetPr
@Override
public AssetProfile findDefaultAssetProfile(TenantId tenantId) {
log.trace("Executing findDefaultAssetProfile tenantId [{}]", tenantId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
return cache.getAndPutInTransaction(AssetProfileCacheKey.defaultProfile(tenantId),
() -> assetProfileDao.findDefaultAssetProfile(tenantId), true);
}
@ -266,14 +266,14 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService<AssetPr
@Override
public AssetProfileInfo findDefaultAssetProfileInfo(TenantId tenantId) {
log.trace("Executing findDefaultAssetProfileInfo tenantId [{}]", tenantId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
return toAssetProfileInfo(findDefaultAssetProfile(tenantId));
}
@Override
public boolean setDefaultAssetProfile(TenantId tenantId, AssetProfileId assetProfileId) {
log.trace("Executing setDefaultAssetProfile [{}]", assetProfileId);
Validator.validateId(assetProfileId, INCORRECT_ASSET_PROFILE_ID + assetProfileId);
Validator.validateId(assetProfileId, id -> INCORRECT_ASSET_PROFILE_ID + id);
AssetProfile assetProfile = assetProfileDao.findById(tenantId, assetProfileId.getId());
if (!assetProfile.isDefault()) {
assetProfile.setDefault(true);
@ -299,7 +299,7 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService<AssetPr
@Override
public void deleteAssetProfilesByTenantId(TenantId tenantId) {
log.trace("Executing deleteAssetProfilesByTenantId, tenantId [{}]", tenantId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
tenantAssetProfilesRemover.removeEntities(tenantId, tenantId);
}
@ -322,7 +322,7 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService<AssetPr
@Override
public List<EntityInfo> findAssetProfileNamesByTenantId(TenantId tenantId, boolean activeOnly) {
log.trace("Executing findAssetProfileNamesByTenantId, tenantId [{}]", tenantId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
return assetProfileDao.findTenantAssetProfileNames(tenantId.getId(), activeOnly)
.stream().sorted(Comparator.comparing(EntityInfo::getName))
.collect(Collectors.toList());

81
dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java

@ -75,7 +75,6 @@ public class BaseAssetService extends AbstractCachedEntityService<AssetCacheKey,
public static final String INCORRECT_ASSET_PROFILE_ID = "Incorrect assetProfileId ";
public static final String INCORRECT_CUSTOMER_ID = "Incorrect customerId ";
public static final String INCORRECT_ASSET_ID = "Incorrect assetId ";
public static final String TB_SERVICE_QUEUE = "TbServiceQueue";
@Autowired
private AssetDao assetDao;
@ -103,28 +102,28 @@ public class BaseAssetService extends AbstractCachedEntityService<AssetCacheKey,
@Override
public AssetInfo findAssetInfoById(TenantId tenantId, AssetId assetId) {
log.trace("Executing findAssetInfoById [{}]", assetId);
validateId(assetId, INCORRECT_ASSET_ID + assetId);
validateId(assetId, id -> INCORRECT_ASSET_ID + id);
return assetDao.findAssetInfoById(tenantId, assetId.getId());
}
@Override
public Asset findAssetById(TenantId tenantId, AssetId assetId) {
log.trace("Executing findAssetById [{}]", assetId);
validateId(assetId, INCORRECT_ASSET_ID + assetId);
validateId(assetId, id -> INCORRECT_ASSET_ID + id);
return assetDao.findById(tenantId, assetId.getId());
}
@Override
public ListenableFuture<Asset> findAssetByIdAsync(TenantId tenantId, AssetId assetId) {
log.trace("Executing findAssetById [{}]", assetId);
validateId(assetId, INCORRECT_ASSET_ID + assetId);
validateId(assetId, id -> INCORRECT_ASSET_ID + id);
return assetDao.findByIdAsync(tenantId, assetId.getId());
}
@Override
public Asset findAssetByTenantIdAndName(TenantId tenantId, String name) {
log.trace("Executing findAssetByTenantIdAndName [{}][{}]", tenantId, name);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
return cache.getAndPutInTransaction(new AssetCacheKey(tenantId, name),
() -> assetDao.findAssetsByTenantIdAndName(tenantId.getId(), name)
.orElse(null), true);
@ -199,7 +198,7 @@ public class BaseAssetService extends AbstractCachedEntityService<AssetCacheKey,
@Override
@Transactional
public void deleteAsset(TenantId tenantId, AssetId assetId) {
validateId(assetId, INCORRECT_ASSET_ID + assetId);
validateId(assetId, id -> INCORRECT_ASSET_ID + id);
if (entityViewService.existsByTenantIdAndEntityId(tenantId, assetId)) {
throw new DataValidationException("Can't delete asset that has entity views!");
}
@ -223,7 +222,7 @@ public class BaseAssetService extends AbstractCachedEntityService<AssetCacheKey,
@Override
public PageData<Asset> findAssetsByTenantId(TenantId tenantId, PageLink pageLink) {
log.trace("Executing findAssetsByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validatePageLink(pageLink);
return assetDao.findAssetsByTenantId(tenantId.getId(), pageLink);
}
@ -231,7 +230,7 @@ public class BaseAssetService extends AbstractCachedEntityService<AssetCacheKey,
@Override
public PageData<AssetInfo> findAssetInfosByTenantId(TenantId tenantId, PageLink pageLink) {
log.trace("Executing findAssetInfosByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validatePageLink(pageLink);
return assetDao.findAssetInfosByTenantId(tenantId.getId(), pageLink);
}
@ -239,8 +238,8 @@ public class BaseAssetService extends AbstractCachedEntityService<AssetCacheKey,
@Override
public PageData<Asset> findAssetsByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink) {
log.trace("Executing findAssetsByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateString(type, "Incorrect type " + type);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateString(type, t -> "Incorrect type " + t);
validatePageLink(pageLink);
return assetDao.findAssetsByTenantIdAndType(tenantId.getId(), type, pageLink);
}
@ -248,8 +247,8 @@ public class BaseAssetService extends AbstractCachedEntityService<AssetCacheKey,
@Override
public PageData<AssetInfo> findAssetInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink) {
log.trace("Executing findAssetInfosByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateString(type, "Incorrect type " + type);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateString(type, t -> "Incorrect type " + t);
validatePageLink(pageLink);
return assetDao.findAssetInfosByTenantIdAndType(tenantId.getId(), type, pageLink);
}
@ -257,8 +256,8 @@ public class BaseAssetService extends AbstractCachedEntityService<AssetCacheKey,
@Override
public PageData<AssetInfo> findAssetInfosByTenantIdAndAssetProfileId(TenantId tenantId, AssetProfileId assetProfileId, PageLink pageLink) {
log.trace("Executing findAssetInfosByTenantIdAndAssetProfileId, tenantId [{}], assetProfileId [{}], pageLink [{}]", tenantId, assetProfileId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(assetProfileId, INCORRECT_ASSET_PROFILE_ID + assetProfileId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(assetProfileId, id -> INCORRECT_ASSET_PROFILE_ID + id);
validatePageLink(pageLink);
return assetDao.findAssetInfosByTenantIdAndAssetProfileId(tenantId.getId(), assetProfileId.getId(), pageLink);
}
@ -266,23 +265,23 @@ public class BaseAssetService extends AbstractCachedEntityService<AssetCacheKey,
@Override
public ListenableFuture<List<Asset>> findAssetsByTenantIdAndIdsAsync(TenantId tenantId, List<AssetId> assetIds) {
log.trace("Executing findAssetsByTenantIdAndIdsAsync, tenantId [{}], assetIds [{}]", tenantId, assetIds);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateIds(assetIds, "Incorrect assetIds " + assetIds);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateIds(assetIds, ids -> "Incorrect assetIds " + ids);
return assetDao.findAssetsByTenantIdAndIdsAsync(tenantId.getId(), toUUIDs(assetIds));
}
@Override
public void deleteAssetsByTenantId(TenantId tenantId) {
log.trace("Executing deleteAssetsByTenantId, tenantId [{}]", tenantId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
tenantAssetsRemover.removeEntities(tenantId, tenantId);
}
@Override
public PageData<Asset> findAssetsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, PageLink pageLink) {
log.trace("Executing findAssetsByTenantIdAndCustomerId, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id);
validatePageLink(pageLink);
return assetDao.findAssetsByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink);
}
@ -290,8 +289,8 @@ public class BaseAssetService extends AbstractCachedEntityService<AssetCacheKey,
@Override
public PageData<AssetInfo> findAssetInfosByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, PageLink pageLink) {
log.trace("Executing findAssetInfosByTenantIdAndCustomerId, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id);
validatePageLink(pageLink);
return assetDao.findAssetInfosByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink);
}
@ -299,9 +298,9 @@ public class BaseAssetService extends AbstractCachedEntityService<AssetCacheKey,
@Override
public PageData<Asset> findAssetsByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, PageLink pageLink) {
log.trace("Executing findAssetsByTenantIdAndCustomerIdAndType, tenantId [{}], customerId [{}], type [{}], pageLink [{}]", tenantId, customerId, type, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
validateString(type, "Incorrect type " + type);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id);
validateString(type, t -> "Incorrect type " + t);
validatePageLink(pageLink);
return assetDao.findAssetsByTenantIdAndCustomerIdAndType(tenantId.getId(), customerId.getId(), type, pageLink);
}
@ -309,9 +308,9 @@ public class BaseAssetService extends AbstractCachedEntityService<AssetCacheKey,
@Override
public PageData<AssetInfo> findAssetInfosByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, PageLink pageLink) {
log.trace("Executing findAssetInfosByTenantIdAndCustomerIdAndType, tenantId [{}], customerId [{}], type [{}], pageLink [{}]", tenantId, customerId, type, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
validateString(type, "Incorrect type " + type);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id);
validateString(type, t -> "Incorrect type " + t);
validatePageLink(pageLink);
return assetDao.findAssetInfosByTenantIdAndCustomerIdAndType(tenantId.getId(), customerId.getId(), type, pageLink);
}
@ -319,9 +318,9 @@ public class BaseAssetService extends AbstractCachedEntityService<AssetCacheKey,
@Override
public PageData<AssetInfo> findAssetInfosByTenantIdAndCustomerIdAndAssetProfileId(TenantId tenantId, CustomerId customerId, AssetProfileId assetProfileId, PageLink pageLink) {
log.trace("Executing findAssetInfosByTenantIdAndCustomerIdAndAssetProfileId, tenantId [{}], customerId [{}], assetProfileId [{}], pageLink [{}]", tenantId, customerId, assetProfileId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
validateId(assetProfileId, INCORRECT_ASSET_PROFILE_ID + assetProfileId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id);
validateId(assetProfileId, id -> INCORRECT_ASSET_PROFILE_ID + id);
validatePageLink(pageLink);
return assetDao.findAssetInfosByTenantIdAndCustomerIdAndAssetProfileId(tenantId.getId(), customerId.getId(), assetProfileId.getId(), pageLink);
}
@ -329,17 +328,17 @@ public class BaseAssetService extends AbstractCachedEntityService<AssetCacheKey,
@Override
public ListenableFuture<List<Asset>> findAssetsByTenantIdCustomerIdAndIdsAsync(TenantId tenantId, CustomerId customerId, List<AssetId> assetIds) {
log.trace("Executing findAssetsByTenantIdAndCustomerIdAndIdsAsync, tenantId [{}], customerId [{}], assetIds [{}]", tenantId, customerId, assetIds);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
validateIds(assetIds, "Incorrect assetIds " + assetIds);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id);
validateIds(assetIds, ids -> "Incorrect assetIds " + ids);
return assetDao.findAssetsByTenantIdAndCustomerIdAndIdsAsync(tenantId.getId(), customerId.getId(), toUUIDs(assetIds));
}
@Override
public void unassignCustomerAssets(TenantId tenantId, CustomerId customerId) {
log.trace("Executing unassignCustomerAssets, tenantId [{}], customerId [{}]", tenantId, customerId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id);
customerAssetsUnasigner.removeEntities(tenantId, customerId);
}
@ -371,7 +370,7 @@ public class BaseAssetService extends AbstractCachedEntityService<AssetCacheKey,
@Override
public ListenableFuture<List<EntitySubtype>> findAssetTypesByTenantId(TenantId tenantId) {
log.trace("Executing findAssetTypesByTenantId, tenantId [{}]", tenantId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
return assetDao.findTenantAssetTypesAsync(tenantId.getId());
}
@ -420,8 +419,8 @@ public class BaseAssetService extends AbstractCachedEntityService<AssetCacheKey,
@Override
public PageData<Asset> findAssetsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink) {
log.trace("Executing findAssetsByTenantIdAndEdgeId, tenantId [{}], edgeId [{}], pageLink [{}]", tenantId, edgeId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(edgeId, INCORRECT_EDGE_ID + edgeId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(edgeId, id -> INCORRECT_EDGE_ID + id);
validatePageLink(pageLink);
return assetDao.findAssetsByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink);
}
@ -429,9 +428,9 @@ public class BaseAssetService extends AbstractCachedEntityService<AssetCacheKey,
@Override
public PageData<Asset> findAssetsByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, PageLink pageLink) {
log.trace("Executing findAssetsByTenantIdAndEdgeIdAndType, tenantId [{}], edgeId [{}], type [{}] pageLink [{}]", tenantId, edgeId, type, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(edgeId, INCORRECT_EDGE_ID + edgeId);
validateString(type, "Incorrect type " + type);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(edgeId, id -> INCORRECT_EDGE_ID + id);
validateString(type, t -> "Incorrect type " + t);
validatePageLink(pageLink);
return assetDao.findAssetsByTenantIdAndEdgeIdAndType(tenantId.getId(), edgeId.getId(), type, pageLink);
}

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

@ -28,12 +28,12 @@ public class AttributeUtils {
@Deprecated(since = "3.7.0")
public static void validate(EntityId id, String scope) {
Validator.validateId(id.getId(), "Incorrect id " + id);
Validator.validateString(scope, "Incorrect scope " + scope);
Validator.validateId(id.getId(), uuid -> "Incorrect id " + uuid);
Validator.validateString(scope, sc -> "Incorrect scope " + sc);
}
public static void validate(EntityId id, AttributeScope scope) {
Validator.validateId(id.getId(), "Incorrect id " + id);
Validator.validateId(id.getId(), uuid -> "Incorrect id " + uuid);
Validator.checkNotNull(scope, "Incorrect scope " + scope);
}

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

@ -58,28 +58,28 @@ public class BaseAttributesService implements AttributesService {
@Override
public ListenableFuture<Optional<AttributeKvEntry>> find(TenantId tenantId, EntityId entityId, String scope, String attributeKey) {
validate(entityId, scope);
Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey);
Validator.validateString(attributeKey, k -> "Incorrect attribute key " + k);
return Futures.immediateFuture(attributesDao.find(tenantId, entityId, AttributeScope.valueOf(scope), attributeKey));
}
@Override
public ListenableFuture<Optional<AttributeKvEntry>> find(TenantId tenantId, EntityId entityId, AttributeScope scope, String attributeKey) {
validate(entityId, scope);
Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey);
Validator.validateString(attributeKey, k -> "Incorrect attribute key " + k);
return Futures.immediateFuture(attributesDao.find(tenantId, entityId, scope, attributeKey));
}
@Override
public ListenableFuture<List<AttributeKvEntry>> find(TenantId tenantId, EntityId entityId, String scope, Collection<String> attributeKeys) {
validate(entityId, scope);
attributeKeys.forEach(attributeKey -> Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey));
attributeKeys.forEach(attributeKey -> Validator.validateString(attributeKey, k -> "Incorrect attribute key " + k));
return Futures.immediateFuture(attributesDao.find(tenantId, entityId, AttributeScope.valueOf(scope), attributeKeys));
}
@Override
public ListenableFuture<List<AttributeKvEntry>> find(TenantId tenantId, EntityId entityId, AttributeScope scope, Collection<String> attributeKeys) {
validate(entityId, scope);
attributeKeys.forEach(attributeKey -> Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey));
attributeKeys.forEach(attributeKey -> Validator.validateString(attributeKey, k -> "Incorrect attribute key " + k));
return Futures.immediateFuture(attributesDao.find(tenantId, entityId, scope, attributeKeys));
}

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

@ -117,7 +117,7 @@ public class CachedAttributesService implements AttributesService {
@Override
public ListenableFuture<Optional<AttributeKvEntry>> find(TenantId tenantId, EntityId entityId, AttributeScope scope, String attributeKey) {
validate(entityId, scope);
Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey);
Validator.validateString(attributeKey, k -> "Incorrect attribute key " + k);
return cacheExecutor.submit(() -> {
AttributeCacheKey attributeCacheKey = new AttributeCacheKey(scope, entityId, attributeKey);
@ -152,7 +152,7 @@ public class CachedAttributesService implements AttributesService {
public ListenableFuture<List<AttributeKvEntry>> find(TenantId tenantId, EntityId entityId, AttributeScope scope, final Collection<String> attributeKeysNonUnique) {
validate(entityId, scope);
final var attributeKeys = new LinkedHashSet<>(attributeKeysNonUnique); // deduplicate the attributes
attributeKeys.forEach(attributeKey -> Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey));
attributeKeys.forEach(attributeKey -> Validator.validateString(attributeKey, k ->"Incorrect attribute key " + k));
//CacheExecutor for Redis or DirectExecutor for local Caffeine
return Futures.transformAsync(cacheExecutor.submit(() -> findCachedAttributes(entityId, scope, attributeKeys)),

14
dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java

@ -85,31 +85,31 @@ public class AuditLogServiceImpl implements AuditLogService {
@Override
public PageData<AuditLog> findAuditLogsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, List<ActionType> actionTypes, TimePageLink pageLink) {
log.trace("Executing findAuditLogsByTenantIdAndCustomerId [{}], [{}], [{}]", tenantId, customerId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, "Incorrect customerId " + customerId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(customerId, id -> "Incorrect customerId " + id);
return auditLogDao.findAuditLogsByTenantIdAndCustomerId(tenantId.getId(), customerId, actionTypes, pageLink);
}
@Override
public PageData<AuditLog> findAuditLogsByTenantIdAndUserId(TenantId tenantId, UserId userId, List<ActionType> actionTypes, TimePageLink pageLink) {
log.trace("Executing findAuditLogsByTenantIdAndUserId [{}], [{}], [{}]", tenantId, userId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(userId, "Incorrect userId" + userId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(userId, id -> "Incorrect userId" + id);
return auditLogDao.findAuditLogsByTenantIdAndUserId(tenantId.getId(), userId, actionTypes, pageLink);
}
@Override
public PageData<AuditLog> findAuditLogsByTenantIdAndEntityId(TenantId tenantId, EntityId entityId, List<ActionType> actionTypes, TimePageLink pageLink) {
log.trace("Executing findAuditLogsByTenantIdAndEntityId [{}], [{}], [{}]", tenantId, entityId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateEntityId(entityId, INCORRECT_TENANT_ID + entityId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateEntityId(entityId, id -> "Incorrect entityId" + id);
return auditLogDao.findAuditLogsByTenantIdAndEntityId(tenantId.getId(), entityId, actionTypes, pageLink);
}
@Override
public PageData<AuditLog> findAuditLogsByTenantId(TenantId tenantId, List<ActionType> actionTypes, TimePageLink pageLink) {
log.trace("Executing findAuditLogs [{}]", pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
return auditLogDao.findAuditLogsByTenantId(tenantId.getId(), actionTypes, pageLink);
}

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

@ -84,21 +84,21 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom
@Override
public Customer findCustomerById(TenantId tenantId, CustomerId customerId) {
log.trace("Executing findCustomerById [{}]", customerId);
Validator.validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
Validator.validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id);
return customerDao.findById(tenantId, customerId.getId());
}
@Override
public Optional<Customer> findCustomerByTenantIdAndTitle(TenantId tenantId, String title) {
log.trace("Executing findCustomerByTenantIdAndTitle [{}] [{}]", tenantId, title);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
return customerDao.findCustomersByTenantIdAndTitle(tenantId.getId(), title);
}
@Override
public ListenableFuture<Customer> findCustomerByIdAsync(TenantId tenantId, CustomerId customerId) {
log.trace("Executing findCustomerByIdAsync [{}]", customerId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id);
return customerDao.findByIdAsync(tenantId, customerId.getId());
}
@ -126,7 +126,7 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom
@Transactional
public void deleteCustomer(TenantId tenantId, CustomerId customerId) {
log.trace("Executing deleteCustomer [{}]", customerId);
Validator.validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
Validator.validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id);
Customer customer = findCustomerById(tenantId, customerId);
if (customer == null) {
throw new IncorrectParameterException("Unable to delete non-existent customer.");
@ -147,7 +147,7 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom
@Override
public Customer findOrCreatePublicCustomer(TenantId tenantId) {
log.trace("Executing findOrCreatePublicCustomer, tenantId [{}]", tenantId);
Validator.validateId(tenantId, INCORRECT_CUSTOMER_ID + tenantId);
Validator.validateId(tenantId, id -> INCORRECT_CUSTOMER_ID + id);
Optional<Customer> publicCustomerOpt = customerDao.findCustomersByTenantIdAndTitle(tenantId.getId(), PUBLIC_CUSTOMER_TITLE);
if (publicCustomerOpt.isPresent()) {
return publicCustomerOpt.get();
@ -169,7 +169,7 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom
@Override
public PageData<Customer> findCustomersByTenantId(TenantId tenantId, PageLink pageLink) {
log.trace("Executing findCustomersByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink);
Validator.validateId(tenantId, "Incorrect tenantId " + tenantId);
Validator.validateId(tenantId, id -> "Incorrect tenantId " + id);
Validator.validatePageLink(pageLink);
return customerDao.findCustomersByTenantId(tenantId.getId(), pageLink);
}
@ -177,7 +177,7 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom
@Override
public void deleteCustomersByTenantId(TenantId tenantId) {
log.trace("Executing deleteCustomersByTenantId, tenantId [{}]", tenantId);
Validator.validateId(tenantId, "Incorrect tenantId " + tenantId);
Validator.validateId(tenantId, id -> "Incorrect tenantId " + id);
customersByTenantRemover.removeEntities(tenantId, tenantId);
}

32
dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java

@ -110,21 +110,21 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
@Override
public Dashboard findDashboardById(TenantId tenantId, DashboardId dashboardId) {
log.trace("Executing findDashboardById [{}]", dashboardId);
Validator.validateId(dashboardId, INCORRECT_DASHBOARD_ID + dashboardId);
Validator.validateId(dashboardId, id -> INCORRECT_DASHBOARD_ID + id);
return dashboardDao.findById(tenantId, dashboardId.getId());
}
@Override
public ListenableFuture<Dashboard> findDashboardByIdAsync(TenantId tenantId, DashboardId dashboardId) {
log.trace("Executing findDashboardByIdAsync [{}]", dashboardId);
validateId(dashboardId, INCORRECT_DASHBOARD_ID + dashboardId);
validateId(dashboardId, id -> INCORRECT_DASHBOARD_ID + id);
return dashboardDao.findByIdAsync(tenantId, dashboardId.getId());
}
@Override
public DashboardInfo findDashboardInfoById(TenantId tenantId, DashboardId dashboardId) {
log.trace("Executing findDashboardInfoById [{}]", dashboardId);
Validator.validateId(dashboardId, INCORRECT_DASHBOARD_ID + dashboardId);
Validator.validateId(dashboardId, id -> INCORRECT_DASHBOARD_ID + id);
return dashboardInfoDao.findById(tenantId, dashboardId.getId());
}
@ -137,7 +137,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
@Override
public ListenableFuture<DashboardInfo> findDashboardInfoByIdAsync(TenantId tenantId, DashboardId dashboardId) {
log.trace("Executing findDashboardInfoByIdAsync [{}]", dashboardId);
validateId(dashboardId, INCORRECT_DASHBOARD_ID + dashboardId);
validateId(dashboardId, id -> INCORRECT_DASHBOARD_ID + id);
return dashboardInfoDao.findByIdAsync(tenantId, dashboardId.getId());
}
@ -229,7 +229,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
@Transactional
public void deleteDashboard(TenantId tenantId, DashboardId dashboardId) {
log.trace("Executing deleteDashboard [{}]", dashboardId);
Validator.validateId(dashboardId, INCORRECT_DASHBOARD_ID + dashboardId);
Validator.validateId(dashboardId, id -> INCORRECT_DASHBOARD_ID + id);
deleteEntityRelations(tenantId, dashboardId);
try {
dashboardDao.removeById(tenantId, dashboardId.getId());
@ -249,7 +249,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
@Override
public PageData<DashboardInfo> findDashboardsByTenantId(TenantId tenantId, PageLink pageLink) {
log.trace("Executing findDashboardsByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink);
Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
Validator.validatePageLink(pageLink);
return dashboardInfoDao.findDashboardsByTenantId(tenantId.getId(), pageLink);
}
@ -257,7 +257,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
@Override
public PageData<DashboardInfo> findMobileDashboardsByTenantId(TenantId tenantId, PageLink pageLink) {
log.trace("Executing findMobileDashboardsByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink);
Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
Validator.validatePageLink(pageLink);
return dashboardInfoDao.findMobileDashboardsByTenantId(tenantId.getId(), pageLink);
}
@ -265,15 +265,15 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
@Override
public void deleteDashboardsByTenantId(TenantId tenantId) {
log.trace("Executing deleteDashboardsByTenantId, tenantId [{}]", tenantId);
Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
tenantDashboardsRemover.removeEntities(tenantId, tenantId);
}
@Override
public PageData<DashboardInfo> findDashboardsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, PageLink pageLink) {
log.trace("Executing findDashboardsByTenantIdAndCustomerId, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink);
Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
Validator.validateId(customerId, "Incorrect customerId " + customerId);
Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
Validator.validateId(customerId, id -> "Incorrect customerId " + id);
Validator.validatePageLink(pageLink);
return dashboardInfoDao.findDashboardsByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink);
}
@ -281,8 +281,8 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
@Override
public PageData<DashboardInfo> findMobileDashboardsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, PageLink pageLink) {
log.trace("Executing findMobileDashboardsByTenantIdAndCustomerId, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink);
Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
Validator.validateId(customerId, "Incorrect customerId " + customerId);
Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
Validator.validateId(customerId, id -> "Incorrect customerId " + id);
Validator.validatePageLink(pageLink);
return dashboardInfoDao.findMobileDashboardsByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink);
}
@ -290,7 +290,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
@Override
public void unassignCustomerDashboards(TenantId tenantId, CustomerId customerId) {
log.trace("Executing unassignCustomerDashboards, customerId [{}]", customerId);
Validator.validateId(customerId, "Incorrect customerId " + customerId);
Validator.validateId(customerId, id -> "Incorrect customerId " + id);
Customer customer = customerDao.findById(tenantId, customerId.getId());
if (customer == null) {
throw new DataValidationException("Can't unassign dashboards from non-existent customer!");
@ -301,7 +301,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
@Override
public void updateCustomerDashboards(TenantId tenantId, CustomerId customerId) {
log.trace("Executing updateCustomerDashboards, customerId [{}]", customerId);
Validator.validateId(customerId, "Incorrect customerId " + customerId);
Validator.validateId(customerId, id -> "Incorrect customerId " + id);
Customer customer = customerDao.findById(tenantId, customerId.getId());
if (customer == null) {
throw new DataValidationException("Can't update dashboards for non-existent customer!");
@ -351,8 +351,8 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
@Override
public PageData<DashboardInfo> findDashboardsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink) {
log.trace("Executing findDashboardsByTenantIdAndEdgeId, tenantId [{}], edgeId [{}], pageLink [{}]", tenantId, edgeId, pageLink);
Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
Validator.validateId(edgeId, INCORRECT_EDGE_ID + edgeId);
Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
Validator.validateId(edgeId, id -> INCORRECT_EDGE_ID + id);
Validator.validatePageLink(pageLink);
return dashboardInfoDao.findDashboardsByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink);
}

2
dao/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityServiceImpl.java

@ -88,7 +88,7 @@ public class DeviceConnectivityServiceImpl implements DeviceConnectivityService
public JsonNode findDevicePublishTelemetryCommands(String baseUrl, Device device) throws URISyntaxException {
DeviceId deviceId = device.getId();
log.trace("Executing findDevicePublishTelemetryCommands [{}]", deviceId);
validateId(deviceId, INCORRECT_DEVICE_ID + deviceId);
validateId(deviceId, id -> INCORRECT_DEVICE_ID + id);
DeviceCredentials creds = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getTenantId(), deviceId);
DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(device.getTenantId(), device.getDeviceProfileId());

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

@ -72,14 +72,14 @@ public class DeviceCredentialsServiceImpl extends AbstractCachedEntityService<St
@Override
public DeviceCredentials findDeviceCredentialsByDeviceId(TenantId tenantId, DeviceId deviceId) {
log.trace("Executing findDeviceCredentialsByDeviceId [{}]", deviceId);
validateId(deviceId, "Incorrect deviceId " + deviceId);
validateId(deviceId, id -> "Incorrect deviceId " + id);
return deviceCredentialsDao.findByDeviceId(tenantId, deviceId.getId());
}
@Override
public DeviceCredentials findDeviceCredentialsByCredentialsId(String credentialsId) {
log.trace("Executing findDeviceCredentialsByCredentialsId [{}]", credentialsId);
validateString(credentialsId, "Incorrect credentialsId " + credentialsId);
validateString(credentialsId, id -> "Incorrect credentialsId " + id);
return cache.getAndPutInTransaction(credentialsId,
() -> deviceCredentialsDao.findByCredentialsId(TenantId.SYS_TENANT_ID, credentialsId),
true); // caching null values is essential for permanently invalid requests

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

@ -126,7 +126,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
@Override
public DeviceProfile findDeviceProfileById(TenantId tenantId, DeviceProfileId deviceProfileId, boolean putInCache) {
log.trace("Executing findDeviceProfileById [{}]", deviceProfileId);
validateId(deviceProfileId, INCORRECT_DEVICE_PROFILE_ID + deviceProfileId);
validateId(deviceProfileId, id -> INCORRECT_DEVICE_PROFILE_ID + id);
return cache.getOrFetchFromDB(DeviceProfileCacheKey.fromId(deviceProfileId),
() -> deviceProfileDao.findById(tenantId, deviceProfileId.getId()), true, putInCache);
}
@ -139,7 +139,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
@Override
public DeviceProfile findDeviceProfileByName(TenantId tenantId, String profileName, boolean putInCache) {
log.trace("Executing findDeviceProfileByName [{}][{}]", tenantId, profileName);
validateString(profileName, INCORRECT_DEVICE_PROFILE_NAME + profileName);
validateString(profileName, pn -> INCORRECT_DEVICE_PROFILE_NAME + pn);
return cache.getOrFetchFromDB(DeviceProfileCacheKey.fromName(tenantId, profileName),
() -> deviceProfileDao.findByName(tenantId, profileName), true, putInCache);
}
@ -147,7 +147,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
@Override
public DeviceProfile findDeviceProfileByProvisionDeviceKey(String provisionDeviceKey) {
log.trace("Executing findDeviceProfileByProvisionDeviceKey provisionKey [{}]", provisionDeviceKey);
validateString(provisionDeviceKey, INCORRECT_PROVISION_DEVICE_KEY + provisionDeviceKey);
validateString(provisionDeviceKey, dk -> INCORRECT_PROVISION_DEVICE_KEY + dk);
return cache.getAndPutInTransaction(DeviceProfileCacheKey.fromProvisionDeviceKey(provisionDeviceKey),
() -> deviceProfileDao.findByProvisionDeviceKey(provisionDeviceKey), false);
}
@ -155,7 +155,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
@Override
public DeviceProfileInfo findDeviceProfileInfoById(TenantId tenantId, DeviceProfileId deviceProfileId) {
log.trace("Executing findDeviceProfileById [{}]", deviceProfileId);
validateId(deviceProfileId, INCORRECT_DEVICE_PROFILE_ID + deviceProfileId);
validateId(deviceProfileId, id -> INCORRECT_DEVICE_PROFILE_ID + id);
return toDeviceProfileInfo(findDeviceProfileById(tenantId, deviceProfileId));
}
@ -222,7 +222,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
@Transactional
public void deleteDeviceProfile(TenantId tenantId, DeviceProfileId deviceProfileId) {
log.trace("Executing deleteDeviceProfile [{}]", deviceProfileId);
validateId(deviceProfileId, INCORRECT_DEVICE_PROFILE_ID + deviceProfileId);
validateId(deviceProfileId, id -> INCORRECT_DEVICE_PROFILE_ID + id);
DeviceProfile deviceProfile = deviceProfileDao.findById(tenantId, deviceProfileId.getId());
if (deviceProfile != null && deviceProfile.isDefault()) {
throw new DataValidationException("Deletion of Default Device Profile is prohibited!");
@ -252,7 +252,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
@Override
public PageData<DeviceProfile> findDeviceProfiles(TenantId tenantId, PageLink pageLink) {
log.trace("Executing findDeviceProfiles tenantId [{}], pageLink [{}]", tenantId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
Validator.validatePageLink(pageLink);
return deviceProfileDao.findDeviceProfiles(tenantId, pageLink);
}
@ -260,7 +260,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
@Override
public PageData<DeviceProfileInfo> findDeviceProfileInfos(TenantId tenantId, PageLink pageLink, String transportType) {
log.trace("Executing findDeviceProfileInfos tenantId [{}], pageLink [{}]", tenantId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
Validator.validatePageLink(pageLink);
return deviceProfileDao.findDeviceProfileInfos(tenantId, pageLink, transportType);
}
@ -290,7 +290,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
}
private DeviceProfile doCreateDefaultDeviceProfile(TenantId tenantId, String profileName, boolean defaultProfile, boolean publishSaveEvent) {
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
DeviceProfile deviceProfile = new DeviceProfile();
deviceProfile.setTenantId(tenantId);
deviceProfile.setDefault(defaultProfile);
@ -313,7 +313,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
@Override
public DeviceProfile findDefaultDeviceProfile(TenantId tenantId) {
log.trace("Executing findDefaultDeviceProfile tenantId [{}]", tenantId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
return cache.getAndPutInTransaction(DeviceProfileCacheKey.defaultProfile(tenantId),
() -> deviceProfileDao.findDefaultDeviceProfile(tenantId), true);
}
@ -321,14 +321,14 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
@Override
public DeviceProfileInfo findDefaultDeviceProfileInfo(TenantId tenantId) {
log.trace("Executing findDefaultDeviceProfileInfo tenantId [{}]", tenantId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
return toDeviceProfileInfo(findDefaultDeviceProfile(tenantId));
}
@Override
public boolean setDefaultDeviceProfile(TenantId tenantId, DeviceProfileId deviceProfileId) {
log.trace("Executing setDefaultDeviceProfile [{}]", deviceProfileId);
validateId(deviceProfileId, INCORRECT_DEVICE_PROFILE_ID + deviceProfileId);
validateId(deviceProfileId, id -> INCORRECT_DEVICE_PROFILE_ID + id);
DeviceProfile deviceProfile = deviceProfileDao.findById(tenantId, deviceProfileId.getId());
if (!deviceProfile.isDefault()) {
deviceProfile.setDefault(true);
@ -354,7 +354,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
@Override
public void deleteDeviceProfilesByTenantId(TenantId tenantId) {
log.trace("Executing deleteDeviceProfilesByTenantId, tenantId [{}]", tenantId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
tenantDeviceProfilesRemover.removeEntities(tenantId, tenantId);
}
@ -377,7 +377,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
@Override
public List<EntityInfo> findDeviceProfileNamesByTenantId(TenantId tenantId, boolean activeOnly) {
log.trace("Executing findDeviceProfileNamesByTenantId, tenantId [{}]", tenantId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
return deviceProfileDao.findTenantDeviceProfileNames(tenantId.getId(), activeOnly)
.stream().sorted(Comparator.comparing(EntityInfo::getName))
.collect(Collectors.toList());

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

@ -126,14 +126,14 @@ public class DeviceServiceImpl extends AbstractCachedEntityService<DeviceCacheKe
@Override
public DeviceInfo findDeviceInfoById(TenantId tenantId, DeviceId deviceId) {
log.trace("Executing findDeviceInfoById [{}]", deviceId);
validateId(deviceId, INCORRECT_DEVICE_ID + deviceId);
validateId(deviceId, id -> INCORRECT_DEVICE_ID + id);
return deviceDao.findDeviceInfoById(tenantId, deviceId.getId());
}
@Override
public Device findDeviceById(TenantId tenantId, DeviceId deviceId) {
log.trace("Executing findDeviceById [{}]", deviceId);
validateId(deviceId, INCORRECT_DEVICE_ID + 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);
@ -146,7 +146,7 @@ public class DeviceServiceImpl extends AbstractCachedEntityService<DeviceCacheKe
@Override
public ListenableFuture<Device> findDeviceByIdAsync(TenantId tenantId, DeviceId deviceId) {
log.trace("Executing findDeviceById [{}]", deviceId);
validateId(deviceId, INCORRECT_DEVICE_ID + deviceId);
validateId(deviceId, id -> INCORRECT_DEVICE_ID + id);
if (TenantId.SYS_TENANT_ID.equals(tenantId)) {
return deviceDao.findByIdAsync(tenantId, deviceId.getId());
} else {
@ -157,7 +157,7 @@ public class DeviceServiceImpl extends AbstractCachedEntityService<DeviceCacheKe
@Override
public Device findDeviceByTenantIdAndName(TenantId tenantId, String name) {
log.trace("Executing findDeviceByTenantIdAndName [{}][{}]", tenantId, name);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
return cache.getAndPutInTransaction(new DeviceCacheKey(tenantId, name),
() -> deviceDao.findDeviceByTenantIdAndName(tenantId.getId(), name).orElse(null), true);
}
@ -321,7 +321,7 @@ public class DeviceServiceImpl extends AbstractCachedEntityService<DeviceCacheKe
@Transactional
@Override
public void deleteDevice(final TenantId tenantId, final DeviceId deviceId) {
validateId(deviceId, INCORRECT_DEVICE_ID + deviceId);
validateId(deviceId, id -> INCORRECT_DEVICE_ID + id);
if (entityViewService.existsByTenantIdAndEntityId(tenantId, deviceId)) {
throw new DataValidationException("Can't delete device that has entity views!");
}
@ -347,7 +347,7 @@ public class DeviceServiceImpl extends AbstractCachedEntityService<DeviceCacheKe
@Override
public PageData<Device> findDevicesByTenantId(TenantId tenantId, PageLink pageLink) {
log.trace("Executing findDevicesByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id ->INCORRECT_TENANT_ID + id);
validatePageLink(pageLink);
return deviceDao.findDevicesByTenantId(tenantId.getId(), pageLink);
}
@ -359,7 +359,7 @@ public class DeviceServiceImpl extends AbstractCachedEntityService<DeviceCacheKe
if (filter == null) {
throw new IncorrectParameterException("Filter is empty!");
}
validateId(filter.getTenantId(), INCORRECT_TENANT_ID + filter.getTenantId());
validateId(filter.getTenantId(), id -> INCORRECT_TENANT_ID + id);
validatePageLink(pageLink);
return deviceDao.findDeviceInfosByFilter(filter, pageLink);
@ -375,8 +375,8 @@ public class DeviceServiceImpl extends AbstractCachedEntityService<DeviceCacheKe
@Override
public PageData<Device> findDevicesByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink) {
log.trace("Executing findDevicesByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateString(type, "Incorrect type " + type);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateString(type, t -> "Incorrect type " + t);
validatePageLink(pageLink);
return deviceDao.findDevicesByTenantIdAndType(tenantId.getId(), type, pageLink);
}
@ -388,8 +388,8 @@ public class DeviceServiceImpl extends AbstractCachedEntityService<DeviceCacheKe
PageLink pageLink) {
log.trace("Executing findDevicesByTenantIdAndTypeAndEmptyOtaPackage, tenantId [{}], deviceProfileId [{}], type [{}], pageLink [{}]",
tenantId, deviceProfileId, type, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, INCORRECT_DEVICE_PROFILE_ID + deviceProfileId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(deviceProfileId, id -> INCORRECT_DEVICE_PROFILE_ID + id);
validatePageLink(pageLink);
return deviceDao.findDevicesByTenantIdAndTypeAndEmptyOtaPackage(tenantId.getId(), deviceProfileId.getId(), type, pageLink);
}
@ -397,30 +397,30 @@ public class DeviceServiceImpl extends AbstractCachedEntityService<DeviceCacheKe
@Override
public Long countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType type) {
log.trace("Executing countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage, tenantId [{}], deviceProfileId [{}], type [{}]", tenantId, deviceProfileId, type);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, INCORRECT_DEVICE_PROFILE_ID + deviceProfileId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(deviceProfileId, id -> INCORRECT_DEVICE_PROFILE_ID + id);
return deviceDao.countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(tenantId.getId(), deviceProfileId.getId(), type);
}
@Override
public ListenableFuture<List<Device>> findDevicesByTenantIdAndIdsAsync(TenantId tenantId, List<DeviceId> deviceIds) {
log.trace("Executing findDevicesByTenantIdAndIdsAsync, tenantId [{}], deviceIds [{}]", tenantId, deviceIds);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateIds(deviceIds, "Incorrect deviceIds " + deviceIds);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateIds(deviceIds, ids -> "Incorrect deviceIds " + ids);
return deviceDao.findDevicesByTenantIdAndIdsAsync(tenantId.getId(), toUUIDs(deviceIds));
}
@Override
public List<Device> findDevicesByIds(List<DeviceId> deviceIds) {
log.trace("Executing findDevicesByIdsAsync, deviceIds [{}]", deviceIds);
validateIds(deviceIds, "Incorrect deviceIds " + deviceIds);
validateIds(deviceIds, ids-> "Incorrect deviceIds " + ids);
return deviceDao.findDevicesByIds(toUUIDs(deviceIds));
}
@Override
public ListenableFuture<List<Device>> findDevicesByIdsAsync(List<DeviceId> deviceIds) {
log.trace("Executing findDevicesByIdsAsync, deviceIds [{}]", deviceIds);
validateIds(deviceIds, "Incorrect deviceIds " + deviceIds);
validateIds(deviceIds, ids-> "Incorrect deviceIds " + ids);
return deviceDao.findDevicesByIdsAsync(toUUIDs(deviceIds));
}
@ -428,15 +428,15 @@ public class DeviceServiceImpl extends AbstractCachedEntityService<DeviceCacheKe
@Override
public void deleteDevicesByTenantId(TenantId tenantId) {
log.trace("Executing deleteDevicesByTenantId, tenantId [{}]", tenantId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
tenantDevicesRemover.removeEntities(tenantId, tenantId);
}
@Override
public PageData<Device> findDevicesByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, PageLink pageLink) {
log.trace("Executing findDevicesByTenantIdAndCustomerId, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id);
validatePageLink(pageLink);
return deviceDao.findDevicesByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink);
}
@ -444,9 +444,9 @@ public class DeviceServiceImpl extends AbstractCachedEntityService<DeviceCacheKe
@Override
public PageData<Device> findDevicesByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, PageLink pageLink) {
log.trace("Executing findDevicesByTenantIdAndCustomerIdAndType, tenantId [{}], customerId [{}], type [{}], pageLink [{}]", tenantId, customerId, type, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
validateString(type, "Incorrect type " + type);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id);
validateString(type, t -> "Incorrect type " + t);
validatePageLink(pageLink);
return deviceDao.findDevicesByTenantIdAndCustomerIdAndType(tenantId.getId(), customerId.getId(), type, pageLink);
}
@ -454,9 +454,9 @@ public class DeviceServiceImpl extends AbstractCachedEntityService<DeviceCacheKe
@Override
public ListenableFuture<List<Device>> findDevicesByTenantIdCustomerIdAndIdsAsync(TenantId tenantId, CustomerId customerId, List<DeviceId> deviceIds) {
log.trace("Executing findDevicesByTenantIdCustomerIdAndIdsAsync, tenantId [{}], customerId [{}], deviceIds [{}]", tenantId, customerId, deviceIds);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
validateIds(deviceIds, "Incorrect deviceIds " + deviceIds);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id);
validateIds(deviceIds, ids -> "Incorrect deviceIds " + ids);
return deviceDao.findDevicesByTenantIdCustomerIdAndIdsAsync(tenantId.getId(),
customerId.getId(), toUUIDs(deviceIds));
}
@ -464,8 +464,8 @@ public class DeviceServiceImpl extends AbstractCachedEntityService<DeviceCacheKe
@Override
public void unassignCustomerDevices(TenantId tenantId, CustomerId customerId) {
log.trace("Executing unassignCustomerDevices, tenantId [{}], customerId [{}]", tenantId, customerId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id);
customerDevicesRemover.removeEntities(tenantId, customerId);
}
@ -491,7 +491,7 @@ public class DeviceServiceImpl extends AbstractCachedEntityService<DeviceCacheKe
@Override
public ListenableFuture<List<EntitySubtype>> findDeviceTypesByTenantId(TenantId tenantId) {
log.trace("Executing findDeviceTypesByTenantId, tenantId [{}]", tenantId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
return deviceDao.findTenantDeviceTypesAsync(tenantId.getId());
}
@ -627,8 +627,8 @@ public class DeviceServiceImpl extends AbstractCachedEntityService<DeviceCacheKe
@Override
public PageData<Device> findDevicesByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink) {
log.trace("Executing findDevicesByTenantIdAndEdgeId, tenantId [{}], edgeId [{}], pageLink [{}]", tenantId, edgeId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(edgeId, INCORRECT_EDGE_ID + edgeId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(edgeId, id -> INCORRECT_EDGE_ID + id);
validatePageLink(pageLink);
return deviceDao.findDevicesByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink);
}
@ -636,9 +636,9 @@ public class DeviceServiceImpl extends AbstractCachedEntityService<DeviceCacheKe
@Override
public PageData<Device> findDevicesByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, PageLink pageLink) {
log.trace("Executing findDevicesByTenantIdAndEdgeIdAndType, tenantId [{}], edgeId [{}], type [{}] pageLink [{}]", tenantId, edgeId, type, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(edgeId, INCORRECT_EDGE_ID + edgeId);
validateString(type, "Incorrect type " + type);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(edgeId, id -> INCORRECT_EDGE_ID + id);
validateString(type, t -> "Incorrect type " + t);
validatePageLink(pageLink);
return deviceDao.findDevicesByTenantIdAndEdgeIdAndType(tenantId.getId(), edgeId.getId(), type, pageLink);
}

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

@ -138,28 +138,28 @@ public class EdgeServiceImpl extends AbstractCachedEntityService<EdgeCacheKey, E
@Override
public Edge findEdgeById(TenantId tenantId, EdgeId edgeId) {
log.trace("Executing findEdgeById [{}]", edgeId);
validateId(edgeId, INCORRECT_EDGE_ID + edgeId);
validateId(edgeId, id -> INCORRECT_EDGE_ID + id);
return edgeDao.findById(tenantId, edgeId.getId());
}
@Override
public EdgeInfo findEdgeInfoById(TenantId tenantId, EdgeId edgeId) {
log.trace("Executing findEdgeInfoById [{}]", edgeId);
validateId(edgeId, INCORRECT_EDGE_ID + edgeId);
validateId(edgeId, id -> INCORRECT_EDGE_ID + id);
return edgeDao.findEdgeInfoById(tenantId, edgeId.getId());
}
@Override
public ListenableFuture<Edge> findEdgeByIdAsync(TenantId tenantId, EdgeId edgeId) {
log.trace("Executing findEdgeById [{}]", edgeId);
validateId(edgeId, INCORRECT_EDGE_ID + edgeId);
validateId(edgeId, id -> INCORRECT_EDGE_ID + id);
return edgeDao.findByIdAsync(tenantId, edgeId.getId());
}
@Override
public Edge findEdgeByTenantIdAndName(TenantId tenantId, String name) {
log.trace("Executing findEdgeByTenantIdAndName [{}][{}]", tenantId, name);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
return cache.getAndPutInTransaction(new EdgeCacheKey(tenantId, name),
() -> edgeDao.findEdgeByTenantIdAndName(tenantId.getId(), name)
.orElse(null), true);
@ -221,7 +221,7 @@ public class EdgeServiceImpl extends AbstractCachedEntityService<EdgeCacheKey, E
@Transactional
public void deleteEdge(TenantId tenantId, EdgeId edgeId) {
log.trace("Executing deleteEdge [{}]", edgeId);
validateId(edgeId, INCORRECT_EDGE_ID + edgeId);
validateId(edgeId, id -> INCORRECT_EDGE_ID + id);
Edge edge = edgeDao.findById(tenantId, edgeId.getId());
@ -236,7 +236,7 @@ public class EdgeServiceImpl extends AbstractCachedEntityService<EdgeCacheKey, E
@Override
public PageData<Edge> findEdgesByTenantId(TenantId tenantId, PageLink pageLink) {
log.trace("Executing findEdgesByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validatePageLink(pageLink);
return edgeDao.findEdgesByTenantId(tenantId.getId(), pageLink);
}
@ -244,8 +244,8 @@ public class EdgeServiceImpl extends AbstractCachedEntityService<EdgeCacheKey, E
@Override
public PageData<Edge> findEdgesByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink) {
log.trace("Executing findEdgesByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateString(type, "Incorrect type " + type);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateString(type, t -> "Incorrect type " + t);
validatePageLink(pageLink);
return edgeDao.findEdgesByTenantIdAndType(tenantId.getId(), type, pageLink);
}
@ -253,8 +253,8 @@ public class EdgeServiceImpl extends AbstractCachedEntityService<EdgeCacheKey, E
@Override
public PageData<EdgeInfo> findEdgeInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink) {
log.trace("Executing findEdgeInfosByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateString(type, "Incorrect type " + type);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateString(type, t -> "Incorrect type " + t);
validatePageLink(pageLink);
return edgeDao.findEdgeInfosByTenantIdAndType(tenantId.getId(), type, pageLink);
}
@ -262,7 +262,7 @@ public class EdgeServiceImpl extends AbstractCachedEntityService<EdgeCacheKey, E
@Override
public PageData<EdgeInfo> findEdgeInfosByTenantId(TenantId tenantId, PageLink pageLink) {
log.trace("Executing findEdgeInfosByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validatePageLink(pageLink);
return edgeDao.findEdgeInfosByTenantId(tenantId.getId(), pageLink);
}
@ -270,23 +270,23 @@ public class EdgeServiceImpl extends AbstractCachedEntityService<EdgeCacheKey, E
@Override
public ListenableFuture<List<Edge>> findEdgesByTenantIdAndIdsAsync(TenantId tenantId, List<EdgeId> edgeIds) {
log.trace("Executing findEdgesByTenantIdAndIdsAsync, tenantId [{}], edgeIds [{}]", tenantId, edgeIds);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateIds(edgeIds, "Incorrect edgeIds " + edgeIds);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateIds(edgeIds, ids -> "Incorrect edgeIds " + ids);
return edgeDao.findEdgesByTenantIdAndIdsAsync(tenantId.getId(), toUUIDs(edgeIds));
}
@Override
public void deleteEdgesByTenantId(TenantId tenantId) {
log.trace("Executing deleteEdgesByTenantId, tenantId [{}]", tenantId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
tenantEdgesRemover.removeEntities(tenantId, tenantId);
}
@Override
public PageData<Edge> findEdgesByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, PageLink pageLink) {
log.trace("Executing findEdgesByTenantIdAndCustomerId, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id);
validatePageLink(pageLink);
return edgeDao.findEdgesByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink);
}
@ -294,9 +294,9 @@ public class EdgeServiceImpl extends AbstractCachedEntityService<EdgeCacheKey, E
@Override
public PageData<Edge> findEdgesByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, PageLink pageLink) {
log.trace("Executing findEdgesByTenantIdAndCustomerIdAndType, tenantId [{}], customerId [{}], type [{}], pageLink [{}]", tenantId, customerId, type, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
validateString(type, "Incorrect type " + type);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id);
validateString(type, t -> "Incorrect type " + t);
validatePageLink(pageLink);
return edgeDao.findEdgesByTenantIdAndCustomerIdAndType(tenantId.getId(), customerId.getId(), type, pageLink);
}
@ -304,8 +304,8 @@ public class EdgeServiceImpl extends AbstractCachedEntityService<EdgeCacheKey, E
@Override
public PageData<EdgeInfo> findEdgeInfosByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, PageLink pageLink) {
log.trace("Executing findEdgeInfosByTenantIdAndCustomerId, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id);
validatePageLink(pageLink);
return edgeDao.findEdgeInfosByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink);
}
@ -313,9 +313,9 @@ public class EdgeServiceImpl extends AbstractCachedEntityService<EdgeCacheKey, E
@Override
public PageData<EdgeInfo> findEdgeInfosByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, PageLink pageLink) {
log.trace("Executing findEdgeInfosByTenantIdAndCustomerIdAndType, tenantId [{}], customerId [{}], type [{}], pageLink [{}]", tenantId, customerId, type, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
validateString(type, "Incorrect type " + type);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id);
validateString(type, t -> "Incorrect type " + t);
validatePageLink(pageLink);
return edgeDao.findEdgeInfosByTenantIdAndCustomerIdAndType(tenantId.getId(), customerId.getId(), type, pageLink);
}
@ -323,9 +323,9 @@ public class EdgeServiceImpl extends AbstractCachedEntityService<EdgeCacheKey, E
@Override
public ListenableFuture<List<Edge>> findEdgesByTenantIdCustomerIdAndIdsAsync(TenantId tenantId, CustomerId customerId, List<EdgeId> edgeIds) {
log.trace("Executing findEdgesByTenantIdCustomerIdAndIdsAsync, tenantId [{}], customerId [{}], edgeIds [{}]", tenantId, customerId, edgeIds);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
validateIds(edgeIds, "Incorrect edgeIds " + edgeIds);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id);
validateIds(edgeIds, ids -> "Incorrect edgeIds " + ids);
return edgeDao.findEdgesByTenantIdCustomerIdAndIdsAsync(tenantId.getId(),
customerId.getId(), toUUIDs(edgeIds));
}
@ -333,8 +333,8 @@ public class EdgeServiceImpl extends AbstractCachedEntityService<EdgeCacheKey, E
@Override
public void unassignCustomerEdges(TenantId tenantId, CustomerId customerId) {
log.trace("Executing unassignCustomerEdges, tenantId [{}], customerId [{}]", tenantId, customerId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id);
customerEdgeUnassigner.removeEntities(tenantId, customerId);
}
@ -370,7 +370,7 @@ public class EdgeServiceImpl extends AbstractCachedEntityService<EdgeCacheKey, E
@Override
public ListenableFuture<List<EntitySubtype>> findEdgeTypesByTenantId(TenantId tenantId) {
log.trace("Executing findEdgeTypesByTenantId, tenantId [{}]", tenantId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
ListenableFuture<List<EntitySubtype>> tenantEdgeTypes = edgeDao.findTenantEdgeTypesAsync(tenantId.getId());
return Futures.transform(tenantEdgeTypes,
edgeTypes -> {
@ -400,7 +400,7 @@ public class EdgeServiceImpl extends AbstractCachedEntityService<EdgeCacheKey, E
@Override
public PageData<Edge> findEdgesByTenantIdAndEntityId(TenantId tenantId, EntityId entityId, PageLink pageLink) {
log.trace("Executing findEdgesByTenantIdAndEntityId, tenantId [{}], entityId [{}], pageLink [{}]", tenantId, entityId, pageLink);
Validator.validateId(tenantId, "Incorrect tenantId " + tenantId);
Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validatePageLink(pageLink);
return edgeDao.findEdgesByTenantIdAndEntityId(tenantId.getId(), entityId.getId(), entityId.getEntityType(), pageLink);
}
@ -408,7 +408,7 @@ public class EdgeServiceImpl extends AbstractCachedEntityService<EdgeCacheKey, E
@Override
public PageData<Edge> findEdgesByTenantProfileId(TenantProfileId tenantProfileId, PageLink pageLink) {
log.trace("Executing findEdgesByTenantProfileId, tenantProfileId [{}], pageLink [{}]", tenantProfileId, pageLink);
Validator.validateId(tenantProfileId, "Incorrect tenantProfileId " + tenantProfileId);
Validator.validateId(tenantProfileId, id -> "Incorrect tenantProfileId " + id);
validatePageLink(pageLink);
return edgeDao.findEdgesByTenantProfileId(tenantProfileId.getId(), pageLink);
}

66
dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java

@ -20,6 +20,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.HasCustomerId;
import org.thingsboard.server.common.data.HasEmail;
import org.thingsboard.server.common.data.HasLabel;
@ -34,13 +35,18 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.query.EntityCountQuery;
import org.thingsboard.server.common.data.query.EntityData;
import org.thingsboard.server.common.data.query.EntityDataPageLink;
import org.thingsboard.server.common.data.query.EntityDataQuery;
import org.thingsboard.server.common.data.query.EntityFilterType;
import org.thingsboard.server.common.data.query.EntityKey;
import org.thingsboard.server.common.data.query.EntityListFilter;
import org.thingsboard.server.common.data.query.RelationsQueryFilter;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
import static org.thingsboard.server.common.data.id.EntityId.NULL_UUID;
import static org.thingsboard.server.dao.service.Validator.validateEntityDataPageLink;
@ -67,8 +73,8 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe
@Override
public long countEntitiesByQuery(TenantId tenantId, CustomerId customerId, EntityCountQuery query) {
log.trace("Executing countEntitiesByQuery, tenantId [{}], customerId [{}], query [{}]", tenantId, customerId, query);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id);
validateEntityCountQuery(query);
return this.entityQueryDao.countEntitiesByQuery(tenantId, customerId, query);
}
@ -76,10 +82,56 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe
@Override
public PageData<EntityData> findEntityDataByQuery(TenantId tenantId, CustomerId customerId, EntityDataQuery query) {
log.trace("Executing findEntityDataByQuery, tenantId [{}], customerId [{}], query [{}]", tenantId, customerId, query);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id);
validateEntityDataQuery(query);
return this.entityQueryDao.findEntityDataByQuery(tenantId, customerId, query);
if (EntityFilterType.RELATIONS_QUERY.equals(query.getEntityFilter().getType())
|| EntityFilterType.SINGLE_ENTITY.equals(query.getEntityFilter().getType())
|| StringUtils.isNotEmpty(query.getPageLink().getTextSearch())) {
return this.entityQueryDao.findEntityDataByQuery(tenantId, customerId, query);
}
// 1 step - find entity data by filter and sort columns
PageData<EntityData> entityDataByQuery = findEntityIdsByFilterAndSorterColumns(tenantId, customerId, query);
if (entityDataByQuery == null || entityDataByQuery.getData().isEmpty()) {
return entityDataByQuery;
}
// 2 step - find entity data by entity ids from the 1st step
PageData<EntityData> result = findEntityDataByEntityIds(tenantId, customerId, query, entityDataByQuery.getData());
return new PageData<>(result.getData(), entityDataByQuery.getTotalPages(), entityDataByQuery.getTotalElements(), entityDataByQuery.hasNext());
}
private PageData<EntityData> findEntityIdsByFilterAndSorterColumns(TenantId tenantId, CustomerId customerId, EntityDataQuery query) {
List<EntityKey> entityFields = null;
List<EntityKey> latestValues = null;
if (query.getPageLink().getSortOrder() != null) {
if (query.getEntityFields() != null) {
entityFields = query.getEntityFields().stream()
.filter(entityKey -> entityKey.getKey().equals(query.getPageLink().getSortOrder().getKey().getKey()))
.collect(Collectors.toList());
}
if (query.getLatestValues() != null) {
latestValues = query.getLatestValues().stream()
.filter(entityKey -> entityKey.getKey().equals(query.getPageLink().getSortOrder().getKey().getKey()))
.collect(Collectors.toList());
}
}
EntityDataQuery entityQuery = new EntityDataQuery(query.getEntityFilter(), query.getPageLink(), entityFields, latestValues, query.getKeyFilters());
return this.entityQueryDao.findEntityDataByQuery(tenantId, customerId, entityQuery);
}
private PageData<EntityData> findEntityDataByEntityIds(TenantId tenantId, CustomerId customerId, EntityDataQuery query, List<EntityData> data) {
List<String> entityIds = data.stream().map(d -> d.getEntityId().getId().toString()).toList();
EntityType entityType = data.isEmpty() ? null : data.get(0).getEntityId().getEntityType();
EntityListFilter filter = new EntityListFilter();
filter.setEntityType(entityType);
filter.setEntityList(entityIds);
EntityDataPageLink pageLink = new EntityDataPageLink(query.getPageLink().getPageSize(), 0, null, query.getPageLink().getSortOrder());
EntityDataQuery entityQuery = new EntityDataQuery(filter, pageLink, query.getEntityFields(), query.getLatestValues(), null);
return this.entityQueryDao.findEntityDataByQuery(tenantId, customerId, entityQuery);
}
@Override
@ -133,8 +185,7 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe
}
private CustomerId getCustomerId(HasId<?> entity) {
if (entity instanceof HasCustomerId) {
HasCustomerId hasCustomerId = (HasCustomerId) entity;
if (entity instanceof HasCustomerId hasCustomerId) {
CustomerId customerId = hasCustomerId.getCustomerId();
if (customerId == null) {
customerId = NULL_CUSTOMER_ID;
@ -176,4 +227,5 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe
throw new IncorrectParameterException("Relation query filter root entity should not be blank");
}
}
}

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

@ -145,15 +145,15 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService<EntityVie
@Override
public void unassignCustomerEntityViews(TenantId tenantId, CustomerId customerId) {
log.trace("Executing unassignCustomerEntityViews, tenantId [{}], customerId [{}]", tenantId, customerId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id);
customerEntityViewsUnAssigner.removeEntities(tenantId, customerId);
}
@Override
public EntityViewInfo findEntityViewInfoById(TenantId tenantId, EntityViewId entityViewId) {
log.trace("Executing findEntityViewInfoById [{}]", entityViewId);
validateId(entityViewId, INCORRECT_ENTITY_VIEW_ID + entityViewId);
validateId(entityViewId, id -> INCORRECT_ENTITY_VIEW_ID + id);
return entityViewDao.findEntityViewInfoById(tenantId, entityViewId.getId());
}
@ -165,7 +165,7 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService<EntityVie
@Override
public EntityView findEntityViewById(TenantId tenantId, EntityViewId entityViewId, boolean putInCache) {
log.trace("Executing findEntityViewById [{}]", entityViewId);
validateId(entityViewId, INCORRECT_ENTITY_VIEW_ID + 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);
@ -174,7 +174,7 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService<EntityVie
@Override
public EntityView findEntityViewByTenantIdAndName(TenantId tenantId, String name) {
log.trace("Executing findEntityViewByTenantIdAndName [{}][{}]", tenantId, name);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
return cache.getAndPutInTransaction(EntityViewCacheKey.byName(tenantId, name),
() -> entityViewDao.findEntityViewByTenantIdAndName(tenantId.getId(), name).orElse(null)
, EntityViewCacheValue::getEntityView, v -> new EntityViewCacheValue(v, null), true);
@ -184,7 +184,7 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService<EntityVie
@Override
public PageData<EntityView> findEntityViewByTenantId(TenantId tenantId, PageLink pageLink) {
log.trace("Executing findEntityViewsByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validatePageLink(pageLink);
return entityViewDao.findEntityViewsByTenantId(tenantId.getId(), pageLink);
}
@ -192,7 +192,7 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService<EntityVie
@Override
public PageData<EntityViewInfo> findEntityViewInfosByTenantId(TenantId tenantId, PageLink pageLink) {
log.trace("Executing findEntityViewInfosByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validatePageLink(pageLink);
return entityViewDao.findEntityViewInfosByTenantId(tenantId.getId(), pageLink);
}
@ -200,18 +200,18 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService<EntityVie
@Override
public PageData<EntityView> findEntityViewByTenantIdAndType(TenantId tenantId, PageLink pageLink, String type) {
log.trace("Executing findEntityViewByTenantIdAndType, tenantId [{}], pageLink [{}], type [{}]", tenantId, pageLink, type);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validatePageLink(pageLink);
validateString(type, "Incorrect type " + type);
validateString(type, t -> "Incorrect type " + t);
return entityViewDao.findEntityViewsByTenantIdAndType(tenantId.getId(), type, pageLink);
}
@Override
public PageData<EntityViewInfo> findEntityViewInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink) {
log.trace("Executing findEntityViewInfosByTenantIdAndType, tenantId [{}], pageLink [{}], type [{}]", tenantId, pageLink, type);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validatePageLink(pageLink);
validateString(type, "Incorrect type " + type);
validateString(type, t -> "Incorrect type " + t);
return entityViewDao.findEntityViewInfosByTenantIdAndType(tenantId.getId(), type, pageLink);
}
@ -220,8 +220,8 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService<EntityVie
PageLink pageLink) {
log.trace("Executing findEntityViewByTenantIdAndCustomerId, tenantId [{}], customerId [{}]," +
" pageLink [{}]", tenantId, customerId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
validateId(tenantId, id ->INCORRECT_TENANT_ID + id);
validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id);
validatePageLink(pageLink);
return entityViewDao.findEntityViewsByTenantIdAndCustomerId(tenantId.getId(),
customerId.getId(), pageLink);
@ -231,8 +231,8 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService<EntityVie
public PageData<EntityViewInfo> findEntityViewInfosByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, PageLink pageLink) {
log.trace("Executing findEntityViewInfosByTenantIdAndCustomerId, tenantId [{}], customerId [{}]," +
" pageLink [{}]", tenantId, customerId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id);
validatePageLink(pageLink);
return entityViewDao.findEntityViewInfosByTenantIdAndCustomerId(tenantId.getId(),
customerId.getId(), pageLink);
@ -242,10 +242,10 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService<EntityVie
public PageData<EntityView> findEntityViewsByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, PageLink pageLink, String type) {
log.trace("Executing findEntityViewsByTenantIdAndCustomerIdAndType, tenantId [{}], customerId [{}]," +
" pageLink [{}], type [{}]", tenantId, customerId, pageLink, type);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id);
validatePageLink(pageLink);
validateString(type, "Incorrect type " + type);
validateString(type, t -> "Incorrect type " + t);
return entityViewDao.findEntityViewsByTenantIdAndCustomerIdAndType(tenantId.getId(),
customerId.getId(), type, pageLink);
}
@ -254,10 +254,10 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService<EntityVie
public PageData<EntityViewInfo> findEntityViewInfosByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, PageLink pageLink) {
log.trace("Executing findEntityViewInfosByTenantIdAndCustomerIdAndType, tenantId [{}], customerId [{}]," +
" pageLink [{}], type [{}]", tenantId, customerId, pageLink, type);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id);
validatePageLink(pageLink);
validateString(type, "Incorrect type " + type);
validateString(type, t -> "Incorrect type " + t);
return entityViewDao.findEntityViewInfosByTenantIdAndCustomerIdAndType(tenantId.getId(),
customerId.getId(), type, pageLink);
}
@ -291,15 +291,15 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService<EntityVie
@Override
public ListenableFuture<EntityView> findEntityViewByIdAsync(TenantId tenantId, EntityViewId entityViewId) {
log.trace("Executing findEntityViewById [{}]", entityViewId);
validateId(entityViewId, INCORRECT_ENTITY_VIEW_ID + entityViewId);
validateId(entityViewId, id -> INCORRECT_ENTITY_VIEW_ID + id);
return entityViewDao.findByIdAsync(tenantId, entityViewId.getId());
}
@Override
public ListenableFuture<List<EntityView>> findEntityViewsByTenantIdAndEntityIdAsync(TenantId tenantId, EntityId entityId) {
log.trace("Executing findEntityViewsByTenantIdAndEntityIdAsync, tenantId [{}], entityId [{}]", tenantId, entityId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(entityId.getId(), "Incorrect entityId" + entityId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(entityId.getId(), id -> "Incorrect entityId" + id);
return service.submit(() -> cache.getAndPutInTransaction(EntityViewCacheKey.byEntityId(tenantId, entityId),
() -> entityViewDao.findEntityViewsByTenantIdAndEntityId(tenantId.getId(), entityId.getId()),
@ -309,8 +309,8 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService<EntityVie
@Override
public List<EntityView> findEntityViewsByTenantIdAndEntityId(TenantId tenantId, EntityId entityId) {
log.trace("Executing findEntityViewsByTenantIdAndEntityId, tenantId [{}], entityId [{}]", tenantId, entityId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(entityId.getId(), "Incorrect entityId" + entityId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(entityId.getId(), id -> "Incorrect entityId" + id);
return cache.getAndPutInTransaction(EntityViewCacheKey.byEntityId(tenantId, entityId),
() -> entityViewDao.findEntityViewsByTenantIdAndEntityId(tenantId.getId(), entityId.getId()),
@ -326,7 +326,7 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService<EntityVie
@Transactional
public void deleteEntityView(TenantId tenantId, EntityViewId entityViewId) {
log.trace("Executing deleteEntityView [{}]", entityViewId);
validateId(entityViewId, INCORRECT_ENTITY_VIEW_ID + entityViewId);
validateId(entityViewId, id -> INCORRECT_ENTITY_VIEW_ID + id);
deleteEntityRelations(tenantId, entityViewId);
EntityView entityView = entityViewDao.findById(tenantId, entityViewId.getId());
entityViewDao.removeById(tenantId, entityViewId.getId());
@ -337,14 +337,14 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService<EntityVie
@Override
public void deleteEntityViewsByTenantId(TenantId tenantId) {
log.trace("Executing deleteEntityViewsByTenantId, tenantId [{}]", tenantId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
tenantEntityViewRemover.removeEntities(tenantId, tenantId);
}
@Override
public ListenableFuture<List<EntitySubtype>> findEntityViewTypesByTenantId(TenantId tenantId) {
log.trace("Executing findEntityViewTypesByTenantId, tenantId [{}]", tenantId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
ListenableFuture<List<EntitySubtype>> tenantEntityViewTypes = entityViewDao.findTenantEntityViewTypesAsync(tenantId.getId());
return Futures.transform(tenantEntityViewTypes,
entityViewTypes -> {
@ -402,8 +402,8 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService<EntityVie
@Override
public PageData<EntityView> findEntityViewsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink) {
log.trace("Executing findEntityViewsByTenantIdAndEdgeId, tenantId [{}], edgeId [{}], pageLink [{}]", tenantId, edgeId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(edgeId, INCORRECT_EDGE_ID + edgeId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(edgeId, id -> INCORRECT_EDGE_ID + id);
validatePageLink(pageLink);
return entityViewDao.findEntityViewsByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink);
}
@ -411,9 +411,9 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService<EntityVie
@Override
public PageData<EntityView> findEntityViewsByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, PageLink pageLink) {
log.trace("Executing findEntityViewsByTenantIdAndEdgeIdAndType, tenantId [{}], edgeId [{}], type [{}], pageLink [{}]", tenantId, edgeId, type, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(edgeId, INCORRECT_EDGE_ID + edgeId);
validateString(type, "Incorrect type " + type);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(edgeId, id -> INCORRECT_EDGE_ID + id);
validateString(type, t -> "Incorrect type " + t);
validatePageLink(pageLink);
return entityViewDao.findEntityViewsByTenantIdAndEdgeIdAndType(tenantId.getId(), edgeId.getId(), type, pageLink);
}

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

@ -427,6 +427,7 @@ public class ModelConstants {
*/
public static final String OAUTH2_PARAMS_TABLE_NAME = "oauth2_params";
public static final String OAUTH2_PARAMS_ENABLED_PROPERTY = "enabled";
public static final String OAUTH2_PARAMS_EDGE_ENABLED_PROPERTY = "edge_enabled";
public static final String OAUTH2_PARAMS_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY;
public static final String OAUTH2_REGISTRATION_TABLE_NAME = "oauth2_registration";
@ -610,6 +611,14 @@ public class ModelConstants {
public static final String QUEUE_TABLE_NAME = "queue";
public static final String QUEUE_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY;
/**
* Tenant queue stats constants.
*/
public static final String QUEUE_STATS_TABLE_NAME = "queue_stats";
public static final String QUEUE_STATS_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY;
public static final String QUEUE_STATS_QUEUE_NAME_PROPERTY = "queue_name";
public static final String QUEUE_STATS_SERVICE_ID_PROPERTY = "service_id";
/**
* Notification constants
*/

5
dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ParamsEntity.java

@ -39,6 +39,9 @@ public class OAuth2ParamsEntity extends BaseSqlEntity<OAuth2Params> {
@Column(name = ModelConstants.OAUTH2_PARAMS_ENABLED_PROPERTY)
private Boolean enabled;
@Column(name = ModelConstants.OAUTH2_PARAMS_EDGE_ENABLED_PROPERTY)
private Boolean edgeEnabled;
@Column(name = ModelConstants.OAUTH2_PARAMS_TENANT_ID_PROPERTY)
private UUID tenantId;
@ -48,6 +51,7 @@ public class OAuth2ParamsEntity extends BaseSqlEntity<OAuth2Params> {
}
this.setCreatedTime(oauth2Params.getCreatedTime());
this.enabled = oauth2Params.isEnabled();
this.edgeEnabled = oauth2Params.isEdgeEnabled();
if (oauth2Params.getTenantId() != null) {
this.tenantId = oauth2Params.getTenantId().getId();
}
@ -60,6 +64,7 @@ public class OAuth2ParamsEntity extends BaseSqlEntity<OAuth2Params> {
oauth2Params.setCreatedTime(createdTime);
oauth2Params.setTenantId(TenantId.fromUUID(tenantId));
oauth2Params.setEnabled(enabled);
oauth2Params.setEdgeEnabled(edgeEnabled);
return oauth2Params;
}
}

69
dao/src/main/java/org/thingsboard/server/dao/model/sql/QueueStatsEntity.java

@ -0,0 +1,69 @@
/**
* 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.model.sql;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.id.QueueStatsId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.queue.QueueStats;
import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.ModelConstants;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import java.util.UUID;
@Data
@EqualsAndHashCode(callSuper = true)
@Entity
@Table(name = ModelConstants.QUEUE_STATS_TABLE_NAME)
public class QueueStatsEntity extends BaseSqlEntity<QueueStats> {
@Column(name = ModelConstants.QUEUE_STATS_TENANT_ID_PROPERTY)
private UUID tenantId;
@Column(name = ModelConstants.QUEUE_STATS_QUEUE_NAME_PROPERTY)
private String queueName;
@Column(name = ModelConstants.QUEUE_STATS_SERVICE_ID_PROPERTY)
private String serviceId;
public QueueStatsEntity() {
}
public QueueStatsEntity(QueueStats queueStats) {
if (queueStats.getId() != null) {
this.setId(queueStats.getId().getId());
}
this.setCreatedTime(queueStats.getCreatedTime());
this.tenantId = DaoUtil.getId(queueStats.getTenantId());
this.queueName = queueStats.getQueueName();
this.serviceId = queueStats.getServiceId();
}
@Override
public QueueStats toData() {
QueueStats queueStats = new QueueStats(new QueueStatsId(getUuid()));
queueStats.setCreatedTime(createdTime);
queueStats.setTenantId(new TenantId(tenantId));
queueStats.setQueueName(queueName);
queueStats.setServiceId(serviceId);
return queueStats;
}
}

6
dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ConfigTemplateServiceImpl.java

@ -64,14 +64,14 @@ public class OAuth2ConfigTemplateServiceImpl extends AbstractEntityService imple
@Override
public Optional<OAuth2ClientRegistrationTemplate> findClientRegistrationTemplateByProviderId(String providerId) {
log.trace("Executing findClientRegistrationTemplateByProviderId [{}]", providerId);
validateString(providerId, INCORRECT_CLIENT_REGISTRATION_PROVIDER_ID + providerId);
validateString(providerId, id -> INCORRECT_CLIENT_REGISTRATION_PROVIDER_ID + id);
return clientRegistrationTemplateDao.findByProviderId(providerId);
}
@Override
public OAuth2ClientRegistrationTemplate findClientRegistrationTemplateById(OAuth2ClientRegistrationTemplateId templateId) {
log.trace("Executing findClientRegistrationTemplateById [{}]", templateId);
validateId(templateId, INCORRECT_CLIENT_REGISTRATION_TEMPLATE_ID + templateId);
validateId(templateId, id -> INCORRECT_CLIENT_REGISTRATION_TEMPLATE_ID + id);
return clientRegistrationTemplateDao.findById(TenantId.SYS_TENANT_ID, templateId.getId());
}
@ -84,7 +84,7 @@ public class OAuth2ConfigTemplateServiceImpl extends AbstractEntityService imple
@Override
public void deleteClientRegistrationTemplateById(OAuth2ClientRegistrationTemplateId templateId) {
log.trace("Executing deleteClientRegistrationTemplateById [{}]", templateId);
validateId(templateId, INCORRECT_CLIENT_REGISTRATION_TEMPLATE_ID + templateId);
validateId(templateId, id -> INCORRECT_CLIENT_REGISTRATION_TEMPLATE_ID + id);
clientRegistrationTemplateDao.removeById(TenantId.SYS_TENANT_ID, templateId.getId());
}
}

12
dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java

@ -40,6 +40,7 @@ import org.thingsboard.server.common.data.oauth2.PlatformType;
import org.thingsboard.server.common.data.oauth2.SchemeType;
import org.thingsboard.server.common.data.oauth2.TenantNameStrategyType;
import org.thingsboard.server.dao.entity.AbstractEntityService;
import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
@ -58,6 +59,7 @@ import static org.thingsboard.server.dao.service.Validator.validateString;
@Slf4j
@Service
public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Service {
public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
public static final String INCORRECT_CLIENT_REGISTRATION_ID = "Incorrect clientRegistrationId ";
public static final String INCORRECT_DOMAIN_NAME = "Incorrect domainName ";
@ -84,7 +86,7 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se
} catch (IllegalArgumentException e){
throw new IncorrectParameterException(INCORRECT_DOMAIN_SCHEME);
}
validateString(domainName, INCORRECT_DOMAIN_NAME + domainName);
validateString(domainName, dn -> INCORRECT_DOMAIN_NAME + dn);
return oauth2RegistrationDao.findEnabledByDomainSchemesDomainNameAndPkgNameAndPlatformType(
Arrays.asList(domainScheme, SchemeType.MIXED), domainName, pkgName, platformType)
.stream()
@ -116,6 +118,7 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se
});
}
});
eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(TenantId.SYS_TENANT_ID).entity(oauth2Info).build());
}
@Override
@ -123,7 +126,8 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se
log.trace("Executing findOAuth2Info");
OAuth2Info oauth2Info = new OAuth2Info();
List<OAuth2Params> oauth2ParamsList = oauth2ParamsDao.find(TenantId.SYS_TENANT_ID);
oauth2Info.setEnabled(oauth2ParamsList.stream().anyMatch(param -> param.isEnabled()));
oauth2Info.setEnabled(oauth2ParamsList.stream().anyMatch(OAuth2Params::isEnabled));
oauth2Info.setEdgeEnabled(oauth2ParamsList.stream().anyMatch(OAuth2Params::isEdgeEnabled));
List<OAuth2ParamsInfo> oauth2ParamsInfos = new ArrayList<>();
oauth2Info.setOauth2ParamsInfos(oauth2ParamsInfos);
oauth2ParamsList.stream().sorted(Comparator.comparing(BaseData::getUuidId)).forEach(oauth2Params -> {
@ -138,14 +142,14 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se
@Override
public OAuth2Registration findRegistration(UUID id) {
log.trace("Executing findRegistration [{}]", id);
validateId(id, INCORRECT_CLIENT_REGISTRATION_ID + id);
validateId(id, uuid -> INCORRECT_CLIENT_REGISTRATION_ID + uuid);
return oauth2RegistrationDao.findById(null, id);
}
@Override
public String findAppSecret(UUID id, String pkgName) {
log.trace("Executing findAppSecret [{}][{}]", id, pkgName);
validateId(id, INCORRECT_CLIENT_REGISTRATION_ID + id);
validateId(id, uuid -> INCORRECT_CLIENT_REGISTRATION_ID + uuid);
validateString(pkgName, "Incorrect package name");
return oauth2RegistrationDao.findAppSecret(id, pkgName);
}

1
dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java

@ -88,6 +88,7 @@ public class OAuth2Utils {
public static OAuth2Params infoToOAuth2Params(OAuth2Info oauth2Info) {
OAuth2Params oauth2Params = new OAuth2Params();
oauth2Params.setEnabled(oauth2Info.isEnabled());
oauth2Params.setEdgeEnabled(oauth2Info.isEdgeEnabled());
oauth2Params.setTenantId(TenantId.SYS_TENANT_ID);
return oauth2Params;
}

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

Loading…
Cancel
Save