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>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.4.0-SNAPSHOT</version>
<version>3.4.1-SNAPSHOT</version>
<artifactId>thingsboard</artifactId>
</parent>
<artifactId>application</artifactId>

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

@ -37,7 +37,7 @@
<logger name="org.thingsboard.server" level="INFO" />
<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.clients.consumer.ConsumerConfig" level="WARN"/>
<logger name="org.apache.kafka.clients" level="WARN"/>
<root level="INFO">
<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
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_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);
ALTER TABLE admin_settings
@ -70,3 +61,80 @@ CREATE TABLE IF NOT EXISTS user_auth_settings (
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);
}
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) {
return partitionService.resolve(serviceType, tenantId, entityId, queueName);
return partitionService.resolve(serviceType, queueName, tenantId, entityId);
}
public String getServiceId() {

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

@ -162,18 +162,11 @@ class DefaultTbContext implements TbContext {
}
@Override
@Deprecated
public void enqueue(TbMsg tbMsg, String queueName, Runnable onSuccess, Consumer<Throwable> onFailure) {
TopicPartitionInfo tpi = resolvePartition(tbMsg, queueName);
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) {
if (!tbMsg.isValid()) {
log.trace("[{}] Skip invalid message: {}", getTenantId(), tbMsg);
@ -223,35 +216,30 @@ class DefaultTbContext implements TbContext {
}
@Override
public void enqueueForTellNext(TbMsg tbMsg, QueueId queueId, String relationType, Runnable onSuccess, Consumer<Throwable> onFailure) {
TopicPartitionInfo tpi = resolvePartition(tbMsg, queueId);
enqueueForTellNext(tpi, queueId, tbMsg, Collections.singleton(relationType), null, onSuccess, onFailure);
public void enqueueForTellNext(TbMsg tbMsg, String queueName, String relationType, Runnable onSuccess, Consumer<Throwable> onFailure) {
TopicPartitionInfo tpi = resolvePartition(tbMsg, queueName);
enqueueForTellNext(tpi, queueName, tbMsg, Collections.singleton(relationType), null, onSuccess, onFailure);
}
@Override
public void enqueueForTellNext(TbMsg tbMsg, QueueId queueId, Set<String> relationTypes, Runnable onSuccess, Consumer<Throwable> onFailure) {
TopicPartitionInfo tpi = resolvePartition(tbMsg, queueId);
enqueueForTellNext(tpi, queueId, tbMsg, relationTypes, null, onSuccess, onFailure);
}
private TopicPartitionInfo resolvePartition(TbMsg tbMsg, QueueId queueId) {
return mainCtx.resolve(ServiceType.TB_RULE_ENGINE, queueId, getTenantId(), tbMsg.getOriginator());
public void enqueueForTellNext(TbMsg tbMsg, String queueName, Set<String> relationTypes, Runnable onSuccess, Consumer<Throwable> onFailure) {
TopicPartitionInfo tpi = resolvePartition(tbMsg, queueName);
enqueueForTellNext(tpi, queueName, tbMsg, relationTypes, null, onSuccess, onFailure);
}
@Deprecated
private TopicPartitionInfo resolvePartition(TbMsg tbMsg, String queueName) {
return mainCtx.resolve(ServiceType.TB_RULE_ENGINE, queueName, getTenantId(), tbMsg.getOriginator());
}
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) {
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()) {
log.trace("[{}] Skip invalid message: {}", getTenantId(), source);
if (onFailure != null) {
@ -261,7 +249,7 @@ class DefaultTbContext implements TbContext {
}
RuleChainId ruleChainId = nodeCtx.getSelf().getRuleChainId();
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()
.setTenantIdMSB(getTenantId().getId().getMostSignificantBits())
.setTenantIdLSB(getTenantId().getId().getLeastSignificantBits())
@ -320,13 +308,13 @@ class DefaultTbContext implements TbContext {
}
@Override
public TbMsg newMsg(QueueId queueId, String type, EntityId originator, TbMsgMetaData metaData, String data) {
return newMsg(queueId, type, originator, null, metaData, data);
public TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data) {
return newMsg(queueName, type, originator, null, metaData, data);
}
@Override
public TbMsg newMsg(QueueId queueId, 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());
public TbMsg newMsg(String queueName, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) {
return TbMsg.newMsg(queueName, type, originator, customerId, metaData, data, nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId());
}
@Override
@ -340,17 +328,17 @@ class DefaultTbContext implements TbContext {
public TbMsg deviceCreatedMsg(Device device, RuleNodeId ruleNodeId) {
RuleChainId ruleChainId = null;
QueueId queueId = null;
String queueName = null;
if (device.getDeviceProfileId() != null) {
DeviceProfile deviceProfile = mainCtx.getDeviceProfileCache().find(device.getDeviceProfileId());
if (deviceProfile == null) {
log.warn("[{}] Device profile is null!", device.getDeviceProfileId());
} else {
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) {
@ -359,7 +347,7 @@ class DefaultTbContext implements TbContext {
public TbMsg alarmActionMsg(Alarm alarm, RuleNodeId ruleNodeId, String action) {
RuleChainId ruleChainId = null;
QueueId queueId = null;
String queueName = null;
if (EntityType.DEVICE.equals(alarm.getOriginator().getEntityType())) {
DeviceId deviceId = new DeviceId(alarm.getOriginator().getId());
DeviceProfile deviceProfile = mainCtx.getDeviceProfileCache().get(getTenantId(), deviceId);
@ -367,10 +355,10 @@ class DefaultTbContext implements TbContext {
log.warn("[{}] Device profile is null!", deviceId);
} else {
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
@ -382,9 +370,9 @@ class DefaultTbContext implements TbContext {
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 {
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) {
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 {
checkComponentStateActive(msg);
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);
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'. " +
"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 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)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@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 +
"The newly created Asset id will be present in the response. " +
"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')")
@RequestMapping(value = "/asset", method = RequestMethod.POST)
@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 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_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_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";

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 +
"The newly created Customer Id will be present in the response. " +
"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')")
@RequestMapping(value = "/customer", method = RequestMethod.POST)
@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. " +
"Specify existing Dashboard id to update the dashboard. " +
"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,
produces = MediaType.APPLICATION_JSON_VALUE,
consumes = MediaType.APPLICATION_JSON_VALUE)
@ -261,7 +262,7 @@ public class DashboardController extends BaseController {
checkParameter(DASHBOARD_ID, strDashboardId);
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
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());
}
@ -281,7 +282,7 @@ public class DashboardController extends BaseController {
checkParameter(DASHBOARD_ID, strDashboardId);
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
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());
}
@ -301,7 +302,7 @@ public class DashboardController extends BaseController {
checkParameter(DASHBOARD_ID, strDashboardId);
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
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());
}
@ -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<>();
if (strCustomerIds != null) {
for (String strCustomerId : strCustomerIds) {
CustomerId customerId = new CustomerId(UUID.fromString(strCustomerId));
if (dashboard.isAssignedToCustomer(customerId)) {
customerIds.add(customerId);
}
customerIds.add(new CustomerId(UUID.fromString(strCustomerId)));
}
}
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. " +
"Specify existing Device id to update the device. " +
"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."
+ TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
"\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." +
"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')")
@RequestMapping(value = "/device", method = RequestMethod.POST)
@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. " +
"You may find the example of LwM2M device and RPK credentials below: \n\n" +
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)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@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. " +
"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 +
"Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Device Profile entity. " +
TENANT_AUTHORITY_PARAGRAPH,
produces = "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. " +
"Specify existing Edge id to update the edge. " +
"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."
+ TENANT_AUTHORITY_PARAGRAPH,
"\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." +
"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)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@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")
public VersionCreationResult getVersionCreateRequestStatus(@ApiParam(value = VC_REQUEST_ID_PARAM_DESCRIPTION, required = true)
@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);
}
@ -469,7 +469,7 @@ public class EntitiesVersionControlController extends BaseController {
@GetMapping(value = "/entity/{requestId}/status")
public VersionLoadResult getVersionLoadRequestStatus(@ApiParam(value = VC_REQUEST_ID_PARAM_DESCRIPTION, required = true)
@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);
}

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)",
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)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@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;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
@ -37,6 +39,22 @@ import org.thingsboard.server.service.security.permission.Resource;
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
@TbCoreComponent
@RequestMapping("/api")
@ -45,14 +63,23 @@ public class QueueController extends BaseController {
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')")
@RequestMapping(value = "/queues", params = {"serviceType", "pageSize", "page"}, method = RequestMethod.GET)
@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,
@ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
@ApiParam(value = QUEUE_QUEUE_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
@ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = QUEUE_SORT_PROPERTY_ALLOWABLE_VALUES)
@RequestParam(required = false) String sortProperty,
@ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
checkParameter("serviceType", serviceType);
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')")
@RequestMapping(value = "/queues/{queueId}", method = RequestMethod.GET)
@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);
QueueId queueId = new QueueId(UUID.fromString(queueIdStr));
checkQueueId(queueId, Operation.READ);
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')")
@RequestMapping(value = "/queues", params = {"serviceType"}, method = RequestMethod.POST)
@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 {
checkParameter("serviceType", serviceType);
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')")
@RequestMapping(value = "/queues/{queueId}", method = RequestMethod.DELETE)
@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);
QueueId queueId = new QueueId(toUUID(queueIdStr));
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. " +
"Specify existing Rule Chain id to update the rule chain. " +
"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')")
@RequestMapping(value = "/ruleChain", method = RequestMethod.POST)
@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. " +
"Specify existing Resource id to update the Resource. " +
"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",
consumes = "application/json")
@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. " +
"Specify existing Tenant Id id to update the Tenant. " +
"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)
@PreAuthorize("hasAuthority('SYS_ADMIN')")
@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" +
" \"name\": \"Default\",\n" +
" \"description\": \"Default tenant profile\",\n" +
" \"isolatedTbCore\": false,\n" +
" \"isolatedTbRuleEngine\": false,\n" +
" \"profileData\": {\n" +
" \"configuration\": {\n" +
@ -165,6 +164,7 @@ public class TenantProfileController extends BaseController {
" \"default\": true\n" +
"}" +
MARKDOWN_CODE_BLOCK_END +
"Remove 'id', from the request body example (below) to create new Tenant Profile entity." +
SYSTEM_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('SYS_ADMIN')")
@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")
@TbCoreComponent
@RequiredArgsConstructor
public class TwoFaConfigController extends BaseController {
public class TwoFactorAuthConfigController extends BaseController {
private final TwoFaConfigManager twoFaConfigManager;
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. " +
"Specify existing User Id to update the device. " +
"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')")
@RequestMapping(value = "/user", method = RequestMethod.POST)
@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. " +
"Referencing non-existing Widget Type Id will cause 'Not Found' error." +
"\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."
+ SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
"Special Tenant Id '13814000-1dd2-11b2-8080-808080808080' is automatically used if the create request is sent by user with 'SYS_ADMIN' authority." +
"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')")
@RequestMapping(value = "/widgetType", method = RequestMethod.POST)
@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. " +
"Referencing non-existing Widget Bundle Id will cause 'Not Found' error." +
"\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."
+ SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
"Special Tenant Id '13814000-1dd2-11b2-8080-808080808080' is automatically used if the create bundle request is sent by user with 'SYS_ADMIN' authority." +
"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')")
@RequestMapping(value = "/widgetsBundle", method = RequestMethod.POST)
@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);
}
@ApiModelProperty(position = 5, value = "Password reset token", readOnly = true)
@ApiModelProperty(position = 5, value = "Password reset token", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
public String getResetToken() {
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);
}
@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() {
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() {
return message;
}
@ -69,12 +69,12 @@ public class ThingsboardErrorResponse {
"\n\n* `34` - Too many updates (Too many updates over Websocket session)" +
"\n\n* `40` - Subscription violation (HTTP: 403 - Forbidden)",
example = "10", dataType = "integer",
readOnly = true)
accessMode = ApiModelProperty.AccessMode.READ_ONLY)
public ThingsboardErrorCode getErrorCode() {
return errorCode;
}
@ApiModelProperty(position = 4, value = "Timestamp", readOnly = true)
@ApiModelProperty(position = 4, value = "Timestamp", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
public Date getTimestamp() {
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())
// .setDefaultRuleChainIdLSB(deviceProfile.getDefaultRuleChainId().getId().getLeastSignificantBits());
// }
if (deviceProfile.getDefaultQueueId() != null) {
builder.setDefaultQueueIdMSB(deviceProfile.getDefaultQueueId().getId().getMostSignificantBits())
.setDefaultQueueIdLSB(deviceProfile.getDefaultQueueId().getId().getLeastSignificantBits());
if (deviceProfile.getDefaultQueueName() != null) {
builder.setDefaultQueueName(deviceProfile.getDefaultQueueName());
}
if (deviceProfile.getDescription() != null) {
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;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
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.rule.RuleChain;
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.RuleChainMetadataUpdateMsg;
import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg;
@ -35,11 +33,8 @@ import org.thingsboard.server.service.edge.rpc.constructor.rule.RuleChainMetadat
@Component
@Slf4j
@TbCoreComponent
@AllArgsConstructor
public class RuleChainMsgConstructor {
private final QueueService queueService;
public RuleChainUpdateMsg constructRuleChainUpdatedMsg(RuleChainId edgeRootRuleChainId, UpdateMsgType msgType, RuleChain ruleChain) {
RuleChainUpdateMsg.Builder builder = RuleChainUpdateMsg.newBuilder()
.setMsgType(msgType)
@ -61,7 +56,7 @@ public class RuleChainMsgConstructor {
RuleChainMetaData ruleChainMetaData,
EdgeVersion edgeVersion) {
RuleChainMetadataConstructor ruleChainMetadataConstructor
= RuleChainMetadataConstructorFactory.getByEdgeVersion(edgeVersion, queueService);
= RuleChainMetadataConstructorFactory.getByEdgeVersion(edgeVersion);
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;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
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.queue.Queue;
import org.thingsboard.server.common.data.rule.NodeConnectionInfo;
import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo;
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.NodeConnectionInfoProto;
import org.thingsboard.server.gen.edge.v1.RuleChainConnectionInfoProto;
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.List;
import java.util.NavigableSet;
import java.util.UUID;
@Slf4j
@AllArgsConstructor
public abstract class AbstractRuleChainMetadataConstructor implements RuleChainMetadataConstructor {
public static final List<String> nodeTypes = List.of(TbCheckpointNode.class.getName());
private final QueueService queueService;
@Override
public RuleChainMetadataUpdateMsg constructRuleChainMetadataUpdatedMsg(TenantId tenantId,
UpdateMsgType msgType,
@ -144,24 +134,4 @@ public abstract class AbstractRuleChainMetadataConstructor implements RuleChainM
.setAdditionalInfo(JacksonUtil.OBJECT_MAPPER.writeValueAsString(ruleChainConnectionInfo.getAdditionalInfo()))
.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;
import org.thingsboard.server.dao.queue.QueueService;
import org.thingsboard.server.gen.edge.v1.EdgeVersion;
public final class RuleChainMetadataConstructorFactory {
public static RuleChainMetadataConstructor getByEdgeVersion(EdgeVersion edgeVersion,
QueueService queueService) {
public static RuleChainMetadataConstructor getByEdgeVersion(EdgeVersion edgeVersion) {
switch (edgeVersion) {
case V_3_3_0:
return new RuleChainMetadataConstructorV330(queueService);
return new RuleChainMetadataConstructorV330();
case V_3_3_3:
return new RuleChainMetadataConstructorV333(queueService);
case V_3_4_0:
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.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.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 TB_RULE_CHAIN_OUTPUT_NODE = TbRuleChainOutputNode.class.getName();
public RuleChainMetadataConstructorV330(QueueService queueService) {
super(queueService);
}
@Override
protected void constructRuleChainMetadataUpdatedMsg(TenantId tenantId,
RuleChainMetadataUpdateMsg.Builder builder,
RuleChainMetaData ruleChainMetaData) throws JsonProcessingException {
List<RuleNode> supportedNodes = filterNodes(ruleChainMetaData.getNodes());
supportedNodes = updateQueueIdToQueueNameNodeConfiguration(tenantId, supportedNodes);
NavigableSet<Integer> removedNodeIndexes = getRemovedNodeIndexes(ruleChainMetaData.getNodes(), ruleChainMetaData.getConnections());
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 org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.dao.queue.QueueService;
import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg;
import java.util.TreeSet;
@ -27,10 +26,6 @@ import java.util.TreeSet;
@Slf4j
public class RuleChainMetadataConstructorV340 extends AbstractRuleChainMetadataConstructor {
public RuleChainMetadataConstructorV340(QueueService queueService) {
super(queueService);
}
@Override
protected void constructRuleChainMetadataUpdatedMsg(TenantId tenantId,
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;
}
private Pair<QueueId, RuleChainId> getDefaultQueueNameAndRuleChainId(TenantId tenantId, EntityId entityId) {
private Pair<String, RuleChainId> getDefaultQueueNameAndRuleChainId(TenantId tenantId, EntityId entityId) {
if (EntityType.DEVICE.equals(entityId.getEntityType())) {
DeviceProfile deviceProfile = deviceProfileCache.get(tenantId, new DeviceId(entityId.getId()));
RuleChainId ruleChainId;
QueueId queueId;
String queueName;
if (deviceProfile == null) {
log.warn("[{}] Device profile is null!", entityId);
ruleChainId = null;
queueId = null;
queueName = null;
} else {
ruleChainId = deviceProfile.getDefaultRuleChainId();
queueId = deviceProfile.getDefaultQueueId();
queueName = deviceProfile.getDefaultQueueName();
}
return new ImmutablePair<>(queueId, ruleChainId);
return new ImmutablePair<>(queueName, ruleChainId);
} else {
return new ImmutablePair<>(null, null);
}
@ -183,10 +183,8 @@ public class TelemetryEdgeProcessor extends BaseEdgeProcessor {
for (TransportProtos.TsKvListProto tsKv : msg.getTsKvListList()) {
JsonObject json = JsonUtils.getJsonObject(tsKv.getKvList());
metaData.putValue("ts", tsKv.getTs() + "");
Pair<QueueId, RuleChainId> defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId);
QueueId queueId = defaultQueueAndRuleChain.getKey();
RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue();
TbMsg tbMsg = TbMsg.newMsg(queueId, SessionMsgType.POST_TELEMETRY_REQUEST.name(), entityId, customerId, metaData, gson.toJson(json), ruleChainId, null);
var defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId);
TbMsg tbMsg = TbMsg.newMsg(defaultQueueAndRuleChain.getKey(), SessionMsgType.POST_TELEMETRY_REQUEST.name(), entityId, customerId, metaData, gson.toJson(json), defaultQueueAndRuleChain.getValue(), null);
tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() {
@Override
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) {
SettableFuture<Void> futureToSet = SettableFuture.create();
JsonObject json = JsonUtils.getJsonObject(msg.getKvList());
Pair<QueueId, RuleChainId> defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId);
QueueId queueId = defaultQueueAndRuleChain.getKey();
RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue();
TbMsg tbMsg = TbMsg.newMsg(queueId, SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), entityId, customerId, metaData, gson.toJson(json), ruleChainId, null);
var defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId);
TbMsg tbMsg = TbMsg.newMsg(defaultQueueAndRuleChain.getKey(), SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), entityId, customerId, metaData, gson.toJson(json), defaultQueueAndRuleChain.getValue(), null);
tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() {
@Override
public void onSuccess(TbQueueMsgMetadata metadata) {
@ -233,10 +229,8 @@ public class TelemetryEdgeProcessor extends BaseEdgeProcessor {
Futures.addCallback(future, new FutureCallback<>() {
@Override
public void onSuccess(@Nullable List<String> keys) {
Pair<QueueId, RuleChainId> defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId);
QueueId queueId = defaultQueueAndRuleChain.getKey();
RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue();
TbMsg tbMsg = TbMsg.newMsg(queueId, DataConstants.ATTRIBUTES_UPDATED, entityId, customerId, metaData, gson.toJson(json), ruleChainId, null);
var defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId);
TbMsg tbMsg = TbMsg.newMsg(defaultQueueAndRuleChain.getKey(), DataConstants.ATTRIBUTES_UPDATED, entityId, customerId, metaData, gson.toJson(json), defaultQueueAndRuleChain.getValue(), null);
tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() {
@Override
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.UserCredentialsRequestMsg;
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.executors.DbCallbackExecutorService;
import org.thingsboard.server.service.state.DefaultDeviceStateService;
@ -83,6 +84,7 @@ import java.util.Map;
import java.util.UUID;
@Service
@TbCoreComponent
@Slf4j
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();
DashboardId dashboardId = dashboard.getId();
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;
} else {
Dashboard savedDashboard = null;
for (CustomerId customerId : customerIds) {
for (CustomerId customerId : addedCustomerIds) {
savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(tenantId, dashboardId, customerId));
ShortCustomerInfo customerInfo = savedDashboard.getAssignedCustomerInfo(customerId);
notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, dashboardId, customerId, savedDashboard,
@ -207,11 +213,17 @@ public class DefaultTbDashboardService extends AbstractTbEntityService implement
TenantId tenantId = dashboard.getTenantId();
DashboardId dashboardId = dashboard.getId();
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;
} else {
Dashboard savedDashboard = null;
for (CustomerId customerId : customerIds) {
for (CustomerId customerId : removedCustomerIds) {
ShortCustomerInfo customerInfo = dashboard.getAssignedCustomerInfo(customerId);
savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(tenantId, dashboardId, customerId));
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
@AllArgsConstructor
public class DefaultTbQueueService extends AbstractTbEntityService implements TbQueueService {
private static final String MAIN = "Main";
private static final long DELETE_DELAY = 30;
private final QueueService queueService;
private final TbClusterService tbClusterService;
private final TbQueueAdmin tbQueueAdmin;
private final DeviceProfileService deviceProfileService;
private final SchedulerComponent scheduler;
@Override
@ -205,35 +203,7 @@ public class DefaultTbQueueService extends AbstractTbEntityService implements Tb
}
tenantIds.forEach(tenantId -> {
Map<QueueId, List<DeviceProfile>> deviceProfileQueues;
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);
}
});
}
toCreate.forEach(key -> saveQueue(new Queue(tenantId, newQueues.get(key))));
toUpdate.forEach(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.setCreatedTime(foundQueue.getCreatedTime());
if (queueToUpdate.equals(foundQueue)) {
//Queue not changed
} else {
if (!queueToUpdate.equals(foundQueue)) {
saveQueue(queueToUpdate);
}
});
@ -251,26 +219,9 @@ public class DefaultTbQueueService extends AbstractTbEntityService implements Tb
toRemove.forEach(q -> {
Queue queue = queueService.findQueueByTenantIdAndNameInternal(tenantId, q);
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);
});
});
}
//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);
return savedUser;
} 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;
}
}

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 {
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();
isolatedRuleEngineTenantProfileData.setConfiguration(new DefaultTenantProfileConfiguration());
@ -239,7 +223,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
isolatedTbRuleEngineProfile.setDefault(false);
isolatedTbRuleEngineProfile.setName("Isolated TB Rule Engine");
isolatedTbRuleEngineProfile.setDescription("Isolated TB Rule Engine tenant profile");
isolatedTbRuleEngineProfile.setIsolatedTbCore(false);
isolatedTbRuleEngineProfile.setIsolatedTbRuleEngine(true);
isolatedTbRuleEngineProfile.setProfileData(isolatedRuleEngineTenantProfileData);
@ -248,20 +231,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
} catch (DataValidationException e) {
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

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

@ -589,10 +589,6 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
} 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...");
conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3004000;");
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;
case "3.3.4":
log.info("Updating data from version 3.3.4 to 3.4.0 ...");
rateLimitsUpdater.updateEntities();
tenantsProfileQueueConfigurationUpdater.updateEntities();
checkPointRuleNodesUpdater.updateEntities();
rateLimitsUpdater.updateEntities();
break;
default:
throw new RuntimeException("Unable to update data, unsupported fromVersion: " + fromVersion);
@ -629,47 +628,4 @@ public class DefaultDataUpdateService implements DataUpdateService {
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 lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.NestedRuntimeException;
@ -47,13 +49,17 @@ import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
import javax.annotation.PostConstruct;
import javax.mail.MessagingException;
import javax.annotation.PreDestroy;
import javax.mail.internet.MimeMessage;
import java.io.ByteArrayInputStream;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@Service
@Slf4j
@ -70,6 +76,8 @@ public class DefaultMailService implements MailService {
private final AdminSettingsService adminSettingsService;
private final TbApiUsageClient apiUsageClient;
private static final long DEFAULT_TIMEOUT = 10_000;
@Lazy
@Autowired
private TbApiUsageStateService apiUsageStateService;
@ -77,10 +85,15 @@ public class DefaultMailService implements MailService {
@Autowired
private MailExecutorService mailExecutorService;
@Autowired
private PasswordResetExecutorService passwordResetExecutorService;
private JavaMailSenderImpl mailSender;
private String mailFrom;
private long timeout;
public DefaultMailService(MessageSource messages, Configuration freemarkerConfig, AdminSettingsService adminSettingsService, TbApiUsageClient apiUsageClient) {
this.messages = messages;
this.freemarkerConfig = freemarkerConfig;
@ -100,6 +113,7 @@ public class DefaultMailService implements MailService {
JsonNode jsonConfig = settings.getJsonValue();
mailSender = createMailSender(jsonConfig);
mailFrom = jsonConfig.get("mailFrom").asText();
timeout = jsonConfig.get("timeout").asLong(DEFAULT_TIMEOUT);
} else {
throw new IncorrectParameterException("Failed to update mail configuration. Settings not found!");
}
@ -166,7 +180,7 @@ public class DefaultMailService implements MailService {
@Override
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
@ -174,13 +188,14 @@ public class DefaultMailService implements MailService {
JavaMailSenderImpl testMailSender = createMailSender(jsonConfig);
String mailFrom = jsonConfig.get("mailFrom").asText();
String subject = messages.getMessage("test.message.subject", null, Locale.US);
long timeout = jsonConfig.get("timeout").asLong(DEFAULT_TIMEOUT);
Map<String, Object> model = new HashMap<>();
model.put(TARGET_EMAIL, email);
String message = mergeTemplateIntoString("test.ftl", model);
sendMail(testMailSender, mailFrom, email, subject, message);
sendMail(testMailSender, mailFrom, email, subject, message, timeout);
}
@Override
@ -194,7 +209,7 @@ public class DefaultMailService implements MailService {
String message = mergeTemplateIntoString("activation.ftl", model);
sendMail(mailSender, mailFrom, email, subject, message);
sendMail(mailSender, mailFrom, email, subject, message, timeout);
}
@Override
@ -208,7 +223,7 @@ public class DefaultMailService implements MailService {
String message = mergeTemplateIntoString("account.activated.ftl", model);
sendMail(mailSender, mailFrom, email, subject, message);
sendMail(mailSender, mailFrom, email, subject, message, timeout);
}
@Override
@ -222,12 +237,12 @@ public class DefaultMailService implements MailService {
String message = mergeTemplateIntoString("reset.password.ftl", model);
sendMail(mailSender, mailFrom, email, subject, message);
sendMail(mailSender, mailFrom, email, subject, message, timeout);
}
@Override
public void sendResetPasswordEmailAsync(String passwordResetLink, String email) {
mailExecutorService.execute(() -> {
passwordResetExecutorService.execute(() -> {
try {
this.sendResetPasswordEmail(passwordResetLink, email);
} catch (ThingsboardException e) {
@ -247,20 +262,20 @@ public class DefaultMailService implements MailService {
String message = mergeTemplateIntoString("password.was.reset.ftl", model);
sendMail(mailSender, mailFrom, email, subject, message);
sendMail(mailSender, mailFrom, email, subject, message, timeout);
}
@Override
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
public void send(TenantId tenantId, CustomerId customerId, TbEmail tbEmail, JavaMailSender javaMailSender) throws ThingsboardException {
sendMail(tenantId, customerId, tbEmail, javaMailSender);
public void send(TenantId tenantId, CustomerId customerId, TbEmail tbEmail, JavaMailSender javaMailSender, long timeout) throws ThingsboardException {
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()) {
try {
MimeMessage mailMsg = javaMailSender.createMimeMessage();
@ -287,7 +302,7 @@ public class DefaultMailService implements MailService {
helper.addInline(imgId, iss, contentType);
}
}
javaMailSender.send(helper.getMimeMessage());
sendMailWithTimeout(javaMailSender, helper.getMimeMessage(), timeout);
apiUsageClient.report(tenantId, customerId, ApiUsageRecordKey.EMAIL_EXEC_COUNT, 1);
} catch (Exception e) {
throw handleException(e);
@ -308,7 +323,7 @@ public class DefaultMailService implements MailService {
String message = mergeTemplateIntoString("account.lockout.ftl", model);
sendMail(mailSender, mailFrom, email, subject, message);
sendMail(mailSender, mailFrom, email, subject, message, timeout);
}
@Override
@ -320,7 +335,7 @@ public class DefaultMailService implements MailService {
"expirationTimeSeconds", expirationTimeSeconds
));
sendMail(mailSender, mailFrom, email, subject, message);
sendMail(mailSender, mailFrom, email, subject, message, timeout);
}
@Override
@ -347,7 +362,7 @@ public class DefaultMailService implements MailService {
message = mergeTemplateIntoString("state.disabled.ftl", model);
break;
}
sendMail(mailSender, mailFrom, email, subject, message);
sendMail(mailSender, mailFrom, email, subject, message, timeout);
}
@Override
@ -447,9 +462,8 @@ public class DefaultMailService implements MailService {
}
}
private void sendMail(JavaMailSenderImpl mailSender,
String mailFrom, String email,
String subject, String message) throws ThingsboardException {
private void sendMail(JavaMailSenderImpl mailSender, String mailFrom, String email,
String subject, String message, long timeout) throws ThingsboardException {
try {
MimeMessage mimeMsg = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMsg, UTF_8);
@ -457,12 +471,24 @@ public class DefaultMailService implements MailService {
helper.setTo(email);
helper.setSubject(subject);
helper.setText(message, true);
mailSender.send(helper.getMimeMessage());
sendMailWithTimeout(mailSender, helper.getMimeMessage(), timeout);
} catch (Exception 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,
Map<String, Object> model) throws ThingsboardException {
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
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -13,18 +13,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const config = require('config'),
logger = require('../config/logger')._logger('httpServer'),
express = require('express');
package org.thingsboard.server.service.mail;
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) => {
const date = new Date();
const message = { now: date.toISOString() };
res.send(message);
})
@Value("${actors.rule.mail_password_reset_thread_pool_size:10}")
private int mailExecutorThreadPoolSize;
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())));
}
}
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);
ToRuleEngineMsg msg = ToRuleEngineMsg.newBuilder()
.setTenantIdMSB(tenantId.getId().getMostSignificantBits())
@ -194,16 +194,16 @@ public class DefaultTbClusterService implements TbClusterService {
private TbMsg transformMsg(TbMsg tbMsg, DeviceProfile deviceProfile) {
if (deviceProfile != null) {
RuleChainId targetRuleChainId = deviceProfile.getDefaultRuleChainId();
QueueId targetQueueId = deviceProfile.getDefaultQueueId();
String targetQueueName = deviceProfile.getDefaultQueueName();
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) {
tbMsg = TbMsg.transformMsg(tbMsg, targetRuleChainId, targetQueueId);
tbMsg = TbMsg.transformMsg(tbMsg, targetRuleChainId, targetQueueName);
} else if (isRuleChainTransform) {
tbMsg = TbMsg.transformMsg(tbMsg, targetRuleChainId);
} else if (isQueueTransform) {
tbMsg = TbMsg.transformMsg(tbMsg, targetQueueId);
tbMsg = TbMsg.transformMsg(tbMsg, targetQueueName);
}
}
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);
TbRuleEngineProcessingResult result = new TbRuleEngineProcessingResult(configuration.getId(), timeout, ctx);
TbRuleEngineProcessingResult result = new TbRuleEngineProcessingResult(configuration.getName(), timeout, ctx);
if (timeout) {
printFirstOrAll(configuration, ctx, ctx.getPendingMap(), "Timeout");
}
@ -339,7 +339,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
new TbMsgPackCallback(id, tenantId, ctx);
try {
if (toRuleEngineMsg.getTbMsg() != null && !toRuleEngineMsg.getTbMsg().isEmpty()) {
forwardToRuleEngineActor(configuration.getId(), tenantId, toRuleEngineMsg, callback);
forwardToRuleEngineActor(configuration.getName(), tenantId, toRuleEngineMsg, callback);
} else {
callback.onSuccess();
}
@ -353,7 +353,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
log.info("{} to process [{}] messages", prefix, map.size());
for (Map.Entry<UUID, TbProtoQueueMsg<ToRuleEngineMsg>> pending : map.entrySet()) {
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());
if (printAll) {
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);
}
private void forwardToRuleEngineActor(QueueId queueId, TenantId tenantId, ToRuleEngineMsg toRuleEngineMsg, TbMsgCallback callback) {
TbMsg tbMsg = TbMsg.fromBytes(queueId, toRuleEngineMsg.getTbMsg().toByteArray(), callback);
private void forwardToRuleEngineActor(String queueName, TenantId tenantId, ToRuleEngineMsg toRuleEngineMsg, TbMsgCallback callback) {
TbMsg tbMsg = TbMsg.fromBytes(queueName, toRuleEngineMsg.getTbMsg().toByteArray(), callback);
QueueToRuleEngineMsg msg;
ProtocolStringList relationTypesList = toRuleEngineMsg.getRelationTypesList();
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);
if (tenant != null) {
TenantProfile tenantProfile = tenantProfileCache.get(tenant.getTenantProfileId());
return new TenantRoutingInfo(tenantId, tenantProfile.isIsolatedTbCore(), tenantProfile.isIsolatedTbRuleEngine());
return new TenantRoutingInfo(tenantId, tenantProfile.isIsolatedTbRuleEngine());
} else {
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 {
@Getter
private final QueueId queueId;
private final String queueName;
@Getter
private final boolean success;
@Getter
@ -37,8 +37,8 @@ public class TbRuleEngineProcessingResult {
@Getter
private final TbMsgPackProcessingContext ctx;
public TbRuleEngineProcessingResult(QueueId queueId, boolean timeout, TbMsgPackProcessingContext ctx) {
this.queueId = queueId;
public TbRuleEngineProcessingResult(String queueName, boolean timeout, TbMsgPackProcessingContext ctx) {
this.queueName = queueName;
this.timeout = timeout;
this.ctx = ctx;
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());
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) {
try {
@ -164,10 +164,10 @@ public class TbRuleEngineProcessingStrategyFactory {
log.debug("[{}] Reprocessing skipped for {} failed and {} timeout messages", queueName, result.getFailedMap().size(), result.getPendingMap().size());
}
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()) {
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);
}

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

@ -159,6 +159,8 @@ public abstract class AbstractNashornJsInvokeService extends AbstractJsInvokeSer
} else {
return ((Invocable) engine).invokeFunction(functionName, args);
}
} catch (ScriptException e) {
throw new ExecutionException(e);
} catch (Exception e) {
onScriptExecutionError(scriptId, e, functionName);
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}")
private long maxRequestsTimeout;
@Value("${queue.js.max_exec_requests_timeout:2000}")
private long maxExecRequestsTimeout;
@Getter
@Value("${js.remote.max_errors}")
private int maxErrors;
@ -170,7 +173,7 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService {
.setScriptIdMSB(scriptId.getMostSignificantBits())
.setScriptIdLSB(scriptId.getLeastSignificantBits())
.setFunctionName(functionName)
.setTimeout((int) maxRequestsTimeout)
.setTimeout((int) maxExecRequestsTimeout)
.setScriptBody(scriptBody);
for (Object arg : args) {
@ -197,7 +200,6 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService {
@Override
public void onFailure(Throwable t) {
onScriptExecutionError(scriptId, t, scriptBody);
if (t instanceof TimeoutException || (t.getCause() != null && t.getCause() instanceof TimeoutException)) {
queueTimeoutMsgs.incrementAndGet();
}
@ -212,8 +214,14 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService {
return invokeResult.getResult();
} else {
final RuntimeException e = new RuntimeException(invokeResult.getErrorDetails());
onScriptExecutionError(scriptId, e, scriptBody);
log.debug("[{}] Failed to compile script due to [{}]: {}", scriptId, invokeResult.getErrorCode().name(), invokeResult.getErrorDetails());
if (JsInvokeProtos.JsInvokeErrorCode.TIMEOUT_ERROR.equals(invokeResult.getErrorCode())) {
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;
}
}, 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.EDGE, customerEntityPermissionChecker);
put(Resource.RPC, rpcPermissionChecker);
put(Resource.DEVICE_PROFILE, deviceProfilePermissionChecker);
}
private static final PermissionChecker customerAlarmPermissionChecker = new PermissionChecker() {
@ -152,4 +153,19 @@ public class CustomerUserPermissions extends AbstractPermissions {
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) {
if (externalId == null || externalId.isNullUid()) return null;
if (EntityType.TENANT.equals(externalId.getEntityType())) {
return (ID) ctx.getTenantId();
}
EntityId localId = ctx.getInternalId(externalId);
if (localId != null) {
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.setFirmwareId(getOldEntityField(old, DeviceProfile::getFirmwareId));
deviceProfile.setSoftwareId(getOldEntityField(old, DeviceProfile::getSoftwareId));
deviceProfile.setDefaultQueueId(getOldEntityField(old, DeviceProfile::getDefaultQueueId));
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 -> {
cachePut(commit.getTxId(), new VersionCreationResult());
try {
List<ListenableFuture<Void>> gitFutures = new ArrayList<>();
EntitiesExportCtx<?> theCtx;
switch (request.getType()) {
case SINGLE_ENTITY: {
handleSingleEntityRequest(new SimpleEntitiesExportCtx(user, commit, (SingleEntityVersionCreateRequest) request));
var ctx = new SimpleEntitiesExportCtx(user, commit, (SingleEntityVersionCreateRequest) request);
handleSingleEntityRequest(ctx);
theCtx = ctx;
break;
}
case COMPLEX: {
handleComplexRequest(new ComplexEntitiesExportCtx(user, commit, (ComplexVersionCreateRequest) request));
var ctx = new ComplexEntitiesExportCtx(user, commit, (ComplexVersionCreateRequest) request);
handleComplexRequest(ctx);
theCtx = ctx;
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);
} catch (Exception 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.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
@ -102,7 +104,7 @@ public class DefaultGitVersionControlQueueService implements GitVersionControlQu
private final SchedulerComponent scheduler;
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}")
private int requestTimeout;
@ -286,7 +288,7 @@ public class DefaultGitVersionControlQueueService implements GitVersionControlQu
@SuppressWarnings("rawtypes")
public ListenableFuture<EntityExportData> getEntity(TenantId tenantId, String versionId, EntityId 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()
.setVersionId(versionId)
.setEntityType(entityId.getEntityType().name())
@ -328,7 +330,7 @@ public class DefaultGitVersionControlQueueService implements GitVersionControlQu
@SuppressWarnings("rawtypes")
public ListenableFuture<List<EntityExportData>> getEntities(TenantId tenantId, String versionId, EntityType entityType, int offset, int limit) {
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()
.setVersionId(versionId)
.setEntityType(entityType.name())
@ -412,10 +414,10 @@ public class DefaultGitVersionControlQueueService implements GitVersionControlQu
((ListVersionsGitRequest) request).getFuture().set(toPageData(listVersionsResponse));
} else if (vcResponseMsg.hasEntityContentResponse()) {
TransportProtos.EntityContentResponseMsg responseMsg = vcResponseMsg.getEntityContentResponse();
log.trace("[{}] received chunk {} for 'getEntity'", responseMsg.getChunkedMsgId(), responseMsg.getChunkIndex());
var joined = joinChunks(requestId, responseMsg, 1);
log.trace("Received chunk {} for 'getEntity'", responseMsg.getChunkIndex());
var joined = joinChunks(requestId, responseMsg, 0, 1);
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));
} else {
completed = false;
@ -424,7 +426,7 @@ public class DefaultGitVersionControlQueueService implements GitVersionControlQu
TransportProtos.EntitiesContentResponseMsg responseMsg = vcResponseMsg.getEntitiesContentResponse();
TransportProtos.EntityContentResponseMsg item = responseMsg.getItem();
if (responseMsg.getItemsCount() > 0) {
var joined = joinChunks(requestId, item, responseMsg.getItemsCount());
var joined = joinChunks(requestId, item, responseMsg.getItemIdx(), responseMsg.getItemsCount());
if (joined.isPresent()) {
((EntitiesContentGitRequest) request).getFuture().set(joined.get());
} else {
@ -459,16 +461,17 @@ public class DefaultGitVersionControlQueueService implements GitVersionControlQu
}
@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);
if (chunksMap == null) {
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();
if (chunksMap.size() == expectedMsgCount && chunksMap.values().stream()
.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(this::toData)
.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;
}
@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() {
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() {
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() {
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;
}
@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() {
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() {
return value;
}

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

@ -27,7 +27,7 @@
<logger name="org.thingsboard.server" level="INFO"/>
<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-->
<!-- <logger name="org.thingsboard.server.service.component.AnnotationComponentDiscoveryService" level="DEBUG" />-->
<!-- Other useful logs -->

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

@ -148,7 +148,7 @@ ui:
# Help parameters
help:
# Base url for UI help assets
base-url: "${UI_HELP_BASE_URL:https://raw.githubusercontent.com/thingsboard/thingsboard-ui-help/release-3.3.4}"
base-url: "${UI_HELP_BASE_URL:https://raw.githubusercontent.com/thingsboard/thingsboard-ui-help/release-3.4}"
database:
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
js_thread_pool_size: "${ACTORS_RULE_JS_THREAD_POOL_SIZE:50}"
# 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
sms_thread_pool_size: "${ACTORS_RULE_SMS_THREAD_POOL_SIZE:50}"
# Whether to allow usage of system mail service for rules
@ -545,10 +547,6 @@ spring:
audit-log:
# Enable/disable audit log functionality.
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.
# Allowed values: OFF (disable), W (log write operations), RW (log read and write operations)
logging-level:
@ -945,10 +943,11 @@ queue:
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}"
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}"
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}"
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:
enabled: "${TB_QUEUE_KAFKA_CONSUMER_STATS_ENABLED:true}"
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}"
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}"
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
pubsub:
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}"
notifications: "${TB_QUEUE_PUBSUB_NOTIFICATIONS_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:
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}"
@ -988,6 +990,7 @@ queue:
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}"
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:
exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}"
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}"
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}"
version-control: "${TB_QUEUE_RABBIT_MQ_VC_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
partitions:
hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256
transport_api:
@ -1045,6 +1049,8 @@ queue:
max_eval_requests_timeout: "${REMOTE_JS_MAX_EVAL_REQUEST_TIMEOUT:60000}"
# JS max request timeout
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
response_poll_interval: "${REMOTE_JS_RESPONSE_POLL_INTERVAL_MS:25}"
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.springframework.boot.test.mock.mockito.SpyBean;
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.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.edge.EdgeEventActionType;
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.dao.audit.AuditLogService;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.service.gateway_device.GatewayNotificationsService;
import java.util.ArrayList;
import java.util.List;
@ -58,21 +58,19 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
@SpyBean
protected AuditLogService auditLogService;
@SpyBean
protected GatewayNotificationsService gatewayNotificationsService;
protected final String msgErrorPermission = "You don't have permission to perform this operation!";
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,
TenantId tenantId, CustomerId customerId, UserId userId, String userName,
ActionType actionType, Object... additionalInfo) {
int cntTime = 1;
testSendNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime);
testNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime);
testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo);
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTime);
Mockito.reset(tbClusterService, auditLogService);
}
@ -85,27 +83,35 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
Mockito.eq(edgeTypeByActionType(actionType)));
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(relation.getTo());
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));
testPushMsgToRuleEngineNever(relation.getTo());
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));
testPushMsgToRuleEngineNever(relation.getFrom());
Mockito.reset(tbClusterService, auditLogService);
}
protected void testNotifyEntityAllManyRelation(EntityRelation relation,
TenantId tenantId, CustomerId customerId, UserId userId, String userName,
ActionType actionType, int cntTime) {
TenantId tenantId, CustomerId customerId, UserId userId, String userName,
ActionType actionType, int cntTime) {
Mockito.verify(tbClusterService, times(cntTime)).sendNotificationMsgToEdge(Mockito.eq(tenantId),
Mockito.isNull(), Mockito.isNull(), Mockito.any(), Mockito.eq(EdgeEventType.RELATION),
Mockito.eq(edgeTypeByActionType(actionType)));
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.getClass().equals(relation.getFrom().getClass());
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);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, new Tenant(), cntTime);
Mockito.reset(tbClusterService, auditLogService);
}
@ -113,16 +119,16 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
TenantId tenantId, CustomerId customerId, UserId userId, String userName,
ActionType actionType, Object... additionalInfo) {
int cntTime = 1;
testSendNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime);
testNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime);
testLogEntityActionEntityEqClass(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo);
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTime);
Mockito.reset(tbClusterService, auditLogService);
}
protected void testNotifyEntityNeverMsgToEdgeServiceOneTime(HasName entity, EntityId entityId, TenantId tenantId,
ActionType actionType) {
testSendNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, 1);
testNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, 1);
testLogEntityActionNever(entityId, entity);
testPushMsgToRuleEngineNever(entityId);
Mockito.reset(tbClusterService, auditLogService);
@ -132,13 +138,13 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
TenantId tenantId, CustomerId customerId, UserId userId,
String userName, ActionType actionType, Object... additionalInfo) {
int cntTime = 1;
testNotificationMsgToEdgeServiceNever(entityId);
testNotificationMsgToEdgeServiceNeverWithActionType(entityId, actionType);
testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo);
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId);
if (ActionType.RELATIONS_DELETED.equals(actionType)) {
testPushMsgToRuleEngineNever(originatorId);
} else {
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTime);
}
Mockito.reset(tbClusterService, auditLogService);
}
@ -148,12 +154,16 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
ActionType actionType, int cntTime, Object... additionalInfo) {
EntityId entityId = createEntityId_NULL_UUID(entity);
EntityId originatorId = createEntityId_NULL_UUID(originator);
testNotificationMsgToEdgeServiceNever(entityId);
testNotificationMsgToEdgeServiceNeverWithActionType(entityId, actionType);
ArgumentMatcher<HasName> matcherEntityClassEquals = argument -> argument.getClass().equals(entity.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));
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTime);
Mockito.reset(tbClusterService, auditLogService);
}
@ -165,10 +175,13 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
testSendNotificationMsgToEdgeServiceTimeEntityEqAny(tenantId, actionTypeEdge, cntTimeEdge);
ArgumentMatcher<HasName> matcherEntityClassEquals = argument -> argument.getClass().equals(entity.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));
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTimeRuleEngine);
Mockito.reset(tbClusterService, auditLogService);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTimeRuleEngine);
}
protected void testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAnyAdditionalInfoAny(HasName entity, HasName originator,
@ -178,9 +191,13 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
testSendNotificationMsgToEdgeServiceTimeEntityEqAny(tenantId, actionTypeEdge, cntTimeEdge);
ArgumentMatcher<HasName> matcherEntityClassEquals = argument -> argument.getClass().equals(entity.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);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTimeEdge);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTimeEdge);
Mockito.reset(tbClusterService, auditLogService);
}
@ -189,12 +206,16 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
ActionType actionType, int cntTime, int cntAdditionalInfo) {
EntityId entityId = createEntityId_NULL_UUID(entity);
EntityId originatorId = createEntityId_NULL_UUID(originator);
testNotificationMsgToEdgeServiceNever(entityId);
testNotificationMsgToEdgeServiceNeverWithActionType(entityId, actionType);
ArgumentMatcher<HasName> matcherEntityClassEquals = argument -> argument.getClass().equals(entity.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);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTime);
Mockito.reset(tbClusterService, auditLogService);
}
@ -202,10 +223,10 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
TenantId tenantId, CustomerId customerId, UserId userId, String userName,
ActionType actionType, Object... additionalInfo) {
int cntTime = 1;
testSendNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime);
testNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime);
testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo);
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTime);
testBroadcastEntityStateChangeEventTime(entityId, tenantId, cntTime);
Mockito.reset(tbClusterService, auditLogService);
}
@ -214,34 +235,40 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
TenantId tenantId, CustomerId customerId, UserId userId, String userName,
ActionType actionType, Object... additionalInfo) {
int cntTime = 1;
testNotificationMsgToEdgeServiceNever(entityId);
testNotificationMsgToEdgeServiceNeverWithActionType(entityId, actionType);
testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo);
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTime);
testBroadcastEntityStateChangeEventTime(entityId, tenantId, cntTime);
Mockito.reset(tbClusterService, auditLogService);
}
protected void testNotifyEntityBroadcastEntityStateChangeEventManyMsgToEdgeServiceNever(HasName entity, HasName originator,
TenantId tenantId, CustomerId customerId, UserId userId, String userName,
ActionType actionType, int cntTime, int cntAdditionalInfo) {
protected void testNotifyEntityBroadcastEntityStateChangeEventMany(HasName entity, HasName originator,
TenantId tenantId, CustomerId customerId,
UserId userId, String userName, ActionType actionType,
ActionType actionTypeEdge,
int cntTime, int cntTimeEdge, int cntTimeRuleEngine,
int cntAdditionalInfo) {
EntityId entityId = createEntityId_NULL_UUID(entity);
EntityId originatorId = createEntityId_NULL_UUID(originator);
testNotificationMsgToEdgeServiceNever(entityId);
testNotificationMsgToEdgeServiceTime(entityId, tenantId, actionTypeEdge, cntTimeEdge);
ArgumentMatcher<HasName> matcherEntityClassEquals = argument -> argument.getClass().equals(entity.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);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTimeRuleEngine);
testBroadcastEntityStateChangeEventTime(entityId, tenantId, cntTime);
Mockito.reset(tbClusterService, auditLogService);
}
protected void testNotifyEntityMsgToEdgePushMsgToCoreOneTime(HasName entity, EntityId entityId, EntityId originatorId,
TenantId tenantId, CustomerId customerId, UserId userId, String userName,
ActionType actionType, Object... additionalInfo) {
int cntTime = 1;
testSendNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime);
testNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime);
testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo);
tesPushMsgToCoreTime(cntTime);
Mockito.reset(tbClusterService, auditLogService);
@ -252,7 +279,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
Object... additionalInfo) {
CustomerId customer_NULL_UUID = (CustomerId) EntityIdFactory.getByTypeAndUuid(EntityType.CUSTOMER, ModelConstants.NULL_UUID);
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<Exception> matcherError = argument -> argument.getMessage().contains(exp.getMessage())
& argument.getClass().equals(exp.getClass());
@ -266,7 +293,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
Object... additionalInfo) {
CustomerId customer_NULL_UUID = (CustomerId) EntityIdFactory.getByTypeAndUuid(EntityType.CUSTOMER, ModelConstants.NULL_UUID);
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<Exception> matcherError = argument -> argument.getMessage().contains(exp.getMessage()) &
argument.getClass().equals(exp.getClass());
@ -283,20 +310,11 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
Mockito.reset(tbClusterService, auditLogService);
}
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));
private void testNotificationMsgToEdgeServiceNeverWithActionType(EntityId entityId, ActionType actionType) {
EdgeEventActionType edgeEventActionType = ActionType.CREDENTIALS_UPDATED.equals(actionType) ?
EdgeEventActionType.CREDENTIALS_UPDATED : edgeTypeByActionType(actionType);
Mockito.verify(tbClusterService, never()).sendNotificationMsgToEdge(Mockito.any(),
Mockito.any(), Mockito.any(entityId.getClass()), Mockito.any(), Mockito.any(), Mockito.eq(edgeEventActionType));
}
private void testNotificationMsgToEdgeServiceNever(EntityId entityId) {
@ -317,16 +335,24 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
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.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.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.any(), Mockito.eq(entityId), Mockito.any(), Mockito.isNull(),
Mockito.any(), Mockito.argThat(matcherEntityId), Mockito.any(), Mockito.isNull(),
Mockito.eq(edgeEventActionType));
}
@ -336,8 +362,10 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
Mockito.eq(edgeTypeByActionType(actionType)));
}
private void testBroadcastEntityStateChangeEventTime(EntityId entityId, TenantId tenantId, int cntTime) {
Mockito.verify(tbClusterService, times(cntTime)).broadcastEntityStateChangeEvent(Mockito.eq(tenantId),
protected void testBroadcastEntityStateChangeEventTime(EntityId entityId, TenantId tenantId, int cntTime) {
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));
}
@ -348,9 +376,13 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
private void testLogEntityAction(HasName entity, EntityId originatorId, TenantId tenantId,
CustomerId customerId, UserId userId, String userName,
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);
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));
}
@ -359,19 +391,24 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
ActionType actionType, int cntTime, Object... additionalInfo) {
ArgumentMatcher<HasName> matcherEntityEquals = argument -> argument.getClass().equals(entity.getClass());
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));
}
private void testLogEntityActionAdditionalInfo(ArgumentMatcher<HasName> matcherEntity, ArgumentMatcher<EntityId> matcherOriginatorId,
TenantId tenantId, CustomerId customerId, UserId userId, String userName,
ActionType actionType, int cntTime, List<ArgumentMatcher<Object>> matcherAdditionalInfos) {
TenantId tenantId, ArgumentMatcher<CustomerId> matcherCustomerId,
ArgumentMatcher<UserId> matcherUserId, String userName, ActionType actionType,
int cntTime, List<ArgumentMatcher<Object>> matcherAdditionalInfos) {
switch (matcherAdditionalInfos.size()) {
case 1:
Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId),
Mockito.eq(userId),
Mockito.argThat(matcherCustomerId),
Mockito.argThat(matcherUserId),
Mockito.eq(userName),
Mockito.argThat(matcherOriginatorId),
Mockito.argThat(matcherEntity),
@ -382,8 +419,8 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
case 2:
Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId),
Mockito.eq(userId),
Mockito.argThat(matcherCustomerId),
Mockito.argThat(matcherUserId),
Mockito.eq(userName),
Mockito.argThat(matcherOriginatorId),
Mockito.argThat(matcherEntity),
@ -395,8 +432,8 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
case 3:
Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId),
Mockito.eq(userId),
Mockito.argThat(matcherCustomerId),
Mockito.argThat(matcherUserId),
Mockito.eq(userName),
Mockito.argThat(matcherOriginatorId),
Mockito.argThat(matcherEntity),
@ -409,8 +446,8 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
default:
Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId),
Mockito.eq(userId),
Mockito.argThat(matcherCustomerId),
Mockito.argThat(matcherUserId),
Mockito.eq(userName),
Mockito.argThat(matcherOriginatorId),
Mockito.argThat(matcherEntity),
@ -420,14 +457,15 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
}
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) {
switch (cntAdditionalInfo) {
case 1:
Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId),
Mockito.eq(userId),
Mockito.argThat(matcherCustomerId),
Mockito.argThat(matcherUserId),
Mockito.eq(userName),
Mockito.argThat(matcherOriginatorId),
Mockito.argThat(matcherEntity),
@ -438,8 +476,8 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
case 2:
Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId),
Mockito.eq(userId),
Mockito.argThat(matcherCustomerId),
Mockito.argThat(matcherUserId),
Mockito.eq(userName),
Mockito.argThat(matcherOriginatorId),
Mockito.argThat(matcherEntity),
@ -451,8 +489,8 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
case 3:
Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId),
Mockito.eq(userId),
Mockito.argThat(matcherCustomerId),
Mockito.argThat(matcherUserId),
Mockito.eq(userName),
Mockito.argThat(matcherOriginatorId),
Mockito.argThat(matcherEntity),
@ -465,8 +503,8 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
default:
Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId),
Mockito.eq(userId),
Mockito.argThat(matcherCustomerId),
Mockito.argThat(matcherUserId),
Mockito.eq(userName),
Mockito.argThat(matcherOriginatorId),
Mockito.argThat(matcherEntity),
@ -479,12 +517,14 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
CustomerId customerId, UserId userId, String userName, ActionType actionType,
int cntTime, ArgumentMatcher<Exception> matcherError,
List<ArgumentMatcher<Object>> matcherAdditionalInfos) {
ArgumentMatcher<UserId> matcherUserId = userId == null ? argument -> argument.getClass().equals(UserId.class) :
argument -> argument.equals(userId);
switch (matcherAdditionalInfos.size()) {
case 1:
Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId),
Mockito.eq(userId),
Mockito.argThat(matcherUserId),
Mockito.eq(userName),
Mockito.eq(originatorId),
Mockito.argThat(matcherEntity),
@ -496,7 +536,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId),
Mockito.eq(userId),
Mockito.argThat(matcherUserId),
Mockito.eq(userName),
Mockito.eq(originatorId),
Mockito.argThat(matcherEntity),
@ -508,7 +548,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId),
Mockito.eq(userId),
Mockito.argThat(matcherUserId),
Mockito.eq(userName),
Mockito.eq(originatorId),
Mockito.argThat(matcherEntity),
@ -522,7 +562,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId),
Mockito.eq(userId),
Mockito.argThat(matcherUserId),
Mockito.eq(userName),
Mockito.eq(originatorId),
Mockito.argThat(matcherEntity),
@ -558,7 +598,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
return result;
}
private EntityId createEntityId_NULL_UUID(HasName entity) {
protected EntityId createEntityId_NULL_UUID(HasName entity) {
return EntityIdFactory.getByTypeAndUuid(entityClassToEntityTypeName(entity), ModelConstants.NULL_UUID);
}
@ -571,6 +611,12 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
}
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()
.substring(entity.getClass().toString().lastIndexOf(".") + 1);
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 {
if (savedDifferentTenant != null) {
login(savedDifferentTenant.getEmail(), TENANT_ADMIN_PASSWORD);
login(DIFFERENT_TENANT_ADMIN_EMAIL, DIFFERENT_TENANT_ADMIN_PASSWORD);
} else {
loginSysAdmin();
@ -422,12 +422,6 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
}
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.setName(name);
deviceProfile.setType(DeviceProfileType.DEFAULT);
@ -445,7 +439,6 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
deviceProfile.setProfileData(deviceProfileData);
deviceProfile.setDefault(false);
deviceProfile.setDefaultRuleChainId(null);
deviceProfile.setDefaultQueueId(defaultQueueId);
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.node.ObjectNode;
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.service.mail.DefaultMailService;
import static org.hamcrest.Matchers.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.hamcrest.Matchers.containsString;
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 {
@Autowired
MailService mailService;
@Autowired
DefaultMailService defaultMailService;
@Test
public void testFindAdminSettingsByKey() throws Exception {
loginSysAdmin();
@ -100,5 +115,28 @@ public abstract class BaseAdminControllerTest extends AbstractControllerTest {
doPost("/api/admin/settings/testMail", adminSettings)
.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())
.andExpect(status().isForbidden())
.andExpect(statusReason(containsString(msgErrorPermission)));
testNotifyEntityNever(savedDashboard.getId(), savedDashboard);
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.Test;
import org.mockito.Mockito;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.thingsboard.common.util.ThingsBoardExecutors;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.OtaPackageInfo;
import org.thingsboard.server.common.data.SaveOtaPackageInfoRequest;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
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.DeviceCredentialsValidationException;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.service.gateway_device.GatewayNotificationsService;
import java.util.ArrayList;
import java.util.List;
@ -56,12 +61,15 @@ import java.util.concurrent.TimeUnit;
import static org.assertj.core.api.Assertions.assertThat;
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.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;
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;
@ -71,6 +79,9 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
private Tenant savedTenant;
private User tenantAdmin;
@SpyBean
private GatewayNotificationsService gatewayNotificationsService;
@Before
public void beforeTest() throws Exception {
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 oldDevice = new Device(savedDevice);
testNotifyEntityOneTimeMsgToEdgeServiceNever(savedDevice, savedDevice.getId(), savedDevice.getId(),
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.ADDED);
@ -203,7 +215,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
String savedDeviceIdStr = savedDevice.getId().getId().toString();
doPost("/api/device", savedDevice)
.andExpect( status().isNotFound())
.andExpect(status().isNotFound())
.andExpect(statusReason(containsString(msgErrorNoFound("Device", savedDeviceIdStr))));
testNotifyEntityNever(savedDevice.getId(), savedDevice);
@ -221,6 +233,66 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
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
public void testFindDeviceById() throws Exception {
Device device = new Device();
@ -248,8 +320,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
}
testNotifyManyEntityManyTimeMsgToEdgeServiceNever(new Device(), new Device(),
savedTenant.getId(),
tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.ADDED, cntEntity);
testNotificationUpdateGatewayNever();
@ -813,7 +884,9 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new Device(), new Device(),
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.ADDED, ActionType.ASSIGNED_TO_CUSTOMER, cntEntity, cntEntity, cntEntity * 2);
Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService);
testNotificationUpdateGatewayNever();
Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService);
List<Device> loadedDevices = new ArrayList<>(cntEntity);
PageLink pageLink = new PageLink(23);
@ -828,8 +901,6 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
assertThat(devices).containsExactlyInAnyOrderElementsOf(loadedDevices);
Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService);
deleteEntitiesAsync("/api/customer/device/", loadedDevices, executor).get(TIMEOUT, TimeUnit.SECONDS);
testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAnyAdditionalInfoAny(new Device(), new Device(),
@ -1116,4 +1187,20 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
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.Test;
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.DeviceProfile;
import org.thingsboard.server.common.data.DeviceProfileInfo;
import org.thingsboard.server.common.data.DeviceProfileProvisionType;
import org.thingsboard.server.common.data.DeviceProfileType;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.OtaPackageInfo;
import org.thingsboard.server.common.data.SaveOtaPackageInfoRequest;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
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.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.exception.DataValidationException;
@ -58,6 +63,8 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
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 {
@ -170,6 +177,25 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
Assert.assertEquals(savedDeviceProfile.getId(), foundDeviceProfileInfo.getId());
Assert.assertEquals(savedDeviceProfile.getName(), foundDeviceProfileInfo.getName());
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
@ -325,6 +351,80 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
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
public void testDeleteDeviceProfile() throws Exception {
DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
@ -367,6 +467,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new DeviceProfile(), new DeviceProfile(),
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.ADDED, ActionType.ADDED, cntEntity, cntEntity, cntEntity);
Mockito.reset(tbClusterService, auditLogService);
List<DeviceProfile> loadedDeviceProfiles = new ArrayList<>();
pageLink = new PageLink(17);
@ -988,7 +1089,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString(errorMsg)));
testNotifyEntityEqualsOneTimeServiceNeverError(deviceProfile,savedTenant.getId(),
testNotifyEntityEqualsOneTimeServiceNeverError(deviceProfile, savedTenant.getId(),
tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(errorMsg));
}
@ -1003,7 +1104,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString(errorMsg)));
testNotifyEntityEqualsOneTimeServiceNeverError(deviceProfile,savedTenant.getId(),
testNotifyEntityEqualsOneTimeServiceNeverError(deviceProfile, savedTenant.getId(),
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)
.andExpect(status().isNotFound())
.andExpect(statusReason(containsString("Requested item wasn't found!")));
.andExpect(statusReason(containsString(msgErrorNotFound)));
testNotifyEntityNever(mainDevice.getId(), null);
}
@Test
@ -377,7 +376,7 @@ public abstract class BaseEntityRelationControllerTest extends AbstractControlle
doDelete(url)
.andExpect(status().isNotFound())
.andExpect(statusReason(containsString("Requested item wasn't found!")));
.andExpect(statusReason(containsString(msgErrorNotFound)));
testNotifyEntityNever(mainDevice.getId(), null);
}
@ -570,7 +569,6 @@ public abstract class BaseEntityRelationControllerTest extends AbstractControlle
.andExpect(status().isNotFound())
.andExpect(statusReason(containsString(msgErrorNoFound("Device", relation.getFrom().getId().toString()))));
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.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.web.servlet.ResultActions;
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.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.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.security.Authority;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.model.ModelConstants;
import java.util.ArrayList;
@ -92,7 +95,6 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
@Before
public void beforeTest() throws Exception {
log.debug("beforeTest");
executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(8, getClass()));
loginTenantAdmin();
@ -126,6 +128,9 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
@Test
public void testSaveEntityView() throws Exception {
String name = "Test entity view";
Mockito.reset(tbClusterService, auditLogService);
EntityView savedView = getNewSavedEntityView(name);
Assert.assertNotNull(savedView);
@ -140,6 +145,12 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
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");
doPost("/api/entityView", savedView, EntityView.class);
@ -147,23 +158,57 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
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
public void testSaveEntityViewWithViolationOfValidation() throws Exception {
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");
msgError = msgErrorFieldLength("type");
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
public void testUpdateEntityViewFromDifferentTenant() throws Exception {
EntityView savedView = getNewSavedEntityView("Test entity view");
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();
}
@ -174,20 +219,36 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
view.setCustomerId(customer.getId());
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());
doGet("/api/entityView/" + savedView.getId().getId().toString())
.andExpect(status().isNotFound());
testNotifyEntityBroadcastEntityStateChangeEventOneTimeMsgToEdgeServiceNever(savedView, savedView.getId(), savedView.getId(),
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
public void testSaveEntityViewWithEmptyName() throws Exception {
EntityView entityView = new EntityView();
entityView.setType("default");
Mockito.reset(tbClusterService, auditLogService);
String msgError = "Entity view name " + msgErrorShouldBeSpecified;
doPost("/api/entityView", entityView)
.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
@ -195,8 +256,17 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
EntityView view = getNewSavedEntityView("Test entity view");
Customer savedCustomer = doPost("/api/customer", getNewCustomer("My customer"), Customer.class);
view.setCustomerId(savedCustomer.getId());
Mockito.reset(tbClusterService, auditLogService);
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(
"/api/customer/" + savedCustomer.getId().getId().toString() + "/entityView/" + savedView.getId().getId().toString(),
EntityView.class);
@ -205,18 +275,38 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
EntityView foundView = doGet("/api/entityView/" + savedView.getId().getId().toString(), EntityView.class);
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);
assertEquals(ModelConstants.NULL_UUID, unAssignedView.getCustomerId().getId());
foundView = doGet("/api/entityView/" + savedView.getId().getId().toString(), EntityView.class);
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
public void testAssignEntityViewToNonExistentCustomer() throws Exception {
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
@ -242,8 +332,13 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
EntityView savedView = getNewSavedEntityView("Test entity view");
Mockito.reset(tbClusterService, auditLogService);
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();
@ -257,8 +352,11 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
CustomerId customerId = customer.getId();
String urlTemplate = "/api/customer/" + customerId.getId().toString() + "/entityViewInfos?";
List<ListenableFuture<EntityViewInfo>> viewFutures = new ArrayList<>(128);
for (int i = 0; i < 128; i++) {
Mockito.reset(tbClusterService, auditLogService);
int cntEntity = 128;
List<ListenableFuture<EntityViewInfo>> viewFutures = new ArrayList<>(cntEntity);
for (int i = 0; i < cntEntity; i++) {
String entityName = "Test entity view " + i;
viewFutures.add(executor.submit(() ->
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);
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
@ -289,12 +396,21 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
assertThat(namesOfView2).as(name2).containsExactlyInAnyOrderElementsOf(loadedNamesOfView2);
deleteFutures.clear();
Mockito.reset(tbClusterService, auditLogService);
int cntEntity = loadedNamesOfView1.size();
for (EntityView view : loadedNamesOfView1) {
deleteFutures.add(executor.submit(() ->
doDelete("/api/customer/entityView/" + view.getId().getId().toString()).andExpect(status().isOk())));
}
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,
new PageLink(4, 0, name1));
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> actualAttributesSet =
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");
log.debug("fetching entity view telemetry...");
List<Map<String, Object>> values = await("telemetry/ENTITY_VIEW")
.atMost(TIMEOUT, SECONDS)
.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());
log.debug("asserting...");
assertEquals("value1", getValue(values, "caKey1"));
assertEquals(true, getValue(values, "caKey2"));
assertEquals(42.0, getValue(values, "caKey3"));
@ -526,7 +639,6 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
getWsClient().subscribeLatestUpdate(keysToSubscribe, dtf);
String viewDeviceId = testDevice.getId().getId().toString();
log.debug("deviceid {}", viewDeviceId);
DeviceCredentials deviceCredentials =
doGet("/api/device/" + viewDeviceId + "/credentials", DeviceCredentials.class);
assertEquals(testDevice.getId(), deviceCredentials.getDeviceId());
@ -534,7 +646,6 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
String accessToken = deviceCredentials.getCredentialsId();
assertNotNull(accessToken);
log.debug("creating mqtt client...");
String clientId = MqttAsyncClient.generateClientId();
MqttAsyncClient client = new MqttAsyncClient("tcp://localhost:1883", clientId, new MemoryPersistence());
@ -542,14 +653,11 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
options.setUserName(accessToken);
client.connect(options);
awaitConnected(client, SECONDS.toMillis(30));
log.debug("mqtt connected...");
MqttMessage message = new MqttMessage();
message.setPayload((stringKV).getBytes());
getWsClient().registerWaitForUpdate();
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);
log.debug("token.message delivered {}", token.getMessage());
assertThat(getWsClient().waitForUpdate()).as("ws update received").isNotBlank();
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.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder;
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.Tenant;
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.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.exception.DataValidationException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
@ -102,6 +105,8 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes
firmwareInfo.setVersion(VERSION);
firmwareInfo.setUsesUrl(false);
Mockito.reset(tbClusterService, auditLogService);
OtaPackageInfo savedFirmwareInfo = save(firmwareInfo);
Assert.assertNotNull(savedFirmwareInfo);
@ -111,12 +116,20 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes
Assert.assertEquals(firmwareInfo.getTitle(), savedFirmwareInfo.getTitle());
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());
save(new SaveOtaPackageInfoRequest(savedFirmwareInfo, false));
OtaPackageInfo foundFirmwareInfo = doGet("/api/otaPackage/info/" + savedFirmwareInfo.getId().getId().toString(), OtaPackageInfo.class);
Assert.assertEquals(foundFirmwareInfo.getTitle(), savedFirmwareInfo.getTitle());
testNotifyEntityOneTimeMsgToEdgeServiceNever(foundFirmwareInfo, foundFirmwareInfo.getId(), foundFirmwareInfo.getId(),
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.UPDATED);
}
@Test
@ -127,14 +140,42 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes
firmwareInfo.setTitle(RandomStringUtils.randomAlphabetic(300));
firmwareInfo.setVersion(VERSION);
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.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.setUsesUrl(true);
msgError = msgErrorFieldLength("url");
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
@ -164,12 +205,19 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes
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(CONTENT_TYPE, savedFirmware.getContentType());
Assert.assertEquals(CHECKSUM_ALGORITHM, savedFirmware.getChecksumAlgorithm().name());
Assert.assertEquals(CHECKSUM, savedFirmware.getChecksum());
testNotifyEntityAllOneTime(savedFirmware, savedFirmware.getId(), savedFirmware.getId(),
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.UPDATED);
}
@Test
@ -184,10 +232,16 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes
OtaPackageInfo savedFirmwareInfo = save(firmwareInfo);
loginDifferentTenant();
Mockito.reset(tbClusterService, auditLogService);
doPost("/api/otaPackage",
new SaveOtaPackageInfoRequest(savedFirmwareInfo, false),
OtaPackageInfo.class,
status().isForbidden());
new SaveOtaPackageInfoRequest(savedFirmwareInfo, false))
.andExpect(status().isForbidden())
.andExpect(statusReason(containsString(msgErrorPermission)));
testNotifyEntityNever(savedFirmwareInfo.getId(), savedFirmwareInfo);
deleteDifferentTenant();
}
@ -220,11 +274,12 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes
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);
Assert.assertNotNull(foundFirmware);
Assert.assertEquals(savedFirmware, new OtaPackageInfo(foundFirmware));
Assert.assertEquals(savedFirmware, foundFirmware);
Assert.assertEquals(DATA, foundFirmware.getData());
}
@ -239,17 +294,29 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes
OtaPackageInfo savedFirmwareInfo = save(firmwareInfo);
Mockito.reset(tbClusterService, auditLogService);
doDelete("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString())
.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())
.andExpect(status().isNotFound());
.andExpect(status().isNotFound())
.andExpect(statusReason(containsString(msgErrorNotFound)));
}
@Test
public void testFindTenantFirmwares() throws Exception {
Mockito.reset(tbClusterService, auditLogService);
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();
firmwareInfo.setDeviceProfileId(deviceProfileId);
firmwareInfo.setType(FIRMWARE);
@ -259,16 +326,19 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes
OtaPackageInfo savedFirmwareInfo = save(firmwareInfo);
if (i > 100) {
if (i >= startIndexSaveData) {
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);
otaPackages.add(savedFirmware);
} else {
otaPackages.add(savedFirmwareInfo);
OtaPackage savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM);
savedFirmwareInfo = new OtaPackageInfo(savedFirmware);
}
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<>();
PageLink pageLink = new PageLink(24);
PageData<OtaPackageInfo> pageData;
@ -306,7 +376,7 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes
if (i > 100) {
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);
otaPackagesWithData.add(savedFirmwareInfo);
}
@ -352,11 +422,11 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes
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);
postRequest.file(content);
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.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.exception.DataValidationException;
import java.util.ArrayList;
import java.util.Collections;
@ -75,22 +78,45 @@ public abstract class BaseRuleChainControllerTest extends AbstractControllerTest
public void testSaveRuleChain() throws Exception {
RuleChain ruleChain = new RuleChain();
ruleChain.setName("RuleChain");
Mockito.reset(tbClusterService, auditLogService);
RuleChain savedRuleChain = doPost("/api/ruleChain", ruleChain, RuleChain.class);
Assert.assertNotNull(savedRuleChain);
Assert.assertNotNull(savedRuleChain.getId());
Assert.assertTrue(savedRuleChain.getCreatedTime() > 0);
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");
doPost("/api/ruleChain", savedRuleChain, RuleChain.class);
RuleChain foundRuleChain = doGet("/api/ruleChain/" + savedRuleChain.getId().getId().toString(), RuleChain.class);
Assert.assertEquals(savedRuleChain.getName(), foundRuleChain.getName());
testNotifyEntityOneTimeMsgToEdgeServiceNever(savedRuleChain, savedRuleChain.getId(), savedRuleChain.getId(),
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.UPDATED);
}
@Test
public void testSaveRuleChainWithViolationOfLengthValidation() throws Exception {
Mockito.reset(tbClusterService, auditLogService);
RuleChain ruleChain = new RuleChain();
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
@ -109,11 +135,19 @@ public abstract class BaseRuleChainControllerTest extends AbstractControllerTest
ruleChain.setName("RuleChain");
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())
.andExpect(status().isOk());
doGet("/api/ruleChain/" + savedRuleChain.getId().getId().toString())
.andExpect(status().isNotFound());
testNotifyEntityBroadcastEntityStateChangeEventOneTimeMsgToEdgeServiceNever(savedRuleChain, savedRuleChain.getId(), savedRuleChain.getId(),
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
@ -121,15 +155,20 @@ public abstract class BaseRuleChainControllerTest extends AbstractControllerTest
Edge edge = constructEdge("My edge", "default");
Edge savedEdge = doPost("/api/edge", edge, Edge.class);
List<RuleChain> edgeRuleChains = new ArrayList<>();
PageLink pageLink = new PageLink(17);
PageData<RuleChain> pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId() + "/ruleChains?",
new TypeReference<>() {}, pageLink);
new TypeReference<>() {
}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(1, pageData.getTotalElements());
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.setName("RuleChain " + i);
ruleChain.setType(RuleChainType.EDGE);
@ -139,11 +178,21 @@ public abstract class BaseRuleChainControllerTest extends AbstractControllerTest
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<>();
pageLink = new PageLink(17);
do {
pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId() + "/ruleChains?",
new TypeReference<>() {}, pageLink);
new TypeReference<>() {
}, pageLink);
loadedEdgeRuleChains.addAll(pageData.getData());
if (pageData.hasNext()) {
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);
pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId() + "/ruleChains?",
new TypeReference<>() {}, pageLink);
new TypeReference<>() {
}, pageLink);
Assert.assertFalse(pageData.hasNext());
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.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.thingsboard.server.common.data.ResourceType;
import org.thingsboard.server.common.data.TbResource;
import org.thingsboard.server.common.data.TbResourceInfo;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.exception.DataValidationException;
import java.util.ArrayList;
import java.util.Collections;
@ -75,6 +78,9 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes
@Test
public void testSaveTbResource() throws Exception {
Mockito.reset(tbClusterService, auditLogService);
TbResource resource = new TbResource();
resource.setResourceType(ResourceType.JKS);
resource.setTitle("My first resource");
@ -83,6 +89,10 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes
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.getId());
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);
Assert.assertEquals(foundResource.getTitle(), savedResource.getTitle());
testNotifyEntityOneTimeMsgToEdgeServiceNever(foundResource, foundResource.getId(), foundResource.getId(),
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.UPDATED);
}
@Test
@ -107,7 +121,16 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes
resource.setTitle(RandomStringUtils.randomAlphabetic(300));
resource.setFileName(DEFAULT_FILE_NAME);
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
@ -118,10 +141,24 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes
resource.setFileName(DEFAULT_FILE_NAME);
resource.setData("Test Data");
TbResource savedResource = save(resource);
TbResource savedResource = save(resource);
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();
}
@ -150,17 +187,29 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes
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());
testNotifyEntityOneTimeMsgToEdgeServiceNever(savedResource, savedResource.getId(), savedResource.getId(),
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.DELETED, resourceIdStr);
doGet("/api/resource/" + savedResource.getId().getId().toString())
.andExpect(status().isNotFound());
.andExpect(status().isNotFound())
.andExpect(statusReason(containsString(msgErrorNoFound("Resource", resourceIdStr))));
}
@Test
public void testFindTenantTbResources() throws Exception {
Mockito.reset(tbClusterService, auditLogService);
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();
resource.setTitle("Resource" + i);
resource.setResourceType(ResourceType.JKS);
@ -173,7 +222,7 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes
PageData<TbResourceInfo> pageData;
do {
pageData = doGetTypedWithPageLink("/api/resource?",
new TypeReference<PageData<TbResourceInfo>>() {
new TypeReference<>() {
}, pageLink);
loadedResources.addAll(pageData.getData());
if (pageData.hasNext()) {
@ -181,6 +230,10 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes
}
} 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(loadedResources, idComparator);
@ -205,7 +258,7 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes
PageData<TbResourceInfo> pageData;
do {
pageData = doGetTypedWithPageLink("/api/resource?",
new TypeReference<PageData<TbResourceInfo>>() {
new TypeReference<>() {
}, pageLink);
loadedResources.addAll(pageData.getData());
if (pageData.hasNext()) {
@ -218,16 +271,23 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes
Assert.assertEquals(resources, loadedResources);
Mockito.reset(tbClusterService, auditLogService);
int cntEntity = resources.size();
for (TbResourceInfo resource : resources) {
doDelete("/api/resource/" + resource.getId().getId().toString())
.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);
loadedResources.clear();
do {
pageData = doGetTypedWithPageLink("/api/resource?",
new TypeReference<PageData<TbResourceInfo>>() {
new TypeReference<>() {
}, pageLink);
loadedResources.addAll(pageData.getData());
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.Before;
import org.junit.Test;
import org.mockito.ArgumentMatcher;
import org.mockito.Mockito;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.web.servlet.ResultActions;
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.TenantProfile;
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.PageLink;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.data.queue.ProcessingStrategy;
import org.thingsboard.server.common.data.queue.ProcessingStrategyType;
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.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;
@TestPropertySource(properties = {
@ -86,17 +92,28 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest {
loginSysAdmin();
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
Mockito.reset(tbClusterService);
Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
Assert.assertNotNull(savedTenant);
Assert.assertNotNull(savedTenant.getId());
Assert.assertTrue(savedTenant.getCreatedTime() > 0);
Assert.assertEquals(tenant.getTitle(), savedTenant.getTitle());
testBroadcastEntityStateChangeEventTimeManyTimeTenant(savedTenant, ComponentLifecycleEvent.CREATED, 1);
savedTenant.setTitle("My new tenant");
doPost("/api/tenant", savedTenant, Tenant.class);
Tenant foundTenant = doGet("/api/tenant/" + savedTenant.getId().getId().toString(), Tenant.class);
Assert.assertEquals(foundTenant.getTitle(), savedTenant.getTitle());
testBroadcastEntityStateChangeEventTimeManyTimeTenant(savedTenant, ComponentLifecycleEvent.UPDATED, 1);
doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
.andExpect(status().isOk());
testBroadcastEntityStateChangeEventTimeManyTimeTenant(savedTenant, ComponentLifecycleEvent.DELETED, 1);
}
@Test
@ -104,7 +121,14 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest {
loginSysAdmin();
Tenant tenant = new Tenant();
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
@ -136,21 +160,31 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest {
@Test
public void testSaveTenantWithEmptyTitle() throws Exception {
loginSysAdmin();
Mockito.reset(tbClusterService);
Tenant tenant = new Tenant();
doPost("/api/tenant", tenant)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Tenant title should be specified")));
.andExpect(statusReason(containsString("Tenant title " + msgErrorShouldBeSpecified)));
testBroadcastEntityStateChangeEventNeverTenant();
}
@Test
public void testSaveTenantWithInvalidEmail() throws Exception {
loginSysAdmin();
Mockito.reset(tbClusterService);
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
tenant.setEmail("invalid@mail");
doPost("/api/tenant", tenant)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Invalid email address format")));
testBroadcastEntityStateChangeEventNeverTenant();
}
@Test
@ -159,10 +193,13 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest {
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
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());
doGet("/api/tenant/" + savedTenant.getId().getId().toString())
.andExpect(status().isNotFound());
doGet("/api/tenant/" + tenantIdStr)
.andExpect(status().isNotFound())
.andExpect(statusReason(containsString(msgErrorNoFound("Tenant", tenantIdStr))));
}
@Test
@ -175,8 +212,11 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest {
Assert.assertEquals(1, pageData.getData().size());
tenants.addAll(pageData.getData());
Mockito.reset(tbClusterService);
int cntEntity = 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.setTitle("Tenant" + i);
createFutures.add(executor.submit(() ->
@ -184,6 +224,8 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest {
}
tenants.addAll(Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS));
testBroadcastEntityStateChangeEventTimeManyTimeTenant(new Tenant(), ComponentLifecycleEvent.CREATED, cntEntity);
List<Tenant> loadedTenants = new ArrayList<>();
pageLink = new PageLink(17);
do {
@ -200,6 +242,8 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest {
.filter((t) -> !TEST_TENANT_NAME.equals(t.getTitle()))
.collect(Collectors.toList()), executor).get(TIMEOUT, TimeUnit.SECONDS);
testBroadcastEntityStateChangeEventTimeManyTimeTenant(new Tenant(), ComponentLifecycleEvent.DELETED, cntEntity);
pageLink = new PageLink(17);
pageData = doGetTypedWithPageLink("/api/tenants?", PAGE_DATA_TENANT_TYPE_REF, pageLink);
Assert.assertFalse(pageData.hasNext());
@ -464,7 +508,9 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest {
login(username, password);
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();
@ -476,7 +522,7 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest {
queueConfiguration.setName(queueName);
queueConfiguration.setTopic("tb_rule_engine." + queueName.toLowerCase());
queueConfiguration.setPollInterval(25);
queueConfiguration.setPartitions(new Random().nextInt(100));
queueConfiguration.setPartitions(1 + new Random().nextInt(99));
queueConfiguration.setConsumerPerPartition(true);
queueConfiguration.setPackProcessingTimeout(2000);
SubmitStrategy submitStrategy = new SubmitStrategy();
@ -519,4 +565,26 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest {
}
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 org.apache.commons.lang3.RandomStringUtils;
import org.junit.After;
import org.junit.Assert;
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.Tenant;
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.ProcessingStrategyType;
import org.thingsboard.server.common.data.queue.SubmitStrategy;
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.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.dao.tenant.TenantProfileService;
import java.util.ArrayList;
import java.util.Collections;
@ -42,6 +42,8 @@ import java.util.List;
import java.util.stream.Collectors;
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;
public abstract class BaseTenantProfileControllerTest extends AbstractControllerTest {
@ -52,6 +54,9 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController
@Test
public void testSaveTenantProfile() throws Exception {
loginSysAdmin();
Mockito.reset(tbClusterService);
TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile");
TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class);
Assert.assertNotNull(savedTenantProfile);
@ -61,20 +66,30 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController
Assert.assertEquals(tenantProfile.getDescription(), savedTenantProfile.getDescription());
Assert.assertEquals(tenantProfile.getProfileData(), savedTenantProfile.getProfileData());
Assert.assertEquals(tenantProfile.isDefault(), savedTenantProfile.isDefault());
Assert.assertEquals(tenantProfile.isIsolatedTbCore(), savedTenantProfile.isIsolatedTbCore());
Assert.assertEquals(tenantProfile.isIsolatedTbRuleEngine(), savedTenantProfile.isIsolatedTbRuleEngine());
testBroadcastEntityStateChangeEventTimeManyTimeTenantProfile(savedTenantProfile, ComponentLifecycleEvent.CREATED, 1);
savedTenantProfile.setName("New tenant profile");
doPost("/api/tenantProfile", savedTenantProfile, TenantProfile.class);
TenantProfile foundTenantProfile = doGet("/api/tenantProfile/"+savedTenantProfile.getId().getId().toString(), TenantProfile.class);
Assert.assertEquals(foundTenantProfile.getName(), savedTenantProfile.getName());
testBroadcastEntityStateChangeEventTimeManyTimeTenantProfile(savedTenantProfile, ComponentLifecycleEvent.UPDATED, 1);
}
@Test
public void testSaveTenantProfileWithViolationOfLengthValidation() throws Exception {
loginSysAdmin();
Mockito.reset(tbClusterService);
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
@ -122,9 +137,15 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController
@Test
public void testSaveTenantProfileWithEmptyName() throws Exception {
loginSysAdmin();
Mockito.reset(tbClusterService);
TenantProfile tenantProfile = new TenantProfile();
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
@ -132,9 +153,15 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController
loginSysAdmin();
TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile");
doPost("/api/tenantProfile", tenantProfile).andExpect(status().isOk());
Mockito.reset(tbClusterService);
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")));
testBroadcastEntityStateChangeEventNeverTenantProfile();
}
@Test
@ -144,18 +171,14 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController
TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class);
savedTenantProfile.setIsolatedTbRuleEngine(true);
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")));
}
@Test
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")));
testBroadcastEntityStateChangeEventNeverTenantProfile();
}
@Test
@ -169,10 +192,14 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController
tenant.setTenantProfileId(savedTenantProfile.getId());
Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
Mockito.reset(tbClusterService);
doDelete("/api/tenantProfile/" + savedTenantProfile.getId().getId().toString())
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("The tenant profile referenced by the tenants cannot be deleted")));
testBroadcastEntityStateChangeEventNeverTenantProfile();
doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
.andExpect(status().isOk());
}
@ -183,11 +210,16 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController
TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile");
TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class);
Mockito.reset(tbClusterService);
doDelete("/api/tenantProfile/" + savedTenantProfile.getId().getId().toString())
.andExpect(status().isOk());
testBroadcastEntityStateChangeEventTimeManyTimeTenantProfile(savedTenantProfile, ComponentLifecycleEvent.DELETED, 1);
doGet("/api/tenantProfile/" + savedTenantProfile.getId().getId().toString())
.andExpect(status().isNotFound());
.andExpect(status().isNotFound())
.andExpect(statusReason(containsString(msgErrorNoFound("Tenant profile", savedTenantProfile.getId().getId().toString()))));
}
@Test
@ -196,21 +228,26 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController
List<TenantProfile> tenantProfiles = new ArrayList<>();
PageLink pageLink = new PageLink(17);
PageData<TenantProfile> pageData = doGetTypedWithPageLink("/api/tenantProfiles?",
new TypeReference<PageData<TenantProfile>>(){}, pageLink);
new TypeReference<>(){}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(1, pageData.getTotalElements());
tenantProfiles.addAll(pageData.getData());
Mockito.reset(tbClusterService);
int cntEntity = 28;
for (int i=0;i<28;i++) {
TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile"+i);
tenantProfiles.add(doPost("/api/tenantProfile", tenantProfile, TenantProfile.class));
}
testBroadcastEntityStateChangeEventTimeManyTimeTenantProfile(new TenantProfile(), ComponentLifecycleEvent.CREATED, cntEntity);
List<TenantProfile> loadedTenantProfiles = new ArrayList<>();
pageLink = new PageLink(17);
do {
pageData = doGetTypedWithPageLink("/api/tenantProfiles?",
new TypeReference<PageData<TenantProfile>>(){}, pageLink);
new TypeReference<>(){}, pageLink);
loadedTenantProfiles.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
@ -222,6 +259,8 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController
Assert.assertEquals(tenantProfiles, loadedTenantProfiles);
Mockito.reset(tbClusterService);
for (TenantProfile tenantProfile : loadedTenantProfiles) {
if (!tenantProfile.isDefault()) {
doDelete("/api/tenantProfile/" + tenantProfile.getId().getId().toString())
@ -234,6 +273,8 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController
new TypeReference<PageData<TenantProfile>>(){}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(1, pageData.getTotalElements());
testBroadcastEntityStateChangeEventTimeManyTimeTenantProfile(new TenantProfile(), ComponentLifecycleEvent.DELETED, cntEntity);
}
@Test
@ -242,7 +283,7 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController
List<TenantProfile> tenantProfiles = new ArrayList<>();
PageLink pageLink = new PageLink(17);
PageData<TenantProfile> tenantProfilePageData = doGetTypedWithPageLink("/api/tenantProfiles?",
new TypeReference<PageData<TenantProfile>>(){}, pageLink);
new TypeReference<>(){}, pageLink);
Assert.assertFalse(tenantProfilePageData.hasNext());
Assert.assertEquals(1, tenantProfilePageData.getTotalElements());
tenantProfiles.addAll(tenantProfilePageData.getData());
@ -294,7 +335,6 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController
tenantProfileData.setConfiguration(new DefaultTenantProfileConfiguration());
tenantProfile.setProfileData(tenantProfileData);
tenantProfile.setDefault(false);
tenantProfile.setIsolatedTbCore(false);
tenantProfile.setIsolatedTbRuleEngine(false);
return tenantProfile;
}
@ -322,4 +362,28 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController
profileData.setQueueConfiguration(Collections.singletonList(mainQueueConfiguration));
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.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.http.HttpHeaders;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.service.mail.TestMailService;
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.jsonPath;
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 {
private IdComparator<User> idComparator = new IdComparator<>();
private CustomerId customerNUULId = (CustomerId) createEntityId_NULL_UUID(new Customer());
@Test
public void testSaveUser() throws Exception {
loginSysAdmin();
@ -58,6 +64,9 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
user.setEmail(email);
user.setFirstName("Joe");
user.setLastName("Downs");
Mockito.reset(tbClusterService, auditLogService);
User savedUser = doPost("/api/user", user, User.class);
Assert.assertNotNull(savedUser);
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);
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();
doGet("/api/noauth/activate?activateToken={activateToken}", TestMailService.currentActivateToken)
.andExpect(status().isSeeOther())
@ -94,14 +108,24 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
.andExpect(jsonPath("$.email", is(email)));
loginSysAdmin();
foundUser = doGet("/api/user/" + savedUser.getId().getId().toString(), User.class);
Mockito.reset(tbClusterService, auditLogService);
doDelete("/api/user/" + savedUser.getId().getId().toString())
.andExpect(status().isOk());
testNotifyEntityOneTimeMsgToEdgeServiceNever(foundUser, foundUser.getId(), foundUser.getId(),
SYSTEM_TENANT, customerNUULId, null, SYS_ADMIN_EMAIL,
ActionType.DELETED, foundUser.getId().getId().toString());
}
@Test
public void testSaveUserWithViolationOfFiledValidation() throws Exception {
loginSysAdmin();
Mockito.reset(tbClusterService, auditLogService);
String email = "tenant2@thingsboard.org";
User user = new User();
user.setAuthority(Authority.TENANT_ADMIN);
@ -109,10 +133,26 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
user.setEmail(email);
user.setFirstName(RandomStringUtils.randomAlphabetic(300));
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");
msgError = msgErrorFieldLength("last name");
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
@ -128,9 +168,16 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
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
@ -162,7 +209,9 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
.put("resetToken", TestMailService.currentResetPasswordToken)
.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);
doGet("/api/auth/user")
@ -205,6 +254,8 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
public void testSaveUserWithSameEmail() throws Exception {
loginSysAdmin();
Mockito.reset(tbClusterService, auditLogService);
String email = TENANT_ADMIN_EMAIL;
User user = new User();
user.setAuthority(Authority.TENANT_ADMIN);
@ -213,15 +264,22 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
user.setFirstName("Joe");
user.setLastName("Downs");
String msgError = "User with email '" + email + "' already present in database";
doPost("/api/user", user)
.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
public void testSaveUserWithInvalidEmail() throws Exception {
loginSysAdmin();
Mockito.reset(tbClusterService, auditLogService);
String email = "tenant_thingsboard.org";
User user = new User();
user.setAuthority(Authority.TENANT_ADMIN);
@ -230,39 +288,59 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
user.setFirstName("Joe");
user.setLastName("Downs");
String msgError = "Invalid email address format '" + email + "'";
doPost("/api/user", user)
.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
public void testSaveUserWithEmptyEmail() throws Exception {
loginSysAdmin();
Mockito.reset(tbClusterService, auditLogService);
User user = new User();
user.setAuthority(Authority.TENANT_ADMIN);
user.setTenantId(tenantId);
user.setFirstName("Joe");
user.setLastName("Downs");
String msgError = "User email " + msgErrorShouldBeSpecified;
doPost("/api/user", user)
.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
public void testSaveUserWithoutTenant() throws Exception {
loginSysAdmin();
Mockito.reset(tbClusterService, auditLogService);
User user = new User();
user.setAuthority(Authority.TENANT_ADMIN);
user.setEmail("tenant2@thingsboard.org");
user.setFirstName("Joe");
user.setLastName("Downs");
String msgError = "Tenant administrator should be assigned to tenant";
doPost("/api/user", user)
.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
@ -284,8 +362,10 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
doDelete("/api/user/" + savedUser.getId().getId().toString())
.andExpect(status().isOk());
doGet("/api/user/" + savedUser.getId().getId().toString())
.andExpect(status().isNotFound());
String userIdStr = savedUser.getId().getId().toString();
doGet("/api/user/" + userIdStr)
.andExpect(status().isNotFound())
.andExpect(statusReason(containsString( msgErrorNoFound("User",userIdStr))));
}
@Test
@ -300,8 +380,11 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
TenantId tenantId = savedTenant.getId();
Mockito.reset(tbClusterService, auditLogService);
int cntEntity = 64;
List<User> tenantAdmins = new ArrayList<>();
for (int i = 0; i < 64; i++) {
for (int i = 0; i < cntEntity; i++) {
User user = new User();
user.setAuthority(Authority.TENANT_ADMIN);
user.setTenantId(tenantId);
@ -309,12 +392,18 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
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<>();
PageLink pageLink = new PageLink(33);
PageData<User> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
new TypeReference<PageData<User>>() {
new TypeReference<>() {
}, pageLink);
loadedTenantAdmins.addAll(pageData.getData());
if (pageData.hasNext()) {
@ -333,7 +422,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
pageLink = new PageLink(33);
pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
new TypeReference<PageData<User>>() {
new TypeReference<>() {
}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertTrue(pageData.getData().isEmpty());
@ -377,7 +466,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
PageData<User> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
new TypeReference<PageData<User>>() {
new TypeReference<>() {
}, pageLink);
loadedTenantAdminsEmail1.addAll(pageData.getData());
if (pageData.hasNext()) {
@ -394,7 +483,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
pageLink = new PageLink(16, 0, email2);
do {
pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
new TypeReference<PageData<User>>() {
new TypeReference<>() {
}, pageLink);
loadedTenantAdminsEmail2.addAll(pageData.getData());
if (pageData.hasNext()) {
@ -407,14 +496,22 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
Assert.assertEquals(tenantAdminsEmail2, loadedTenantAdminsEmail2);
Mockito.reset(tbClusterService, auditLogService);
int cntEntity = loadedTenantAdminsEmail1.size();
for (User user : loadedTenantAdminsEmail1) {
doDelete("/api/user/" + user.getId().getId().toString())
.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);
pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
new TypeReference<PageData<User>>() {
new TypeReference<>() {
}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
@ -426,7 +523,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
pageLink = new PageLink(4, 0, email2);
pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
new TypeReference<PageData<User>>() {
new TypeReference<>() {
}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
@ -443,7 +540,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
tenantAdmin.setFirstName("Joe");
tenantAdmin.setLastName("Downs");
tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
createUserAndLogin(tenantAdmin, "testPassword1");
Customer customer = new Customer();
customer.setTitle("My customer");
@ -465,7 +562,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
PageData<User> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
new TypeReference<PageData<User>>() {
new TypeReference<>() {
}, pageLink);
loadedCustomerUsers.addAll(pageData.getData());
if (pageData.hasNext()) {
@ -493,7 +590,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
tenantAdmin.setFirstName("Joe");
tenantAdmin.setLastName("Downs");
tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
createUserAndLogin(tenantAdmin, "testPassword1");
Customer customer = new Customer();
customer.setTitle("My customer");
@ -531,10 +628,10 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
List<User> loadedCustomerUsersEmail1 = new ArrayList<>();
PageLink pageLink = new PageLink(33, 0, email1);
PageData<User> pageData = null;
PageData<User> pageData;
do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
new TypeReference<PageData<User>>() {
new TypeReference<>() {
}, pageLink);
loadedCustomerUsersEmail1.addAll(pageData.getData());
if (pageData.hasNext()) {
@ -551,7 +648,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
pageLink = new PageLink(16, 0, email2);
do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
new TypeReference<PageData<User>>() {
new TypeReference<>() {
}, pageLink);
loadedCustomerUsersEmail2.addAll(pageData.getData());
if (pageData.hasNext()) {
@ -571,7 +668,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
pageLink = new PageLink(4, 0, email1);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
new TypeReference<PageData<User>>() {
new TypeReference<>() {
}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
@ -583,7 +680,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
pageLink = new PageLink(4, 0, email2);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
new TypeReference<PageData<User>>() {
new TypeReference<>() {
}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
@ -591,5 +688,4 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
doDelete("/api/customer/" + customerId.getId().toString())
.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.Before;
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.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.PageLink;
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.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.dao.model.ModelConstants.SYSTEM_TENANT;
public abstract class BaseWidgetsBundleControllerTest extends AbstractControllerTest {
@ -73,8 +78,16 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController
public void testSaveWidgetsBundle() throws Exception {
WidgetsBundle widgetsBundle = new WidgetsBundle();
widgetsBundle.setTitle("My widgets bundle");
Mockito.reset(tbClusterService);
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.getId());
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);
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
public void testSaveWidgetBundleWithViolationOfLengthValidation() throws Exception {
WidgetsBundle widgetsBundle = new WidgetsBundle();
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
@ -103,7 +128,15 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController
WidgetsBundle savedWidgetsBundle = doPost("/api/widgetsBundle", widgetsBundle, WidgetsBundle.class);
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();
}
@ -121,21 +154,35 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController
public void testDeleteWidgetsBundle() throws Exception {
WidgetsBundle widgetsBundle = new WidgetsBundle();
widgetsBundle.setTitle("My widgets bundle");
Mockito.reset(tbClusterService, auditLogService);
WidgetsBundle savedWidgetsBundle = doPost("/api/widgetsBundle", widgetsBundle, WidgetsBundle.class);
doDelete("/api/widgetsBundle/"+savedWidgetsBundle.getId().getId().toString())
.andExpect(status().isOk());
doGet("/api/widgetsBundle/"+savedWidgetsBundle.getId().getId().toString())
.andExpect(status().isNotFound());
String savedWidgetsBundleIdStr = savedWidgetsBundle.getId().getId().toString();
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
public void testSaveWidgetsBundleWithEmptyTitle() throws Exception {
Mockito.reset(tbClusterService, auditLogService);
WidgetsBundle widgetsBundle = new WidgetsBundle();
doPost("/api/widgetsBundle", widgetsBundle)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Widgets bundle title should be specified")));
.andExpect(statusReason(containsString("Widgets bundle title " + msgErrorShouldBeSpecified)));
testNotifyEntityNever(widgetsBundle.getId(), widgetsBundle);
}
@Test
@ -144,10 +191,14 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController
widgetsBundle.setTitle("My widgets bundle");
WidgetsBundle savedWidgetsBundle = doPost("/api/widgetsBundle", widgetsBundle, WidgetsBundle.class);
savedWidgetsBundle.setAlias("new_alias");
Mockito.reset(tbClusterService);
doPost("/api/widgetsBundle", savedWidgetsBundle)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Update of widgets bundle alias is prohibited")));
testNotifyEntityNever(savedWidgetsBundle.getId(), savedWidgetsBundle);
}
@Test
@ -156,16 +207,22 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController
login(tenantAdmin.getEmail(), "testPassword1");
List<WidgetsBundle> sysWidgetsBundles = doGetTyped("/api/widgetsBundles?",
new TypeReference<List<WidgetsBundle>>(){});
new TypeReference<>(){});
Mockito.reset(tbClusterService);
int cntEntity = 73;
List<WidgetsBundle> widgetsBundles = new ArrayList<>();
for (int i=0;i<73;i++) {
for (int i=0;i<cntEntity;i++) {
WidgetsBundle widgetsBundle = new WidgetsBundle();
widgetsBundle.setTitle("Widgets bundle"+i);
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);
List<WidgetsBundle> loadedWidgetsBundles = new ArrayList<>();
@ -173,7 +230,7 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController
PageData<WidgetsBundle> pageData;
do {
pageData = doGetTypedWithPageLink("/api/widgetsBundles?",
new TypeReference<PageData<WidgetsBundle>>(){}, pageLink);
new TypeReference<>(){}, pageLink);
loadedWidgetsBundles.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
@ -192,10 +249,11 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController
loginSysAdmin();
List<WidgetsBundle> sysWidgetsBundles = doGetTyped("/api/widgetsBundles?",
new TypeReference<List<WidgetsBundle>>(){});
new TypeReference<>(){});
int cntEntity = 120;
List<WidgetsBundle> createdWidgetsBundles = new ArrayList<>();
for (int i=0;i<120;i++) {
for (int i=0;i<cntEntity;i++) {
WidgetsBundle widgetsBundle = new WidgetsBundle();
widgetsBundle.setTitle("Widgets bundle"+i);
createdWidgetsBundles.add(doPost("/api/widgetsBundle", widgetsBundle, WidgetsBundle.class));
@ -209,7 +267,7 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController
PageData<WidgetsBundle> pageData;
do {
pageData = doGetTypedWithPageLink("/api/widgetsBundles?",
new TypeReference<PageData<WidgetsBundle>>(){}, pageLink);
new TypeReference<>(){}, pageLink);
loadedWidgetsBundles.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
@ -221,11 +279,17 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController
Assert.assertEquals(widgetsBundles, loadedWidgetsBundles);
Mockito.reset(tbClusterService);
for (WidgetsBundle widgetsBundle : createdWidgetsBundles) {
doDelete("/api/widgetsBundle/"+widgetsBundle.getId().getId().toString())
.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);
loadedWidgetsBundles.clear();
do {
@ -262,7 +326,7 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController
widgetsBundles.addAll(sysWidgetsBundles);
List<WidgetsBundle> loadedWidgetsBundles = doGetTyped("/api/widgetsBundles?",
new TypeReference<List<WidgetsBundle>>(){});
new TypeReference<>(){});
Collections.sort(widgetsBundles, idComparator);
Collections.sort(loadedWidgetsBundles, idComparator);
@ -277,7 +341,7 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController
List<WidgetsBundle> sysWidgetsBundles = doGetTyped("/api/widgetsBundles?",
new TypeReference<List<WidgetsBundle>>(){});
new TypeReference<>(){});
List<WidgetsBundle> createdSystemWidgetsBundles = new ArrayList<>();
for (int i=0;i<82;i++) {
@ -324,7 +388,7 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController
}
loadedWidgetsBundles = doGetTyped("/api/widgetsBundles?",
new TypeReference<List<WidgetsBundle>>(){});
new TypeReference<>(){});
Collections.sort(sysWidgetsBundles, 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 TenantId tenantId;
private User tenantAdmin;
private QueueId defaultQueueId;
private DeviceProfile thermostatDeviceProfile;
@ -186,8 +185,6 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
tenantId = savedTenant.getId();
Assert.assertNotNull(savedTenant);
defaultQueueId = getRandomQueueId();
tenantAdmin = new User();
tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
tenantAdmin.setTenantId(savedTenant.getId());
@ -378,7 +375,7 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
@Test
public void testDeviceProfiles() throws Exception {
// 1
DeviceProfile deviceProfile = this.createDeviceProfile("ONE_MORE_DEVICE_PROFILE", null, defaultQueueId);
DeviceProfile deviceProfile = this.createDeviceProfile("ONE_MORE_DEVICE_PROFILE", null);
extendDeviceProfileData(deviceProfile);
edgeImitator.expectMessageAmount(1);
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(deviceProfileUpdateMsg.getIdMSB(), deviceProfile.getUuidId().getMostSignificantBits());
Assert.assertEquals(deviceProfileUpdateMsg.getIdLSB(), deviceProfile.getUuidId().getLeastSignificantBits());
Assert.assertEquals(defaultQueueId.getId().getMostSignificantBits(), deviceProfileUpdateMsg.getDefaultQueueIdMSB());
Assert.assertEquals(defaultQueueId.getId().getLeastSignificantBits(), deviceProfileUpdateMsg.getDefaultQueueIdLSB());
// 2
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
* 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 lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.junit.Assert;
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.TopicPartitionInfo;
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.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static org.mockito.Mockito.mock;
@ -111,15 +112,56 @@ public class HashPartitionServiceTest {
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();
double diff = (data.get(data.size() - 1).getValue() - data.get(0).getValue());
double diffPercent = (diff / ITERATIONS) * 100.0;
double ideal = ((double) iterations) / map.size();
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) + "%)");
Assert.assertTrue(diffPercent < 0.5);
for (Map.Entry<Integer, Integer> entry : data) {
for (Map.Entry<T, Integer> entry : data) {
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.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
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.RuleNodeId;
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.RuleChainMetaData;
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.RuleChainConnectionInfoProto;
import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg;
import org.thingsboard.server.gen.edge.v1.RuleNodeProto;
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.List;
import java.util.Optional;
import java.util.UUID;
import static org.mockito.Mockito.mock;
@Slf4j
@RunWith(MockitoJUnitRunner.class)
public class RuleChainMsgConstructorTest {
private RuleChainMsgConstructor constructor;
private QueueService queueService;
private TenantId tenantId;
private String queueId = "af588000-6c7c-11ec-bafd-c9a47a5c8d99";
@Before
public void setup() {
queueService = mock(QueueService.class);
constructor = new RuleChainMsgConstructor(queueService);
constructor = new RuleChainMsgConstructor();
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
@ -88,7 +72,7 @@ public class RuleChainMsgConstructorTest {
assertCheckpointRuleNodeConfiguration(
ruleChainMetadataUpdateMsg.getNodesList(),
"{\"queueId\":\"" + queueId + "\"}");
"{\"queueName\":\"HighPriority\"}");
}
@Test
@ -345,7 +329,7 @@ public class RuleChainMsgConstructorTest {
return createRuleNode(ruleChainId,
"org.thingsboard.rule.engine.flow.TbCheckpointNode",
"Checkpoint node",
JacksonUtil.OBJECT_MAPPER.readTree("{\"queueId\":\"" + queueId + "\"}"),
JacksonUtil.OBJECT_MAPPER.readTree("{\"queueName\":\"HighPriority\"}"),
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.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
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.assertEquals;
import static org.junit.Assert.assertNotNull;
@ -235,25 +235,37 @@ public abstract class AbstractCoapAttributesIntegrationTest extends AbstractCoap
CoapTestCallback callbackCoap = new CoapTestCallback(1);
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) {
validateUpdateAttributesJsonResponse(callbackCoap, "{}", 0);
validateUpdateAttributesJsonResponse(callbackCoap, "{}");
} 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 expectedObserveCnt = callbackCoap.getObserve().intValue() + 1;
int expectedObserveForAttributesUpdate = callbackCoap.getObserve().intValue() + 1;
doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", SHARED_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
latch.await(3, TimeUnit.SECONDS);
validateUpdateAttributesJsonResponse(callbackCoap, SHARED_ATTRIBUTES_PAYLOAD, expectedObserveCnt);
latch = new CountDownLatch(1);
int expectedObserveBeforeDeleteCnt = callbackCoap.getObserve().intValue() + 1;
awaitAlias = "await Json Test Subscribe To AttributesUpdates (add attributes)";
await(awaitAlias)
.atMost(10, TimeUnit.SECONDS)
.until(() -> CoAP.ResponseCode.CONTENT.equals(callbackCoap.getResponseCode()) &&
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);
latch.await(3, TimeUnit.SECONDS);
validateUpdateAttributesJsonResponse(callbackCoap, SHARED_ATTRIBUTES_DELETED_RESPONSE, expectedObserveBeforeDeleteCnt);
awaitAlias = "await Json Test Subscribe To AttributesUpdates (deleted attributes)";
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();
assertTrue(observeRelation.isCanceled());
@ -269,8 +281,13 @@ public abstract class AbstractCoapAttributesIntegrationTest extends AbstractCoap
client = new CoapTestClient(accessToken, FeatureType.ATTRIBUTES);
CoapTestCallback callbackCoap = new CoapTestCallback(1);
String awaitAlias = "await Proto Test Subscribe To Attributes Updates (add attributes)";
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) {
validateEmptyCurrentStateAttributesProtoResponse(callbackCoap);
@ -278,17 +295,25 @@ public abstract class AbstractCoapAttributesIntegrationTest extends AbstractCoap
validateCurrentStateAttributesProtoResponse(callbackCoap);
}
CountDownLatch latch = new CountDownLatch(1);
int expectedObserveCnt = callbackCoap.getObserve().intValue() + 1;
int expectedObserveForAttributesUpdate = callbackCoap.getObserve().intValue() + 1;
doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", SHARED_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
latch.await(3, TimeUnit.SECONDS);
validateUpdateProtoAttributesResponse(callbackCoap, expectedObserveCnt);
latch = new CountDownLatch(1);
int expectedObserveBeforeDeleteCnt = callbackCoap.getObserve().intValue() + 1;
awaitAlias = "await Proto Test Subscribe To Attributes Updates (add attributes)";
await(awaitAlias)
.atMost(10, TimeUnit.SECONDS)
.until(() -> CoAP.ResponseCode.CONTENT.equals(callbackCoap.getResponseCode()) &&
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);
latch.await(3, TimeUnit.SECONDS);
validateDeleteProtoAttributesResponse(callbackCoap, expectedObserveBeforeDeleteCnt);
awaitAlias = "await Proto Test Subscribe To Attributes Updates (deleted attributes)";
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();
assertTrue(observeRelation.isCanceled());
@ -314,27 +339,18 @@ public abstract class AbstractCoapAttributesIntegrationTest extends AbstractCoap
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.getObserve());
assertEquals(CoAP.ResponseCode.CONTENT, callback.getResponseCode());
assertEquals(expectedObserveCnt, callback.getObserve().intValue());
String response = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8);
assertEquals(JacksonUtil.toJsonNode(expectedResponse), JacksonUtil.toJsonNode(response));
}
protected void validateEmptyCurrentStateAttributesProtoResponse(CoapTestCallback callback) throws InvalidProtocolBufferException {
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 {
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.TsKvProto tsKvProtoAttribute1 = getTsKvProto("sharedStr", "value", TransportProtos.KeyValueType.STRING_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 {
assertNotNull(callback.getPayloadBytes());
assertNotNull(callback.getObserve());
assertEquals(CoAP.ResponseCode.CONTENT, callback.getResponseCode());
assertEquals(expectedObserveCnt, callback.getObserve().intValue());
TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder();
List<TransportProtos.TsKvProto> tsKvProtoList = getTsKvProtoList("shared");
attributeUpdateNotificationMsgBuilder.addAllSharedUpdated(tsKvProtoList);
@ -378,9 +391,6 @@ public abstract class AbstractCoapAttributesIntegrationTest extends AbstractCoap
protected void validateDeleteProtoAttributesResponse(CoapTestCallback callback, int expectedObserveCnt) throws InvalidProtocolBufferException {
assertNotNull(callback.getPayloadBytes());
assertNotNull(callback.getObserve());
assertEquals(CoAP.ResponseCode.CONTENT, callback.getResponseCode());
assertEquals(expectedObserveCnt, callback.getObserve().intValue());
TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder();
attributeUpdateNotificationMsgBuilder.addSharedDeleted("sharedJson");
@ -395,9 +405,10 @@ public abstract class AbstractCoapAttributesIntegrationTest extends AbstractCoap
Awaitility.await("awaitClientAfterCancelObserve")
.pollInterval(10, TimeUnit.MILLISECONDS)
.atMost(5, TimeUnit.SECONDS)
.until(()->{
.until(() -> {
log.trace("awaiting defaultTransportService.sessions is empty");
return defaultTransportService.sessions.isEmpty();});
return defaultTransportService.sessions.isEmpty();
});
}
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.TimeUnit;
import static org.awaitility.Awaitility.await;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@ -75,14 +76,23 @@ public abstract class AbstractCoapServerSideRpcIntegrationTest extends AbstractC
CoapTestCallback callbackCoap = new TestCoapCallbackForRPC(client, 1, true, protobuf);
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);
CountDownLatch latch = new CountDownLatch(1);
int expectedObserveCountAfterGpioRequest = callbackCoap.getObserve().intValue() + 1;
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}";
String deviceId = savedDevice.getId().getId().toString();
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);
observeRelation.proactiveCancel();
@ -94,23 +104,36 @@ public abstract class AbstractCoapServerSideRpcIntegrationTest extends AbstractC
CoapTestCallback callbackCoap = new TestCoapCallbackForRPC(client, 1, false, protobuf);
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);
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"26\",\"value\": 1}}";
String deviceId = savedDevice.getId().getId().toString();
int expectedObserveCountAfterGpioRequest1 = callbackCoap.getObserve().intValue() + 1;
String actualResult = doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
callbackCoap.getLatch().await(3, TimeUnit.SECONDS);
validateTwoWayStateChangedNotification(callbackCoap, 1, expectedResponseResult, actualResult);
CountDownLatch latch = new CountDownLatch(1);
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 &&
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());
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();
assertTrue(observeRelation.isCanceled());
@ -184,25 +207,16 @@ public abstract class AbstractCoapServerSideRpcIntegrationTest extends AbstractC
private void validateCurrentStateNotification(CoapTestCallback callback) {
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) {
assertTrue(StringUtils.isEmpty(result));
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);
assertNotNull(callback.getPayloadBytes());
assertNotNull(callback.getObserve());
assertEquals(CoAP.ResponseCode.CONTENT, callback.getResponseCode());
assertEquals(expectedObserveNumber, callback.getObserve().intValue());
}
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 {
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();
lwM2MTestClient.start(isStartLw);
await(awaitAlias)
.atMost(1000, TimeUnit.MILLISECONDS)
.atMost(20, TimeUnit.SECONDS)
.until(() -> finishState.equals(lwM2MTestClient.getClientState()));
Assert.assertEquals(expectedStatuses, lwM2MTestClient.getClientStates());
}
@ -234,7 +234,7 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M
String deviceId = device.getId().getId().toString();
lwM2MTestClient.start(true);
await(awaitAlias)
.atMost(1000, TimeUnit.MILLISECONDS)
.atMost(20, TimeUnit.SECONDS)
.until(() -> ON_REGISTRATION_SUCCESS.equals(lwM2MTestClient.getClientState()));
Assert.assertEquals(expectedStatusesLwm2m, lwM2MTestClient.getClientStates());
@ -246,7 +246,7 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M
expectedStatusesBs.add(ON_DEREGISTRATION_STARTED);
expectedStatusesBs.add(ON_DEREGISTRATION_SUCCESS);
await(awaitAlias)
.atMost(1000, TimeUnit.MILLISECONDS)
.atMost(20, TimeUnit.SECONDS)
.until(() -> ON_REGISTRATION_SUCCESS.equals(lwM2MTestClient.getClientState()));
Assert.assertEquals(expectedStatusesBs, lwM2MTestClient.getClientStates());
}

2
common/actor/pom.xml

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

2
common/cache/pom.xml

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

2
common/cluster-api/pom.xml

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

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

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

2
common/coap-server/pom.xml

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

2
common/dao-api/pom.xml

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

2
common/data/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.4.0-SNAPSHOT</version>
<version>3.4.1-SNAPSHOT</version>
<artifactId>common</artifactId>
</parent>
<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();
}
@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
public long 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() {
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();
}
@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
public long getCreatedTime() {
return super.getCreatedTime();
@ -159,7 +159,7 @@ public class Customer extends ContactBased<CustomerId> implements HasTenantId, E
@Override
@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() {
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();
}
@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
public long 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() {
return tenantId;
}
@ -95,7 +95,7 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa
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() {
return image;
}
@ -104,7 +104,7 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa
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() {
return assignedCustomers;
}
@ -113,7 +113,7 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa
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() {
return mobileHide;
}
@ -122,7 +122,7 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa
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() {
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
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
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();
}
@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
public long 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() {
return tenantId;
}
@ -127,7 +127,7 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
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() {
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
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;
@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;
@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;
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;
@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;
@NoXss
@Length(fieldName = "name")
@ -74,12 +74,11 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
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.")
private DashboardId defaultDashboardId;
@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. " +
"Otherwise, the 'Main' queue will be used to store those messages.")
private QueueId defaultQueueId;
private String defaultQueueName;
@Valid
private transient DeviceProfileData profileData;
@ -113,7 +112,7 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
this.isDefault = deviceProfile.isDefault();
this.defaultRuleChainId = deviceProfile.getDefaultRuleChainId();
this.defaultDashboardId = deviceProfile.getDefaultDashboardId();
this.defaultQueueId = deviceProfile.getDefaultQueueId();
this.defaultQueueName = deviceProfile.getDefaultQueueName();
this.setProfileData(deviceProfile.getProfileData());
this.provisionDeviceKey = deviceProfile.getProvisionDeviceKey();
this.firmwareId = deviceProfile.getFirmwareId();
@ -130,7 +129,7 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
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
public long 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 ...*/;
}
@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
public CustomerId getCustomerId() {
return customerId;
@ -98,7 +98,7 @@ public class EntityView extends SearchTextBasedWithAdditionalInfo<EntityViewId>
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
public TenantId getTenantId() {
return tenantId;
@ -113,7 +113,7 @@ public class EntityView extends SearchTextBasedWithAdditionalInfo<EntityViewId>
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
public long 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
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;
@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;
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
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;
@ApiModelProperty(position = 2, value = "Event type", example = "STATS")
private String type;
@ApiModelProperty(position = 3, value = "string", example = "784f394c-42b6-435a-983c-b7beff2784f9")
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;
@ApiModelProperty(position = 5, value = "Event body.", dataType = "com.fasterxml.jackson.databind.JsonNode")
private transient JsonNode body;
@ -53,7 +53,7 @@ public class Event extends BaseData<EventId> {
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
public long 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;
import io.swagger.annotations.ApiModelProperty;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.HasId;
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);
@ApiModelProperty(position = 100, value = "JSON object with External Id from the VCS", accessMode = ApiModelProperty.AccessMode.READ_ONLY, hidden = true)
I getExternalId();
void setExternalId(I externalId);
long getCreatedTime();
void setCreatedTime(long createdTime);
void setTenantId(TenantId tenantId);

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

Loading…
Cancel
Save