Browse Source

Merge branch 'master' of github.com:thingsboard/thingsboard into feature/node_copy_from_metadata_to_msg

pull/7240/head
Yuriy Lytvynchuk 4 years ago
parent
commit
cfc85f3f4b
  1. 2
      application/pom.xml
  2. 234
      application/src/main/data/upgrade/3.4.0/schema_update.sql
  3. 159
      application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
  4. 3
      application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
  5. 18
      application/src/main/java/org/thingsboard/server/actors/stats/StatsActor.java
  6. 2
      application/src/main/java/org/thingsboard/server/config/CustomOAuth2AuthorizationRequestResolver.java
  7. 2
      application/src/main/java/org/thingsboard/server/config/RateLimitProcessingFilter.java
  8. 8
      application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java
  9. 2
      application/src/main/java/org/thingsboard/server/controller/AbstractRpcController.java
  10. 2
      application/src/main/java/org/thingsboard/server/controller/AlarmController.java
  11. 2
      application/src/main/java/org/thingsboard/server/controller/AuditLogController.java
  12. 6
      application/src/main/java/org/thingsboard/server/controller/BaseController.java
  13. 2
      application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java
  14. 2
      application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java
  15. 2
      application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java
  16. 75
      application/src/main/java/org/thingsboard/server/controller/EventController.java
  17. 9
      application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
  18. 2
      application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
  19. 9
      application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java
  20. 8
      application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java
  21. 4
      application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java
  22. 5
      application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java
  23. 2
      application/src/main/java/org/thingsboard/server/service/apiusage/DefaultRateLimitService.java
  24. 13
      application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java
  25. 2
      application/src/main/java/org/thingsboard/server/service/device/ClaimDevicesServiceImpl.java
  26. 9
      application/src/main/java/org/thingsboard/server/service/device/DeviceBulkImportService.java
  27. 7
      application/src/main/java/org/thingsboard/server/service/device/DeviceProvisionServiceImpl.java
  28. 4
      application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java
  29. 1
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java
  30. 14
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceMsgConstructor.java
  31. 6
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceProfileMsgConstructor.java
  32. 7
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/OtaPackageMsgConstructor.java
  33. 2
      application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/AdminSettingsEdgeEventFetcher.java
  34. 24
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/DeviceEdgeProcessor.java
  35. 2
      application/src/main/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewService.java
  36. 3
      application/src/main/java/org/thingsboard/server/service/install/DatabaseHelper.java
  37. 3
      application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java
  38. 14
      application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java
  39. 2
      application/src/main/java/org/thingsboard/server/service/install/SqlTsDatabaseUpgradeService.java
  40. 2
      application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseUpgradeService.java
  41. 2
      application/src/main/java/org/thingsboard/server/service/install/migrate/CassandraTsLatestToSqlMigrateService.java
  42. 27
      application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java
  43. 2
      application/src/main/java/org/thingsboard/server/service/install/update/RateLimitsUpdater.java
  44. 6
      application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java
  45. 57
      application/src/main/java/org/thingsboard/server/service/partition/AbstractPartitionBasedService.java
  46. 6
      application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java
  47. 5
      application/src/main/java/org/thingsboard/server/service/queue/DefaultTenantRoutingInfoService.java
  48. 2
      application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java
  49. 2
      application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java
  50. 2
      application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenProcessingFilter.java
  51. 2
      application/src/main/java/org/thingsboard/server/service/security/auth/jwt/extractor/JwtHeaderTokenExtractor.java
  52. 2
      application/src/main/java/org/thingsboard/server/service/security/auth/jwt/extractor/JwtQueryTokenExtractor.java
  53. 8
      application/src/main/java/org/thingsboard/server/service/security/auth/mfa/DefaultTwoFactorAuthService.java
  54. 4
      application/src/main/java/org/thingsboard/server/service/security/auth/mfa/provider/impl/BackupCodeTwoFaProvider.java
  55. 7
      application/src/main/java/org/thingsboard/server/service/security/auth/mfa/provider/impl/OtpBasedTwoFaProvider.java
  56. 4
      application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java
  57. 2
      application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AppleOAuth2ClientMapper.java
  58. 2
      application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicMapperUtils.java
  59. 2
      application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java
  60. 3
      application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestLoginProcessingFilter.java
  61. 2
      application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestPublicLoginProcessingFilter.java
  62. 2
      application/src/main/java/org/thingsboard/server/service/security/model/token/JwtTokenFactory.java
  63. 2
      application/src/main/java/org/thingsboard/server/service/security/model/token/OAuth2AppTokenFactory.java
  64. 4
      application/src/main/java/org/thingsboard/server/service/security/system/DefaultSystemSecurityService.java
  65. 4
      application/src/main/java/org/thingsboard/server/service/sms/aws/AwsSmsSender.java
  66. 2
      application/src/main/java/org/thingsboard/server/service/sms/smpp/SmppSmsSender.java
  67. 6
      application/src/main/java/org/thingsboard/server/service/sms/twilio/TwilioSmsSender.java
  68. 308
      application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java
  69. 39
      application/src/main/java/org/thingsboard/server/service/subscription/DefaultSubscriptionManagerService.java
  70. 293
      application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java
  71. 29
      application/src/main/java/org/thingsboard/server/service/subscription/ReadTsKvQueryInfo.java
  72. 25
      application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractDataSubCtx.java
  73. 13
      application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractSubCtx.java
  74. 2
      application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java
  75. 2
      application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java
  76. 4
      application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java
  77. 8
      application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/AggHistoryCmd.java
  78. 32
      application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/AggKey.java
  79. 19
      application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/AggTimeSeriesCmd.java
  80. 21
      application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/EntityDataCmd.java
  81. 29
      application/src/main/java/org/thingsboard/server/service/ttl/EventsCleanUpService.java
  82. 29
      application/src/main/resources/thingsboard.yml
  83. 3
      application/src/test/java/org/thingsboard/server/actors/stats/StatsActorTest.java
  84. 17
      application/src/test/java/org/thingsboard/server/controller/AbstractRuleEngineControllerTest.java
  85. 67
      application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java
  86. 25
      application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java
  87. 51
      application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java
  88. 43
      application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java
  89. 64
      application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java
  90. 50
      application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java
  91. 28
      application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java
  92. 48
      application/src/test/java/org/thingsboard/server/controller/BaseEdgeControllerTest.java
  93. 29
      application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java
  94. 8
      application/src/test/java/org/thingsboard/server/controller/BaseOtaPackageControllerTest.java
  95. 29
      application/src/test/java/org/thingsboard/server/controller/BaseRuleChainControllerTest.java
  96. 4
      application/src/test/java/org/thingsboard/server/controller/BaseTbResourceControllerTest.java
  97. 8
      application/src/test/java/org/thingsboard/server/controller/BaseTenantControllerTest.java
  98. 4
      application/src/test/java/org/thingsboard/server/controller/BaseTenantProfileControllerTest.java
  99. 53
      application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java
  100. 4
      application/src/test/java/org/thingsboard/server/controller/BaseWidgetsBundleControllerTest.java

2
application/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.4.1-SNAPSHOT</version>
<version>3.4.2-SNAPSHOT</version>
<artifactId>thingsboard</artifactId>
</parent>
<artifactId>application</artifactId>

234
application/src/main/data/upgrade/3.4.0/schema_update.sql

@ -0,0 +1,234 @@
--
-- Copyright © 2016-2022 The Thingsboard Authors
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
CREATE TABLE IF NOT EXISTS rule_node_debug_event (
id uuid NOT NULL,
tenant_id uuid NOT NULL ,
ts bigint NOT NULL,
entity_id uuid NOT NULL,
service_id varchar,
e_type varchar,
e_entity_id uuid,
e_entity_type varchar,
e_msg_id uuid,
e_msg_type varchar,
e_data_type varchar,
e_relation_type varchar,
e_data varchar,
e_metadata varchar,
e_error varchar
) PARTITION BY RANGE (ts);
CREATE TABLE IF NOT EXISTS rule_chain_debug_event (
id uuid NOT NULL,
tenant_id uuid NOT NULL,
ts bigint NOT NULL,
entity_id uuid NOT NULL,
service_id varchar NOT NULL,
e_message varchar,
e_error varchar
) PARTITION BY RANGE (ts);
CREATE TABLE IF NOT EXISTS stats_event (
id uuid NOT NULL,
tenant_id uuid NOT NULL,
ts bigint NOT NULL,
entity_id uuid NOT NULL,
service_id varchar NOT NULL,
e_messages_processed bigint NOT NULL,
e_errors_occurred bigint NOT NULL
) PARTITION BY RANGE (ts);
CREATE TABLE IF NOT EXISTS lc_event (
id uuid NOT NULL,
tenant_id uuid NOT NULL,
ts bigint NOT NULL,
entity_id uuid NOT NULL,
service_id varchar NOT NULL,
e_type varchar NOT NULL,
e_success boolean NOT NULL,
e_error varchar
) PARTITION BY RANGE (ts);
CREATE TABLE IF NOT EXISTS error_event (
id uuid NOT NULL,
tenant_id uuid NOT NULL,
ts bigint NOT NULL,
entity_id uuid NOT NULL,
service_id varchar NOT NULL,
e_method varchar NOT NULL,
e_error varchar
) PARTITION BY RANGE (ts);
CREATE INDEX IF NOT EXISTS idx_rule_node_debug_event_main
ON rule_node_debug_event (tenant_id ASC, entity_id ASC, ts DESC NULLS LAST) WITH (FILLFACTOR=95);
CREATE INDEX IF NOT EXISTS idx_rule_chain_debug_event_main
ON rule_chain_debug_event (tenant_id ASC, entity_id ASC, ts DESC NULLS LAST) WITH (FILLFACTOR=95);
CREATE INDEX IF NOT EXISTS idx_stats_event_main
ON stats_event (tenant_id ASC, entity_id ASC, ts DESC NULLS LAST) WITH (FILLFACTOR=95);
CREATE INDEX IF NOT EXISTS idx_lc_event_main
ON lc_event (tenant_id ASC, entity_id ASC, ts DESC NULLS LAST) WITH (FILLFACTOR=95);
CREATE INDEX IF NOT EXISTS idx_error_event_main
ON error_event (tenant_id ASC, entity_id ASC, ts DESC NULLS LAST) WITH (FILLFACTOR=95);
CREATE OR REPLACE FUNCTION to_safe_json(p_json text) RETURNS json
LANGUAGE plpgsql AS
$$
BEGIN
return REPLACE(p_json, '\u0000', '' )::json;
EXCEPTION
WHEN OTHERS THEN
return '{}'::json;
END;
$$;
-- Useful to migrate old events to the new table structure;
CREATE OR REPLACE PROCEDURE migrate_regular_events(IN start_ts_in_ms bigint, IN end_ts_in_ms bigint, IN partition_size_in_hours int)
LANGUAGE plpgsql AS
$$
DECLARE
partition_size_in_ms bigint;
p record;
table_name varchar;
BEGIN
partition_size_in_ms = partition_size_in_hours * 3600 * 1000;
FOR p IN SELECT DISTINCT event_type as event_type, (created_time - created_time % partition_size_in_ms) as partition_ts FROM event e WHERE e.event_type in ('STATS', 'LC_EVENT', 'ERROR') and ts >= start_ts_in_ms and ts < end_ts_in_ms
LOOP
IF p.event_type = 'STATS' THEN
table_name := 'stats_event';
ELSEIF p.event_type = 'LC_EVENT' THEN
table_name := 'lc_event';
ELSEIF p.event_type = 'ERROR' THEN
table_name := 'error_event';
END IF;
RAISE NOTICE '[%] Partition to create : [%-%]', table_name, p.partition_ts, (p.partition_ts + partition_size_in_ms);
EXECUTE format('CREATE TABLE IF NOT EXISTS %s_%s PARTITION OF %s FOR VALUES FROM ( %s ) TO ( %s )', table_name, p.partition_ts, table_name, p.partition_ts, (p.partition_ts + partition_size_in_ms));
END LOOP;
INSERT INTO stats_event
SELECT id,
tenant_id,
ts,
entity_id,
body ->> 'server',
(body ->> 'messagesProcessed')::bigint,
(body ->> 'errorsOccurred')::bigint
FROM
(select id, tenant_id, ts, entity_id, to_safe_json(body) as body
FROM event WHERE ts >= start_ts_in_ms and ts < end_ts_in_ms AND event_type = 'STATS' AND to_safe_json(body) ->> 'server' IS NOT NULL
) safe_event
ON CONFLICT DO NOTHING;
INSERT INTO lc_event
SELECT id,
tenant_id,
ts,
entity_id,
body ->> 'server',
body ->> 'event',
(body ->> 'success')::boolean,
body ->> 'error'
FROM
(select id, tenant_id, ts, entity_id, to_safe_json(body) as body
FROM event WHERE ts >= start_ts_in_ms and ts < end_ts_in_ms AND event_type = 'LC_EVENT' AND to_safe_json(body) ->> 'server' IS NOT NULL
) safe_event
ON CONFLICT DO NOTHING;
INSERT INTO error_event
SELECT id,
tenant_id,
ts,
entity_id,
body ->> 'server',
body ->> 'method',
body ->> 'error'
FROM
(select id, tenant_id, ts, entity_id, to_safe_json(body) as body
FROM event WHERE ts >= start_ts_in_ms and ts < end_ts_in_ms AND event_type = 'ERROR' AND to_safe_json(body) ->> 'server' IS NOT NULL
) safe_event
ON CONFLICT DO NOTHING;
END
$$;
-- Useful to migrate old debug events to the new table structure;
CREATE OR REPLACE PROCEDURE migrate_debug_events(IN start_ts_in_ms bigint, IN end_ts_in_ms bigint, IN partition_size_in_hours int)
LANGUAGE plpgsql AS
$$
DECLARE
partition_size_in_ms bigint;
p record;
table_name varchar;
BEGIN
partition_size_in_ms = partition_size_in_hours * 3600 * 1000;
FOR p IN SELECT DISTINCT event_type as event_type, (created_time - created_time % partition_size_in_ms) as partition_ts FROM event e WHERE e.event_type in ('DEBUG_RULE_NODE', 'DEBUG_RULE_CHAIN') and ts >= start_ts_in_ms and ts < end_ts_in_ms
LOOP
IF p.event_type = 'DEBUG_RULE_NODE' THEN
table_name := 'rule_node_debug_event';
ELSEIF p.event_type = 'DEBUG_RULE_CHAIN' THEN
table_name := 'rule_chain_debug_event';
END IF;
RAISE NOTICE '[%] Partition to create : [%-%]', table_name, p.partition_ts, (p.partition_ts + partition_size_in_ms);
EXECUTE format('CREATE TABLE IF NOT EXISTS %s_%s PARTITION OF %s FOR VALUES FROM ( %s ) TO ( %s )', table_name, p.partition_ts, table_name, p.partition_ts, (p.partition_ts + partition_size_in_ms));
END LOOP;
INSERT INTO rule_node_debug_event
SELECT id,
tenant_id,
ts,
entity_id,
body ->> 'server',
body ->> 'type',
(body ->> 'entityId')::uuid,
body ->> 'entityName',
(body ->> 'msgId')::uuid,
body ->> 'msgType',
body ->> 'dataType',
body ->> 'relationType',
body ->> 'data',
body ->> 'metadata',
body ->> 'error'
FROM
(select id, tenant_id, ts, entity_id, to_safe_json(body) as body
FROM event WHERE ts >= start_ts_in_ms and ts < end_ts_in_ms AND event_type = 'DEBUG_RULE_NODE' AND to_safe_json(body) ->> 'server' IS NOT NULL
) safe_event
ON CONFLICT DO NOTHING;
INSERT INTO rule_chain_debug_event
SELECT id,
tenant_id,
ts,
entity_id,
body ->> 'server',
body ->> 'message',
body ->> 'error'
FROM
(select id, tenant_id, ts, entity_id, to_safe_json(body) as body
FROM event WHERE ts >= start_ts_in_ms and ts < end_ts_in_ms AND event_type = 'DEBUG_RULE_CHAIN' AND to_safe_json(body) ->> 'server' IS NOT NULL
) safe_event
ON CONFLICT DO NOTHING;
END
$$;
UPDATE tb_user
SET additional_info = REPLACE(additional_info, '"lang":"ja_JA"', '"lang":"ja_JP"')
WHERE additional_info LIKE '%"lang":"ja_JA"%';

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

@ -37,10 +37,11 @@ import org.thingsboard.rule.engine.api.sms.SmsSenderFactory;
import org.thingsboard.server.actors.service.ActorService;
import org.thingsboard.server.actors.tenant.DebugTbRateLimits;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Event;
import org.thingsboard.server.common.data.event.ErrorEvent;
import org.thingsboard.server.common.data.event.LifecycleEvent;
import org.thingsboard.server.common.data.event.RuleChainDebugEvent;
import org.thingsboard.server.common.data.event.RuleNodeDebugEvent;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.QueueId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.msg.TbActorMsg;
@ -112,6 +113,29 @@ import java.util.concurrent.TimeUnit;
@Component
public class ActorSystemContext {
private static final FutureCallback<Void> RULE_CHAIN_DEBUG_EVENT_ERROR_CALLBACK = new FutureCallback<>() {
@Override
public void onSuccess(@Nullable Void event) {
}
@Override
public void onFailure(Throwable th) {
log.error("Could not save debug Event for Rule Chain", th);
}
};
private static final FutureCallback<Void> RULE_NODE_DEBUG_EVENT_ERROR_CALLBACK = new FutureCallback<>() {
@Override
public void onSuccess(@Nullable Void event) {
}
@Override
public void onFailure(Throwable th) {
log.error("Could not save debug Event for Node", th);
}
};
protected final ObjectMapper mapper = new ObjectMapper();
private final ConcurrentMap<TenantId, DebugTbRateLimits> debugPerTenantLimits = new ConcurrentHashMap<>();
@ -462,25 +486,28 @@ public class ActorSystemContext {
}
public void persistError(TenantId tenantId, EntityId entityId, String method, Exception e) {
Event event = new Event();
event.setTenantId(tenantId);
event.setEntityId(entityId);
event.setType(DataConstants.ERROR);
event.setBody(toBodyJson(serviceInfoProvider.getServiceInfo().getServiceId(), method, toString(e)));
persistEvent(event);
eventService.saveAsync(ErrorEvent.builder()
.tenantId(tenantId)
.entityId(entityId.getId())
.serviceId(getServiceId())
.method(method)
.error(toString(e)).build());
}
public void persistLifecycleEvent(TenantId tenantId, EntityId entityId, ComponentLifecycleEvent lcEvent, Exception e) {
Event event = new Event();
event.setTenantId(tenantId);
event.setEntityId(entityId);
event.setType(DataConstants.LC_EVENT);
event.setBody(toBodyJson(serviceInfoProvider.getServiceInfo().getServiceId(), lcEvent, Optional.ofNullable(e)));
persistEvent(event);
}
LifecycleEvent.LifecycleEventBuilder event = LifecycleEvent.builder()
.tenantId(tenantId)
.entityId(entityId.getId())
.serviceId(getServiceId())
.lcEventType(lcEvent.name());
if (e != null) {
event.success(false).error(toString(e));
} else {
event.success(true);
}
private void persistEvent(Event event) {
eventService.saveAsync(event);
eventService.saveAsync(event.build());
}
private String toString(Throwable e) {
@ -489,21 +516,6 @@ public class ActorSystemContext {
return sw.toString();
}
private JsonNode toBodyJson(String serviceId, ComponentLifecycleEvent event, Optional<Exception> e) {
ObjectNode node = mapper.createObjectNode().put("server", serviceId).put("event", event.name());
if (e.isPresent()) {
node = node.put("success", false);
node = node.put("error", toString(e.get()));
} else {
node = node.put("success", true);
}
return node;
}
private JsonNode toBodyJson(String serviceId, String method, String body) {
return mapper.createObjectNode().put("server", serviceId).put("method", method).put("error", body);
}
public TopicPartitionInfo resolve(ServiceType serviceType, TenantId tenantId, EntityId entityId) {
return partitionService.resolve(serviceType, tenantId, entityId);
}
@ -539,44 +551,27 @@ public class ActorSystemContext {
private void persistDebugAsync(TenantId tenantId, EntityId entityId, String type, TbMsg tbMsg, String relationType, Throwable error, String failureMessage) {
if (checkLimits(tenantId, tbMsg, error)) {
try {
Event event = new Event();
event.setTenantId(tenantId);
event.setEntityId(entityId);
event.setType(DataConstants.DEBUG_RULE_NODE);
String metadata = mapper.writeValueAsString(tbMsg.getMetaData().getData());
ObjectNode node = mapper.createObjectNode()
.put("type", type)
.put("server", getServiceId())
.put("entityId", tbMsg.getOriginator().getId().toString())
.put("entityName", tbMsg.getOriginator().getEntityType().name())
.put("msgId", tbMsg.getId().toString())
.put("msgType", tbMsg.getType())
.put("dataType", tbMsg.getDataType().name())
.put("relationType", relationType)
.put("data", tbMsg.getData())
.put("metadata", metadata);
RuleNodeDebugEvent.RuleNodeDebugEventBuilder event = RuleNodeDebugEvent.builder()
.tenantId(tenantId)
.entityId(entityId.getId())
.serviceId(getServiceId())
.eventType(type)
.eventEntity(tbMsg.getOriginator())
.msgId(tbMsg.getId())
.msgType(tbMsg.getType())
.dataType(tbMsg.getDataType().name())
.relationType(relationType)
.data(tbMsg.getData())
.metadata(mapper.writeValueAsString(tbMsg.getMetaData().getData()));
if (error != null) {
node = node.put("error", toString(error));
event.error(toString(error));
} else if (failureMessage != null) {
node = node.put("error", failureMessage);
event.error(failureMessage);
}
event.setBody(node);
ListenableFuture<Void> future = eventService.saveAsync(event);
Futures.addCallback(future, new FutureCallback<Void>() {
@Override
public void onSuccess(@Nullable Void event) {
}
@Override
public void onFailure(Throwable th) {
log.error("Could not save debug Event for Node", th);
}
}, MoreExecutors.directExecutor());
ListenableFuture<Void> future = eventService.saveAsync(event.build());
Futures.addCallback(future, RULE_NODE_DEBUG_EVENT_ERROR_CALLBACK, MoreExecutors.directExecutor());
} catch (IOException ex) {
log.warn("Failed to persist rule node debug message", ex);
}
@ -603,33 +598,17 @@ public class ActorSystemContext {
}
private void persistRuleChainDebugModeEvent(TenantId tenantId, EntityId entityId, Throwable error) {
Event event = new Event();
event.setTenantId(tenantId);
event.setEntityId(entityId);
event.setType(DataConstants.DEBUG_RULE_CHAIN);
ObjectNode node = mapper.createObjectNode()
//todo: what fields are needed here?
.put("server", getServiceId())
.put("message", "Reached debug mode rate limit!");
RuleChainDebugEvent.RuleChainDebugEventBuilder event = RuleChainDebugEvent.builder()
.tenantId(tenantId)
.entityId(entityId.getId())
.serviceId(getServiceId())
.message("Reached debug mode rate limit!");
if (error != null) {
node = node.put("error", toString(error));
event.error(toString(error));
}
event.setBody(node);
ListenableFuture<Void> future = eventService.saveAsync(event);
Futures.addCallback(future, new FutureCallback<Void>() {
@Override
public void onSuccess(@Nullable Void event) {
}
@Override
public void onFailure(Throwable th) {
log.error("Could not save debug Event for Rule Chain", th);
}
}, MoreExecutors.directExecutor());
ListenableFuture<Void> future = eventService.saveAsync(event.build());
Futures.addCallback(future, RULE_CHAIN_DEBUG_EVENT_ERROR_CALLBACK, MoreExecutors.directExecutor());
}
public static Exception toException(Throwable error) {

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

@ -19,7 +19,6 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.netty.channel.EventLoopGroup;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import org.thingsboard.common.util.ListeningExecutor;
import org.thingsboard.rule.engine.api.MailService;
import org.thingsboard.rule.engine.api.RuleEngineAlarmService;
@ -39,6 +38,7 @@ 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.EntityType;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.asset.Asset;
@ -46,7 +46,6 @@ import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.QueueId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.id.TenantId;

18
application/src/main/java/org/thingsboard/server/actors/stats/StatsActor.java

@ -20,13 +20,13 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.TbActor;
import org.thingsboard.server.actors.TbActorCtx;
import org.thingsboard.server.actors.TbActorId;
import org.thingsboard.server.actors.TbStringActorId;
import org.thingsboard.server.actors.service.ContextAwareActor;
import org.thingsboard.server.actors.service.ContextBasedCreator;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Event;
import org.thingsboard.server.common.data.EventInfo;
import org.thingsboard.server.common.data.event.StatisticsEvent;
import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.TbActorMsg;
@ -54,12 +54,14 @@ public class StatsActor extends ContextAwareActor {
if (msg.isEmpty()) {
return;
}
Event event = new Event();
event.setEntityId(msg.getEntityId());
event.setTenantId(msg.getTenantId());
event.setType(DataConstants.STATS);
event.setBody(toBodyJson(systemContext.getServiceInfoProvider().getServiceId(), msg.getMessagesProcessed(), msg.getErrorsOccurred()));
systemContext.getEventService().saveAsync(event);
systemContext.getEventService().saveAsync(StatisticsEvent.builder()
.tenantId(msg.getTenantId())
.entityId(msg.getEntityId().getId())
.serviceId(systemContext.getServiceInfoProvider().getServiceId())
.messagesProcessed(msg.getMessagesProcessed())
.errorsOccurred(msg.getErrorsOccurred())
.build()
);
}
private JsonNode toBodyJson(String serviceId, long messagesProcessed, long errorsOccurred) {

2
application/src/main/java/org/thingsboard/server/config/CustomOAuth2AuthorizationRequestResolver.java

@ -33,9 +33,9 @@ import org.springframework.security.web.util.UrlUtils;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.dao.oauth2.OAuth2Configuration;
import org.thingsboard.server.dao.oauth2.OAuth2Service;
import org.thingsboard.server.service.security.auth.oauth2.TbOAuth2ParameterNames;

2
application/src/main/java/org/thingsboard/server/config/RateLimitProcessingFilter.java

@ -16,7 +16,6 @@
package org.thingsboard.server.config;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.authentication.BadCredentialsException;
@ -24,6 +23,7 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.GenericFilterBean;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;

8
application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java

@ -17,8 +17,6 @@ package org.thingsboard.server.config;
import com.fasterxml.classmate.TypeResolver;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -153,7 +151,7 @@ public class SwaggerConfiguration {
}
@Override
public boolean supports(@NotNull DocumentationType delimiter) {
public boolean supports(DocumentationType delimiter) {
return DocumentationType.SWAGGER_2.equals(delimiter) || DocumentationType.OAS_30.equals(delimiter);
}
};
@ -175,7 +173,7 @@ public class SwaggerConfiguration {
}
@Override
public boolean supports(@NotNull DocumentationType delimiter) {
public boolean supports(DocumentationType delimiter) {
return DocumentationType.SWAGGER_2.equals(delimiter) || DocumentationType.OAS_30.equals(delimiter);
}
};
@ -334,7 +332,7 @@ public class SwaggerConfiguration {
errorResponse("401 ", "Unauthorized (**Expired credentials**)",
List.of(
errorExample("credentials-expired", "Expired credentials",
ThingsboardCredentialsExpiredResponse.of("User password expired!", RandomStringUtils.randomAlphanumeric(30)))
ThingsboardCredentialsExpiredResponse.of("User password expired!", StringUtils.randomAlphanumeric(30)))
), ThingsboardCredentialsExpiredResponse.class
)
);

2
application/src/main/java/org/thingsboard/server/controller/AbstractRpcController.java

@ -22,10 +22,10 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.async.DeferredResult;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException;

2
application/src/main/java/org/thingsboard/server/controller/AlarmController.java

@ -18,7 +18,6 @@ package org.thingsboard.server.controller;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
@ -30,6 +29,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmInfo;
import org.thingsboard.server.common.data.alarm.AlarmQuery;

2
application/src/main/java/org/thingsboard/server/controller/AuditLogController.java

@ -17,7 +17,6 @@ package org.thingsboard.server.controller;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
@ -26,6 +25,7 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.audit.AuditLog;
import org.thingsboard.server.common.data.exception.ThingsboardException;

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

@ -23,7 +23,6 @@ import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.support.DefaultMessageSourceResolvable;
@ -47,6 +46,7 @@ import org.thingsboard.server.common.data.EntityViewInfo;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.OtaPackage;
import org.thingsboard.server.common.data.OtaPackageInfo;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.TbResource;
import org.thingsboard.server.common.data.TbResourceInfo;
import org.thingsboard.server.common.data.Tenant;
@ -84,7 +84,6 @@ import org.thingsboard.server.common.data.id.TenantProfileId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.id.WidgetTypeId;
import org.thingsboard.server.common.data.id.WidgetsBundleId;
import org.thingsboard.server.common.data.page.PageDataIterableByTenantIdEntityId;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.SortOrder;
import org.thingsboard.server.common.data.page.TimePageLink;
@ -146,15 +145,12 @@ import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
import javax.mail.MessagingException;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import static org.thingsboard.server.controller.ControllerConstants.DEFAULT_PAGE_SIZE;
import static org.thingsboard.server.controller.ControllerConstants.INCORRECT_TENANT_ID;
import static org.thingsboard.server.controller.UserController.YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION;
import static org.thingsboard.server.dao.service.Validator.validateId;

2
application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java

@ -17,7 +17,6 @@ package org.thingsboard.server.controller;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@ -25,6 +24,7 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
import org.thingsboard.server.common.data.plugin.ComponentType;

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

@ -94,7 +94,7 @@ public class ControllerConstants {
protected static final String DEVICE_PROFILE_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, type, transportType, description, isDefault";
protected static final String ASSET_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, type, label, customerTitle";
protected static final String ALARM_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, startTs, endTs, type, ackTs, clearTs, severity, status";
protected static final String EVENT_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, id";
protected static final String EVENT_SORT_PROPERTY_ALLOWABLE_VALUES = "ts, id";
protected static final String EDGE_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, type, label, customerTitle";
protected static final String RULE_CHAIN_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, root";
protected static final String WIDGET_BUNDLE_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, title, tenantId";

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

@ -19,7 +19,6 @@ import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
@ -33,6 +32,7 @@ import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceProfileInfo;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.page.PageData;

75
application/src/main/java/org/thingsboard/server/controller/EventController.java

@ -29,8 +29,11 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.Event;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.EventInfo;
import org.thingsboard.server.common.data.event.EventFilter;
import org.thingsboard.server.common.data.event.EventType;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
@ -42,6 +45,8 @@ import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.permission.Operation;
import java.util.Locale;
import static org.thingsboard.server.controller.ControllerConstants.ENTITY_ID;
import static org.thingsboard.server.controller.ControllerConstants.ENTITY_ID_PARAM_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.ENTITY_TYPE;
@ -110,7 +115,7 @@ public class EventController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/events/{entityType}/{entityId}/{eventType}", method = RequestMethod.GET)
@ResponseBody
public PageData<Event> getEvents(
public PageData<EventInfo> getEvents(
@ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true)
@PathVariable(ENTITY_TYPE) String strEntityType,
@ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true)
@ -135,25 +140,24 @@ public class EventController extends BaseController {
@RequestParam(required = false) Long endTime) throws ThingsboardException {
checkParameter("EntityId", strEntityId);
checkParameter("EntityType", strEntityType);
try {
TenantId tenantId = TenantId.fromUUID(toUUID(strTenantId));
TenantId tenantId = TenantId.fromUUID(toUUID(strTenantId));
EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId);
checkEntityId(entityId, Operation.READ);
TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
return checkNotNull(eventService.findEvents(tenantId, entityId, eventType, pageLink));
} catch (Exception e) {
throw handleException(e);
}
EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId);
checkEntityId(entityId, Operation.READ);
TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
return checkNotNull(eventService.findEvents(tenantId, entityId, resolveEventType(eventType), pageLink));
}
@ApiOperation(value = "Get Events (getEvents)",
notes = "Returns a page of events for specified entity. " +
@ApiOperation(value = "Get Events (Deprecated)",
notes = "Returns a page of events for specified entity. Deprecated and will be removed in next minor release. " +
"The call was deprecated to improve the performance of the system. " +
"Current implementation will return 'Lifecycle' events only. " +
"Use 'Get events by type' or 'Get events by filter' instead. " +
PAGE_DATA_PARAMETERS, produces = MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/events/{entityType}/{entityId}", method = RequestMethod.GET)
@ResponseBody
public PageData<Event> getEvents(
public PageData<EventInfo> getEvents(
@ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true)
@PathVariable(ENTITY_TYPE) String strEntityType,
@ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true)
@ -176,18 +180,14 @@ public class EventController extends BaseController {
@RequestParam(required = false) Long endTime) throws ThingsboardException {
checkParameter("EntityId", strEntityId);
checkParameter("EntityType", strEntityType);
try {
TenantId tenantId = TenantId.fromUUID(toUUID(strTenantId));
TenantId tenantId = TenantId.fromUUID(toUUID(strTenantId));
EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId);
checkEntityId(entityId, Operation.READ);
EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId);
checkEntityId(entityId, Operation.READ);
TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
return checkNotNull(eventService.findEvents(tenantId, entityId, pageLink));
} catch (Exception e) {
throw handleException(e);
}
return checkNotNull(eventService.findEvents(tenantId, entityId, EventType.LC_EVENT, pageLink));
}
@ApiOperation(value = "Get Events by event filter (getEvents)",
@ -198,7 +198,7 @@ public class EventController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/events/{entityType}/{entityId}", method = RequestMethod.POST)
@ResponseBody
public PageData<Event> getEvents(
public PageData<EventInfo> getEvents(
@ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true)
@PathVariable(ENTITY_TYPE) String strEntityType,
@ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true)
@ -223,21 +223,13 @@ public class EventController extends BaseController {
@RequestParam(required = false) Long endTime) throws ThingsboardException {
checkParameter("EntityId", strEntityId);
checkParameter("EntityType", strEntityType);
try {
TenantId tenantId = TenantId.fromUUID(toUUID(strTenantId));
EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId);
checkEntityId(entityId, Operation.READ);
TenantId tenantId = TenantId.fromUUID(toUUID(strTenantId));
if (sortProperty != null && sortProperty.equals("createdTime") && eventFilter.hasFilterForJsonBody()) {
sortProperty = ModelConstants.CREATED_TIME_PROPERTY;
}
EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId);
checkEntityId(entityId, Operation.READ);
TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
return checkNotNull(eventService.findEventsByFilter(tenantId, entityId, eventFilter, pageLink));
} catch (Exception e) {
throw handleException(e);
}
TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
return checkNotNull(eventService.findEventsByFilter(tenantId, entityId, eventFilter, pageLink));
}
@ApiOperation(value = "Clear Events (clearEvents)", notes = "Clears events by filter for specified entity.")
@ -266,4 +258,13 @@ public class EventController extends BaseController {
}
}
private static EventType resolveEventType(String eventType) throws ThingsboardException {
for (var et : EventType.values()) {
if (et.name().equalsIgnoreCase(eventType) || et.getOldName().equalsIgnoreCase(eventType)) {
return et;
}
}
throw new ThingsboardException("Event type: '" + eventType + "' is not supported!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
}
}

9
application/src/main/java/org/thingsboard/server/controller/RuleChainController.java

@ -39,10 +39,10 @@ import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.rule.engine.api.ScriptEngine;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.tenant.DebugTbRateLimits;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Event;
import org.thingsboard.server.common.data.EventInfo;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.event.EventType;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.RuleChainId;
@ -70,7 +70,6 @@ import org.thingsboard.server.service.script.RuleNodeJsScriptEngine;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@ -353,10 +352,10 @@ public class RuleChainController extends BaseController {
RuleNodeId ruleNodeId = new RuleNodeId(toUUID(strRuleNodeId));
checkRuleNode(ruleNodeId, Operation.READ);
TenantId tenantId = getCurrentUser().getTenantId();
List<Event> events = eventService.findLatestEvents(tenantId, ruleNodeId, DataConstants.DEBUG_RULE_NODE, 2);
List<EventInfo> events = eventService.findLatestEvents(tenantId, ruleNodeId, EventType.DEBUG_RULE_NODE, 2);
JsonNode result = null;
if (events != null) {
for (Event event : events) {
for (EventInfo event : events) {
JsonNode body = event.getBody();
if (body.has("type") && body.get("type").asText().equals("IN")) {
result = body;

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

@ -35,7 +35,6 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
@ -49,6 +48,7 @@ import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardException;

9
application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java

@ -16,7 +16,6 @@
package org.thingsboard.server.controller.plugin;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.BeanCreationNotAllowedException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
@ -28,6 +27,7 @@ import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.adapter.NativeWebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
@ -49,7 +49,6 @@ import javax.websocket.SendResult;
import javax.websocket.Session;
import java.io.IOException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.security.InvalidParameterException;
import java.util.Queue;
import java.util.Set;
@ -143,7 +142,7 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
externalSessionMap.put(externalSessionId, internalSessionId);
processInWebSocketService(sessionRef, SessionEvent.onEstablished());
log.info("[{}][{}][{}] Session is opened", sessionRef.getSecurityCtx().getTenantId(), externalSessionId, session.getId());
log.info("[{}][{}][{}] Session is opened from address: {}", sessionRef.getSecurityCtx().getTenantId(), externalSessionId, session.getId(), session.getRemoteAddress());
} catch (InvalidParameterException e) {
log.warn("[{}] Failed to start session", session.getId(), e);
session.close(CloseStatus.BAD_DATA.withReason(e.getMessage()));
@ -173,8 +172,10 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
cleanupLimits(session, sessionMd.sessionRef);
externalSessionMap.remove(sessionMd.sessionRef.getSessionId());
processInWebSocketService(sessionMd.sessionRef, SessionEvent.onClosed());
log.info("[{}][{}][{}] Session is closed", sessionMd.sessionRef.getSecurityCtx().getTenantId(), sessionMd.sessionRef.getSessionId(), session.getId());
} else {
log.info("[{}] Session is closed", session.getId());
}
log.info("[{}] Session is closed", session.getId());
}
private void processInWebSocketService(TelemetryWebSocketSessionRef sessionRef, SessionEvent event) {

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

@ -17,7 +17,6 @@ package org.thingsboard.server.exception;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
@ -139,12 +138,11 @@ public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHand
}
}
@NotNull
@Override
protected ResponseEntity<Object> handleExceptionInternal(
@NotNull Exception ex, @Nullable Object body,
@NotNull HttpHeaders headers, @NotNull HttpStatus status,
@NotNull WebRequest request) {
Exception ex, @Nullable Object body,
HttpHeaders headers, HttpStatus status,
WebRequest request) {
if (HttpStatus.INTERNAL_SERVER_ERROR.equals(status)) {
request.setAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE, ex, WebRequest.SCOPE_REQUEST);
}

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

@ -221,6 +221,10 @@ public class ThingsboardInstallService {
log.info("Upgrading ThingsBoard from version 3.3.4 to 3.4.0 ...");
databaseEntitiesUpgradeService.upgradeDatabase("3.3.4");
dataUpdateService.updateData("3.3.4");
case "3.4.0":
log.info("Upgrading ThingsBoard from version 3.4.0 to 3.4.1 ...");
databaseEntitiesUpgradeService.upgradeDatabase("3.4.0");
dataUpdateService.updateData("3.4.0");
log.info("Updating system data...");
systemDataLoaderService.updateSystemWidgets();
break;

5
application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java

@ -20,13 +20,13 @@ import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
@ -41,7 +41,6 @@ import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.dao.audit.AuditLogService;
import org.thingsboard.server.queue.util.TbCoreComponent;
import java.util.List;
import java.util.Map;
@ -199,7 +198,7 @@ public class EntityActionService {
}
}
TbMsg tbMsg = TbMsg.newMsg(msgType, entityId, customerId, metaData, TbMsgDataType.JSON, json.writeValueAsString(entityNode));
if (tenantId.isNullUid()) {
if (tenantId == null || tenantId.isNullUid()) {
if (entity instanceof HasTenantId) {
tenantId = ((HasTenantId) entity).getTenantId();
}

2
application/src/main/java/org/thingsboard/server/service/apiusage/DefaultRateLimitService.java

@ -16,8 +16,8 @@
package org.thingsboard.server.service.apiusage;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
import org.thingsboard.server.common.msg.tools.TbRateLimits;

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

@ -16,10 +16,8 @@
package org.thingsboard.server.service.apiusage;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
@ -34,6 +32,7 @@ import org.thingsboard.server.common.data.ApiUsageState;
import org.thingsboard.server.common.data.ApiUsageStateMailMessage;
import org.thingsboard.server.common.data.ApiUsageStateValue;
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.TenantProfile;
import org.thingsboard.server.common.data.exception.ThingsboardException;
@ -70,6 +69,7 @@ import javax.annotation.PreDestroy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@ -500,19 +500,19 @@ public class DefaultTbApiUsageStateService extends AbstractPartitionBasedService
}
@Override
protected void onAddedPartitions(Set<TopicPartitionInfo> addedPartitions) {
protected Map<TopicPartitionInfo, List<ListenableFuture<?>>> onAddedPartitions(Set<TopicPartitionInfo> addedPartitions) {
var result = new HashMap<TopicPartitionInfo, List<ListenableFuture<?>>>();
try {
log.info("Initializing tenant states.");
updateLock.lock();
try {
PageDataIterable<Tenant> tenantIterator = new PageDataIterable<>(tenantService::findTenants, 1024);
List<ListenableFuture<?>> futures = new ArrayList<>();
for (Tenant tenant : tenantIterator) {
TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenant.getId(), tenant.getId());
if (addedPartitions.contains(tpi)) {
if (!myUsageStates.containsKey(tenant.getId()) && tpi.isMyPartition()) {
log.debug("[{}] Initializing tenant state.", tenant.getId());
futures.add(dbExecutor.submit(() -> {
result.computeIfAbsent(tpi, tmp -> new ArrayList<>()).add(dbExecutor.submit(() -> {
try {
updateTenantState((TenantApiUsageState) getOrFetchState(tenant.getId(), tenant.getId()), tenantProfileCache.get(tenant.getTenantProfileId()));
log.debug("[{}] Initialized tenant state.", tenant.getId());
@ -526,14 +526,13 @@ public class DefaultTbApiUsageStateService extends AbstractPartitionBasedService
log.debug("[{}][{}] Tenant doesn't belong to current partition. tpi [{}]", tenant.getName(), tenant.getId(), tpi);
}
}
Futures.whenAllComplete(futures);
} finally {
updateLock.unlock();
}
log.info("Initialized {} tenant states.", myUsageStates.size());
} catch (Exception e) {
log.warn("Unknown failure", e);
}
return result;
}
@PreDestroy

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

@ -27,13 +27,13 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;

9
application/src/main/java/org/thingsboard/server/service/device/DeviceBulkImportService.java

@ -21,8 +21,6 @@ import com.fasterxml.jackson.databind.node.TextNode;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Device;
@ -31,6 +29,7 @@ import org.thingsboard.server.common.data.DeviceProfileProvisionType;
import org.thingsboard.server.common.data.DeviceProfileType;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials;
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MClientCredential;
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode;
@ -44,15 +43,15 @@ import org.thingsboard.server.common.data.device.profile.lwm2m.TelemetryMappingC
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportColumnType;
import org.thingsboard.server.dao.device.DeviceCredentialsService;
import org.thingsboard.server.dao.device.DeviceProfileService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.exception.DeviceCredentialsValidationException;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.sync.ie.importing.csv.AbstractBulkImportService;
import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportColumnType;
import org.thingsboard.server.service.entitiy.device.TbDeviceService;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.sync.ie.importing.csv.AbstractBulkImportService;
import java.util.Collection;
import java.util.Collections;
@ -157,7 +156,7 @@ public class DeviceBulkImportService extends AbstractBulkImportService<Device> {
private void setUpAccessTokenCredentials(Map<BulkImportColumnType, String> fields, DeviceCredentials credentials) {
credentials.setCredentialsId(Optional.ofNullable(fields.get(BulkImportColumnType.ACCESS_TOKEN))
.orElseGet(() -> RandomStringUtils.randomAlphanumeric(20)));
.orElseGet(() -> StringUtils.randomAlphanumeric(20)));
}
private void setUpBasicMqttCredentials(Map<BulkImportColumnType, String> fields, DeviceCredentials credentials) {

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

@ -20,14 +20,14 @@ import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.cluster.TbClusterService;
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.StringUtils;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
@ -51,7 +51,6 @@ import org.thingsboard.server.dao.device.provision.ProvisionFailedException;
import org.thingsboard.server.dao.device.provision.ProvisionRequest;
import org.thingsboard.server.dao.device.provision.ProvisionResponse;
import org.thingsboard.server.dao.device.provision.ProvisionResponseStatus;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
import org.thingsboard.server.queue.TbQueueCallback;
@ -189,7 +188,7 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService {
private ProvisionResponse processCreateDevice(ProvisionRequest provisionRequest, DeviceProfile profile) {
try {
if (StringUtils.isEmpty(provisionRequest.getDeviceName())) {
String newDeviceName = RandomStringUtils.randomAlphanumeric(20);
String newDeviceName = StringUtils.randomAlphanumeric(20);
log.info("Device name not found in provision request. Generated name is: {}", newDeviceName);
provisionRequest.setDeviceName(newDeviceName);
}

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

@ -20,6 +20,7 @@ import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.dashboard.DashboardService;
@ -61,6 +62,9 @@ import org.thingsboard.server.service.executors.GrpcCallbackExecutorService;
@Lazy
public class EdgeContextComponent {
@Autowired
private TbClusterService clusterService;
@Autowired
private EdgeService edgeService;

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

@ -211,6 +211,7 @@ public final class EdgeGrpcSession implements Closeable {
@Override
public void onSuccess(Void result) {
syncCompleted = true;
ctx.getClusterService().onEdgeEventUpdate(edge.getTenantId(), edge.getId());
}
@Override

14
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceMsgConstructor.java

@ -16,7 +16,8 @@
package org.thingsboard.server.service.edge.rpc.constructor;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.protobuf.ByteString;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Device;
@ -28,6 +29,7 @@ import org.thingsboard.server.gen.edge.v1.DeviceRpcCallMsg;
import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg;
import org.thingsboard.server.gen.edge.v1.RpcRequestMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.queue.util.DataDecodingEncodingService;
import org.thingsboard.server.queue.util.TbCoreComponent;
import java.util.UUID;
@ -36,7 +38,8 @@ import java.util.UUID;
@TbCoreComponent
public class DeviceMsgConstructor {
protected static final ObjectMapper mapper = new ObjectMapper();
@Autowired
private DataDecodingEncodingService dataDecodingEncodingService;
public DeviceUpdateMsg constructDeviceUpdatedMsg(UpdateMsgType msgType, Device device, CustomerId customerId, String conflictName) {
DeviceUpdateMsg.Builder builder = DeviceUpdateMsg.newBuilder()
@ -59,9 +62,16 @@ public class DeviceMsgConstructor {
if (device.getAdditionalInfo() != null) {
builder.setAdditionalInfo(JacksonUtil.toString(device.getAdditionalInfo()));
}
if (device.getFirmwareId() != null) {
builder.setFirmwareIdMSB(device.getFirmwareId().getId().getMostSignificantBits())
.setFirmwareIdLSB(device.getFirmwareId().getId().getLeastSignificantBits());
}
if (conflictName != null) {
builder.setConflictName(conflictName);
}
if (device.getDeviceData() != null) {
builder.setDeviceDataBytes(ByteString.copyFrom(dataDecodingEncodingService.encode(device.getDeviceData())));
}
return builder.build();
}

6
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceProfileMsgConstructor.java

@ -20,9 +20,9 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.queue.util.DataDecodingEncodingService;
import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.queue.util.DataDecodingEncodingService;
import org.thingsboard.server.queue.util.TbCoreComponent;
import java.nio.charset.StandardCharsets;
@ -66,6 +66,10 @@ public class DeviceProfileMsgConstructor {
if (deviceProfile.getImage() != null) {
builder.setImage(ByteString.copyFrom(deviceProfile.getImage().getBytes(StandardCharsets.UTF_8)));
}
if (deviceProfile.getFirmwareId() != null) {
builder.setFirmwareIdMSB(deviceProfile.getFirmwareId().getId().getMostSignificantBits())
.setFirmwareIdLSB(deviceProfile.getFirmwareId().getId().getLeastSignificantBits());
}
return builder.build();
}

7
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/OtaPackageMsgConstructor.java

@ -33,13 +33,16 @@ public class OtaPackageMsgConstructor {
.setMsgType(msgType)
.setIdMSB(otaPackage.getId().getId().getMostSignificantBits())
.setIdLSB(otaPackage.getId().getId().getLeastSignificantBits())
.setDeviceProfileIdMSB(otaPackage.getDeviceProfileId().getId().getMostSignificantBits())
.setDeviceProfileIdLSB(otaPackage.getDeviceProfileId().getId().getLeastSignificantBits())
.setType(otaPackage.getType().name())
.setTitle(otaPackage.getTitle())
.setVersion(otaPackage.getVersion())
.setTag(otaPackage.getTag());
if (otaPackage.getDeviceProfileId() != null) {
builder.setDeviceProfileIdMSB(otaPackage.getDeviceProfileId().getId().getMostSignificantBits())
.setDeviceProfileIdLSB(otaPackage.getDeviceProfileId().getId().getLeastSignificantBits());
}
if (otaPackage.getUrl() != null) {
builder.setUrl(otaPackage.getUrl());
}

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

@ -23,10 +23,10 @@ import freemarker.template.Configuration;
import freemarker.template.Template;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.text.WordUtils;
import org.thingsboard.server.common.data.AdminSettings;
import org.thingsboard.server.common.data.EdgeUtils;
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.edge.EdgeEventActionType;

24
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/DeviceEdgeProcessor.java

@ -22,14 +22,15 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.device.data.DeviceData;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
@ -60,9 +61,11 @@ import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.queue.TbQueueCallback;
import org.thingsboard.server.queue.TbQueueMsgMetadata;
import org.thingsboard.server.queue.util.DataDecodingEncodingService;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.rpc.FromDeviceRpcResponseActorMsg;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.locks.ReentrantLock;
@ -71,6 +74,9 @@ import java.util.concurrent.locks.ReentrantLock;
@TbCoreComponent
public class DeviceEdgeProcessor extends BaseEdgeProcessor {
@Autowired
private DataDecodingEncodingService dataDecodingEncodingService;
private static final ReentrantLock deviceCreationLock = new ReentrantLock();
public ListenableFuture<Void> processDeviceFromEdge(TenantId tenantId, Edge edge, DeviceUpdateMsg deviceUpdateMsg) {
@ -88,7 +94,7 @@ public class DeviceEdgeProcessor extends BaseEdgeProcessor {
} else {
log.info("[{}] Device with name '{}' already exists on the cloud, but not related to this edge [{}]. deviceUpdateMsg [{}]." +
"Creating a new device with random prefix and relate to this edge", tenantId, deviceName, edge.getId(), deviceUpdateMsg);
String newDeviceName = deviceUpdateMsg.getName() + "_" + RandomStringUtils.randomAlphabetic(15);
String newDeviceName = deviceUpdateMsg.getName() + "_" + StringUtils.randomAlphabetic(15);
Device newDevice;
try {
newDevice = createDevice(tenantId, edge, deviceUpdateMsg, newDeviceName);
@ -192,6 +198,11 @@ public class DeviceEdgeProcessor extends BaseEdgeProcessor {
deviceUpdateMsg.getDeviceProfileIdLSB()));
device.setDeviceProfileId(deviceProfileId);
}
Optional<DeviceData> deviceDataOpt =
dataDecodingEncodingService.decode(deviceUpdateMsg.getDeviceDataBytes().toByteArray());
if (deviceDataOpt.isPresent()) {
device.setDeviceData(deviceDataOpt.get());
}
Device savedDevice = deviceService.saveDevice(device);
tbClusterService.onDeviceUpdated(savedDevice, device);
return saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.CREDENTIALS_REQUEST, deviceId, null);
@ -232,6 +243,11 @@ public class DeviceEdgeProcessor extends BaseEdgeProcessor {
deviceUpdateMsg.getDeviceProfileIdLSB()));
device.setDeviceProfileId(deviceProfileId);
}
Optional<DeviceData> deviceDataOpt =
dataDecodingEncodingService.decode(deviceUpdateMsg.getDeviceDataBytes().toByteArray());
if (deviceDataOpt.isPresent()) {
device.setDeviceData(deviceDataOpt.get());
}
if (created) {
deviceValidator.validate(device, Device::getTenantId);
device.setId(deviceId);
@ -244,7 +260,7 @@ public class DeviceEdgeProcessor extends BaseEdgeProcessor {
DeviceCredentials deviceCredentials = new DeviceCredentials();
deviceCredentials.setDeviceId(new DeviceId(savedDevice.getUuidId()));
deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN);
deviceCredentials.setCredentialsId(org.apache.commons.lang3.RandomStringUtils.randomAlphanumeric(20));
deviceCredentials.setCredentialsId(StringUtils.randomAlphanumeric(20));
deviceCredentialsService.createDeviceCredentials(device.getTenantId(), deviceCredentials);
}
createRelationFromEdge(tenantId, edge.getId(), device.getId());

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

@ -59,7 +59,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.thingsboard.server.common.data.StringUtils.isBlank;
@Service
@AllArgsConstructor

3
application/src/main/java/org/thingsboard/server/service/install/DatabaseHelper.java

@ -20,12 +20,11 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.lang3.StringUtils;
import org.thingsboard.server.common.data.ShortCustomerInfo;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.UUIDConverter;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.dashboard.DashboardService;

3
application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java

@ -20,10 +20,9 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate;
import org.thingsboard.server.common.data.rule.RuleChain;

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

@ -25,6 +25,7 @@ import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.id.QueueId;
@ -596,6 +597,18 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
log.error("Failed updating schema!!!", e);
}
break;
case "3.4.0":
try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
log.info("Updating schema ...");
schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.4.0", SCHEMA_UPDATE_SQL);
loadSql(schemaUpdateFile, conn);
log.info("Updating schema settings...");
conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3004001;");
log.info("Schema updated.");
} catch (Exception e) {
log.error("Failed updating schema!!!", e);
}
break;
default:
throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion);
}
@ -666,5 +679,4 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
}
}

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

@ -16,11 +16,11 @@
package org.thingsboard.server.service.install;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.SystemUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.dao.util.SqlTsDao;
import java.io.File;

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

@ -16,12 +16,12 @@
package org.thingsboard.server.service.install;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.SystemUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.dao.util.TimescaleDBTsDao;
import java.io.File;

2
application/src/main/java/org/thingsboard/server/service/install/migrate/CassandraTsLatestToSqlMigrateService.java

@ -16,12 +16,12 @@
package org.thingsboard.server.service.install.migrate;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.exception.ConstraintViolationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.UUIDConverter;
import org.thingsboard.server.dao.cassandra.CassandraCluster;
import org.thingsboard.server.dao.model.sqlts.dictionary.TsKvDictionary;

27
application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java

@ -37,7 +37,10 @@ import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmInfo;
import org.thingsboard.server.common.data.alarm.AlarmQuery;
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.id.*;
import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
import org.thingsboard.server.common.data.kv.TsKvEntry;
@ -46,7 +49,11 @@ import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.query.DynamicValue;
import org.thingsboard.server.common.data.query.FilterPredicateValue;
import org.thingsboard.server.common.data.queue.*;
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.SubmitStrategy;
import org.thingsboard.server.common.data.queue.SubmitStrategyType;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.common.data.rule.RuleChain;
@ -58,6 +65,7 @@ import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.alarm.AlarmDao;
import org.thingsboard.server.dao.entity.EntityService;
import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.event.EventService;
import org.thingsboard.server.dao.model.sql.DeviceProfileEntity;
import org.thingsboard.server.dao.queue.QueueService;
import org.thingsboard.server.dao.relation.RelationService;
@ -73,12 +81,11 @@ import org.thingsboard.server.service.install.TbRuleEngineQueueConfigService;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.thingsboard.server.common.data.StringUtils.isBlank;
@Service
@Profile("install")
@ -128,6 +135,9 @@ public class DefaultDataUpdateService implements DataUpdateService {
@Autowired
private SystemDataLoaderService systemDataLoaderService;
@Autowired
private EventService eventService;
@Override
public void updateData(String fromVersion) throws Exception {
switch (fromVersion) {
@ -159,6 +169,13 @@ public class DefaultDataUpdateService implements DataUpdateService {
tenantsProfileQueueConfigurationUpdater.updateEntities();
rateLimitsUpdater.updateEntities();
break;
case "3.4.0":
String skipEventsMigration = System.getenv("TB_SKIP_EVENTS_MIGRATION");
if (skipEventsMigration == null || skipEventsMigration.equalsIgnoreCase("false")) {
log.info("Updating data from version 3.4.0 to 3.4.1 ...");
eventService.migrateEvents();
}
break;
default:
throw new RuntimeException("Unable to update data, unsupported fromVersion: " + fromVersion);
}
@ -602,7 +619,7 @@ public class DefaultDataUpdateService implements DataUpdateService {
});
}
} catch (Exception e) {
log.error("Failed to update tenant profile queue configuration name=["+profile.getName()+"], id=["+ profile.getId().getId() +"]", e);
log.error("Failed to update tenant profile queue configuration name=[" + profile.getName() + "], id=[" + profile.getId().getId() + "]", e);
}
}

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

@ -15,10 +15,10 @@
*/
package org.thingsboard.server.service.install.update;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;

6
application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java

@ -19,10 +19,8 @@ import com.fasterxml.jackson.databind.JsonNode;
import freemarker.template.Configuration;
import freemarker.template.Template;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.NestedRuntimeException;
@ -39,6 +37,7 @@ import org.thingsboard.server.common.data.ApiFeature;
import org.thingsboard.server.common.data.ApiUsageRecordKey;
import org.thingsboard.server.common.data.ApiUsageStateMailMessage;
import org.thingsboard.server.common.data.ApiUsageStateValue;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
@ -49,15 +48,12 @@ import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.mail.internet.MimeMessage;
import java.io.ByteArrayInputStream;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

57
application/src/main/java/org/thingsboard/server/service/partition/AbstractPartitionBasedService.java

@ -15,9 +15,12 @@
*/
package org.thingsboard.server.service.partition;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningScheduledExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.common.util.DonAsynchron;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.msg.queue.ServiceType;
@ -25,9 +28,14 @@ import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ConcurrentMap;
@ -37,6 +45,7 @@ import java.util.concurrent.Executors;
public abstract class AbstractPartitionBasedService<T extends EntityId> extends TbApplicationEventListener<PartitionChangeEvent> {
protected final ConcurrentMap<TopicPartitionInfo, Set<T>> partitionedEntities = new ConcurrentHashMap<>();
protected final ConcurrentMap<TopicPartitionInfo, List<ListenableFuture<?>>> partitionedFetchTasks = new ConcurrentHashMap<>();
final Queue<Set<TopicPartitionInfo>> subscribeQueue = new ConcurrentLinkedQueue<>();
protected ListeningScheduledExecutorService scheduledExecutor;
@ -45,7 +54,7 @@ public abstract class AbstractPartitionBasedService<T extends EntityId> extends
abstract protected String getSchedulerExecutorName();
abstract protected void onAddedPartitions(Set<TopicPartitionInfo> addedPartitions);
abstract protected Map<TopicPartitionInfo, List<ListenableFuture<?>>> onAddedPartitions(Set<TopicPartitionInfo> addedPartitions);
abstract protected void cleanupEntityOnPartitionRemoval(T entityId);
@ -108,29 +117,59 @@ public abstract class AbstractPartitionBasedService<T extends EntityId> extends
log.info("[{}] REMOVED PARTITIONS: {}", getServiceName(), removedPartitions);
boolean partitionListChanged = false;
// We no longer manage current partition of entities;
removedPartitions.forEach(partition -> {
for (var partition : removedPartitions) {
Set<T> entities = partitionedEntities.remove(partition);
entities.forEach(this::cleanupEntityOnPartitionRemoval);
});
if (entities != null) {
entities.forEach(this::cleanupEntityOnPartitionRemoval);
}
List<ListenableFuture<?>> fetchTasks = partitionedFetchTasks.remove(partition);
if (fetchTasks != null) {
fetchTasks.forEach(f -> f.cancel(false));
}
partitionListChanged = true;
}
onRepartitionEvent();
addedPartitions.forEach(tpi -> partitionedEntities.computeIfAbsent(tpi, key -> ConcurrentHashMap.newKeySet()));
if (!addedPartitions.isEmpty()) {
onAddedPartitions(addedPartitions);
var fetchTasks = onAddedPartitions(addedPartitions);
if (fetchTasks != null && !fetchTasks.isEmpty()) {
partitionedFetchTasks.putAll(fetchTasks);
}
partitionListChanged = true;
}
log.info("[{}] Managing following partitions:", getServiceName());
partitionedEntities.forEach((tpi, entities) -> {
log.info("[{}][{}]: {} entities", getServiceName(), tpi.getFullTopicName(), entities.size());
});
if (partitionListChanged) {
List<ListenableFuture<?>> partitionFetchFutures = new ArrayList<>();
partitionedFetchTasks.values().forEach(partitionFetchFutures::addAll);
DonAsynchron.withCallback(Futures.allAsList(partitionFetchFutures), t -> logPartitions(), this::logFailure);
}
} catch (Throwable t) {
log.warn("[{}] Failed to init entities state from DB", getServiceName(), t);
}
}
private void logFailure(Throwable e) {
if (e instanceof CancellationException) {
//Probably this is fine and happens due to re-balancing.
log.trace("Partition fetch task error", e);
} else {
log.error("Partition fetch task error", e);
}
}
private void logPartitions() {
log.info("[{}] Managing following partitions:", getServiceName());
partitionedEntities.forEach((tpi, entities) -> {
log.info("[{}][{}]: {} entities", getServiceName(), tpi.getFullTopicName(), entities.size());
});
}
protected void onRepartitionEvent() {
}

6
application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java

@ -41,7 +41,6 @@ import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.QueueId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
@ -53,7 +52,6 @@ import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse;
import org.thingsboard.server.queue.util.DataDecodingEncodingService;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.FromDeviceRPCResponseProto;
import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
@ -68,11 +66,11 @@ import org.thingsboard.server.queue.common.TbProtoQueueMsg;
import org.thingsboard.server.queue.discovery.NotificationsTopicService;
import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
import org.thingsboard.server.queue.util.DataDecodingEncodingService;
import org.thingsboard.server.service.gateway_device.GatewayNotificationsService;
import org.thingsboard.server.service.ota.OtaPackageStateService;
import org.thingsboard.server.service.profile.TbDeviceProfileCache;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
@ -167,7 +165,7 @@ public class DefaultTbClusterService implements TbClusterService {
@Override
public void pushMsgToRuleEngine(TenantId tenantId, EntityId entityId, TbMsg tbMsg, TbQueueCallback callback) {
if (tenantId.isNullUid()) {
if (tenantId == null || tenantId.isNullUid()) {
if (entityId.getEntityType().equals(EntityType.TENANT)) {
tenantId = TenantId.fromUUID(entityId.getId());
} else {

5
application/src/main/java/org/thingsboard/server/service/queue/DefaultTenantRoutingInfoService.java

@ -42,9 +42,8 @@ public class DefaultTenantRoutingInfoService implements TenantRoutingInfoService
@Override
public TenantRoutingInfo getRoutingInfo(TenantId tenantId) {
Tenant tenant = tenantService.findTenantById(tenantId);
if (tenant != null) {
TenantProfile tenantProfile = tenantProfileCache.get(tenant.getTenantProfileId());
TenantProfile tenantProfile = tenantProfileCache.get(tenantId);
if (tenantProfile != null) {
return new TenantRoutingInfo(tenantId, tenantProfile.isIsolatedTbRuleEngine());
} else {
throw new RuntimeException("Tenant not found!");

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

@ -16,7 +16,6 @@
package org.thingsboard.server.service.resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.leshan.core.model.DDFFileParser;
import org.eclipse.leshan.core.model.DefaultDDFFileValidator;
import org.eclipse.leshan.core.model.InvalidDDFFileException;
@ -24,6 +23,7 @@ import org.eclipse.leshan.core.model.ObjectModel;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.ResourceType;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.TbResource;
import org.thingsboard.server.common.data.TbResourceInfo;
import org.thingsboard.server.common.data.User;

2
application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java

@ -22,7 +22,7 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;

2
application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenProcessingFilter.java

@ -17,7 +17,6 @@ package org.thingsboard.server.service.security.auth.jwt;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
@ -26,6 +25,7 @@ import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.service.security.auth.RefreshAuthenticationToken;
import org.thingsboard.server.service.security.exception.AuthMethodNotSupportedException;
import org.thingsboard.server.service.security.model.token.RawAccessJwtToken;

2
application/src/main/java/org/thingsboard/server/service/security/auth/jwt/extractor/JwtHeaderTokenExtractor.java

@ -15,9 +15,9 @@
*/
package org.thingsboard.server.service.security.auth.jwt.extractor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.config.ThingsboardSecurityConfiguration;
import javax.servlet.http.HttpServletRequest;

2
application/src/main/java/org/thingsboard/server/service/security/auth/jwt/extractor/JwtQueryTokenExtractor.java

@ -15,9 +15,9 @@
*/
package org.thingsboard.server.service.security.auth.jwt.extractor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.config.ThingsboardSecurityConfiguration;
import javax.servlet.http.HttpServletRequest;

8
application/src/main/java/org/thingsboard/server/service/security/auth/mfa/DefaultTwoFactorAuthService.java

@ -16,23 +16,23 @@
package org.thingsboard.server.service.security.auth.mfa;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.LockedException;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.security.model.mfa.PlatformTwoFaSettings;
import org.thingsboard.server.common.data.security.model.mfa.account.TwoFaAccountConfig;
import org.thingsboard.server.common.data.security.model.mfa.provider.TwoFaProviderConfig;
import org.thingsboard.server.common.data.security.model.mfa.provider.TwoFaProviderType;
import org.thingsboard.server.common.msg.tools.TbRateLimits;
import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.auth.mfa.config.TwoFaConfigManager;
import org.thingsboard.server.common.data.security.model.mfa.PlatformTwoFaSettings;
import org.thingsboard.server.common.data.security.model.mfa.account.TwoFaAccountConfig;
import org.thingsboard.server.common.data.security.model.mfa.provider.TwoFaProviderConfig;
import org.thingsboard.server.service.security.auth.mfa.provider.TwoFaProvider;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.system.SystemSecurityService;

4
application/src/main/java/org/thingsboard/server/service/security/auth/mfa/provider/impl/BackupCodeTwoFaProvider.java

@ -15,11 +15,11 @@
*/
package org.thingsboard.server.service.security.auth.mfa.provider.impl;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.CollectionsUtil;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.security.model.mfa.account.BackupCodeTwoFaAccountConfig;
import org.thingsboard.server.common.data.security.model.mfa.provider.BackupCodeTwoFaProviderConfig;
@ -49,7 +49,7 @@ public class BackupCodeTwoFaProvider implements TwoFaProvider<BackupCodeTwoFaPro
}
private static Set<String> generateCodes(int count, int length) {
return Stream.generate(() -> RandomStringUtils.random(length, "0123456789abcdef"))
return Stream.generate(() -> StringUtils.random(length, "0123456789abcdef"))
.distinct().limit(count)
.collect(Collectors.toSet());
}

7
application/src/main/java/org/thingsboard/server/service/security/auth/mfa/provider/impl/OtpBasedTwoFaProvider.java

@ -16,16 +16,17 @@
package org.thingsboard.server.service.security.auth.mfa.provider.impl;
import lombok.Data;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.thingsboard.server.common.data.CacheConstants;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.security.model.mfa.account.OtpBasedTwoFaAccountConfig;
import org.thingsboard.server.common.data.security.model.mfa.provider.OtpBasedTwoFaProviderConfig;
import org.thingsboard.server.service.security.auth.mfa.provider.TwoFaProvider;
import org.thingsboard.server.service.security.model.SecurityUser;
import java.io.Serializable;
import java.util.concurrent.TimeUnit;
public abstract class OtpBasedTwoFaProvider<C extends OtpBasedTwoFaProviderConfig, A extends OtpBasedTwoFaAccountConfig> implements TwoFaProvider<C, A> {
@ -39,7 +40,7 @@ public abstract class OtpBasedTwoFaProvider<C extends OtpBasedTwoFaProviderConfi
@Override
public final void prepareVerificationCode(SecurityUser user, C providerConfig, A accountConfig) throws ThingsboardException {
String verificationCode = RandomStringUtils.randomNumeric(6);
String verificationCode = StringUtils.randomNumeric(6);
sendVerificationCode(user, verificationCode, providerConfig, accountConfig);
verificationCodesCache.put(user.getId(), new Otp(System.currentTimeMillis(), verificationCode, accountConfig));
}
@ -67,7 +68,7 @@ public abstract class OtpBasedTwoFaProvider<C extends OtpBasedTwoFaProviderConfi
@Data
public static class Otp {
public static class Otp implements Serializable {
private final long timestamp;
private final String value;
private final OtpBasedTwoFaAccountConfig accountConfig;

4
application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java

@ -24,9 +24,10 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.util.StringUtils;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.DashboardInfo;
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.id.CustomerId;
@ -47,7 +48,6 @@ import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.service.install.InstallScripts;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.model.UserPrincipal;

2
application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AppleOAuth2ClientMapper.java

@ -21,8 +21,8 @@ import org.springframework.security.oauth2.client.authentication.OAuth2Authentic
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig;
import org.thingsboard.server.common.data.oauth2.OAuth2Registration;
import org.thingsboard.server.dao.oauth2.OAuth2User;

2
application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicMapperUtils.java

@ -17,7 +17,7 @@ package org.thingsboard.server.service.security.auth.oauth2;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.text.StrSubstitutor;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig;
import org.thingsboard.server.dao.oauth2.OAuth2User;

2
application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java

@ -22,8 +22,8 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig;
import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig;
import org.thingsboard.server.common.data.oauth2.OAuth2Registration;

3
application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestLoginProcessingFilter.java

@ -17,7 +17,6 @@ package org.thingsboard.server.service.security.auth.rest;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationServiceException;
@ -28,7 +27,7 @@ import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.service.security.exception.AuthMethodNotSupportedException;
import org.thingsboard.server.service.security.model.UserPrincipal;

2
application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestPublicLoginProcessingFilter.java

@ -17,7 +17,6 @@ package org.thingsboard.server.service.security.auth.rest;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@ -27,6 +26,7 @@ import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.service.security.exception.AuthMethodNotSupportedException;
import org.thingsboard.server.service.security.model.UserPrincipal;

2
application/src/main/java/org/thingsboard/server/service/security/model/token/JwtTokenFactory.java

@ -25,11 +25,11 @@ import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.SignatureException;
import io.jsonwebtoken.UnsupportedJwtException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;

2
application/src/main/java/org/thingsboard/server/service/security/model/token/OAuth2AppTokenFactory.java

@ -22,9 +22,9 @@ import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureException;
import io.jsonwebtoken.UnsupportedJwtException;
import io.micrometer.core.instrument.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.StringUtils;
import java.util.Date;
import java.util.concurrent.TimeUnit;

4
application/src/main/java/org/thingsboard/server/service/security/system/DefaultSystemSecurityService.java

@ -19,7 +19,6 @@ import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.passay.CharacterRule;
import org.passay.EnglishCharacterData;
import org.passay.LengthRule;
@ -40,6 +39,7 @@ import org.springframework.stereotype.Service;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.api.MailService;
import org.thingsboard.server.common.data.AdminSettings;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardException;
@ -49,12 +49,12 @@ import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.security.UserCredentials;
import org.thingsboard.server.common.data.security.model.SecuritySettings;
import org.thingsboard.server.common.data.security.model.UserPasswordPolicy;
import org.thingsboard.server.common.data.security.model.mfa.PlatformTwoFaSettings;
import org.thingsboard.server.dao.audit.AuditLogService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.settings.AdminSettingsService;
import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.dao.user.UserServiceImpl;
import org.thingsboard.server.common.data.security.model.mfa.PlatformTwoFaSettings;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.auth.rest.RestAuthenticationDetails;
import org.thingsboard.server.service.security.exception.UserPasswordExpiredException;

4
application/src/main/java/org/thingsboard/server/service/sms/aws/AwsSmsSender.java

@ -23,10 +23,10 @@ import com.amazonaws.services.sns.AmazonSNSClient;
import com.amazonaws.services.sns.model.MessageAttributeValue;
import com.amazonaws.services.sns.model.PublishRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.thingsboard.server.common.data.sms.config.AwsSnsSmsProviderConfiguration;
import org.thingsboard.rule.engine.api.sms.exception.SmsException;
import org.thingsboard.rule.engine.api.sms.exception.SmsSendException;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.sms.config.AwsSnsSmsProviderConfiguration;
import org.thingsboard.server.service.sms.AbstractSmsSender;
import java.util.HashMap;

2
application/src/main/java/org/thingsboard/server/service/sms/smpp/SmppSmsSender.java

@ -16,7 +16,6 @@
package org.thingsboard.server.service.sms.smpp;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.smpp.Connection;
import org.smpp.Data;
@ -34,6 +33,7 @@ import org.smpp.pdu.PDUException;
import org.smpp.pdu.SubmitSM;
import org.smpp.pdu.SubmitSMResp;
import org.thingsboard.rule.engine.api.sms.exception.SmsException;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.sms.config.SmppSmsProviderConfiguration;
import org.thingsboard.server.service.sms.AbstractSmsSender;

6
application/src/main/java/org/thingsboard/server/service/sms/twilio/TwilioSmsSender.java

@ -18,11 +18,11 @@ package org.thingsboard.server.service.sms.twilio;
import com.twilio.http.TwilioRestClient;
import com.twilio.rest.api.v2010.account.Message;
import com.twilio.type.PhoneNumber;
import org.apache.commons.lang3.StringUtils;
import org.thingsboard.rule.engine.api.sms.exception.SmsParseException;
import org.thingsboard.server.common.data.sms.config.TwilioSmsProviderConfiguration;
import org.thingsboard.rule.engine.api.sms.exception.SmsException;
import org.thingsboard.rule.engine.api.sms.exception.SmsParseException;
import org.thingsboard.rule.engine.api.sms.exception.SmsSendException;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.sms.config.TwilioSmsProviderConfiguration;
import org.thingsboard.server.service.sms.AbstractSmsSender;
import java.util.regex.Pattern;

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

@ -17,22 +17,27 @@ package org.thingsboard.server.service.state;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
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.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.common.util.ThingsBoardExecutors;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.DeviceIdInfo;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
@ -42,7 +47,12 @@ import org.thingsboard.server.common.data.kv.LongDataEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry;
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.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.EntityKey;
import org.thingsboard.server.common.data.query.EntityKeyType;
import org.thingsboard.server.common.data.query.EntityListFilter;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.common.msg.TbMsgMetaData;
@ -51,10 +61,13 @@ import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.sql.query.EntityQueryRepository;
import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.timeseries.TimeseriesService;
import org.thingsboard.server.dao.util.DbTypeInfoComponent;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.partition.AbstractPartitionBasedService;
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
@ -66,16 +79,20 @@ import javax.annotation.PreDestroy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Random;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import static org.thingsboard.server.common.data.DataConstants.ACTIVITY_EVENT;
import static org.thingsboard.server.common.data.DataConstants.CONNECT_EVENT;
@ -98,8 +115,28 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService<Dev
public static final String INACTIVITY_ALARM_TIME = "inactivityAlarmTime";
public static final String INACTIVITY_TIMEOUT = "inactivityTimeout";
private static final List<EntityKey> PERSISTENT_TELEMETRY_KEYS = Arrays.asList(
new EntityKey(EntityKeyType.TIME_SERIES, LAST_ACTIVITY_TIME),
new EntityKey(EntityKeyType.TIME_SERIES, INACTIVITY_ALARM_TIME),
new EntityKey(EntityKeyType.TIME_SERIES, INACTIVITY_TIMEOUT),
new EntityKey(EntityKeyType.TIME_SERIES, ACTIVITY_STATE),
new EntityKey(EntityKeyType.TIME_SERIES, LAST_CONNECT_TIME),
new EntityKey(EntityKeyType.TIME_SERIES, LAST_DISCONNECT_TIME));
private static final List<EntityKey> PERSISTENT_ATTRIBUTE_KEYS = Arrays.asList(
new EntityKey(EntityKeyType.SERVER_ATTRIBUTE, LAST_ACTIVITY_TIME),
new EntityKey(EntityKeyType.SERVER_ATTRIBUTE, INACTIVITY_ALARM_TIME),
new EntityKey(EntityKeyType.SERVER_ATTRIBUTE, INACTIVITY_TIMEOUT),
new EntityKey(EntityKeyType.SERVER_ATTRIBUTE, ACTIVITY_STATE),
new EntityKey(EntityKeyType.SERVER_ATTRIBUTE, LAST_CONNECT_TIME),
new EntityKey(EntityKeyType.SERVER_ATTRIBUTE, LAST_DISCONNECT_TIME));
public static final List<String> PERSISTENT_ATTRIBUTES = Arrays.asList(ACTIVITY_STATE, LAST_CONNECT_TIME,
LAST_DISCONNECT_TIME, LAST_ACTIVITY_TIME, INACTIVITY_ALARM_TIME, INACTIVITY_TIMEOUT);
private static final List<EntityKey> PERSISTENT_ENTITY_FIELDS = Arrays.asList(
new EntityKey(EntityKeyType.ENTITY_FIELD, "name"),
new EntityKey(EntityKeyType.ENTITY_FIELD, "type"),
new EntityKey(EntityKeyType.ENTITY_FIELD, "createdTime"));
private final TenantService tenantService;
private final DeviceService deviceService;
@ -107,6 +144,9 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService<Dev
private final TimeseriesService tsService;
private final TbClusterService clusterService;
private final PartitionService partitionService;
private final TbServiceInfoProvider serviceInfoProvider;
private final EntityQueryRepository entityQueryRepository;
private final DbTypeInfoComponent dbTypeInfoComponent;
private TelemetrySubscriptionService tsSubService;
@ -122,23 +162,29 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService<Dev
@Getter
private boolean persistToTelemetry;
@Value("${state.initFetchPackSize:1000}")
@Value("${state.initFetchPackSize:50000}")
@Getter
private int initFetchPackSize;
private ExecutorService deviceStateExecutor;
private ListeningExecutorService deviceStateExecutor;
final ConcurrentMap<DeviceId, DeviceStateData> deviceStates = new ConcurrentHashMap<>();
public DefaultDeviceStateService(TenantService tenantService, DeviceService deviceService,
AttributesService attributesService, TimeseriesService tsService,
TbClusterService clusterService, PartitionService partitionService) {
TbClusterService clusterService, PartitionService partitionService,
TbServiceInfoProvider serviceInfoProvider,
EntityQueryRepository entityQueryRepository,
DbTypeInfoComponent dbTypeInfoComponent) {
this.tenantService = tenantService;
this.deviceService = deviceService;
this.attributesService = attributesService;
this.tsService = tsService;
this.clusterService = clusterService;
this.partitionService = partitionService;
this.serviceInfoProvider = serviceInfoProvider;
this.entityQueryRepository = entityQueryRepository;
this.dbTypeInfoComponent = dbTypeInfoComponent;
}
@Autowired
@ -149,8 +195,8 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService<Dev
@PostConstruct
public void init() {
super.init();
deviceStateExecutor = Executors.newFixedThreadPool(
Runtime.getRuntime().availableProcessors(), ThingsBoardThreadFactory.forName("device-state"));
deviceStateExecutor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(
Math.max(4, Runtime.getRuntime().availableProcessors()), "device-state"));
scheduledExecutor.scheduleAtFixedRate(this::updateInactivityStateIfExpired, new Random().nextInt(defaultStateCheckIntervalInSec), defaultStateCheckIntervalInSec, TimeUnit.SECONDS);
}
@ -167,7 +213,6 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService<Dev
return "Device State";
}
@Override
protected String getSchedulerExecutorName() {
return "device-state-scheduled";
@ -231,17 +276,16 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService<Dev
@Override
public void onDeviceInactivityTimeoutUpdate(TenantId tenantId, DeviceId deviceId, long inactivityTimeout) {
if (inactivityTimeout <= 0L) {
return;
}
if (cleanDeviceStateIfBelongsExternalPartition(tenantId, deviceId)) {
return;
}
if (inactivityTimeout <= 0L) {
inactivityTimeout = defaultInactivityTimeoutInSec;
}
log.trace("on Device Activity Timeout Update device id {} inactivityTimeout {}", deviceId, inactivityTimeout);
DeviceStateData stateData = getOrFetchDeviceStateData(deviceId);
stateData.getState().setInactivityTimeout(inactivityTimeout);
checkAndUpdateState(deviceId, stateData);
}
@Override
@ -260,8 +304,7 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService<Dev
@Override
public void onSuccess(@Nullable DeviceStateData state) {
TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, device.getId());
if (partitionedEntities.containsKey(tpi)) {
addDeviceUsingState(tpi, state);
if (addDeviceUsingState(tpi, state)) {
save(deviceId, ACTIVITY_STATE, false);
callback.onSuccess();
} else {
@ -297,60 +340,62 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService<Dev
}
@Override
protected void onAddedPartitions(Set<TopicPartitionInfo> addedPartitions) {
PageDataIterable<Tenant> tenantIterator = new PageDataIterable<>(tenantService::findTenants, 1024);
for (Tenant tenant : tenantIterator) {
log.debug("Finding devices for tenant [{}]", tenant.getName());
final PageLink pageLink = new PageLink(initFetchPackSize);
processPageAndSubmitNextPage(addedPartitions, tenant, pageLink);
}
}
private void processPageAndSubmitNextPage(final Set<TopicPartitionInfo> addedPartitions, final Tenant tenant, final PageLink pageLink) {
log.trace("[{}] Process page {} from {}", tenant, pageLink.getPage(), pageLink.getPageSize());
List<ListenableFuture<Void>> fetchFutures = new ArrayList<>();
PageData<Device> page = deviceService.findDevicesByTenantId(tenant.getId(), pageLink);
for (Device device : page.getData()) {
TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenant.getId(), device.getId());
if (addedPartitions.contains(tpi) && !deviceStates.containsKey(device.getId())) {
log.debug("[{}][{}] Device belong to current partition. tpi [{}]. Fetching state from DB", device.getName(), device.getId(), tpi);
ListenableFuture<Void> future = Futures.transform(fetchDeviceState(device), new Function<>() {
@Nullable
@Override
public Void apply(@Nullable DeviceStateData state) {
if (state != null) {
addDeviceUsingState(tpi, state);
checkAndUpdateState(device.getId(), state);
protected Map<TopicPartitionInfo, List<ListenableFuture<?>>> onAddedPartitions(Set<TopicPartitionInfo> addedPartitions) {
var result = new HashMap<TopicPartitionInfo, List<ListenableFuture<?>>>();
PageDataIterable<DeviceIdInfo> deviceIdInfos = new PageDataIterable<>(deviceService::findDeviceIdInfos, initFetchPackSize);
Map<TopicPartitionInfo, List<DeviceIdInfo>> tpiDeviceMap = new HashMap<>();
for (DeviceIdInfo idInfo : deviceIdInfos) {
TopicPartitionInfo tpi;
try {
tpi = partitionService.resolve(ServiceType.TB_CORE, idInfo.getTenantId(), idInfo.getDeviceId());
} catch (Exception e) {
log.warn("Failed to resolve partition for device with id [{}], tenant id [{}], customer id [{}]. Reason: {}",
idInfo.getDeviceId(), idInfo.getTenantId(), idInfo.getCustomerId(), e.getMessage());
continue;
}
if (addedPartitions.contains(tpi) && !deviceStates.containsKey(idInfo.getDeviceId())) {
tpiDeviceMap.computeIfAbsent(tpi, tmp -> new ArrayList<>()).add(idInfo);
}
}
for (var entry : tpiDeviceMap.entrySet()) {
AtomicInteger counter = new AtomicInteger(0);
// hard-coded limit of 1000 is due to the Entity Data Query limitations and should not be changed.
for (List<DeviceIdInfo> partition : Lists.partition(entry.getValue(), 1000)) {
log.info("[{}] Submit task for device states: {}", entry.getKey(), partition.size());
DevicePackFutureHolder devicePackFutureHolder = new DevicePackFutureHolder();
var devicePackFuture = deviceStateExecutor.submit(() -> {
try {
List<DeviceStateData> states;
if (persistToTelemetry && !dbTypeInfoComponent.isLatestTsDaoStoredToSql()) {
states = fetchDeviceStateDataUsingSeparateRequests(partition);
} else {
log.warn("{}][{}] Fetched null state from DB", device.getName(), device.getId());
states = fetchDeviceStateDataUsingEntityDataQuery(partition);
}
return null;
if (devicePackFutureHolder.future == null || !devicePackFutureHolder.future.isCancelled()) {
for (var state : states) {
if (!addDeviceUsingState(entry.getKey(), state)) {
return;
}
checkAndUpdateState(state.getDeviceId(), state);
}
log.info("[{}] Initialized {} out of {} device states", entry.getKey().getPartition().orElse(0), counter.addAndGet(states.size()), entry.getValue().size());
}
} catch (Throwable t) {
log.error("Unexpected exception while device pack fetching", t);
throw t;
}
}, deviceStateExecutor);
fetchFutures.add(future);
} else {
log.debug("[{}][{}] Device doesn't belong to current partition. tpi [{}]", device.getName(), device.getId(), tpi);
});
devicePackFutureHolder.future = devicePackFuture;
result.computeIfAbsent(entry.getKey(), tmp -> new ArrayList<>()).add(devicePackFuture);
}
}
return result;
}
Futures.addCallback(Futures.successfulAsList(fetchFutures), new FutureCallback<>() {
@Override
public void onSuccess(List<Void> result) {
log.trace("[{}] Success init device state from DB for batch size {}", tenant.getId(), result.size());
}
@Override
public void onFailure(Throwable t) {
log.warn("[" + tenant.getId() + "] Failed to init device state service from DB", t);
log.warn("[{}] Failed to init device state service from DB", tenant.getId(), t);
}
}, deviceStateExecutor);
final PageLink nextPageLink = page.hasNext() ? pageLink.nextPageLink() : null;
if (nextPageLink != null) {
log.trace("[{}] Submit next page {} from {}", tenant, nextPageLink.getPage(), nextPageLink.getPageSize());
processPageAndSubmitNextPage(addedPartitions, tenant, nextPageLink);
}
private static class DevicePackFutureHolder {
private volatile ListenableFuture<?> future;
}
void checkAndUpdateState(@Nonnull DeviceId deviceId, @Nonnull DeviceStateData state) {
@ -364,25 +409,34 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService<Dev
}
}
private void addDeviceUsingState(TopicPartitionInfo tpi, DeviceStateData state) {
private boolean addDeviceUsingState(TopicPartitionInfo tpi, DeviceStateData state) {
Set<DeviceId> deviceIds = partitionedEntities.get(tpi);
if (deviceIds != null) {
deviceIds.add(state.getDeviceId());
deviceStates.putIfAbsent(state.getDeviceId(), state);
return true;
} else {
log.debug("[{}] Device belongs to external partition {}", state.getDeviceId(), tpi.getFullTopicName());
throw new RuntimeException("Device belongs to external partition " + tpi.getFullTopicName() + "!");
return false;
}
}
void updateInactivityStateIfExpired() {
final long ts = System.currentTimeMillis();
partitionedEntities.forEach((tpi, deviceIds) -> {
log.debug("Calculating state updates. tpi {} for {} devices", tpi.getFullTopicName(), deviceIds.size());
for (DeviceId deviceId : deviceIds) {
updateInactivityStateIfExpired(ts, deviceId);
}
});
try {
final long ts = System.currentTimeMillis();
partitionedEntities.forEach((tpi, deviceIds) -> {
log.debug("Calculating state updates. tpi {} for {} devices", tpi.getFullTopicName(), deviceIds.size());
for (DeviceId deviceId : deviceIds) {
try {
updateInactivityStateIfExpired(ts, deviceId);
} catch (Exception e) {
log.warn("[{}] Failed to update inactivity state", deviceId, e);
}
}
});
} catch (Throwable t) {
log.warn("Failed to update inactivity states", t);
}
}
void updateInactivityStateIfExpired(long ts, DeviceId deviceId) {
@ -423,10 +477,10 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService<Dev
if (deviceStateData != null) {
return deviceStateData;
}
return fetchDeviceStateData(deviceId);
return fetchDeviceStateDataUsingEntityDataQuery(deviceId);
}
DeviceStateData fetchDeviceStateData(final DeviceId deviceId) {
DeviceStateData fetchDeviceStateDataUsingEntityDataQuery(final DeviceId deviceId) {
final Device device = deviceService.findDeviceById(TenantId.SYS_TENANT_ID, deviceId);
if (device == null) {
log.warn("[{}] Failed to fetch device by Id!", deviceId);
@ -540,6 +594,106 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService<Dev
};
}
private List<DeviceStateData> fetchDeviceStateDataUsingSeparateRequests(List<DeviceIdInfo> deviceIds) {
List<Device> devices = deviceService.findDevicesByIds(deviceIds.stream().map(DeviceIdInfo::getDeviceId).collect(Collectors.toList()));
List<ListenableFuture<DeviceStateData>> deviceStateFutures = new ArrayList<>();
for (Device device : devices) {
deviceStateFutures.add(fetchDeviceState(device));
}
try {
List<DeviceStateData> result = Futures.successfulAsList(deviceStateFutures).get(5, TimeUnit.MINUTES);
boolean success = true;
for (int i = 0; i < result.size(); i++) {
success = false;
if (result.get(i) == null) {
DeviceIdInfo deviceIdInfo = deviceIds.get(i);
log.warn("[{}][{}] Failed to initialized device state due to:", deviceIdInfo.getTenantId(), deviceIdInfo.getDeviceId());
}
}
return success ? result : result.stream().filter(Objects::nonNull).collect(Collectors.toList());
} catch (InterruptedException | ExecutionException | TimeoutException e) {
log.warn("Failed to initialized device state futures for ids: {} due to:", deviceIds, e);
throw new RuntimeException(e);
}
}
private List<DeviceStateData> fetchDeviceStateDataUsingEntityDataQuery(List<DeviceIdInfo> deviceIds) {
EntityListFilter ef = new EntityListFilter();
ef.setEntityType(EntityType.DEVICE);
ef.setEntityList(deviceIds.stream().map(DeviceIdInfo::getDeviceId).map(DeviceId::getId).map(UUID::toString).collect(Collectors.toList()));
EntityDataQuery query = new EntityDataQuery(ef,
new EntityDataPageLink(deviceIds.size(), 0, null, null),
PERSISTENT_ENTITY_FIELDS,
persistToTelemetry ? PERSISTENT_TELEMETRY_KEYS : PERSISTENT_ATTRIBUTE_KEYS, Collections.emptyList());
PageData<EntityData> queryResult = entityQueryRepository.findEntityDataByQueryInternal(query);
Map<EntityId, DeviceIdInfo> deviceIdInfos = deviceIds.stream().collect(Collectors.toMap(DeviceIdInfo::getDeviceId, java.util.function.Function.identity()));
return queryResult.getData().stream().map(ed -> toDeviceStateData(ed, deviceIdInfos.get(ed.getEntityId()))).collect(Collectors.toList());
}
private DeviceStateData toDeviceStateData(EntityData ed, DeviceIdInfo deviceIdInfo) {
long lastActivityTime = getEntryValue(ed, getKeyType(), LAST_ACTIVITY_TIME, 0L);
long inactivityAlarmTime = getEntryValue(ed, getKeyType(), INACTIVITY_ALARM_TIME, 0L);
long inactivityTimeout = getEntryValue(ed, getKeyType(), INACTIVITY_TIMEOUT, TimeUnit.SECONDS.toMillis(defaultInactivityTimeoutInSec));
//Actual active state by wall-clock will updated outside this method. This method is only for fetch persistent state
final boolean active = getEntryValue(ed, getKeyType(), ACTIVITY_STATE, false);
DeviceState deviceState = DeviceState.builder()
.active(active)
.lastConnectTime(getEntryValue(ed, getKeyType(), LAST_CONNECT_TIME, 0L))
.lastDisconnectTime(getEntryValue(ed, getKeyType(), LAST_DISCONNECT_TIME, 0L))
.lastActivityTime(lastActivityTime)
.lastInactivityAlarmTime(inactivityAlarmTime)
.inactivityTimeout(inactivityTimeout)
.build();
TbMsgMetaData md = new TbMsgMetaData();
md.putValue("deviceName", getEntryValue(ed, EntityKeyType.ENTITY_FIELD, "name", ""));
md.putValue("deviceType", getEntryValue(ed, EntityKeyType.ENTITY_FIELD, "type", ""));
return DeviceStateData.builder()
.customerId(deviceIdInfo.getCustomerId())
.tenantId(deviceIdInfo.getTenantId())
.deviceId(deviceIdInfo.getDeviceId())
.deviceCreationTime(getEntryValue(ed, EntityKeyType.ENTITY_FIELD, "createdTime", 0L))
.metaData(md)
.state(deviceState).build();
}
private EntityKeyType getKeyType() {
return persistToTelemetry ? EntityKeyType.TIME_SERIES : EntityKeyType.SERVER_ATTRIBUTE;
}
private String getEntryValue(EntityData ed, EntityKeyType keyType, String keyName, String defaultValue) {
return getEntryValue(ed, keyType, keyName, s -> s, defaultValue);
}
private long getEntryValue(EntityData ed, EntityKeyType keyType, String keyName, long defaultValue) {
return getEntryValue(ed, keyType, keyName, Long::parseLong, defaultValue);
}
private boolean getEntryValue(EntityData ed, EntityKeyType keyType, String keyName, boolean defaultValue) {
return getEntryValue(ed, keyType, keyName, Boolean::parseBoolean, defaultValue);
}
private <T> T getEntryValue(EntityData ed, EntityKeyType entityKeyType, String attributeName, Function<String, T> converter, T defaultValue) {
if (ed != null && ed.getLatest() != null) {
var map = ed.getLatest().get(entityKeyType);
if (map != null) {
var value = map.get(attributeName);
if (value != null && !StringUtils.isEmpty(value.getValue())) {
try {
return converter.apply(value.getValue());
} catch (Exception e) {
return defaultValue;
}
}
}
}
return defaultValue;
}
private long getEntryValue(List<? extends KvEntry> kvEntries, String attributeName, long defaultValue) {
if (kvEntries != null) {
for (KvEntry entry : kvEntries) {

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

@ -278,7 +278,15 @@ public class DefaultSubscriptionManagerService extends TbApplicationEventListene
private void updateDeviceInactivityTimeout(TenantId tenantId, EntityId entityId, List<? extends KvEntry> kvEntries) {
for (KvEntry kvEntry : kvEntries) {
if (kvEntry.getKey().equals(DefaultDeviceStateService.INACTIVITY_TIMEOUT)) {
deviceStateService.onDeviceInactivityTimeoutUpdate(tenantId, new DeviceId(entityId.getId()), kvEntry.getLongValue().orElse(0L));
deviceStateService.onDeviceInactivityTimeoutUpdate(tenantId, new DeviceId(entityId.getId()), getLongValue(kvEntry));
}
}
}
private void deleteDeviceInactivityTimeout(TenantId tenantId, EntityId entityId, List<String> keys) {
for (String key : keys) {
if (key.equals(DefaultDeviceStateService.INACTIVITY_TIMEOUT)) {
deviceStateService.onDeviceInactivityTimeoutUpdate(tenantId, new DeviceId(entityId.getId()), 0);
}
}
}
@ -340,6 +348,9 @@ public class DefaultSubscriptionManagerService extends TbApplicationEventListene
}
return subscriptionUpdate;
}, false);
if (entityId.getEntityType() == EntityType.DEVICE) {
deleteDeviceInactivityTimeout(tenantId, entityId, keys);
}
callback.onSuccess();
}
@ -364,6 +375,9 @@ public class DefaultSubscriptionManagerService extends TbApplicationEventListene
}
return subscriptionUpdate;
}, false);
if (entityId.getEntityType() == EntityType.DEVICE) {
deleteDeviceInactivityTimeout(tenantId, entityId, keys);
}
callback.onSuccess();
}
@ -557,4 +571,27 @@ public class DefaultSubscriptionManagerService extends TbApplicationEventListene
return new TbProtoQueueMsg<>(subscription.getEntityId().getId(), toCoreMsg);
}
private static long getLongValue(KvEntry kve) {
switch (kve.getDataType()) {
case LONG:
return kve.getLongValue().orElse(0L);
case DOUBLE:
return kve.getDoubleValue().orElse(0.0).longValue();
case STRING:
try {
return Long.parseLong(kve.getStrValue().orElse("0"));
} catch (NumberFormatException e) {
return 0L;
}
case JSON:
try {
return Long.parseLong(kve.getJsonValue().orElse("0"));
} catch (NumberFormatException e) {
return 0L;
}
default:
return 0L;
}
}
}

293
application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java

@ -29,13 +29,13 @@ import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.web.socket.CloseStatus;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
import org.thingsboard.server.common.data.kv.ReadTsKvQueryResult;
import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.query.AlarmDataQuery;
import org.thingsboard.server.common.data.query.ComparisonTsValue;
import org.thingsboard.server.common.data.query.EntityData;
import org.thingsboard.server.common.data.query.EntityDataQuery;
import org.thingsboard.server.common.data.query.EntityKey;
@ -50,6 +50,9 @@ import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.executors.DbCallbackExecutorService;
import org.thingsboard.server.service.telemetry.TelemetryWebSocketService;
import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef;
import org.thingsboard.server.service.telemetry.cmd.v2.AggHistoryCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.AggKey;
import org.thingsboard.server.service.telemetry.cmd.v2.AggTimeSeriesCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataUpdate;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountCmd;
@ -67,12 +70,12 @@ import javax.annotation.PreDestroy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@ -82,6 +85,7 @@ import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@SuppressWarnings("UnstableApiUsage")
@Slf4j
@TbCoreComponent
@Service
@ -130,6 +134,8 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
private int maxEntitiesPerAlarmSubscription;
@Value("${server.ws.dynamic_page_link.max_alarm_queries_per_refresh_interval:10}")
private int maxAlarmQueriesPerRefreshInterval;
@Value("${ui.dashboard.max_datapoints_limit:50000}")
private int maxDatapointLimit;
private ExecutorService wsCallBackExecutor;
private boolean tsInSqlDB;
@ -164,7 +170,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
TbEntityDataSubCtx ctx = getSubCtx(session.getSessionId(), cmd.getCmdId());
if (ctx != null) {
log.debug("[{}][{}] Updating existing subscriptions using: {}", session.getSessionId(), cmd.getCmdId(), cmd);
if (cmd.getLatestCmd() != null || cmd.getTsCmd() != null || cmd.getHistoryCmd() != null) {
if (cmd.hasAnyCmd()) {
ctx.clearEntitySubscriptions();
}
} else {
@ -172,6 +178,8 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
ctx = createSubCtx(session, cmd);
}
ctx.setCurrentCmd(cmd);
// Fetch entity list using entity data query
if (cmd.getQuery() != null) {
if (ctx.getQuery() == null) {
log.debug("[{}][{}] Initializing data using query: {}", session.getSessionId(), cmd.getCmdId(), cmd.getQuery());
@ -203,43 +211,143 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
finalCtx.setRefreshTask(task);
}
}
ListenableFuture<TbEntityDataSubCtx> historyFuture;
if (cmd.getHistoryCmd() != null) {
log.trace("[{}][{}] Going to process history command: {}", session.getSessionId(), cmd.getCmdId(), cmd.getHistoryCmd());
try {
historyFuture = handleHistoryCmd(ctx, cmd.getHistoryCmd());
} catch (RuntimeException e) {
handleWsCmdRuntimeException(ctx.getSessionId(), e, cmd);
return;
try {
List<ListenableFuture<?>> cmdFutures = new ArrayList<>();
if (cmd.getAggHistoryCmd() != null) {
cmdFutures.add(handleAggHistoryCmd(ctx, cmd.getAggHistoryCmd()));
}
} else {
historyFuture = Futures.immediateFuture(ctx);
if (cmd.getAggTsCmd() != null) {
cmdFutures.add(handleAggTsCmd(ctx, cmd.getAggTsCmd()));
}
if (cmd.getHistoryCmd() != null) {
cmdFutures.add(handleHistoryCmd(ctx, cmd.getHistoryCmd()));
}
if (cmdFutures.isEmpty()) {
handleRegularCommands(ctx, cmd);
} else {
TbEntityDataSubCtx finalCtx = ctx;
Futures.addCallback(Futures.allAsList(cmdFutures), new FutureCallback<>() {
@Override
public void onSuccess(@Nullable List<Object> result) {
handleRegularCommands(finalCtx, cmd);
}
@Override
public void onFailure(Throwable t) {
log.warn("[{}][{}] Failed to process command", finalCtx.getSessionId(), finalCtx.getCmdId());
}
}, wsCallBackExecutor);
}
} catch (RuntimeException e) {
handleWsCmdRuntimeException(ctx.getSessionId(), e, cmd);
}
}
private void handleRegularCommands(TbEntityDataSubCtx ctx, EntityDataCmd cmd) {
try {
if (cmd.getLatestCmd() != null || cmd.getTsCmd() != null) {
if (cmd.getLatestCmd() != null) {
handleLatestCmd(ctx, cmd.getLatestCmd());
}
if (cmd.getTsCmd() != null) {
handleTimeSeriesCmd(ctx, cmd.getTsCmd());
}
} else {
checkAndSendInitialData(ctx);
}
} catch (RuntimeException e) {
handleWsCmdRuntimeException(ctx.getSessionId(), e, cmd);
}
}
private void checkAndSendInitialData(@Nullable TbEntityDataSubCtx theCtx) {
if (!theCtx.isInitialDataSent()) {
EntityDataUpdate update = new EntityDataUpdate(theCtx.getCmdId(), theCtx.getData(), null, theCtx.getMaxEntitiesPerDataSubscription());
theCtx.sendWsMsg(update);
theCtx.setInitialDataSent(true);
}
}
private ListenableFuture<TbEntityDataSubCtx> handleAggHistoryCmd(TbEntityDataSubCtx ctx, AggHistoryCmd cmd) {
ConcurrentMap<Integer, ReadTsKvQueryInfo> queries = new ConcurrentHashMap<>();
for (AggKey key : cmd.getKeys()) {
if (key.getPreviousValueOnly() == null || !key.getPreviousValueOnly()) {
var query = new BaseReadTsKvQuery(key.getKey(), cmd.getStartTs(), cmd.getEndTs(), cmd.getEndTs() - cmd.getStartTs(), 1, key.getAgg());
queries.put(query.getId(), new ReadTsKvQueryInfo(key, query, false));
}
if (key.getPreviousStartTs() != null && key.getPreviousEndTs() != null && key.getPreviousEndTs() >= key.getPreviousStartTs()) {
var query = new BaseReadTsKvQuery(key.getKey(), key.getPreviousStartTs(), key.getPreviousEndTs(), key.getPreviousEndTs() - key.getPreviousStartTs(), 1, key.getAgg());
queries.put(query.getId(), new ReadTsKvQueryInfo(key, query, true));
}
}
return handleAggCmd(ctx, cmd.getKeys(), queries, cmd.getStartTs(), cmd.getEndTs(), false);
}
private ListenableFuture<TbEntityDataSubCtx> handleAggTsCmd(TbEntityDataSubCtx ctx, AggTimeSeriesCmd cmd) {
ConcurrentMap<Integer, ReadTsKvQueryInfo> queries = new ConcurrentHashMap<>();
for (AggKey key : cmd.getKeys()) {
var query = new BaseReadTsKvQuery(key.getKey(), cmd.getStartTs(), cmd.getStartTs() + cmd.getTimeWindow(), cmd.getTimeWindow(), 1, key.getAgg());
queries.put(query.getId(), new ReadTsKvQueryInfo(key, query, false));
}
Futures.addCallback(historyFuture, new FutureCallback<>() {
@Override
public void onSuccess(@Nullable TbEntityDataSubCtx theCtx) {
return handleAggCmd(ctx, cmd.getKeys(), queries, cmd.getStartTs(), cmd.getStartTs() + cmd.getTimeWindow(), true);
}
private ListenableFuture<TbEntityDataSubCtx> handleAggCmd(TbEntityDataSubCtx ctx, List<AggKey> keys, ConcurrentMap<Integer, ReadTsKvQueryInfo> queries,
long startTs, long endTs, boolean subscribe) {
Map<EntityData, ListenableFuture<List<ReadTsKvQueryResult>>> fetchResultMap = new HashMap<>();
List<EntityData> entityDataList = ctx.getData().getData();
List<ReadTsKvQuery> queryList = queries.values().stream().map(ReadTsKvQueryInfo::getQuery).collect(Collectors.toList());
entityDataList.forEach(entityData -> fetchResultMap.put(entityData,
tsService.findAllByQueries(ctx.getTenantId(), entityData.getEntityId(), queryList)));
return Futures.transform(Futures.allAsList(fetchResultMap.values()), f -> {
// Map that holds last ts for each key for each entity.
Map<EntityData, Map<String, Long>> lastTsEntityMap = new HashMap<>();
fetchResultMap.forEach((entityData, future) -> {
try {
if (cmd.getLatestCmd() != null || cmd.getTsCmd() != null) {
if (cmd.getLatestCmd() != null) {
handleLatestCmd(theCtx, cmd.getLatestCmd());
}
if (cmd.getTsCmd() != null) {
handleTimeSeriesCmd(theCtx, cmd.getTsCmd());
Map<String, Long> lastTsMap = new HashMap<>();
lastTsEntityMap.put(entityData, lastTsMap);
List<ReadTsKvQueryResult> queryResults = future.get();
if (queryResults != null) {
for (ReadTsKvQueryResult queryResult : queryResults) {
ReadTsKvQueryInfo queryInfo = queries.get(queryResult.getQueryId());
ComparisonTsValue comparisonTsValue = entityData.getAggLatest().computeIfAbsent(queryInfo.getKey().getId(), agg -> new ComparisonTsValue());
if (queryInfo.isPrevious()) {
comparisonTsValue.setPrevious(queryResult.toTsValue());
} else {
comparisonTsValue.setCurrent(queryResult.toTsValue());
lastTsMap.put(queryInfo.getQuery().getKey(), queryResult.getLastEntryTs());
}
}
} else if (!theCtx.isInitialDataSent()) {
EntityDataUpdate update = new EntityDataUpdate(theCtx.getCmdId(), theCtx.getData(), null, theCtx.getMaxEntitiesPerDataSubscription());
theCtx.sendWsMsg(update);
theCtx.setInitialDataSent(true);
}
} catch (RuntimeException e) {
handleWsCmdRuntimeException(theCtx.getSessionId(), e, cmd);
// Populate with empty values if no data found.
keys.forEach(key -> {
entityData.getAggLatest().putIfAbsent(key.getId(), new ComparisonTsValue(TsValue.EMPTY, TsValue.EMPTY));
});
} catch (InterruptedException | ExecutionException e) {
log.warn("[{}][{}][{}] Failed to fetch historical data", ctx.getSessionId(), ctx.getCmdId(), entityData.getEntityId(), e);
ctx.sendWsMsg(new EntityDataUpdate(ctx.getCmdId(), SubscriptionErrorCode.INTERNAL_ERROR.getCode(), "Failed to fetch historical data!"));
}
});
ctx.getWsLock().lock();
try {
EntityDataUpdate update;
if (!ctx.isInitialDataSent()) {
update = new EntityDataUpdate(ctx.getCmdId(), ctx.getData(), null, ctx.getMaxEntitiesPerDataSubscription());
ctx.setInitialDataSent(true);
} else {
update = new EntityDataUpdate(ctx.getCmdId(), null, entityDataList, ctx.getMaxEntitiesPerDataSubscription());
}
if (subscribe) {
ctx.createTimeSeriesSubscriptions(lastTsEntityMap, startTs, endTs, true);
}
ctx.sendWsMsg(update);
entityDataList.forEach(ed -> ed.getTimeseries().clear());
} finally {
ctx.getWsLock().unlock();
}
@Override
public void onFailure(Throwable t) {
log.warn("[{}][{}] Failed to process command", session.getSessionId(), cmd.getCmdId());
}
return ctx;
}, wsCallBackExecutor);
}
@ -294,25 +402,53 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
if (adq.getPageLink().getTimeWindow() > 0) {
TbAlarmDataSubCtx finalCtx = ctx;
ScheduledFuture<?> task = scheduler.scheduleWithFixedDelay(
finalCtx::checkAndResetInvocationCounter, dynamicPageLinkRefreshInterval, dynamicPageLinkRefreshInterval, TimeUnit.SECONDS);
() -> refreshAlarmQuery(finalCtx), dynamicPageLinkRefreshInterval, dynamicPageLinkRefreshInterval, TimeUnit.SECONDS);
finalCtx.setRefreshTask(task);
}
}
}
private void refreshDynamicQuery(TbAbstractSubCtx finalCtx) {
private boolean validate(TbAbstractSubCtx<?> finalCtx) {
if (finalCtx.isStopped()) {
log.warn("[{}][{}][{}] Received validation task for already stopped context.", finalCtx.getTenantId(), finalCtx.getSessionId(), finalCtx.getCmdId());
return false;
}
var cmdMap = subscriptionsBySessionId.get(finalCtx.getSessionId());
if (cmdMap == null) {
log.warn("[{}][{}][{}] Received validation task for already removed session.", finalCtx.getTenantId(), finalCtx.getSessionId(), finalCtx.getCmdId());
return false;
} else if (!cmdMap.containsKey(finalCtx.getCmdId())) {
log.warn("[{}][{}][{}] Received validation task for unregistered cmdId.", finalCtx.getTenantId(), finalCtx.getSessionId(), finalCtx.getCmdId());
return false;
}
return true;
}
private void refreshDynamicQuery(TbAbstractSubCtx<?> finalCtx) {
try {
long start = System.currentTimeMillis();
finalCtx.update();
long end = System.currentTimeMillis();
log.trace("[{}][{}] Executing query: {}", finalCtx.getSessionId(), finalCtx.getCmdId(), finalCtx.getQuery());
stats.getDynamicQueryInvocationCnt().incrementAndGet();
stats.getDynamicQueryTimeSpent().addAndGet(end - start);
if (validate(finalCtx)) {
long start = System.currentTimeMillis();
finalCtx.update();
long end = System.currentTimeMillis();
log.trace("[{}][{}] Executing query: {}", finalCtx.getSessionId(), finalCtx.getCmdId(), finalCtx.getQuery());
stats.getDynamicQueryInvocationCnt().incrementAndGet();
stats.getDynamicQueryTimeSpent().addAndGet(end - start);
} else {
finalCtx.stop();
}
} catch (Exception e) {
log.warn("[{}][{}] Failed to refresh query", finalCtx.getSessionId(), finalCtx.getCmdId(), e);
}
}
private void refreshAlarmQuery(TbAlarmDataSubCtx finalCtx) {
if (validate(finalCtx)) {
finalCtx.checkAndResetInvocationCounter();
} else {
finalCtx.stop();
}
}
@Scheduled(fixedDelayString = "${server.ws.dynamic_page_link.stats:10000}")
public void printStats() {
int alarmQueryInvocationCntValue = stats.getAlarmQueryInvocationCnt().getAndSet(0);
@ -387,36 +523,58 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
}
private ListenableFuture<TbEntityDataSubCtx> handleGetTsCmd(TbEntityDataSubCtx ctx, GetTsCmd cmd, boolean subscribe) {
Map<Integer, String> queriesKeys = new ConcurrentHashMap<>();
List<String> keys = cmd.getKeys();
List<ReadTsKvQuery> finalTsKvQueryList;
List<ReadTsKvQuery> tsKvQueryList = cmd.getKeys().stream().map(key -> new BaseReadTsKvQuery(
key, cmd.getStartTs(), cmd.getEndTs(), cmd.getInterval(), getLimit(cmd.getLimit()), cmd.getAgg()
)).collect(Collectors.toList());
List<ReadTsKvQuery> tsKvQueryList = keys.stream().map(key -> {
var query = new BaseReadTsKvQuery(
key, cmd.getStartTs(), cmd.getEndTs(), cmd.getInterval(), getLimit(cmd.getLimit()), cmd.getAgg()
);
queriesKeys.put(query.getId(), query.getKey());
return query;
}).collect(Collectors.toList());
if (cmd.isFetchLatestPreviousPoint()) {
finalTsKvQueryList = new ArrayList<>(tsKvQueryList);
finalTsKvQueryList.addAll(cmd.getKeys().stream().map(key -> new BaseReadTsKvQuery(
key, cmd.getStartTs() - TimeUnit.DAYS.toMillis(365), cmd.getStartTs(), cmd.getInterval(), 1, cmd.getAgg()
)).collect(Collectors.toList()));
finalTsKvQueryList.addAll(keys.stream().map(key -> {
var query = new BaseReadTsKvQuery(
key, cmd.getStartTs() - TimeUnit.DAYS.toMillis(365), cmd.getStartTs(), cmd.getInterval(), 1, cmd.getAgg());
queriesKeys.put(query.getId(), query.getKey());
return query;
}
).collect(Collectors.toList()));
} else {
finalTsKvQueryList = tsKvQueryList;
}
Map<EntityData, ListenableFuture<List<TsKvEntry>>> fetchResultMap = new HashMap<>();
ctx.getData().getData().forEach(entityData -> fetchResultMap.put(entityData,
tsService.findAll(ctx.getTenantId(), entityData.getEntityId(), finalTsKvQueryList)));
Map<EntityData, ListenableFuture<List<ReadTsKvQueryResult>>> fetchResultMap = new HashMap<>();
List<EntityData> entityDataList = ctx.getData().getData();
entityDataList.forEach(entityData -> fetchResultMap.put(entityData,
tsService.findAllByQueries(ctx.getTenantId(), entityData.getEntityId(), finalTsKvQueryList)));
return Futures.transform(Futures.allAsList(fetchResultMap.values()), f -> {
// Map that holds last ts for each key for each entity.
Map<EntityData, Map<String, Long>> lastTsEntityMap = new HashMap<>();
fetchResultMap.forEach((entityData, future) -> {
Map<String, List<TsValue>> keyData = new LinkedHashMap<>();
cmd.getKeys().forEach(key -> keyData.put(key, new ArrayList<>()));
try {
List<TsKvEntry> entityTsData = future.get();
if (entityTsData != null) {
entityTsData.forEach(entry -> keyData.get(entry.getKey()).add(new TsValue(entry.getTs(), entry.getValueAsString())));
Map<String, Long> lastTsMap = new HashMap<>();
lastTsEntityMap.put(entityData, lastTsMap);
List<ReadTsKvQueryResult> queryResults = future.get();
if (queryResults != null) {
for (ReadTsKvQueryResult queryResult : queryResults) {
String queryKey = queriesKeys.get(queryResult.getQueryId());
entityData.getTimeseries().put(queryKey, queryResult.toTsValues());
lastTsMap.put(queryKey, queryResult.getLastEntryTs());
}
}
keyData.forEach((k, v) -> entityData.getTimeseries().put(k, v.toArray(new TsValue[v.size()])));
// Populate with empty values if no data found.
keys.forEach(key -> {
if (!entityData.getTimeseries().containsKey(key)) {
entityData.getTimeseries().put(key, new TsValue[0]);
}
});
if (cmd.isFetchLatestPreviousPoint()) {
entityData.getTimeseries().values().forEach(dataArray -> {
Arrays.sort(dataArray, (o1, o2) -> Long.compare(o2.getTs(), o1.getTs()));
});
entityData.getTimeseries().values().forEach(dataArray -> Arrays.sort(dataArray, (o1, o2) -> Long.compare(o2.getTs(), o1.getTs())));
}
} catch (InterruptedException | ExecutionException e) {
log.warn("[{}][{}][{}] Failed to fetch historical data", ctx.getSessionId(), ctx.getCmdId(), entityData.getEntityId(), e);
@ -430,13 +588,13 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
update = new EntityDataUpdate(ctx.getCmdId(), ctx.getData(), null, ctx.getMaxEntitiesPerDataSubscription());
ctx.setInitialDataSent(true);
} else {
update = new EntityDataUpdate(ctx.getCmdId(), null, ctx.getData().getData(), ctx.getMaxEntitiesPerDataSubscription());
update = new EntityDataUpdate(ctx.getCmdId(), null, entityDataList, ctx.getMaxEntitiesPerDataSubscription());
}
if (subscribe) {
ctx.createTimeseriesSubscriptions(keys.stream().map(key -> new EntityKey(EntityKeyType.TIME_SERIES, key)).collect(Collectors.toList()), cmd.getStartTs(), cmd.getEndTs());
ctx.createTimeSeriesSubscriptions(lastTsEntityMap, cmd.getStartTs(), cmd.getEndTs());
}
ctx.sendWsMsg(update);
ctx.getData().getData().forEach(ed -> ed.getTimeseries().clear());
entityDataList.forEach(ed -> ed.getTimeseries().clear());
} finally {
ctx.getWsLock().unlock();
}
@ -504,11 +662,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
ctx.getWsLock().lock();
try {
ctx.createLatestValuesSubscriptions(latestCmd.getKeys());
if (!ctx.isInitialDataSent()) {
EntityDataUpdate update = new EntityDataUpdate(ctx.getCmdId(), ctx.getData(), null, ctx.getMaxEntitiesPerDataSubscription());
ctx.sendWsMsg(update);
ctx.setInitialDataSent(true);
}
checkAndSendInitialData(ctx);
} finally {
ctx.getWsLock().unlock();
}
@ -526,8 +680,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
private void cleanupAndCancel(TbAbstractSubCtx ctx) {
if (ctx != null) {
ctx.cancelTasks();
ctx.clearSubscriptions();
ctx.stop();
if (ctx.getSessionId() != null) {
Map<Integer, TbAbstractSubCtx> sessionSubs = subscriptionsBySessionId.get(ctx.getSessionId());
if (sessionSubs != null) {

29
application/src/main/java/org/thingsboard/server/service/subscription/ReadTsKvQueryInfo.java

@ -0,0 +1,29 @@
/**
* Copyright © 2016-2022 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.subscription;
import lombok.Data;
import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
import org.thingsboard.server.service.telemetry.cmd.v2.AggKey;
@Data
public class ReadTsKvQueryInfo {
private final AggKey key;
private final ReadTsKvQuery query;
private final boolean previous;
}

25
application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractDataSubCtx.java

@ -86,7 +86,7 @@ public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery<? extends
} else {
oldDataMap = Collections.emptyMap();
}
Map<EntityId, EntityData> newDataMap = newData.getData().stream().collect(Collectors.toMap(EntityData::getEntityId, Function.identity(), (a,b)-> a));
Map<EntityId, EntityData> newDataMap = newData.getData().stream().collect(Collectors.toMap(EntityData::getEntityId, Function.identity(), (a, b) -> a));
if (oldDataMap.size() == newDataMap.size() && oldDataMap.keySet().equals(newDataMap.keySet())) {
log.trace("[{}][{}] No updates to entity data found", sessionRef.getSessionId(), cmdId);
} else {
@ -122,8 +122,17 @@ public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery<? extends
createSubscriptions(keys, true, 0, 0);
}
public void createTimeseriesSubscriptions(List<EntityKey> keys, long startTs, long endTs) {
createSubscriptions(keys, false, startTs, endTs);
public void createTimeSeriesSubscriptions(Map<EntityData, Map<String, Long>> entityKeyStates, long startTs, long endTs) {
createTimeSeriesSubscriptions(entityKeyStates, startTs, endTs, false);
}
public void createTimeSeriesSubscriptions(Map<EntityData, Map<String, Long>> entityKeyStates, long startTs, long endTs, boolean resultToLatestValues) {
entityKeyStates.forEach((entityData, keyStates) -> {
int subIdx = sessionRef.getSessionSubIdSeq().incrementAndGet();
subToEntityIdMap.put(subIdx, entityData.getEntityId());
localSubscriptionService.addSubscription(
createTsSub(entityData, subIdx, false, startTs, endTs, keyStates, resultToLatestValues));
});
}
private void createSubscriptions(List<EntityKey> keys, boolean latestValues, long startTs, long endTs) {
@ -191,6 +200,14 @@ public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery<? extends
keyStates.put(k, ts);
});
}
return createTsSub(entityData, subIdx, latestValues, startTs, endTs, keyStates);
}
private TbTimeseriesSubscription createTsSub(EntityData entityData, int subIdx, boolean latestValues, long startTs, long endTs, Map<String, Long> keyStates) {
return createTsSub(entityData, subIdx, latestValues, startTs, endTs, keyStates, latestValues);
}
private TbTimeseriesSubscription createTsSub(EntityData entityData, int subIdx, boolean latestValues, long startTs, long endTs, Map<String, Long> keyStates, boolean resultToLatestValues) {
log.trace("[{}][{}][{}] Creating time-series subscription for [{}] with keys: {}", serviceId, cmdId, subIdx, entityData.getEntityId(), keyStates);
return TbTimeseriesSubscription.builder()
.serviceId(serviceId)
@ -198,7 +215,7 @@ public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery<? extends
.subscriptionId(subIdx)
.tenantId(sessionRef.getSecurityCtx().getTenantId())
.entityId(entityData.getEntityId())
.updateConsumer((sessionId, subscriptionUpdate) -> sendWsMsg(sessionId, subscriptionUpdate, EntityKeyType.TIME_SERIES, latestValues))
.updateConsumer((sessionId, subscriptionUpdate) -> sendWsMsg(sessionId, subscriptionUpdate, EntityKeyType.TIME_SERIES, resultToLatestValues))
.allKeys(false)
.keyStates(keyStates)
.latestValues(latestValues)

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

@ -78,6 +78,7 @@ public abstract class TbAbstractSubCtx<T extends EntityCountQuery> {
protected T query;
@Setter
protected volatile ScheduledFuture<?> refreshTask;
protected volatile boolean stopped;
public TbAbstractSubCtx(String serviceId, TelemetryWebSocketService wsService,
EntityService entityService, TbLocalSubscriptionService localSubscriptionService,
@ -189,6 +190,12 @@ public abstract class TbAbstractSubCtx<T extends EntityCountQuery> {
clearDynamicValueSubscriptions();
}
public void stop() {
stopped = true;
cancelTasks();
clearSubscriptions();
}
@Data
private static class DynamicValueKeySub {
private final DynamicValueKey key;
@ -299,7 +306,11 @@ public abstract class TbAbstractSubCtx<T extends EntityCountQuery> {
}
public void setRefreshTask(ScheduledFuture<?> task) {
this.refreshTask = task;
if (!stopped) {
this.refreshTask = task;
} else {
task.cancel(true);
}
}
public void cancelTasks() {

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

@ -20,7 +20,6 @@ import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import lombok.Data;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContext;
@ -29,6 +28,7 @@ import org.thingsboard.common.util.DonAsynchron;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.id.EntityId;

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

@ -20,7 +20,6 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
@ -145,7 +144,6 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer
}
}
@NotNull
private FutureCallback<Integer> getCallback(TenantId tenantId, CustomerId customerId, boolean sysTenant, FutureCallback<Void> callback) {
return new FutureCallback<>() {
@Override

4
application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java

@ -26,11 +26,11 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.socket.CloseStatus;
import org.thingsboard.common.util.ThingsBoardExecutors;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
@ -716,7 +716,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
"Cmd id is negative value!");
sendWsMsg(sessionRef, update);
return false;
} else if (cmd.getQuery() == null && cmd.getLatestCmd() == null && cmd.getHistoryCmd() == null && cmd.getTsCmd() == null) {
} else if (cmd.getQuery() == null && !cmd.hasAnyCmd()) {
TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST,
"Query is empty!");
sendWsMsg(sessionRef, update);

8
common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MBootstrapServersConfiguration.java → application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/AggHistoryCmd.java

@ -13,15 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap;
package org.thingsboard.server.service.telemetry.cmd.v2;
import lombok.Data;
import java.util.List;
@Data
public class LwM2MBootstrapServersConfiguration {
public class AggHistoryCmd {
List<LwM2MBootstrapServerCredential> bootstrap;
private List<AggKey> keys;
private long startTs;
private long endTs;
}

32
application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/AggKey.java

@ -0,0 +1,32 @@
/**
* Copyright © 2016-2022 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.telemetry.cmd.v2;
import lombok.Data;
import org.thingsboard.server.common.data.kv.Aggregation;
@Data
public class AggKey {
private int id;
private String key;
private Aggregation agg;
private Long previousStartTs;
private Long previousEndTs;
private Boolean previousValueOnly;
}

19
common/data/src/main/java/org/thingsboard/server/common/data/event/DebugRuleNodeEventFilter.java → application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/AggTimeSeriesCmd.java

@ -13,14 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.event;
package org.thingsboard.server.service.telemetry.cmd.v2;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import java.util.List;
@Data
public class AggTimeSeriesCmd {
private List<AggKey> keys;
private long startTs;
private long timeWindow;
@ApiModel
public class DebugRuleNodeEventFilter extends DebugEvent {
@Override
public EventType getEventType() {
return EventType.DEBUG_RULE_NODE;
}
}

21
application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/EntityDataCmd.java

@ -16,6 +16,7 @@
package org.thingsboard.server.service.telemetry.cmd.v2;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import org.thingsboard.server.common.data.query.EntityDataQuery;
@ -30,17 +31,35 @@ public class EntityDataCmd extends DataCmd {
private final LatestValueCmd latestCmd;
@Getter
private final TimeSeriesCmd tsCmd;
@Getter
private final AggHistoryCmd aggHistoryCmd;
@Getter
private final AggTimeSeriesCmd aggTsCmd;
public EntityDataCmd(int cmdId, EntityDataQuery query, EntityHistoryCmd historyCmd, LatestValueCmd latestCmd, TimeSeriesCmd tsCmd) {
this(cmdId, query, historyCmd, latestCmd, tsCmd, null, null);
}
@JsonCreator
public EntityDataCmd(@JsonProperty("cmdId") int cmdId,
@JsonProperty("query") EntityDataQuery query,
@JsonProperty("historyCmd") EntityHistoryCmd historyCmd,
@JsonProperty("latestCmd") LatestValueCmd latestCmd,
@JsonProperty("tsCmd") TimeSeriesCmd tsCmd) {
@JsonProperty("tsCmd") TimeSeriesCmd tsCmd,
@JsonProperty("aggHistoryCmd") AggHistoryCmd aggHistoryCmd,
@JsonProperty("aggTsCmd") AggTimeSeriesCmd aggTsCmd) {
super(cmdId);
this.query = query;
this.historyCmd = historyCmd;
this.latestCmd = latestCmd;
this.tsCmd = tsCmd;
this.aggHistoryCmd = aggHistoryCmd;
this.aggTsCmd = aggTsCmd;
}
@JsonIgnore
public boolean hasAnyCmd() {
return historyCmd != null || latestCmd != null || tsCmd != null || aggHistoryCmd != null || aggTsCmd != null;
}
}

29
application/src/main/java/org/thingsboard/server/service/ttl/EventsCleanUpService.java

@ -25,7 +25,6 @@ import org.thingsboard.server.queue.util.TbCoreComponent;
import java.util.concurrent.TimeUnit;
@TbCoreComponent
@Slf4j
@Service
public class EventsCleanUpService extends AbstractCleanUpService {
@ -39,9 +38,6 @@ public class EventsCleanUpService extends AbstractCleanUpService {
@Value("${sql.ttl.events.debug_events_ttl}")
private long debugTtlInSec;
@Value("${sql.ttl.events.execution_interval_ms}")
private long executionIntervalInMs;
@Value("${sql.ttl.events.enabled}")
private boolean ttlTaskExecutionEnabled;
@ -54,28 +50,11 @@ public class EventsCleanUpService extends AbstractCleanUpService {
@Scheduled(initialDelayString = RANDOM_DELAY_INTERVAL_MS_EXPRESSION, fixedDelayString = "${sql.ttl.events.execution_interval_ms}")
public void cleanUp() {
if (ttlTaskExecutionEnabled && isSystemTenantPartitionMine()) {
if (ttlTaskExecutionEnabled) {
long ts = System.currentTimeMillis();
long regularEventStartTs;
long regularEventEndTs;
long debugEventStartTs;
long debugEventEndTs;
if (ttlInSec > 0) {
regularEventEndTs = ts - TimeUnit.SECONDS.toMillis(ttlInSec);
regularEventStartTs = regularEventEndTs - 2 * executionIntervalInMs;
} else {
regularEventStartTs = regularEventEndTs = 0;
}
if (debugTtlInSec > 0) {
debugEventEndTs = ts - TimeUnit.SECONDS.toMillis(debugTtlInSec);
debugEventStartTs = debugEventEndTs - 2 * executionIntervalInMs;
} else {
debugEventStartTs = debugEventEndTs = 0;
}
eventService.cleanupEvents(regularEventStartTs, regularEventEndTs, debugEventStartTs, debugEventEndTs);
long regularEventExpTs = ttlInSec > 0 ? ts - TimeUnit.SECONDS.toMillis(ttlInSec) : 0;
long debugEventExpTs = debugTtlInSec > 0 ? ts - TimeUnit.SECONDS.toMillis(debugTtlInSec) : 0;
eventService.cleanupEvents(regularEventExpTs, debugEventExpTs, isSystemTenantPartitionMine());
}
}

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

@ -39,7 +39,7 @@ server:
key_password: "${SSL_PEM_KEY_PASSWORD:server_key_password}"
# Keystore server credentials
keystore:
# Type of the key store
# Type of the key store (JKS or PKCS12)
type: "${SSL_KEY_STORE_TYPE:PKCS12}"
# Path to the key store that holds the SSL certificate
store_file: "${SSL_KEY_STORE:classpath:keystore/keystore.p12}"
@ -148,7 +148,7 @@ ui:
# Help parameters
help:
# Base url for UI help assets
base-url: "${UI_HELP_BASE_URL:https://raw.githubusercontent.com/thingsboard/thingsboard-ui-help/release-3.4}"
base-url: "${UI_HELP_BASE_URL:https://raw.githubusercontent.com/thingsboard/thingsboard-ui-help/release-3.4.1}"
database:
ts_max_intervals: "${DATABASE_TS_MAX_INTERVALS:700}" # Max number of DB queries generated by single API call to fetch telemetry records
@ -221,9 +221,6 @@ cassandra:
ts_key_value_partitioning: "${TS_KV_PARTITIONING:MONTHS}"
ts_key_value_partitions_max_cache_size: "${TS_KV_PARTITIONS_MAX_CACHE_SIZE:100000}"
ts_key_value_ttl: "${TS_KV_TTL:0}"
events_ttl: "${TS_EVENTS_TTL:0}"
# Specify TTL of debug log in seconds. The current value corresponds to one week
debug_events_ttl: "${DEBUG_EVENTS_TTL:604800}"
buffer_size: "${CASSANDRA_QUERY_BUFFER_SIZE:200000}"
concurrent_limit: "${CASSANDRA_QUERY_CONCURRENT_LIMIT:1000}"
permit_max_wait_time: "${PERMIT_MAX_WAIT_TIME:120000}"
@ -262,6 +259,8 @@ sql:
batch_max_delay: "${SQL_EVENTS_BATCH_MAX_DELAY_MS:100}"
stats_print_interval_ms: "${SQL_EVENTS_BATCH_STATS_PRINT_MS:10000}"
batch_threads: "${SQL_EVENTS_BATCH_THREADS:3}" # batch thread count have to be a prime number like 3 or 5 to gain perfect hash distribution
partition_size: "${SQL_EVENTS_REGULAR_PARTITION_SIZE_HOURS:168}" # Number of hours to partition the events. The current value corresponds to one week.
debug_partition_size: "${SQL_EVENTS_DEBUG_PARTITION_SIZE_HOURS:1}" # Number of hours to partition the debug events. The current value corresponds to one hour.
edge_events:
batch_size: "${SQL_EDGE_EVENTS_BATCH_SIZE:1000}"
batch_max_delay: "${SQL_EDGE_EVENTS_BATCH_MAX_DELAY_MS:100}"
@ -287,9 +286,11 @@ sql:
ts_key_value_ttl: "${SQL_TTL_TS_TS_KEY_VALUE_TTL:0}" # Number of seconds
events:
enabled: "${SQL_TTL_EVENTS_ENABLED:true}"
execution_interval_ms: "${SQL_TTL_EVENTS_EXECUTION_INTERVAL:2220000}" # Number of milliseconds (max random initial delay and fixed period). # 37minutes to avoid common interval spikes
events_ttl: "${SQL_TTL_EVENTS_EVENTS_TTL:0}" # Number of seconds
debug_events_ttl: "${SQL_TTL_EVENTS_DEBUG_EVENTS_TTL:604800}" # Number of seconds. The current value corresponds to one week
execution_interval_ms: "${SQL_TTL_EVENTS_EXECUTION_INTERVAL:3600000}" # Number of milliseconds (max random initial delay and fixed period).
# Number of seconds. TTL is disabled by default. Accuracy of the cleanup depends on the sql.events.partition_size parameter.
events_ttl: "${SQL_TTL_EVENTS_EVENTS_TTL:0}"
# Number of seconds. The current value corresponds to one week. Accuracy of the cleanup depends on the sql.events.debug_partition_size parameter.
debug_events_ttl: "${SQL_TTL_EVENTS_DEBUG_EVENTS_TTL:604800}"
edge_events:
enabled: "${SQL_TTL_EDGE_EVENTS_ENABLED:true}"
execution_interval_ms: "${SQL_TTL_EDGE_EVENTS_EXECUTION_INTERVAL:86400000}" # Number of milliseconds. The current value corresponds to one day
@ -684,7 +685,7 @@ transport:
key_password: "${MQTT_SSL_PEM_KEY_PASSWORD:server_key_password}"
# Keystore server credentials
keystore:
# Type of the key store
# Type of the key store (JKS or PKCS12)
type: "${MQTT_SSL_KEY_STORE_TYPE:JKS}"
# Path to the key store that holds the SSL certificate
store_file: "${MQTT_SSL_KEY_STORE:mqttserver.jks}"
@ -727,7 +728,7 @@ transport:
key_password: "${COAP_DTLS_PEM_KEY_PASSWORD:server_key_password}"
# Keystore server credentials
keystore:
# Type of the key store
# Type of the key store (JKS or PKCS12)
type: "${COAP_DTLS_KEY_STORE_TYPE:JKS}"
# Path to the key store that holds the SSL certificate
store_file: "${COAP_DTLS_KEY_STORE:coapserver.jks}"
@ -769,7 +770,7 @@ transport:
key_password: "${LWM2M_SERVER_PEM_KEY_PASSWORD:server_key_password}"
# Keystore server credentials
keystore:
# Type of the key store
# Type of the key store (JKS or PKCS12)
type: "${LWM2M_SERVER_KEY_STORE_TYPE:JKS}"
# Path to the key store that holds the SSL certificate
store_file: "${LWM2M_SERVER_KEY_STORE:lwm2mserver.jks}"
@ -805,7 +806,7 @@ transport:
key_password: "${LWM2M_BS_PEM_KEY_PASSWORD:server_key_password}"
# Keystore server credentials
keystore:
# Type of the key store
# Type of the key store (JKS or PKCS12)
type: "${LWM2M_BS_KEY_STORE_TYPE:JKS}"
# Path to the key store that holds the SSL certificate
store_file: "${LWM2M_BS_KEY_STORE:lwm2mserver.jks}"
@ -828,7 +829,7 @@ transport:
cert_file: "${LWM2M_TRUST_PEM_CERT:lwm2mtruststorechain.pem}"
# Keystore with trust certificates
keystore:
# Type of the key store
# Type of the key store (JKS or PKCS12)
type: "${LWM2M_TRUST_KEY_STORE_TYPE:JKS}"
# Path to the key store that holds the X509 certificates
store_file: "${LWM2M_TRUST_KEY_STORE:lwm2mtruststorechain.jks}"
@ -1037,7 +1038,7 @@ queue:
poll-interval: "${TB_QUEUE_VC_INTERVAL_MS:25}"
pack-processing-timeout: "${TB_QUEUE_VC_PACK_PROCESSING_TIMEOUT_MS:60000}"
request-timeout: "${TB_QUEUE_VC_REQUEST_TIMEOUT:60000}"
msg-chunk-size: "${TB_QUEUE_VC_MSG_CHUNK_SIZE:500000}"
msg-chunk-size: "${TB_QUEUE_VC_MSG_CHUNK_SIZE:250000}"
js:
# JS Eval request topic
request_topic: "${REMOTE_JS_EVAL_REQUEST_TOPIC:js_eval.requests}"

3
application/src/test/java/org/thingsboard/server/actors/stats/StatsActorTest.java

@ -18,7 +18,8 @@ package org.thingsboard.server.actors.stats;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.common.data.Event;
import org.thingsboard.server.common.data.EventInfo;
import org.thingsboard.server.common.data.event.Event;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.event.EventService;
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;

17
application/src/test/java/org/thingsboard/server/controller/AbstractRuleEngineControllerTest.java

@ -19,8 +19,8 @@ import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.TestPropertySource;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Event;
import org.thingsboard.server.common.data.EventInfo;
import org.thingsboard.server.common.data.event.EventType;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
@ -60,20 +60,19 @@ public abstract class AbstractRuleEngineControllerTest extends AbstractControlle
return doGet("/api/ruleChain/metadata/" + ruleChainId.getId().toString(), RuleChainMetaData.class);
}
protected PageData<Event> getDebugEvents(TenantId tenantId, EntityId entityId, int limit) throws Exception {
return getEvents(tenantId, entityId, DataConstants.DEBUG_RULE_NODE, limit);
protected PageData<EventInfo> getDebugEvents(TenantId tenantId, EntityId entityId, int limit) throws Exception {
return getEvents(tenantId, entityId, EventType.DEBUG_RULE_NODE.getOldName(), limit);
}
protected PageData<Event> getEvents(TenantId tenantId, EntityId entityId, String eventType, int limit) throws Exception {
protected PageData<EventInfo> getEvents(TenantId tenantId, EntityId entityId, String eventType, int limit) throws Exception {
TimePageLink pageLink = new TimePageLink(limit);
return doGetTypedWithTimePageLink("/api/events/{entityType}/{entityId}/{eventType}?tenantId={tenantId}&",
new TypeReference<PageData<Event>>() {
new TypeReference<PageData<EventInfo>>() {
}, pageLink, entityId.getEntityType(), entityId.getId(), eventType, tenantId.getId());
}
protected JsonNode getMetadata(Event outEvent) {
protected JsonNode getMetadata(EventInfo outEvent) {
String metaDataStr = outEvent.getBody().get("metadata").asText();
try {
return mapper.readTree(metaDataStr);
@ -82,7 +81,7 @@ public abstract class AbstractRuleEngineControllerTest extends AbstractControlle
}
}
protected Predicate<Event> filterByCustomEvent() {
protected Predicate<EventInfo> filterByCustomEvent() {
return event -> event.getBody().get("msgType").textValue().equals("CUSTOM");
}

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

@ -26,9 +26,8 @@ import io.jsonwebtoken.Header;
import io.jsonwebtoken.Jwt;
import io.jsonwebtoken.Jwts;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.hamcrest.Matcher;
import org.hibernate.exception.ConstraintViolationException;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@ -36,6 +35,7 @@ import org.junit.Rule;
import org.junit.rules.TestRule;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
import org.mockito.BDDMockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
@ -56,6 +56,7 @@ import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceProfileType;
import org.thingsboard.server.common.data.DeviceTransportType;
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.device.profile.DefaultDeviceProfileConfiguration;
@ -68,28 +69,33 @@ import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadCo
import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.HasId;
import org.thingsboard.server.common.data.id.QueueId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UUIDBased;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.config.ThingsboardSecurityConfiguration;
import org.thingsboard.server.dao.Dao;
import org.thingsboard.server.dao.tenant.TenantProfileService;
import org.thingsboard.server.service.mail.TestMailService;
import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRequest;
import org.thingsboard.server.service.security.auth.rest.LoginRequest;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
@ -658,7 +664,11 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
}
protected <T> T readResponse(ResultActions result, TypeReference<T> type) throws Exception {
byte[] content = result.andReturn().getResponse().getContentAsByteArray();
return readResponse(result.andReturn(), type);
}
protected <T> T readResponse(MvcResult result, TypeReference<T> type) throws Exception {
byte[] content = result.getResponse().getContentAsByteArray();
return mapper.readerFor(type).readValue(content);
}
@ -686,8 +696,8 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
edge.setTenantId(tenantId);
edge.setName(name);
edge.setType(type);
edge.setSecret(RandomStringUtils.randomAlphanumeric(20));
edge.setRoutingKey(RandomStringUtils.randomAlphanumeric(20));
edge.setSecret(StringUtils.randomAlphanumeric(20));
edge.setRoutingKey(StringUtils.randomAlphanumeric(20));
return edge;
}
@ -701,4 +711,49 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
return Futures.allAsList(futures);
}
protected void testEntityDaoWithRelationsOk(EntityId entityIdFrom, EntityId entityTo, String urlDelete) throws Exception {
createEntityRelation(entityIdFrom, entityTo, "TEST_TYPE");
assertThat(findRelationsByTo(entityTo)).hasSize(1);
doDelete(urlDelete).andExpect(status().isOk());
assertThat(findRelationsByTo(entityTo)).hasSize(0);
}
protected <T> void testEntityDaoWithRelationsTransactionalException(Dao<T> dao, EntityId entityIdFrom, EntityId entityTo,
String urlDelete) throws Exception {
entityDaoRemoveByIdWithException (dao);
createEntityRelation(entityIdFrom, entityTo, "TEST_TRANSACTIONAL_TYPE");
assertThat(findRelationsByTo(entityTo)).hasSize(1);
doDelete(urlDelete)
.andExpect(status().isInternalServerError());
assertThat(findRelationsByTo(entityTo)).hasSize(1);
}
protected <T> void entityDaoRemoveByIdWithException (Dao<T> dao) throws Exception {
BDDMockito.willThrow(new ConstraintViolationException("mock message", new SQLException(), "MOCK_CONSTRAINT"))
.given(dao).removeById(any(), any());
}
protected <T> void afterTestEntityDaoRemoveByIdWithException (Dao<T> dao) throws Exception {
BDDMockito.willCallRealMethod().given(dao).removeById(any(), any());
}
protected void createEntityRelation(EntityId entityIdFrom, EntityId entityIdTo, String typeRelation) throws Exception {
EntityRelation relation = new EntityRelation(entityIdFrom, entityIdTo, typeRelation);
doPost("/api/relation", relation);
}
protected List<EntityRelation> findRelationsByTo(EntityId entityId) throws Exception {
String url = String.format("/api/relations?toId=%s&toType=%s", entityId.getId(), entityId.getEntityType().name());
MvcResult mvcResult = doGet(url).andReturn();
switch (mvcResult.getResponse().getStatus()) {
case 200: return readResponse(mvcResult, new TypeReference<>() {});
case 404: return Collections.emptyList();
}
throw new AssertionError("Unexpected status " + mvcResult.getResponse().getStatus());
}
}

25
application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java

@ -23,6 +23,7 @@ import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntityType;
@ -31,7 +32,9 @@ import org.thingsboard.server.common.data.alarm.AlarmInfo;
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.alarm.AlarmStatus;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.dao.alarm.AlarmDao;
import java.util.LinkedList;
import java.util.List;
@ -46,6 +49,10 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest {
protected Device customerDevice;
@SpyBean
private AlarmDao alarmDao;
@Before
public void setup() throws Exception {
loginTenantAdmin();
@ -64,6 +71,9 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest {
@After
public void teardown() throws Exception {
loginSysAdmin();
afterTestEntityDaoRemoveByIdWithException (alarmDao);
deleteDifferentTenant();
}
@ -421,6 +431,21 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest {
Assert.assertTrue("Created alarm doesn't match the found one!", equals);
}
@Test
public void testDeleteAlarmWithDeleteRelationsOk() throws Exception {
loginCustomerUser();
AlarmId alarmId = createAlarm("Alarm for Test WithRelationsOk").getId();
testEntityDaoWithRelationsOk(customerDevice.getId(), alarmId, "/api/alarm/" + alarmId);
}
@Test
public void testDeleteAlarmExceptionWithRelationsTransactional() throws Exception {
loginCustomerUser();
AlarmId alarmId = createAlarm("Alarm for Test WithRelations Transactional Exception").getId();
testEntityDaoWithRelationsTransactionalException(alarmDao, customerDevice.getId(), alarmId, "/api/alarm/" + alarmId);
}
private Alarm createAlarm(String type) throws Exception {
Alarm alarm = Alarm.builder()
.tenantId(tenantId)

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

@ -17,24 +17,27 @@ package org.thingsboard.server.controller;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.fasterxml.jackson.core.type.TypeReference;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityView;
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.asset.Asset;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.edge.Edge;
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.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.service.stats.DefaultRuleEngineStatisticsService;
@ -54,6 +57,9 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
private Tenant savedTenant;
private User tenantAdmin;
@SpyBean
private AssetDao assetDao;
@Before
public void beforeTest() throws Exception {
loginSysAdmin();
@ -77,6 +83,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
public void afterTest() throws Exception {
loginSysAdmin();
afterTestEntityDaoRemoveByIdWithException (assetDao);
doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
.andExpect(status().isOk());
}
@ -117,7 +125,7 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
@Test
public void testSaveAssetWithViolationOfLengthValidation() throws Exception {
Asset asset = new Asset();
asset.setName(RandomStringUtils.randomAlphabetic(300));
asset.setName(StringUtils.randomAlphabetic(300));
asset.setType("default");
Mockito.reset(tbClusterService, auditLogService);
@ -132,7 +140,7 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
Mockito.reset(tbClusterService, auditLogService);
asset.setName("Normal name");
asset.setType(RandomStringUtils.randomAlphabetic(300));
asset.setType(StringUtils.randomAlphabetic(300));
msgError = msgErrorFieldLength("type");
doPost("/api/asset", asset)
.andExpect(status().isBadRequest())
@ -143,7 +151,7 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
Mockito.reset(tbClusterService, auditLogService);
asset.setType("default");
asset.setLabel(RandomStringUtils.randomAlphabetic(300));
asset.setLabel(StringUtils.randomAlphabetic(300));
msgError = msgErrorFieldLength("label");
doPost("/api/asset", asset)
.andExpect(status().isBadRequest())
@ -471,7 +479,7 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
List<Asset> assetsTitle1 = new ArrayList<>();
for (int i = 0; i < 143; i++) {
Asset asset = new Asset();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String suffix = StringUtils.randomAlphanumeric(15);
String name = title1 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
asset.setName(name);
@ -482,7 +490,7 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
List<Asset> assetsTitle2 = new ArrayList<>();
for (int i = 0; i < 75; i++) {
Asset asset = new Asset();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String suffix = StringUtils.randomAlphanumeric(15);
String name = title2 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
asset.setName(name);
@ -557,7 +565,7 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
List<Asset> assetsType1 = new ArrayList<>();
for (int i = 0; i < 143; i++) {
Asset asset = new Asset();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String suffix = StringUtils.randomAlphanumeric(15);
String name = title1 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
asset.setName(name);
@ -569,7 +577,7 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
List<Asset> assetsType2 = new ArrayList<>();
for (int i = 0; i < 75; i++) {
Asset asset = new Asset();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String suffix = StringUtils.randomAlphanumeric(15);
String name = title2 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
asset.setName(name);
@ -684,7 +692,7 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
List<Asset> assetsTitle1 = new ArrayList<>();
for (int i = 0; i < 125; i++) {
Asset asset = new Asset();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String suffix = StringUtils.randomAlphanumeric(15);
String name = title1 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
asset.setName(name);
@ -697,7 +705,7 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
List<Asset> assetsTitle2 = new ArrayList<>();
for (int i = 0; i < 143; i++) {
Asset asset = new Asset();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String suffix = StringUtils.randomAlphanumeric(15);
String name = title2 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
asset.setName(name);
@ -779,7 +787,7 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
List<Asset> assetsType1 = new ArrayList<>();
for (int i = 0; i < 125; i++) {
Asset asset = new Asset();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String suffix = StringUtils.randomAlphanumeric(15);
String name = title1 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
asset.setName(name);
@ -793,7 +801,7 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
List<Asset> assetsType2 = new ArrayList<>();
for (int i = 0; i < 143; i++) {
Asset asset = new Asset();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String suffix = StringUtils.randomAlphanumeric(15);
String name = title2 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
asset.setName(name);
@ -905,4 +913,23 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
Assert.assertEquals(0, pageData.getData().size());
}
@Test
public void testDeleteAssetWithDeleteRelationsOk() throws Exception {
AssetId assetId = createAsset("Asset for Test WithRelationsOk").getId();
testEntityDaoWithRelationsOk(savedTenant.getId(), assetId, "/api/asset/" + assetId);
}
@Test
public void testDeleteAssetExceptionWithRelationsTransactional() throws Exception {
AssetId assetId = createAsset("Asset for Test WithRelations Transactional Exception").getId();
testEntityDaoWithRelationsTransactionalException(assetDao, savedTenant.getId(), assetId, "/api/asset/" + assetId);
}
private Asset createAsset(String name) {
Asset asset = new Asset();
asset.setName(name);
asset.setType("default");
return doPost("/api/asset", asset, Asset.class);
}
}

43
application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java

@ -20,14 +20,15 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.thingsboard.common.util.ThingsBoardExecutors;
import org.thingsboard.server.common.data.Customer;
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.audit.ActionType;
@ -36,6 +37,7 @@ 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.security.Authority;
import org.thingsboard.server.dao.customer.CustomerDao;
import org.thingsboard.server.dao.exception.DataValidationException;
import java.util.ArrayList;
@ -55,6 +57,9 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
private Tenant savedTenant;
private User tenantAdmin;
@SpyBean
private CustomerDao customerDao;
@Before
public void beforeTest() throws Exception {
executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(8, getClass()));
@ -82,6 +87,8 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
loginSysAdmin();
afterTestEntityDaoRemoveByIdWithException (customerDao);
doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
.andExpect(status().isOk());
}
@ -122,7 +129,7 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
@Test
public void testSaveCustomerWithViolationOfValidation() throws Exception {
Customer customer = new Customer();
customer.setTitle(RandomStringUtils.randomAlphabetic(300));
customer.setTitle(StringUtils.randomAlphabetic(300));
Mockito.reset(tbClusterService, auditLogService);
@ -137,7 +144,7 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
Mockito.reset(tbClusterService, auditLogService);
customer.setTitle("Normal title");
customer.setCity(RandomStringUtils.randomAlphabetic(300));
customer.setCity(StringUtils.randomAlphabetic(300));
msgError = msgErrorFieldLength("city");
doPost("/api/customer", customer)
.andExpect(status().isBadRequest())
@ -148,7 +155,7 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
Mockito.reset(tbClusterService, auditLogService);
customer.setCity("Normal city");
customer.setCountry(RandomStringUtils.randomAlphabetic(300));
customer.setCountry(StringUtils.randomAlphabetic(300));
msgError = msgErrorFieldLength("country");
doPost("/api/customer", customer)
.andExpect(status().isBadRequest())
@ -159,7 +166,7 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
Mockito.reset(tbClusterService, auditLogService);
customer.setCountry("Ukraine");
customer.setPhone(RandomStringUtils.randomAlphabetic(300));
customer.setPhone(StringUtils.randomAlphabetic(300));
msgError = msgErrorFieldLength("phone");
doPost("/api/customer", customer)
.andExpect(status().isBadRequest())
@ -170,7 +177,7 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
Mockito.reset(tbClusterService, auditLogService);
customer.setPhone("+3892555554512");
customer.setState(RandomStringUtils.randomAlphabetic(300));
customer.setState(StringUtils.randomAlphabetic(300));
msgError = msgErrorFieldLength("state");
doPost("/api/customer", customer)
.andExpect(status().isBadRequest())
@ -181,7 +188,7 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
Mockito.reset(tbClusterService, auditLogService);
customer.setState("Normal state");
customer.setZip(RandomStringUtils.randomAlphabetic(300));
customer.setZip(StringUtils.randomAlphabetic(300));
msgError = msgErrorFieldLength("zip or postal code");
doPost("/api/customer", customer)
.andExpect(status().isBadRequest())
@ -339,7 +346,7 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
for (int i = 0; i < 143; i++) {
Customer customer = new Customer();
customer.setTenantId(tenantId);
String suffix = RandomStringUtils.randomAlphanumeric((int) (5 + Math.random() * 10));
String suffix = StringUtils.randomAlphanumeric((int) (5 + Math.random() * 10));
String title = title1 + suffix;
title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase();
customer.setTitle(title);
@ -353,7 +360,7 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
for (int i = 0; i < 175; i++) {
Customer customer = new Customer();
customer.setTenantId(tenantId);
String suffix = RandomStringUtils.randomAlphanumeric((int) (5 + Math.random() * 10));
String suffix = StringUtils.randomAlphanumeric((int) (5 + Math.random() * 10));
String title = title2 + suffix;
title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase();
customer.setTitle(title);
@ -402,4 +409,22 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
}
@Test
public void testDeleteCustomerWithDeleteRelationsOk() throws Exception {
CustomerId customerId = createCustomer("Customer for Test WithRelationsOk").getId();
testEntityDaoWithRelationsOk(savedTenant.getId(), customerId, "/api/customer/" + customerId);
}
@Test
public void testDeleteCustomerExceptionWithRelationsTransactional() throws Exception {
CustomerId customerId = createCustomer("Customer for Test WithRelations Transactional Exception").getId();
testEntityDaoWithRelationsTransactionalException(customerDao, savedTenant.getId(), customerId, "/api/customer/" + customerId);
}
private Customer createCustomer(String title) {
Customer customer = new Customer();
customer.setTitle(title);
return doPost("/api/customer", customer, Customer.class);
}
}

64
application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java

@ -17,23 +17,26 @@ package org.thingsboard.server.controller;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.fasterxml.jackson.core.type.TypeReference;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.DashboardInfo;
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.audit.ActionType;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.dashboard.DashboardDao;
import org.thingsboard.server.dao.exception.DataValidationException;
import java.util.ArrayList;
@ -50,6 +53,9 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest
private Tenant savedTenant;
private User tenantAdmin;
@SpyBean
private DashboardDao dashboardDao;
@Before
public void beforeTest() throws Exception {
loginSysAdmin();
@ -73,45 +79,16 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest
public void afterTest() throws Exception {
loginSysAdmin();
afterTestEntityDaoRemoveByIdWithException (dashboardDao);
doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
.andExpect(status().isOk());
}
@Test
public void testSaveDashboard() throws Exception {
Dashboard dashboard = new Dashboard();
dashboard.setTitle("My dashboard");
Mockito.reset(tbClusterService, auditLogService);
Dashboard savedDashboard = doPost("/api/dashboard", dashboard, Dashboard.class);
testNotifyEntityOneTimeMsgToEdgeServiceNever(savedDashboard, savedDashboard.getId(), savedDashboard.getId(), savedTenant.getId(),
tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED);
Assert.assertNotNull(savedDashboard);
Assert.assertNotNull(savedDashboard.getId());
Assert.assertTrue(savedDashboard.getCreatedTime() > 0);
Assert.assertEquals(savedTenant.getId(), savedDashboard.getTenantId());
Assert.assertEquals(dashboard.getTitle(), savedDashboard.getTitle());
savedDashboard.setTitle("My new dashboard");
Mockito.reset(tbClusterService, auditLogService);
doPost("/api/dashboard", savedDashboard, Dashboard.class);
testNotifyEntityAllOneTime(savedDashboard, savedDashboard.getId(), savedDashboard.getId(), savedTenant.getId(),
tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.UPDATED);
Dashboard foundDashboard = doGet("/api/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class);
Assert.assertEquals(foundDashboard.getTitle(), savedDashboard.getTitle());
}
@Test
public void testSaveDashboardInfoWithViolationOfValidation() throws Exception {
Dashboard dashboard = new Dashboard();
dashboard.setTitle(RandomStringUtils.randomAlphabetic(300));
dashboard.setTitle(StringUtils.randomAlphabetic(300));
String msgError = msgErrorFieldLength("title");
Mockito.reset(tbClusterService, auditLogService);
@ -335,7 +312,7 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest
int cntEntity = 134;
for (int i = 0; i < cntEntity; i++) {
Dashboard dashboard = new Dashboard();
String suffix = RandomStringUtils.randomAlphanumeric((int) (Math.random() * 15));
String suffix = StringUtils.randomAlphanumeric((int) (Math.random() * 15));
String title = title1 + suffix;
title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase();
dashboard.setTitle(title);
@ -346,7 +323,7 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest
for (int i = 0; i < 112; i++) {
Dashboard dashboard = new Dashboard();
String suffix = RandomStringUtils.randomAlphanumeric((int) (Math.random() * 15));
String suffix = StringUtils.randomAlphanumeric((int) (Math.random() * 15));
String title = title2 + suffix;
title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase();
dashboard.setTitle(title);
@ -495,4 +472,21 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest
Assert.assertEquals(0, pageData.getData().size());
}
@Test
public void testDeleteDashboardWithDeleteRelationsOk() throws Exception {
DashboardId dashboardId = createDashboard("Dashboard for Test WithRelationsOk").getId();
testEntityDaoWithRelationsOk(savedTenant.getId(), dashboardId, "/api/dashboard/" + dashboardId);
}
@Test
public void testDeleteDashboardExceptionWithRelationsTransactional() throws Exception {
DashboardId dashboardId = createDashboard("Dashboard for Test WithRelations Transactional Exception").getId();
testEntityDaoWithRelationsTransactionalException(dashboardDao, savedTenant.getId(), dashboardId, "/api/dashboard/" + dashboardId);
}
private Dashboard createDashboard(String title) {
Dashboard dashboard = new Dashboard();
dashboard.setTitle(title);
return doPost("/api/dashboard", dashboard, Dashboard.class);
}
}

50
application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java

@ -21,7 +21,6 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@ -35,6 +34,7 @@ import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.OtaPackageInfo;
import org.thingsboard.server.common.data.SaveOtaPackageInfoRequest;
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.audit.ActionType;
@ -50,6 +50,7 @@ import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.dao.device.DeviceDao;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.exception.DeviceCredentialsValidationException;
import org.thingsboard.server.dao.model.ModelConstants;
@ -82,6 +83,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
@SpyBean
private GatewayNotificationsService gatewayNotificationsService;
@SpyBean
private DeviceDao deviceDao;
@Before
public void beforeTest() throws Exception {
executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(8, getClass()));
@ -109,6 +114,8 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
loginSysAdmin();
afterTestEntityDaoRemoveByIdWithException (deviceDao);
doDelete("/api/tenant/" + savedTenant.getId().getId())
.andExpect(status().isOk());
}
@ -164,7 +171,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
@Test
public void saveDeviceWithViolationOfValidation() throws Exception {
Device device = new Device();
device.setName(RandomStringUtils.randomAlphabetic(300));
device.setName(StringUtils.randomAlphabetic(300));
device.setType("default");
Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService);
@ -181,7 +188,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
device.setTenantId(savedTenant.getId());
msgError = msgErrorFieldLength("type");
device.setType(RandomStringUtils.randomAlphabetic(300));
device.setType(StringUtils.randomAlphabetic(300));
doPost("/api/device", device)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString(msgError)));
@ -193,7 +200,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
msgError = msgErrorFieldLength("label");
device.setType("Normal type");
device.setLabel(RandomStringUtils.randomAlphabetic(300));
device.setLabel(StringUtils.randomAlphabetic(300));
doPost("/api/device", device)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString(msgError)));
@ -709,7 +716,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
futures = new ArrayList<>(143);
for (int i = 0; i < 143; i++) {
Device device = new Device();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String suffix = StringUtils.randomAlphanumeric(15);
String name = title1 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
@ -723,7 +730,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
futures = new ArrayList<>(75);
for (int i = 0; i < 75; i++) {
Device device = new Device();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String suffix = StringUtils.randomAlphanumeric(15);
String name = title2 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
@ -783,7 +790,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
futures = new ArrayList<>(143);
for (int i = 0; i < 143; i++) {
Device device = new Device();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String suffix = StringUtils.randomAlphanumeric(15);
String name = title1 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
@ -801,7 +808,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
futures = new ArrayList<>(75);
for (int i = 0; i < 75; i++) {
Device device = new Device();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String suffix = StringUtils.randomAlphanumeric(15);
String name = title2 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
@ -920,7 +927,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
futures = new ArrayList<>(125);
for (int i = 0; i < 125; i++) {
Device device = new Device();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String suffix = StringUtils.randomAlphanumeric(15);
String name = title1 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
@ -936,7 +943,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
futures = new ArrayList<>(143);
for (int i = 0; i < 143; i++) {
Device device = new Device();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String suffix = StringUtils.randomAlphanumeric(15);
String name = title2 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
@ -1003,7 +1010,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
futures = new ArrayList<>(125);
for (int i = 0; i < 125; i++) {
Device device = new Device();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String suffix = StringUtils.randomAlphanumeric(15);
String name = title1 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
@ -1023,7 +1030,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
futures = new ArrayList<>(143);
for (int i = 0; i < 143; i++) {
Device device = new Device();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String suffix = StringUtils.randomAlphanumeric(15);
String name = title2 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
@ -1203,4 +1210,23 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
protected void testNotificationDeleteGatewayNever() {
Mockito.verify(gatewayNotificationsService, never()).onDeviceDeleted(Mockito.any(Device.class));
}
@Test
public void testDeleteDashboardWithDeleteRelationsOk() throws Exception {
DeviceId deviceId = createDevice("Device for Test WithRelationsOk").getId();
testEntityDaoWithRelationsOk(savedTenant.getId(), deviceId, "/api/device/" + deviceId);
}
@Test
public void testDeleteDeviceExceptionWithRelationsTransactional() throws Exception {
DeviceId deviceId = createDevice("Device for Test WithRelations Transactional Exception").getId();
testEntityDaoWithRelationsTransactionalException(deviceDao, savedTenant.getId(), deviceId, "/api/device/" + deviceId);
}
private Device createDevice(String name) {
Device device = new Device();
device.setName(name);
device.setType("default");
return doPost("/api/device", device, Device.class);
}
}

28
application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java

@ -22,12 +22,12 @@ import com.google.protobuf.DynamicMessage;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.util.JsonFormat;
import com.squareup.wire.schema.internal.parser.ProtoFileElement;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.Device;
@ -38,6 +38,7 @@ import org.thingsboard.server.common.data.DeviceProfileType;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.OtaPackageInfo;
import org.thingsboard.server.common.data.SaveOtaPackageInfoRequest;
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.audit.ActionType;
@ -46,10 +47,12 @@ import org.thingsboard.server.common.data.device.profile.JsonTransportPayloadCon
import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.device.DeviceProfileDao;
import org.thingsboard.server.dao.exception.DataValidationException;
import java.util.ArrayList;
@ -74,6 +77,9 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
private Tenant savedTenant;
private User tenantAdmin;
@SpyBean
private DeviceProfileDao deviceProfileDao;
@Before
public void beforeTest() throws Exception {
loginSysAdmin();
@ -97,6 +103,8 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
public void afterTest() throws Exception {
loginSysAdmin();
afterTestEntityDaoRemoveByIdWithException (deviceProfileDao);
doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
.andExpect(status().isOk());
}
@ -138,7 +146,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
Mockito.reset(tbClusterService, auditLogService);
DeviceProfile createDeviceProfile = this.createDeviceProfile(RandomStringUtils.randomAlphabetic(300));
DeviceProfile createDeviceProfile = this.createDeviceProfile(StringUtils.randomAlphabetic(300));
doPost("/api/deviceProfile", createDeviceProfile)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString(msgError)));
@ -1125,4 +1133,20 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
return JsonFormat.printer().includingDefaultValueFields().print(dynamicMessage);
}
@Test
public void testDeleteDeviceProfileWithDeleteRelationsOk() throws Exception {
DeviceProfileId deviceProfileId = savedDeviceProfile("DeviceProfile for Test WithRelationsOk").getId();
testEntityDaoWithRelationsOk(savedTenant.getId(), deviceProfileId, "/api/deviceProfile/" + deviceProfileId);
}
@Test
public void testDeleteDeviceProfileExceptionWithRelationsTransactional() throws Exception {
DeviceProfileId deviceProfileId = savedDeviceProfile("DeviceProfile for Test WithRelations Transactional Exception").getId();
testEntityDaoWithRelationsTransactionalException(deviceProfileDao, savedTenant.getId(), deviceProfileId, "/api/deviceProfile/" + deviceProfileId);
}
private DeviceProfile savedDeviceProfile(String name) {
DeviceProfile deviceProfile = createDeviceProfile(name);
return doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
}
}

48
application/src/test/java/org/thingsboard/server/controller/BaseEdgeControllerTest.java

@ -17,26 +17,29 @@ package org.thingsboard.server.controller;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.fasterxml.jackson.core.type.TypeReference;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.test.context.TestPropertySource;
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.StringUtils;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId;
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.security.Authority;
import org.thingsboard.server.dao.edge.EdgeDao;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.edge.imitator.EdgeImitator;
@ -72,6 +75,9 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
private TenantId tenantId;
private User tenantAdmin;
@SpyBean
private EdgeDao edgeDao;
@Before
public void beforeTest() throws Exception {
loginSysAdmin();
@ -96,6 +102,8 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
public void afterTest() throws Exception {
loginSysAdmin();
afterTestEntityDaoRemoveByIdWithException (edgeDao);
doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
.andExpect(status().isOk());
}
@ -133,7 +141,7 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
@Test
public void testSaveEdgeWithViolationOfLengthValidation() throws Exception {
Edge edge = constructEdge(RandomStringUtils.randomAlphabetic(300), "default");
Edge edge = constructEdge(StringUtils.randomAlphabetic(300), "default");
String msgError = msgErrorFieldLength("name");
Mockito.reset(tbClusterService, auditLogService);
@ -148,7 +156,7 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
msgError = msgErrorFieldLength("type");
edge.setName("normal name");
edge.setType(RandomStringUtils.randomAlphabetic(300));
edge.setType(StringUtils.randomAlphabetic(300));
doPost("/api/edge", edge)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString(msgError)));
@ -159,7 +167,7 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
msgError = msgErrorFieldLength("label");
edge.setType("normal type");
edge.setLabel(RandomStringUtils.randomAlphabetic(300));
edge.setLabel(StringUtils.randomAlphabetic(300));
doPost("/api/edge", edge)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString(msgError)));
@ -389,7 +397,7 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
String title1 = "Edge title 1";
List<Edge> edgesTitle1 = new ArrayList<>();
for (int i = 0; i < 143; i++) {
String suffix = RandomStringUtils.randomAlphanumeric(15);
String suffix = StringUtils.randomAlphanumeric(15);
String name = title1 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
Edge edge = constructEdge(name, "default");
@ -398,7 +406,7 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
String title2 = "Edge title 2";
List<Edge> edgesTitle2 = new ArrayList<>();
for (int i = 0; i < 75; i++) {
String suffix = RandomStringUtils.randomAlphanumeric(15);
String suffix = StringUtils.randomAlphanumeric(15);
String name = title2 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
Edge edge = constructEdge(name, "default");
@ -471,7 +479,7 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
String type1 = "typeA";
List<Edge> edgesType1 = new ArrayList<>();
for (int i = 0; i < 143; i++) {
String suffix = RandomStringUtils.randomAlphanumeric(15);
String suffix = StringUtils.randomAlphanumeric(15);
String name = title1 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
Edge edge = constructEdge(name, type1);
@ -481,7 +489,7 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
String type2 = "typeB";
List<Edge> edgesType2 = new ArrayList<>();
for (int i = 0; i < 75; i++) {
String suffix = RandomStringUtils.randomAlphanumeric(15);
String suffix = StringUtils.randomAlphanumeric(15);
String name = title2 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
Edge edge = constructEdge(name, type2);
@ -600,7 +608,7 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
String title1 = "Edge title 1";
List<Edge> edgesTitle1 = new ArrayList<>();
for (int i = 0; i < 125; i++) {
String suffix = RandomStringUtils.randomAlphanumeric(15);
String suffix = StringUtils.randomAlphanumeric(15);
String name = title1 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
Edge edge = constructEdge(name, "default");
@ -611,7 +619,7 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
String title2 = "Edge title 2";
List<Edge> edgesTitle2 = new ArrayList<>();
for (int i = 0; i < 143; i++) {
String suffix = RandomStringUtils.randomAlphanumeric(15);
String suffix = StringUtils.randomAlphanumeric(15);
String name = title2 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
Edge edge = constructEdge(name, "default");
@ -698,7 +706,7 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
String type1 = "typeC";
List<Edge> edgesType1 = new ArrayList<>();
for (int i = 0; i < 125; i++) {
String suffix = RandomStringUtils.randomAlphanumeric(15);
String suffix = StringUtils.randomAlphanumeric(15);
String name = title1 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
Edge edge = constructEdge(name, type1);
@ -710,7 +718,7 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
String type2 = "typeD";
List<Edge> edgesType2 = new ArrayList<>();
for (int i = 0; i < 143; i++) {
String suffix = RandomStringUtils.randomAlphanumeric(15);
String suffix = StringUtils.randomAlphanumeric(15);
String name = title2 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
Edge edge = constructEdge(name, type2);
@ -837,4 +845,20 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
.andExpect(status().isOk());
}
@Test
public void testDeleteEdgeWithDeleteRelationsOk() throws Exception {
EdgeId edgeId = savedEdge("Edge for Test WithRelationsOk").getId();
testEntityDaoWithRelationsOk(savedTenant.getId(), edgeId, "/api/edge/" + edgeId);
}
@Test
public void testDeleteEdgeExceptionWithRelationsTransactional() throws Exception {
EdgeId edgeId = savedEdge("Edge for Test WithRelations Transactional Exception").getId();
testEntityDaoWithRelationsTransactionalException(edgeDao, savedTenant.getId(), edgeId, "/api/edge/" + edgeId);
}
private Edge savedEdge(String name) {
Edge edge = constructEdge(name, "default");
return doPost("/api/edge", edge, Edge.class);
}
}

29
application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java

@ -22,7 +22,6 @@ import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
@ -33,6 +32,7 @@ import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.web.servlet.ResultActions;
import org.thingsboard.common.util.ThingsBoardExecutors;
@ -40,11 +40,13 @@ import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.EntityViewInfo;
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.audit.ActionType;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.objects.AttributesEntityView;
import org.thingsboard.server.common.data.objects.TelemetryEntityView;
import org.thingsboard.server.common.data.page.PageData;
@ -54,6 +56,7 @@ import org.thingsboard.server.common.data.query.EntityKey;
import org.thingsboard.server.common.data.query.EntityKeyType;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.dao.entityview.EntityViewDao;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.model.ModelConstants;
@ -93,6 +96,9 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
List<ListenableFuture<ResultActions>> deleteFutures = new ArrayList<>();
ListeningExecutorService executor;
@SpyBean
private EntityViewDao entityViewDao;
@Before
public void beforeTest() throws Exception {
executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(8, getClass()));
@ -114,6 +120,9 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
@After
public void afterTest() throws Exception {
afterTestEntityDaoRemoveByIdWithException (entityViewDao);
executor.shutdownNow();
}
@ -170,7 +179,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
@Test
public void testSaveEntityViewWithViolationOfValidation() throws Exception {
EntityView entityView = createEntityView(RandomStringUtils.randomAlphabetic(300), 0, 0);
EntityView entityView = createEntityView(StringUtils.randomAlphabetic(300), 0, 0);
Mockito.reset(tbClusterService, auditLogService);
@ -186,7 +195,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
entityView.setName("Normal name");
msgError = msgErrorFieldLength("type");
entityView.setType(RandomStringUtils.randomAlphabetic(300));
entityView.setType(StringUtils.randomAlphabetic(300));
doPost("/api/entityView", entityView)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString(msgError)));
@ -722,7 +731,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
});
viewNameFutures.add(Futures.transform(customerFuture, customerId -> {
String fullName = partOfName + ' ' + RandomStringUtils.randomAlphanumeric(15);
String fullName = partOfName + ' ' + StringUtils.randomAlphanumeric(15);
fullName = even ? fullName.toLowerCase() : fullName.toUpperCase();
EntityView view = getNewSavedEntityView(fullName);
view.setCustomerId(customerId);
@ -786,4 +795,16 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
Assert.assertEquals(0, pageData.getData().size());
}
@Test
public void testDeleteEntityViewWithDeleteRelationsOk() throws Exception {
EntityViewId entityViewId = getNewSavedEntityView("EntityView for Test WithRelationsOk").getId();
testEntityDaoWithRelationsOk(tenantId, entityViewId, "/api/entityView/" + entityViewId);
}
@Test
public void testDeleteEntityViewExceptionWithRelationsTransactional() throws Exception {
EntityViewId entityViewId = getNewSavedEntityView("EntityView for Test WithRelations Transactional Exception").getId();
testEntityDaoWithRelationsTransactionalException(entityViewDao, tenantId, entityViewId, "/api/entityView/" + entityViewId);
}
}

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

@ -16,7 +16,6 @@
package org.thingsboard.server.controller;
import com.fasterxml.jackson.core.type.TypeReference;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@ -30,6 +29,7 @@ import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.OtaPackage;
import org.thingsboard.server.common.data.OtaPackageInfo;
import org.thingsboard.server.common.data.SaveOtaPackageInfoRequest;
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.audit.ActionType;
@ -137,7 +137,7 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes
SaveOtaPackageInfoRequest firmwareInfo = new SaveOtaPackageInfoRequest();
firmwareInfo.setDeviceProfileId(deviceProfileId);
firmwareInfo.setType(FIRMWARE);
firmwareInfo.setTitle(RandomStringUtils.randomAlphabetic(300));
firmwareInfo.setTitle(StringUtils.randomAlphabetic(300));
firmwareInfo.setVersion(VERSION);
firmwareInfo.setUsesUrl(false);
String msgError = msgErrorFieldLength("title");
@ -154,7 +154,7 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes
ActionType.ADDED, new DataValidationException(msgError));
firmwareInfo.setTitle(TITLE);
firmwareInfo.setVersion(RandomStringUtils.randomAlphabetic(300));
firmwareInfo.setVersion(StringUtils.randomAlphabetic(300));
msgError = msgErrorFieldLength("version");
doPost("/api/otaPackage", firmwareInfo)
.andExpect(status().isBadRequest())
@ -168,7 +168,7 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes
firmwareInfo.setVersion(VERSION);
firmwareInfo.setUsesUrl(true);
msgError = msgErrorFieldLength("url");
firmwareInfo.setUrl(RandomStringUtils.randomAlphabetic(300));
firmwareInfo.setUrl(StringUtils.randomAlphabetic(300));
doPost("/api/otaPackage", firmwareInfo)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString(msgError)));

29
application/src/test/java/org/thingsboard/server/controller/BaseRuleChainControllerTest.java

@ -16,22 +16,25 @@
package org.thingsboard.server.controller;
import com.fasterxml.jackson.core.type.TypeReference;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.boot.test.mock.mockito.SpyBean;
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.audit.ActionType;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.rule.RuleChainDao;
import java.util.ArrayList;
import java.util.Collections;
@ -47,6 +50,9 @@ public abstract class BaseRuleChainControllerTest extends AbstractControllerTest
private Tenant savedTenant;
private User tenantAdmin;
@SpyBean
private RuleChainDao ruleChainDao;
@Before
public void beforeTest() throws Exception {
loginSysAdmin();
@ -70,6 +76,8 @@ public abstract class BaseRuleChainControllerTest extends AbstractControllerTest
public void afterTest() throws Exception {
loginSysAdmin();
afterTestEntityDaoRemoveByIdWithException(ruleChainDao);
doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
.andExpect(status().isOk());
}
@ -107,7 +115,7 @@ public abstract class BaseRuleChainControllerTest extends AbstractControllerTest
Mockito.reset(tbClusterService, auditLogService);
RuleChain ruleChain = new RuleChain();
ruleChain.setName(RandomStringUtils.randomAlphabetic(300));
ruleChain.setName(StringUtils.randomAlphabetic(300));
String msgError = msgErrorFieldLength("name");
doPost("/api/ruleChain", ruleChain)
.andExpect(status().isBadRequest())
@ -223,4 +231,21 @@ public abstract class BaseRuleChainControllerTest extends AbstractControllerTest
Assert.assertEquals(1, pageData.getTotalElements());
}
@Test
public void testDeleteRuleChainWithDeleteRelationsOk() throws Exception {
RuleChainId ruleChainId = createRuleChain("RuleChain for Test WithRelationsOk").getId();
testEntityDaoWithRelationsOk(savedTenant.getId(), ruleChainId, "/api/ruleChain/" + ruleChainId);
}
@Test
public void testDeleteRuleChainExceptionWithRelationsTransactional() throws Exception {
RuleChainId ruleChainId = createRuleChain("RuleChain for Test WithRelations Transactional Exception").getId();
testEntityDaoWithRelationsTransactionalException(ruleChainDao, savedTenant.getId(), ruleChainId, "/api/ruleChain/" + ruleChainId);
}
private RuleChain createRuleChain(String name) {
RuleChain ruleChain = new RuleChain();
ruleChain.setName(name);
return doPost("/api/ruleChain", ruleChain, RuleChain.class);
}
}

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

@ -16,13 +16,13 @@
package org.thingsboard.server.controller;
import com.fasterxml.jackson.core.type.TypeReference;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.thingsboard.server.common.data.ResourceType;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.TbResource;
import org.thingsboard.server.common.data.TbResourceInfo;
import org.thingsboard.server.common.data.Tenant;
@ -118,7 +118,7 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes
public void saveResourceInfoWithViolationOfLengthValidation() throws Exception {
TbResource resource = new TbResource();
resource.setResourceType(ResourceType.JKS);
resource.setTitle(RandomStringUtils.randomAlphabetic(300));
resource.setTitle(StringUtils.randomAlphabetic(300));
resource.setFileName(DEFAULT_FILE_NAME);
resource.setData("Test Data");

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

@ -21,7 +21,6 @@ import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@ -31,6 +30,7 @@ import org.mockito.Mockito;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.web.servlet.ResultActions;
import org.thingsboard.common.util.ThingsBoardExecutors;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.TenantInfo;
import org.thingsboard.server.common.data.TenantProfile;
@ -120,7 +120,7 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest {
public void testSaveTenantWithViolationOfValidation() throws Exception {
loginSysAdmin();
Tenant tenant = new Tenant();
tenant.setTitle(RandomStringUtils.randomAlphanumeric(300));
tenant.setTitle(StringUtils.randomAlphanumeric(300));
Mockito.reset(tbClusterService);
@ -259,7 +259,7 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest {
List<ListenableFuture<Tenant>> createFutures = new ArrayList<>(134);
for (int i = 0; i < 134; i++) {
Tenant tenant = new Tenant();
String suffix = RandomStringUtils.randomAlphanumeric((int) (5 + Math.random() * 10));
String suffix = StringUtils.randomAlphanumeric((int) (5 + Math.random() * 10));
String title = title1 + suffix;
title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase();
tenant.setTitle(title);
@ -274,7 +274,7 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest {
createFutures = new ArrayList<>(127);
for (int i = 0; i < 127; i++) {
Tenant tenant = new Tenant();
String suffix = RandomStringUtils.randomAlphanumeric((int) (5 + Math.random() * 10));
String suffix = StringUtils.randomAlphanumeric((int) (5 + Math.random() * 10));
String title = title2 + suffix;
title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase();
tenant.setTitle(title);

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

@ -16,12 +16,12 @@
package org.thingsboard.server.controller;
import com.fasterxml.jackson.core.type.TypeReference;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.ArgumentMatcher;
import org.mockito.Mockito;
import org.thingsboard.server.common.data.EntityInfo;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.id.TenantProfileId;
@ -84,7 +84,7 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController
Mockito.reset(tbClusterService);
TenantProfile tenantProfile = this.createTenantProfile(RandomStringUtils.randomAlphabetic(300));
TenantProfile tenantProfile = this.createTenantProfile(StringUtils.randomAlphabetic(300));
doPost("/api/tenantProfile", tenantProfile)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString(msgErrorFieldLength("name"))));

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

@ -18,21 +18,25 @@ package org.thingsboard.server.controller;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.http.HttpHeaders;
import org.thingsboard.server.common.data.Customer;
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.audit.ActionType;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.user.UserDao;
import org.thingsboard.server.service.mail.TestMailService;
import java.util.ArrayList;
@ -53,6 +57,16 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
private CustomerId customerNUULId = (CustomerId) createEntityId_NULL_UUID(new Customer());
@SpyBean
private UserDao userDao;
@After
public void afterTest() throws Exception {
loginSysAdmin();
afterTestEntityDaoRemoveByIdWithException(userDao);
}
@Test
public void testSaveUser() throws Exception {
loginSysAdmin();
@ -131,7 +145,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
user.setAuthority(Authority.TENANT_ADMIN);
user.setTenantId(tenantId);
user.setEmail(email);
user.setFirstName(RandomStringUtils.randomAlphabetic(300));
user.setFirstName(StringUtils.randomAlphabetic(300));
user.setLastName("Downs");
String msgError = msgErrorFieldLength("first name");
doPost("/api/user", user)
@ -145,7 +159,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
user.setFirstName("Normal name");
msgError = msgErrorFieldLength("last name");
user.setLastName(RandomStringUtils.randomAlphabetic(300));
user.setLastName(StringUtils.randomAlphabetic(300));
doPost("/api/user", user)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString(msgError)));
@ -440,7 +454,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
User user = new User();
user.setAuthority(Authority.TENANT_ADMIN);
user.setTenantId(tenantId);
String suffix = RandomStringUtils.randomAlphanumeric((int) (5 + Math.random() * 10));
String suffix = StringUtils.randomAlphanumeric((int) (5 + Math.random() * 10));
String email = email1 + suffix + "@thingsboard.org";
email = i % 2 == 0 ? email.toLowerCase() : email.toUpperCase();
user.setEmail(email);
@ -454,7 +468,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
User user = new User();
user.setAuthority(Authority.TENANT_ADMIN);
user.setTenantId(tenantId);
String suffix = RandomStringUtils.randomAlphanumeric((int) (5 + Math.random() * 10));
String suffix = StringUtils.randomAlphanumeric((int) (5 + Math.random() * 10));
String email = email2 + suffix + "@thingsboard.org";
email = i % 2 == 0 ? email.toLowerCase() : email.toUpperCase();
user.setEmail(email);
@ -605,7 +619,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
User user = new User();
user.setAuthority(Authority.CUSTOMER_USER);
user.setCustomerId(customerId);
String suffix = RandomStringUtils.randomAlphanumeric((int) (5 + Math.random() * 10));
String suffix = StringUtils.randomAlphanumeric((int) (5 + Math.random() * 10));
String email = email1 + suffix + "@thingsboard.org";
email = i % 2 == 0 ? email.toLowerCase() : email.toUpperCase();
user.setEmail(email);
@ -619,7 +633,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
User user = new User();
user.setAuthority(Authority.CUSTOMER_USER);
user.setCustomerId(customerId);
String suffix = RandomStringUtils.randomAlphanumeric((int) (5 + Math.random() * 10));
String suffix = StringUtils.randomAlphanumeric((int) (5 + Math.random() * 10));
String email = email2 + suffix + "@thingsboard.org";
email = i % 2 == 0 ? email.toLowerCase() : email.toUpperCase();
user.setEmail(email);
@ -688,4 +702,29 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
doDelete("/api/customer/" + customerId.getId().toString())
.andExpect(status().isOk());
}
@Test
public void testDeleteUserWithDeleteRelationsOk() throws Exception {
UserId userId = createUser().getId();
testEntityDaoWithRelationsOk(tenantId, userId, "/api/user/" + userId);
}
@Test
public void testDeleteUserExceptionWithRelationsTransactional() throws Exception {
UserId userId = createUser().getId();
testEntityDaoWithRelationsTransactionalException(userDao, tenantId, userId, "/api/user/" + userId);
}
private User createUser() throws Exception {
loginSysAdmin();
String email = "tenant2@thingsboard.org";
User user = new User();
user.setAuthority(Authority.TENANT_ADMIN);
user.setTenantId(tenantId);
user.setEmail(email);
user.setFirstName("Joe");
user.setLastName("Downs");
return doPost("/api/user", user, User.class);
}
}

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

@ -16,13 +16,13 @@
package org.thingsboard.server.controller;
import com.fasterxml.jackson.core.type.TypeReference;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.thingsboard.server.common.data.Customer;
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.audit.ActionType;
@ -109,7 +109,7 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController
@Test
public void testSaveWidgetBundleWithViolationOfLengthValidation() throws Exception {
WidgetsBundle widgetsBundle = new WidgetsBundle();
widgetsBundle.setTitle(RandomStringUtils.randomAlphabetic(300));
widgetsBundle.setTitle(StringUtils.randomAlphabetic(300));
Mockito.reset(tbClusterService);

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

Loading…
Cancel
Save