Browse Source

Merge remote-tracking branch 'thingsboard/master' into tenant-profile-rate-limits

pull/6979/head
Artem Babak 4 years ago
parent
commit
a88f4ec160
  1. 2
      application/pom.xml
  2. 2
      application/src/main/conf/logback.xml
  3. 86
      application/src/main/data/upgrade/3.3.4/schema_update.sql
  4. 49
      application/src/main/data/upgrade/3.3.4/schema_update_device_profile.sql
  5. 7
      application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
  6. 56
      application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
  7. 2
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java
  8. 4
      application/src/main/java/org/thingsboard/server/controller/AlarmController.java
  9. 4
      application/src/main/java/org/thingsboard/server/controller/AssetController.java
  10. 4
      application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java
  11. 4
      application/src/main/java/org/thingsboard/server/controller/CustomerController.java
  12. 14
      application/src/main/java/org/thingsboard/server/controller/DashboardController.java
  13. 6
      application/src/main/java/org/thingsboard/server/controller/DeviceController.java
  14. 1
      application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java
  15. 5
      application/src/main/java/org/thingsboard/server/controller/EdgeController.java
  16. 4
      application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java
  17. 4
      application/src/main/java/org/thingsboard/server/controller/EntityViewController.java
  18. 61
      application/src/main/java/org/thingsboard/server/controller/QueueController.java
  19. 4
      application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
  20. 4
      application/src/main/java/org/thingsboard/server/controller/TbResourceController.java
  21. 1
      application/src/main/java/org/thingsboard/server/controller/TenantController.java
  22. 2
      application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java
  23. 2
      application/src/main/java/org/thingsboard/server/controller/TwoFactorAuthConfigController.java
  24. 4
      application/src/main/java/org/thingsboard/server/controller/UserController.java
  25. 5
      application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java
  26. 5
      application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java
  27. 2
      application/src/main/java/org/thingsboard/server/exception/ThingsboardCredentialsExpiredResponse.java
  28. 8
      application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponse.java
  29. 5
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceProfileMsgConstructor.java
  30. 7
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java
  31. 30
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/AbstractRuleChainMetadataConstructor.java
  32. 9
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorFactory.java
  33. 6
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV330.java
  34. 50
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV333.java
  35. 5
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV340.java
  36. 28
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/TelemetryEdgeProcessor.java
  37. 2
      application/src/main/java/org/thingsboard/server/service/edge/rpc/sync/DefaultEdgeRequestsService.java
  38. 20
      application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/DefaultTbDashboardService.java
  39. 53
      application/src/main/java/org/thingsboard/server/service/entitiy/queue/DefaultTbQueueService.java
  40. 2
      application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java
  41. 31
      application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
  42. 4
      application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java
  43. 46
      application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java
  44. 66
      application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java
  45. 27
      application/src/main/java/org/thingsboard/server/service/mail/PasswordResetExecutorService.java
  46. 10
      application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java
  47. 10
      application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java
  48. 2
      application/src/main/java/org/thingsboard/server/service/queue/DefaultTenantRoutingInfoService.java
  49. 6
      application/src/main/java/org/thingsboard/server/service/queue/processing/TbRuleEngineProcessingResult.java
  50. 6
      application/src/main/java/org/thingsboard/server/service/queue/processing/TbRuleEngineProcessingStrategyFactory.java
  51. 2
      application/src/main/java/org/thingsboard/server/service/script/AbstractNashornJsInvokeService.java
  52. 16
      application/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java
  53. 16
      application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java
  54. 4
      application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/BaseEntityImportService.java
  55. 1
      application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/DeviceProfileImportService.java
  56. 14
      application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java
  57. 23
      application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitVersionControlQueueService.java
  58. 6
      application/src/main/java/org/thingsboard/server/service/telemetry/AttributeData.java
  59. 4
      application/src/main/java/org/thingsboard/server/service/telemetry/TsData.java
  60. 2
      application/src/main/resources/logback.xml
  61. 20
      application/src/main/resources/thingsboard.yml
  62. 224
      application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java
  63. 9
      application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java
  64. 44
      application/src/test/java/org/thingsboard/server/controller/BaseAdminControllerTest.java
  65. 1
      application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java
  66. 101
      application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java
  67. 105
      application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java
  68. 6
      application/src/test/java/org/thingsboard/server/controller/BaseEntityRelationControllerTest.java
  69. 152
      application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java
  70. 108
      application/src/test/java/org/thingsboard/server/controller/BaseOtaPackageControllerTest.java
  71. 68
      application/src/test/java/org/thingsboard/server/controller/BaseRuleChainControllerTest.java
  72. 78
      application/src/test/java/org/thingsboard/server/controller/BaseTbResourceControllerTest.java
  73. 84
      application/src/test/java/org/thingsboard/server/controller/BaseTenantControllerTest.java
  74. 114
      application/src/test/java/org/thingsboard/server/controller/BaseTenantProfileControllerTest.java
  75. 150
      application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java
  76. 92
      application/src/test/java/org/thingsboard/server/controller/BaseWidgetsBundleControllerTest.java
  77. 7
      application/src/test/java/org/thingsboard/server/edge/BaseEdgeTest.java
  78. 62
      application/src/test/java/org/thingsboard/server/queue/discovery/HashPartitionServiceTest.java
  79. 22
      application/src/test/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructorTest.java
  80. 95
      application/src/test/java/org/thingsboard/server/transport/coap/attributes/AbstractCoapAttributesIntegrationTest.java
  81. 62
      application/src/test/java/org/thingsboard/server/transport/coap/rpc/AbstractCoapServerSideRpcIntegrationTest.java
  82. 1
      application/src/test/java/org/thingsboard/server/transport/coap/rpc/CoapServerSideRpcJsonIntegrationTest.java
  83. 6
      application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java
  84. 2
      common/actor/pom.xml
  85. 2
      common/cache/pom.xml
  86. 2
      common/cluster-api/pom.xml
  87. 9
      common/cluster-api/src/main/proto/queue.proto
  88. 2
      common/coap-server/pom.xml
  89. 2
      common/dao-api/pom.xml
  90. 2
      common/data/pom.xml
  91. 4
      common/data/src/main/java/org/thingsboard/server/common/data/AdminSettings.java
  92. 4
      common/data/src/main/java/org/thingsboard/server/common/data/Customer.java
  93. 14
      common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java
  94. 6
      common/data/src/main/java/org/thingsboard/server/common/data/Device.java
  95. 6
      common/data/src/main/java/org/thingsboard/server/common/data/DeviceInfo.java
  96. 20
      common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java
  97. 6
      common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java
  98. 4
      common/data/src/main/java/org/thingsboard/server/common/data/EntityViewInfo.java
  99. 6
      common/data/src/main/java/org/thingsboard/server/common/data/Event.java
  100. 4
      common/data/src/main/java/org/thingsboard/server/common/data/ExportableEntity.java

2
application/pom.xml

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

2
application/src/main/conf/logback.xml

@ -37,7 +37,7 @@
<logger name="org.thingsboard.server" level="INFO" /> <logger name="org.thingsboard.server" level="INFO" />
<logger name="com.microsoft.azure.servicebus.primitives.CoreMessageReceiver" level="OFF" /> <logger name="com.microsoft.azure.servicebus.primitives.CoreMessageReceiver" level="OFF" />
<logger name="org.apache.kafka.common.utils.AppInfoParser" level="WARN"/> <logger name="org.apache.kafka.common.utils.AppInfoParser" level="WARN"/>
<logger name="org.apache.kafka.clients.consumer.ConsumerConfig" level="WARN"/> <logger name="org.apache.kafka.clients" level="WARN"/>
<root level="INFO"> <root level="INFO">
<appender-ref ref="fileLogAppender"/> <appender-ref ref="fileLogAppender"/>

86
application/src/main/data/upgrade/3.3.4/schema_update.sql

@ -33,16 +33,7 @@ ALTER TABLE widgets_bundle
ALTER TABLE entity_view ALTER TABLE entity_view
ADD COLUMN IF NOT EXISTS external_id UUID; ADD COLUMN IF NOT EXISTS external_id UUID;
CREATE INDEX IF NOT EXISTS idx_device_external_id ON device(tenant_id, external_id);
CREATE INDEX IF NOT EXISTS idx_device_profile_external_id ON device_profile(tenant_id, external_id);
CREATE INDEX IF NOT EXISTS idx_asset_external_id ON asset(tenant_id, external_id);
CREATE INDEX IF NOT EXISTS idx_rule_chain_external_id ON rule_chain(tenant_id, external_id);
CREATE INDEX IF NOT EXISTS idx_rule_node_external_id ON rule_node(rule_chain_id, external_id); CREATE INDEX IF NOT EXISTS idx_rule_node_external_id ON rule_node(rule_chain_id, external_id);
CREATE INDEX IF NOT EXISTS idx_dashboard_external_id ON dashboard(tenant_id, external_id);
CREATE INDEX IF NOT EXISTS idx_customer_external_id ON customer(tenant_id, external_id);
CREATE INDEX IF NOT EXISTS idx_widgets_bundle_external_id ON widgets_bundle(tenant_id, external_id);
CREATE INDEX IF NOT EXISTS idx_entity_view_external_id ON entity_view(tenant_id, external_id);
CREATE INDEX IF NOT EXISTS idx_rule_node_type ON rule_node(type); CREATE INDEX IF NOT EXISTS idx_rule_node_type ON rule_node(type);
ALTER TABLE admin_settings ALTER TABLE admin_settings
@ -70,3 +61,80 @@ CREATE TABLE IF NOT EXISTS user_auth_settings (
two_fa_settings varchar two_fa_settings varchar
); );
CREATE INDEX IF NOT EXISTS idx_api_usage_state_entity_id ON api_usage_state(entity_id);
ALTER TABLE tenant_profile DROP COLUMN IF EXISTS isolated_tb_core;
DO
$$
BEGIN
IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'device_external_id_unq_key') THEN
ALTER TABLE device ADD CONSTRAINT device_external_id_unq_key UNIQUE (tenant_id, external_id);
END IF;
END;
$$;
DO
$$
BEGIN
IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'device_profile_external_id_unq_key') THEN
ALTER TABLE device_profile ADD CONSTRAINT device_profile_external_id_unq_key UNIQUE (tenant_id, external_id);
END IF;
END;
$$;
DO
$$
BEGIN
IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'asset_external_id_unq_key') THEN
ALTER TABLE asset ADD CONSTRAINT asset_external_id_unq_key UNIQUE (tenant_id, external_id);
END IF;
END;
$$;
DO
$$
BEGIN
IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'rule_chain_external_id_unq_key') THEN
ALTER TABLE rule_chain ADD CONSTRAINT rule_chain_external_id_unq_key UNIQUE (tenant_id, external_id);
END IF;
END;
$$;
DO
$$
BEGIN
IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'dashboard_external_id_unq_key') THEN
ALTER TABLE dashboard ADD CONSTRAINT dashboard_external_id_unq_key UNIQUE (tenant_id, external_id);
END IF;
END;
$$;
DO
$$
BEGIN
IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'customer_external_id_unq_key') THEN
ALTER TABLE customer ADD CONSTRAINT customer_external_id_unq_key UNIQUE (tenant_id, external_id);
END IF;
END;
$$;
DO
$$
BEGIN
IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'widgets_bundle_external_id_unq_key') THEN
ALTER TABLE widgets_bundle ADD CONSTRAINT widgets_bundle_external_id_unq_key UNIQUE (tenant_id, external_id);
END IF;
END;
$$;
DO
$$
BEGIN
IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'entity_view_external_id_unq_key') THEN
ALTER TABLE entity_view ADD CONSTRAINT entity_view_external_id_unq_key UNIQUE (tenant_id, external_id);
END IF;
END;
$$;

49
application/src/main/data/upgrade/3.3.4/schema_update_device_profile.sql

@ -1,49 +0,0 @@
--
-- 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.
--
ALTER TABLE device_profile
ADD COLUMN IF NOT EXISTS default_queue_id uuid;
DO
$$
BEGIN
IF EXISTS
(SELECT column_name
FROM information_schema.columns
WHERE table_name = 'device_profile'
AND column_name = 'default_queue_name'
)
THEN
UPDATE device_profile
SET default_queue_id = q.id
FROM queue as q
WHERE default_queue_name = q.name;
END IF;
END
$$;
DO
$$
BEGIN
IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'fk_default_queue_device_profile') THEN
ALTER TABLE device_profile
ADD CONSTRAINT fk_default_queue_device_profile FOREIGN KEY (default_queue_id) REFERENCES queue (id);
END IF;
END;
$$;
ALTER TABLE device_profile
DROP COLUMN IF EXISTS default_queue_name;

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

@ -508,13 +508,8 @@ public class ActorSystemContext {
return partitionService.resolve(serviceType, tenantId, entityId); return partitionService.resolve(serviceType, tenantId, entityId);
} }
public TopicPartitionInfo resolve(ServiceType serviceType, QueueId queueId, TenantId tenantId, EntityId entityId) {
return partitionService.resolve(serviceType, queueId, tenantId, entityId);
}
@Deprecated
public TopicPartitionInfo resolve(ServiceType serviceType, String queueName, TenantId tenantId, EntityId entityId) { public TopicPartitionInfo resolve(ServiceType serviceType, String queueName, TenantId tenantId, EntityId entityId) {
return partitionService.resolve(serviceType, tenantId, entityId, queueName); return partitionService.resolve(serviceType, queueName, tenantId, entityId);
} }
public String getServiceId() { public String getServiceId() {

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

@ -162,18 +162,11 @@ class DefaultTbContext implements TbContext {
} }
@Override @Override
@Deprecated
public void enqueue(TbMsg tbMsg, String queueName, Runnable onSuccess, Consumer<Throwable> onFailure) { public void enqueue(TbMsg tbMsg, String queueName, Runnable onSuccess, Consumer<Throwable> onFailure) {
TopicPartitionInfo tpi = resolvePartition(tbMsg, queueName); TopicPartitionInfo tpi = resolvePartition(tbMsg, queueName);
enqueue(tpi, tbMsg, onFailure, onSuccess); enqueue(tpi, tbMsg, onFailure, onSuccess);
} }
@Override
public void enqueue(TbMsg tbMsg, QueueId queueId, Runnable onSuccess, Consumer<Throwable> onFailure) {
TopicPartitionInfo tpi = resolvePartition(tbMsg, queueId);
enqueue(tpi, tbMsg, onFailure, onSuccess);
}
private void enqueue(TopicPartitionInfo tpi, TbMsg tbMsg, Consumer<Throwable> onFailure, Runnable onSuccess) { private void enqueue(TopicPartitionInfo tpi, TbMsg tbMsg, Consumer<Throwable> onFailure, Runnable onSuccess) {
if (!tbMsg.isValid()) { if (!tbMsg.isValid()) {
log.trace("[{}] Skip invalid message: {}", getTenantId(), tbMsg); log.trace("[{}] Skip invalid message: {}", getTenantId(), tbMsg);
@ -223,35 +216,30 @@ class DefaultTbContext implements TbContext {
} }
@Override @Override
public void enqueueForTellNext(TbMsg tbMsg, QueueId queueId, String relationType, Runnable onSuccess, Consumer<Throwable> onFailure) { public void enqueueForTellNext(TbMsg tbMsg, String queueName, String relationType, Runnable onSuccess, Consumer<Throwable> onFailure) {
TopicPartitionInfo tpi = resolvePartition(tbMsg, queueId); TopicPartitionInfo tpi = resolvePartition(tbMsg, queueName);
enqueueForTellNext(tpi, queueId, tbMsg, Collections.singleton(relationType), null, onSuccess, onFailure); enqueueForTellNext(tpi, queueName, tbMsg, Collections.singleton(relationType), null, onSuccess, onFailure);
} }
@Override @Override
public void enqueueForTellNext(TbMsg tbMsg, QueueId queueId, Set<String> relationTypes, Runnable onSuccess, Consumer<Throwable> onFailure) { public void enqueueForTellNext(TbMsg tbMsg, String queueName, Set<String> relationTypes, Runnable onSuccess, Consumer<Throwable> onFailure) {
TopicPartitionInfo tpi = resolvePartition(tbMsg, queueId); TopicPartitionInfo tpi = resolvePartition(tbMsg, queueName);
enqueueForTellNext(tpi, queueId, tbMsg, relationTypes, null, onSuccess, onFailure); enqueueForTellNext(tpi, queueName, tbMsg, relationTypes, null, onSuccess, onFailure);
}
private TopicPartitionInfo resolvePartition(TbMsg tbMsg, QueueId queueId) {
return mainCtx.resolve(ServiceType.TB_RULE_ENGINE, queueId, getTenantId(), tbMsg.getOriginator());
} }
@Deprecated
private TopicPartitionInfo resolvePartition(TbMsg tbMsg, String queueName) { private TopicPartitionInfo resolvePartition(TbMsg tbMsg, String queueName) {
return mainCtx.resolve(ServiceType.TB_RULE_ENGINE, queueName, getTenantId(), tbMsg.getOriginator()); return mainCtx.resolve(ServiceType.TB_RULE_ENGINE, queueName, getTenantId(), tbMsg.getOriginator());
} }
private TopicPartitionInfo resolvePartition(TbMsg tbMsg) { private TopicPartitionInfo resolvePartition(TbMsg tbMsg) {
return resolvePartition(tbMsg, tbMsg.getQueueId()); return resolvePartition(tbMsg, tbMsg.getQueueName());
} }
private void enqueueForTellNext(TopicPartitionInfo tpi, TbMsg source, Set<String> relationTypes, String failureMessage, Runnable onSuccess, Consumer<Throwable> onFailure) { private void enqueueForTellNext(TopicPartitionInfo tpi, TbMsg source, Set<String> relationTypes, String failureMessage, Runnable onSuccess, Consumer<Throwable> onFailure) {
enqueueForTellNext(tpi, source.getQueueId(), source, relationTypes, failureMessage, onSuccess, onFailure); enqueueForTellNext(tpi, source.getQueueName(), source, relationTypes, failureMessage, onSuccess, onFailure);
} }
private void enqueueForTellNext(TopicPartitionInfo tpi, QueueId queueId, TbMsg source, Set<String> relationTypes, String failureMessage, Runnable onSuccess, Consumer<Throwable> onFailure) { private void enqueueForTellNext(TopicPartitionInfo tpi, String queueName, TbMsg source, Set<String> relationTypes, String failureMessage, Runnable onSuccess, Consumer<Throwable> onFailure) {
if (!source.isValid()) { if (!source.isValid()) {
log.trace("[{}] Skip invalid message: {}", getTenantId(), source); log.trace("[{}] Skip invalid message: {}", getTenantId(), source);
if (onFailure != null) { if (onFailure != null) {
@ -261,7 +249,7 @@ class DefaultTbContext implements TbContext {
} }
RuleChainId ruleChainId = nodeCtx.getSelf().getRuleChainId(); RuleChainId ruleChainId = nodeCtx.getSelf().getRuleChainId();
RuleNodeId ruleNodeId = nodeCtx.getSelf().getId(); RuleNodeId ruleNodeId = nodeCtx.getSelf().getId();
TbMsg tbMsg = TbMsg.newMsg(source, queueId, ruleChainId, ruleNodeId); TbMsg tbMsg = TbMsg.newMsg(source, queueName, ruleChainId, ruleNodeId);
TransportProtos.ToRuleEngineMsg.Builder msg = TransportProtos.ToRuleEngineMsg.newBuilder() TransportProtos.ToRuleEngineMsg.Builder msg = TransportProtos.ToRuleEngineMsg.newBuilder()
.setTenantIdMSB(getTenantId().getId().getMostSignificantBits()) .setTenantIdMSB(getTenantId().getId().getMostSignificantBits())
.setTenantIdLSB(getTenantId().getId().getLeastSignificantBits()) .setTenantIdLSB(getTenantId().getId().getLeastSignificantBits())
@ -320,13 +308,13 @@ class DefaultTbContext implements TbContext {
} }
@Override @Override
public TbMsg newMsg(QueueId queueId, String type, EntityId originator, TbMsgMetaData metaData, String data) { public TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data) {
return newMsg(queueId, type, originator, null, metaData, data); return newMsg(queueName, type, originator, null, metaData, data);
} }
@Override @Override
public TbMsg newMsg(QueueId queueId, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) { public TbMsg newMsg(String queueName, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) {
return TbMsg.newMsg(queueId, type, originator, customerId, metaData, data, nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId()); return TbMsg.newMsg(queueName, type, originator, customerId, metaData, data, nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId());
} }
@Override @Override
@ -340,17 +328,17 @@ class DefaultTbContext implements TbContext {
public TbMsg deviceCreatedMsg(Device device, RuleNodeId ruleNodeId) { public TbMsg deviceCreatedMsg(Device device, RuleNodeId ruleNodeId) {
RuleChainId ruleChainId = null; RuleChainId ruleChainId = null;
QueueId queueId = null; String queueName = null;
if (device.getDeviceProfileId() != null) { if (device.getDeviceProfileId() != null) {
DeviceProfile deviceProfile = mainCtx.getDeviceProfileCache().find(device.getDeviceProfileId()); DeviceProfile deviceProfile = mainCtx.getDeviceProfileCache().find(device.getDeviceProfileId());
if (deviceProfile == null) { if (deviceProfile == null) {
log.warn("[{}] Device profile is null!", device.getDeviceProfileId()); log.warn("[{}] Device profile is null!", device.getDeviceProfileId());
} else { } else {
ruleChainId = deviceProfile.getDefaultRuleChainId(); ruleChainId = deviceProfile.getDefaultRuleChainId();
queueId = deviceProfile.getDefaultQueueId(); queueName = deviceProfile.getDefaultQueueName();
} }
} }
return entityActionMsg(device, device.getId(), ruleNodeId, DataConstants.ENTITY_CREATED, queueId, ruleChainId); return entityActionMsg(device, device.getId(), ruleNodeId, DataConstants.ENTITY_CREATED, queueName, ruleChainId);
} }
public TbMsg assetCreatedMsg(Asset asset, RuleNodeId ruleNodeId) { public TbMsg assetCreatedMsg(Asset asset, RuleNodeId ruleNodeId) {
@ -359,7 +347,7 @@ class DefaultTbContext implements TbContext {
public TbMsg alarmActionMsg(Alarm alarm, RuleNodeId ruleNodeId, String action) { public TbMsg alarmActionMsg(Alarm alarm, RuleNodeId ruleNodeId, String action) {
RuleChainId ruleChainId = null; RuleChainId ruleChainId = null;
QueueId queueId = null; String queueName = null;
if (EntityType.DEVICE.equals(alarm.getOriginator().getEntityType())) { if (EntityType.DEVICE.equals(alarm.getOriginator().getEntityType())) {
DeviceId deviceId = new DeviceId(alarm.getOriginator().getId()); DeviceId deviceId = new DeviceId(alarm.getOriginator().getId());
DeviceProfile deviceProfile = mainCtx.getDeviceProfileCache().get(getTenantId(), deviceId); DeviceProfile deviceProfile = mainCtx.getDeviceProfileCache().get(getTenantId(), deviceId);
@ -367,10 +355,10 @@ class DefaultTbContext implements TbContext {
log.warn("[{}] Device profile is null!", deviceId); log.warn("[{}] Device profile is null!", deviceId);
} else { } else {
ruleChainId = deviceProfile.getDefaultRuleChainId(); ruleChainId = deviceProfile.getDefaultRuleChainId();
queueId = deviceProfile.getDefaultQueueId(); queueName = deviceProfile.getDefaultQueueName();
} }
} }
return entityActionMsg(alarm, alarm.getId(), ruleNodeId, action, queueId, ruleChainId); return entityActionMsg(alarm, alarm.getId(), ruleNodeId, action, queueName, ruleChainId);
} }
@Override @Override
@ -382,9 +370,9 @@ class DefaultTbContext implements TbContext {
return entityActionMsg(entity, id, ruleNodeId, action, null, null); return entityActionMsg(entity, id, ruleNodeId, action, null, null);
} }
public <E, I extends EntityId> TbMsg entityActionMsg(E entity, I id, RuleNodeId ruleNodeId, String action, QueueId queueId, RuleChainId ruleChainId) { public <E, I extends EntityId> TbMsg entityActionMsg(E entity, I id, RuleNodeId ruleNodeId, String action, String queueName, RuleChainId ruleChainId) {
try { try {
return TbMsg.newMsg(queueId, action, id, getActionMetaData(ruleNodeId), mapper.writeValueAsString(mapper.valueToTree(entity)), ruleChainId, null); return TbMsg.newMsg(queueName, action, id, getActionMetaData(ruleNodeId), mapper.writeValueAsString(mapper.valueToTree(entity)), ruleChainId, null);
} catch (JsonProcessingException | IllegalArgumentException e) { } catch (JsonProcessingException | IllegalArgumentException e) {
throw new RuntimeException("Failed to process " + id.getEntityType().name().toLowerCase() + " " + action + " msg: " + e); throw new RuntimeException("Failed to process " + id.getEntityType().name().toLowerCase() + " " + action + " msg: " + e);
} }

2
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java

@ -293,7 +293,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
try { try {
checkComponentStateActive(msg); checkComponentStateActive(msg);
EntityId entityId = msg.getOriginator(); EntityId entityId = msg.getOriginator();
TopicPartitionInfo tpi = systemContext.resolve(ServiceType.TB_RULE_ENGINE, msg.getQueueId(), tenantId, entityId); TopicPartitionInfo tpi = systemContext.resolve(ServiceType.TB_RULE_ENGINE, msg.getQueueName(), tenantId, entityId);
List<RuleNodeRelation> ruleNodeRelations = nodeRoutes.get(originatorNodeId); List<RuleNodeRelation> ruleNodeRelations = nodeRoutes.get(originatorNodeId);
if (ruleNodeRelations == null) { // When unchecked, this will cause NullPointerException when rule node doesn't exist anymore if (ruleNodeRelations == null) { // When unchecked, this will cause NullPointerException when rule node doesn't exist anymore

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

@ -126,7 +126,9 @@ public class AlarmController extends BaseController {
"\n\nPlatform also deduplicate the alarms based on the entity id of originator and alarm 'type'. " + "\n\nPlatform also deduplicate the alarms based on the entity id of originator and alarm 'type'. " +
"For example, if the user or system component create the alarm with the type 'HighTemperature' for device 'Device A' the new active alarm is created. " + "For example, if the user or system component create the alarm with the type 'HighTemperature' for device 'Device A' the new active alarm is created. " +
"If the user tries to create 'HighTemperature' alarm for the same device again, the previous alarm will be updated (the 'end_ts' will be set to current timestamp). " + "If the user tries to create 'HighTemperature' alarm for the same device again, the previous alarm will be updated (the 'end_ts' will be set to current timestamp). " +
"If the user clears the alarm (see 'Clear Alarm(clearAlarm)'), than new alarm with the same type and same device may be created. " + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH "If the user clears the alarm (see 'Clear Alarm(clearAlarm)'), than new alarm with the same type and same device may be created. " +
"Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Alarm entity. " +
TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH
, produces = MediaType.APPLICATION_JSON_VALUE) , produces = MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/alarm", method = RequestMethod.POST) @RequestMapping(value = "/alarm", method = RequestMethod.POST)

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

@ -139,7 +139,9 @@ public class AssetController extends BaseController {
notes = "Creates or Updates the Asset. When creating asset, platform generates Asset Id as " + UUID_WIKI_LINK + notes = "Creates or Updates the Asset. When creating asset, platform generates Asset Id as " + UUID_WIKI_LINK +
"The newly created Asset id will be present in the response. " + "The newly created Asset id will be present in the response. " +
"Specify existing Asset id to update the asset. " + "Specify existing Asset id to update the asset. " +
"Referencing non-existing Asset Id will cause 'Not Found' error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, produces = MediaType.APPLICATION_JSON_VALUE) "Referencing non-existing Asset Id will cause 'Not Found' error. " +
"Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Asset entity. "
+ TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, produces = MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/asset", method = RequestMethod.POST) @RequestMapping(value = "/asset", method = RequestMethod.POST)
@ResponseBody @ResponseBody

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

@ -112,6 +112,10 @@ public class ControllerConstants {
protected static final String DEVICE_PROFILE_INFO_DESCRIPTION = "Device Profile Info is a lightweight object that includes main information about Device Profile excluding the heavyweight configuration object. "; protected static final String DEVICE_PROFILE_INFO_DESCRIPTION = "Device Profile Info is a lightweight object that includes main information about Device Profile excluding the heavyweight configuration object. ";
protected static final String QUEUE_SERVICE_TYPE_DESCRIPTION = "Service type (implemented only for the TB-RULE-ENGINE)"; protected static final String QUEUE_SERVICE_TYPE_DESCRIPTION = "Service type (implemented only for the TB-RULE-ENGINE)";
protected static final String QUEUE_SERVICE_TYPE_ALLOWABLE_VALUES = "TB-RULE-ENGINE, TB-CORE, TB-TRANSPORT, JS-EXECUTOR"; protected static final String QUEUE_SERVICE_TYPE_ALLOWABLE_VALUES = "TB-RULE-ENGINE, TB-CORE, TB-TRANSPORT, JS-EXECUTOR";
protected static final String QUEUE_QUEUE_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the queue name.";
protected static final String QUEUE_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, topic";
protected static final String QUEUE_ID_PARAM_DESCRIPTION = "A string value representing the queue id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'";
protected static final String QUEUE_NAME_PARAM_DESCRIPTION = "A string value representing the queue id. For example, 'Main'";
protected static final String OTA_PACKAGE_INFO_DESCRIPTION = "OTA Package Info is a lightweight object that includes main information about the OTA Package excluding the heavyweight data. "; protected static final String OTA_PACKAGE_INFO_DESCRIPTION = "OTA Package Info is a lightweight object that includes main information about the OTA Package excluding the heavyweight data. ";
protected static final String OTA_PACKAGE_DESCRIPTION = "OTA Package is a heavyweight object that includes main information about the OTA Package and also data. "; protected static final String OTA_PACKAGE_DESCRIPTION = "OTA Package is a heavyweight object that includes main information about the OTA Package and also data. ";
protected static final String OTA_PACKAGE_CHECKSUM_ALGORITHM_ALLOWABLE_VALUES = "MD5, SHA256, SHA384, SHA512, CRC32, MURMUR3_32, MURMUR3_128"; protected static final String OTA_PACKAGE_CHECKSUM_ALGORITHM_ALLOWABLE_VALUES = "MD5, SHA256, SHA384, SHA512, CRC32, MURMUR3_32, MURMUR3_128";

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

@ -138,7 +138,9 @@ public class CustomerController extends BaseController {
notes = "Creates or Updates the Customer. When creating customer, platform generates Customer Id as " + UUID_WIKI_LINK + notes = "Creates or Updates the Customer. When creating customer, platform generates Customer Id as " + UUID_WIKI_LINK +
"The newly created Customer Id will be present in the response. " + "The newly created Customer Id will be present in the response. " +
"Specify existing Customer Id to update the Customer. " + "Specify existing Customer Id to update the Customer. " +
"Referencing non-existing Customer Id will cause 'Not Found' error." + TENANT_AUTHORITY_PARAGRAPH) "Referencing non-existing Customer Id will cause 'Not Found' error." +
"Remove 'id', 'tenantId' from the request body example (below) to create new Customer entity. " +
TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')") @PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/customer", method = RequestMethod.POST) @RequestMapping(value = "/customer", method = RequestMethod.POST)
@ResponseBody @ResponseBody

14
application/src/main/java/org/thingsboard/server/controller/DashboardController.java

@ -172,6 +172,7 @@ public class DashboardController extends BaseController {
"The newly created Dashboard id will be present in the response. " + "The newly created Dashboard id will be present in the response. " +
"Specify existing Dashboard id to update the dashboard. " + "Specify existing Dashboard id to update the dashboard. " +
"Referencing non-existing dashboard Id will cause 'Not Found' error. " + "Referencing non-existing dashboard Id will cause 'Not Found' error. " +
"Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Dashboard entity. " +
TENANT_AUTHORITY_PARAGRAPH, TENANT_AUTHORITY_PARAGRAPH,
produces = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE,
consumes = MediaType.APPLICATION_JSON_VALUE) consumes = MediaType.APPLICATION_JSON_VALUE)
@ -261,7 +262,7 @@ public class DashboardController extends BaseController {
checkParameter(DASHBOARD_ID, strDashboardId); checkParameter(DASHBOARD_ID, strDashboardId);
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId, Operation.ASSIGN_TO_CUSTOMER); Dashboard dashboard = checkDashboardId(dashboardId, Operation.ASSIGN_TO_CUSTOMER);
Set<CustomerId> customerIds = customerIdFromStr(strCustomerIds, dashboard); Set<CustomerId> customerIds = customerIdFromStr(strCustomerIds);
return tbDashboardService.updateDashboardCustomers(dashboard, customerIds, getCurrentUser()); return tbDashboardService.updateDashboardCustomers(dashboard, customerIds, getCurrentUser());
} }
@ -281,7 +282,7 @@ public class DashboardController extends BaseController {
checkParameter(DASHBOARD_ID, strDashboardId); checkParameter(DASHBOARD_ID, strDashboardId);
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId, Operation.ASSIGN_TO_CUSTOMER); Dashboard dashboard = checkDashboardId(dashboardId, Operation.ASSIGN_TO_CUSTOMER);
Set<CustomerId> customerIds = customerIdFromStr(strCustomerIds, dashboard); Set<CustomerId> customerIds = customerIdFromStr(strCustomerIds);
return tbDashboardService.addDashboardCustomers(dashboard, customerIds, getCurrentUser()); return tbDashboardService.addDashboardCustomers(dashboard, customerIds, getCurrentUser());
} }
@ -301,7 +302,7 @@ public class DashboardController extends BaseController {
checkParameter(DASHBOARD_ID, strDashboardId); checkParameter(DASHBOARD_ID, strDashboardId);
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId, Operation.UNASSIGN_FROM_CUSTOMER); Dashboard dashboard = checkDashboardId(dashboardId, Operation.UNASSIGN_FROM_CUSTOMER);
Set<CustomerId> customerIds = customerIdFromStr(strCustomerIds, dashboard); Set<CustomerId> customerIds = customerIdFromStr(strCustomerIds);
return tbDashboardService.removeDashboardCustomers(dashboard, customerIds, getCurrentUser()); return tbDashboardService.removeDashboardCustomers(dashboard, customerIds, getCurrentUser());
} }
@ -704,14 +705,11 @@ public class DashboardController extends BaseController {
} }
} }
private Set<CustomerId> customerIdFromStr(String[] strCustomerIds, Dashboard dashboard) { private Set<CustomerId> customerIdFromStr(String[] strCustomerIds) {
Set<CustomerId> customerIds = new HashSet<>(); Set<CustomerId> customerIds = new HashSet<>();
if (strCustomerIds != null) { if (strCustomerIds != null) {
for (String strCustomerId : strCustomerIds) { for (String strCustomerId : strCustomerIds) {
CustomerId customerId = new CustomerId(UUID.fromString(strCustomerId)); customerIds.add(new CustomerId(UUID.fromString(strCustomerId)));
if (dashboard.isAssignedToCustomer(customerId)) {
customerIds.add(customerId);
}
} }
} }
return customerIds; return customerIds;

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

@ -158,8 +158,9 @@ public class DeviceController extends BaseController {
"The newly created device id will be present in the response. " + "The newly created device id will be present in the response. " +
"Specify existing Device id to update the device. " + "Specify existing Device id to update the device. " +
"Referencing non-existing device Id will cause 'Not Found' error." + "Referencing non-existing device Id will cause 'Not Found' error." +
"\n\nDevice name is unique in the scope of tenant. Use unique identifiers like MAC or IMEI for the device names and non-unique 'label' field for user-friendly visualization purposes." "\n\nDevice name is unique in the scope of tenant. Use unique identifiers like MAC or IMEI for the device names and non-unique 'label' field for user-friendly visualization purposes." +
+ TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) "Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Device entity. " +
TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/device", method = RequestMethod.POST) @RequestMapping(value = "/device", method = RequestMethod.POST)
@ResponseBody @ResponseBody
@ -181,6 +182,7 @@ public class DeviceController extends BaseController {
"Requires to provide the Device Credentials object as well. Useful to create device and credentials in one request. " + "Requires to provide the Device Credentials object as well. Useful to create device and credentials in one request. " +
"You may find the example of LwM2M device and RPK credentials below: \n\n" + "You may find the example of LwM2M device and RPK credentials below: \n\n" +
DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_DESCRIPTION_MARKDOWN + DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_DESCRIPTION_MARKDOWN +
"Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Device entity. " +
TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/device-with-credentials", method = RequestMethod.POST) @RequestMapping(value = "/device-with-credentials", method = RequestMethod.POST)

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

@ -191,6 +191,7 @@ public class DeviceProfileController extends BaseController {
"Specify existing device profile id to update the device profile. " + "Specify existing device profile id to update the device profile. " +
"Referencing non-existing device profile Id will cause 'Not Found' error. " + NEW_LINE + "Referencing non-existing device profile Id will cause 'Not Found' error. " + NEW_LINE +
"Device profile name is unique in the scope of tenant. Only one 'default' device profile may exist in scope of tenant." + DEVICE_PROFILE_DATA + "Device profile name is unique in the scope of tenant. Only one 'default' device profile may exist in scope of tenant." + DEVICE_PROFILE_DATA +
"Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Device Profile entity. " +
TENANT_AUTHORITY_PARAGRAPH, TENANT_AUTHORITY_PARAGRAPH,
produces = "application/json", produces = "application/json",
consumes = "application/json") consumes = "application/json")

5
application/src/main/java/org/thingsboard/server/controller/EdgeController.java

@ -141,8 +141,9 @@ public class EdgeController extends BaseController {
"The newly created edge id will be present in the response. " + "The newly created edge id will be present in the response. " +
"Specify existing Edge id to update the edge. " + "Specify existing Edge id to update the edge. " +
"Referencing non-existing Edge Id will cause 'Not Found' error." + "Referencing non-existing Edge Id will cause 'Not Found' error." +
"\n\nEdge name is unique in the scope of tenant. Use unique identifiers like MAC or IMEI for the edge names and non-unique 'label' field for user-friendly visualization purposes." "\n\nEdge name is unique in the scope of tenant. Use unique identifiers like MAC or IMEI for the edge names and non-unique 'label' field for user-friendly visualization purposes." +
+ TENANT_AUTHORITY_PARAGRAPH, "Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Edge entity. " +
TENANT_AUTHORITY_PARAGRAPH,
produces = MediaType.APPLICATION_JSON_VALUE) produces = MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize("hasAuthority('TENANT_ADMIN')") @PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge", method = RequestMethod.POST) @RequestMapping(value = "/edge", method = RequestMethod.POST)

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

@ -192,7 +192,7 @@ public class EntitiesVersionControlController extends BaseController {
@GetMapping(value = "/version/{requestId}/status") @GetMapping(value = "/version/{requestId}/status")
public VersionCreationResult getVersionCreateRequestStatus(@ApiParam(value = VC_REQUEST_ID_PARAM_DESCRIPTION, required = true) public VersionCreationResult getVersionCreateRequestStatus(@ApiParam(value = VC_REQUEST_ID_PARAM_DESCRIPTION, required = true)
@PathVariable UUID requestId) throws Exception { @PathVariable UUID requestId) throws Exception {
accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.READ); accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.WRITE);
return versionControlService.getVersionCreateStatus(getCurrentUser(), requestId); return versionControlService.getVersionCreateStatus(getCurrentUser(), requestId);
} }
@ -469,7 +469,7 @@ public class EntitiesVersionControlController extends BaseController {
@GetMapping(value = "/entity/{requestId}/status") @GetMapping(value = "/entity/{requestId}/status")
public VersionLoadResult getVersionLoadRequestStatus(@ApiParam(value = VC_REQUEST_ID_PARAM_DESCRIPTION, required = true) public VersionLoadResult getVersionLoadRequestStatus(@ApiParam(value = VC_REQUEST_ID_PARAM_DESCRIPTION, required = true)
@PathVariable UUID requestId) throws Exception { @PathVariable UUID requestId) throws Exception {
accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.READ); accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.WRITE);
return versionControlService.getVersionLoadStatus(getCurrentUser(), requestId); return versionControlService.getVersionLoadStatus(getCurrentUser(), requestId);
} }

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

@ -132,7 +132,9 @@ public class EntityViewController extends BaseController {
} }
@ApiOperation(value = "Save or update entity view (saveEntityView)", @ApiOperation(value = "Save or update entity view (saveEntityView)",
notes = ENTITY_VIEW_DESCRIPTION + MODEL_DESCRIPTION, notes = ENTITY_VIEW_DESCRIPTION + MODEL_DESCRIPTION +
"Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Entity View entity." +
TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
produces = MediaType.APPLICATION_JSON_VALUE) produces = MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/entityView", method = RequestMethod.POST) @RequestMapping(value = "/entityView", method = RequestMethod.POST)

61
application/src/main/java/org/thingsboard/server/controller/QueueController.java

@ -15,6 +15,8 @@
*/ */
package org.thingsboard.server.controller; package org.thingsboard.server.controller;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
@ -37,6 +39,22 @@ import org.thingsboard.server.service.security.permission.Resource;
import java.util.UUID; import java.util.UUID;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.QUEUE_ID_PARAM_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.QUEUE_NAME_PARAM_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.QUEUE_QUEUE_TEXT_SEARCH_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.QUEUE_SERVICE_TYPE_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.QUEUE_SERVICE_TYPE_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.QUEUE_SORT_PROPERTY_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.SYSTEM_AUTHORITY_PARAGRAPH;
import static org.thingsboard.server.controller.ControllerConstants.SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH;
import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LINK;
@RestController @RestController
@TbCoreComponent @TbCoreComponent
@RequestMapping("/api") @RequestMapping("/api")
@ -45,14 +63,23 @@ public class QueueController extends BaseController {
private final TbQueueService tbQueueService; private final TbQueueService tbQueueService;
@ApiOperation(value = "Get Queues (getTenantQueuesByServiceType)",
notes = "Returns a page of queues registered in the platform. " +
PAGE_DATA_PARAMETERS + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/queues", params = {"serviceType", "pageSize", "page"}, method = RequestMethod.GET) @RequestMapping(value = "/queues", params = {"serviceType", "pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody @ResponseBody
public PageData<Queue> getTenantQueuesByServiceType(@RequestParam String serviceType, public PageData<Queue> getTenantQueuesByServiceType(@ApiParam(value = QUEUE_SERVICE_TYPE_DESCRIPTION, allowableValues = QUEUE_SERVICE_TYPE_ALLOWABLE_VALUES, required = true)
@RequestParam String serviceType,
@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize, @RequestParam int pageSize,
@ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page, @RequestParam int page,
@ApiParam(value = QUEUE_QUEUE_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch, @RequestParam(required = false) String textSearch,
@ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = QUEUE_SORT_PROPERTY_ALLOWABLE_VALUES)
@RequestParam(required = false) String sortProperty, @RequestParam(required = false) String sortProperty,
@ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
@RequestParam(required = false) String sortOrder) throws ThingsboardException { @RequestParam(required = false) String sortOrder) throws ThingsboardException {
checkParameter("serviceType", serviceType); checkParameter("serviceType", serviceType);
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
@ -65,20 +92,44 @@ public class QueueController extends BaseController {
} }
} }
@ApiOperation(value = "Get Queue (getQueueById)",
notes = "Fetch the Queue object based on the provided Queue Id. " + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/queues/{queueId}", method = RequestMethod.GET) @RequestMapping(value = "/queues/{queueId}", method = RequestMethod.GET)
@ResponseBody @ResponseBody
public Queue getQueueById(@PathVariable("queueId") String queueIdStr) throws ThingsboardException { public Queue getQueueById(@ApiParam(value = QUEUE_ID_PARAM_DESCRIPTION)
@PathVariable("queueId") String queueIdStr) throws ThingsboardException {
checkParameter("queueId", queueIdStr); checkParameter("queueId", queueIdStr);
QueueId queueId = new QueueId(UUID.fromString(queueIdStr)); QueueId queueId = new QueueId(UUID.fromString(queueIdStr));
checkQueueId(queueId, Operation.READ); checkQueueId(queueId, Operation.READ);
return checkNotNull(queueService.findQueueById(getTenantId(), queueId)); return checkNotNull(queueService.findQueueById(getTenantId(), queueId));
} }
@ApiOperation(value = "Get Queue (getQueueByName)",
notes = "Fetch the Queue object based on the provided Queue name. " + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/queues/name/{queueName}", method = RequestMethod.GET)
@ResponseBody
public Queue getQueueByName(@ApiParam(value = QUEUE_NAME_PARAM_DESCRIPTION)
@PathVariable("queueName") String queueName) throws ThingsboardException {
checkParameter("queueName", queueName);
return checkNotNull(queueService.findQueueByTenantIdAndName(getTenantId(), queueName));
}
@ApiOperation(value = "Create Or Update Queue (saveQueue)",
notes = "Create or update the Queue. When creating queue, platform generates Queue Id as " + UUID_WIKI_LINK +
"Specify existing Queue id to update the queue. " +
"Referencing non-existing Queue Id will cause 'Not Found' error." +
"\n\nQueue name is unique in the scope of sysadmin. " +
"Remove 'id', 'tenantId' from the request body example (below) to create new Queue entity. " +
SYSTEM_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
@RequestMapping(value = "/queues", params = {"serviceType"}, method = RequestMethod.POST) @RequestMapping(value = "/queues", params = {"serviceType"}, method = RequestMethod.POST)
@ResponseBody @ResponseBody
public Queue saveQueue(@RequestBody Queue queue,
public Queue saveQueue(@ApiParam(value = "A JSON value representing the queue.")
@RequestBody Queue queue,
@ApiParam(value = QUEUE_SERVICE_TYPE_DESCRIPTION, allowableValues = QUEUE_SERVICE_TYPE_ALLOWABLE_VALUES, required = true)
@RequestParam String serviceType) throws ThingsboardException { @RequestParam String serviceType) throws ThingsboardException {
checkParameter("serviceType", serviceType); checkParameter("serviceType", serviceType);
queue.setTenantId(getCurrentUser().getTenantId()); queue.setTenantId(getCurrentUser().getTenantId());
@ -97,10 +148,12 @@ public class QueueController extends BaseController {
} }
} }
@ApiOperation(value = "Delete Queue (deleteQueue)", notes = "Deletes the Queue. " + SYSTEM_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
@RequestMapping(value = "/queues/{queueId}", method = RequestMethod.DELETE) @RequestMapping(value = "/queues/{queueId}", method = RequestMethod.DELETE)
@ResponseBody @ResponseBody
public void deleteQueue(@PathVariable("queueId") String queueIdStr) throws ThingsboardException { public void deleteQueue(@ApiParam(value = QUEUE_ID_PARAM_DESCRIPTION)
@PathVariable("queueId") String queueIdStr) throws ThingsboardException {
checkParameter("queueId", queueIdStr); checkParameter("queueId", queueIdStr);
QueueId queueId = new QueueId(toUUID(queueIdStr)); QueueId queueId = new QueueId(toUUID(queueIdStr));
checkQueueId(queueId, Operation.DELETE); checkQueueId(queueId, Operation.DELETE);

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

@ -227,7 +227,9 @@ public class RuleChainController extends BaseController {
"The newly created Rule Chain Id will be present in the response. " + "The newly created Rule Chain Id will be present in the response. " +
"Specify existing Rule Chain id to update the rule chain. " + "Specify existing Rule Chain id to update the rule chain. " +
"Referencing non-existing rule chain Id will cause 'Not Found' error." + "Referencing non-existing rule chain Id will cause 'Not Found' error." +
"\n\n" + RULE_CHAIN_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH) "\n\n" + RULE_CHAIN_DESCRIPTION +
"Remove 'id', 'tenantId' from the request body example (below) to create new Rule Chain entity." +
TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/ruleChain", method = RequestMethod.POST) @RequestMapping(value = "/ruleChain", method = RequestMethod.POST)
@ResponseBody @ResponseBody

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

@ -139,7 +139,9 @@ public class TbResourceController extends BaseController {
"The newly created Resource id will be present in the response. " + "The newly created Resource id will be present in the response. " +
"Specify existing Resource id to update the Resource. " + "Specify existing Resource id to update the Resource. " +
"Referencing non-existing Resource Id will cause 'Not Found' error. " + "Referencing non-existing Resource Id will cause 'Not Found' error. " +
"\n\nResource combination of the title with the key is unique in the scope of tenant. " + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH, "\n\nResource combination of the title with the key is unique in the scope of tenant. " +
"Remove 'id', 'tenantId' from the request body example (below) to create new Resource entity." +
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH,
produces = "application/json", produces = "application/json",
consumes = "application/json") consumes = "application/json")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")

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

@ -115,6 +115,7 @@ public class TenantController extends BaseController {
"The newly created Tenant Id will be present in the response. " + "The newly created Tenant Id will be present in the response. " +
"Specify existing Tenant Id id to update the Tenant. " + "Specify existing Tenant Id id to update the Tenant. " +
"Referencing non-existing Tenant Id will cause 'Not Found' error." + "Referencing non-existing Tenant Id will cause 'Not Found' error." +
"Remove 'id', 'tenantId' from the request body example (below) to create new Tenant entity." +
SYSTEM_AUTHORITY_PARAGRAPH) SYSTEM_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('SYS_ADMIN')") @PreAuthorize("hasAuthority('SYS_ADMIN')")
@RequestMapping(value = "/tenant", method = RequestMethod.POST) @RequestMapping(value = "/tenant", method = RequestMethod.POST)

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

@ -128,7 +128,6 @@ public class TenantProfileController extends BaseController {
"{\n" + "{\n" +
" \"name\": \"Default\",\n" + " \"name\": \"Default\",\n" +
" \"description\": \"Default tenant profile\",\n" + " \"description\": \"Default tenant profile\",\n" +
" \"isolatedTbCore\": false,\n" +
" \"isolatedTbRuleEngine\": false,\n" + " \"isolatedTbRuleEngine\": false,\n" +
" \"profileData\": {\n" + " \"profileData\": {\n" +
" \"configuration\": {\n" + " \"configuration\": {\n" +
@ -165,6 +164,7 @@ public class TenantProfileController extends BaseController {
" \"default\": true\n" + " \"default\": true\n" +
"}" + "}" +
MARKDOWN_CODE_BLOCK_END + MARKDOWN_CODE_BLOCK_END +
"Remove 'id', from the request body example (below) to create new Tenant Profile entity." +
SYSTEM_AUTHORITY_PARAGRAPH) SYSTEM_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('SYS_ADMIN')") @PreAuthorize("hasAuthority('SYS_ADMIN')")
@RequestMapping(value = "/tenantProfile", method = RequestMethod.POST) @RequestMapping(value = "/tenantProfile", method = RequestMethod.POST)

2
application/src/main/java/org/thingsboard/server/controller/TwoFaConfigController.java → application/src/main/java/org/thingsboard/server/controller/TwoFactorAuthConfigController.java

@ -50,7 +50,7 @@ import static org.thingsboard.server.controller.ControllerConstants.NEW_LINE;
@RequestMapping("/api/2fa") @RequestMapping("/api/2fa")
@TbCoreComponent @TbCoreComponent
@RequiredArgsConstructor @RequiredArgsConstructor
public class TwoFaConfigController extends BaseController { public class TwoFactorAuthConfigController extends BaseController {
private final TwoFaConfigManager twoFaConfigManager; private final TwoFaConfigManager twoFaConfigManager;
private final TwoFactorAuthService twoFactorAuthService; private final TwoFactorAuthService twoFactorAuthService;

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

@ -176,7 +176,9 @@ public class UserController extends BaseController {
"The newly created User Id will be present in the response. " + "The newly created User Id will be present in the response. " +
"Specify existing User Id to update the device. " + "Specify existing User Id to update the device. " +
"Referencing non-existing User Id will cause 'Not Found' error." + "Referencing non-existing User Id will cause 'Not Found' error." +
"\n\nDevice email is unique for entire platform setup.") "\n\nDevice email is unique for entire platform setup." +
"Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new User entity." +
"\n\nAvailable for users with 'SYS_ADMIN', 'TENANT_ADMIN' or 'CUSTOMER_USER' authority.")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/user", method = RequestMethod.POST) @RequestMapping(value = "/user", method = RequestMethod.POST)
@ResponseBody @ResponseBody

5
application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java

@ -85,8 +85,9 @@ public class WidgetTypeController extends AutoCommitController {
"Specify existing Widget Type id to update the Widget Type. " + "Specify existing Widget Type id to update the Widget Type. " +
"Referencing non-existing Widget Type Id will cause 'Not Found' error." + "Referencing non-existing Widget Type Id will cause 'Not Found' error." +
"\n\nWidget Type alias is unique in the scope of Widget Bundle. " + "\n\nWidget Type alias is unique in the scope of Widget Bundle. " +
"Special Tenant Id '13814000-1dd2-11b2-8080-808080808080' is automatically used if the create request is sent by user with 'SYS_ADMIN' authority." "Special Tenant Id '13814000-1dd2-11b2-8080-808080808080' is automatically used if the create request is sent by user with 'SYS_ADMIN' authority." +
+ SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) "Remove 'id', 'tenantId' rom the request body example (below) to create new Widget Type entity." +
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/widgetType", method = RequestMethod.POST) @RequestMapping(value = "/widgetType", method = RequestMethod.POST)
@ResponseBody @ResponseBody

5
application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java

@ -89,8 +89,9 @@ public class WidgetsBundleController extends BaseController {
"Specify existing Widget Bundle id to update the Widget Bundle. " + "Specify existing Widget Bundle id to update the Widget Bundle. " +
"Referencing non-existing Widget Bundle Id will cause 'Not Found' error." + "Referencing non-existing Widget Bundle Id will cause 'Not Found' error." +
"\n\nWidget Bundle alias is unique in the scope of tenant. " + "\n\nWidget Bundle alias is unique in the scope of tenant. " +
"Special Tenant Id '13814000-1dd2-11b2-8080-808080808080' is automatically used if the create bundle request is sent by user with 'SYS_ADMIN' authority." "Special Tenant Id '13814000-1dd2-11b2-8080-808080808080' is automatically used if the create bundle request is sent by user with 'SYS_ADMIN' authority." +
+ SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) "Remove 'id', 'tenantId' from the request body example (below) to create new Widgets Bundle entity." +
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/widgetsBundle", method = RequestMethod.POST) @RequestMapping(value = "/widgetsBundle", method = RequestMethod.POST)
@ResponseBody @ResponseBody

2
application/src/main/java/org/thingsboard/server/exception/ThingsboardCredentialsExpiredResponse.java

@ -34,7 +34,7 @@ public class ThingsboardCredentialsExpiredResponse extends ThingsboardErrorRespo
return new ThingsboardCredentialsExpiredResponse(message, resetToken); return new ThingsboardCredentialsExpiredResponse(message, resetToken);
} }
@ApiModelProperty(position = 5, value = "Password reset token", readOnly = true) @ApiModelProperty(position = 5, value = "Password reset token", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
public String getResetToken() { public String getResetToken() {
return resetToken; return resetToken;
} }

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

@ -46,12 +46,12 @@ public class ThingsboardErrorResponse {
return new ThingsboardErrorResponse(message, errorCode, status); return new ThingsboardErrorResponse(message, errorCode, status);
} }
@ApiModelProperty(position = 1, value = "HTTP Response Status Code", example = "401", readOnly = true) @ApiModelProperty(position = 1, value = "HTTP Response Status Code", example = "401", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
public Integer getStatus() { public Integer getStatus() {
return status.value(); return status.value();
} }
@ApiModelProperty(position = 2, value = "Error message", example = "Authentication failed", readOnly = true) @ApiModelProperty(position = 2, value = "Error message", example = "Authentication failed", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
public String getMessage() { public String getMessage() {
return message; return message;
} }
@ -69,12 +69,12 @@ public class ThingsboardErrorResponse {
"\n\n* `34` - Too many updates (Too many updates over Websocket session)" + "\n\n* `34` - Too many updates (Too many updates over Websocket session)" +
"\n\n* `40` - Subscription violation (HTTP: 403 - Forbidden)", "\n\n* `40` - Subscription violation (HTTP: 403 - Forbidden)",
example = "10", dataType = "integer", example = "10", dataType = "integer",
readOnly = true) accessMode = ApiModelProperty.AccessMode.READ_ONLY)
public ThingsboardErrorCode getErrorCode() { public ThingsboardErrorCode getErrorCode() {
return errorCode; return errorCode;
} }
@ApiModelProperty(position = 4, value = "Timestamp", readOnly = true) @ApiModelProperty(position = 4, value = "Timestamp", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
public Date getTimestamp() { public Date getTimestamp() {
return timestamp; return timestamp;
} }

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

@ -48,9 +48,8 @@ public class DeviceProfileMsgConstructor {
// builder.setDefaultRuleChainIdMSB(deviceProfile.getDefaultRuleChainId().getId().getMostSignificantBits()) // builder.setDefaultRuleChainIdMSB(deviceProfile.getDefaultRuleChainId().getId().getMostSignificantBits())
// .setDefaultRuleChainIdLSB(deviceProfile.getDefaultRuleChainId().getId().getLeastSignificantBits()); // .setDefaultRuleChainIdLSB(deviceProfile.getDefaultRuleChainId().getId().getLeastSignificantBits());
// } // }
if (deviceProfile.getDefaultQueueId() != null) { if (deviceProfile.getDefaultQueueName() != null) {
builder.setDefaultQueueIdMSB(deviceProfile.getDefaultQueueId().getId().getMostSignificantBits()) builder.setDefaultQueueName(deviceProfile.getDefaultQueueName());
.setDefaultQueueIdLSB(deviceProfile.getDefaultQueueId().getId().getLeastSignificantBits());
} }
if (deviceProfile.getDescription() != null) { if (deviceProfile.getDescription() != null) {
builder.setDescription(deviceProfile.getDescription()); builder.setDescription(deviceProfile.getDescription());

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

@ -15,7 +15,6 @@
*/ */
package org.thingsboard.server.service.edge.rpc.constructor; package org.thingsboard.server.service.edge.rpc.constructor;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
@ -23,7 +22,6 @@ import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.dao.queue.QueueService;
import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.EdgeVersion;
import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg;
import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg; import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg;
@ -35,11 +33,8 @@ import org.thingsboard.server.service.edge.rpc.constructor.rule.RuleChainMetadat
@Component @Component
@Slf4j @Slf4j
@TbCoreComponent @TbCoreComponent
@AllArgsConstructor
public class RuleChainMsgConstructor { public class RuleChainMsgConstructor {
private final QueueService queueService;
public RuleChainUpdateMsg constructRuleChainUpdatedMsg(RuleChainId edgeRootRuleChainId, UpdateMsgType msgType, RuleChain ruleChain) { public RuleChainUpdateMsg constructRuleChainUpdatedMsg(RuleChainId edgeRootRuleChainId, UpdateMsgType msgType, RuleChain ruleChain) {
RuleChainUpdateMsg.Builder builder = RuleChainUpdateMsg.newBuilder() RuleChainUpdateMsg.Builder builder = RuleChainUpdateMsg.newBuilder()
.setMsgType(msgType) .setMsgType(msgType)
@ -61,7 +56,7 @@ public class RuleChainMsgConstructor {
RuleChainMetaData ruleChainMetaData, RuleChainMetaData ruleChainMetaData,
EdgeVersion edgeVersion) { EdgeVersion edgeVersion) {
RuleChainMetadataConstructor ruleChainMetadataConstructor RuleChainMetadataConstructor ruleChainMetadataConstructor
= RuleChainMetadataConstructorFactory.getByEdgeVersion(edgeVersion, queueService); = RuleChainMetadataConstructorFactory.getByEdgeVersion(edgeVersion);
return ruleChainMetadataConstructor.constructRuleChainMetadataUpdatedMsg(tenantId, msgType, ruleChainMetaData); return ruleChainMetadataConstructor.constructRuleChainMetadataUpdatedMsg(tenantId, msgType, ruleChainMetaData);
} }

30
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/AbstractRuleChainMetadataConstructor.java

@ -16,20 +16,15 @@
package org.thingsboard.server.service.edge.rpc.constructor.rule; package org.thingsboard.server.service.edge.rpc.constructor.rule;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.flow.TbCheckpointNode;
import org.thingsboard.server.common.data.id.QueueId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.queue.Queue;
import org.thingsboard.server.common.data.rule.NodeConnectionInfo; import org.thingsboard.server.common.data.rule.NodeConnectionInfo;
import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo; import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo;
import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.server.dao.queue.QueueService;
import org.thingsboard.server.gen.edge.v1.NodeConnectionInfoProto; import org.thingsboard.server.gen.edge.v1.NodeConnectionInfoProto;
import org.thingsboard.server.gen.edge.v1.RuleChainConnectionInfoProto; import org.thingsboard.server.gen.edge.v1.RuleChainConnectionInfoProto;
import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg;
@ -39,16 +34,11 @@ import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.NavigableSet; import java.util.NavigableSet;
import java.util.UUID;
@Slf4j @Slf4j
@AllArgsConstructor @AllArgsConstructor
public abstract class AbstractRuleChainMetadataConstructor implements RuleChainMetadataConstructor { public abstract class AbstractRuleChainMetadataConstructor implements RuleChainMetadataConstructor {
public static final List<String> nodeTypes = List.of(TbCheckpointNode.class.getName());
private final QueueService queueService;
@Override @Override
public RuleChainMetadataUpdateMsg constructRuleChainMetadataUpdatedMsg(TenantId tenantId, public RuleChainMetadataUpdateMsg constructRuleChainMetadataUpdatedMsg(TenantId tenantId,
UpdateMsgType msgType, UpdateMsgType msgType,
@ -144,24 +134,4 @@ public abstract class AbstractRuleChainMetadataConstructor implements RuleChainM
.setAdditionalInfo(JacksonUtil.OBJECT_MAPPER.writeValueAsString(ruleChainConnectionInfo.getAdditionalInfo())) .setAdditionalInfo(JacksonUtil.OBJECT_MAPPER.writeValueAsString(ruleChainConnectionInfo.getAdditionalInfo()))
.build(); .build();
} }
protected List<RuleNode> updateQueueIdToQueueNameNodeConfiguration(TenantId tenantId, List<RuleNode> nodes) {
List<RuleNode> result = new ArrayList<>();
for (RuleNode node : nodes) {
if (nodeTypes.contains(node.getType())) {
ObjectNode configuration = (ObjectNode) node.getConfiguration();
JsonNode queueIdNode = configuration.remove("queueId");
if (queueIdNode != null) {
String queueId = queueIdNode.asText();
Queue queueById = queueService.findQueueById(tenantId, new QueueId(UUID.fromString(queueId)));
if (queueById != null) {
configuration.put("queueName", queueById.getName());
node.setConfiguration(configuration);
}
}
}
result.add(node);
}
return result;
}
} }

9
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorFactory.java

@ -15,21 +15,18 @@
*/ */
package org.thingsboard.server.service.edge.rpc.constructor.rule; package org.thingsboard.server.service.edge.rpc.constructor.rule;
import org.thingsboard.server.dao.queue.QueueService;
import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.EdgeVersion;
public final class RuleChainMetadataConstructorFactory { public final class RuleChainMetadataConstructorFactory {
public static RuleChainMetadataConstructor getByEdgeVersion(EdgeVersion edgeVersion, public static RuleChainMetadataConstructor getByEdgeVersion(EdgeVersion edgeVersion) {
QueueService queueService) {
switch (edgeVersion) { switch (edgeVersion) {
case V_3_3_0: case V_3_3_0:
return new RuleChainMetadataConstructorV330(queueService); return new RuleChainMetadataConstructorV330();
case V_3_3_3: case V_3_3_3:
return new RuleChainMetadataConstructorV333(queueService);
case V_3_4_0: case V_3_4_0:
default: default:
return new RuleChainMetadataConstructorV340(queueService); return new RuleChainMetadataConstructorV340();
} }
} }
} }

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

@ -27,7 +27,6 @@ import org.thingsboard.server.common.data.rule.NodeConnectionInfo;
import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo; import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo;
import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.server.dao.queue.QueueService;
import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg;
import java.util.ArrayList; import java.util.ArrayList;
@ -43,16 +42,11 @@ public class RuleChainMetadataConstructorV330 extends AbstractRuleChainMetadataC
private static final String RULE_CHAIN_INPUT_NODE = TbRuleChainInputNode.class.getName(); private static final String RULE_CHAIN_INPUT_NODE = TbRuleChainInputNode.class.getName();
private static final String TB_RULE_CHAIN_OUTPUT_NODE = TbRuleChainOutputNode.class.getName(); private static final String TB_RULE_CHAIN_OUTPUT_NODE = TbRuleChainOutputNode.class.getName();
public RuleChainMetadataConstructorV330(QueueService queueService) {
super(queueService);
}
@Override @Override
protected void constructRuleChainMetadataUpdatedMsg(TenantId tenantId, protected void constructRuleChainMetadataUpdatedMsg(TenantId tenantId,
RuleChainMetadataUpdateMsg.Builder builder, RuleChainMetadataUpdateMsg.Builder builder,
RuleChainMetaData ruleChainMetaData) throws JsonProcessingException { RuleChainMetaData ruleChainMetaData) throws JsonProcessingException {
List<RuleNode> supportedNodes = filterNodes(ruleChainMetaData.getNodes()); List<RuleNode> supportedNodes = filterNodes(ruleChainMetaData.getNodes());
supportedNodes = updateQueueIdToQueueNameNodeConfiguration(tenantId, supportedNodes);
NavigableSet<Integer> removedNodeIndexes = getRemovedNodeIndexes(ruleChainMetaData.getNodes(), ruleChainMetaData.getConnections()); NavigableSet<Integer> removedNodeIndexes = getRemovedNodeIndexes(ruleChainMetaData.getNodes(), ruleChainMetaData.getConnections());
List<NodeConnectionInfo> connections = filterConnections(ruleChainMetaData.getNodes(), ruleChainMetaData.getConnections(), removedNodeIndexes); List<NodeConnectionInfo> connections = filterConnections(ruleChainMetaData.getNodes(), ruleChainMetaData.getConnections(), removedNodeIndexes);

50
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV333.java

@ -1,50 +0,0 @@
/**
* 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.edge.rpc.constructor.rule;
import com.fasterxml.jackson.core.JsonProcessingException;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.server.dao.queue.QueueService;
import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg;
import java.util.List;
import java.util.TreeSet;
@Slf4j
public class RuleChainMetadataConstructorV333 extends AbstractRuleChainMetadataConstructor {
public RuleChainMetadataConstructorV333(QueueService queueService) {
super(queueService);
}
@Override
protected void constructRuleChainMetadataUpdatedMsg(TenantId tenantId,
RuleChainMetadataUpdateMsg.Builder builder,
RuleChainMetaData ruleChainMetaData) throws JsonProcessingException {
List<RuleNode> nodes = updateQueueIdToQueueNameNodeConfiguration(tenantId, ruleChainMetaData.getNodes());
builder.addAllNodes(constructNodes(nodes))
.addAllConnections(constructConnections(ruleChainMetaData.getConnections()))
.addAllRuleChainConnections(constructRuleChainConnections(ruleChainMetaData.getRuleChainConnections(), new TreeSet<>()));
if (ruleChainMetaData.getFirstNodeIndex() != null) {
builder.setFirstNodeIndex(ruleChainMetaData.getFirstNodeIndex());
} else {
builder.setFirstNodeIndex(-1);
}
}
}

5
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV340.java

@ -19,7 +19,6 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.dao.queue.QueueService;
import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg;
import java.util.TreeSet; import java.util.TreeSet;
@ -27,10 +26,6 @@ import java.util.TreeSet;
@Slf4j @Slf4j
public class RuleChainMetadataConstructorV340 extends AbstractRuleChainMetadataConstructor { public class RuleChainMetadataConstructorV340 extends AbstractRuleChainMetadataConstructor {
public RuleChainMetadataConstructorV340(QueueService queueService) {
super(queueService);
}
@Override @Override
protected void constructRuleChainMetadataUpdatedMsg(TenantId tenantId, protected void constructRuleChainMetadataUpdatedMsg(TenantId tenantId,
RuleChainMetadataUpdateMsg.Builder builder, RuleChainMetadataUpdateMsg.Builder builder,

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

@ -158,21 +158,21 @@ public class TelemetryEdgeProcessor extends BaseEdgeProcessor {
return metaData; return metaData;
} }
private Pair<QueueId, RuleChainId> getDefaultQueueNameAndRuleChainId(TenantId tenantId, EntityId entityId) { private Pair<String, RuleChainId> getDefaultQueueNameAndRuleChainId(TenantId tenantId, EntityId entityId) {
if (EntityType.DEVICE.equals(entityId.getEntityType())) { if (EntityType.DEVICE.equals(entityId.getEntityType())) {
DeviceProfile deviceProfile = deviceProfileCache.get(tenantId, new DeviceId(entityId.getId())); DeviceProfile deviceProfile = deviceProfileCache.get(tenantId, new DeviceId(entityId.getId()));
RuleChainId ruleChainId; RuleChainId ruleChainId;
QueueId queueId; String queueName;
if (deviceProfile == null) { if (deviceProfile == null) {
log.warn("[{}] Device profile is null!", entityId); log.warn("[{}] Device profile is null!", entityId);
ruleChainId = null; ruleChainId = null;
queueId = null; queueName = null;
} else { } else {
ruleChainId = deviceProfile.getDefaultRuleChainId(); ruleChainId = deviceProfile.getDefaultRuleChainId();
queueId = deviceProfile.getDefaultQueueId(); queueName = deviceProfile.getDefaultQueueName();
} }
return new ImmutablePair<>(queueId, ruleChainId); return new ImmutablePair<>(queueName, ruleChainId);
} else { } else {
return new ImmutablePair<>(null, null); return new ImmutablePair<>(null, null);
} }
@ -183,10 +183,8 @@ public class TelemetryEdgeProcessor extends BaseEdgeProcessor {
for (TransportProtos.TsKvListProto tsKv : msg.getTsKvListList()) { for (TransportProtos.TsKvListProto tsKv : msg.getTsKvListList()) {
JsonObject json = JsonUtils.getJsonObject(tsKv.getKvList()); JsonObject json = JsonUtils.getJsonObject(tsKv.getKvList());
metaData.putValue("ts", tsKv.getTs() + ""); metaData.putValue("ts", tsKv.getTs() + "");
Pair<QueueId, RuleChainId> defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); var defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId);
QueueId queueId = defaultQueueAndRuleChain.getKey(); TbMsg tbMsg = TbMsg.newMsg(defaultQueueAndRuleChain.getKey(), SessionMsgType.POST_TELEMETRY_REQUEST.name(), entityId, customerId, metaData, gson.toJson(json), defaultQueueAndRuleChain.getValue(), null);
RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue();
TbMsg tbMsg = TbMsg.newMsg(queueId, SessionMsgType.POST_TELEMETRY_REQUEST.name(), entityId, customerId, metaData, gson.toJson(json), ruleChainId, null);
tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() {
@Override @Override
public void onSuccess(TbQueueMsgMetadata metadata) { public void onSuccess(TbQueueMsgMetadata metadata) {
@ -206,10 +204,8 @@ public class TelemetryEdgeProcessor extends BaseEdgeProcessor {
private ListenableFuture<Void> processPostAttributes(TenantId tenantId, CustomerId customerId, EntityId entityId, TransportProtos.PostAttributeMsg msg, TbMsgMetaData metaData) { private ListenableFuture<Void> processPostAttributes(TenantId tenantId, CustomerId customerId, EntityId entityId, TransportProtos.PostAttributeMsg msg, TbMsgMetaData metaData) {
SettableFuture<Void> futureToSet = SettableFuture.create(); SettableFuture<Void> futureToSet = SettableFuture.create();
JsonObject json = JsonUtils.getJsonObject(msg.getKvList()); JsonObject json = JsonUtils.getJsonObject(msg.getKvList());
Pair<QueueId, RuleChainId> defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); var defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId);
QueueId queueId = defaultQueueAndRuleChain.getKey(); TbMsg tbMsg = TbMsg.newMsg(defaultQueueAndRuleChain.getKey(), SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), entityId, customerId, metaData, gson.toJson(json), defaultQueueAndRuleChain.getValue(), null);
RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue();
TbMsg tbMsg = TbMsg.newMsg(queueId, SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), entityId, customerId, metaData, gson.toJson(json), ruleChainId, null);
tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() {
@Override @Override
public void onSuccess(TbQueueMsgMetadata metadata) { public void onSuccess(TbQueueMsgMetadata metadata) {
@ -233,10 +229,8 @@ public class TelemetryEdgeProcessor extends BaseEdgeProcessor {
Futures.addCallback(future, new FutureCallback<>() { Futures.addCallback(future, new FutureCallback<>() {
@Override @Override
public void onSuccess(@Nullable List<String> keys) { public void onSuccess(@Nullable List<String> keys) {
Pair<QueueId, RuleChainId> defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); var defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId);
QueueId queueId = defaultQueueAndRuleChain.getKey(); TbMsg tbMsg = TbMsg.newMsg(defaultQueueAndRuleChain.getKey(), DataConstants.ATTRIBUTES_UPDATED, entityId, customerId, metaData, gson.toJson(json), defaultQueueAndRuleChain.getValue(), null);
RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue();
TbMsg tbMsg = TbMsg.newMsg(queueId, DataConstants.ATTRIBUTES_UPDATED, entityId, customerId, metaData, gson.toJson(json), ruleChainId, null);
tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() {
@Override @Override
public void onSuccess(TbQueueMsgMetadata metadata) { public void onSuccess(TbQueueMsgMetadata metadata) {

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

@ -72,6 +72,7 @@ import org.thingsboard.server.gen.edge.v1.RelationRequestMsg;
import org.thingsboard.server.gen.edge.v1.RuleChainMetadataRequestMsg; import org.thingsboard.server.gen.edge.v1.RuleChainMetadataRequestMsg;
import org.thingsboard.server.gen.edge.v1.UserCredentialsRequestMsg; import org.thingsboard.server.gen.edge.v1.UserCredentialsRequestMsg;
import org.thingsboard.server.gen.edge.v1.WidgetBundleTypesRequestMsg; import org.thingsboard.server.gen.edge.v1.WidgetBundleTypesRequestMsg;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.entitiy.entityview.TbEntityViewService; import org.thingsboard.server.service.entitiy.entityview.TbEntityViewService;
import org.thingsboard.server.service.executors.DbCallbackExecutorService; import org.thingsboard.server.service.executors.DbCallbackExecutorService;
import org.thingsboard.server.service.state.DefaultDeviceStateService; import org.thingsboard.server.service.state.DefaultDeviceStateService;
@ -83,6 +84,7 @@ import java.util.Map;
import java.util.UUID; import java.util.UUID;
@Service @Service
@TbCoreComponent
@Slf4j @Slf4j
public class DefaultEdgeRequestsService implements EdgeRequestsService { public class DefaultEdgeRequestsService implements EdgeRequestsService {

20
application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/DefaultTbDashboardService.java

@ -183,11 +183,17 @@ public class DefaultTbDashboardService extends AbstractTbEntityService implement
TenantId tenantId = dashboard.getTenantId(); TenantId tenantId = dashboard.getTenantId();
DashboardId dashboardId = dashboard.getId(); DashboardId dashboardId = dashboard.getId();
try { try {
if (customerIds.isEmpty()) { Set<CustomerId> addedCustomerIds = new HashSet<>();
for (CustomerId customerId : customerIds) {
if (!dashboard.isAssignedToCustomer(customerId)) {
addedCustomerIds.add(customerId);
}
}
if (addedCustomerIds.isEmpty()) {
return dashboard; return dashboard;
} else { } else {
Dashboard savedDashboard = null; Dashboard savedDashboard = null;
for (CustomerId customerId : customerIds) { for (CustomerId customerId : addedCustomerIds) {
savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(tenantId, dashboardId, customerId)); savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(tenantId, dashboardId, customerId));
ShortCustomerInfo customerInfo = savedDashboard.getAssignedCustomerInfo(customerId); ShortCustomerInfo customerInfo = savedDashboard.getAssignedCustomerInfo(customerId);
notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, dashboardId, customerId, savedDashboard, notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, dashboardId, customerId, savedDashboard,
@ -207,11 +213,17 @@ public class DefaultTbDashboardService extends AbstractTbEntityService implement
TenantId tenantId = dashboard.getTenantId(); TenantId tenantId = dashboard.getTenantId();
DashboardId dashboardId = dashboard.getId(); DashboardId dashboardId = dashboard.getId();
try { try {
if (customerIds.isEmpty()) { Set<CustomerId> removedCustomerIds = new HashSet<>();
for (CustomerId customerId : customerIds) {
if (dashboard.isAssignedToCustomer(customerId)) {
removedCustomerIds.add(customerId);
}
}
if (removedCustomerIds.isEmpty()) {
return dashboard; return dashboard;
} else { } else {
Dashboard savedDashboard = null; Dashboard savedDashboard = null;
for (CustomerId customerId : customerIds) { for (CustomerId customerId : removedCustomerIds) {
ShortCustomerInfo customerInfo = dashboard.getAssignedCustomerInfo(customerId); ShortCustomerInfo customerInfo = dashboard.getAssignedCustomerInfo(customerId);
savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(tenantId, dashboardId, customerId)); savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(tenantId, dashboardId, customerId));
notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, dashboardId, customerId, savedDashboard, notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, dashboardId, customerId, savedDashboard,

53
application/src/main/java/org/thingsboard/server/service/entitiy/queue/DefaultTbQueueService.java

@ -48,13 +48,11 @@ import java.util.stream.Collectors;
@TbCoreComponent @TbCoreComponent
@AllArgsConstructor @AllArgsConstructor
public class DefaultTbQueueService extends AbstractTbEntityService implements TbQueueService { public class DefaultTbQueueService extends AbstractTbEntityService implements TbQueueService {
private static final String MAIN = "Main";
private static final long DELETE_DELAY = 30; private static final long DELETE_DELAY = 30;
private final QueueService queueService; private final QueueService queueService;
private final TbClusterService tbClusterService; private final TbClusterService tbClusterService;
private final TbQueueAdmin tbQueueAdmin; private final TbQueueAdmin tbQueueAdmin;
private final DeviceProfileService deviceProfileService;
private final SchedulerComponent scheduler; private final SchedulerComponent scheduler;
@Override @Override
@ -205,35 +203,7 @@ public class DefaultTbQueueService extends AbstractTbEntityService implements Tb
} }
tenantIds.forEach(tenantId -> { tenantIds.forEach(tenantId -> {
Map<QueueId, List<DeviceProfile>> deviceProfileQueues; toCreate.forEach(key -> saveQueue(new Queue(tenantId, newQueues.get(key))));
if (oldTenantProfile != null && !newTenantProfile.getId().equals(oldTenantProfile.getId()) || !toRemove.isEmpty()) {
List<DeviceProfile> deviceProfiles = deviceProfileService.findDeviceProfiles(tenantId, new PageLink(Integer.MAX_VALUE)).getData();
deviceProfileQueues = deviceProfiles.stream()
.filter(dp -> dp.getDefaultQueueId() != null)
.collect(Collectors.groupingBy(DeviceProfile::getDefaultQueueId));
} else {
deviceProfileQueues = Collections.emptyMap();
}
Map<String, QueueId> createdQueues = toCreate.stream()
.map(key -> saveQueue(new Queue(tenantId, newQueues.get(key))))
.collect(Collectors.toMap(Queue::getName, Queue::getId));
// assigning created queues to device profiles instead of system queues
if (oldTenantProfile != null && !oldTenantProfile.isIsolatedTbRuleEngine()) {
deviceProfileQueues.forEach((queueId, list) -> {
Queue queue = queueService.findQueueById(TenantId.SYS_TENANT_ID, queueId);
QueueId queueIdToAssign = createdQueues.get(queue.getName());
if (queueIdToAssign == null) {
queueIdToAssign = createdQueues.get(MAIN);
}
for (DeviceProfile deviceProfile : list) {
deviceProfile.setDefaultQueueId(queueIdToAssign);
saveDeviceProfile(deviceProfile);
}
});
}
toUpdate.forEach(key -> { toUpdate.forEach(key -> {
Queue queueToUpdate = new Queue(tenantId, newQueues.get(key)); Queue queueToUpdate = new Queue(tenantId, newQueues.get(key));
@ -241,9 +211,7 @@ public class DefaultTbQueueService extends AbstractTbEntityService implements Tb
queueToUpdate.setId(foundQueue.getId()); queueToUpdate.setId(foundQueue.getId());
queueToUpdate.setCreatedTime(foundQueue.getCreatedTime()); queueToUpdate.setCreatedTime(foundQueue.getCreatedTime());
if (queueToUpdate.equals(foundQueue)) { if (!queueToUpdate.equals(foundQueue)) {
//Queue not changed
} else {
saveQueue(queueToUpdate); saveQueue(queueToUpdate);
} }
}); });
@ -251,26 +219,9 @@ public class DefaultTbQueueService extends AbstractTbEntityService implements Tb
toRemove.forEach(q -> { toRemove.forEach(q -> {
Queue queue = queueService.findQueueByTenantIdAndNameInternal(tenantId, q); Queue queue = queueService.findQueueByTenantIdAndNameInternal(tenantId, q);
QueueId queueIdForRemove = queue.getId(); QueueId queueIdForRemove = queue.getId();
if (deviceProfileQueues.containsKey(queueIdForRemove)) {
Queue foundQueue = queueService.findQueueByTenantIdAndName(tenantId, q);
if (foundQueue == null || queue.equals(foundQueue)) {
foundQueue = queueService.findQueueByTenantIdAndName(tenantId, MAIN);
}
QueueId newQueueId = foundQueue.getId();
deviceProfileQueues.get(queueIdForRemove).stream()
.peek(dp -> dp.setDefaultQueueId(newQueueId))
.forEach(this::saveDeviceProfile);
}
deleteQueue(tenantId, queueIdForRemove); deleteQueue(tenantId, queueIdForRemove);
}); });
}); });
} }
//TODO: remove after implementing TbDeviceProfileService
private void saveDeviceProfile(DeviceProfile deviceProfile) {
DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile);
tbClusterService.onDeviceProfileChange(savedDeviceProfile, null);
tbClusterService.broadcastEntityStateChangeEvent(deviceProfile.getTenantId(), savedDeviceProfile.getId(), ComponentLifecycleEvent.UPDATED);
}
} }

2
application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java

@ -72,7 +72,7 @@ public class DefaultUserService extends AbstractTbEntityService implements TbUse
savedUser, user, actionType, true, null); savedUser, user, actionType, true, null);
return savedUser; return savedUser;
} catch (Exception e) { } catch (Exception e) {
notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.USER), user, actionType, user, e); notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.USER), tbUser, actionType, user, e);
throw e; throw e;
} }
} }

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

@ -195,22 +195,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
public void createDefaultTenantProfiles() throws Exception { public void createDefaultTenantProfiles() throws Exception {
tenantProfileService.findOrCreateDefaultTenantProfile(TenantId.SYS_TENANT_ID); tenantProfileService.findOrCreateDefaultTenantProfile(TenantId.SYS_TENANT_ID);
TenantProfileData tenantProfileData = new TenantProfileData();
tenantProfileData.setConfiguration(new DefaultTenantProfileConfiguration());
TenantProfile isolatedTbCoreProfile = new TenantProfile();
isolatedTbCoreProfile.setDefault(false);
isolatedTbCoreProfile.setName("Isolated TB Core");
isolatedTbCoreProfile.setDescription("Isolated TB Core tenant profile");
isolatedTbCoreProfile.setIsolatedTbCore(true);
isolatedTbCoreProfile.setIsolatedTbRuleEngine(false);
isolatedTbCoreProfile.setProfileData(tenantProfileData);
try {
tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, isolatedTbCoreProfile);
} catch (DataValidationException e) {
log.warn(e.getMessage());
}
TenantProfileData isolatedRuleEngineTenantProfileData = new TenantProfileData(); TenantProfileData isolatedRuleEngineTenantProfileData = new TenantProfileData();
isolatedRuleEngineTenantProfileData.setConfiguration(new DefaultTenantProfileConfiguration()); isolatedRuleEngineTenantProfileData.setConfiguration(new DefaultTenantProfileConfiguration());
@ -239,7 +223,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
isolatedTbRuleEngineProfile.setDefault(false); isolatedTbRuleEngineProfile.setDefault(false);
isolatedTbRuleEngineProfile.setName("Isolated TB Rule Engine"); isolatedTbRuleEngineProfile.setName("Isolated TB Rule Engine");
isolatedTbRuleEngineProfile.setDescription("Isolated TB Rule Engine tenant profile"); isolatedTbRuleEngineProfile.setDescription("Isolated TB Rule Engine tenant profile");
isolatedTbRuleEngineProfile.setIsolatedTbCore(false);
isolatedTbRuleEngineProfile.setIsolatedTbRuleEngine(true); isolatedTbRuleEngineProfile.setIsolatedTbRuleEngine(true);
isolatedTbRuleEngineProfile.setProfileData(isolatedRuleEngineTenantProfileData); isolatedTbRuleEngineProfile.setProfileData(isolatedRuleEngineTenantProfileData);
@ -248,20 +231,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
} catch (DataValidationException e) { } catch (DataValidationException e) {
log.warn(e.getMessage()); log.warn(e.getMessage());
} }
TenantProfile isolatedTbCoreAndTbRuleEngineProfile = new TenantProfile();
isolatedTbCoreAndTbRuleEngineProfile.setDefault(false);
isolatedTbCoreAndTbRuleEngineProfile.setName("Isolated TB Core and TB Rule Engine");
isolatedTbCoreAndTbRuleEngineProfile.setDescription("Isolated TB Core and TB Rule Engine tenant profile");
isolatedTbCoreAndTbRuleEngineProfile.setIsolatedTbCore(true);
isolatedTbCoreAndTbRuleEngineProfile.setIsolatedTbRuleEngine(true);
isolatedTbCoreAndTbRuleEngineProfile.setProfileData(isolatedRuleEngineTenantProfileData);
try {
tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, isolatedTbCoreAndTbRuleEngineProfile);
} catch (DataValidationException e) {
log.warn(e.getMessage());
}
} }
@Override @Override

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

@ -589,10 +589,6 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
} catch (Exception e) { } catch (Exception e) {
} }
log.info("Updating device profiles...");
schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.3.4", "schema_update_device_profile.sql");
loadSql(schemaUpdateFile, conn);
log.info("Updating schema settings..."); log.info("Updating schema settings...");
conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3004000;"); conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3004000;");
log.info("Schema updated."); log.info("Schema updated.");

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

@ -156,9 +156,8 @@ public class DefaultDataUpdateService implements DataUpdateService {
break; break;
case "3.3.4": case "3.3.4":
log.info("Updating data from version 3.3.4 to 3.4.0 ..."); log.info("Updating data from version 3.3.4 to 3.4.0 ...");
rateLimitsUpdater.updateEntities();
tenantsProfileQueueConfigurationUpdater.updateEntities(); tenantsProfileQueueConfigurationUpdater.updateEntities();
checkPointRuleNodesUpdater.updateEntities(); rateLimitsUpdater.updateEntities();
break; break;
default: default:
throw new RuntimeException("Unable to update data, unsupported fromVersion: " + fromVersion); throw new RuntimeException("Unable to update data, unsupported fromVersion: " + fromVersion);
@ -629,47 +628,4 @@ public class DefaultDataUpdateService implements DataUpdateService {
return mainQueueConfiguration; return mainQueueConfiguration;
} }
private final PaginatedUpdater<String, RuleNode> checkPointRuleNodesUpdater =
new PaginatedUpdater<>() {
@Override
protected String getName() {
return "Checkpoint rule nodes updater";
}
@Override
protected boolean forceReportTotal() {
return true;
}
@Override
protected PageData<RuleNode> findEntities(String id, PageLink pageLink) {
return ruleChainService.findAllRuleNodesByType("org.thingsboard.rule.engine.flow.TbCheckpointNode", pageLink);
}
@Override
protected void updateEntity(RuleNode ruleNode) {
updateCheckPointRuleNodeConfiguration(ruleNode);
}
};
private void updateCheckPointRuleNodeConfiguration(RuleNode node) {
try {
ObjectNode configuration = (ObjectNode) node.getConfiguration();
JsonNode queueNameNode = configuration.remove("queueName");
if (queueNameNode != null) {
RuleChain ruleChain = this.ruleChainService.findRuleChainById(TenantId.SYS_TENANT_ID, node.getRuleChainId());
TenantId tenantId = ruleChain.getTenantId();
Map<String, QueueId> queues =
queueService.findQueuesByTenantId(tenantId).stream().collect(Collectors.toMap(Queue::getName, Queue::getId));
String queueName = queueNameNode.asText();
QueueId queueId = queues.get(queueName);
configuration.put("queueId", queueId != null ? queueId.toString() : "");
ruleChainService.saveRuleNode(tenantId, node);
}
} catch (Exception e) {
log.error("Failed to update checkpoint rule node configuration name=["+node.getName()+"], id=["+ node.getId().getId() +"]", e);
}
}
} }

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

@ -20,7 +20,9 @@ import freemarker.template.Configuration;
import freemarker.template.Template; import freemarker.template.Template;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; 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.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.MessageSource; import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.core.NestedRuntimeException; import org.springframework.core.NestedRuntimeException;
@ -47,13 +49,17 @@ import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
import org.thingsboard.server.service.apiusage.TbApiUsageStateService; import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.mail.MessagingException; import javax.annotation.PreDestroy;
import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMessage;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.util.HashMap; import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@Service @Service
@Slf4j @Slf4j
@ -70,6 +76,8 @@ public class DefaultMailService implements MailService {
private final AdminSettingsService adminSettingsService; private final AdminSettingsService adminSettingsService;
private final TbApiUsageClient apiUsageClient; private final TbApiUsageClient apiUsageClient;
private static final long DEFAULT_TIMEOUT = 10_000;
@Lazy @Lazy
@Autowired @Autowired
private TbApiUsageStateService apiUsageStateService; private TbApiUsageStateService apiUsageStateService;
@ -77,10 +85,15 @@ public class DefaultMailService implements MailService {
@Autowired @Autowired
private MailExecutorService mailExecutorService; private MailExecutorService mailExecutorService;
@Autowired
private PasswordResetExecutorService passwordResetExecutorService;
private JavaMailSenderImpl mailSender; private JavaMailSenderImpl mailSender;
private String mailFrom; private String mailFrom;
private long timeout;
public DefaultMailService(MessageSource messages, Configuration freemarkerConfig, AdminSettingsService adminSettingsService, TbApiUsageClient apiUsageClient) { public DefaultMailService(MessageSource messages, Configuration freemarkerConfig, AdminSettingsService adminSettingsService, TbApiUsageClient apiUsageClient) {
this.messages = messages; this.messages = messages;
this.freemarkerConfig = freemarkerConfig; this.freemarkerConfig = freemarkerConfig;
@ -100,6 +113,7 @@ public class DefaultMailService implements MailService {
JsonNode jsonConfig = settings.getJsonValue(); JsonNode jsonConfig = settings.getJsonValue();
mailSender = createMailSender(jsonConfig); mailSender = createMailSender(jsonConfig);
mailFrom = jsonConfig.get("mailFrom").asText(); mailFrom = jsonConfig.get("mailFrom").asText();
timeout = jsonConfig.get("timeout").asLong(DEFAULT_TIMEOUT);
} else { } else {
throw new IncorrectParameterException("Failed to update mail configuration. Settings not found!"); throw new IncorrectParameterException("Failed to update mail configuration. Settings not found!");
} }
@ -166,7 +180,7 @@ public class DefaultMailService implements MailService {
@Override @Override
public void sendEmail(TenantId tenantId, String email, String subject, String message) throws ThingsboardException { public void sendEmail(TenantId tenantId, String email, String subject, String message) throws ThingsboardException {
sendMail(mailSender, mailFrom, email, subject, message); sendMail(mailSender, mailFrom, email, subject, message, timeout);
} }
@Override @Override
@ -174,13 +188,14 @@ public class DefaultMailService implements MailService {
JavaMailSenderImpl testMailSender = createMailSender(jsonConfig); JavaMailSenderImpl testMailSender = createMailSender(jsonConfig);
String mailFrom = jsonConfig.get("mailFrom").asText(); String mailFrom = jsonConfig.get("mailFrom").asText();
String subject = messages.getMessage("test.message.subject", null, Locale.US); String subject = messages.getMessage("test.message.subject", null, Locale.US);
long timeout = jsonConfig.get("timeout").asLong(DEFAULT_TIMEOUT);
Map<String, Object> model = new HashMap<>(); Map<String, Object> model = new HashMap<>();
model.put(TARGET_EMAIL, email); model.put(TARGET_EMAIL, email);
String message = mergeTemplateIntoString("test.ftl", model); String message = mergeTemplateIntoString("test.ftl", model);
sendMail(testMailSender, mailFrom, email, subject, message); sendMail(testMailSender, mailFrom, email, subject, message, timeout);
} }
@Override @Override
@ -194,7 +209,7 @@ public class DefaultMailService implements MailService {
String message = mergeTemplateIntoString("activation.ftl", model); String message = mergeTemplateIntoString("activation.ftl", model);
sendMail(mailSender, mailFrom, email, subject, message); sendMail(mailSender, mailFrom, email, subject, message, timeout);
} }
@Override @Override
@ -208,7 +223,7 @@ public class DefaultMailService implements MailService {
String message = mergeTemplateIntoString("account.activated.ftl", model); String message = mergeTemplateIntoString("account.activated.ftl", model);
sendMail(mailSender, mailFrom, email, subject, message); sendMail(mailSender, mailFrom, email, subject, message, timeout);
} }
@Override @Override
@ -222,12 +237,12 @@ public class DefaultMailService implements MailService {
String message = mergeTemplateIntoString("reset.password.ftl", model); String message = mergeTemplateIntoString("reset.password.ftl", model);
sendMail(mailSender, mailFrom, email, subject, message); sendMail(mailSender, mailFrom, email, subject, message, timeout);
} }
@Override @Override
public void sendResetPasswordEmailAsync(String passwordResetLink, String email) { public void sendResetPasswordEmailAsync(String passwordResetLink, String email) {
mailExecutorService.execute(() -> { passwordResetExecutorService.execute(() -> {
try { try {
this.sendResetPasswordEmail(passwordResetLink, email); this.sendResetPasswordEmail(passwordResetLink, email);
} catch (ThingsboardException e) { } catch (ThingsboardException e) {
@ -247,20 +262,20 @@ public class DefaultMailService implements MailService {
String message = mergeTemplateIntoString("password.was.reset.ftl", model); String message = mergeTemplateIntoString("password.was.reset.ftl", model);
sendMail(mailSender, mailFrom, email, subject, message); sendMail(mailSender, mailFrom, email, subject, message, timeout);
} }
@Override @Override
public void send(TenantId tenantId, CustomerId customerId, TbEmail tbEmail) throws ThingsboardException { public void send(TenantId tenantId, CustomerId customerId, TbEmail tbEmail) throws ThingsboardException {
sendMail(tenantId, customerId, tbEmail, this.mailSender); sendMail(tenantId, customerId, tbEmail, this.mailSender, timeout);
} }
@Override @Override
public void send(TenantId tenantId, CustomerId customerId, TbEmail tbEmail, JavaMailSender javaMailSender) throws ThingsboardException { public void send(TenantId tenantId, CustomerId customerId, TbEmail tbEmail, JavaMailSender javaMailSender, long timeout) throws ThingsboardException {
sendMail(tenantId, customerId, tbEmail, javaMailSender); sendMail(tenantId, customerId, tbEmail, javaMailSender, timeout);
} }
private void sendMail(TenantId tenantId, CustomerId customerId, TbEmail tbEmail, JavaMailSender javaMailSender) throws ThingsboardException { private void sendMail(TenantId tenantId, CustomerId customerId, TbEmail tbEmail, JavaMailSender javaMailSender, long timeout) throws ThingsboardException {
if (apiUsageStateService.getApiUsageState(tenantId).isEmailSendEnabled()) { if (apiUsageStateService.getApiUsageState(tenantId).isEmailSendEnabled()) {
try { try {
MimeMessage mailMsg = javaMailSender.createMimeMessage(); MimeMessage mailMsg = javaMailSender.createMimeMessage();
@ -287,7 +302,7 @@ public class DefaultMailService implements MailService {
helper.addInline(imgId, iss, contentType); helper.addInline(imgId, iss, contentType);
} }
} }
javaMailSender.send(helper.getMimeMessage()); sendMailWithTimeout(javaMailSender, helper.getMimeMessage(), timeout);
apiUsageClient.report(tenantId, customerId, ApiUsageRecordKey.EMAIL_EXEC_COUNT, 1); apiUsageClient.report(tenantId, customerId, ApiUsageRecordKey.EMAIL_EXEC_COUNT, 1);
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
@ -308,7 +323,7 @@ public class DefaultMailService implements MailService {
String message = mergeTemplateIntoString("account.lockout.ftl", model); String message = mergeTemplateIntoString("account.lockout.ftl", model);
sendMail(mailSender, mailFrom, email, subject, message); sendMail(mailSender, mailFrom, email, subject, message, timeout);
} }
@Override @Override
@ -320,7 +335,7 @@ public class DefaultMailService implements MailService {
"expirationTimeSeconds", expirationTimeSeconds "expirationTimeSeconds", expirationTimeSeconds
)); ));
sendMail(mailSender, mailFrom, email, subject, message); sendMail(mailSender, mailFrom, email, subject, message, timeout);
} }
@Override @Override
@ -347,7 +362,7 @@ public class DefaultMailService implements MailService {
message = mergeTemplateIntoString("state.disabled.ftl", model); message = mergeTemplateIntoString("state.disabled.ftl", model);
break; break;
} }
sendMail(mailSender, mailFrom, email, subject, message); sendMail(mailSender, mailFrom, email, subject, message, timeout);
} }
@Override @Override
@ -447,9 +462,8 @@ public class DefaultMailService implements MailService {
} }
} }
private void sendMail(JavaMailSenderImpl mailSender, private void sendMail(JavaMailSenderImpl mailSender, String mailFrom, String email,
String mailFrom, String email, String subject, String message, long timeout) throws ThingsboardException {
String subject, String message) throws ThingsboardException {
try { try {
MimeMessage mimeMsg = mailSender.createMimeMessage(); MimeMessage mimeMsg = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMsg, UTF_8); MimeMessageHelper helper = new MimeMessageHelper(mimeMsg, UTF_8);
@ -457,12 +471,24 @@ public class DefaultMailService implements MailService {
helper.setTo(email); helper.setTo(email);
helper.setSubject(subject); helper.setSubject(subject);
helper.setText(message, true); helper.setText(message, true);
mailSender.send(helper.getMimeMessage());
sendMailWithTimeout(mailSender, helper.getMimeMessage(), timeout);
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }
} }
private void sendMailWithTimeout(JavaMailSender mailSender, MimeMessage msg, long timeout) {
try {
mailExecutorService.submit(() -> mailSender.send(msg)).get(timeout, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
log.debug("Error during mail submission", e);
throw new RuntimeException("Timeout!");
} catch (Exception e) {
throw new RuntimeException(ExceptionUtils.getRootCause(e));
}
}
private String mergeTemplateIntoString(String templateLocation, private String mergeTemplateIntoString(String templateLocation,
Map<String, Object> model) throws ThingsboardException { Map<String, Object> model) throws ThingsboardException {
try { try {

27
msa/js-executor/api/httpServer.js → application/src/main/java/org/thingsboard/server/service/mail/PasswordResetExecutorService.java

@ -1,4 +1,4 @@
/* /**
* Copyright © 2016-2022 The Thingsboard Authors * Copyright © 2016-2022 The Thingsboard Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -13,18 +13,21 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
const config = require('config'), package org.thingsboard.server.service.mail;
logger = require('../config/logger')._logger('httpServer'),
express = require('express');
const httpPort = Number(config.get('http_port')); import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.thingsboard.common.util.AbstractListeningExecutor;
const app = express(); @Component
public class PasswordResetExecutorService extends AbstractListeningExecutor {
app.get('/livenessProbe', async (req, res) => { @Value("${actors.rule.mail_password_reset_thread_pool_size:10}")
const date = new Date(); private int mailExecutorThreadPoolSize;
const message = { now: date.toISOString() };
res.send(message);
})
app.listen(httpPort, () => logger.info(`Started http endpoint on port ${httpPort}. Please, use /livenessProbe !`)) @Override
protected int getThreadPollSize() {
return mailExecutorThreadPoolSize;
}
}

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

@ -181,7 +181,7 @@ public class DefaultTbClusterService implements TbClusterService {
tbMsg = transformMsg(tbMsg, deviceProfileCache.get(tenantId, new DeviceProfileId(entityId.getId()))); tbMsg = transformMsg(tbMsg, deviceProfileCache.get(tenantId, new DeviceProfileId(entityId.getId())));
} }
} }
TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, tbMsg.getQueueId(), tenantId, entityId); TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, tbMsg.getQueueName(), tenantId, entityId);
log.trace("PUSHING msg: {} to:{}", tbMsg, tpi); log.trace("PUSHING msg: {} to:{}", tbMsg, tpi);
ToRuleEngineMsg msg = ToRuleEngineMsg.newBuilder() ToRuleEngineMsg msg = ToRuleEngineMsg.newBuilder()
.setTenantIdMSB(tenantId.getId().getMostSignificantBits()) .setTenantIdMSB(tenantId.getId().getMostSignificantBits())
@ -194,16 +194,16 @@ public class DefaultTbClusterService implements TbClusterService {
private TbMsg transformMsg(TbMsg tbMsg, DeviceProfile deviceProfile) { private TbMsg transformMsg(TbMsg tbMsg, DeviceProfile deviceProfile) {
if (deviceProfile != null) { if (deviceProfile != null) {
RuleChainId targetRuleChainId = deviceProfile.getDefaultRuleChainId(); RuleChainId targetRuleChainId = deviceProfile.getDefaultRuleChainId();
QueueId targetQueueId = deviceProfile.getDefaultQueueId(); String targetQueueName = deviceProfile.getDefaultQueueName();
boolean isRuleChainTransform = targetRuleChainId != null && !targetRuleChainId.equals(tbMsg.getRuleChainId()); boolean isRuleChainTransform = targetRuleChainId != null && !targetRuleChainId.equals(tbMsg.getRuleChainId());
boolean isQueueTransform = targetQueueId != null && !targetQueueId.equals(tbMsg.getQueueId()); boolean isQueueTransform = targetQueueName != null && !targetQueueName.equals(tbMsg.getQueueName());
if (isRuleChainTransform && isQueueTransform) { if (isRuleChainTransform && isQueueTransform) {
tbMsg = TbMsg.transformMsg(tbMsg, targetRuleChainId, targetQueueId); tbMsg = TbMsg.transformMsg(tbMsg, targetRuleChainId, targetQueueName);
} else if (isRuleChainTransform) { } else if (isRuleChainTransform) {
tbMsg = TbMsg.transformMsg(tbMsg, targetRuleChainId); tbMsg = TbMsg.transformMsg(tbMsg, targetRuleChainId);
} else if (isQueueTransform) { } else if (isQueueTransform) {
tbMsg = TbMsg.transformMsg(tbMsg, targetQueueId); tbMsg = TbMsg.transformMsg(tbMsg, targetQueueName);
} }
} }
return tbMsg; return tbMsg;

10
application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java

@ -274,7 +274,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
final boolean timeout = !ctx.await(configuration.getPackProcessingTimeout(), TimeUnit.MILLISECONDS); final boolean timeout = !ctx.await(configuration.getPackProcessingTimeout(), TimeUnit.MILLISECONDS);
TbRuleEngineProcessingResult result = new TbRuleEngineProcessingResult(configuration.getId(), timeout, ctx); TbRuleEngineProcessingResult result = new TbRuleEngineProcessingResult(configuration.getName(), timeout, ctx);
if (timeout) { if (timeout) {
printFirstOrAll(configuration, ctx, ctx.getPendingMap(), "Timeout"); printFirstOrAll(configuration, ctx, ctx.getPendingMap(), "Timeout");
} }
@ -339,7 +339,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
new TbMsgPackCallback(id, tenantId, ctx); new TbMsgPackCallback(id, tenantId, ctx);
try { try {
if (toRuleEngineMsg.getTbMsg() != null && !toRuleEngineMsg.getTbMsg().isEmpty()) { if (toRuleEngineMsg.getTbMsg() != null && !toRuleEngineMsg.getTbMsg().isEmpty()) {
forwardToRuleEngineActor(configuration.getId(), tenantId, toRuleEngineMsg, callback); forwardToRuleEngineActor(configuration.getName(), tenantId, toRuleEngineMsg, callback);
} else { } else {
callback.onSuccess(); callback.onSuccess();
} }
@ -353,7 +353,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
log.info("{} to process [{}] messages", prefix, map.size()); log.info("{} to process [{}] messages", prefix, map.size());
for (Map.Entry<UUID, TbProtoQueueMsg<ToRuleEngineMsg>> pending : map.entrySet()) { for (Map.Entry<UUID, TbProtoQueueMsg<ToRuleEngineMsg>> pending : map.entrySet()) {
ToRuleEngineMsg tmp = pending.getValue().getValue(); ToRuleEngineMsg tmp = pending.getValue().getValue();
TbMsg tmpMsg = TbMsg.fromBytes(configuration.getId(), tmp.getTbMsg().toByteArray(), TbMsgCallback.EMPTY); TbMsg tmpMsg = TbMsg.fromBytes(configuration.getName(), tmp.getTbMsg().toByteArray(), TbMsgCallback.EMPTY);
RuleNodeInfo ruleNodeInfo = ctx.getLastVisitedRuleNode(pending.getKey()); RuleNodeInfo ruleNodeInfo = ctx.getLastVisitedRuleNode(pending.getKey());
if (printAll) { if (printAll) {
log.trace("[{}] {} to process message: {}, Last Rule Node: {}", TenantId.fromUUID(new UUID(tmp.getTenantIdMSB(), tmp.getTenantIdLSB())), prefix, tmpMsg, ruleNodeInfo); log.trace("[{}] {} to process message: {}, Last Rule Node: {}", TenantId.fromUUID(new UUID(tmp.getTenantIdMSB(), tmp.getTenantIdLSB())), prefix, tmpMsg, ruleNodeInfo);
@ -461,8 +461,8 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
partitionService.removeQueue(queueDeleteMsg); partitionService.removeQueue(queueDeleteMsg);
} }
private void forwardToRuleEngineActor(QueueId queueId, TenantId tenantId, ToRuleEngineMsg toRuleEngineMsg, TbMsgCallback callback) { private void forwardToRuleEngineActor(String queueName, TenantId tenantId, ToRuleEngineMsg toRuleEngineMsg, TbMsgCallback callback) {
TbMsg tbMsg = TbMsg.fromBytes(queueId, toRuleEngineMsg.getTbMsg().toByteArray(), callback); TbMsg tbMsg = TbMsg.fromBytes(queueName, toRuleEngineMsg.getTbMsg().toByteArray(), callback);
QueueToRuleEngineMsg msg; QueueToRuleEngineMsg msg;
ProtocolStringList relationTypesList = toRuleEngineMsg.getRelationTypesList(); ProtocolStringList relationTypesList = toRuleEngineMsg.getRelationTypesList();
Set<String> relationTypes = null; Set<String> relationTypes = null;

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

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

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

@ -29,7 +29,7 @@ import java.util.concurrent.ConcurrentMap;
public class TbRuleEngineProcessingResult { public class TbRuleEngineProcessingResult {
@Getter @Getter
private final QueueId queueId; private final String queueName;
@Getter @Getter
private final boolean success; private final boolean success;
@Getter @Getter
@ -37,8 +37,8 @@ public class TbRuleEngineProcessingResult {
@Getter @Getter
private final TbMsgPackProcessingContext ctx; private final TbMsgPackProcessingContext ctx;
public TbRuleEngineProcessingResult(QueueId queueId, boolean timeout, TbMsgPackProcessingContext ctx) { public TbRuleEngineProcessingResult(String queueName, boolean timeout, TbMsgPackProcessingContext ctx) {
this.queueId = queueId; this.queueName = queueName;
this.timeout = timeout; this.timeout = timeout;
this.ctx = ctx; this.ctx = ctx;
this.success = !timeout && ctx.getPendingMap().isEmpty() && ctx.getFailedMap().isEmpty(); this.success = !timeout && ctx.getPendingMap().isEmpty() && ctx.getFailedMap().isEmpty();

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

@ -125,7 +125,7 @@ public class TbRuleEngineProcessingStrategyFactory {
} }
log.debug("[{}] Going to reprocess {} messages", queueName, toReprocess.size()); log.debug("[{}] Going to reprocess {} messages", queueName, toReprocess.size());
if (log.isTraceEnabled()) { if (log.isTraceEnabled()) {
toReprocess.forEach((id, msg) -> log.trace("Going to reprocess [{}]: {}", id, TbMsg.fromBytes(result.getQueueId(), msg.getValue().getTbMsg().toByteArray(), TbMsgCallback.EMPTY))); toReprocess.forEach((id, msg) -> log.trace("Going to reprocess [{}]: {}", id, TbMsg.fromBytes(result.getQueueName(), msg.getValue().getTbMsg().toByteArray(), TbMsgCallback.EMPTY)));
} }
if (pauseBetweenRetries > 0) { if (pauseBetweenRetries > 0) {
try { try {
@ -164,10 +164,10 @@ public class TbRuleEngineProcessingStrategyFactory {
log.debug("[{}] Reprocessing skipped for {} failed and {} timeout messages", queueName, result.getFailedMap().size(), result.getPendingMap().size()); log.debug("[{}] Reprocessing skipped for {} failed and {} timeout messages", queueName, result.getFailedMap().size(), result.getPendingMap().size());
} }
if (log.isTraceEnabled()) { if (log.isTraceEnabled()) {
result.getFailedMap().forEach((id, msg) -> log.trace("Failed messages [{}]: {}", id, TbMsg.fromBytes(result.getQueueId(), msg.getValue().getTbMsg().toByteArray(), TbMsgCallback.EMPTY))); result.getFailedMap().forEach((id, msg) -> log.trace("Failed messages [{}]: {}", id, TbMsg.fromBytes(result.getQueueName(), msg.getValue().getTbMsg().toByteArray(), TbMsgCallback.EMPTY)));
} }
if (log.isTraceEnabled()) { if (log.isTraceEnabled()) {
result.getPendingMap().forEach((id, msg) -> log.trace("Timeout messages [{}]: {}", id, TbMsg.fromBytes(result.getQueueId(), msg.getValue().getTbMsg().toByteArray(), TbMsgCallback.EMPTY))); result.getPendingMap().forEach((id, msg) -> log.trace("Timeout messages [{}]: {}", id, TbMsg.fromBytes(result.getQueueName(), msg.getValue().getTbMsg().toByteArray(), TbMsgCallback.EMPTY)));
} }
return new TbRuleEngineProcessingDecision(true, null); return new TbRuleEngineProcessingDecision(true, null);
} }

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

@ -159,6 +159,8 @@ public abstract class AbstractNashornJsInvokeService extends AbstractJsInvokeSer
} else { } else {
return ((Invocable) engine).invokeFunction(functionName, args); return ((Invocable) engine).invokeFunction(functionName, args);
} }
} catch (ScriptException e) {
throw new ExecutionException(e);
} catch (Exception e) { } catch (Exception e) {
onScriptExecutionError(scriptId, e, functionName); onScriptExecutionError(scriptId, e, functionName);
throw new ExecutionException(e); throw new ExecutionException(e);

16
application/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java

@ -57,6 +57,9 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService {
@Value("${queue.js.max_requests_timeout}") @Value("${queue.js.max_requests_timeout}")
private long maxRequestsTimeout; private long maxRequestsTimeout;
@Value("${queue.js.max_exec_requests_timeout:2000}")
private long maxExecRequestsTimeout;
@Getter @Getter
@Value("${js.remote.max_errors}") @Value("${js.remote.max_errors}")
private int maxErrors; private int maxErrors;
@ -170,7 +173,7 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService {
.setScriptIdMSB(scriptId.getMostSignificantBits()) .setScriptIdMSB(scriptId.getMostSignificantBits())
.setScriptIdLSB(scriptId.getLeastSignificantBits()) .setScriptIdLSB(scriptId.getLeastSignificantBits())
.setFunctionName(functionName) .setFunctionName(functionName)
.setTimeout((int) maxRequestsTimeout) .setTimeout((int) maxExecRequestsTimeout)
.setScriptBody(scriptBody); .setScriptBody(scriptBody);
for (Object arg : args) { for (Object arg : args) {
@ -197,7 +200,6 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService {
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable t) {
onScriptExecutionError(scriptId, t, scriptBody);
if (t instanceof TimeoutException || (t.getCause() != null && t.getCause() instanceof TimeoutException)) { if (t instanceof TimeoutException || (t.getCause() != null && t.getCause() instanceof TimeoutException)) {
queueTimeoutMsgs.incrementAndGet(); queueTimeoutMsgs.incrementAndGet();
} }
@ -212,8 +214,14 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService {
return invokeResult.getResult(); return invokeResult.getResult();
} else { } else {
final RuntimeException e = new RuntimeException(invokeResult.getErrorDetails()); final RuntimeException e = new RuntimeException(invokeResult.getErrorDetails());
onScriptExecutionError(scriptId, e, scriptBody); if (JsInvokeProtos.JsInvokeErrorCode.TIMEOUT_ERROR.equals(invokeResult.getErrorCode())) {
log.debug("[{}] Failed to compile script due to [{}]: {}", scriptId, invokeResult.getErrorCode().name(), invokeResult.getErrorDetails()); onScriptExecutionError(scriptId, e, scriptBody);
queueTimeoutMsgs.incrementAndGet();
} else if (JsInvokeProtos.JsInvokeErrorCode.COMPILATION_ERROR.equals(invokeResult.getErrorCode())) {
onScriptExecutionError(scriptId, e, scriptBody);
}
queueFailedMsgs.incrementAndGet();
log.debug("[{}] Failed to invoke function due to [{}]: {}", scriptId, invokeResult.getErrorCode().name(), invokeResult.getErrorDetails());
throw e; throw e;
} }
}, callbackExecutor); }, callbackExecutor);

16
application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java

@ -42,6 +42,7 @@ public class CustomerUserPermissions extends AbstractPermissions {
put(Resource.WIDGET_TYPE, widgetsPermissionChecker); put(Resource.WIDGET_TYPE, widgetsPermissionChecker);
put(Resource.EDGE, customerEntityPermissionChecker); put(Resource.EDGE, customerEntityPermissionChecker);
put(Resource.RPC, rpcPermissionChecker); put(Resource.RPC, rpcPermissionChecker);
put(Resource.DEVICE_PROFILE, deviceProfilePermissionChecker);
} }
private static final PermissionChecker customerAlarmPermissionChecker = new PermissionChecker() { private static final PermissionChecker customerAlarmPermissionChecker = new PermissionChecker() {
@ -152,4 +153,19 @@ public class CustomerUserPermissions extends AbstractPermissions {
return user.getTenantId().equals(entity.getTenantId()); return user.getTenantId().equals(entity.getTenantId());
} }
}; };
private static final PermissionChecker deviceProfilePermissionChecker = new PermissionChecker.GenericPermissionChecker(Operation.READ) {
@Override
@SuppressWarnings("unchecked")
public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) {
if (!super.hasPermission(user, operation, entityId, entity)) {
return false;
}
if (entity.getTenantId() == null || entity.getTenantId().isNullUid()) {
return true;
}
return user.getTenantId().equals(entity.getTenantId());
}
};
} }

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

@ -308,6 +308,10 @@ public abstract class BaseEntityImportService<I extends EntityId, E extends Expo
public <ID extends EntityId> ID getInternalId(ID externalId, boolean throwExceptionIfNotFound) { public <ID extends EntityId> ID getInternalId(ID externalId, boolean throwExceptionIfNotFound) {
if (externalId == null || externalId.isNullUid()) return null; if (externalId == null || externalId.isNullUid()) return null;
if (EntityType.TENANT.equals(externalId.getEntityType())) {
return (ID) ctx.getTenantId();
}
EntityId localId = ctx.getInternalId(externalId); EntityId localId = ctx.getInternalId(externalId);
if (localId != null) { if (localId != null) {
return (ID) localId; return (ID) localId;

1
application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/DeviceProfileImportService.java

@ -53,7 +53,6 @@ public class DeviceProfileImportService extends BaseEntityImportService<DevicePr
deviceProfile.setDefaultDashboardId(idProvider.getInternalId(deviceProfile.getDefaultDashboardId())); deviceProfile.setDefaultDashboardId(idProvider.getInternalId(deviceProfile.getDefaultDashboardId()));
deviceProfile.setFirmwareId(getOldEntityField(old, DeviceProfile::getFirmwareId)); deviceProfile.setFirmwareId(getOldEntityField(old, DeviceProfile::getFirmwareId));
deviceProfile.setSoftwareId(getOldEntityField(old, DeviceProfile::getSoftwareId)); deviceProfile.setSoftwareId(getOldEntityField(old, DeviceProfile::getSoftwareId));
deviceProfile.setDefaultQueueId(getOldEntityField(old, DeviceProfile::getDefaultQueueId));
return deviceProfile; return deviceProfile;
} }

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

@ -139,18 +139,24 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont
DonAsynchron.withCallback(pendingCommit, commit -> { DonAsynchron.withCallback(pendingCommit, commit -> {
cachePut(commit.getTxId(), new VersionCreationResult()); cachePut(commit.getTxId(), new VersionCreationResult());
try { try {
List<ListenableFuture<Void>> gitFutures = new ArrayList<>(); EntitiesExportCtx<?> theCtx;
switch (request.getType()) { switch (request.getType()) {
case SINGLE_ENTITY: { case SINGLE_ENTITY: {
handleSingleEntityRequest(new SimpleEntitiesExportCtx(user, commit, (SingleEntityVersionCreateRequest) request)); var ctx = new SimpleEntitiesExportCtx(user, commit, (SingleEntityVersionCreateRequest) request);
handleSingleEntityRequest(ctx);
theCtx = ctx;
break; break;
} }
case COMPLEX: { case COMPLEX: {
handleComplexRequest(new ComplexEntitiesExportCtx(user, commit, (ComplexVersionCreateRequest) request)); var ctx = new ComplexEntitiesExportCtx(user, commit, (ComplexVersionCreateRequest) request);
handleComplexRequest(ctx);
theCtx = ctx;
break; break;
} }
default:
throw new RuntimeException("Unsupported request type: " + request.getType());
} }
var resultFuture = Futures.transformAsync(Futures.allAsList(gitFutures), f -> gitServiceQueue.push(commit), executor); var resultFuture = Futures.transformAsync(Futures.allAsList(theCtx.getFutures()), f -> gitServiceQueue.push(commit), executor);
DonAsynchron.withCallback(resultFuture, result -> cachePut(commit.getTxId(), result), e -> processCommitError(user, request, commit, e), executor); DonAsynchron.withCallback(resultFuture, result -> cachePut(commit.getTxId(), result), e -> processCommitError(user, request, commit, e), executor);
} catch (Exception e) { } catch (Exception e) {
processCommitError(user, request, commit, e); processCommitError(user, request, commit, e);

23
application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitVersionControlQueueService.java

@ -76,11 +76,13 @@ import org.thingsboard.server.service.sync.vc.data.VoidGitRequest;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.TreeMap;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -102,7 +104,7 @@ public class DefaultGitVersionControlQueueService implements GitVersionControlQu
private final SchedulerComponent scheduler; private final SchedulerComponent scheduler;
private final Map<UUID, PendingGitRequest<?>> pendingRequestMap = new HashMap<>(); private final Map<UUID, PendingGitRequest<?>> pendingRequestMap = new HashMap<>();
private final Map<UUID, LinkedHashMap<String, String[]>> chunkedMsgs = new ConcurrentHashMap<>(); private final Map<UUID, HashMap<Integer, String[]>> chunkedMsgs = new ConcurrentHashMap<>();
@Value("${queue.vc.request-timeout:60000}") @Value("${queue.vc.request-timeout:60000}")
private int requestTimeout; private int requestTimeout;
@ -286,7 +288,7 @@ public class DefaultGitVersionControlQueueService implements GitVersionControlQu
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
public ListenableFuture<EntityExportData> getEntity(TenantId tenantId, String versionId, EntityId entityId) { public ListenableFuture<EntityExportData> getEntity(TenantId tenantId, String versionId, EntityId entityId) {
EntityContentGitRequest request = new EntityContentGitRequest(tenantId, versionId, entityId); EntityContentGitRequest request = new EntityContentGitRequest(tenantId, versionId, entityId);
chunkedMsgs.put(request.getRequestId(), new LinkedHashMap<>()); chunkedMsgs.put(request.getRequestId(), new HashMap<>());
registerAndSend(request, builder -> builder.setEntityContentRequest(EntityContentRequestMsg.newBuilder() registerAndSend(request, builder -> builder.setEntityContentRequest(EntityContentRequestMsg.newBuilder()
.setVersionId(versionId) .setVersionId(versionId)
.setEntityType(entityId.getEntityType().name()) .setEntityType(entityId.getEntityType().name())
@ -328,7 +330,7 @@ public class DefaultGitVersionControlQueueService implements GitVersionControlQu
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
public ListenableFuture<List<EntityExportData>> getEntities(TenantId tenantId, String versionId, EntityType entityType, int offset, int limit) { public ListenableFuture<List<EntityExportData>> getEntities(TenantId tenantId, String versionId, EntityType entityType, int offset, int limit) {
EntitiesContentGitRequest request = new EntitiesContentGitRequest(tenantId, versionId, entityType); EntitiesContentGitRequest request = new EntitiesContentGitRequest(tenantId, versionId, entityType);
chunkedMsgs.put(request.getRequestId(), new LinkedHashMap<>()); chunkedMsgs.put(request.getRequestId(), new HashMap<>());
registerAndSend(request, builder -> builder.setEntitiesContentRequest(EntitiesContentRequestMsg.newBuilder() registerAndSend(request, builder -> builder.setEntitiesContentRequest(EntitiesContentRequestMsg.newBuilder()
.setVersionId(versionId) .setVersionId(versionId)
.setEntityType(entityType.name()) .setEntityType(entityType.name())
@ -412,10 +414,10 @@ public class DefaultGitVersionControlQueueService implements GitVersionControlQu
((ListVersionsGitRequest) request).getFuture().set(toPageData(listVersionsResponse)); ((ListVersionsGitRequest) request).getFuture().set(toPageData(listVersionsResponse));
} else if (vcResponseMsg.hasEntityContentResponse()) { } else if (vcResponseMsg.hasEntityContentResponse()) {
TransportProtos.EntityContentResponseMsg responseMsg = vcResponseMsg.getEntityContentResponse(); TransportProtos.EntityContentResponseMsg responseMsg = vcResponseMsg.getEntityContentResponse();
log.trace("[{}] received chunk {} for 'getEntity'", responseMsg.getChunkedMsgId(), responseMsg.getChunkIndex()); log.trace("Received chunk {} for 'getEntity'", responseMsg.getChunkIndex());
var joined = joinChunks(requestId, responseMsg, 1); var joined = joinChunks(requestId, responseMsg, 0, 1);
if (joined.isPresent()) { if (joined.isPresent()) {
log.trace("[{}] collected all chunks for 'getEntity'", responseMsg.getChunkedMsgId()); log.trace("Collected all chunks for 'getEntity'");
((EntityContentGitRequest) request).getFuture().set(joined.get().get(0)); ((EntityContentGitRequest) request).getFuture().set(joined.get().get(0));
} else { } else {
completed = false; completed = false;
@ -424,7 +426,7 @@ public class DefaultGitVersionControlQueueService implements GitVersionControlQu
TransportProtos.EntitiesContentResponseMsg responseMsg = vcResponseMsg.getEntitiesContentResponse(); TransportProtos.EntitiesContentResponseMsg responseMsg = vcResponseMsg.getEntitiesContentResponse();
TransportProtos.EntityContentResponseMsg item = responseMsg.getItem(); TransportProtos.EntityContentResponseMsg item = responseMsg.getItem();
if (responseMsg.getItemsCount() > 0) { if (responseMsg.getItemsCount() > 0) {
var joined = joinChunks(requestId, item, responseMsg.getItemsCount()); var joined = joinChunks(requestId, item, responseMsg.getItemIdx(), responseMsg.getItemsCount());
if (joined.isPresent()) { if (joined.isPresent()) {
((EntitiesContentGitRequest) request).getFuture().set(joined.get()); ((EntitiesContentGitRequest) request).getFuture().set(joined.get());
} else { } else {
@ -459,16 +461,17 @@ public class DefaultGitVersionControlQueueService implements GitVersionControlQu
} }
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
private Optional<List<EntityExportData>> joinChunks(UUID requestId, TransportProtos.EntityContentResponseMsg responseMsg, int expectedMsgCount) { private Optional<List<EntityExportData>> joinChunks(UUID requestId, TransportProtos.EntityContentResponseMsg responseMsg, int itemIdx, int expectedMsgCount) {
var chunksMap = chunkedMsgs.get(requestId); var chunksMap = chunkedMsgs.get(requestId);
if (chunksMap == null) { if (chunksMap == null) {
return Optional.empty(); return Optional.empty();
} }
String[] msgChunks = chunksMap.computeIfAbsent(responseMsg.getChunkedMsgId(), id -> new String[responseMsg.getChunksCount()]); String[] msgChunks = chunksMap.computeIfAbsent(itemIdx, id -> new String[responseMsg.getChunksCount()]);
msgChunks[responseMsg.getChunkIndex()] = responseMsg.getData(); msgChunks[responseMsg.getChunkIndex()] = responseMsg.getData();
if (chunksMap.size() == expectedMsgCount && chunksMap.values().stream() if (chunksMap.size() == expectedMsgCount && chunksMap.values().stream()
.allMatch(chunks -> CollectionsUtil.countNonNull(chunks) == chunks.length)) { .allMatch(chunks -> CollectionsUtil.countNonNull(chunks) == chunks.length)) {
return Optional.of(chunksMap.values().stream() return Optional.of(chunksMap.entrySet().stream()
.sorted(Comparator.comparingInt(Map.Entry::getKey)).map(Map.Entry::getValue)
.map(chunks -> String.join("", chunks)) .map(chunks -> String.join("", chunks))
.map(this::toData) .map(this::toData)
.collect(Collectors.toList())); .collect(Collectors.toList()));

6
application/src/main/java/org/thingsboard/server/service/telemetry/AttributeData.java

@ -32,17 +32,17 @@ public class AttributeData implements Comparable<AttributeData>{
this.value = value; this.value = value;
} }
@ApiModelProperty(position = 1, value = "Timestamp last updated attribute, in milliseconds", example = "1609459200000", readOnly = true) @ApiModelProperty(position = 1, value = "Timestamp last updated attribute, in milliseconds", example = "1609459200000", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
public long getLastUpdateTs() { public long getLastUpdateTs() {
return lastUpdateTs; return lastUpdateTs;
} }
@ApiModelProperty(position = 2, value = "String representing attribute key", example = "active", readOnly = true) @ApiModelProperty(position = 2, value = "String representing attribute key", example = "active", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
public String getKey() { public String getKey() {
return key; return key;
} }
@ApiModelProperty(position = 3, value = "Object representing value of attribute key", example = "false", readOnly = true) @ApiModelProperty(position = 3, value = "Object representing value of attribute key", example = "false", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
public Object getValue() { public Object getValue() {
return value; return value;
} }

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

@ -30,12 +30,12 @@ public class TsData implements Comparable<TsData>{
this.value = value; this.value = value;
} }
@ApiModelProperty(position = 1, value = "Timestamp last updated timeseries, in milliseconds", example = "1609459200000", readOnly = true) @ApiModelProperty(position = 1, value = "Timestamp last updated timeseries, in milliseconds", example = "1609459200000", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
public long getTs() { public long getTs() {
return ts; return ts;
} }
@ApiModelProperty(position = 2, value = "Object representing value of timeseries key", example = "20", readOnly = true) @ApiModelProperty(position = 2, value = "Object representing value of timeseries key", example = "20", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
public Object getValue() { public Object getValue() {
return value; return value;
} }

2
application/src/main/resources/logback.xml

@ -27,7 +27,7 @@
<logger name="org.thingsboard.server" level="INFO"/> <logger name="org.thingsboard.server" level="INFO"/>
<logger name="org.apache.kafka.common.utils.AppInfoParser" level="WARN"/> <logger name="org.apache.kafka.common.utils.AppInfoParser" level="WARN"/>
<logger name="org.apache.kafka.clients.consumer.ConsumerConfig" level="WARN"/> <logger name="org.apache.kafka.clients" level="WARN"/>
<!-- To enable the logging of scanned rule engine components--> <!-- To enable the logging of scanned rule engine components-->
<!-- <logger name="org.thingsboard.server.service.component.AnnotationComponentDiscoveryService" level="DEBUG" />--> <!-- <logger name="org.thingsboard.server.service.component.AnnotationComponentDiscoveryService" level="DEBUG" />-->
<!-- Other useful logs --> <!-- Other useful logs -->

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

@ -148,7 +148,7 @@ ui:
# Help parameters # Help parameters
help: help:
# Base url for UI help assets # Base url for UI help assets
base-url: "${UI_HELP_BASE_URL:https://raw.githubusercontent.com/thingsboard/thingsboard-ui-help/release-3.3.4}" base-url: "${UI_HELP_BASE_URL:https://raw.githubusercontent.com/thingsboard/thingsboard-ui-help/release-3.4}"
database: database:
ts_max_intervals: "${DATABASE_TS_MAX_INTERVALS:700}" # Max number of DB queries generated by single API call to fetch telemetry records ts_max_intervals: "${DATABASE_TS_MAX_INTERVALS:700}" # Max number of DB queries generated by single API call to fetch telemetry records
@ -326,7 +326,9 @@ actors:
# Specify thread pool size for javascript executor service # Specify thread pool size for javascript executor service
js_thread_pool_size: "${ACTORS_RULE_JS_THREAD_POOL_SIZE:50}" js_thread_pool_size: "${ACTORS_RULE_JS_THREAD_POOL_SIZE:50}"
# Specify thread pool size for mail sender executor service # Specify thread pool size for mail sender executor service
mail_thread_pool_size: "${ACTORS_RULE_MAIL_THREAD_POOL_SIZE:50}" mail_thread_pool_size: "${ACTORS_RULE_MAIL_THREAD_POOL_SIZE:40}"
# Specify thread pool size for password reset emails
mail_password_reset_thread_pool_size: "${ACTORS_RULE_MAIL_PASSWORD_RESET_THREAD_POOL_SIZE:10}"
# Specify thread pool size for sms sender executor service # Specify thread pool size for sms sender executor service
sms_thread_pool_size: "${ACTORS_RULE_SMS_THREAD_POOL_SIZE:50}" sms_thread_pool_size: "${ACTORS_RULE_SMS_THREAD_POOL_SIZE:50}"
# Whether to allow usage of system mail service for rules # Whether to allow usage of system mail service for rules
@ -545,10 +547,6 @@ spring:
audit-log: audit-log:
# Enable/disable audit log functionality. # Enable/disable audit log functionality.
enabled: "${AUDIT_LOG_ENABLED:true}" enabled: "${AUDIT_LOG_ENABLED:true}"
# Specify partitioning size for audit log by tenant id storage. Example MINUTES, HOURS, DAYS, MONTHS
by_tenant_partitioning: "${AUDIT_LOG_BY_TENANT_PARTITIONING:MONTHS}"
# Number of days as history period if startTime and endTime are not specified
default_query_period: "${AUDIT_LOG_DEFAULT_QUERY_PERIOD:30}"
# Logging levels per each entity type. # Logging levels per each entity type.
# Allowed values: OFF (disable), W (log write operations), RW (log read and write operations) # Allowed values: OFF (disable), W (log write operations), RW (log read and write operations)
logging-level: logging-level:
@ -945,10 +943,11 @@ queue:
topic-properties: topic-properties:
rule-engine: "${TB_QUEUE_KAFKA_RE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" rule-engine: "${TB_QUEUE_KAFKA_RE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:10;min.insync.replicas:1}"
notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100;min.insync.replicas:1}" js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100;min.insync.replicas:1}"
ota-updates: "${TB_QUEUE_KAFKA_OTA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:10;min.insync.replicas:1}" ota-updates: "${TB_QUEUE_KAFKA_OTA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:10;min.insync.replicas:1}"
version-control: "${TB_QUEUE_KAFKA_VC_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:10;min.insync.replicas:1}"
consumer-stats: consumer-stats:
enabled: "${TB_QUEUE_KAFKA_CONSUMER_STATS_ENABLED:true}" enabled: "${TB_QUEUE_KAFKA_CONSUMER_STATS_ENABLED:true}"
print-interval-ms: "${TB_QUEUE_KAFKA_CONSUMER_STATS_MIN_PRINT_INTERVAL_MS:60000}" print-interval-ms: "${TB_QUEUE_KAFKA_CONSUMER_STATS_MIN_PRINT_INTERVAL_MS:60000}"
@ -965,6 +964,8 @@ queue:
transport-api: "${TB_QUEUE_AWS_SQS_TA_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" transport-api: "${TB_QUEUE_AWS_SQS_TA_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
notifications: "${TB_QUEUE_AWS_SQS_NOTIFICATIONS_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" notifications: "${TB_QUEUE_AWS_SQS_NOTIFICATIONS_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
js-executor: "${TB_QUEUE_AWS_SQS_JE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" js-executor: "${TB_QUEUE_AWS_SQS_JE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
ota-updates: "${TB_QUEUE_AWS_SQS_OTA_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
version-control: "${TB_QUEUE_AWS_SQS_VC_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
# VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds # VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds
pubsub: pubsub:
project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}" project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}"
@ -977,6 +978,7 @@ queue:
transport-api: "${TB_QUEUE_PUBSUB_TA_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" transport-api: "${TB_QUEUE_PUBSUB_TA_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
notifications: "${TB_QUEUE_PUBSUB_NOTIFICATIONS_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" notifications: "${TB_QUEUE_PUBSUB_NOTIFICATIONS_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
js-executor: "${TB_QUEUE_PUBSUB_JE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" js-executor: "${TB_QUEUE_PUBSUB_JE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
version-control: "${TB_QUEUE_PUBSUB_VC_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
service_bus: service_bus:
namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}" namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}"
sas_key_name: "${TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME:YOUR_SAS_KEY_NAME}" sas_key_name: "${TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME:YOUR_SAS_KEY_NAME}"
@ -988,6 +990,7 @@ queue:
transport-api: "${TB_QUEUE_SERVICE_BUS_TA_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" transport-api: "${TB_QUEUE_SERVICE_BUS_TA_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
notifications: "${TB_QUEUE_SERVICE_BUS_NOTIFICATIONS_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" notifications: "${TB_QUEUE_SERVICE_BUS_NOTIFICATIONS_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
js-executor: "${TB_QUEUE_SERVICE_BUS_JE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" js-executor: "${TB_QUEUE_SERVICE_BUS_JE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
version-control: "${TB_QUEUE_SERVICE_BUS_VC_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
rabbitmq: rabbitmq:
exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}" exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}"
host: "${TB_QUEUE_RABBIT_MQ_HOST:localhost}" host: "${TB_QUEUE_RABBIT_MQ_HOST:localhost}"
@ -1004,6 +1007,7 @@ queue:
transport-api: "${TB_QUEUE_RABBIT_MQ_TA_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" transport-api: "${TB_QUEUE_RABBIT_MQ_TA_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
notifications: "${TB_QUEUE_RABBIT_MQ_NOTIFICATIONS_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" notifications: "${TB_QUEUE_RABBIT_MQ_NOTIFICATIONS_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
js-executor: "${TB_QUEUE_RABBIT_MQ_JE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" js-executor: "${TB_QUEUE_RABBIT_MQ_JE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
version-control: "${TB_QUEUE_RABBIT_MQ_VC_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
partitions: partitions:
hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256
transport_api: transport_api:
@ -1045,6 +1049,8 @@ queue:
max_eval_requests_timeout: "${REMOTE_JS_MAX_EVAL_REQUEST_TIMEOUT:60000}" max_eval_requests_timeout: "${REMOTE_JS_MAX_EVAL_REQUEST_TIMEOUT:60000}"
# JS max request timeout # JS max request timeout
max_requests_timeout: "${REMOTE_JS_MAX_REQUEST_TIMEOUT:10000}" max_requests_timeout: "${REMOTE_JS_MAX_REQUEST_TIMEOUT:10000}"
# JS execution max request timeout
max_exec_requests_timeout: "${REMOTE_JS_MAX_EXEC_REQUEST_TIMEOUT:2000}"
# JS response poll interval # JS response poll interval
response_poll_interval: "${REMOTE_JS_RESPONSE_POLL_INTERVAL_MS:25}" response_poll_interval: "${REMOTE_JS_RESPONSE_POLL_INTERVAL_MS:25}"
rule-engine: rule-engine:

224
application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java

@ -20,9 +20,10 @@ import org.mockito.ArgumentMatcher;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.boot.test.mock.mockito.SpyBean;
import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.edge.EdgeEventType; import org.thingsboard.server.common.data.edge.EdgeEventType;
@ -37,7 +38,6 @@ import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg; import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg;
import org.thingsboard.server.dao.audit.AuditLogService; import org.thingsboard.server.dao.audit.AuditLogService;
import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.service.gateway_device.GatewayNotificationsService;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -58,21 +58,19 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
@SpyBean @SpyBean
protected AuditLogService auditLogService; protected AuditLogService auditLogService;
@SpyBean
protected GatewayNotificationsService gatewayNotificationsService;
protected final String msgErrorPermission = "You don't have permission to perform this operation!"; protected final String msgErrorPermission = "You don't have permission to perform this operation!";
protected final String msgErrorShouldBeSpecified = "should be specified"; protected final String msgErrorShouldBeSpecified = "should be specified";
protected final String msgErrorNotFound = "Requested item wasn't found!";
protected void testNotifyEntityAllOneTime(HasName entity, EntityId entityId, EntityId originatorId, protected void testNotifyEntityAllOneTime(HasName entity, EntityId entityId, EntityId originatorId,
TenantId tenantId, CustomerId customerId, UserId userId, String userName, TenantId tenantId, CustomerId customerId, UserId userId, String userName,
ActionType actionType, Object... additionalInfo) { ActionType actionType, Object... additionalInfo) {
int cntTime = 1; int cntTime = 1;
testSendNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime); testNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime);
testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo); testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo);
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId); ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTime);
Mockito.reset(tbClusterService, auditLogService); Mockito.reset(tbClusterService, auditLogService);
} }
@ -85,27 +83,35 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
Mockito.eq(edgeTypeByActionType(actionType))); Mockito.eq(edgeTypeByActionType(actionType)));
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(relation.getTo()); ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(relation.getTo());
ArgumentMatcher<HasName> matcherEntityClassEquals = Objects::isNull; ArgumentMatcher<HasName> matcherEntityClassEquals = Objects::isNull;
testLogEntityActionAdditionalInfo(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, userName, actionType, cntTime, ArgumentMatcher<CustomerId> matcherCustomerId = customerId == null ?
argument -> argument.getClass().equals(CustomerId.class) : argument -> argument.equals(customerId);
ArgumentMatcher<UserId> matcherUserId = userId == null ?
argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId);
testLogEntityActionAdditionalInfo(matcherEntityClassEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName, actionType, cntTime,
extractMatcherAdditionalInfo(additionalInfo)); extractMatcherAdditionalInfo(additionalInfo));
testPushMsgToRuleEngineNever(relation.getTo()); testPushMsgToRuleEngineNever(relation.getTo());
matcherOriginatorId = argument -> argument.equals(relation.getFrom()); matcherOriginatorId = argument -> argument.equals(relation.getFrom());
testLogEntityActionAdditionalInfo(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, userName, actionType, cntTime, testLogEntityActionAdditionalInfo(matcherEntityClassEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName, actionType, cntTime,
extractMatcherAdditionalInfo(additionalInfo)); extractMatcherAdditionalInfo(additionalInfo));
testPushMsgToRuleEngineNever(relation.getFrom()); testPushMsgToRuleEngineNever(relation.getFrom());
Mockito.reset(tbClusterService, auditLogService); Mockito.reset(tbClusterService, auditLogService);
} }
protected void testNotifyEntityAllManyRelation(EntityRelation relation, protected void testNotifyEntityAllManyRelation(EntityRelation relation,
TenantId tenantId, CustomerId customerId, UserId userId, String userName, TenantId tenantId, CustomerId customerId, UserId userId, String userName,
ActionType actionType, int cntTime) { ActionType actionType, int cntTime) {
Mockito.verify(tbClusterService, times(cntTime)).sendNotificationMsgToEdge(Mockito.eq(tenantId), Mockito.verify(tbClusterService, times(cntTime)).sendNotificationMsgToEdge(Mockito.eq(tenantId),
Mockito.isNull(), Mockito.isNull(), Mockito.any(), Mockito.eq(EdgeEventType.RELATION), Mockito.isNull(), Mockito.isNull(), Mockito.any(), Mockito.eq(EdgeEventType.RELATION),
Mockito.eq(edgeTypeByActionType(actionType))); Mockito.eq(edgeTypeByActionType(actionType)));
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.getClass().equals(relation.getFrom().getClass()); ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.getClass().equals(relation.getFrom().getClass());
ArgumentMatcher<HasName> matcherEntityClassEquals = Objects::isNull; ArgumentMatcher<HasName> matcherEntityClassEquals = Objects::isNull;
testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, ArgumentMatcher<CustomerId> matcherCustomerId = customerId == null ?
argument -> argument.getClass().equals(CustomerId.class) : argument -> argument.equals(customerId);
ArgumentMatcher<UserId> matcherUserId = userId == null ?
argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId);
testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId,
userName, actionType, cntTime * 2, 1); userName, actionType, cntTime * 2, 1);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, new Tenant(), cntTime);
Mockito.reset(tbClusterService, auditLogService); Mockito.reset(tbClusterService, auditLogService);
} }
@ -113,16 +119,16 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
TenantId tenantId, CustomerId customerId, UserId userId, String userName, TenantId tenantId, CustomerId customerId, UserId userId, String userName,
ActionType actionType, Object... additionalInfo) { ActionType actionType, Object... additionalInfo) {
int cntTime = 1; int cntTime = 1;
testSendNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime); testNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime);
testLogEntityActionEntityEqClass(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo); testLogEntityActionEntityEqClass(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo);
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId); ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTime);
Mockito.reset(tbClusterService, auditLogService); Mockito.reset(tbClusterService, auditLogService);
} }
protected void testNotifyEntityNeverMsgToEdgeServiceOneTime(HasName entity, EntityId entityId, TenantId tenantId, protected void testNotifyEntityNeverMsgToEdgeServiceOneTime(HasName entity, EntityId entityId, TenantId tenantId,
ActionType actionType) { ActionType actionType) {
testSendNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, 1); testNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, 1);
testLogEntityActionNever(entityId, entity); testLogEntityActionNever(entityId, entity);
testPushMsgToRuleEngineNever(entityId); testPushMsgToRuleEngineNever(entityId);
Mockito.reset(tbClusterService, auditLogService); Mockito.reset(tbClusterService, auditLogService);
@ -132,13 +138,13 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
TenantId tenantId, CustomerId customerId, UserId userId, TenantId tenantId, CustomerId customerId, UserId userId,
String userName, ActionType actionType, Object... additionalInfo) { String userName, ActionType actionType, Object... additionalInfo) {
int cntTime = 1; int cntTime = 1;
testNotificationMsgToEdgeServiceNever(entityId); testNotificationMsgToEdgeServiceNeverWithActionType(entityId, actionType);
testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo); testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo);
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId); ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId);
if (ActionType.RELATIONS_DELETED.equals(actionType)) { if (ActionType.RELATIONS_DELETED.equals(actionType)) {
testPushMsgToRuleEngineNever(originatorId); testPushMsgToRuleEngineNever(originatorId);
} else { } else {
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTime);
} }
Mockito.reset(tbClusterService, auditLogService); Mockito.reset(tbClusterService, auditLogService);
} }
@ -148,12 +154,16 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
ActionType actionType, int cntTime, Object... additionalInfo) { ActionType actionType, int cntTime, Object... additionalInfo) {
EntityId entityId = createEntityId_NULL_UUID(entity); EntityId entityId = createEntityId_NULL_UUID(entity);
EntityId originatorId = createEntityId_NULL_UUID(originator); EntityId originatorId = createEntityId_NULL_UUID(originator);
testNotificationMsgToEdgeServiceNever(entityId); testNotificationMsgToEdgeServiceNeverWithActionType(entityId, actionType);
ArgumentMatcher<HasName> matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass()); ArgumentMatcher<HasName> matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass());
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass()); ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass());
testLogEntityActionAdditionalInfo(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, userName, actionType, cntTime, ArgumentMatcher<CustomerId> matcherCustomerId = customerId == null ?
argument -> argument.getClass().equals(CustomerId.class) : argument -> argument.equals(customerId);
ArgumentMatcher<UserId> matcherUserId = userId == null ?
argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId);
testLogEntityActionAdditionalInfo(matcherEntityClassEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName, actionType, cntTime,
extractMatcherAdditionalInfo(additionalInfo)); extractMatcherAdditionalInfo(additionalInfo));
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTime);
Mockito.reset(tbClusterService, auditLogService); Mockito.reset(tbClusterService, auditLogService);
} }
@ -165,10 +175,13 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
testSendNotificationMsgToEdgeServiceTimeEntityEqAny(tenantId, actionTypeEdge, cntTimeEdge); testSendNotificationMsgToEdgeServiceTimeEntityEqAny(tenantId, actionTypeEdge, cntTimeEdge);
ArgumentMatcher<HasName> matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass()); ArgumentMatcher<HasName> matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass());
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass()); ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass());
testLogEntityActionAdditionalInfo(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, userName, actionType, cntTime, ArgumentMatcher<CustomerId> matcherCustomerId = customerId == null ?
argument -> argument.getClass().equals(CustomerId.class) : argument -> argument.equals(customerId);
ArgumentMatcher<UserId> matcherUserId = userId == null ?
argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId);
testLogEntityActionAdditionalInfo(matcherEntityClassEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName, actionType, cntTime,
extractMatcherAdditionalInfoClass(additionalInfo)); extractMatcherAdditionalInfoClass(additionalInfo));
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTimeRuleEngine); testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTimeRuleEngine);
Mockito.reset(tbClusterService, auditLogService);
} }
protected void testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAnyAdditionalInfoAny(HasName entity, HasName originator, protected void testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAnyAdditionalInfoAny(HasName entity, HasName originator,
@ -178,9 +191,13 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
testSendNotificationMsgToEdgeServiceTimeEntityEqAny(tenantId, actionTypeEdge, cntTimeEdge); testSendNotificationMsgToEdgeServiceTimeEntityEqAny(tenantId, actionTypeEdge, cntTimeEdge);
ArgumentMatcher<HasName> matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass()); ArgumentMatcher<HasName> matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass());
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass()); ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass());
testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, userName, actionType, cntTime, ArgumentMatcher<CustomerId> matcherCustomerId = customerId == null ?
argument -> argument.getClass().equals(CustomerId.class) : argument -> argument.equals(customerId);
ArgumentMatcher<UserId> matcherUserId = userId == null ?
argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId);
testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName, actionType, cntTime,
cntAdditionalInfo); cntAdditionalInfo);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTimeEdge); testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTimeEdge);
Mockito.reset(tbClusterService, auditLogService); Mockito.reset(tbClusterService, auditLogService);
} }
@ -189,12 +206,16 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
ActionType actionType, int cntTime, int cntAdditionalInfo) { ActionType actionType, int cntTime, int cntAdditionalInfo) {
EntityId entityId = createEntityId_NULL_UUID(entity); EntityId entityId = createEntityId_NULL_UUID(entity);
EntityId originatorId = createEntityId_NULL_UUID(originator); EntityId originatorId = createEntityId_NULL_UUID(originator);
testNotificationMsgToEdgeServiceNever(entityId); testNotificationMsgToEdgeServiceNeverWithActionType(entityId, actionType);
ArgumentMatcher<HasName> matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass()); ArgumentMatcher<HasName> matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass());
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass()); ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass());
testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, userName, actionType, cntTime, ArgumentMatcher<CustomerId> matcherCustomerId = customerId == null ?
argument -> argument.getClass().equals(CustomerId.class) : argument -> argument.equals(customerId);
ArgumentMatcher<UserId> matcherUserId = userId == null ?
argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId);
testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName, actionType, cntTime,
cntAdditionalInfo); cntAdditionalInfo);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTime);
Mockito.reset(tbClusterService, auditLogService); Mockito.reset(tbClusterService, auditLogService);
} }
@ -202,10 +223,10 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
TenantId tenantId, CustomerId customerId, UserId userId, String userName, TenantId tenantId, CustomerId customerId, UserId userId, String userName,
ActionType actionType, Object... additionalInfo) { ActionType actionType, Object... additionalInfo) {
int cntTime = 1; int cntTime = 1;
testSendNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime); testNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime);
testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo); testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo);
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId); ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTime);
testBroadcastEntityStateChangeEventTime(entityId, tenantId, cntTime); testBroadcastEntityStateChangeEventTime(entityId, tenantId, cntTime);
Mockito.reset(tbClusterService, auditLogService); Mockito.reset(tbClusterService, auditLogService);
} }
@ -214,34 +235,40 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
TenantId tenantId, CustomerId customerId, UserId userId, String userName, TenantId tenantId, CustomerId customerId, UserId userId, String userName,
ActionType actionType, Object... additionalInfo) { ActionType actionType, Object... additionalInfo) {
int cntTime = 1; int cntTime = 1;
testNotificationMsgToEdgeServiceNever(entityId); testNotificationMsgToEdgeServiceNeverWithActionType(entityId, actionType);
testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo); testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo);
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId); ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTime);
testBroadcastEntityStateChangeEventTime(entityId, tenantId, cntTime); testBroadcastEntityStateChangeEventTime(entityId, tenantId, cntTime);
Mockito.reset(tbClusterService, auditLogService); Mockito.reset(tbClusterService, auditLogService);
} }
protected void testNotifyEntityBroadcastEntityStateChangeEventManyMsgToEdgeServiceNever(HasName entity, HasName originator, protected void testNotifyEntityBroadcastEntityStateChangeEventMany(HasName entity, HasName originator,
TenantId tenantId, CustomerId customerId, UserId userId, String userName, TenantId tenantId, CustomerId customerId,
ActionType actionType, int cntTime, int cntAdditionalInfo) { UserId userId, String userName, ActionType actionType,
ActionType actionTypeEdge,
int cntTime, int cntTimeEdge, int cntTimeRuleEngine,
int cntAdditionalInfo) {
EntityId entityId = createEntityId_NULL_UUID(entity); EntityId entityId = createEntityId_NULL_UUID(entity);
EntityId originatorId = createEntityId_NULL_UUID(originator); EntityId originatorId = createEntityId_NULL_UUID(originator);
testNotificationMsgToEdgeServiceNever(entityId); testNotificationMsgToEdgeServiceTime(entityId, tenantId, actionTypeEdge, cntTimeEdge);
ArgumentMatcher<HasName> matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass()); ArgumentMatcher<HasName> matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass());
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass()); ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass());
testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, userName, actionType, cntTime, ArgumentMatcher<CustomerId> matcherCustomerId = customerId == null ?
argument -> argument.getClass().equals(CustomerId.class) : argument -> argument.equals(customerId);
ArgumentMatcher<UserId> matcherUserId = userId == null ?
argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId);
testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName, actionType, cntTime,
cntAdditionalInfo); cntAdditionalInfo);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTimeRuleEngine);
testBroadcastEntityStateChangeEventTime(entityId, tenantId, cntTime); testBroadcastEntityStateChangeEventTime(entityId, tenantId, cntTime);
Mockito.reset(tbClusterService, auditLogService);
} }
protected void testNotifyEntityMsgToEdgePushMsgToCoreOneTime(HasName entity, EntityId entityId, EntityId originatorId, protected void testNotifyEntityMsgToEdgePushMsgToCoreOneTime(HasName entity, EntityId entityId, EntityId originatorId,
TenantId tenantId, CustomerId customerId, UserId userId, String userName, TenantId tenantId, CustomerId customerId, UserId userId, String userName,
ActionType actionType, Object... additionalInfo) { ActionType actionType, Object... additionalInfo) {
int cntTime = 1; int cntTime = 1;
testSendNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime); testNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime);
testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo); testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo);
tesPushMsgToCoreTime(cntTime); tesPushMsgToCoreTime(cntTime);
Mockito.reset(tbClusterService, auditLogService); Mockito.reset(tbClusterService, auditLogService);
@ -252,7 +279,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
Object... additionalInfo) { Object... additionalInfo) {
CustomerId customer_NULL_UUID = (CustomerId) EntityIdFactory.getByTypeAndUuid(EntityType.CUSTOMER, ModelConstants.NULL_UUID); CustomerId customer_NULL_UUID = (CustomerId) EntityIdFactory.getByTypeAndUuid(EntityType.CUSTOMER, ModelConstants.NULL_UUID);
EntityId entity_originator_NULL_UUID = createEntityId_NULL_UUID(entity); EntityId entity_originator_NULL_UUID = createEntityId_NULL_UUID(entity);
testNotificationMsgToEdgeServiceNever(entity_originator_NULL_UUID); testNotificationMsgToEdgeServiceNeverWithActionType(entity_originator_NULL_UUID, actionType);
ArgumentMatcher<HasName> matcherEntityEquals = argument -> argument.getClass().equals(entity.getClass()); ArgumentMatcher<HasName> matcherEntityEquals = argument -> argument.getClass().equals(entity.getClass());
ArgumentMatcher<Exception> matcherError = argument -> argument.getMessage().contains(exp.getMessage()) ArgumentMatcher<Exception> matcherError = argument -> argument.getMessage().contains(exp.getMessage())
& argument.getClass().equals(exp.getClass()); & argument.getClass().equals(exp.getClass());
@ -266,7 +293,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
Object... additionalInfo) { Object... additionalInfo) {
CustomerId customer_NULL_UUID = (CustomerId) EntityIdFactory.getByTypeAndUuid(EntityType.CUSTOMER, ModelConstants.NULL_UUID); CustomerId customer_NULL_UUID = (CustomerId) EntityIdFactory.getByTypeAndUuid(EntityType.CUSTOMER, ModelConstants.NULL_UUID);
EntityId entity_originator_NULL_UUID = createEntityId_NULL_UUID(entity); EntityId entity_originator_NULL_UUID = createEntityId_NULL_UUID(entity);
testNotificationMsgToEdgeServiceNever(entity_originator_NULL_UUID); testNotificationMsgToEdgeServiceNeverWithActionType(entity_originator_NULL_UUID, actionType);
ArgumentMatcher<HasName> matcherEntityIsNull = Objects::isNull; ArgumentMatcher<HasName> matcherEntityIsNull = Objects::isNull;
ArgumentMatcher<Exception> matcherError = argument -> argument.getMessage().contains(exp.getMessage()) & ArgumentMatcher<Exception> matcherError = argument -> argument.getMessage().contains(exp.getMessage()) &
argument.getClass().equals(exp.getClass()); argument.getClass().equals(exp.getClass());
@ -283,20 +310,11 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
Mockito.reset(tbClusterService, auditLogService); Mockito.reset(tbClusterService, auditLogService);
} }
protected void testNotificationUpdateGatewayOneTime(Device device, Device oldDevice) { private void testNotificationMsgToEdgeServiceNeverWithActionType(EntityId entityId, ActionType actionType) {
Mockito.verify(gatewayNotificationsService, times(1)).onDeviceUpdated(Mockito.eq(device), Mockito.eq(oldDevice)); EdgeEventActionType edgeEventActionType = ActionType.CREDENTIALS_UPDATED.equals(actionType) ?
} EdgeEventActionType.CREDENTIALS_UPDATED : edgeTypeByActionType(actionType);
Mockito.verify(tbClusterService, never()).sendNotificationMsgToEdge(Mockito.any(),
protected void testNotificationUpdateGatewayNever() { Mockito.any(), Mockito.any(entityId.getClass()), Mockito.any(), Mockito.any(), Mockito.eq(edgeEventActionType));
Mockito.verify(gatewayNotificationsService, never()).onDeviceUpdated(Mockito.any(Device.class), Mockito.any(Device.class));
}
protected void testNotificationDeleteGatewayOneTime(Device device) {
Mockito.verify(gatewayNotificationsService, times(1)).onDeviceDeleted(device);
}
protected void testNotificationDeleteGatewayNever() {
Mockito.verify(gatewayNotificationsService, never()).onDeviceDeleted(Mockito.any(Device.class));
} }
private void testNotificationMsgToEdgeServiceNever(EntityId entityId) { private void testNotificationMsgToEdgeServiceNever(EntityId entityId) {
@ -317,16 +335,24 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
Mockito.any(entityId.getClass()), Mockito.any(), Mockito.any()); Mockito.any(entityId.getClass()), Mockito.any(), Mockito.any());
} }
private void testPushMsgToRuleEngineTime(ArgumentMatcher<EntityId> matcherOriginatorId, TenantId tenantId, int cntTime) { protected void testBroadcastEntityStateChangeEventNever(EntityId entityId) {
Mockito.verify(tbClusterService, never()).broadcastEntityStateChangeEvent(Mockito.any(),
Mockito.any(entityId.getClass()), Mockito.any(ComponentLifecycleEvent.class));
}
private void testPushMsgToRuleEngineTime(ArgumentMatcher<EntityId> matcherOriginatorId, TenantId tenantId, HasName entity, int cntTime) {
tenantId = tenantId.isNullUid() && ((HasTenantId) entity).getTenantId() != null ? ((HasTenantId) entity).getTenantId() : tenantId;
Mockito.verify(tbClusterService, times(cntTime)).pushMsgToRuleEngine(Mockito.eq(tenantId), Mockito.verify(tbClusterService, times(cntTime)).pushMsgToRuleEngine(Mockito.eq(tenantId),
Mockito.argThat(matcherOriginatorId), Mockito.any(TbMsg.class), Mockito.isNull()); Mockito.argThat(matcherOriginatorId), Mockito.any(TbMsg.class), Mockito.isNull());
} }
private void testSendNotificationMsgToEdgeServiceTime(EntityId entityId, TenantId tenantId, ActionType actionType, int cntTime) { private void testNotificationMsgToEdgeServiceTime(EntityId entityId, TenantId tenantId, ActionType actionType, int cntTime) {
EdgeEventActionType edgeEventActionType = ActionType.CREDENTIALS_UPDATED.equals(actionType) ? EdgeEventActionType edgeEventActionType = ActionType.CREDENTIALS_UPDATED.equals(actionType) ?
EdgeEventActionType.CREDENTIALS_UPDATED : edgeTypeByActionType(actionType); EdgeEventActionType.CREDENTIALS_UPDATED : edgeTypeByActionType(actionType);
ArgumentMatcher<EntityId> matcherEntityId = cntTime == 1 ? argument -> argument.equals(entityId) :
argument -> argument.getClass().equals(entityId.getClass());
Mockito.verify(tbClusterService, times(cntTime)).sendNotificationMsgToEdge(Mockito.eq(tenantId), Mockito.verify(tbClusterService, times(cntTime)).sendNotificationMsgToEdge(Mockito.eq(tenantId),
Mockito.any(), Mockito.eq(entityId), Mockito.any(), Mockito.isNull(), Mockito.any(), Mockito.argThat(matcherEntityId), Mockito.any(), Mockito.isNull(),
Mockito.eq(edgeEventActionType)); Mockito.eq(edgeEventActionType));
} }
@ -336,8 +362,10 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
Mockito.eq(edgeTypeByActionType(actionType))); Mockito.eq(edgeTypeByActionType(actionType)));
} }
private void testBroadcastEntityStateChangeEventTime(EntityId entityId, TenantId tenantId, int cntTime) { protected void testBroadcastEntityStateChangeEventTime(EntityId entityId, TenantId tenantId, int cntTime) {
Mockito.verify(tbClusterService, times(cntTime)).broadcastEntityStateChangeEvent(Mockito.eq(tenantId), ArgumentMatcher<TenantId> matcherTenantIdId = cntTime > 1 || tenantId == null ? argument -> argument.getClass().equals(TenantId.class) :
argument -> argument.equals(tenantId) ;
Mockito.verify(tbClusterService, times(cntTime)).broadcastEntityStateChangeEvent(Mockito.argThat(matcherTenantIdId),
Mockito.any(entityId.getClass()), Mockito.any(ComponentLifecycleEvent.class)); Mockito.any(entityId.getClass()), Mockito.any(ComponentLifecycleEvent.class));
} }
@ -348,9 +376,13 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
private void testLogEntityAction(HasName entity, EntityId originatorId, TenantId tenantId, private void testLogEntityAction(HasName entity, EntityId originatorId, TenantId tenantId,
CustomerId customerId, UserId userId, String userName, CustomerId customerId, UserId userId, String userName,
ActionType actionType, int cntTime, Object... additionalInfo) { ActionType actionType, int cntTime, Object... additionalInfo) {
ArgumentMatcher<HasName> matcherEntityEquals = entity == null ? Objects::isNull : argument -> argument.equals(entity); ArgumentMatcher<HasName> matcherEntityEquals = entity == null ? Objects::isNull : argument -> argument.toString().equals(entity.toString());
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId); ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId);
testLogEntityActionAdditionalInfo(matcherEntityEquals, matcherOriginatorId, tenantId, customerId, userId, userName, ArgumentMatcher<CustomerId> matcherCustomerId = customerId == null ?
argument -> argument.getClass().equals(CustomerId.class) : argument -> argument.equals(customerId);
ArgumentMatcher<UserId> matcherUserId = userId == null ?
argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId);
testLogEntityActionAdditionalInfo(matcherEntityEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName,
actionType, cntTime, extractMatcherAdditionalInfo(additionalInfo)); actionType, cntTime, extractMatcherAdditionalInfo(additionalInfo));
} }
@ -359,19 +391,24 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
ActionType actionType, int cntTime, Object... additionalInfo) { ActionType actionType, int cntTime, Object... additionalInfo) {
ArgumentMatcher<HasName> matcherEntityEquals = argument -> argument.getClass().equals(entity.getClass()); ArgumentMatcher<HasName> matcherEntityEquals = argument -> argument.getClass().equals(entity.getClass());
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId); ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId);
testLogEntityActionAdditionalInfo(matcherEntityEquals, matcherOriginatorId, tenantId, customerId, userId, userName, ArgumentMatcher<CustomerId> matcherCustomerId = customerId == null ?
argument -> argument.getClass().equals(CustomerId.class) : argument -> argument.equals(customerId);
ArgumentMatcher<UserId> matcherUserId = userId == null ?
argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId);
testLogEntityActionAdditionalInfo(matcherEntityEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName,
actionType, cntTime, extractMatcherAdditionalInfo(additionalInfo)); actionType, cntTime, extractMatcherAdditionalInfo(additionalInfo));
} }
private void testLogEntityActionAdditionalInfo(ArgumentMatcher<HasName> matcherEntity, ArgumentMatcher<EntityId> matcherOriginatorId, private void testLogEntityActionAdditionalInfo(ArgumentMatcher<HasName> matcherEntity, ArgumentMatcher<EntityId> matcherOriginatorId,
TenantId tenantId, CustomerId customerId, UserId userId, String userName, TenantId tenantId, ArgumentMatcher<CustomerId> matcherCustomerId,
ActionType actionType, int cntTime, List<ArgumentMatcher<Object>> matcherAdditionalInfos) { ArgumentMatcher<UserId> matcherUserId, String userName, ActionType actionType,
int cntTime, List<ArgumentMatcher<Object>> matcherAdditionalInfos) {
switch (matcherAdditionalInfos.size()) { switch (matcherAdditionalInfos.size()) {
case 1: case 1:
Mockito.verify(auditLogService, times(cntTime)) Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId), .logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId), Mockito.argThat(matcherCustomerId),
Mockito.eq(userId), Mockito.argThat(matcherUserId),
Mockito.eq(userName), Mockito.eq(userName),
Mockito.argThat(matcherOriginatorId), Mockito.argThat(matcherOriginatorId),
Mockito.argThat(matcherEntity), Mockito.argThat(matcherEntity),
@ -382,8 +419,8 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
case 2: case 2:
Mockito.verify(auditLogService, times(cntTime)) Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId), .logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId), Mockito.argThat(matcherCustomerId),
Mockito.eq(userId), Mockito.argThat(matcherUserId),
Mockito.eq(userName), Mockito.eq(userName),
Mockito.argThat(matcherOriginatorId), Mockito.argThat(matcherOriginatorId),
Mockito.argThat(matcherEntity), Mockito.argThat(matcherEntity),
@ -395,8 +432,8 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
case 3: case 3:
Mockito.verify(auditLogService, times(cntTime)) Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId), .logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId), Mockito.argThat(matcherCustomerId),
Mockito.eq(userId), Mockito.argThat(matcherUserId),
Mockito.eq(userName), Mockito.eq(userName),
Mockito.argThat(matcherOriginatorId), Mockito.argThat(matcherOriginatorId),
Mockito.argThat(matcherEntity), Mockito.argThat(matcherEntity),
@ -409,8 +446,8 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
default: default:
Mockito.verify(auditLogService, times(cntTime)) Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId), .logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId), Mockito.argThat(matcherCustomerId),
Mockito.eq(userId), Mockito.argThat(matcherUserId),
Mockito.eq(userName), Mockito.eq(userName),
Mockito.argThat(matcherOriginatorId), Mockito.argThat(matcherOriginatorId),
Mockito.argThat(matcherEntity), Mockito.argThat(matcherEntity),
@ -420,14 +457,15 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
} }
private void testLogEntityActionAdditionalInfoAny(ArgumentMatcher<HasName> matcherEntity, ArgumentMatcher<EntityId> matcherOriginatorId, private void testLogEntityActionAdditionalInfoAny(ArgumentMatcher<HasName> matcherEntity, ArgumentMatcher<EntityId> matcherOriginatorId,
TenantId tenantId, CustomerId customerId, UserId userId, String userName, TenantId tenantId, ArgumentMatcher<CustomerId> matcherCustomerId,
ArgumentMatcher<UserId> matcherUserId, String userName,
ActionType actionType, int cntTime, int cntAdditionalInfo) { ActionType actionType, int cntTime, int cntAdditionalInfo) {
switch (cntAdditionalInfo) { switch (cntAdditionalInfo) {
case 1: case 1:
Mockito.verify(auditLogService, times(cntTime)) Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId), .logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId), Mockito.argThat(matcherCustomerId),
Mockito.eq(userId), Mockito.argThat(matcherUserId),
Mockito.eq(userName), Mockito.eq(userName),
Mockito.argThat(matcherOriginatorId), Mockito.argThat(matcherOriginatorId),
Mockito.argThat(matcherEntity), Mockito.argThat(matcherEntity),
@ -438,8 +476,8 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
case 2: case 2:
Mockito.verify(auditLogService, times(cntTime)) Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId), .logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId), Mockito.argThat(matcherCustomerId),
Mockito.eq(userId), Mockito.argThat(matcherUserId),
Mockito.eq(userName), Mockito.eq(userName),
Mockito.argThat(matcherOriginatorId), Mockito.argThat(matcherOriginatorId),
Mockito.argThat(matcherEntity), Mockito.argThat(matcherEntity),
@ -451,8 +489,8 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
case 3: case 3:
Mockito.verify(auditLogService, times(cntTime)) Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId), .logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId), Mockito.argThat(matcherCustomerId),
Mockito.eq(userId), Mockito.argThat(matcherUserId),
Mockito.eq(userName), Mockito.eq(userName),
Mockito.argThat(matcherOriginatorId), Mockito.argThat(matcherOriginatorId),
Mockito.argThat(matcherEntity), Mockito.argThat(matcherEntity),
@ -465,8 +503,8 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
default: default:
Mockito.verify(auditLogService, times(cntTime)) Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId), .logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId), Mockito.argThat(matcherCustomerId),
Mockito.eq(userId), Mockito.argThat(matcherUserId),
Mockito.eq(userName), Mockito.eq(userName),
Mockito.argThat(matcherOriginatorId), Mockito.argThat(matcherOriginatorId),
Mockito.argThat(matcherEntity), Mockito.argThat(matcherEntity),
@ -479,12 +517,14 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
CustomerId customerId, UserId userId, String userName, ActionType actionType, CustomerId customerId, UserId userId, String userName, ActionType actionType,
int cntTime, ArgumentMatcher<Exception> matcherError, int cntTime, ArgumentMatcher<Exception> matcherError,
List<ArgumentMatcher<Object>> matcherAdditionalInfos) { List<ArgumentMatcher<Object>> matcherAdditionalInfos) {
ArgumentMatcher<UserId> matcherUserId = userId == null ? argument -> argument.getClass().equals(UserId.class) :
argument -> argument.equals(userId);
switch (matcherAdditionalInfos.size()) { switch (matcherAdditionalInfos.size()) {
case 1: case 1:
Mockito.verify(auditLogService, times(cntTime)) Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId), .logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId), Mockito.eq(customerId),
Mockito.eq(userId), Mockito.argThat(matcherUserId),
Mockito.eq(userName), Mockito.eq(userName),
Mockito.eq(originatorId), Mockito.eq(originatorId),
Mockito.argThat(matcherEntity), Mockito.argThat(matcherEntity),
@ -496,7 +536,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
Mockito.verify(auditLogService, times(cntTime)) Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId), .logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId), Mockito.eq(customerId),
Mockito.eq(userId), Mockito.argThat(matcherUserId),
Mockito.eq(userName), Mockito.eq(userName),
Mockito.eq(originatorId), Mockito.eq(originatorId),
Mockito.argThat(matcherEntity), Mockito.argThat(matcherEntity),
@ -508,7 +548,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
Mockito.verify(auditLogService, times(cntTime)) Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId), .logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId), Mockito.eq(customerId),
Mockito.eq(userId), Mockito.argThat(matcherUserId),
Mockito.eq(userName), Mockito.eq(userName),
Mockito.eq(originatorId), Mockito.eq(originatorId),
Mockito.argThat(matcherEntity), Mockito.argThat(matcherEntity),
@ -522,7 +562,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
Mockito.verify(auditLogService, times(cntTime)) Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId), .logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId), Mockito.eq(customerId),
Mockito.eq(userId), Mockito.argThat(matcherUserId),
Mockito.eq(userName), Mockito.eq(userName),
Mockito.eq(originatorId), Mockito.eq(originatorId),
Mockito.argThat(matcherEntity), Mockito.argThat(matcherEntity),
@ -558,7 +598,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
return result; return result;
} }
private EntityId createEntityId_NULL_UUID(HasName entity) { protected EntityId createEntityId_NULL_UUID(HasName entity) {
return EntityIdFactory.getByTypeAndUuid(entityClassToEntityTypeName(entity), ModelConstants.NULL_UUID); return EntityIdFactory.getByTypeAndUuid(entityClassToEntityTypeName(entity), ModelConstants.NULL_UUID);
} }
@ -571,6 +611,12 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
} }
private String entityClassToEntityTypeName(HasName entity) { private String entityClassToEntityTypeName(HasName entity) {
String entityType = entityClassToString(entity);
return "SAVE_OTA_PACKAGE_INFO_REQUEST".equals(entityType) || "OTA_PACKAGE_INFO".equals(entityType)?
EntityType.OTA_PACKAGE.name().toUpperCase(Locale.ENGLISH) : entityType;
}
private String entityClassToString(HasName entity) {
String className = entity.getClass().toString() String className = entity.getClass().toString()
.substring(entity.getClass().toString().lastIndexOf(".") + 1); .substring(entity.getClass().toString().lastIndexOf(".") + 1);
List str = className.chars() List str = className.chars()

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

@ -285,7 +285,7 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
protected void loginDifferentTenant() throws Exception { protected void loginDifferentTenant() throws Exception {
if (savedDifferentTenant != null) { if (savedDifferentTenant != null) {
login(savedDifferentTenant.getEmail(), TENANT_ADMIN_PASSWORD); login(DIFFERENT_TENANT_ADMIN_EMAIL, DIFFERENT_TENANT_ADMIN_PASSWORD);
} else { } else {
loginSysAdmin(); loginSysAdmin();
@ -422,12 +422,6 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
} }
protected DeviceProfile createDeviceProfile(String name, DeviceProfileTransportConfiguration deviceProfileTransportConfiguration) { protected DeviceProfile createDeviceProfile(String name, DeviceProfileTransportConfiguration deviceProfileTransportConfiguration) {
return createDeviceProfile(name, deviceProfileTransportConfiguration, null);
}
protected DeviceProfile createDeviceProfile(String name,
DeviceProfileTransportConfiguration deviceProfileTransportConfiguration,
QueueId defaultQueueId) {
DeviceProfile deviceProfile = new DeviceProfile(); DeviceProfile deviceProfile = new DeviceProfile();
deviceProfile.setName(name); deviceProfile.setName(name);
deviceProfile.setType(DeviceProfileType.DEFAULT); deviceProfile.setType(DeviceProfileType.DEFAULT);
@ -445,7 +439,6 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
deviceProfile.setProfileData(deviceProfileData); deviceProfile.setProfileData(deviceProfileData);
deviceProfile.setDefault(false); deviceProfile.setDefault(false);
deviceProfile.setDefaultRuleChainId(null); deviceProfile.setDefaultRuleChainId(null);
deviceProfile.setDefaultQueueId(defaultQueueId);
return deviceProfile; return deviceProfile;
} }

44
application/src/test/java/org/thingsboard/server/controller/BaseAdminControllerTest.java

@ -18,14 +18,29 @@ package org.thingsboard.server.controller;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
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.AdminSettings;
import org.thingsboard.server.service.mail.DefaultMailService;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
public abstract class BaseAdminControllerTest extends AbstractControllerTest { public abstract class BaseAdminControllerTest extends AbstractControllerTest {
@Autowired
MailService mailService;
@Autowired
DefaultMailService defaultMailService;
@Test @Test
public void testFindAdminSettingsByKey() throws Exception { public void testFindAdminSettingsByKey() throws Exception {
loginSysAdmin(); loginSysAdmin();
@ -100,5 +115,28 @@ public abstract class BaseAdminControllerTest extends AbstractControllerTest {
doPost("/api/admin/settings/testMail", adminSettings) doPost("/api/admin/settings/testMail", adminSettings)
.andExpect(status().isOk()); .andExpect(status().isOk());
} }
@Test
public void testSendTestMailTimeout() throws Exception {
loginSysAdmin();
AdminSettings adminSettings = doGet("/api/admin/settings/mail", AdminSettings.class);
ObjectNode objectNode = JacksonUtil.fromString(adminSettings.getJsonValue().toString(), ObjectNode.class);
objectNode.put("smtpHost", "mail.gandi.net");
objectNode.put("timeout", 1_000);
objectNode.put("username", "username");
objectNode.put("password", "password");
adminSettings.setJsonValue(objectNode);
Mockito.doAnswer((invocations) -> {
var jsonConfig = (JsonNode) invocations.getArgument(0);
var email = (String) invocations.getArgument(1);
defaultMailService.sendTestMail(jsonConfig, email);
return null;
}).when(mailService).sendTestMail(Mockito.any(), Mockito.anyString());
doPost("/api/admin/settings/testMail", adminSettings).andExpect(status().is5xxServerError());
Mockito.doNothing().when(mailService).sendTestMail(Mockito.any(), Mockito.any());
}
} }

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

@ -283,6 +283,7 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest
doDelete("/api/tenant/" + savedTenant2.getId().getId().toString()) doDelete("/api/tenant/" + savedTenant2.getId().getId().toString())
.andExpect(status().isForbidden()) .andExpect(status().isForbidden())
.andExpect(statusReason(containsString(msgErrorPermission))); .andExpect(statusReason(containsString(msgErrorPermission)));
testNotifyEntityNever(savedDashboard.getId(), savedDashboard); testNotifyEntityNever(savedDashboard.getId(), savedDashboard);
loginSysAdmin(); loginSysAdmin();

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

@ -27,10 +27,14 @@ import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.common.util.ThingsBoardExecutors;
import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.EntitySubtype; 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.Tenant; import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.audit.ActionType;
@ -49,6 +53,7 @@ import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.exception.DeviceCredentialsValidationException; import org.thingsboard.server.dao.exception.DeviceCredentialsValidationException;
import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.service.gateway_device.GatewayNotificationsService;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -56,12 +61,15 @@ import java.util.concurrent.TimeUnit;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE;
import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE;
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
public abstract class BaseDeviceControllerTest extends AbstractControllerTest { public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
static final TypeReference<PageData<Device>> PAGE_DATA_DEVICE_TYPE_REF = new TypeReference<>() { static final TypeReference<PageData<Device>> PAGE_DATA_DEVICE_TYPE_REF = new TypeReference<>() {};
};
ListeningExecutorService executor; ListeningExecutorService executor;
@ -71,6 +79,9 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
private Tenant savedTenant; private Tenant savedTenant;
private User tenantAdmin; private User tenantAdmin;
@SpyBean
private GatewayNotificationsService gatewayNotificationsService;
@Before @Before
public void beforeTest() throws Exception { public void beforeTest() throws Exception {
executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(8, getClass())); executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(8, getClass()));
@ -113,6 +124,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
Device savedDevice = doPost("/api/device", device, Device.class); Device savedDevice = doPost("/api/device", device, Device.class);
Device oldDevice = new Device(savedDevice); Device oldDevice = new Device(savedDevice);
testNotifyEntityOneTimeMsgToEdgeServiceNever(savedDevice, savedDevice.getId(), savedDevice.getId(), testNotifyEntityOneTimeMsgToEdgeServiceNever(savedDevice, savedDevice.getId(), savedDevice.getId(),
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.ADDED); ActionType.ADDED);
@ -203,7 +215,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
String savedDeviceIdStr = savedDevice.getId().getId().toString(); String savedDeviceIdStr = savedDevice.getId().getId().toString();
doPost("/api/device", savedDevice) doPost("/api/device", savedDevice)
.andExpect( status().isNotFound()) .andExpect(status().isNotFound())
.andExpect(statusReason(containsString(msgErrorNoFound("Device", savedDeviceIdStr)))); .andExpect(statusReason(containsString(msgErrorNoFound("Device", savedDeviceIdStr))));
testNotifyEntityNever(savedDevice.getId(), savedDevice); testNotifyEntityNever(savedDevice.getId(), savedDevice);
@ -221,6 +233,66 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
deleteDifferentTenant(); deleteDifferentTenant();
} }
@Test
public void testSaveDeviceWithProfileFromDifferentTenant() throws Exception {
loginDifferentTenant();
DeviceProfile differentProfile = createDeviceProfile("Different profile");
differentProfile = doPost("/api/deviceProfile", differentProfile, DeviceProfile.class);
loginTenantAdmin();
Device device = new Device();
device.setName("My device");
device.setDeviceProfileId(differentProfile.getId());
doPost("/api/device", device).andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Device can`t be referencing to device profile from different tenant!")));
}
@Test
public void testSaveDeviceWithFirmwareFromDifferentTenant() throws Exception {
loginDifferentTenant();
DeviceProfile differentProfile = createDeviceProfile("Different profile");
differentProfile = doPost("/api/deviceProfile", differentProfile, DeviceProfile.class);
SaveOtaPackageInfoRequest firmwareInfo = new SaveOtaPackageInfoRequest();
firmwareInfo.setDeviceProfileId(differentProfile.getId());
firmwareInfo.setType(FIRMWARE);
firmwareInfo.setTitle("title");
firmwareInfo.setVersion("1.0");
firmwareInfo.setUrl("test.url");
firmwareInfo.setUsesUrl(true);
OtaPackageInfo savedFw = doPost("/api/otaPackage", firmwareInfo, OtaPackageInfo.class);
loginTenantAdmin();
Device device = new Device();
device.setName("My device");
device.setType("default");
device.setFirmwareId(savedFw.getId());
doPost("/api/device", device).andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Can't assign firmware from different tenant!")));
}
@Test
public void testSaveDeviceWithSoftwareFromDifferentTenant() throws Exception {
loginDifferentTenant();
DeviceProfile differentProfile = createDeviceProfile("Different profile");
differentProfile = doPost("/api/deviceProfile", differentProfile, DeviceProfile.class);
SaveOtaPackageInfoRequest softwareInfo = new SaveOtaPackageInfoRequest();
softwareInfo.setDeviceProfileId(differentProfile.getId());
softwareInfo.setType(SOFTWARE);
softwareInfo.setTitle("title");
softwareInfo.setVersion("1.0");
softwareInfo.setUrl("test.url");
softwareInfo.setUsesUrl(true);
OtaPackageInfo savedSw = doPost("/api/otaPackage", softwareInfo, OtaPackageInfo.class);
loginTenantAdmin();
Device device = new Device();
device.setName("My device");
device.setType("default");
device.setSoftwareId(savedSw.getId());
doPost("/api/device", device).andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Can't assign software from different tenant!")));
}
@Test @Test
public void testFindDeviceById() throws Exception { public void testFindDeviceById() throws Exception {
Device device = new Device(); Device device = new Device();
@ -248,8 +320,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
} }
testNotifyManyEntityManyTimeMsgToEdgeServiceNever(new Device(), new Device(), testNotifyManyEntityManyTimeMsgToEdgeServiceNever(new Device(), new Device(),
savedTenant.getId(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.ADDED, cntEntity); ActionType.ADDED, cntEntity);
testNotificationUpdateGatewayNever(); testNotificationUpdateGatewayNever();
@ -813,7 +884,9 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new Device(), new Device(), testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new Device(), new Device(),
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.ADDED, ActionType.ASSIGNED_TO_CUSTOMER, cntEntity, cntEntity, cntEntity * 2); ActionType.ADDED, ActionType.ASSIGNED_TO_CUSTOMER, cntEntity, cntEntity, cntEntity * 2);
Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService);
testNotificationUpdateGatewayNever(); testNotificationUpdateGatewayNever();
Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService);
List<Device> loadedDevices = new ArrayList<>(cntEntity); List<Device> loadedDevices = new ArrayList<>(cntEntity);
PageLink pageLink = new PageLink(23); PageLink pageLink = new PageLink(23);
@ -828,8 +901,6 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
assertThat(devices).containsExactlyInAnyOrderElementsOf(loadedDevices); assertThat(devices).containsExactlyInAnyOrderElementsOf(loadedDevices);
Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService);
deleteEntitiesAsync("/api/customer/device/", loadedDevices, executor).get(TIMEOUT, TimeUnit.SECONDS); deleteEntitiesAsync("/api/customer/device/", loadedDevices, executor).get(TIMEOUT, TimeUnit.SECONDS);
testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAnyAdditionalInfoAny(new Device(), new Device(), testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAnyAdditionalInfoAny(new Device(), new Device(),
@ -1116,4 +1187,20 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
Assert.assertEquals(0, pageData.getData().size()); Assert.assertEquals(0, pageData.getData().size());
} }
protected void testNotificationUpdateGatewayOneTime(Device device, Device oldDevice) {
Mockito.verify(gatewayNotificationsService, times(1)).onDeviceUpdated(Mockito.eq(device), Mockito.eq(oldDevice));
}
protected void testNotificationUpdateGatewayNever() {
Mockito.verify(gatewayNotificationsService, never()).onDeviceUpdated(Mockito.any(Device.class), Mockito.any(Device.class));
}
protected void testNotificationDeleteGatewayOneTime(Device device) {
Mockito.verify(gatewayNotificationsService, times(1)).onDeviceDeleted(device);
}
protected void testNotificationDeleteGatewayNever() {
Mockito.verify(gatewayNotificationsService, never()).onDeviceDeleted(Mockito.any(Device.class));
}
} }

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

@ -28,12 +28,16 @@ import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceProfileInfo; import org.thingsboard.server.common.data.DeviceProfileInfo;
import org.thingsboard.server.common.data.DeviceProfileProvisionType; import org.thingsboard.server.common.data.DeviceProfileProvisionType;
import org.thingsboard.server.common.data.DeviceProfileType; import org.thingsboard.server.common.data.DeviceProfileType;
import org.thingsboard.server.common.data.DeviceTransportType; 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.Tenant; import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.audit.ActionType;
@ -44,6 +48,7 @@ import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadCo
import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink; 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.common.data.security.Authority;
import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.DataValidationException;
@ -58,6 +63,8 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE;
import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE;
public abstract class BaseDeviceProfileControllerTest extends AbstractControllerTest { public abstract class BaseDeviceProfileControllerTest extends AbstractControllerTest {
@ -170,6 +177,25 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
Assert.assertEquals(savedDeviceProfile.getId(), foundDeviceProfileInfo.getId()); Assert.assertEquals(savedDeviceProfile.getId(), foundDeviceProfileInfo.getId());
Assert.assertEquals(savedDeviceProfile.getName(), foundDeviceProfileInfo.getName()); Assert.assertEquals(savedDeviceProfile.getName(), foundDeviceProfileInfo.getName());
Assert.assertEquals(savedDeviceProfile.getType(), foundDeviceProfileInfo.getType()); Assert.assertEquals(savedDeviceProfile.getType(), foundDeviceProfileInfo.getType());
Customer customer = new Customer();
customer.setTitle("Customer");
customer.setTenantId(savedTenant.getId());
Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
User customerUser = new User();
customerUser.setAuthority(Authority.CUSTOMER_USER);
customerUser.setTenantId(savedTenant.getId());
customerUser.setCustomerId(savedCustomer.getId());
customerUser.setEmail("customer2@thingsboard.org");
createUserAndLogin(customerUser, "customer");
foundDeviceProfileInfo = doGet("/api/deviceProfileInfo/" + savedDeviceProfile.getId().getId().toString(), DeviceProfileInfo.class);
Assert.assertNotNull(foundDeviceProfileInfo);
Assert.assertEquals(savedDeviceProfile.getId(), foundDeviceProfileInfo.getId());
Assert.assertEquals(savedDeviceProfile.getName(), foundDeviceProfileInfo.getName());
Assert.assertEquals(savedDeviceProfile.getType(), foundDeviceProfileInfo.getType());
} }
@Test @Test
@ -325,6 +351,80 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
testNotifyEntityNever(savedDeviceProfile.getId(), savedDeviceProfile); testNotifyEntityNever(savedDeviceProfile.getId(), savedDeviceProfile);
} }
@Test
public void testSaveDeviceProfileWithRuleChainFromDifferentTenant() throws Exception {
loginDifferentTenant();
RuleChain ruleChain = new RuleChain();
ruleChain.setName("Different rule chain");
RuleChain savedRuleChain = doPost("/api/ruleChain", ruleChain, RuleChain.class);
loginTenantAdmin();
DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
deviceProfile.setDefaultRuleChainId(savedRuleChain.getId());
doPost("/api/deviceProfile", deviceProfile).andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Can't assign rule chain from different tenant!")));
}
@Test
public void testSaveDeviceProfileWithDashboardFromDifferentTenant() throws Exception {
loginDifferentTenant();
Dashboard dashboard = new Dashboard();
dashboard.setTitle("Different dashboard");
Dashboard savedDashboard = doPost("/api/dashboard", dashboard, Dashboard.class);
loginTenantAdmin();
DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
deviceProfile.setDefaultDashboardId(savedDashboard.getId());
doPost("/api/deviceProfile", deviceProfile).andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Can't assign dashboard from different tenant!")));
}
@Test
public void testSaveDeviceProfileWithFirmwareFromDifferentTenant() throws Exception {
loginDifferentTenant();
DeviceProfile differentProfile = createDeviceProfile("Different profile");
differentProfile = doPost("/api/deviceProfile", differentProfile, DeviceProfile.class);
SaveOtaPackageInfoRequest firmwareInfo = new SaveOtaPackageInfoRequest();
firmwareInfo.setDeviceProfileId(differentProfile.getId());
firmwareInfo.setType(FIRMWARE);
firmwareInfo.setTitle("title");
firmwareInfo.setVersion("1.0");
firmwareInfo.setUrl("test.url");
firmwareInfo.setUsesUrl(true);
OtaPackageInfo savedFw = doPost("/api/otaPackage", firmwareInfo, OtaPackageInfo.class);
loginTenantAdmin();
DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
deviceProfile.setFirmwareId(savedFw.getId());
doPost("/api/deviceProfile", deviceProfile).andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Can't assign firmware from different tenant!")));
}
@Test
public void testSaveDeviceProfileWithSoftwareFromDifferentTenant() throws Exception {
loginDifferentTenant();
DeviceProfile differentProfile = createDeviceProfile("Different profile");
differentProfile = doPost("/api/deviceProfile", differentProfile, DeviceProfile.class);
SaveOtaPackageInfoRequest softwareInfo = new SaveOtaPackageInfoRequest();
softwareInfo.setDeviceProfileId(differentProfile.getId());
softwareInfo.setType(SOFTWARE);
softwareInfo.setTitle("title");
softwareInfo.setVersion("1.0");
softwareInfo.setUrl("test.url");
softwareInfo.setUsesUrl(true);
OtaPackageInfo savedSw = doPost("/api/otaPackage", softwareInfo, OtaPackageInfo.class);
loginTenantAdmin();
DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
deviceProfile.setSoftwareId(savedSw.getId());
doPost("/api/deviceProfile", deviceProfile).andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Can't assign software from different tenant!")));
}
@Test @Test
public void testDeleteDeviceProfile() throws Exception { public void testDeleteDeviceProfile() throws Exception {
DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"); DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
@ -367,6 +467,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new DeviceProfile(), new DeviceProfile(), testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new DeviceProfile(), new DeviceProfile(),
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.ADDED, ActionType.ADDED, cntEntity, cntEntity, cntEntity); ActionType.ADDED, ActionType.ADDED, cntEntity, cntEntity, cntEntity);
Mockito.reset(tbClusterService, auditLogService);
List<DeviceProfile> loadedDeviceProfiles = new ArrayList<>(); List<DeviceProfile> loadedDeviceProfiles = new ArrayList<>();
pageLink = new PageLink(17); pageLink = new PageLink(17);
@ -988,7 +1089,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
.andExpect(status().isBadRequest()) .andExpect(status().isBadRequest())
.andExpect(statusReason(containsString(errorMsg))); .andExpect(statusReason(containsString(errorMsg)));
testNotifyEntityEqualsOneTimeServiceNeverError(deviceProfile,savedTenant.getId(), testNotifyEntityEqualsOneTimeServiceNeverError(deviceProfile, savedTenant.getId(),
tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(errorMsg)); tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(errorMsg));
} }
@ -1003,7 +1104,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
.andExpect(status().isBadRequest()) .andExpect(status().isBadRequest())
.andExpect(statusReason(containsString(errorMsg))); .andExpect(statusReason(containsString(errorMsg)));
testNotifyEntityEqualsOneTimeServiceNeverError(deviceProfile,savedTenant.getId(), testNotifyEntityEqualsOneTimeServiceNeverError(deviceProfile, savedTenant.getId(),
tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(errorMsg)); tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(errorMsg));
} }

6
application/src/test/java/org/thingsboard/server/controller/BaseEntityRelationControllerTest.java

@ -354,10 +354,9 @@ public abstract class BaseEntityRelationControllerTest extends AbstractControlle
doDelete(url) doDelete(url)
.andExpect(status().isNotFound()) .andExpect(status().isNotFound())
.andExpect(statusReason(containsString("Requested item wasn't found!"))); .andExpect(statusReason(containsString(msgErrorNotFound)));
testNotifyEntityNever(mainDevice.getId(), null); testNotifyEntityNever(mainDevice.getId(), null);
} }
@Test @Test
@ -377,7 +376,7 @@ public abstract class BaseEntityRelationControllerTest extends AbstractControlle
doDelete(url) doDelete(url)
.andExpect(status().isNotFound()) .andExpect(status().isNotFound())
.andExpect(statusReason(containsString("Requested item wasn't found!"))); .andExpect(statusReason(containsString(msgErrorNotFound)));
testNotifyEntityNever(mainDevice.getId(), null); testNotifyEntityNever(mainDevice.getId(), null);
} }
@ -570,7 +569,6 @@ public abstract class BaseEntityRelationControllerTest extends AbstractControlle
.andExpect(status().isNotFound()) .andExpect(status().isNotFound())
.andExpect(statusReason(containsString(msgErrorNoFound("Device", relation.getFrom().getId().toString())))); .andExpect(statusReason(containsString(msgErrorNoFound("Device", relation.getFrom().getId().toString()))));
deleteDifferentTenant(); deleteDifferentTenant();
} }

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

@ -32,6 +32,7 @@ import org.junit.After;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.TestPropertySource;
import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.ResultActions;
import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.common.util.ThingsBoardExecutors;
@ -41,6 +42,7 @@ import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.EntityViewInfo; import org.thingsboard.server.common.data.EntityViewInfo;
import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User; 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.edge.Edge;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.objects.AttributesEntityView; import org.thingsboard.server.common.data.objects.AttributesEntityView;
@ -52,6 +54,7 @@ import org.thingsboard.server.common.data.query.EntityKey;
import org.thingsboard.server.common.data.query.EntityKeyType; import org.thingsboard.server.common.data.query.EntityKeyType;
import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.ModelConstants;
import java.util.ArrayList; import java.util.ArrayList;
@ -92,7 +95,6 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
@Before @Before
public void beforeTest() throws Exception { public void beforeTest() throws Exception {
log.debug("beforeTest");
executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(8, getClass())); executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(8, getClass()));
loginTenantAdmin(); loginTenantAdmin();
@ -126,6 +128,9 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
@Test @Test
public void testSaveEntityView() throws Exception { public void testSaveEntityView() throws Exception {
String name = "Test entity view"; String name = "Test entity view";
Mockito.reset(tbClusterService, auditLogService);
EntityView savedView = getNewSavedEntityView(name); EntityView savedView = getNewSavedEntityView(name);
Assert.assertNotNull(savedView); Assert.assertNotNull(savedView);
@ -140,6 +145,12 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
assertEquals(savedView, foundEntityView); assertEquals(savedView, foundEntityView);
testBroadcastEntityStateChangeEventTime(foundEntityView.getId(), tenantId, 1);
testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(foundEntityView, foundEntityView,
tenantId, tenantAdminCustomerId, tenantAdminUserId, TENANT_ADMIN_EMAIL,
ActionType.ADDED, ActionType.ADDED, 1, 0, 1);
Mockito.reset(tbClusterService, auditLogService);
savedView.setName("New test entity view"); savedView.setName("New test entity view");
doPost("/api/entityView", savedView, EntityView.class); doPost("/api/entityView", savedView, EntityView.class);
@ -147,23 +158,57 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
assertEquals(savedView, foundEntityView); assertEquals(savedView, foundEntityView);
doGet("/api/tenant/entityViews?entityViewName=" + name, EntityView.class, status().isNotFound()); testBroadcastEntityStateChangeEventTime(foundEntityView.getId(), tenantId, 1);
testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(foundEntityView, foundEntityView,
tenantId, tenantAdminCustomerId, tenantAdminUserId, TENANT_ADMIN_EMAIL,
ActionType.UPDATED, ActionType.UPDATED, 1, 1, 5);
doGet("/api/tenant/entityViews?entityViewName=" + name)
.andExpect(status().isNotFound())
.andExpect(statusReason(containsString(msgErrorNotFound)));
} }
@Test @Test
public void testSaveEntityViewWithViolationOfValidation() throws Exception { public void testSaveEntityViewWithViolationOfValidation() throws Exception {
EntityView entityView = createEntityView(RandomStringUtils.randomAlphabetic(300), 0, 0); EntityView entityView = createEntityView(RandomStringUtils.randomAlphabetic(300), 0, 0);
doPost("/api/entityView", entityView).andExpect(statusReason(containsString("length of name must be equal or less than 255")));
Mockito.reset(tbClusterService, auditLogService);
String msgError = msgErrorFieldLength("name");
doPost("/api/entityView", entityView)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString(msgError)));
testNotifyEntityEqualsOneTimeServiceNeverError(entityView,
tenantId, tenantAdminUserId, TENANT_ADMIN_EMAIL,
ActionType.ADDED, new DataValidationException(msgError));
Mockito.reset(tbClusterService, auditLogService);
entityView.setName("Normal name"); entityView.setName("Normal name");
msgError = msgErrorFieldLength("type");
entityView.setType(RandomStringUtils.randomAlphabetic(300)); entityView.setType(RandomStringUtils.randomAlphabetic(300));
doPost("/api/entityView", entityView).andExpect(statusReason(containsString("length of type must be equal or less than 255"))); doPost("/api/entityView", entityView)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString(msgError)));
testNotifyEntityEqualsOneTimeServiceNeverError(entityView,
tenantId, tenantAdminUserId, TENANT_ADMIN_EMAIL,
ActionType.ADDED, new DataValidationException(msgError));
} }
@Test @Test
public void testUpdateEntityViewFromDifferentTenant() throws Exception { public void testUpdateEntityViewFromDifferentTenant() throws Exception {
EntityView savedView = getNewSavedEntityView("Test entity view"); EntityView savedView = getNewSavedEntityView("Test entity view");
loginDifferentTenant(); loginDifferentTenant();
doPost("/api/entityView", savedView, EntityView.class, status().isForbidden());
Mockito.reset(tbClusterService, auditLogService);
doPost("/api/entityView", savedView)
.andExpect(status().isForbidden())
.andExpect(statusReason(containsString(msgErrorPermission)));
testNotifyEntityNever(savedView.getId(), savedView);
deleteDifferentTenant(); deleteDifferentTenant();
} }
@ -174,20 +219,36 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
view.setCustomerId(customer.getId()); view.setCustomerId(customer.getId());
EntityView savedView = doPost("/api/entityView", view, EntityView.class); EntityView savedView = doPost("/api/entityView", view, EntityView.class);
doDelete("/api/entityView/" + savedView.getId().getId().toString()) Mockito.reset(tbClusterService, auditLogService);
String entityIdStr = savedView.getId().getId().toString();
doDelete("/api/entityView/" + entityIdStr)
.andExpect(status().isOk()); .andExpect(status().isOk());
doGet("/api/entityView/" + savedView.getId().getId().toString()) testNotifyEntityBroadcastEntityStateChangeEventOneTimeMsgToEdgeServiceNever(savedView, savedView.getId(), savedView.getId(),
.andExpect(status().isNotFound()); tenantId, view.getCustomerId(), tenantAdminUserId, TENANT_ADMIN_EMAIL,
ActionType.DELETED, entityIdStr);
doGet("/api/entityView/" + entityIdStr)
.andExpect(status().isNotFound())
.andExpect(statusReason(containsString(msgErrorNoFound("Entity view",entityIdStr))));
} }
@Test @Test
public void testSaveEntityViewWithEmptyName() throws Exception { public void testSaveEntityViewWithEmptyName() throws Exception {
EntityView entityView = new EntityView(); EntityView entityView = new EntityView();
entityView.setType("default"); entityView.setType("default");
Mockito.reset(tbClusterService, auditLogService);
String msgError = "Entity view name " + msgErrorShouldBeSpecified;
doPost("/api/entityView", entityView) doPost("/api/entityView", entityView)
.andExpect(status().isBadRequest()) .andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Entity view name should be specified!"))); .andExpect(statusReason(containsString(msgError)));
testNotifyEntityEqualsOneTimeServiceNeverError(entityView,
tenantId, tenantAdminUserId, TENANT_ADMIN_EMAIL,
ActionType.ADDED, new DataValidationException(msgError));
} }
@Test @Test
@ -195,8 +256,17 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
EntityView view = getNewSavedEntityView("Test entity view"); EntityView view = getNewSavedEntityView("Test entity view");
Customer savedCustomer = doPost("/api/customer", getNewCustomer("My customer"), Customer.class); Customer savedCustomer = doPost("/api/customer", getNewCustomer("My customer"), Customer.class);
view.setCustomerId(savedCustomer.getId()); view.setCustomerId(savedCustomer.getId());
Mockito.reset(tbClusterService, auditLogService);
EntityView savedView = doPost("/api/entityView", view, EntityView.class); EntityView savedView = doPost("/api/entityView", view, EntityView.class);
testBroadcastEntityStateChangeEventTime(savedView.getId(), tenantId, 1);
testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(savedView, savedView,
tenantId, tenantAdminCustomerId, tenantAdminUserId, TENANT_ADMIN_EMAIL,
ActionType.UPDATED, ActionType.UPDATED, 1, 1, 5);
Mockito.reset(tbClusterService, auditLogService);
EntityView assignedView = doPost( EntityView assignedView = doPost(
"/api/customer/" + savedCustomer.getId().getId().toString() + "/entityView/" + savedView.getId().getId().toString(), "/api/customer/" + savedCustomer.getId().getId().toString() + "/entityView/" + savedView.getId().getId().toString(),
EntityView.class); EntityView.class);
@ -205,18 +275,38 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
EntityView foundView = doGet("/api/entityView/" + savedView.getId().getId().toString(), EntityView.class); EntityView foundView = doGet("/api/entityView/" + savedView.getId().getId().toString(), EntityView.class);
assertEquals(savedCustomer.getId(), foundView.getCustomerId()); assertEquals(savedCustomer.getId(), foundView.getCustomerId());
testBroadcastEntityStateChangeEventNever(foundView.getId());
testNotifyEntityAllOneTime(foundView, foundView.getId(), foundView.getId(),
tenantId, foundView.getCustomerId(), tenantAdminUserId, TENANT_ADMIN_EMAIL,
ActionType.ASSIGNED_TO_CUSTOMER,
foundView.getId().getId().toString(), foundView.getCustomerId().getId().toString(), savedCustomer.getTitle());
EntityView unAssignedView = doDelete("/api/customer/entityView/" + savedView.getId().getId().toString(), EntityView.class); EntityView unAssignedView = doDelete("/api/customer/entityView/" + savedView.getId().getId().toString(), EntityView.class);
assertEquals(ModelConstants.NULL_UUID, unAssignedView.getCustomerId().getId()); assertEquals(ModelConstants.NULL_UUID, unAssignedView.getCustomerId().getId());
foundView = doGet("/api/entityView/" + savedView.getId().getId().toString(), EntityView.class); foundView = doGet("/api/entityView/" + savedView.getId().getId().toString(), EntityView.class);
assertEquals(ModelConstants.NULL_UUID, foundView.getCustomerId().getId()); assertEquals(ModelConstants.NULL_UUID, foundView.getCustomerId().getId());
testBroadcastEntityStateChangeEventNever(foundView.getId());
testNotifyEntityAllOneTime(unAssignedView, savedView.getId(), savedView.getId(),
tenantId, savedView.getCustomerId(), tenantAdminUserId, TENANT_ADMIN_EMAIL,
ActionType.UNASSIGNED_FROM_CUSTOMER,
savedView.getCustomerId().getId().toString(), savedCustomer.getTitle());
} }
@Test @Test
public void testAssignEntityViewToNonExistentCustomer() throws Exception { public void testAssignEntityViewToNonExistentCustomer() throws Exception {
EntityView savedView = getNewSavedEntityView("Test entity view"); EntityView savedView = getNewSavedEntityView("Test entity view");
doPost("/api/customer/" + Uuids.timeBased().toString() + "/device/" + savedView.getId().getId().toString())
.andExpect(status().isNotFound()); Mockito.reset(tbClusterService, auditLogService);
String customerIdStr = Uuids.timeBased().toString();
String msgError = msgErrorNoFound("Customer", customerIdStr);
doPost("/api/customer/" + customerIdStr + "/device/" + savedView.getId().getId().toString())
.andExpect(status().isNotFound())
.andExpect(statusReason(containsString(msgError)));
testNotifyEntityNever(savedView.getId(), savedView);
} }
@Test @Test
@ -242,8 +332,13 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
EntityView savedView = getNewSavedEntityView("Test entity view"); EntityView savedView = getNewSavedEntityView("Test entity view");
Mockito.reset(tbClusterService, auditLogService);
doPost("/api/customer/" + savedCustomer.getId().getId().toString() + "/entityView/" + savedView.getId().getId().toString()) doPost("/api/customer/" + savedCustomer.getId().getId().toString() + "/entityView/" + savedView.getId().getId().toString())
.andExpect(status().isForbidden()); .andExpect(status().isForbidden())
.andExpect(statusReason(containsString(msgErrorPermission)));
testNotifyEntityNever(savedView.getId(), savedView);
loginSysAdmin(); loginSysAdmin();
@ -257,8 +352,11 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
CustomerId customerId = customer.getId(); CustomerId customerId = customer.getId();
String urlTemplate = "/api/customer/" + customerId.getId().toString() + "/entityViewInfos?"; String urlTemplate = "/api/customer/" + customerId.getId().toString() + "/entityViewInfos?";
List<ListenableFuture<EntityViewInfo>> viewFutures = new ArrayList<>(128); Mockito.reset(tbClusterService, auditLogService);
for (int i = 0; i < 128; i++) {
int cntEntity = 128;
List<ListenableFuture<EntityViewInfo>> viewFutures = new ArrayList<>(cntEntity);
for (int i = 0; i < cntEntity; i++) {
String entityName = "Test entity view " + i; String entityName = "Test entity view " + i;
viewFutures.add(executor.submit(() -> viewFutures.add(executor.submit(() ->
new EntityViewInfo(doPost("/api/customer/" + customerId.getId().toString() + "/entityView/" new EntityViewInfo(doPost("/api/customer/" + customerId.getId().toString() + "/entityView/"
@ -269,6 +367,15 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
List<EntityViewInfo> loadedViews = loadListOfInfo(new PageLink(23), urlTemplate); List<EntityViewInfo> loadedViews = loadListOfInfo(new PageLink(23), urlTemplate);
assertThat(entityViewInfos).containsExactlyInAnyOrderElementsOf(loadedViews); assertThat(entityViewInfos).containsExactlyInAnyOrderElementsOf(loadedViews);
testNotifyEntityBroadcastEntityStateChangeEventMany(new EntityView(), new EntityView(),
tenantId, tenantAdminCustomerId, tenantAdminUserId, TENANT_ADMIN_EMAIL,
ActionType.ADDED, ActionType.ADDED, cntEntity, 0, cntEntity*2, 0);
testNotifyEntityBroadcastEntityStateChangeEventMany(new EntityView(), new EntityView(),
tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL,
ActionType.ASSIGNED_TO_CUSTOMER, ActionType.ASSIGNED_TO_CUSTOMER, cntEntity, cntEntity,
cntEntity*2, 3);
} }
@Test @Test
@ -289,12 +396,21 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
assertThat(namesOfView2).as(name2).containsExactlyInAnyOrderElementsOf(loadedNamesOfView2); assertThat(namesOfView2).as(name2).containsExactlyInAnyOrderElementsOf(loadedNamesOfView2);
deleteFutures.clear(); deleteFutures.clear();
Mockito.reset(tbClusterService, auditLogService);
int cntEntity = loadedNamesOfView1.size();
for (EntityView view : loadedNamesOfView1) { for (EntityView view : loadedNamesOfView1) {
deleteFutures.add(executor.submit(() -> deleteFutures.add(executor.submit(() ->
doDelete("/api/customer/entityView/" + view.getId().getId().toString()).andExpect(status().isOk()))); doDelete("/api/customer/entityView/" + view.getId().getId().toString()).andExpect(status().isOk())));
} }
Futures.allAsList(deleteFutures).get(TIMEOUT, SECONDS); Futures.allAsList(deleteFutures).get(TIMEOUT, SECONDS);
testBroadcastEntityStateChangeEventNever(loadedNamesOfView1.get(0).getId());
testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAnyAdditionalInfoAny(new EntityView(), new EntityView(),
tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL,
ActionType.UNASSIGNED_FROM_CUSTOMER, ActionType.UNASSIGNED_FROM_CUSTOMER, cntEntity, cntEntity, 2);
PageData<EntityView> pageData = doGetTypedWithPageLink(urlTemplate, PAGE_DATA_ENTITY_VIEW_TYPE_REF, PageData<EntityView> pageData = doGetTypedWithPageLink(urlTemplate, PAGE_DATA_ENTITY_VIEW_TYPE_REF,
new PageLink(4, 0, name1)); new PageLink(4, 0, name1));
Assert.assertFalse(pageData.hasNext()); Assert.assertFalse(pageData.hasNext());
@ -370,10 +486,8 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
Set<String> expectedActualAttributesSet = Set.of("caKey1", "caKey2", "caKey3", "caKey4"); Set<String> expectedActualAttributesSet = Set.of("caKey1", "caKey2", "caKey3", "caKey4");
Set<String> actualAttributesSet = Set<String> actualAttributesSet =
putAttributesAndWait("{\"caKey1\":\"value1\", \"caKey2\":true, \"caKey3\":42.0, \"caKey4\":73}", expectedActualAttributesSet); putAttributesAndWait("{\"caKey1\":\"value1\", \"caKey2\":true, \"caKey3\":42.0, \"caKey4\":73}", expectedActualAttributesSet);
log.debug("got correct actualAttributesSet, saving new entity view...");
EntityView savedView = getNewSavedEntityView("Test entity view"); EntityView savedView = getNewSavedEntityView("Test entity view");
log.debug("fetching entity view telemetry...");
List<Map<String, Object>> values = await("telemetry/ENTITY_VIEW") List<Map<String, Object>> values = await("telemetry/ENTITY_VIEW")
.atMost(TIMEOUT, SECONDS) .atMost(TIMEOUT, SECONDS)
.until(() -> doGetAsyncTyped("/api/plugins/telemetry/ENTITY_VIEW/" + savedView.getId().getId().toString() + .until(() -> doGetAsyncTyped("/api/plugins/telemetry/ENTITY_VIEW/" + savedView.getId().getId().toString() +
@ -381,7 +495,6 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
}), }),
x -> x.size() >= expectedActualAttributesSet.size()); x -> x.size() >= expectedActualAttributesSet.size());
log.debug("asserting...");
assertEquals("value1", getValue(values, "caKey1")); assertEquals("value1", getValue(values, "caKey1"));
assertEquals(true, getValue(values, "caKey2")); assertEquals(true, getValue(values, "caKey2"));
assertEquals(42.0, getValue(values, "caKey3")); assertEquals(42.0, getValue(values, "caKey3"));
@ -526,7 +639,6 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
getWsClient().subscribeLatestUpdate(keysToSubscribe, dtf); getWsClient().subscribeLatestUpdate(keysToSubscribe, dtf);
String viewDeviceId = testDevice.getId().getId().toString(); String viewDeviceId = testDevice.getId().getId().toString();
log.debug("deviceid {}", viewDeviceId);
DeviceCredentials deviceCredentials = DeviceCredentials deviceCredentials =
doGet("/api/device/" + viewDeviceId + "/credentials", DeviceCredentials.class); doGet("/api/device/" + viewDeviceId + "/credentials", DeviceCredentials.class);
assertEquals(testDevice.getId(), deviceCredentials.getDeviceId()); assertEquals(testDevice.getId(), deviceCredentials.getDeviceId());
@ -534,7 +646,6 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
String accessToken = deviceCredentials.getCredentialsId(); String accessToken = deviceCredentials.getCredentialsId();
assertNotNull(accessToken); assertNotNull(accessToken);
log.debug("creating mqtt client...");
String clientId = MqttAsyncClient.generateClientId(); String clientId = MqttAsyncClient.generateClientId();
MqttAsyncClient client = new MqttAsyncClient("tcp://localhost:1883", clientId, new MemoryPersistence()); MqttAsyncClient client = new MqttAsyncClient("tcp://localhost:1883", clientId, new MemoryPersistence());
@ -542,14 +653,11 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
options.setUserName(accessToken); options.setUserName(accessToken);
client.connect(options); client.connect(options);
awaitConnected(client, SECONDS.toMillis(30)); awaitConnected(client, SECONDS.toMillis(30));
log.debug("mqtt connected...");
MqttMessage message = new MqttMessage(); MqttMessage message = new MqttMessage();
message.setPayload((stringKV).getBytes()); message.setPayload((stringKV).getBytes());
getWsClient().registerWaitForUpdate(); getWsClient().registerWaitForUpdate();
IMqttDeliveryToken token = client.publish("v1/devices/me/attributes", message); IMqttDeliveryToken token = client.publish("v1/devices/me/attributes", message);
log.debug("publish token.message {}", token.getMessage());
await("mqtt ack").pollInterval(5, MILLISECONDS).atMost(TIMEOUT, SECONDS).until(() -> token.getMessage() == null); await("mqtt ack").pollInterval(5, MILLISECONDS).atMost(TIMEOUT, SECONDS).until(() -> token.getMessage() == null);
log.debug("token.message delivered {}", token.getMessage());
assertThat(getWsClient().waitForUpdate()).as("ws update received").isNotBlank(); assertThat(getWsClient().waitForUpdate()).as("ws update received").isNotBlank();
return getAttributeKeys("DEVICE", viewDeviceId); return getAttributeKeys("DEVICE", viewDeviceId);
} }

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

@ -21,6 +21,7 @@ import org.junit.After;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.mock.web.MockMultipartFile; import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder; import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
@ -31,10 +32,12 @@ import org.thingsboard.server.common.data.OtaPackageInfo;
import org.thingsboard.server.common.data.SaveOtaPackageInfoRequest; import org.thingsboard.server.common.data.SaveOtaPackageInfoRequest;
import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.exception.DataValidationException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayList; import java.util.ArrayList;
@ -102,6 +105,8 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes
firmwareInfo.setVersion(VERSION); firmwareInfo.setVersion(VERSION);
firmwareInfo.setUsesUrl(false); firmwareInfo.setUsesUrl(false);
Mockito.reset(tbClusterService, auditLogService);
OtaPackageInfo savedFirmwareInfo = save(firmwareInfo); OtaPackageInfo savedFirmwareInfo = save(firmwareInfo);
Assert.assertNotNull(savedFirmwareInfo); Assert.assertNotNull(savedFirmwareInfo);
@ -111,12 +116,20 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes
Assert.assertEquals(firmwareInfo.getTitle(), savedFirmwareInfo.getTitle()); Assert.assertEquals(firmwareInfo.getTitle(), savedFirmwareInfo.getTitle());
Assert.assertEquals(firmwareInfo.getVersion(), savedFirmwareInfo.getVersion()); Assert.assertEquals(firmwareInfo.getVersion(), savedFirmwareInfo.getVersion());
testNotifyEntityOneTimeMsgToEdgeServiceNever(savedFirmwareInfo, savedFirmwareInfo.getId(), savedFirmwareInfo.getId(),
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.ADDED);
savedFirmwareInfo.setAdditionalInfo(JacksonUtil.newObjectNode()); savedFirmwareInfo.setAdditionalInfo(JacksonUtil.newObjectNode());
save(new SaveOtaPackageInfoRequest(savedFirmwareInfo, false)); save(new SaveOtaPackageInfoRequest(savedFirmwareInfo, false));
OtaPackageInfo foundFirmwareInfo = doGet("/api/otaPackage/info/" + savedFirmwareInfo.getId().getId().toString(), OtaPackageInfo.class); OtaPackageInfo foundFirmwareInfo = doGet("/api/otaPackage/info/" + savedFirmwareInfo.getId().getId().toString(), OtaPackageInfo.class);
Assert.assertEquals(foundFirmwareInfo.getTitle(), savedFirmwareInfo.getTitle()); Assert.assertEquals(foundFirmwareInfo.getTitle(), savedFirmwareInfo.getTitle());
testNotifyEntityOneTimeMsgToEdgeServiceNever(foundFirmwareInfo, foundFirmwareInfo.getId(), foundFirmwareInfo.getId(),
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.UPDATED);
} }
@Test @Test
@ -127,14 +140,42 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes
firmwareInfo.setTitle(RandomStringUtils.randomAlphabetic(300)); firmwareInfo.setTitle(RandomStringUtils.randomAlphabetic(300));
firmwareInfo.setVersion(VERSION); firmwareInfo.setVersion(VERSION);
firmwareInfo.setUsesUrl(false); firmwareInfo.setUsesUrl(false);
doPost("/api/otaPackage", firmwareInfo).andExpect(statusReason(containsString("length of title must be equal or less than 255"))); String msgError = msgErrorFieldLength("title");
Mockito.reset(tbClusterService, auditLogService);
doPost("/api/otaPackage", firmwareInfo)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString(msgError)));
firmwareInfo.setTenantId(savedTenant.getId());
testNotifyEntityEqualsOneTimeServiceNeverError(firmwareInfo,
savedTenant.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.ADDED, new DataValidationException(msgError));
firmwareInfo.setTitle(TITLE); firmwareInfo.setTitle(TITLE);
firmwareInfo.setVersion(RandomStringUtils.randomAlphabetic(300)); firmwareInfo.setVersion(RandomStringUtils.randomAlphabetic(300));
doPost("/api/otaPackage", firmwareInfo).andExpect(statusReason(containsString("length of version must be equal or less than 255"))); msgError = msgErrorFieldLength("version");
doPost("/api/otaPackage", firmwareInfo)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString(msgError)));
firmwareInfo.setTenantId(savedTenant.getId());
testNotifyEntityEqualsOneTimeServiceNeverError(firmwareInfo,
savedTenant.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.ADDED, new DataValidationException(msgError));
firmwareInfo.setVersion(VERSION); firmwareInfo.setVersion(VERSION);
firmwareInfo.setUsesUrl(true); firmwareInfo.setUsesUrl(true);
msgError = msgErrorFieldLength("url");
firmwareInfo.setUrl(RandomStringUtils.randomAlphabetic(300)); firmwareInfo.setUrl(RandomStringUtils.randomAlphabetic(300));
doPost("/api/otaPackage", firmwareInfo).andExpect(statusReason(containsString("length of url must be equal or less than 255"))); doPost("/api/otaPackage", firmwareInfo)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString(msgError)));
firmwareInfo.setTenantId(savedTenant.getId());
testNotifyEntityEqualsOneTimeServiceNeverError(firmwareInfo, savedTenant.getId(),
tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError));
} }
@Test @Test
@ -164,12 +205,19 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes
MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array()); MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array());
OtaPackageInfo savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM); Mockito.reset(tbClusterService, auditLogService);
OtaPackage savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString()
+ "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM);
Assert.assertEquals(FILE_NAME, savedFirmware.getFileName()); Assert.assertEquals(FILE_NAME, savedFirmware.getFileName());
Assert.assertEquals(CONTENT_TYPE, savedFirmware.getContentType()); Assert.assertEquals(CONTENT_TYPE, savedFirmware.getContentType());
Assert.assertEquals(CHECKSUM_ALGORITHM, savedFirmware.getChecksumAlgorithm().name()); Assert.assertEquals(CHECKSUM_ALGORITHM, savedFirmware.getChecksumAlgorithm().name());
Assert.assertEquals(CHECKSUM, savedFirmware.getChecksum()); Assert.assertEquals(CHECKSUM, savedFirmware.getChecksum());
testNotifyEntityAllOneTime(savedFirmware, savedFirmware.getId(), savedFirmware.getId(),
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.UPDATED);
} }
@Test @Test
@ -184,10 +232,16 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes
OtaPackageInfo savedFirmwareInfo = save(firmwareInfo); OtaPackageInfo savedFirmwareInfo = save(firmwareInfo);
loginDifferentTenant(); loginDifferentTenant();
Mockito.reset(tbClusterService, auditLogService);
doPost("/api/otaPackage", doPost("/api/otaPackage",
new SaveOtaPackageInfoRequest(savedFirmwareInfo, false), new SaveOtaPackageInfoRequest(savedFirmwareInfo, false))
OtaPackageInfo.class, .andExpect(status().isForbidden())
status().isForbidden()); .andExpect(statusReason(containsString(msgErrorPermission)));
testNotifyEntityNever(savedFirmwareInfo.getId(), savedFirmwareInfo);
deleteDifferentTenant(); deleteDifferentTenant();
} }
@ -220,11 +274,12 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes
MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array()); MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array());
OtaPackageInfo savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM); OtaPackageInfo savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString()
+ "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM);
OtaPackage foundFirmware = doGet("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString(), OtaPackage.class); OtaPackage foundFirmware = doGet("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString(), OtaPackage.class);
Assert.assertNotNull(foundFirmware); Assert.assertNotNull(foundFirmware);
Assert.assertEquals(savedFirmware, new OtaPackageInfo(foundFirmware)); Assert.assertEquals(savedFirmware, foundFirmware);
Assert.assertEquals(DATA, foundFirmware.getData()); Assert.assertEquals(DATA, foundFirmware.getData());
} }
@ -239,17 +294,29 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes
OtaPackageInfo savedFirmwareInfo = save(firmwareInfo); OtaPackageInfo savedFirmwareInfo = save(firmwareInfo);
Mockito.reset(tbClusterService, auditLogService);
doDelete("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString()) doDelete("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString())
.andExpect(status().isOk()); .andExpect(status().isOk());
testNotifyEntityAllOneTime(savedFirmwareInfo, savedFirmwareInfo.getId(), savedFirmwareInfo.getId(),
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.DELETED, savedFirmwareInfo.getId().getId().toString());
doGet("/api/otaPackage/info/" + savedFirmwareInfo.getId().getId().toString()) doGet("/api/otaPackage/info/" + savedFirmwareInfo.getId().getId().toString())
.andExpect(status().isNotFound()); .andExpect(status().isNotFound())
.andExpect(statusReason(containsString(msgErrorNotFound)));
} }
@Test @Test
public void testFindTenantFirmwares() throws Exception { public void testFindTenantFirmwares() throws Exception {
Mockito.reset(tbClusterService, auditLogService);
List<OtaPackageInfo> otaPackages = new ArrayList<>(); List<OtaPackageInfo> otaPackages = new ArrayList<>();
for (int i = 0; i < 165; i++) { int cntEntity = 165;
int startIndexSaveData = 101;
for (int i = 0; i < cntEntity; i++) {
SaveOtaPackageInfoRequest firmwareInfo = new SaveOtaPackageInfoRequest(); SaveOtaPackageInfoRequest firmwareInfo = new SaveOtaPackageInfoRequest();
firmwareInfo.setDeviceProfileId(deviceProfileId); firmwareInfo.setDeviceProfileId(deviceProfileId);
firmwareInfo.setType(FIRMWARE); firmwareInfo.setType(FIRMWARE);
@ -259,16 +326,19 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes
OtaPackageInfo savedFirmwareInfo = save(firmwareInfo); OtaPackageInfo savedFirmwareInfo = save(firmwareInfo);
if (i > 100) { if (i >= startIndexSaveData) {
MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array()); MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array());
OtaPackageInfo savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM); OtaPackage savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM);
otaPackages.add(savedFirmware); savedFirmwareInfo = new OtaPackageInfo(savedFirmware);
} else {
otaPackages.add(savedFirmwareInfo);
} }
otaPackages.add(savedFirmwareInfo);
} }
testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new OtaPackageInfo(), new OtaPackageInfo(),
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.ADDED, ActionType.ADDED, cntEntity, 0, (cntEntity*2 - startIndexSaveData));
List<OtaPackageInfo> loadedFirmwares = new ArrayList<>(); List<OtaPackageInfo> loadedFirmwares = new ArrayList<>();
PageLink pageLink = new PageLink(24); PageLink pageLink = new PageLink(24);
PageData<OtaPackageInfo> pageData; PageData<OtaPackageInfo> pageData;
@ -306,7 +376,7 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes
if (i > 100) { if (i > 100) {
MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array()); MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array());
OtaPackageInfo savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM); OtaPackage savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM);
savedFirmwareInfo = new OtaPackageInfo(savedFirmware); savedFirmwareInfo = new OtaPackageInfo(savedFirmware);
otaPackagesWithData.add(savedFirmwareInfo); otaPackagesWithData.add(savedFirmwareInfo);
} }
@ -352,11 +422,11 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes
return doPost("/api/otaPackage", firmwareInfo, OtaPackageInfo.class); return doPost("/api/otaPackage", firmwareInfo, OtaPackageInfo.class);
} }
protected OtaPackageInfo savaData(String urlTemplate, MockMultipartFile content, String... params) throws Exception { protected OtaPackage savaData(String urlTemplate, MockMultipartFile content, String... params) throws Exception {
MockMultipartHttpServletRequestBuilder postRequest = MockMvcRequestBuilders.multipart(urlTemplate, params); MockMultipartHttpServletRequestBuilder postRequest = MockMvcRequestBuilders.multipart(urlTemplate, params);
postRequest.file(content); postRequest.file(content);
setJwtToken(postRequest); setJwtToken(postRequest);
return readResponse(mockMvc.perform(postRequest).andExpect(status().isOk()), OtaPackageInfo.class); return readResponse(mockMvc.perform(postRequest).andExpect(status().isOk()), OtaPackage.class);
} }
} }

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

@ -21,14 +21,17 @@ import org.junit.After;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito;
import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User; 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.edge.Edge;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.exception.DataValidationException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -75,22 +78,45 @@ public abstract class BaseRuleChainControllerTest extends AbstractControllerTest
public void testSaveRuleChain() throws Exception { public void testSaveRuleChain() throws Exception {
RuleChain ruleChain = new RuleChain(); RuleChain ruleChain = new RuleChain();
ruleChain.setName("RuleChain"); ruleChain.setName("RuleChain");
Mockito.reset(tbClusterService, auditLogService);
RuleChain savedRuleChain = doPost("/api/ruleChain", ruleChain, RuleChain.class); RuleChain savedRuleChain = doPost("/api/ruleChain", ruleChain, RuleChain.class);
Assert.assertNotNull(savedRuleChain); Assert.assertNotNull(savedRuleChain);
Assert.assertNotNull(savedRuleChain.getId()); Assert.assertNotNull(savedRuleChain.getId());
Assert.assertTrue(savedRuleChain.getCreatedTime() > 0); Assert.assertTrue(savedRuleChain.getCreatedTime() > 0);
Assert.assertEquals(ruleChain.getName(), savedRuleChain.getName()); Assert.assertEquals(ruleChain.getName(), savedRuleChain.getName());
testNotifyEntityOneTimeMsgToEdgeServiceNever(savedRuleChain, savedRuleChain.getId(), savedRuleChain.getId(),
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.ADDED);
savedRuleChain.setName("New RuleChain"); savedRuleChain.setName("New RuleChain");
doPost("/api/ruleChain", savedRuleChain, RuleChain.class); doPost("/api/ruleChain", savedRuleChain, RuleChain.class);
RuleChain foundRuleChain = doGet("/api/ruleChain/" + savedRuleChain.getId().getId().toString(), RuleChain.class); RuleChain foundRuleChain = doGet("/api/ruleChain/" + savedRuleChain.getId().getId().toString(), RuleChain.class);
Assert.assertEquals(savedRuleChain.getName(), foundRuleChain.getName()); Assert.assertEquals(savedRuleChain.getName(), foundRuleChain.getName());
testNotifyEntityOneTimeMsgToEdgeServiceNever(savedRuleChain, savedRuleChain.getId(), savedRuleChain.getId(),
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.UPDATED);
} }
@Test @Test
public void testSaveRuleChainWithViolationOfLengthValidation() throws Exception { public void testSaveRuleChainWithViolationOfLengthValidation() throws Exception {
Mockito.reset(tbClusterService, auditLogService);
RuleChain ruleChain = new RuleChain(); RuleChain ruleChain = new RuleChain();
ruleChain.setName(RandomStringUtils.randomAlphabetic(300)); ruleChain.setName(RandomStringUtils.randomAlphabetic(300));
doPost("/api/ruleChain", ruleChain).andExpect(statusReason(containsString("length of name must be equal or less than 255"))); String msgError = msgErrorFieldLength("name");
doPost("/api/ruleChain", ruleChain)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString(msgError)));
ruleChain.setTenantId(savedTenant.getId());
testNotifyEntityEqualsOneTimeServiceNeverError(ruleChain,
savedTenant.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.ADDED, new DataValidationException(msgError));
} }
@Test @Test
@ -109,11 +135,19 @@ public abstract class BaseRuleChainControllerTest extends AbstractControllerTest
ruleChain.setName("RuleChain"); ruleChain.setName("RuleChain");
RuleChain savedRuleChain = doPost("/api/ruleChain", ruleChain, RuleChain.class); RuleChain savedRuleChain = doPost("/api/ruleChain", ruleChain, RuleChain.class);
Mockito.reset(tbClusterService, auditLogService);
String entityIdStr = savedRuleChain.getId().getId().toString();
doDelete("/api/ruleChain/" + savedRuleChain.getId().getId().toString()) doDelete("/api/ruleChain/" + savedRuleChain.getId().getId().toString())
.andExpect(status().isOk()); .andExpect(status().isOk());
doGet("/api/ruleChain/" + savedRuleChain.getId().getId().toString()) testNotifyEntityBroadcastEntityStateChangeEventOneTimeMsgToEdgeServiceNever(savedRuleChain, savedRuleChain.getId(), savedRuleChain.getId(),
.andExpect(status().isNotFound()); savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.DELETED, savedRuleChain.getId().getId().toString());
doGet("/api/ruleChain/" + entityIdStr)
.andExpect(status().isNotFound())
.andExpect(statusReason(containsString(msgErrorNoFound("Rule chain", entityIdStr))));
} }
@Test @Test
@ -121,15 +155,20 @@ public abstract class BaseRuleChainControllerTest extends AbstractControllerTest
Edge edge = constructEdge("My edge", "default"); Edge edge = constructEdge("My edge", "default");
Edge savedEdge = doPost("/api/edge", edge, Edge.class); Edge savedEdge = doPost("/api/edge", edge, Edge.class);
List<RuleChain> edgeRuleChains = new ArrayList<>(); List<RuleChain> edgeRuleChains = new ArrayList<>();
PageLink pageLink = new PageLink(17); PageLink pageLink = new PageLink(17);
PageData<RuleChain> pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId() + "/ruleChains?", PageData<RuleChain> pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId() + "/ruleChains?",
new TypeReference<>() {}, pageLink); new TypeReference<>() {
}, pageLink);
Assert.assertFalse(pageData.hasNext()); Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(1, pageData.getTotalElements()); Assert.assertEquals(1, pageData.getTotalElements());
edgeRuleChains.addAll(pageData.getData()); edgeRuleChains.addAll(pageData.getData());
for (int i = 0; i < 28; i++) { Mockito.reset(tbClusterService, auditLogService);
int cntEntity = 28;
for (int i = 0; i < cntEntity; i++) {
RuleChain ruleChain = new RuleChain(); RuleChain ruleChain = new RuleChain();
ruleChain.setName("RuleChain " + i); ruleChain.setName("RuleChain " + i);
ruleChain.setType(RuleChainType.EDGE); ruleChain.setType(RuleChainType.EDGE);
@ -139,11 +178,21 @@ public abstract class BaseRuleChainControllerTest extends AbstractControllerTest
edgeRuleChains.add(savedRuleChain); edgeRuleChains.add(savedRuleChain);
} }
testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new RuleChain(), new RuleChain(),
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.ADDED, ActionType.ADDED, cntEntity, 0, cntEntity * 2);
testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new RuleChain(), new RuleChain(),
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.ASSIGNED_TO_EDGE, ActionType.ASSIGNED_TO_EDGE, cntEntity, cntEntity, cntEntity * 2,
new String(), new String(), new String());
Mockito.reset(tbClusterService, auditLogService);
List<RuleChain> loadedEdgeRuleChains = new ArrayList<>(); List<RuleChain> loadedEdgeRuleChains = new ArrayList<>();
pageLink = new PageLink(17); pageLink = new PageLink(17);
do { do {
pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId() + "/ruleChains?", pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId() + "/ruleChains?",
new TypeReference<>() {}, pageLink); new TypeReference<>() {
}, pageLink);
loadedEdgeRuleChains.addAll(pageData.getData()); loadedEdgeRuleChains.addAll(pageData.getData());
if (pageData.hasNext()) { if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink(); pageLink = pageLink.nextPageLink();
@ -162,9 +211,14 @@ public abstract class BaseRuleChainControllerTest extends AbstractControllerTest
} }
} }
testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAnyAdditionalInfoAny(new RuleChain(), new RuleChain(),
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.UNASSIGNED_FROM_EDGE, ActionType.UNASSIGNED_FROM_EDGE, cntEntity, cntEntity, 3);
pageLink = new PageLink(17); pageLink = new PageLink(17);
pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId() + "/ruleChains?", pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId() + "/ruleChains?",
new TypeReference<>() {}, pageLink); new TypeReference<>() {
}, pageLink);
Assert.assertFalse(pageData.hasNext()); Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(1, pageData.getTotalElements()); Assert.assertEquals(1, pageData.getTotalElements());
} }

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

@ -21,14 +21,17 @@ import org.junit.After;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito;
import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.ResourceType;
import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResource;
import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.TbResourceInfo;
import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.exception.DataValidationException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -75,6 +78,9 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes
@Test @Test
public void testSaveTbResource() throws Exception { public void testSaveTbResource() throws Exception {
Mockito.reset(tbClusterService, auditLogService);
TbResource resource = new TbResource(); TbResource resource = new TbResource();
resource.setResourceType(ResourceType.JKS); resource.setResourceType(ResourceType.JKS);
resource.setTitle("My first resource"); resource.setTitle("My first resource");
@ -83,6 +89,10 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes
TbResource savedResource = save(resource); TbResource savedResource = save(resource);
testNotifyEntityOneTimeMsgToEdgeServiceNever(savedResource, savedResource.getId(), savedResource.getId(),
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.ADDED);
Assert.assertNotNull(savedResource); Assert.assertNotNull(savedResource);
Assert.assertNotNull(savedResource.getId()); Assert.assertNotNull(savedResource.getId());
Assert.assertTrue(savedResource.getCreatedTime() > 0); Assert.assertTrue(savedResource.getCreatedTime() > 0);
@ -98,6 +108,10 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes
TbResource foundResource = doGet("/api/resource/" + savedResource.getId().getId().toString(), TbResource.class); TbResource foundResource = doGet("/api/resource/" + savedResource.getId().getId().toString(), TbResource.class);
Assert.assertEquals(foundResource.getTitle(), savedResource.getTitle()); Assert.assertEquals(foundResource.getTitle(), savedResource.getTitle());
testNotifyEntityOneTimeMsgToEdgeServiceNever(foundResource, foundResource.getId(), foundResource.getId(),
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.UPDATED);
} }
@Test @Test
@ -107,7 +121,16 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes
resource.setTitle(RandomStringUtils.randomAlphabetic(300)); resource.setTitle(RandomStringUtils.randomAlphabetic(300));
resource.setFileName(DEFAULT_FILE_NAME); resource.setFileName(DEFAULT_FILE_NAME);
resource.setData("Test Data"); resource.setData("Test Data");
doPost("/api/resource", resource).andExpect(statusReason(containsString("length of title must be equal or less than 255")));
Mockito.reset(tbClusterService, auditLogService);
String msgError = msgErrorFieldLength("title");
doPost("/api/resource", resource)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString(msgError)));
testNotifyEntityEqualsOneTimeServiceNeverError(resource, savedTenant.getId(),
tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError));
} }
@Test @Test
@ -118,10 +141,24 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes
resource.setFileName(DEFAULT_FILE_NAME); resource.setFileName(DEFAULT_FILE_NAME);
resource.setData("Test Data"); resource.setData("Test Data");
TbResource savedResource = save(resource); TbResource savedResource = save(resource);
loginDifferentTenant(); loginDifferentTenant();
doPostWithTypedResponse("/api/resource", savedResource, new TypeReference<>(){}, status().isForbidden());
Mockito.reset(tbClusterService, auditLogService);
doPost("/api/resource", savedResource)
.andExpect(status().isForbidden())
.andExpect(statusReason(containsString(msgErrorPermission)));
testNotifyEntityNever(savedResource.getId(), savedResource);
doDelete("/api/resource/" + savedResource.getId().getId().toString())
.andExpect(status().isForbidden())
.andExpect(statusReason(containsString(msgErrorPermission)));
testNotifyEntityNever(savedResource.getId(), savedResource);
deleteDifferentTenant(); deleteDifferentTenant();
} }
@ -150,17 +187,29 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes
TbResource savedResource = save(resource); TbResource savedResource = save(resource);
doDelete("/api/resource/" + savedResource.getId().getId().toString()) Mockito.reset(tbClusterService, auditLogService);
String resourceIdStr = savedResource.getId().getId().toString();
doDelete("/api/resource/" + resourceIdStr)
.andExpect(status().isOk()); .andExpect(status().isOk());
testNotifyEntityOneTimeMsgToEdgeServiceNever(savedResource, savedResource.getId(), savedResource.getId(),
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.DELETED, resourceIdStr);
doGet("/api/resource/" + savedResource.getId().getId().toString()) doGet("/api/resource/" + savedResource.getId().getId().toString())
.andExpect(status().isNotFound()); .andExpect(status().isNotFound())
.andExpect(statusReason(containsString(msgErrorNoFound("Resource", resourceIdStr))));
} }
@Test @Test
public void testFindTenantTbResources() throws Exception { public void testFindTenantTbResources() throws Exception {
Mockito.reset(tbClusterService, auditLogService);
List<TbResourceInfo> resources = new ArrayList<>(); List<TbResourceInfo> resources = new ArrayList<>();
for (int i = 0; i < 173; i++) { int cntEntity = 173;
for (int i = 0; i < cntEntity; i++) {
TbResource resource = new TbResource(); TbResource resource = new TbResource();
resource.setTitle("Resource" + i); resource.setTitle("Resource" + i);
resource.setResourceType(ResourceType.JKS); resource.setResourceType(ResourceType.JKS);
@ -173,7 +222,7 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes
PageData<TbResourceInfo> pageData; PageData<TbResourceInfo> pageData;
do { do {
pageData = doGetTypedWithPageLink("/api/resource?", pageData = doGetTypedWithPageLink("/api/resource?",
new TypeReference<PageData<TbResourceInfo>>() { new TypeReference<>() {
}, pageLink); }, pageLink);
loadedResources.addAll(pageData.getData()); loadedResources.addAll(pageData.getData());
if (pageData.hasNext()) { if (pageData.hasNext()) {
@ -181,6 +230,10 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes
} }
} while (pageData.hasNext()); } while (pageData.hasNext());
testNotifyManyEntityManyTimeMsgToEdgeServiceNever(new TbResource(), new TbResource(),
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.ADDED, cntEntity);
Collections.sort(resources, idComparator); Collections.sort(resources, idComparator);
Collections.sort(loadedResources, idComparator); Collections.sort(loadedResources, idComparator);
@ -205,7 +258,7 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes
PageData<TbResourceInfo> pageData; PageData<TbResourceInfo> pageData;
do { do {
pageData = doGetTypedWithPageLink("/api/resource?", pageData = doGetTypedWithPageLink("/api/resource?",
new TypeReference<PageData<TbResourceInfo>>() { new TypeReference<>() {
}, pageLink); }, pageLink);
loadedResources.addAll(pageData.getData()); loadedResources.addAll(pageData.getData());
if (pageData.hasNext()) { if (pageData.hasNext()) {
@ -218,16 +271,23 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes
Assert.assertEquals(resources, loadedResources); Assert.assertEquals(resources, loadedResources);
Mockito.reset(tbClusterService, auditLogService);
int cntEntity = resources.size();
for (TbResourceInfo resource : resources) { for (TbResourceInfo resource : resources) {
doDelete("/api/resource/" + resource.getId().getId().toString()) doDelete("/api/resource/" + resource.getId().getId().toString())
.andExpect(status().isOk()); .andExpect(status().isOk());
} }
testNotifyManyEntityManyTimeMsgToEdgeServiceNeverAdditionalInfoAny(new TbResource(), new TbResource(),
resources.get(0).getTenantId(), null, null, SYS_ADMIN_EMAIL,
ActionType.DELETED, cntEntity, 1);
pageLink = new PageLink(27); pageLink = new PageLink(27);
loadedResources.clear(); loadedResources.clear();
do { do {
pageData = doGetTypedWithPageLink("/api/resource?", pageData = doGetTypedWithPageLink("/api/resource?",
new TypeReference<PageData<TbResourceInfo>>() { new TypeReference<>() {
}, pageLink); }, pageLink);
loadedResources.addAll(pageData.getData()); loadedResources.addAll(pageData.getData());
if (pageData.hasNext()) { if (pageData.hasNext()) {

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

@ -26,6 +26,8 @@ import org.junit.After;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.mockito.ArgumentMatcher;
import org.mockito.Mockito;
import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.TestPropertySource;
import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.ResultActions;
import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.common.util.ThingsBoardExecutors;
@ -33,8 +35,10 @@ import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.TenantInfo; import org.thingsboard.server.common.data.TenantInfo;
import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.data.queue.ProcessingStrategy; import org.thingsboard.server.common.data.queue.ProcessingStrategy;
import org.thingsboard.server.common.data.queue.ProcessingStrategyType; import org.thingsboard.server.common.data.queue.ProcessingStrategyType;
import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.common.data.queue.Queue;
@ -56,6 +60,8 @@ import java.util.stream.Collectors;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@TestPropertySource(properties = { @TestPropertySource(properties = {
@ -86,17 +92,28 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest {
loginSysAdmin(); loginSysAdmin();
Tenant tenant = new Tenant(); Tenant tenant = new Tenant();
tenant.setTitle("My tenant"); tenant.setTitle("My tenant");
Mockito.reset(tbClusterService);
Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class); Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
Assert.assertNotNull(savedTenant); Assert.assertNotNull(savedTenant);
Assert.assertNotNull(savedTenant.getId()); Assert.assertNotNull(savedTenant.getId());
Assert.assertTrue(savedTenant.getCreatedTime() > 0); Assert.assertTrue(savedTenant.getCreatedTime() > 0);
Assert.assertEquals(tenant.getTitle(), savedTenant.getTitle()); Assert.assertEquals(tenant.getTitle(), savedTenant.getTitle());
testBroadcastEntityStateChangeEventTimeManyTimeTenant(savedTenant, ComponentLifecycleEvent.CREATED, 1);
savedTenant.setTitle("My new tenant"); savedTenant.setTitle("My new tenant");
doPost("/api/tenant", savedTenant, Tenant.class); doPost("/api/tenant", savedTenant, Tenant.class);
Tenant foundTenant = doGet("/api/tenant/" + savedTenant.getId().getId().toString(), Tenant.class); Tenant foundTenant = doGet("/api/tenant/" + savedTenant.getId().getId().toString(), Tenant.class);
Assert.assertEquals(foundTenant.getTitle(), savedTenant.getTitle()); Assert.assertEquals(foundTenant.getTitle(), savedTenant.getTitle());
testBroadcastEntityStateChangeEventTimeManyTimeTenant(savedTenant, ComponentLifecycleEvent.UPDATED, 1);
doDelete("/api/tenant/" + savedTenant.getId().getId().toString()) doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
.andExpect(status().isOk()); .andExpect(status().isOk());
testBroadcastEntityStateChangeEventTimeManyTimeTenant(savedTenant, ComponentLifecycleEvent.DELETED, 1);
} }
@Test @Test
@ -104,7 +121,14 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest {
loginSysAdmin(); loginSysAdmin();
Tenant tenant = new Tenant(); Tenant tenant = new Tenant();
tenant.setTitle(RandomStringUtils.randomAlphanumeric(300)); tenant.setTitle(RandomStringUtils.randomAlphanumeric(300));
doPost("/api/tenant", tenant).andExpect(statusReason(containsString("length of title must be equal or less than 255")));
Mockito.reset(tbClusterService);
doPost("/api/tenant", tenant)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString(msgErrorFieldLength("title"))));
testBroadcastEntityStateChangeEventNeverTenant();
} }
@Test @Test
@ -136,21 +160,31 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest {
@Test @Test
public void testSaveTenantWithEmptyTitle() throws Exception { public void testSaveTenantWithEmptyTitle() throws Exception {
loginSysAdmin(); loginSysAdmin();
Mockito.reset(tbClusterService);
Tenant tenant = new Tenant(); Tenant tenant = new Tenant();
doPost("/api/tenant", tenant) doPost("/api/tenant", tenant)
.andExpect(status().isBadRequest()) .andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Tenant title should be specified"))); .andExpect(statusReason(containsString("Tenant title " + msgErrorShouldBeSpecified)));
testBroadcastEntityStateChangeEventNeverTenant();
} }
@Test @Test
public void testSaveTenantWithInvalidEmail() throws Exception { public void testSaveTenantWithInvalidEmail() throws Exception {
loginSysAdmin(); loginSysAdmin();
Mockito.reset(tbClusterService);
Tenant tenant = new Tenant(); Tenant tenant = new Tenant();
tenant.setTitle("My tenant"); tenant.setTitle("My tenant");
tenant.setEmail("invalid@mail"); tenant.setEmail("invalid@mail");
doPost("/api/tenant", tenant) doPost("/api/tenant", tenant)
.andExpect(status().isBadRequest()) .andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Invalid email address format"))); .andExpect(statusReason(containsString("Invalid email address format")));
testBroadcastEntityStateChangeEventNeverTenant();
} }
@Test @Test
@ -159,10 +193,13 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest {
Tenant tenant = new Tenant(); Tenant tenant = new Tenant();
tenant.setTitle("My tenant"); tenant.setTitle("My tenant");
Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class); Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
String tenantIdStr = savedTenant.getId().getId().toString();
doDelete("/api/tenant/" + tenantIdStr)
.andExpect(status().isOk()); .andExpect(status().isOk());
doGet("/api/tenant/" + savedTenant.getId().getId().toString()) doGet("/api/tenant/" + tenantIdStr)
.andExpect(status().isNotFound()); .andExpect(status().isNotFound())
.andExpect(statusReason(containsString(msgErrorNoFound("Tenant", tenantIdStr))));
} }
@Test @Test
@ -175,8 +212,11 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest {
Assert.assertEquals(1, pageData.getData().size()); Assert.assertEquals(1, pageData.getData().size());
tenants.addAll(pageData.getData()); tenants.addAll(pageData.getData());
Mockito.reset(tbClusterService);
int cntEntity = 56;
List<ListenableFuture<Tenant>> createFutures = new ArrayList<>(56); List<ListenableFuture<Tenant>> createFutures = new ArrayList<>(56);
for (int i = 0; i < 56; i++) { for (int i = 0; i < cntEntity; i++) {
Tenant tenant = new Tenant(); Tenant tenant = new Tenant();
tenant.setTitle("Tenant" + i); tenant.setTitle("Tenant" + i);
createFutures.add(executor.submit(() -> createFutures.add(executor.submit(() ->
@ -184,6 +224,8 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest {
} }
tenants.addAll(Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS)); tenants.addAll(Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS));
testBroadcastEntityStateChangeEventTimeManyTimeTenant(new Tenant(), ComponentLifecycleEvent.CREATED, cntEntity);
List<Tenant> loadedTenants = new ArrayList<>(); List<Tenant> loadedTenants = new ArrayList<>();
pageLink = new PageLink(17); pageLink = new PageLink(17);
do { do {
@ -200,6 +242,8 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest {
.filter((t) -> !TEST_TENANT_NAME.equals(t.getTitle())) .filter((t) -> !TEST_TENANT_NAME.equals(t.getTitle()))
.collect(Collectors.toList()), executor).get(TIMEOUT, TimeUnit.SECONDS); .collect(Collectors.toList()), executor).get(TIMEOUT, TimeUnit.SECONDS);
testBroadcastEntityStateChangeEventTimeManyTimeTenant(new Tenant(), ComponentLifecycleEvent.DELETED, cntEntity);
pageLink = new PageLink(17); pageLink = new PageLink(17);
pageData = doGetTypedWithPageLink("/api/tenants?", PAGE_DATA_TENANT_TYPE_REF, pageLink); pageData = doGetTypedWithPageLink("/api/tenants?", PAGE_DATA_TENANT_TYPE_REF, pageLink);
Assert.assertFalse(pageData.hasNext()); Assert.assertFalse(pageData.hasNext());
@ -464,7 +508,9 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest {
login(username, password); login(username, password);
for (Queue queue : foundTenantQueues) { for (Queue queue : foundTenantQueues) {
doGet("/api/queues/" + queue.getId()).andExpect(status().isNotFound()); doGet("/api/queues/" + queue.getId())
.andExpect(status().isNotFound())
.andExpect(statusReason(containsString(msgErrorNotFound)));
} }
loginSysAdmin(); loginSysAdmin();
@ -476,7 +522,7 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest {
queueConfiguration.setName(queueName); queueConfiguration.setName(queueName);
queueConfiguration.setTopic("tb_rule_engine." + queueName.toLowerCase()); queueConfiguration.setTopic("tb_rule_engine." + queueName.toLowerCase());
queueConfiguration.setPollInterval(25); queueConfiguration.setPollInterval(25);
queueConfiguration.setPartitions(new Random().nextInt(100)); queueConfiguration.setPartitions(1 + new Random().nextInt(99));
queueConfiguration.setConsumerPerPartition(true); queueConfiguration.setConsumerPerPartition(true);
queueConfiguration.setPackProcessingTimeout(2000); queueConfiguration.setPackProcessingTimeout(2000);
SubmitStrategy submitStrategy = new SubmitStrategy(); SubmitStrategy submitStrategy = new SubmitStrategy();
@ -519,4 +565,26 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest {
} }
return result; return result;
} }
private void testBroadcastEntityStateChangeEventTimeManyTimeTenant(Tenant tenant, ComponentLifecycleEvent event, int cntTime) {
ArgumentMatcher<Tenant> matcherTenant = cntTime == 1 ? argument -> argument.equals(tenant) :
argument -> argument.getClass().equals(Tenant.class);
if (ComponentLifecycleEvent.DELETED.equals(event)) {
Mockito.verify(tbClusterService, times( cntTime)).onTenantDelete(Mockito.argThat(matcherTenant),
Mockito.isNull());
} else {
Mockito.verify(tbClusterService, times( cntTime)).onTenantChange(Mockito.argThat(matcherTenant),
Mockito.isNull());
}
TenantId tenantId = cntTime == 1 ? tenant.getId() : (TenantId) createEntityId_NULL_UUID(tenant);
testBroadcastEntityStateChangeEventTime(tenantId, tenantId, cntTime);
Mockito.reset(tbClusterService);
}
private void testBroadcastEntityStateChangeEventNeverTenant() {
Mockito.verify(tbClusterService, never()).onTenantChange(Mockito.any(Tenant.class),
Mockito.isNull());
testBroadcastEntityStateChangeEventNever(createEntityId_NULL_UUID(new Tenant()));
Mockito.reset(tbClusterService);
}
} }

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

@ -17,24 +17,24 @@ package org.thingsboard.server.controller;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.RandomStringUtils;
import org.junit.After;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.mockito.ArgumentMatcher;
import org.mockito.Mockito;
import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.common.data.EntityInfo;
import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.id.TenantProfileId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.data.queue.ProcessingStrategy; import org.thingsboard.server.common.data.queue.ProcessingStrategy;
import org.thingsboard.server.common.data.queue.ProcessingStrategyType; import org.thingsboard.server.common.data.queue.ProcessingStrategyType;
import org.thingsboard.server.common.data.queue.SubmitStrategy; import org.thingsboard.server.common.data.queue.SubmitStrategy;
import org.thingsboard.server.common.data.queue.SubmitStrategyType; import org.thingsboard.server.common.data.queue.SubmitStrategyType;
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; import org.thingsboard.server.common.data.tenant.profile.TenantProfileData;
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.tenant.profile.TenantProfileQueueConfiguration; import org.thingsboard.server.common.data.tenant.profile.TenantProfileQueueConfiguration;
import org.thingsboard.server.dao.tenant.TenantProfileService;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -42,6 +42,8 @@ import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
public abstract class BaseTenantProfileControllerTest extends AbstractControllerTest { public abstract class BaseTenantProfileControllerTest extends AbstractControllerTest {
@ -52,6 +54,9 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController
@Test @Test
public void testSaveTenantProfile() throws Exception { public void testSaveTenantProfile() throws Exception {
loginSysAdmin(); loginSysAdmin();
Mockito.reset(tbClusterService);
TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile"); TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile");
TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class); TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class);
Assert.assertNotNull(savedTenantProfile); Assert.assertNotNull(savedTenantProfile);
@ -61,20 +66,30 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController
Assert.assertEquals(tenantProfile.getDescription(), savedTenantProfile.getDescription()); Assert.assertEquals(tenantProfile.getDescription(), savedTenantProfile.getDescription());
Assert.assertEquals(tenantProfile.getProfileData(), savedTenantProfile.getProfileData()); Assert.assertEquals(tenantProfile.getProfileData(), savedTenantProfile.getProfileData());
Assert.assertEquals(tenantProfile.isDefault(), savedTenantProfile.isDefault()); Assert.assertEquals(tenantProfile.isDefault(), savedTenantProfile.isDefault());
Assert.assertEquals(tenantProfile.isIsolatedTbCore(), savedTenantProfile.isIsolatedTbCore());
Assert.assertEquals(tenantProfile.isIsolatedTbRuleEngine(), savedTenantProfile.isIsolatedTbRuleEngine()); Assert.assertEquals(tenantProfile.isIsolatedTbRuleEngine(), savedTenantProfile.isIsolatedTbRuleEngine());
testBroadcastEntityStateChangeEventTimeManyTimeTenantProfile(savedTenantProfile, ComponentLifecycleEvent.CREATED, 1);
savedTenantProfile.setName("New tenant profile"); savedTenantProfile.setName("New tenant profile");
doPost("/api/tenantProfile", savedTenantProfile, TenantProfile.class); doPost("/api/tenantProfile", savedTenantProfile, TenantProfile.class);
TenantProfile foundTenantProfile = doGet("/api/tenantProfile/"+savedTenantProfile.getId().getId().toString(), TenantProfile.class); TenantProfile foundTenantProfile = doGet("/api/tenantProfile/"+savedTenantProfile.getId().getId().toString(), TenantProfile.class);
Assert.assertEquals(foundTenantProfile.getName(), savedTenantProfile.getName()); Assert.assertEquals(foundTenantProfile.getName(), savedTenantProfile.getName());
testBroadcastEntityStateChangeEventTimeManyTimeTenantProfile(savedTenantProfile, ComponentLifecycleEvent.UPDATED, 1);
} }
@Test @Test
public void testSaveTenantProfileWithViolationOfLengthValidation() throws Exception { public void testSaveTenantProfileWithViolationOfLengthValidation() throws Exception {
loginSysAdmin(); loginSysAdmin();
Mockito.reset(tbClusterService);
TenantProfile tenantProfile = this.createTenantProfile(RandomStringUtils.randomAlphabetic(300)); TenantProfile tenantProfile = this.createTenantProfile(RandomStringUtils.randomAlphabetic(300));
doPost("/api/tenantProfile", tenantProfile).andExpect(statusReason(containsString("length of name must be equal or less than 255"))); doPost("/api/tenantProfile", tenantProfile)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString(msgErrorFieldLength("name"))));
testBroadcastEntityStateChangeEventNeverTenantProfile();
} }
@Test @Test
@ -122,9 +137,15 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController
@Test @Test
public void testSaveTenantProfileWithEmptyName() throws Exception { public void testSaveTenantProfileWithEmptyName() throws Exception {
loginSysAdmin(); loginSysAdmin();
Mockito.reset(tbClusterService);
TenantProfile tenantProfile = new TenantProfile(); TenantProfile tenantProfile = new TenantProfile();
doPost("/api/tenantProfile", tenantProfile).andExpect(status().isBadRequest()) doPost("/api/tenantProfile", tenantProfile).andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Tenant profile name should be specified"))); .andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Tenant profile name " + msgErrorShouldBeSpecified)));
testBroadcastEntityStateChangeEventNeverTenantProfile();
} }
@Test @Test
@ -132,9 +153,15 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController
loginSysAdmin(); loginSysAdmin();
TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile"); TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile");
doPost("/api/tenantProfile", tenantProfile).andExpect(status().isOk()); doPost("/api/tenantProfile", tenantProfile).andExpect(status().isOk());
Mockito.reset(tbClusterService);
TenantProfile tenantProfile2 = this.createTenantProfile("Tenant Profile"); TenantProfile tenantProfile2 = this.createTenantProfile("Tenant Profile");
doPost("/api/tenantProfile", tenantProfile2).andExpect(status().isBadRequest()) doPost("/api/tenantProfile", tenantProfile2)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Tenant profile with such name already exists"))); .andExpect(statusReason(containsString("Tenant profile with such name already exists")));
testBroadcastEntityStateChangeEventNeverTenantProfile();
} }
@Test @Test
@ -144,18 +171,14 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController
TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class); TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class);
savedTenantProfile.setIsolatedTbRuleEngine(true); savedTenantProfile.setIsolatedTbRuleEngine(true);
addMainQueueConfig(savedTenantProfile); addMainQueueConfig(savedTenantProfile);
doPost("/api/tenantProfile", savedTenantProfile).andExpect(status().isBadRequest())
Mockito.reset(tbClusterService);
doPost("/api/tenantProfile", savedTenantProfile)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Can't update isolatedTbRuleEngine property"))); .andExpect(statusReason(containsString("Can't update isolatedTbRuleEngine property")));
}
@Test testBroadcastEntityStateChangeEventNeverTenantProfile();
public void testSaveSameTenantProfileWithDifferentIsolatedTbCore() throws Exception {
loginSysAdmin();
TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile");
TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class);
savedTenantProfile.setIsolatedTbCore(true);
doPost("/api/tenantProfile", savedTenantProfile).andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Can't update isolatedTbCore property")));
} }
@Test @Test
@ -169,10 +192,14 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController
tenant.setTenantProfileId(savedTenantProfile.getId()); tenant.setTenantProfileId(savedTenantProfile.getId());
Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class); Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
Mockito.reset(tbClusterService);
doDelete("/api/tenantProfile/" + savedTenantProfile.getId().getId().toString()) doDelete("/api/tenantProfile/" + savedTenantProfile.getId().getId().toString())
.andExpect(status().isBadRequest()) .andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("The tenant profile referenced by the tenants cannot be deleted"))); .andExpect(statusReason(containsString("The tenant profile referenced by the tenants cannot be deleted")));
testBroadcastEntityStateChangeEventNeverTenantProfile();
doDelete("/api/tenant/"+savedTenant.getId().getId().toString()) doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
.andExpect(status().isOk()); .andExpect(status().isOk());
} }
@ -183,11 +210,16 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController
TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile"); TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile");
TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class); TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class);
Mockito.reset(tbClusterService);
doDelete("/api/tenantProfile/" + savedTenantProfile.getId().getId().toString()) doDelete("/api/tenantProfile/" + savedTenantProfile.getId().getId().toString())
.andExpect(status().isOk()); .andExpect(status().isOk());
testBroadcastEntityStateChangeEventTimeManyTimeTenantProfile(savedTenantProfile, ComponentLifecycleEvent.DELETED, 1);
doGet("/api/tenantProfile/" + savedTenantProfile.getId().getId().toString()) doGet("/api/tenantProfile/" + savedTenantProfile.getId().getId().toString())
.andExpect(status().isNotFound()); .andExpect(status().isNotFound())
.andExpect(statusReason(containsString(msgErrorNoFound("Tenant profile", savedTenantProfile.getId().getId().toString()))));
} }
@Test @Test
@ -196,21 +228,26 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController
List<TenantProfile> tenantProfiles = new ArrayList<>(); List<TenantProfile> tenantProfiles = new ArrayList<>();
PageLink pageLink = new PageLink(17); PageLink pageLink = new PageLink(17);
PageData<TenantProfile> pageData = doGetTypedWithPageLink("/api/tenantProfiles?", PageData<TenantProfile> pageData = doGetTypedWithPageLink("/api/tenantProfiles?",
new TypeReference<PageData<TenantProfile>>(){}, pageLink); new TypeReference<>(){}, pageLink);
Assert.assertFalse(pageData.hasNext()); Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(1, pageData.getTotalElements()); Assert.assertEquals(1, pageData.getTotalElements());
tenantProfiles.addAll(pageData.getData()); tenantProfiles.addAll(pageData.getData());
Mockito.reset(tbClusterService);
int cntEntity = 28;
for (int i=0;i<28;i++) { for (int i=0;i<28;i++) {
TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile"+i); TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile"+i);
tenantProfiles.add(doPost("/api/tenantProfile", tenantProfile, TenantProfile.class)); tenantProfiles.add(doPost("/api/tenantProfile", tenantProfile, TenantProfile.class));
} }
testBroadcastEntityStateChangeEventTimeManyTimeTenantProfile(new TenantProfile(), ComponentLifecycleEvent.CREATED, cntEntity);
List<TenantProfile> loadedTenantProfiles = new ArrayList<>(); List<TenantProfile> loadedTenantProfiles = new ArrayList<>();
pageLink = new PageLink(17); pageLink = new PageLink(17);
do { do {
pageData = doGetTypedWithPageLink("/api/tenantProfiles?", pageData = doGetTypedWithPageLink("/api/tenantProfiles?",
new TypeReference<PageData<TenantProfile>>(){}, pageLink); new TypeReference<>(){}, pageLink);
loadedTenantProfiles.addAll(pageData.getData()); loadedTenantProfiles.addAll(pageData.getData());
if (pageData.hasNext()) { if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink(); pageLink = pageLink.nextPageLink();
@ -222,6 +259,8 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController
Assert.assertEquals(tenantProfiles, loadedTenantProfiles); Assert.assertEquals(tenantProfiles, loadedTenantProfiles);
Mockito.reset(tbClusterService);
for (TenantProfile tenantProfile : loadedTenantProfiles) { for (TenantProfile tenantProfile : loadedTenantProfiles) {
if (!tenantProfile.isDefault()) { if (!tenantProfile.isDefault()) {
doDelete("/api/tenantProfile/" + tenantProfile.getId().getId().toString()) doDelete("/api/tenantProfile/" + tenantProfile.getId().getId().toString())
@ -234,6 +273,8 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController
new TypeReference<PageData<TenantProfile>>(){}, pageLink); new TypeReference<PageData<TenantProfile>>(){}, pageLink);
Assert.assertFalse(pageData.hasNext()); Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(1, pageData.getTotalElements()); Assert.assertEquals(1, pageData.getTotalElements());
testBroadcastEntityStateChangeEventTimeManyTimeTenantProfile(new TenantProfile(), ComponentLifecycleEvent.DELETED, cntEntity);
} }
@Test @Test
@ -242,7 +283,7 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController
List<TenantProfile> tenantProfiles = new ArrayList<>(); List<TenantProfile> tenantProfiles = new ArrayList<>();
PageLink pageLink = new PageLink(17); PageLink pageLink = new PageLink(17);
PageData<TenantProfile> tenantProfilePageData = doGetTypedWithPageLink("/api/tenantProfiles?", PageData<TenantProfile> tenantProfilePageData = doGetTypedWithPageLink("/api/tenantProfiles?",
new TypeReference<PageData<TenantProfile>>(){}, pageLink); new TypeReference<>(){}, pageLink);
Assert.assertFalse(tenantProfilePageData.hasNext()); Assert.assertFalse(tenantProfilePageData.hasNext());
Assert.assertEquals(1, tenantProfilePageData.getTotalElements()); Assert.assertEquals(1, tenantProfilePageData.getTotalElements());
tenantProfiles.addAll(tenantProfilePageData.getData()); tenantProfiles.addAll(tenantProfilePageData.getData());
@ -294,7 +335,6 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController
tenantProfileData.setConfiguration(new DefaultTenantProfileConfiguration()); tenantProfileData.setConfiguration(new DefaultTenantProfileConfiguration());
tenantProfile.setProfileData(tenantProfileData); tenantProfile.setProfileData(tenantProfileData);
tenantProfile.setDefault(false); tenantProfile.setDefault(false);
tenantProfile.setIsolatedTbCore(false);
tenantProfile.setIsolatedTbRuleEngine(false); tenantProfile.setIsolatedTbRuleEngine(false);
return tenantProfile; return tenantProfile;
} }
@ -322,4 +362,28 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController
profileData.setQueueConfiguration(Collections.singletonList(mainQueueConfiguration)); profileData.setQueueConfiguration(Collections.singletonList(mainQueueConfiguration));
tenantProfile.setProfileData(profileData); tenantProfile.setProfileData(profileData);
} }
private void testBroadcastEntityStateChangeEventTimeManyTimeTenantProfile(TenantProfile tenantProfile, ComponentLifecycleEvent event, int cntTime) {
ArgumentMatcher<TenantProfile> matcherTenantProfile = cntTime == 1 ? argument -> argument.equals(tenantProfile) :
argument -> argument.getClass().equals(TenantProfile.class);
if (ComponentLifecycleEvent.DELETED.equals(event)) {
Mockito.verify(tbClusterService, times( cntTime)).onTenantProfileDelete(Mockito.argThat( matcherTenantProfile),
Mockito.isNull());
testBroadcastEntityStateChangeEventNever(createEntityId_NULL_UUID(new Tenant()));
} else {
Mockito.verify(tbClusterService, times( cntTime)).onTenantProfileChange(Mockito.argThat(matcherTenantProfile),
Mockito.isNull());
TenantProfileId tenantProfileIdId = cntTime == 1 ? tenantProfile.getId() : (TenantProfileId) createEntityId_NULL_UUID(tenantProfile);
testBroadcastEntityStateChangeEventTime(tenantProfileIdId, null, cntTime);
}
Mockito.reset(tbClusterService);
}
private void testBroadcastEntityStateChangeEventNeverTenantProfile() {
Mockito.verify(tbClusterService, never()).onTenantProfileChange(Mockito.any(TenantProfile.class),
Mockito.isNull());
testBroadcastEntityStateChangeEventNever(createEntityId_NULL_UUID(new Tenant()));
Mockito.reset(tbClusterService, auditLogService);
}
} }

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

@ -21,15 +21,18 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.RandomStringUtils;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User; 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.CustomerId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.service.mail.TestMailService; import org.thingsboard.server.service.mail.TestMailService;
import java.util.ArrayList; import java.util.ArrayList;
@ -42,11 +45,14 @@ import static org.hamcrest.Matchers.is;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.dao.model.ModelConstants.SYSTEM_TENANT;
public abstract class BaseUserControllerTest extends AbstractControllerTest { public abstract class BaseUserControllerTest extends AbstractControllerTest {
private IdComparator<User> idComparator = new IdComparator<>(); private IdComparator<User> idComparator = new IdComparator<>();
private CustomerId customerNUULId = (CustomerId) createEntityId_NULL_UUID(new Customer());
@Test @Test
public void testSaveUser() throws Exception { public void testSaveUser() throws Exception {
loginSysAdmin(); loginSysAdmin();
@ -58,6 +64,9 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
user.setEmail(email); user.setEmail(email);
user.setFirstName("Joe"); user.setFirstName("Joe");
user.setLastName("Downs"); user.setLastName("Downs");
Mockito.reset(tbClusterService, auditLogService);
User savedUser = doPost("/api/user", user, User.class); User savedUser = doPost("/api/user", user, User.class);
Assert.assertNotNull(savedUser); Assert.assertNotNull(savedUser);
Assert.assertNotNull(savedUser.getId()); Assert.assertNotNull(savedUser.getId());
@ -67,6 +76,11 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
User foundUser = doGet("/api/user/" + savedUser.getId().getId().toString(), User.class); User foundUser = doGet("/api/user/" + savedUser.getId().getId().toString(), User.class);
Assert.assertEquals(foundUser, savedUser); Assert.assertEquals(foundUser, savedUser);
testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(foundUser, foundUser,
SYSTEM_TENANT, customerNUULId, null, SYS_ADMIN_EMAIL,
ActionType.ADDED, ActionType.ADDED, 1, 1, 1);
Mockito.reset(tbClusterService, auditLogService);
logout(); logout();
doGet("/api/noauth/activate?activateToken={activateToken}", TestMailService.currentActivateToken) doGet("/api/noauth/activate?activateToken={activateToken}", TestMailService.currentActivateToken)
.andExpect(status().isSeeOther()) .andExpect(status().isSeeOther())
@ -94,14 +108,24 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
.andExpect(jsonPath("$.email", is(email))); .andExpect(jsonPath("$.email", is(email)));
loginSysAdmin(); loginSysAdmin();
foundUser = doGet("/api/user/" + savedUser.getId().getId().toString(), User.class);
Mockito.reset(tbClusterService, auditLogService);
doDelete("/api/user/" + savedUser.getId().getId().toString()) doDelete("/api/user/" + savedUser.getId().getId().toString())
.andExpect(status().isOk()); .andExpect(status().isOk());
testNotifyEntityOneTimeMsgToEdgeServiceNever(foundUser, foundUser.getId(), foundUser.getId(),
SYSTEM_TENANT, customerNUULId, null, SYS_ADMIN_EMAIL,
ActionType.DELETED, foundUser.getId().getId().toString());
} }
@Test @Test
public void testSaveUserWithViolationOfFiledValidation() throws Exception { public void testSaveUserWithViolationOfFiledValidation() throws Exception {
loginSysAdmin(); loginSysAdmin();
Mockito.reset(tbClusterService, auditLogService);
String email = "tenant2@thingsboard.org"; String email = "tenant2@thingsboard.org";
User user = new User(); User user = new User();
user.setAuthority(Authority.TENANT_ADMIN); user.setAuthority(Authority.TENANT_ADMIN);
@ -109,10 +133,26 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
user.setEmail(email); user.setEmail(email);
user.setFirstName(RandomStringUtils.randomAlphabetic(300)); user.setFirstName(RandomStringUtils.randomAlphabetic(300));
user.setLastName("Downs"); user.setLastName("Downs");
doPost("/api/user", user).andExpect(statusReason(containsString("Validation error: length of first name must be equal or less than 255"))); String msgError = msgErrorFieldLength("first name");
doPost("/api/user", user)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString(msgError)));
testNotifyEntityEqualsOneTimeServiceNeverError(user,
SYSTEM_TENANT, null, SYS_ADMIN_EMAIL,
ActionType.ADDED, new DataValidationException(msgError));
Mockito.reset(tbClusterService, auditLogService);
user.setFirstName("Normal name"); user.setFirstName("Normal name");
msgError = msgErrorFieldLength("last name");
user.setLastName(RandomStringUtils.randomAlphabetic(300)); user.setLastName(RandomStringUtils.randomAlphabetic(300));
doPost("/api/user", user).andExpect(statusReason(containsString("length of last name must be equal or less than 255"))); doPost("/api/user", user)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString(msgError)));
testNotifyEntityEqualsOneTimeServiceNeverError(user,
SYSTEM_TENANT, null, SYS_ADMIN_EMAIL,
ActionType.ADDED, new DataValidationException(msgError));
} }
@Test @Test
@ -128,9 +168,16 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
loginDifferentTenant(); loginDifferentTenant();
doPost("/api/user", tenantAdmin, User.class, status().isForbidden());
deleteDifferentTenant();
Mockito.reset(tbClusterService, auditLogService);
doPost("/api/user", tenantAdmin)
.andExpect(status().isForbidden())
.andExpect(statusReason(containsString(msgErrorPermission)));
testNotifyEntityNever(tenantAdmin.getId(), tenantAdmin);
deleteDifferentTenant();
} }
@Test @Test
@ -162,7 +209,9 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
.put("resetToken", TestMailService.currentResetPasswordToken) .put("resetToken", TestMailService.currentResetPasswordToken)
.put("password", "testPassword2"); .put("password", "testPassword2");
JsonNode tokenInfo = readResponse(doPost("/api/noauth/resetPassword", resetPasswordRequest).andExpect(status().isOk()), JsonNode.class); JsonNode tokenInfo = readResponse(
doPost("/api/noauth/resetPassword", resetPasswordRequest)
.andExpect(status().isOk()), JsonNode.class);
validateAndSetJwtToken(tokenInfo, email); validateAndSetJwtToken(tokenInfo, email);
doGet("/api/auth/user") doGet("/api/auth/user")
@ -205,6 +254,8 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
public void testSaveUserWithSameEmail() throws Exception { public void testSaveUserWithSameEmail() throws Exception {
loginSysAdmin(); loginSysAdmin();
Mockito.reset(tbClusterService, auditLogService);
String email = TENANT_ADMIN_EMAIL; String email = TENANT_ADMIN_EMAIL;
User user = new User(); User user = new User();
user.setAuthority(Authority.TENANT_ADMIN); user.setAuthority(Authority.TENANT_ADMIN);
@ -213,15 +264,22 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
user.setFirstName("Joe"); user.setFirstName("Joe");
user.setLastName("Downs"); user.setLastName("Downs");
String msgError = "User with email '" + email + "' already present in database";
doPost("/api/user", user) doPost("/api/user", user)
.andExpect(status().isBadRequest()) .andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("User with email '" + email + "' already present in database"))); .andExpect(statusReason(containsString(msgError)));
testNotifyEntityEqualsOneTimeServiceNeverError(user,
SYSTEM_TENANT, null, SYS_ADMIN_EMAIL,
ActionType.ADDED, new DataValidationException(msgError));
} }
@Test @Test
public void testSaveUserWithInvalidEmail() throws Exception { public void testSaveUserWithInvalidEmail() throws Exception {
loginSysAdmin(); loginSysAdmin();
Mockito.reset(tbClusterService, auditLogService);
String email = "tenant_thingsboard.org"; String email = "tenant_thingsboard.org";
User user = new User(); User user = new User();
user.setAuthority(Authority.TENANT_ADMIN); user.setAuthority(Authority.TENANT_ADMIN);
@ -230,39 +288,59 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
user.setFirstName("Joe"); user.setFirstName("Joe");
user.setLastName("Downs"); user.setLastName("Downs");
String msgError = "Invalid email address format '" + email + "'";
doPost("/api/user", user) doPost("/api/user", user)
.andExpect(status().isBadRequest()) .andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Invalid email address format '" + email + "'"))); .andExpect(statusReason(containsString(msgError)));
testNotifyEntityEqualsOneTimeServiceNeverError(user,
SYSTEM_TENANT, null, SYS_ADMIN_EMAIL,
ActionType.ADDED, new DataValidationException(msgError));
} }
@Test @Test
public void testSaveUserWithEmptyEmail() throws Exception { public void testSaveUserWithEmptyEmail() throws Exception {
loginSysAdmin(); loginSysAdmin();
Mockito.reset(tbClusterService, auditLogService);
User user = new User(); User user = new User();
user.setAuthority(Authority.TENANT_ADMIN); user.setAuthority(Authority.TENANT_ADMIN);
user.setTenantId(tenantId); user.setTenantId(tenantId);
user.setFirstName("Joe"); user.setFirstName("Joe");
user.setLastName("Downs"); user.setLastName("Downs");
String msgError = "User email " + msgErrorShouldBeSpecified;
doPost("/api/user", user) doPost("/api/user", user)
.andExpect(status().isBadRequest()) .andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("User email should be specified"))); .andExpect(statusReason(containsString("User email " + msgErrorShouldBeSpecified)));
testNotifyEntityEqualsOneTimeServiceNeverError(user,
SYSTEM_TENANT, null, SYS_ADMIN_EMAIL,
ActionType.ADDED, new DataValidationException(msgError));
} }
@Test @Test
public void testSaveUserWithoutTenant() throws Exception { public void testSaveUserWithoutTenant() throws Exception {
loginSysAdmin(); loginSysAdmin();
Mockito.reset(tbClusterService, auditLogService);
User user = new User(); User user = new User();
user.setAuthority(Authority.TENANT_ADMIN); user.setAuthority(Authority.TENANT_ADMIN);
user.setEmail("tenant2@thingsboard.org"); user.setEmail("tenant2@thingsboard.org");
user.setFirstName("Joe"); user.setFirstName("Joe");
user.setLastName("Downs"); user.setLastName("Downs");
String msgError = "Tenant administrator should be assigned to tenant";
doPost("/api/user", user) doPost("/api/user", user)
.andExpect(status().isBadRequest()) .andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Tenant administrator should be assigned to tenant"))); .andExpect(statusReason(containsString(msgError)));
testNotifyEntityEqualsOneTimeServiceNeverError(user,
SYSTEM_TENANT, null, SYS_ADMIN_EMAIL,
ActionType.ADDED, new DataValidationException(msgError));
} }
@Test @Test
@ -284,8 +362,10 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
doDelete("/api/user/" + savedUser.getId().getId().toString()) doDelete("/api/user/" + savedUser.getId().getId().toString())
.andExpect(status().isOk()); .andExpect(status().isOk());
doGet("/api/user/" + savedUser.getId().getId().toString()) String userIdStr = savedUser.getId().getId().toString();
.andExpect(status().isNotFound()); doGet("/api/user/" + userIdStr)
.andExpect(status().isNotFound())
.andExpect(statusReason(containsString( msgErrorNoFound("User",userIdStr))));
} }
@Test @Test
@ -300,8 +380,11 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
TenantId tenantId = savedTenant.getId(); TenantId tenantId = savedTenant.getId();
Mockito.reset(tbClusterService, auditLogService);
int cntEntity = 64;
List<User> tenantAdmins = new ArrayList<>(); List<User> tenantAdmins = new ArrayList<>();
for (int i = 0; i < 64; i++) { for (int i = 0; i < cntEntity; i++) {
User user = new User(); User user = new User();
user.setAuthority(Authority.TENANT_ADMIN); user.setAuthority(Authority.TENANT_ADMIN);
user.setTenantId(tenantId); user.setTenantId(tenantId);
@ -309,12 +392,18 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
tenantAdmins.add(doPost("/api/user", user, User.class)); tenantAdmins.add(doPost("/api/user", user, User.class));
} }
User testManyUser = new User();
testManyUser.setTenantId(tenantId);
testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(testManyUser, testManyUser,
SYSTEM_TENANT, customerNUULId, null, SYS_ADMIN_EMAIL,
ActionType.ADDED, ActionType.ADDED, cntEntity, cntEntity, cntEntity);
List<User> loadedTenantAdmins = new ArrayList<>(); List<User> loadedTenantAdmins = new ArrayList<>();
PageLink pageLink = new PageLink(33); PageLink pageLink = new PageLink(33);
PageData<User> pageData = null; PageData<User> pageData = null;
do { do {
pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?", pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
new TypeReference<PageData<User>>() { new TypeReference<>() {
}, pageLink); }, pageLink);
loadedTenantAdmins.addAll(pageData.getData()); loadedTenantAdmins.addAll(pageData.getData());
if (pageData.hasNext()) { if (pageData.hasNext()) {
@ -333,7 +422,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
pageLink = new PageLink(33); pageLink = new PageLink(33);
pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?", pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
new TypeReference<PageData<User>>() { new TypeReference<>() {
}, pageLink); }, pageLink);
Assert.assertFalse(pageData.hasNext()); Assert.assertFalse(pageData.hasNext());
Assert.assertTrue(pageData.getData().isEmpty()); Assert.assertTrue(pageData.getData().isEmpty());
@ -377,7 +466,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
PageData<User> pageData = null; PageData<User> pageData = null;
do { do {
pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?", pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
new TypeReference<PageData<User>>() { new TypeReference<>() {
}, pageLink); }, pageLink);
loadedTenantAdminsEmail1.addAll(pageData.getData()); loadedTenantAdminsEmail1.addAll(pageData.getData());
if (pageData.hasNext()) { if (pageData.hasNext()) {
@ -394,7 +483,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
pageLink = new PageLink(16, 0, email2); pageLink = new PageLink(16, 0, email2);
do { do {
pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?", pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
new TypeReference<PageData<User>>() { new TypeReference<>() {
}, pageLink); }, pageLink);
loadedTenantAdminsEmail2.addAll(pageData.getData()); loadedTenantAdminsEmail2.addAll(pageData.getData());
if (pageData.hasNext()) { if (pageData.hasNext()) {
@ -407,14 +496,22 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
Assert.assertEquals(tenantAdminsEmail2, loadedTenantAdminsEmail2); Assert.assertEquals(tenantAdminsEmail2, loadedTenantAdminsEmail2);
Mockito.reset(tbClusterService, auditLogService);
int cntEntity = loadedTenantAdminsEmail1.size();
for (User user : loadedTenantAdminsEmail1) { for (User user : loadedTenantAdminsEmail1) {
doDelete("/api/user/" + user.getId().getId().toString()) doDelete("/api/user/" + user.getId().getId().toString())
.andExpect(status().isOk()); .andExpect(status().isOk());
} }
User testManyUser = new User();
testManyUser.setTenantId(tenantId);
testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(testManyUser, testManyUser,
SYSTEM_TENANT, customerNUULId, null, SYS_ADMIN_EMAIL,
ActionType.DELETED, ActionType.DELETED, cntEntity, 0, cntEntity, new String());
pageLink = new PageLink(4, 0, email1); pageLink = new PageLink(4, 0, email1);
pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?", pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
new TypeReference<PageData<User>>() { new TypeReference<>() {
}, pageLink); }, pageLink);
Assert.assertFalse(pageData.hasNext()); Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size()); Assert.assertEquals(0, pageData.getData().size());
@ -426,7 +523,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
pageLink = new PageLink(4, 0, email2); pageLink = new PageLink(4, 0, email2);
pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?", pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
new TypeReference<PageData<User>>() { new TypeReference<>() {
}, pageLink); }, pageLink);
Assert.assertFalse(pageData.hasNext()); Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size()); Assert.assertEquals(0, pageData.getData().size());
@ -443,7 +540,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
tenantAdmin.setFirstName("Joe"); tenantAdmin.setFirstName("Joe");
tenantAdmin.setLastName("Downs"); tenantAdmin.setLastName("Downs");
tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); createUserAndLogin(tenantAdmin, "testPassword1");
Customer customer = new Customer(); Customer customer = new Customer();
customer.setTitle("My customer"); customer.setTitle("My customer");
@ -465,7 +562,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
PageData<User> pageData = null; PageData<User> pageData = null;
do { do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?", pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
new TypeReference<PageData<User>>() { new TypeReference<>() {
}, pageLink); }, pageLink);
loadedCustomerUsers.addAll(pageData.getData()); loadedCustomerUsers.addAll(pageData.getData());
if (pageData.hasNext()) { if (pageData.hasNext()) {
@ -493,7 +590,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
tenantAdmin.setFirstName("Joe"); tenantAdmin.setFirstName("Joe");
tenantAdmin.setLastName("Downs"); tenantAdmin.setLastName("Downs");
tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); createUserAndLogin(tenantAdmin, "testPassword1");
Customer customer = new Customer(); Customer customer = new Customer();
customer.setTitle("My customer"); customer.setTitle("My customer");
@ -531,10 +628,10 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
List<User> loadedCustomerUsersEmail1 = new ArrayList<>(); List<User> loadedCustomerUsersEmail1 = new ArrayList<>();
PageLink pageLink = new PageLink(33, 0, email1); PageLink pageLink = new PageLink(33, 0, email1);
PageData<User> pageData = null; PageData<User> pageData;
do { do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?", pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
new TypeReference<PageData<User>>() { new TypeReference<>() {
}, pageLink); }, pageLink);
loadedCustomerUsersEmail1.addAll(pageData.getData()); loadedCustomerUsersEmail1.addAll(pageData.getData());
if (pageData.hasNext()) { if (pageData.hasNext()) {
@ -551,7 +648,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
pageLink = new PageLink(16, 0, email2); pageLink = new PageLink(16, 0, email2);
do { do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?", pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
new TypeReference<PageData<User>>() { new TypeReference<>() {
}, pageLink); }, pageLink);
loadedCustomerUsersEmail2.addAll(pageData.getData()); loadedCustomerUsersEmail2.addAll(pageData.getData());
if (pageData.hasNext()) { if (pageData.hasNext()) {
@ -571,7 +668,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
pageLink = new PageLink(4, 0, email1); pageLink = new PageLink(4, 0, email1);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?", pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
new TypeReference<PageData<User>>() { new TypeReference<>() {
}, pageLink); }, pageLink);
Assert.assertFalse(pageData.hasNext()); Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size()); Assert.assertEquals(0, pageData.getData().size());
@ -583,7 +680,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
pageLink = new PageLink(4, 0, email2); pageLink = new PageLink(4, 0, email2);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?", pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
new TypeReference<PageData<User>>() { new TypeReference<>() {
}, pageLink); }, pageLink);
Assert.assertFalse(pageData.hasNext()); Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size()); Assert.assertEquals(0, pageData.getData().size());
@ -591,5 +688,4 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
doDelete("/api/customer/" + customerId.getId().toString()) doDelete("/api/customer/" + customerId.getId().toString())
.andExpect(status().isOk()); .andExpect(status().isOk());
} }
} }

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

@ -21,8 +21,12 @@ import org.junit.After;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User; 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.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.Authority;
@ -34,6 +38,7 @@ import java.util.List;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.dao.model.ModelConstants.SYSTEM_TENANT;
public abstract class BaseWidgetsBundleControllerTest extends AbstractControllerTest { public abstract class BaseWidgetsBundleControllerTest extends AbstractControllerTest {
@ -73,8 +78,16 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController
public void testSaveWidgetsBundle() throws Exception { public void testSaveWidgetsBundle() throws Exception {
WidgetsBundle widgetsBundle = new WidgetsBundle(); WidgetsBundle widgetsBundle = new WidgetsBundle();
widgetsBundle.setTitle("My widgets bundle"); widgetsBundle.setTitle("My widgets bundle");
Mockito.reset(tbClusterService);
WidgetsBundle savedWidgetsBundle = doPost("/api/widgetsBundle", widgetsBundle, WidgetsBundle.class); WidgetsBundle savedWidgetsBundle = doPost("/api/widgetsBundle", widgetsBundle, WidgetsBundle.class);
testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(savedWidgetsBundle, savedWidgetsBundle,
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.ADDED, ActionType.ADDED, 0, 1, 0);
Mockito.reset(tbClusterService);
Assert.assertNotNull(savedWidgetsBundle); Assert.assertNotNull(savedWidgetsBundle);
Assert.assertNotNull(savedWidgetsBundle.getId()); Assert.assertNotNull(savedWidgetsBundle.getId());
Assert.assertNotNull(savedWidgetsBundle.getAlias()); Assert.assertNotNull(savedWidgetsBundle.getAlias());
@ -87,13 +100,25 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController
WidgetsBundle foundWidgetsBundle = doGet("/api/widgetsBundle/" + savedWidgetsBundle.getId().getId().toString(), WidgetsBundle.class); WidgetsBundle foundWidgetsBundle = doGet("/api/widgetsBundle/" + savedWidgetsBundle.getId().getId().toString(), WidgetsBundle.class);
Assert.assertEquals(foundWidgetsBundle.getTitle(), savedWidgetsBundle.getTitle()); Assert.assertEquals(foundWidgetsBundle.getTitle(), savedWidgetsBundle.getTitle());
testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(savedWidgetsBundle, savedWidgetsBundle,
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.UPDATED, ActionType.UPDATED, 0, 1, 0);
} }
@Test @Test
public void testSaveWidgetBundleWithViolationOfLengthValidation() throws Exception { public void testSaveWidgetBundleWithViolationOfLengthValidation() throws Exception {
WidgetsBundle widgetsBundle = new WidgetsBundle(); WidgetsBundle widgetsBundle = new WidgetsBundle();
widgetsBundle.setTitle(RandomStringUtils.randomAlphabetic(300)); widgetsBundle.setTitle(RandomStringUtils.randomAlphabetic(300));
doPost("/api/widgetsBundle", widgetsBundle).andExpect(statusReason(containsString("length of title must be equal or less than 255")));
Mockito.reset(tbClusterService);
String msgError = msgErrorFieldLength("title");
doPost("/api/widgetsBundle", widgetsBundle)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString(msgError)));
testNotifyEntityNever(widgetsBundle.getId(), widgetsBundle);
} }
@Test @Test
@ -103,7 +128,15 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController
WidgetsBundle savedWidgetsBundle = doPost("/api/widgetsBundle", widgetsBundle, WidgetsBundle.class); WidgetsBundle savedWidgetsBundle = doPost("/api/widgetsBundle", widgetsBundle, WidgetsBundle.class);
loginDifferentTenant(); loginDifferentTenant();
doPost("/api/widgetsBundle", savedWidgetsBundle, WidgetsBundle.class, status().isForbidden());
Mockito.reset(tbClusterService);
doPost("/api/widgetsBundle", savedWidgetsBundle)
.andExpect(status().isForbidden())
.andExpect(statusReason(containsString(msgErrorPermission)));
testNotifyEntityNever(savedWidgetsBundle.getId(), savedWidgetsBundle);
deleteDifferentTenant(); deleteDifferentTenant();
} }
@ -121,21 +154,35 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController
public void testDeleteWidgetsBundle() throws Exception { public void testDeleteWidgetsBundle() throws Exception {
WidgetsBundle widgetsBundle = new WidgetsBundle(); WidgetsBundle widgetsBundle = new WidgetsBundle();
widgetsBundle.setTitle("My widgets bundle"); widgetsBundle.setTitle("My widgets bundle");
Mockito.reset(tbClusterService, auditLogService);
WidgetsBundle savedWidgetsBundle = doPost("/api/widgetsBundle", widgetsBundle, WidgetsBundle.class); WidgetsBundle savedWidgetsBundle = doPost("/api/widgetsBundle", widgetsBundle, WidgetsBundle.class);
doDelete("/api/widgetsBundle/"+savedWidgetsBundle.getId().getId().toString()) doDelete("/api/widgetsBundle/"+savedWidgetsBundle.getId().getId().toString())
.andExpect(status().isOk()); .andExpect(status().isOk());
doGet("/api/widgetsBundle/"+savedWidgetsBundle.getId().getId().toString()) String savedWidgetsBundleIdStr = savedWidgetsBundle.getId().getId().toString();
.andExpect(status().isNotFound()); doGet("/api/widgetsBundle/" + savedWidgetsBundleIdStr)
.andExpect(status().isNotFound())
.andExpect(statusReason(containsString(msgErrorNoFound("Widgets bundle", savedWidgetsBundleIdStr))));
testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(savedWidgetsBundle, savedWidgetsBundle,
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.DELETED, ActionType.DELETED, 0, 1, 0);
} }
@Test @Test
public void testSaveWidgetsBundleWithEmptyTitle() throws Exception { public void testSaveWidgetsBundleWithEmptyTitle() throws Exception {
Mockito.reset(tbClusterService, auditLogService);
WidgetsBundle widgetsBundle = new WidgetsBundle(); WidgetsBundle widgetsBundle = new WidgetsBundle();
doPost("/api/widgetsBundle", widgetsBundle) doPost("/api/widgetsBundle", widgetsBundle)
.andExpect(status().isBadRequest()) .andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Widgets bundle title should be specified"))); .andExpect(statusReason(containsString("Widgets bundle title " + msgErrorShouldBeSpecified)));
testNotifyEntityNever(widgetsBundle.getId(), widgetsBundle);
} }
@Test @Test
@ -144,10 +191,14 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController
widgetsBundle.setTitle("My widgets bundle"); widgetsBundle.setTitle("My widgets bundle");
WidgetsBundle savedWidgetsBundle = doPost("/api/widgetsBundle", widgetsBundle, WidgetsBundle.class); WidgetsBundle savedWidgetsBundle = doPost("/api/widgetsBundle", widgetsBundle, WidgetsBundle.class);
savedWidgetsBundle.setAlias("new_alias"); savedWidgetsBundle.setAlias("new_alias");
Mockito.reset(tbClusterService);
doPost("/api/widgetsBundle", savedWidgetsBundle) doPost("/api/widgetsBundle", savedWidgetsBundle)
.andExpect(status().isBadRequest()) .andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Update of widgets bundle alias is prohibited"))); .andExpect(statusReason(containsString("Update of widgets bundle alias is prohibited")));
testNotifyEntityNever(savedWidgetsBundle.getId(), savedWidgetsBundle);
} }
@Test @Test
@ -156,16 +207,22 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController
login(tenantAdmin.getEmail(), "testPassword1"); login(tenantAdmin.getEmail(), "testPassword1");
List<WidgetsBundle> sysWidgetsBundles = doGetTyped("/api/widgetsBundles?", List<WidgetsBundle> sysWidgetsBundles = doGetTyped("/api/widgetsBundles?",
new TypeReference<List<WidgetsBundle>>(){}); new TypeReference<>(){});
Mockito.reset(tbClusterService);
int cntEntity = 73;
List<WidgetsBundle> widgetsBundles = new ArrayList<>(); List<WidgetsBundle> widgetsBundles = new ArrayList<>();
for (int i=0;i<73;i++) { for (int i=0;i<cntEntity;i++) {
WidgetsBundle widgetsBundle = new WidgetsBundle(); WidgetsBundle widgetsBundle = new WidgetsBundle();
widgetsBundle.setTitle("Widgets bundle"+i); widgetsBundle.setTitle("Widgets bundle"+i);
widgetsBundles.add(doPost("/api/widgetsBundle", widgetsBundle, WidgetsBundle.class)); widgetsBundles.add(doPost("/api/widgetsBundle", widgetsBundle, WidgetsBundle.class));
} }
testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new WidgetsBundle(), new WidgetsBundle(),
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.ADDED, ActionType.ADDED, 0, cntEntity, 0);
widgetsBundles.addAll(sysWidgetsBundles); widgetsBundles.addAll(sysWidgetsBundles);
List<WidgetsBundle> loadedWidgetsBundles = new ArrayList<>(); List<WidgetsBundle> loadedWidgetsBundles = new ArrayList<>();
@ -173,7 +230,7 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController
PageData<WidgetsBundle> pageData; PageData<WidgetsBundle> pageData;
do { do {
pageData = doGetTypedWithPageLink("/api/widgetsBundles?", pageData = doGetTypedWithPageLink("/api/widgetsBundles?",
new TypeReference<PageData<WidgetsBundle>>(){}, pageLink); new TypeReference<>(){}, pageLink);
loadedWidgetsBundles.addAll(pageData.getData()); loadedWidgetsBundles.addAll(pageData.getData());
if (pageData.hasNext()) { if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink(); pageLink = pageLink.nextPageLink();
@ -192,10 +249,11 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController
loginSysAdmin(); loginSysAdmin();
List<WidgetsBundle> sysWidgetsBundles = doGetTyped("/api/widgetsBundles?", List<WidgetsBundle> sysWidgetsBundles = doGetTyped("/api/widgetsBundles?",
new TypeReference<List<WidgetsBundle>>(){}); new TypeReference<>(){});
int cntEntity = 120;
List<WidgetsBundle> createdWidgetsBundles = new ArrayList<>(); List<WidgetsBundle> createdWidgetsBundles = new ArrayList<>();
for (int i=0;i<120;i++) { for (int i=0;i<cntEntity;i++) {
WidgetsBundle widgetsBundle = new WidgetsBundle(); WidgetsBundle widgetsBundle = new WidgetsBundle();
widgetsBundle.setTitle("Widgets bundle"+i); widgetsBundle.setTitle("Widgets bundle"+i);
createdWidgetsBundles.add(doPost("/api/widgetsBundle", widgetsBundle, WidgetsBundle.class)); createdWidgetsBundles.add(doPost("/api/widgetsBundle", widgetsBundle, WidgetsBundle.class));
@ -209,7 +267,7 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController
PageData<WidgetsBundle> pageData; PageData<WidgetsBundle> pageData;
do { do {
pageData = doGetTypedWithPageLink("/api/widgetsBundles?", pageData = doGetTypedWithPageLink("/api/widgetsBundles?",
new TypeReference<PageData<WidgetsBundle>>(){}, pageLink); new TypeReference<>(){}, pageLink);
loadedWidgetsBundles.addAll(pageData.getData()); loadedWidgetsBundles.addAll(pageData.getData());
if (pageData.hasNext()) { if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink(); pageLink = pageLink.nextPageLink();
@ -221,11 +279,17 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController
Assert.assertEquals(widgetsBundles, loadedWidgetsBundles); Assert.assertEquals(widgetsBundles, loadedWidgetsBundles);
Mockito.reset(tbClusterService);
for (WidgetsBundle widgetsBundle : createdWidgetsBundles) { for (WidgetsBundle widgetsBundle : createdWidgetsBundles) {
doDelete("/api/widgetsBundle/"+widgetsBundle.getId().getId().toString()) doDelete("/api/widgetsBundle/"+widgetsBundle.getId().getId().toString())
.andExpect(status().isOk()); .andExpect(status().isOk());
} }
testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new WidgetsBundle(), new WidgetsBundle(),
SYSTEM_TENANT, (CustomerId) createEntityId_NULL_UUID(new Customer()), null, SYS_ADMIN_EMAIL,
ActionType.DELETED, ActionType.DELETED, 0, cntEntity, 0);
pageLink = new PageLink(17); pageLink = new PageLink(17);
loadedWidgetsBundles.clear(); loadedWidgetsBundles.clear();
do { do {
@ -262,7 +326,7 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController
widgetsBundles.addAll(sysWidgetsBundles); widgetsBundles.addAll(sysWidgetsBundles);
List<WidgetsBundle> loadedWidgetsBundles = doGetTyped("/api/widgetsBundles?", List<WidgetsBundle> loadedWidgetsBundles = doGetTyped("/api/widgetsBundles?",
new TypeReference<List<WidgetsBundle>>(){}); new TypeReference<>(){});
Collections.sort(widgetsBundles, idComparator); Collections.sort(widgetsBundles, idComparator);
Collections.sort(loadedWidgetsBundles, idComparator); Collections.sort(loadedWidgetsBundles, idComparator);
@ -277,7 +341,7 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController
List<WidgetsBundle> sysWidgetsBundles = doGetTyped("/api/widgetsBundles?", List<WidgetsBundle> sysWidgetsBundles = doGetTyped("/api/widgetsBundles?",
new TypeReference<List<WidgetsBundle>>(){}); new TypeReference<>(){});
List<WidgetsBundle> createdSystemWidgetsBundles = new ArrayList<>(); List<WidgetsBundle> createdSystemWidgetsBundles = new ArrayList<>();
for (int i=0;i<82;i++) { for (int i=0;i<82;i++) {
@ -324,7 +388,7 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController
} }
loadedWidgetsBundles = doGetTyped("/api/widgetsBundles?", loadedWidgetsBundles = doGetTyped("/api/widgetsBundles?",
new TypeReference<List<WidgetsBundle>>(){}); new TypeReference<>(){});
Collections.sort(sysWidgetsBundles, idComparator); Collections.sort(sysWidgetsBundles, idComparator);
Collections.sort(loadedWidgetsBundles, idComparator); Collections.sort(loadedWidgetsBundles, idComparator);

7
application/src/test/java/org/thingsboard/server/edge/BaseEdgeTest.java

@ -163,7 +163,6 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
private Tenant savedTenant; private Tenant savedTenant;
private TenantId tenantId; private TenantId tenantId;
private User tenantAdmin; private User tenantAdmin;
private QueueId defaultQueueId;
private DeviceProfile thermostatDeviceProfile; private DeviceProfile thermostatDeviceProfile;
@ -186,8 +185,6 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
tenantId = savedTenant.getId(); tenantId = savedTenant.getId();
Assert.assertNotNull(savedTenant); Assert.assertNotNull(savedTenant);
defaultQueueId = getRandomQueueId();
tenantAdmin = new User(); tenantAdmin = new User();
tenantAdmin.setAuthority(Authority.TENANT_ADMIN); tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
tenantAdmin.setTenantId(savedTenant.getId()); tenantAdmin.setTenantId(savedTenant.getId());
@ -378,7 +375,7 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
@Test @Test
public void testDeviceProfiles() throws Exception { public void testDeviceProfiles() throws Exception {
// 1 // 1
DeviceProfile deviceProfile = this.createDeviceProfile("ONE_MORE_DEVICE_PROFILE", null, defaultQueueId); DeviceProfile deviceProfile = this.createDeviceProfile("ONE_MORE_DEVICE_PROFILE", null);
extendDeviceProfileData(deviceProfile); extendDeviceProfileData(deviceProfile);
edgeImitator.expectMessageAmount(1); edgeImitator.expectMessageAmount(1);
deviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); deviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
@ -389,8 +386,6 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, deviceProfileUpdateMsg.getMsgType()); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, deviceProfileUpdateMsg.getMsgType());
Assert.assertEquals(deviceProfileUpdateMsg.getIdMSB(), deviceProfile.getUuidId().getMostSignificantBits()); Assert.assertEquals(deviceProfileUpdateMsg.getIdMSB(), deviceProfile.getUuidId().getMostSignificantBits());
Assert.assertEquals(deviceProfileUpdateMsg.getIdLSB(), deviceProfile.getUuidId().getLeastSignificantBits()); Assert.assertEquals(deviceProfileUpdateMsg.getIdLSB(), deviceProfile.getUuidId().getLeastSignificantBits());
Assert.assertEquals(defaultQueueId.getId().getMostSignificantBits(), deviceProfileUpdateMsg.getDefaultQueueIdMSB());
Assert.assertEquals(defaultQueueId.getId().getLeastSignificantBits(), deviceProfileUpdateMsg.getDefaultQueueIdLSB());
// 2 // 2
edgeImitator.expectMessageAmount(1); edgeImitator.expectMessageAmount(1);

62
application/src/test/java/org/thingsboard/server/service/cluster/routing/HashPartitionServiceTest.java → application/src/test/java/org/thingsboard/server/queue/discovery/HashPartitionServiceTest.java

@ -13,9 +13,11 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.service.cluster.routing; package org.thingsboard.server.queue.discovery;
import com.datastax.driver.core.utils.UUIDs;
import com.datastax.oss.driver.api.core.uuid.Uuids; import com.datastax.oss.driver.api.core.uuid.Uuids;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
@ -29,17 +31,16 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.discovery.HashPartitionService;
import org.thingsboard.server.queue.discovery.QueueRoutingInfoService;
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
import org.thingsboard.server.queue.discovery.TenantRoutingInfoService;
import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
@ -111,15 +112,56 @@ public class HashPartitionServiceTest {
map.put(partition, map.getOrDefault(partition, 0) + 1); map.put(partition, map.getOrDefault(partition, 0) + 1);
} }
List<Map.Entry<Integer, Integer>> data = map.entrySet().stream().sorted(Comparator.comparingInt(Map.Entry::getValue)).collect(Collectors.toList()); checkDispersion(start, map, ITERATIONS, 5.0);
}
@SneakyThrows
@Test
public void testDispersionOnResolveByPartitionIdx() {
int serverCount = 5;
int tenantCount = 1000;
int queueCount = 3;
int partitionCount = 3;
List<TransportProtos.ServiceInfo> services = new ArrayList<>();
for (int i = 0; i < serverCount; i++) {
services.add(TransportProtos.ServiceInfo.newBuilder().setServiceId("RE-" + i).build());
}
long start = System.currentTimeMillis();
Map<String, Integer> map = new HashMap<>();
services.forEach(s -> map.put(s.getServiceId(), 0));
Random random = new Random();
long ts = new SimpleDateFormat("dd-MM-yyyy").parse("06-12-2016").getTime() - TimeUnit.DAYS.toMillis(tenantCount);
for (int tenantIndex = 0; tenantIndex < tenantCount; tenantIndex++) {
TenantId tenantId = new TenantId(UUIDs.startOf(ts));
ts += TimeUnit.DAYS.toMillis(1) + random.nextInt(1000);
for (int queueIndex = 0; queueIndex < queueCount; queueIndex++) {
QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, "queue" + queueIndex, tenantId);
for (int partition = 0; partition < partitionCount; partition++) {
TransportProtos.ServiceInfo serviceInfo = clusterRoutingService.resolveByPartitionIdx(services, queueKey, partition);
String serviceId = serviceInfo.getServiceId();
map.put(serviceId, map.get(serviceId) + 1);
}
}
}
checkDispersion(start, map, tenantCount * queueCount * partitionCount, 10.0);
}
private <T> void checkDispersion(long start, Map<T, Integer> map, int iterations, double maxDiffPercent) {
List<Map.Entry<T, Integer>> data = map.entrySet().stream().sorted(Comparator.comparingInt(Map.Entry::getValue)).collect(Collectors.toList());
long end = System.currentTimeMillis(); long end = System.currentTimeMillis();
double diff = (data.get(data.size() - 1).getValue() - data.get(0).getValue()); double ideal = ((double) iterations) / map.size();
double diffPercent = (diff / ITERATIONS) * 100.0; double diff = Math.max(data.get(data.size() - 1).getValue() - ideal, ideal - data.get(0).getValue());
double diffPercent = (diff / ideal) * 100.0;
System.out.println("Time: " + (end - start) + " Diff: " + diff + "(" + String.format("%f", diffPercent) + "%)"); System.out.println("Time: " + (end - start) + " Diff: " + diff + "(" + String.format("%f", diffPercent) + "%)");
Assert.assertTrue(diffPercent < 0.5); for (Map.Entry<T, Integer> entry : data) {
for (Map.Entry<Integer, Integer> entry : data) {
System.out.println(entry.getKey() + ": " + entry.getValue()); System.out.println(entry.getKey() + ": " + entry.getValue());
} }
Assert.assertTrue(diffPercent < maxDiffPercent);
} }
} }

22
application/src/test/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructorTest.java

@ -23,53 +23,37 @@ import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.MockitoJUnitRunner;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.id.QueueId;
import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.queue.Queue;
import org.thingsboard.server.common.data.rule.NodeConnectionInfo; import org.thingsboard.server.common.data.rule.NodeConnectionInfo;
import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.server.dao.queue.QueueService;
import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.EdgeVersion;
import org.thingsboard.server.gen.edge.v1.RuleChainConnectionInfoProto; import org.thingsboard.server.gen.edge.v1.RuleChainConnectionInfoProto;
import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg;
import org.thingsboard.server.gen.edge.v1.RuleNodeProto; import org.thingsboard.server.gen.edge.v1.RuleNodeProto;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.service.edge.rpc.constructor.rule.RuleChainMetadataConstructorV333;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import static org.mockito.Mockito.mock;
@Slf4j @Slf4j
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class RuleChainMsgConstructorTest { public class RuleChainMsgConstructorTest {
private RuleChainMsgConstructor constructor; private RuleChainMsgConstructor constructor;
private QueueService queueService;
private TenantId tenantId; private TenantId tenantId;
private String queueId = "af588000-6c7c-11ec-bafd-c9a47a5c8d99";
@Before @Before
public void setup() { public void setup() {
queueService = mock(QueueService.class); constructor = new RuleChainMsgConstructor();
constructor = new RuleChainMsgConstructor(queueService);
tenantId = new TenantId(UUID.randomUUID()); tenantId = new TenantId(UUID.randomUUID());
Queue queue = new Queue();
queue.setName("HighPriority");
Mockito.when(queueService.findQueueById(tenantId, new QueueId(UUID.fromString(queueId)))).thenReturn(queue);
} }
@Test @Test
@ -88,7 +72,7 @@ public class RuleChainMsgConstructorTest {
assertCheckpointRuleNodeConfiguration( assertCheckpointRuleNodeConfiguration(
ruleChainMetadataUpdateMsg.getNodesList(), ruleChainMetadataUpdateMsg.getNodesList(),
"{\"queueId\":\"" + queueId + "\"}"); "{\"queueName\":\"HighPriority\"}");
} }
@Test @Test
@ -345,7 +329,7 @@ public class RuleChainMsgConstructorTest {
return createRuleNode(ruleChainId, return createRuleNode(ruleChainId,
"org.thingsboard.rule.engine.flow.TbCheckpointNode", "org.thingsboard.rule.engine.flow.TbCheckpointNode",
"Checkpoint node", "Checkpoint node",
JacksonUtil.OBJECT_MAPPER.readTree("{\"queueId\":\"" + queueId + "\"}"), JacksonUtil.OBJECT_MAPPER.readTree("{\"queueName\":\"HighPriority\"}"),
JacksonUtil.OBJECT_MAPPER.readTree("{\"description\":\"\",\"layoutX\":178,\"layoutY\":647}")); JacksonUtil.OBJECT_MAPPER.readTree("{\"description\":\"\",\"layoutX\":178,\"layoutY\":647}"));
} }

95
application/src/test/java/org/thingsboard/server/transport/coap/attributes/AbstractCoapAttributesIntegrationTest.java

@ -46,11 +46,11 @@ import org.thingsboard.server.transport.coap.CoapTestClient;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
@ -235,25 +235,37 @@ public abstract class AbstractCoapAttributesIntegrationTest extends AbstractCoap
CoapTestCallback callbackCoap = new CoapTestCallback(1); CoapTestCallback callbackCoap = new CoapTestCallback(1);
CoapObserveRelation observeRelation = client.getObserveRelation(callbackCoap); CoapObserveRelation observeRelation = client.getObserveRelation(callbackCoap);
callbackCoap.getLatch().await(3, TimeUnit.SECONDS); String awaitAlias = "await Json Test Subscribe To AttributesUpdates (client.getObserveRelation)";
await(awaitAlias)
.atMost(10, TimeUnit.SECONDS)
.until(() -> CoAP.ResponseCode.CONTENT.equals(callbackCoap.getResponseCode()) &&
callbackCoap.getObserve() != null &&
0 == callbackCoap.getObserve().intValue());
if (emptyCurrentStateNotification) { if (emptyCurrentStateNotification) {
validateUpdateAttributesJsonResponse(callbackCoap, "{}", 0); validateUpdateAttributesJsonResponse(callbackCoap, "{}");
} else { } else {
validateUpdateAttributesJsonResponse(callbackCoap, SHARED_ATTRIBUTES_PAYLOAD_ON_CURRENT_STATE_NOTIFICATION, 0); validateUpdateAttributesJsonResponse(callbackCoap, SHARED_ATTRIBUTES_PAYLOAD_ON_CURRENT_STATE_NOTIFICATION);
} }
CountDownLatch latch = new CountDownLatch(1); int expectedObserveForAttributesUpdate = callbackCoap.getObserve().intValue() + 1;
int expectedObserveCnt = callbackCoap.getObserve().intValue() + 1;
doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", SHARED_ATTRIBUTES_PAYLOAD, String.class, status().isOk()); doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", SHARED_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
latch.await(3, TimeUnit.SECONDS); awaitAlias = "await Json Test Subscribe To AttributesUpdates (add attributes)";
validateUpdateAttributesJsonResponse(callbackCoap, SHARED_ATTRIBUTES_PAYLOAD, expectedObserveCnt); await(awaitAlias)
.atMost(10, TimeUnit.SECONDS)
latch = new CountDownLatch(1); .until(() -> CoAP.ResponseCode.CONTENT.equals(callbackCoap.getResponseCode()) &&
int expectedObserveBeforeDeleteCnt = callbackCoap.getObserve().intValue() + 1; callbackCoap.getObserve() != null &&
expectedObserveForAttributesUpdate == callbackCoap.getObserve().intValue());
validateUpdateAttributesJsonResponse(callbackCoap, SHARED_ATTRIBUTES_PAYLOAD);
int expectedObserveForAttributesDelete = callbackCoap.getObserve().intValue() + 1;
doDelete("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/SHARED_SCOPE?keys=sharedJson", String.class); doDelete("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/SHARED_SCOPE?keys=sharedJson", String.class);
latch.await(3, TimeUnit.SECONDS); awaitAlias = "await Json Test Subscribe To AttributesUpdates (deleted attributes)";
validateUpdateAttributesJsonResponse(callbackCoap, SHARED_ATTRIBUTES_DELETED_RESPONSE, expectedObserveBeforeDeleteCnt); await(awaitAlias)
.atMost(10, TimeUnit.SECONDS)
.until(() -> CoAP.ResponseCode.CONTENT.equals(callbackCoap.getResponseCode()) &&
callbackCoap.getObserve() != null &&
expectedObserveForAttributesDelete == callbackCoap.getObserve().intValue());
validateUpdateAttributesJsonResponse(callbackCoap, SHARED_ATTRIBUTES_DELETED_RESPONSE);
observeRelation.proactiveCancel(); observeRelation.proactiveCancel();
assertTrue(observeRelation.isCanceled()); assertTrue(observeRelation.isCanceled());
@ -269,8 +281,13 @@ public abstract class AbstractCoapAttributesIntegrationTest extends AbstractCoap
client = new CoapTestClient(accessToken, FeatureType.ATTRIBUTES); client = new CoapTestClient(accessToken, FeatureType.ATTRIBUTES);
CoapTestCallback callbackCoap = new CoapTestCallback(1); CoapTestCallback callbackCoap = new CoapTestCallback(1);
String awaitAlias = "await Proto Test Subscribe To Attributes Updates (add attributes)";
CoapObserveRelation observeRelation = client.getObserveRelation(callbackCoap); CoapObserveRelation observeRelation = client.getObserveRelation(callbackCoap);
callbackCoap.getLatch().await(3, TimeUnit.SECONDS); await(awaitAlias)
.atMost(10, TimeUnit.SECONDS)
.until(() -> CoAP.ResponseCode.CONTENT.equals(callbackCoap.getResponseCode()) &&
callbackCoap.getObserve() != null &&
0 == callbackCoap.getObserve().intValue());
if (emptyCurrentStateNotification) { if (emptyCurrentStateNotification) {
validateEmptyCurrentStateAttributesProtoResponse(callbackCoap); validateEmptyCurrentStateAttributesProtoResponse(callbackCoap);
@ -278,17 +295,25 @@ public abstract class AbstractCoapAttributesIntegrationTest extends AbstractCoap
validateCurrentStateAttributesProtoResponse(callbackCoap); validateCurrentStateAttributesProtoResponse(callbackCoap);
} }
CountDownLatch latch = new CountDownLatch(1); int expectedObserveForAttributesUpdate = callbackCoap.getObserve().intValue() + 1;
int expectedObserveCnt = callbackCoap.getObserve().intValue() + 1;
doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", SHARED_ATTRIBUTES_PAYLOAD, String.class, status().isOk()); doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", SHARED_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
latch.await(3, TimeUnit.SECONDS); awaitAlias = "await Proto Test Subscribe To Attributes Updates (add attributes)";
validateUpdateProtoAttributesResponse(callbackCoap, expectedObserveCnt); await(awaitAlias)
.atMost(10, TimeUnit.SECONDS)
latch = new CountDownLatch(1); .until(() -> CoAP.ResponseCode.CONTENT.equals(callbackCoap.getResponseCode()) &&
int expectedObserveBeforeDeleteCnt = callbackCoap.getObserve().intValue() + 1; callbackCoap.getObserve() != null &&
expectedObserveForAttributesUpdate == callbackCoap.getObserve().intValue());
validateUpdateProtoAttributesResponse(callbackCoap, expectedObserveForAttributesUpdate);
int expectedObserveForAttributesDelete = callbackCoap.getObserve().intValue() + 1;
doDelete("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/SHARED_SCOPE?keys=sharedJson", String.class); doDelete("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/SHARED_SCOPE?keys=sharedJson", String.class);
latch.await(3, TimeUnit.SECONDS); awaitAlias = "await Proto Test Subscribe To Attributes Updates (deleted attributes)";
validateDeleteProtoAttributesResponse(callbackCoap, expectedObserveBeforeDeleteCnt); await(awaitAlias)
.atMost(10, TimeUnit.SECONDS)
.until(() -> CoAP.ResponseCode.CONTENT.equals(callbackCoap.getResponseCode()) &&
callbackCoap.getObserve() != null &&
expectedObserveForAttributesDelete == callbackCoap.getObserve().intValue());
validateDeleteProtoAttributesResponse(callbackCoap, expectedObserveForAttributesDelete);
observeRelation.proactiveCancel(); observeRelation.proactiveCancel();
assertTrue(observeRelation.isCanceled()); assertTrue(observeRelation.isCanceled());
@ -314,27 +339,18 @@ public abstract class AbstractCoapAttributesIntegrationTest extends AbstractCoap
assertTrue(actualSharedKeyValueProtos.containsAll(expectedSharedKeyValueProtos)); assertTrue(actualSharedKeyValueProtos.containsAll(expectedSharedKeyValueProtos));
} }
protected void validateUpdateAttributesJsonResponse(CoapTestCallback callback, String expectedResponse, int expectedObserveCnt) { protected void validateUpdateAttributesJsonResponse(CoapTestCallback callback, String expectedResponse) {
assertNotNull(callback.getPayloadBytes()); assertNotNull(callback.getPayloadBytes());
assertNotNull(callback.getObserve());
assertEquals(CoAP.ResponseCode.CONTENT, callback.getResponseCode());
assertEquals(expectedObserveCnt, callback.getObserve().intValue());
String response = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8); String response = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8);
assertEquals(JacksonUtil.toJsonNode(expectedResponse), JacksonUtil.toJsonNode(response)); assertEquals(JacksonUtil.toJsonNode(expectedResponse), JacksonUtil.toJsonNode(response));
} }
protected void validateEmptyCurrentStateAttributesProtoResponse(CoapTestCallback callback) throws InvalidProtocolBufferException { protected void validateEmptyCurrentStateAttributesProtoResponse(CoapTestCallback callback) throws InvalidProtocolBufferException {
assertArrayEquals(EMPTY_PAYLOAD, callback.getPayloadBytes()); assertArrayEquals(EMPTY_PAYLOAD, callback.getPayloadBytes());
assertNotNull(callback.getObserve());
assertEquals(CoAP.ResponseCode.CONTENT, callback.getResponseCode());
assertEquals(0, callback.getObserve().intValue());
} }
protected void validateCurrentStateAttributesProtoResponse(CoapTestCallback callback) throws InvalidProtocolBufferException { protected void validateCurrentStateAttributesProtoResponse(CoapTestCallback callback) throws InvalidProtocolBufferException {
assertNotNull(callback.getPayloadBytes()); assertNotNull(callback.getPayloadBytes());
assertNotNull(callback.getObserve());
assertEquals(CoAP.ResponseCode.CONTENT, callback.getResponseCode());
assertEquals(0, callback.getObserve().intValue());
TransportProtos.AttributeUpdateNotificationMsg.Builder expectedCurrentStateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder(); TransportProtos.AttributeUpdateNotificationMsg.Builder expectedCurrentStateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder();
TransportProtos.TsKvProto tsKvProtoAttribute1 = getTsKvProto("sharedStr", "value", TransportProtos.KeyValueType.STRING_V); TransportProtos.TsKvProto tsKvProtoAttribute1 = getTsKvProto("sharedStr", "value", TransportProtos.KeyValueType.STRING_V);
TransportProtos.TsKvProto tsKvProtoAttribute2 = getTsKvProto("sharedBool", "false", TransportProtos.KeyValueType.BOOLEAN_V); TransportProtos.TsKvProto tsKvProtoAttribute2 = getTsKvProto("sharedBool", "false", TransportProtos.KeyValueType.BOOLEAN_V);
@ -359,9 +375,6 @@ public abstract class AbstractCoapAttributesIntegrationTest extends AbstractCoap
protected void validateUpdateProtoAttributesResponse(CoapTestCallback callback, int expectedObserveCnt) throws InvalidProtocolBufferException { protected void validateUpdateProtoAttributesResponse(CoapTestCallback callback, int expectedObserveCnt) throws InvalidProtocolBufferException {
assertNotNull(callback.getPayloadBytes()); assertNotNull(callback.getPayloadBytes());
assertNotNull(callback.getObserve());
assertEquals(CoAP.ResponseCode.CONTENT, callback.getResponseCode());
assertEquals(expectedObserveCnt, callback.getObserve().intValue());
TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder(); TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder();
List<TransportProtos.TsKvProto> tsKvProtoList = getTsKvProtoList("shared"); List<TransportProtos.TsKvProto> tsKvProtoList = getTsKvProtoList("shared");
attributeUpdateNotificationMsgBuilder.addAllSharedUpdated(tsKvProtoList); attributeUpdateNotificationMsgBuilder.addAllSharedUpdated(tsKvProtoList);
@ -378,9 +391,6 @@ public abstract class AbstractCoapAttributesIntegrationTest extends AbstractCoap
protected void validateDeleteProtoAttributesResponse(CoapTestCallback callback, int expectedObserveCnt) throws InvalidProtocolBufferException { protected void validateDeleteProtoAttributesResponse(CoapTestCallback callback, int expectedObserveCnt) throws InvalidProtocolBufferException {
assertNotNull(callback.getPayloadBytes()); assertNotNull(callback.getPayloadBytes());
assertNotNull(callback.getObserve());
assertEquals(CoAP.ResponseCode.CONTENT, callback.getResponseCode());
assertEquals(expectedObserveCnt, callback.getObserve().intValue());
TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder(); TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder();
attributeUpdateNotificationMsgBuilder.addSharedDeleted("sharedJson"); attributeUpdateNotificationMsgBuilder.addSharedDeleted("sharedJson");
@ -395,9 +405,10 @@ public abstract class AbstractCoapAttributesIntegrationTest extends AbstractCoap
Awaitility.await("awaitClientAfterCancelObserve") Awaitility.await("awaitClientAfterCancelObserve")
.pollInterval(10, TimeUnit.MILLISECONDS) .pollInterval(10, TimeUnit.MILLISECONDS)
.atMost(5, TimeUnit.SECONDS) .atMost(5, TimeUnit.SECONDS)
.until(()->{ .until(() -> {
log.trace("awaiting defaultTransportService.sessions is empty"); log.trace("awaiting defaultTransportService.sessions is empty");
return defaultTransportService.sessions.isEmpty();}); return defaultTransportService.sessions.isEmpty();
});
} }
private TransportProtos.GetAttributeResponseMsg getExpectedAttributeResponseMsg() { private TransportProtos.GetAttributeResponseMsg getExpectedAttributeResponseMsg() {

62
application/src/test/java/org/thingsboard/server/transport/coap/rpc/AbstractCoapServerSideRpcIntegrationTest.java

@ -43,6 +43,7 @@ import org.thingsboard.server.transport.coap.CoapTestClient;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static org.awaitility.Awaitility.await;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
@ -75,14 +76,23 @@ public abstract class AbstractCoapServerSideRpcIntegrationTest extends AbstractC
CoapTestCallback callbackCoap = new TestCoapCallbackForRPC(client, 1, true, protobuf); CoapTestCallback callbackCoap = new TestCoapCallbackForRPC(client, 1, true, protobuf);
CoapObserveRelation observeRelation = client.getObserveRelation(callbackCoap); CoapObserveRelation observeRelation = client.getObserveRelation(callbackCoap);
callbackCoap.getLatch().await(3, TimeUnit.SECONDS); String awaitAlias = "await One Way Rpc (client.getObserveRelation)";
await(awaitAlias)
.atMost(10, TimeUnit.SECONDS)
.until(() -> CoAP.ResponseCode.VALID.equals(callbackCoap.getResponseCode()) &&
callbackCoap.getObserve() != null &&
0 == callbackCoap.getObserve().intValue());
validateCurrentStateNotification(callbackCoap); validateCurrentStateNotification(callbackCoap);
int expectedObserveCountAfterGpioRequest = callbackCoap.getObserve().intValue() + 1;
CountDownLatch latch = new CountDownLatch(1);
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}"; String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}";
String deviceId = savedDevice.getId().getId().toString(); String deviceId = savedDevice.getId().getId().toString();
String result = doPostAsync("/api/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isOk()); String result = doPostAsync("/api/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isOk());
latch.await(3, TimeUnit.SECONDS); awaitAlias = "await One Way Rpc setGpio(method, params, value)";
await(awaitAlias)
.atMost(10, TimeUnit.SECONDS)
.until(() -> CoAP.ResponseCode.CONTENT.equals(callbackCoap.getResponseCode()) &&
callbackCoap.getObserve() != null &&
expectedObserveCountAfterGpioRequest == callbackCoap.getObserve().intValue());
validateOneWayStateChangedNotification(callbackCoap, result); validateOneWayStateChangedNotification(callbackCoap, result);
observeRelation.proactiveCancel(); observeRelation.proactiveCancel();
@ -94,23 +104,36 @@ public abstract class AbstractCoapServerSideRpcIntegrationTest extends AbstractC
CoapTestCallback callbackCoap = new TestCoapCallbackForRPC(client, 1, false, protobuf); CoapTestCallback callbackCoap = new TestCoapCallbackForRPC(client, 1, false, protobuf);
CoapObserveRelation observeRelation = client.getObserveRelation(callbackCoap); CoapObserveRelation observeRelation = client.getObserveRelation(callbackCoap);
callbackCoap.getLatch().await(3, TimeUnit.SECONDS); String awaitAlias = "await Two Way Rpc (client.getObserveRelation)";
await(awaitAlias)
.atMost(10, TimeUnit.SECONDS)
.until(() -> CoAP.ResponseCode.VALID.equals(callbackCoap.getResponseCode()) &&
callbackCoap.getObserve() != null &&
0 == callbackCoap.getObserve().intValue());
validateCurrentStateNotification(callbackCoap); validateCurrentStateNotification(callbackCoap);
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"26\",\"value\": 1}}"; String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"26\",\"value\": 1}}";
String deviceId = savedDevice.getId().getId().toString(); String deviceId = savedDevice.getId().getId().toString();
int expectedObserveCountAfterGpioRequest1 = callbackCoap.getObserve().intValue() + 1;
String actualResult = doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk()); String actualResult = doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
callbackCoap.getLatch().await(3, TimeUnit.SECONDS); awaitAlias = "await Two Way Rpc (setGpio(method, params, value) first";
await(awaitAlias)
validateTwoWayStateChangedNotification(callbackCoap, 1, expectedResponseResult, actualResult); .atMost(10, TimeUnit.SECONDS)
.until(() -> CoAP.ResponseCode.CONTENT.equals(callbackCoap.getResponseCode()) &&
CountDownLatch latch = new CountDownLatch(1); callbackCoap.getObserve() != null &&
expectedObserveCountAfterGpioRequest1 == callbackCoap.getObserve().intValue());
validateTwoWayStateChangedNotification(callbackCoap, expectedResponseResult, actualResult);
int expectedObserveCountAfterGpioRequest2 = callbackCoap.getObserve().intValue() + 1;
actualResult = doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk()); actualResult = doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
callbackCoap.getLatch().await(3, TimeUnit.SECONDS); awaitAlias = "await Two Way Rpc (setGpio(method, params, value) first";
await(awaitAlias)
.atMost(10, TimeUnit.SECONDS)
.until(() -> CoAP.ResponseCode.CONTENT.equals(callbackCoap.getResponseCode()) &&
callbackCoap.getObserve() != null &&
expectedObserveCountAfterGpioRequest2 == callbackCoap.getObserve().intValue());
validateTwoWayStateChangedNotification(callbackCoap, 2, expectedResponseResult, actualResult); validateTwoWayStateChangedNotification(callbackCoap, expectedResponseResult, actualResult);
observeRelation.proactiveCancel(); observeRelation.proactiveCancel();
assertTrue(observeRelation.isCanceled()); assertTrue(observeRelation.isCanceled());
@ -184,25 +207,16 @@ public abstract class AbstractCoapServerSideRpcIntegrationTest extends AbstractC
private void validateCurrentStateNotification(CoapTestCallback callback) { private void validateCurrentStateNotification(CoapTestCallback callback) {
assertArrayEquals(EMPTY_PAYLOAD, callback.getPayloadBytes()); assertArrayEquals(EMPTY_PAYLOAD, callback.getPayloadBytes());
assertNotNull(callback.getObserve());
assertEquals(callback.getResponseCode(), CoAP.ResponseCode.VALID);
assertEquals(0, callback.getObserve().intValue());
} }
private void validateOneWayStateChangedNotification(CoapTestCallback callback, String result) { private void validateOneWayStateChangedNotification(CoapTestCallback callback, String result) {
assertTrue(StringUtils.isEmpty(result)); assertTrue(StringUtils.isEmpty(result));
assertNotNull(callback.getPayloadBytes()); assertNotNull(callback.getPayloadBytes());
assertNotNull(callback.getObserve());
assertEquals(CoAP.ResponseCode.CONTENT, callback.getResponseCode());
assertEquals(1, callback.getObserve().intValue());
} }
private void validateTwoWayStateChangedNotification(CoapTestCallback callback, int expectedObserveNumber, String expectedResult, String actualResult) { private void validateTwoWayStateChangedNotification(CoapTestCallback callback, String expectedResult, String actualResult) {
assertEquals(expectedResult, actualResult); assertEquals(expectedResult, actualResult);
assertNotNull(callback.getPayloadBytes()); assertNotNull(callback.getPayloadBytes());
assertNotNull(callback.getObserve());
assertEquals(CoAP.ResponseCode.CONTENT, callback.getResponseCode());
assertEquals(expectedObserveNumber, callback.getObserve().intValue());
} }
protected class TestCoapCallbackForRPC extends CoapTestCallback { protected class TestCoapCallbackForRPC extends CoapTestCallback {

1
application/src/test/java/org/thingsboard/server/transport/coap/rpc/CoapServerSideRpcJsonIntegrationTest.java

@ -52,5 +52,4 @@ public class CoapServerSideRpcJsonIntegrationTest extends AbstractCoapServerSide
public void testServerCoapTwoWayRpc() throws Exception { public void testServerCoapTwoWayRpc() throws Exception {
processTwoWayRpcTest("{\"value1\":\"A\",\"value2\":\"B\"}", false); processTwoWayRpcTest("{\"value1\":\"A\",\"value2\":\"B\"}", false);
} }
} }

6
application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java

@ -196,7 +196,7 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M
device.getId().getId().toString(); device.getId().getId().toString();
lwM2MTestClient.start(isStartLw); lwM2MTestClient.start(isStartLw);
await(awaitAlias) await(awaitAlias)
.atMost(1000, TimeUnit.MILLISECONDS) .atMost(20, TimeUnit.SECONDS)
.until(() -> finishState.equals(lwM2MTestClient.getClientState())); .until(() -> finishState.equals(lwM2MTestClient.getClientState()));
Assert.assertEquals(expectedStatuses, lwM2MTestClient.getClientStates()); Assert.assertEquals(expectedStatuses, lwM2MTestClient.getClientStates());
} }
@ -234,7 +234,7 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M
String deviceId = device.getId().getId().toString(); String deviceId = device.getId().getId().toString();
lwM2MTestClient.start(true); lwM2MTestClient.start(true);
await(awaitAlias) await(awaitAlias)
.atMost(1000, TimeUnit.MILLISECONDS) .atMost(20, TimeUnit.SECONDS)
.until(() -> ON_REGISTRATION_SUCCESS.equals(lwM2MTestClient.getClientState())); .until(() -> ON_REGISTRATION_SUCCESS.equals(lwM2MTestClient.getClientState()));
Assert.assertEquals(expectedStatusesLwm2m, lwM2MTestClient.getClientStates()); Assert.assertEquals(expectedStatusesLwm2m, lwM2MTestClient.getClientStates());
@ -246,7 +246,7 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M
expectedStatusesBs.add(ON_DEREGISTRATION_STARTED); expectedStatusesBs.add(ON_DEREGISTRATION_STARTED);
expectedStatusesBs.add(ON_DEREGISTRATION_SUCCESS); expectedStatusesBs.add(ON_DEREGISTRATION_SUCCESS);
await(awaitAlias) await(awaitAlias)
.atMost(1000, TimeUnit.MILLISECONDS) .atMost(20, TimeUnit.SECONDS)
.until(() -> ON_REGISTRATION_SUCCESS.equals(lwM2MTestClient.getClientState())); .until(() -> ON_REGISTRATION_SUCCESS.equals(lwM2MTestClient.getClientState()));
Assert.assertEquals(expectedStatusesBs, lwM2MTestClient.getClientStates()); Assert.assertEquals(expectedStatusesBs, lwM2MTestClient.getClientStates());
} }

2
common/actor/pom.xml

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

2
common/cache/pom.xml

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

2
common/cluster-api/pom.xml

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

9
common/cluster-api/src/main/proto/queue.proto

@ -273,7 +273,6 @@ message GetTenantRoutingInfoRequestMsg {
} }
message GetTenantRoutingInfoResponseMsg { message GetTenantRoutingInfoResponseMsg {
bool isolatedTbCore = 1;
bool isolatedTbRuleEngine = 2; bool isolatedTbRuleEngine = 2;
} }
@ -804,9 +803,8 @@ message EntityContentRequestMsg {
message EntityContentResponseMsg { message EntityContentResponseMsg {
string data = 1; string data = 1;
string chunkedMsgId = 2; int32 chunkIndex = 2;
int32 chunkIndex = 3; int32 chunksCount = 3;
int32 chunksCount = 4;
} }
message EntitiesContentRequestMsg { message EntitiesContentRequestMsg {
@ -818,7 +816,8 @@ message EntitiesContentRequestMsg {
message EntitiesContentResponseMsg { message EntitiesContentResponseMsg {
EntityContentResponseMsg item = 1; EntityContentResponseMsg item = 1;
int32 itemsCount = 2; int32 itemIdx = 2;
int32 itemsCount = 3;
} }
message VersionsDiffRequestMsg { message VersionsDiffRequestMsg {

2
common/coap-server/pom.xml

@ -22,7 +22,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>3.4.0-SNAPSHOT</version> <version>3.4.1-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

2
common/dao-api/pom.xml

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

2
common/data/pom.xml

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

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

@ -58,13 +58,13 @@ public class AdminSettings extends BaseData<AdminSettingsId> implements HasTenan
return super.getId(); return super.getId();
} }
@ApiModelProperty(position = 2, value = "Timestamp of the settings creation, in milliseconds", example = "1609459200000", readOnly = true) @ApiModelProperty(position = 2, value = "Timestamp of the settings creation, in milliseconds", example = "1609459200000", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
@Override @Override
public long getCreatedTime() { public long getCreatedTime() {
return super.getCreatedTime(); return super.getCreatedTime();
} }
@ApiModelProperty(position = 3, value = "JSON object with Tenant Id.", readOnly = true) @ApiModelProperty(position = 3, value = "JSON object with Tenant Id.", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
public TenantId getTenantId() { public TenantId getTenantId() {
return tenantId; return tenantId;
} }

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

@ -83,7 +83,7 @@ public class Customer extends ContactBased<CustomerId> implements HasTenantId, E
return super.getId(); return super.getId();
} }
@ApiModelProperty(position = 2, value = "Timestamp of the customer creation, in milliseconds", example = "1609459200000", readOnly = true) @ApiModelProperty(position = 2, value = "Timestamp of the customer creation, in milliseconds", example = "1609459200000", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
@Override @Override
public long getCreatedTime() { public long getCreatedTime() {
return super.getCreatedTime(); return super.getCreatedTime();
@ -159,7 +159,7 @@ public class Customer extends ContactBased<CustomerId> implements HasTenantId, E
@Override @Override
@JsonProperty(access = Access.READ_ONLY) @JsonProperty(access = Access.READ_ONLY)
@ApiModelProperty(position = 4, value = "Name of the customer. Read-only, duplicated from title for backward compatibility", example = "Company A", readOnly = true) @ApiModelProperty(position = 4, value = "Name of the customer. Read-only, duplicated from title for backward compatibility", example = "Company A", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
public String getName() { public String getName() {
return title; return title;
} }

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

@ -71,13 +71,13 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa
return super.getId(); return super.getId();
} }
@ApiModelProperty(position = 2, value = "Timestamp of the dashboard creation, in milliseconds", example = "1609459200000", readOnly = true) @ApiModelProperty(position = 2, value = "Timestamp of the dashboard creation, in milliseconds", example = "1609459200000", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
@Override @Override
public long getCreatedTime() { public long getCreatedTime() {
return super.getCreatedTime(); return super.getCreatedTime();
} }
@ApiModelProperty(position = 3, value = "JSON object with Tenant Id. Tenant Id of the dashboard can't be changed.", readOnly = true) @ApiModelProperty(position = 3, value = "JSON object with Tenant Id. Tenant Id of the dashboard can't be changed.", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
public TenantId getTenantId() { public TenantId getTenantId() {
return tenantId; return tenantId;
} }
@ -95,7 +95,7 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa
this.title = title; this.title = title;
} }
@ApiModelProperty(position = 8, value = "Thumbnail picture for rendering of the dashboards in a grid view on mobile devices.", readOnly = true) @ApiModelProperty(position = 8, value = "Thumbnail picture for rendering of the dashboards in a grid view on mobile devices.", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
public String getImage() { public String getImage() {
return image; return image;
} }
@ -104,7 +104,7 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa
this.image = image; this.image = image;
} }
@ApiModelProperty(position = 5, value = "List of assigned customers with their info.", readOnly = true) @ApiModelProperty(position = 5, value = "List of assigned customers with their info.", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
public Set<ShortCustomerInfo> getAssignedCustomers() { public Set<ShortCustomerInfo> getAssignedCustomers() {
return assignedCustomers; return assignedCustomers;
} }
@ -113,7 +113,7 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa
this.assignedCustomers = assignedCustomers; this.assignedCustomers = assignedCustomers;
} }
@ApiModelProperty(position = 6, value = "Hide dashboard from mobile devices. Useful if the dashboard is not designed for small screens.", readOnly = true) @ApiModelProperty(position = 6, value = "Hide dashboard from mobile devices. Useful if the dashboard is not designed for small screens.", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
public boolean isMobileHide() { public boolean isMobileHide() {
return mobileHide; return mobileHide;
} }
@ -122,7 +122,7 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa
this.mobileHide = mobileHide; this.mobileHide = mobileHide;
} }
@ApiModelProperty(position = 7, value = "Order on mobile devices. Useful to adjust sorting of the dashboards for mobile applications", readOnly = true) @ApiModelProperty(position = 7, value = "Order on mobile devices. Useful to adjust sorting of the dashboards for mobile applications", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
public Integer getMobileOrder() { public Integer getMobileOrder() {
return mobileOrder; return mobileOrder;
} }
@ -180,7 +180,7 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa
} }
} }
@ApiModelProperty(position = 4, value = "Same as title of the dashboard. Read-only field. Update the 'title' to change the 'name' of the dashboard.", readOnly = true) @ApiModelProperty(position = 4, value = "Same as title of the dashboard. Read-only field. Update the 'title' to change the 'name' of the dashboard.", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
@Override @Override
@JsonProperty(access = JsonProperty.Access.READ_ONLY) @JsonProperty(access = JsonProperty.Access.READ_ONLY)
public String getName() { public String getName() {

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

@ -112,13 +112,13 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
return super.getId(); return super.getId();
} }
@ApiModelProperty(position = 2, value = "Timestamp of the device creation, in milliseconds", example = "1609459200000", readOnly = true) @ApiModelProperty(position = 2, value = "Timestamp of the device creation, in milliseconds", example = "1609459200000", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
@Override @Override
public long getCreatedTime() { public long getCreatedTime() {
return super.getCreatedTime(); return super.getCreatedTime();
} }
@ApiModelProperty(position = 3, value = "JSON object with Tenant Id. Use 'assignDeviceToTenant' to change the Tenant Id.", readOnly = true) @ApiModelProperty(position = 3, value = "JSON object with Tenant Id. Use 'assignDeviceToTenant' to change the Tenant Id.", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
public TenantId getTenantId() { public TenantId getTenantId() {
return tenantId; return tenantId;
} }
@ -127,7 +127,7 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
this.tenantId = tenantId; this.tenantId = tenantId;
} }
@ApiModelProperty(position = 4, value = "JSON object with Customer Id. Use 'assignDeviceToCustomer' to change the Customer Id.", readOnly = true) @ApiModelProperty(position = 4, value = "JSON object with Customer Id. Use 'assignDeviceToCustomer' to change the Customer Id.", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
public CustomerId getCustomerId() { public CustomerId getCustomerId() {
return customerId; return customerId;
} }

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

@ -24,11 +24,11 @@ import org.thingsboard.server.common.data.id.DeviceId;
@Data @Data
public class DeviceInfo extends Device { public class DeviceInfo extends Device {
@ApiModelProperty(position = 13, value = "Title of the Customer that owns the device.", readOnly = true) @ApiModelProperty(position = 13, value = "Title of the Customer that owns the device.", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
private String customerTitle; private String customerTitle;
@ApiModelProperty(position = 14, value = "Indicates special 'Public' Customer that is auto-generated to use the devices on public dashboards.", readOnly = true) @ApiModelProperty(position = 14, value = "Indicates special 'Public' Customer that is auto-generated to use the devices on public dashboards.", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
private boolean customerIsPublic; private boolean customerIsPublic;
@ApiModelProperty(position = 15, value = "Name of the corresponding Device Profile.", readOnly = true) @ApiModelProperty(position = 15, value = "Name of the corresponding Device Profile.", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
private String deviceProfileName; private String deviceProfileName;
public DeviceInfo() { public DeviceInfo() {

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

@ -49,7 +49,7 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
private static final long serialVersionUID = 6998485460273302018L; private static final long serialVersionUID = 6998485460273302018L;
@ApiModelProperty(position = 3, value = "JSON object with Tenant Id that owns the profile.", readOnly = true) @ApiModelProperty(position = 3, value = "JSON object with Tenant Id that owns the profile.", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
private TenantId tenantId; private TenantId tenantId;
@NoXss @NoXss
@Length(fieldName = "name") @Length(fieldName = "name")
@ -74,12 +74,11 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
private RuleChainId defaultRuleChainId; private RuleChainId defaultRuleChainId;
@ApiModelProperty(position = 6, value = "Reference to the dashboard. Used in the mobile application to open the default dashboard when user navigates to device details.") @ApiModelProperty(position = 6, value = "Reference to the dashboard. Used in the mobile application to open the default dashboard when user navigates to device details.")
private DashboardId defaultDashboardId; private DashboardId defaultDashboardId;
@NoXss @NoXss
@ApiModelProperty(position = 8, value = "Reference to the rule engine queue. " + @ApiModelProperty(position = 8, value = "Rule engine queue name. " +
"If present, the specified queue will be used to store all unprocessed messages related to device, including telemetry, attribute updates, etc. " + "If present, the specified queue will be used to store all unprocessed messages related to device, including telemetry, attribute updates, etc. " +
"Otherwise, the 'Main' queue will be used to store those messages.") "Otherwise, the 'Main' queue will be used to store those messages.")
private QueueId defaultQueueId;
private String defaultQueueName; private String defaultQueueName;
@Valid @Valid
private transient DeviceProfileData profileData; private transient DeviceProfileData profileData;
@ -113,7 +112,7 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
this.isDefault = deviceProfile.isDefault(); this.isDefault = deviceProfile.isDefault();
this.defaultRuleChainId = deviceProfile.getDefaultRuleChainId(); this.defaultRuleChainId = deviceProfile.getDefaultRuleChainId();
this.defaultDashboardId = deviceProfile.getDefaultDashboardId(); this.defaultDashboardId = deviceProfile.getDefaultDashboardId();
this.defaultQueueId = deviceProfile.getDefaultQueueId(); this.defaultQueueName = deviceProfile.getDefaultQueueName();
this.setProfileData(deviceProfile.getProfileData()); this.setProfileData(deviceProfile.getProfileData());
this.provisionDeviceKey = deviceProfile.getProvisionDeviceKey(); this.provisionDeviceKey = deviceProfile.getProvisionDeviceKey();
this.firmwareId = deviceProfile.getFirmwareId(); this.firmwareId = deviceProfile.getFirmwareId();
@ -130,7 +129,7 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
return super.getId(); return super.getId();
} }
@ApiModelProperty(position = 2, value = "Timestamp of the profile creation, in milliseconds", example = "1609459200000", readOnly = true) @ApiModelProperty(position = 2, value = "Timestamp of the profile creation, in milliseconds", example = "1609459200000", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
@Override @Override
public long getCreatedTime() { public long getCreatedTime() {
return super.getCreatedTime(); return super.getCreatedTime();
@ -174,13 +173,4 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
} }
} }
@JsonIgnore
public String getDefaultQueueName() {
return defaultQueueName;
}
@JsonProperty
public void setDefaultQueueName(String defaultQueueName) {
this.defaultQueueName = defaultQueueName;
}
} }

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

@ -87,7 +87,7 @@ public class EntityView extends SearchTextBasedWithAdditionalInfo<EntityViewId>
return getName() /*What the ...*/; return getName() /*What the ...*/;
} }
@ApiModelProperty(position = 4, value = "JSON object with Customer Id. Use 'assignEntityViewToCustomer' to change the Customer Id.", readOnly = true) @ApiModelProperty(position = 4, value = "JSON object with Customer Id. Use 'assignEntityViewToCustomer' to change the Customer Id.", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
@Override @Override
public CustomerId getCustomerId() { public CustomerId getCustomerId() {
return customerId; return customerId;
@ -98,7 +98,7 @@ public class EntityView extends SearchTextBasedWithAdditionalInfo<EntityViewId>
return name; return name;
} }
@ApiModelProperty(position = 3, value = "JSON object with Tenant Id.", readOnly = true) @ApiModelProperty(position = 3, value = "JSON object with Tenant Id.", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
@Override @Override
public TenantId getTenantId() { public TenantId getTenantId() {
return tenantId; return tenantId;
@ -113,7 +113,7 @@ public class EntityView extends SearchTextBasedWithAdditionalInfo<EntityViewId>
return super.getId(); return super.getId();
} }
@ApiModelProperty(position = 2, value = "Timestamp of the Entity View creation, in milliseconds", example = "1609459200000", readOnly = true) @ApiModelProperty(position = 2, value = "Timestamp of the Entity View creation, in milliseconds", example = "1609459200000", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
@Override @Override
public long getCreatedTime() { public long getCreatedTime() {
return super.getCreatedTime(); return super.getCreatedTime();

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

@ -22,9 +22,9 @@ import org.thingsboard.server.common.data.id.EntityViewId;
@Data @Data
public class EntityViewInfo extends EntityView { public class EntityViewInfo extends EntityView {
@ApiModelProperty(position = 12, value = "Title of the Customer that owns the entity view.", readOnly = true) @ApiModelProperty(position = 12, value = "Title of the Customer that owns the entity view.", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
private String customerTitle; private String customerTitle;
@ApiModelProperty(position = 13, value = "Indicates special 'Public' Customer that is auto-generated to use the entity view on public dashboards.", readOnly = true) @ApiModelProperty(position = 13, value = "Indicates special 'Public' Customer that is auto-generated to use the entity view on public dashboards.", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
private boolean customerIsPublic; private boolean customerIsPublic;
public EntityViewInfo() { public EntityViewInfo() {

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

@ -30,13 +30,13 @@ import org.thingsboard.server.common.data.id.TenantId;
@ApiModel @ApiModel
public class Event extends BaseData<EventId> { public class Event extends BaseData<EventId> {
@ApiModelProperty(position = 1, value = "JSON object with Tenant Id.", readOnly = true) @ApiModelProperty(position = 1, value = "JSON object with Tenant Id.", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
private TenantId tenantId; private TenantId tenantId;
@ApiModelProperty(position = 2, value = "Event type", example = "STATS") @ApiModelProperty(position = 2, value = "Event type", example = "STATS")
private String type; private String type;
@ApiModelProperty(position = 3, value = "string", example = "784f394c-42b6-435a-983c-b7beff2784f9") @ApiModelProperty(position = 3, value = "string", example = "784f394c-42b6-435a-983c-b7beff2784f9")
private String uid; private String uid;
@ApiModelProperty(position = 4, value = "JSON object with Entity Id for which event is created.", readOnly = true) @ApiModelProperty(position = 4, value = "JSON object with Entity Id for which event is created.", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
private EntityId entityId; private EntityId entityId;
@ApiModelProperty(position = 5, value = "Event body.", dataType = "com.fasterxml.jackson.databind.JsonNode") @ApiModelProperty(position = 5, value = "Event body.", dataType = "com.fasterxml.jackson.databind.JsonNode")
private transient JsonNode body; private transient JsonNode body;
@ -53,7 +53,7 @@ public class Event extends BaseData<EventId> {
super(event); super(event);
} }
@ApiModelProperty(position = 6, value = "Timestamp of the event creation, in milliseconds", example = "1609459200000", readOnly = true) @ApiModelProperty(position = 6, value = "Timestamp of the event creation, in milliseconds", example = "1609459200000", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
@Override @Override
public long getCreatedTime() { public long getCreatedTime() {
return super.getCreatedTime(); return super.getCreatedTime();

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

@ -15,6 +15,7 @@
*/ */
package org.thingsboard.server.common.data; package org.thingsboard.server.common.data;
import io.swagger.annotations.ApiModelProperty;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.HasId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
@ -23,10 +24,13 @@ public interface ExportableEntity<I extends EntityId> extends HasId<I>, HasName
void setId(I id); void setId(I id);
@ApiModelProperty(position = 100, value = "JSON object with External Id from the VCS", accessMode = ApiModelProperty.AccessMode.READ_ONLY, hidden = true)
I getExternalId(); I getExternalId();
void setExternalId(I externalId); void setExternalId(I externalId);
long getCreatedTime(); long getCreatedTime();
void setCreatedTime(long createdTime); void setCreatedTime(long createdTime);
void setTenantId(TenantId tenantId); void setTenantId(TenantId tenantId);

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

Loading…
Cancel
Save