Browse Source

Merge remote-tracking branch 'upstream/develop/3.4' into queue-black-box-tests

pull/6933/head
Volodymyr Babak 4 years ago
parent
commit
8fc5fa7526
  1. 3
      application/src/main/data/upgrade/3.3.4/schema_update.sql
  2. 49
      application/src/main/data/upgrade/3.3.4/schema_update_device_profile.sql
  3. 7
      application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
  4. 56
      application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
  5. 2
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java
  6. 8
      application/src/main/java/org/thingsboard/server/controller/QueueController.java
  7. 1
      application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java
  8. 5
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceProfileMsgConstructor.java
  9. 7
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java
  10. 30
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/AbstractRuleChainMetadataConstructor.java
  11. 9
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorFactory.java
  12. 6
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV330.java
  13. 50
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV333.java
  14. 5
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV340.java
  15. 28
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/TelemetryEdgeProcessor.java
  16. 53
      application/src/main/java/org/thingsboard/server/service/entitiy/queue/DefaultTbQueueService.java
  17. 31
      application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
  18. 4
      application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java
  19. 44
      application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java
  20. 66
      application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java
  21. 33
      application/src/main/java/org/thingsboard/server/service/mail/PasswordResetExecutorService.java
  22. 10
      application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java
  23. 10
      application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java
  24. 2
      application/src/main/java/org/thingsboard/server/service/queue/DefaultTenantRoutingInfoService.java
  25. 6
      application/src/main/java/org/thingsboard/server/service/queue/processing/TbRuleEngineProcessingResult.java
  26. 6
      application/src/main/java/org/thingsboard/server/service/queue/processing/TbRuleEngineProcessingStrategyFactory.java
  27. 1
      application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/DeviceProfileImportService.java
  28. 10
      application/src/main/resources/thingsboard.yml
  29. 58
      application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java
  30. 7
      application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java
  31. 44
      application/src/test/java/org/thingsboard/server/controller/BaseAdminControllerTest.java
  32. 26
      application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java
  33. 57
      application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java
  34. 18
      application/src/test/java/org/thingsboard/server/controller/BaseTenantProfileControllerTest.java
  35. 150
      application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java
  36. 92
      application/src/test/java/org/thingsboard/server/controller/BaseWidgetsBundleControllerTest.java
  37. 7
      application/src/test/java/org/thingsboard/server/edge/BaseEdgeTest.java
  38. 22
      application/src/test/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructorTest.java
  39. 95
      application/src/test/java/org/thingsboard/server/transport/coap/attributes/AbstractCoapAttributesIntegrationTest.java
  40. 62
      application/src/test/java/org/thingsboard/server/transport/coap/rpc/AbstractCoapServerSideRpcIntegrationTest.java
  41. 1
      application/src/test/java/org/thingsboard/server/transport/coap/rpc/CoapServerSideRpcJsonIntegrationTest.java
  42. 6
      application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java
  43. 1
      common/cluster-api/src/main/proto/queue.proto
  44. 16
      common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java
  45. 8
      common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java
  46. 2
      common/edge-api/src/main/proto/edge.proto
  47. 48
      common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java
  48. 38
      common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java
  49. 5
      common/queue/src/main/java/org/thingsboard/server/queue/discovery/PartitionService.java
  50. 1
      common/queue/src/main/java/org/thingsboard/server/queue/discovery/TenantRoutingInfo.java
  51. 10
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java
  52. 2
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/TransportTenantRoutingInfoService.java
  53. 4
      common/util/src/main/java/org/thingsboard/common/util/AbstractListeningExecutor.java
  54. 11
      common/util/src/main/java/org/thingsboard/common/util/ListeningExecutor.java
  55. 3
      common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultClusterVersionControlService.java
  56. 6
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java
  57. 1
      dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
  58. 13
      dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java
  59. 5
      dao/src/main/java/org/thingsboard/server/dao/model/sql/TenantProfileEntity.java
  60. 15
      dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java
  61. 9
      dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java
  62. 2
      dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantProfileDataValidator.java
  63. 1
      dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java
  64. 1
      dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java
  65. 4
      dao/src/main/resources/sql/schema-entities-idx.sql
  66. 3
      dao/src/main/resources/sql/schema-entities.sql
  67. 1
      dao/src/test/java/org/thingsboard/server/dao/service/BaseQueueServiceTest.java
  68. 10
      dao/src/test/java/org/thingsboard/server/dao/service/BaseTenantProfileServiceTest.java
  69. 1
      dao/src/test/java/org/thingsboard/server/dao/service/BaseTenantServiceTest.java
  70. 2
      dao/src/test/resources/application-test.properties
  71. 2
      msa/vc-executor/src/main/java/org/thingsboard/server/vc/service/VersionControlTenantRoutingInfoService.java
  72. 3
      rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/MailService.java
  73. 13
      rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java
  74. 2
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java
  75. 2
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java
  76. 6
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbCheckpointNode.java
  77. 2
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbCheckpointNodeConfiguration.java
  78. 2
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNode.java
  79. 6
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmState.java
  80. 4
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNode.java
  81. 2
      rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js
  82. 2
      transport/mqtt/src/main/java/org/thingsboard/server/mqtt/ThingsboardMqttTransportApplication.java
  83. 4
      ui-ngx/src/app/core/http/queue.service.ts
  84. 5
      ui-ngx/src/app/core/services/dashboard-utils.service.ts
  85. 34
      ui-ngx/src/app/core/services/item-buffer.service.ts
  86. 1
      ui-ngx/src/app/modules/home/components/import-export/import-export.service.ts
  87. 2
      ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html
  88. 7
      ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts
  89. 2
      ui-ngx/src/app/modules/home/components/profile/device-profile.component.html
  90. 8
      ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts
  91. 4
      ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.html
  92. 3
      ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.ts
  93. 18
      ui-ngx/src/app/modules/home/components/widget/legend-config.component.html
  94. 4
      ui-ngx/src/app/modules/home/components/widget/lib/maps/common-maps-utils.ts
  95. 3
      ui-ngx/src/app/modules/home/components/widget/widget-container.component.html
  96. 4
      ui-ngx/src/app/modules/home/components/widget/widget-container.component.scss
  97. 2
      ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html
  98. 11
      ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.ts
  99. 2
      ui-ngx/src/app/modules/home/models/dashboard-component.models.ts
  100. 5
      ui-ngx/src/app/shared/components/markdown.component.ts

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

@ -70,3 +70,6 @@ CREATE TABLE IF NOT EXISTS user_auth_settings (
two_fa_settings varchar two_fa_settings varchar
); );
CREATE INDEX IF NOT EXISTS idx_api_usage_state_entity_id ON api_usage_state(entity_id);
ALTER TABLE tenant_profile DROP COLUMN IF EXISTS isolated_tb_core;

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

@ -1,49 +0,0 @@
--
-- Copyright © 2016-2022 The Thingsboard Authors
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
ALTER TABLE device_profile
ADD COLUMN IF NOT EXISTS default_queue_id uuid;
DO
$$
BEGIN
IF EXISTS
(SELECT column_name
FROM information_schema.columns
WHERE table_name = 'device_profile'
AND column_name = 'default_queue_name'
)
THEN
UPDATE device_profile
SET default_queue_id = q.id
FROM queue as q
WHERE default_queue_name = q.name;
END IF;
END
$$;
DO
$$
BEGIN
IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'fk_default_queue_device_profile') THEN
ALTER TABLE device_profile
ADD CONSTRAINT fk_default_queue_device_profile FOREIGN KEY (default_queue_id) REFERENCES queue (id);
END IF;
END;
$$;
ALTER TABLE device_profile
DROP COLUMN IF EXISTS default_queue_name;

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

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

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

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

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

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

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

@ -75,6 +75,14 @@ public class QueueController extends BaseController {
return checkNotNull(queueService.findQueueById(getTenantId(), queueId)); return checkNotNull(queueService.findQueueById(getTenantId(), queueId));
} }
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/queues/name/{queueName}", method = RequestMethod.GET)
@ResponseBody
public Queue getQueueByName(@PathVariable("queueName") String queueName) throws ThingsboardException {
checkParameter("queueName", queueName);
return checkNotNull(queueService.findQueueByTenantIdAndName(getTenantId(), queueName));
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
@RequestMapping(value = "/queues", params = {"serviceType"}, method = RequestMethod.POST) @RequestMapping(value = "/queues", params = {"serviceType"}, method = RequestMethod.POST)
@ResponseBody @ResponseBody

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

@ -128,7 +128,6 @@ public class TenantProfileController extends BaseController {
"{\n" + "{\n" +
" \"name\": \"Default\",\n" + " \"name\": \"Default\",\n" +
" \"description\": \"Default tenant profile\",\n" + " \"description\": \"Default tenant profile\",\n" +
" \"isolatedTbCore\": false,\n" +
" \"isolatedTbRuleEngine\": false,\n" + " \"isolatedTbRuleEngine\": false,\n" +
" \"profileData\": {\n" + " \"profileData\": {\n" +
" \"configuration\": {\n" + " \"configuration\": {\n" +

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

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

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

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

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

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

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

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

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

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

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

@ -1,50 +0,0 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.constructor.rule;
import com.fasterxml.jackson.core.JsonProcessingException;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.server.dao.queue.QueueService;
import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg;
import java.util.List;
import java.util.TreeSet;
@Slf4j
public class RuleChainMetadataConstructorV333 extends AbstractRuleChainMetadataConstructor {
public RuleChainMetadataConstructorV333(QueueService queueService) {
super(queueService);
}
@Override
protected void constructRuleChainMetadataUpdatedMsg(TenantId tenantId,
RuleChainMetadataUpdateMsg.Builder builder,
RuleChainMetaData ruleChainMetaData) throws JsonProcessingException {
List<RuleNode> nodes = updateQueueIdToQueueNameNodeConfiguration(tenantId, ruleChainMetaData.getNodes());
builder.addAllNodes(constructNodes(nodes))
.addAllConnections(constructConnections(ruleChainMetaData.getConnections()))
.addAllRuleChainConnections(constructRuleChainConnections(ruleChainMetaData.getRuleChainConnections(), new TreeSet<>()));
if (ruleChainMetaData.getFirstNodeIndex() != null) {
builder.setFirstNodeIndex(ruleChainMetaData.getFirstNodeIndex());
} else {
builder.setFirstNodeIndex(-1);
}
}
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

33
application/src/main/java/org/thingsboard/server/service/mail/PasswordResetExecutorService.java

@ -0,0 +1,33 @@
/**
* 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.mail;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.thingsboard.common.util.AbstractListeningExecutor;
@Component
public class PasswordResetExecutorService extends AbstractListeningExecutor {
@Value("${actors.rule.mail_password_reset_thread_pool_size:10}")
private int mailExecutorThreadPoolSize;
@Override
protected int getThreadPollSize() {
return mailExecutorThreadPoolSize;
}
}

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -148,7 +148,7 @@ ui:
# Help parameters # Help parameters
help: help:
# Base url for UI help assets # Base url for UI help assets
base-url: "${UI_HELP_BASE_URL:https://raw.githubusercontent.com/thingsboard/thingsboard-ui-help/release-3.3.4}" base-url: "${UI_HELP_BASE_URL:https://raw.githubusercontent.com/thingsboard/thingsboard-ui-help/release-3.4}"
database: database:
ts_max_intervals: "${DATABASE_TS_MAX_INTERVALS:700}" # Max number of DB queries generated by single API call to fetch telemetry records ts_max_intervals: "${DATABASE_TS_MAX_INTERVALS:700}" # Max number of DB queries generated by single API call to fetch telemetry records
@ -326,7 +326,9 @@ actors:
# Specify thread pool size for javascript executor service # Specify thread pool size for javascript executor service
js_thread_pool_size: "${ACTORS_RULE_JS_THREAD_POOL_SIZE:50}" js_thread_pool_size: "${ACTORS_RULE_JS_THREAD_POOL_SIZE:50}"
# Specify thread pool size for mail sender executor service # Specify thread pool size for mail sender executor service
mail_thread_pool_size: "${ACTORS_RULE_MAIL_THREAD_POOL_SIZE:50}" mail_thread_pool_size: "${ACTORS_RULE_MAIL_THREAD_POOL_SIZE:40}"
# Specify thread pool size for password reset emails
mail_password_reset_thread_pool_size: "${ACTORS_RULE_MAIL_PASSWORD_RESET_THREAD_POOL_SIZE:10}"
# Specify thread pool size for sms sender executor service # Specify thread pool size for sms sender executor service
sms_thread_pool_size: "${ACTORS_RULE_SMS_THREAD_POOL_SIZE:50}" sms_thread_pool_size: "${ACTORS_RULE_SMS_THREAD_POOL_SIZE:50}"
# Whether to allow usage of system mail service for rules # Whether to allow usage of system mail service for rules
@ -545,10 +547,6 @@ spring:
audit-log: audit-log:
# Enable/disable audit log functionality. # Enable/disable audit log functionality.
enabled: "${AUDIT_LOG_ENABLED:true}" enabled: "${AUDIT_LOG_ENABLED:true}"
# Specify partitioning size for audit log by tenant id storage. Example MINUTES, HOURS, DAYS, MONTHS
by_tenant_partitioning: "${AUDIT_LOG_BY_TENANT_PARTITIONING:MONTHS}"
# Number of days as history period if startTime and endTime are not specified
default_query_period: "${AUDIT_LOG_DEFAULT_QUERY_PERIOD:30}"
# Logging levels per each entity type. # Logging levels per each entity type.
# Allowed values: OFF (disable), W (log write operations), RW (log read and write operations) # Allowed values: OFF (disable), W (log write operations), RW (log read and write operations)
logging-level: logging-level:

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

@ -20,9 +20,10 @@ import org.mockito.ArgumentMatcher;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.boot.test.mock.mockito.SpyBean;
import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.edge.EdgeEventType; import org.thingsboard.server.common.data.edge.EdgeEventType;
@ -37,7 +38,6 @@ import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg; import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg;
import org.thingsboard.server.dao.audit.AuditLogService; import org.thingsboard.server.dao.audit.AuditLogService;
import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.service.gateway_device.GatewayNotificationsService;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -58,9 +58,6 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
@SpyBean @SpyBean
protected AuditLogService auditLogService; protected AuditLogService auditLogService;
@SpyBean
protected GatewayNotificationsService gatewayNotificationsService;
protected final String msgErrorPermission = "You don't have permission to perform this operation!"; protected final String msgErrorPermission = "You don't have permission to perform this operation!";
protected final String msgErrorShouldBeSpecified = "should be specified"; protected final String msgErrorShouldBeSpecified = "should be specified";
protected final String msgErrorNotFound = "Requested item wasn't found!"; protected final String msgErrorNotFound = "Requested item wasn't found!";
@ -73,7 +70,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
testNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime); testNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime);
testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo); testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo);
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId); ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTime);
Mockito.reset(tbClusterService, auditLogService); Mockito.reset(tbClusterService, auditLogService);
} }
@ -114,7 +111,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId); argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId);
testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId,
userName, actionType, cntTime * 2, 1); userName, actionType, cntTime * 2, 1);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, new Tenant(), cntTime);
Mockito.reset(tbClusterService, auditLogService); Mockito.reset(tbClusterService, auditLogService);
} }
@ -125,7 +122,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
testNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime); testNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime);
testLogEntityActionEntityEqClass(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo); testLogEntityActionEntityEqClass(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo);
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId); ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTime);
Mockito.reset(tbClusterService, auditLogService); Mockito.reset(tbClusterService, auditLogService);
} }
@ -147,7 +144,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
if (ActionType.RELATIONS_DELETED.equals(actionType)) { if (ActionType.RELATIONS_DELETED.equals(actionType)) {
testPushMsgToRuleEngineNever(originatorId); testPushMsgToRuleEngineNever(originatorId);
} else { } else {
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTime);
} }
Mockito.reset(tbClusterService, auditLogService); Mockito.reset(tbClusterService, auditLogService);
} }
@ -166,7 +163,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId); argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId);
testLogEntityActionAdditionalInfo(matcherEntityClassEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName, actionType, cntTime, testLogEntityActionAdditionalInfo(matcherEntityClassEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName, actionType, cntTime,
extractMatcherAdditionalInfo(additionalInfo)); extractMatcherAdditionalInfo(additionalInfo));
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTime);
Mockito.reset(tbClusterService, auditLogService); Mockito.reset(tbClusterService, auditLogService);
} }
@ -184,7 +181,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId); argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId);
testLogEntityActionAdditionalInfo(matcherEntityClassEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName, actionType, cntTime, testLogEntityActionAdditionalInfo(matcherEntityClassEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName, actionType, cntTime,
extractMatcherAdditionalInfoClass(additionalInfo)); extractMatcherAdditionalInfoClass(additionalInfo));
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTimeRuleEngine); testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTimeRuleEngine);
} }
protected void testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAnyAdditionalInfoAny(HasName entity, HasName originator, protected void testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAnyAdditionalInfoAny(HasName entity, HasName originator,
@ -200,7 +197,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId); argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId);
testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName, actionType, cntTime, testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName, actionType, cntTime,
cntAdditionalInfo); cntAdditionalInfo);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTimeEdge); testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTimeEdge);
Mockito.reset(tbClusterService, auditLogService); Mockito.reset(tbClusterService, auditLogService);
} }
@ -218,7 +215,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId); argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId);
testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName, actionType, cntTime, testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName, actionType, cntTime,
cntAdditionalInfo); cntAdditionalInfo);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTime);
Mockito.reset(tbClusterService, auditLogService); Mockito.reset(tbClusterService, auditLogService);
} }
@ -229,7 +226,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
testNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime); testNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime);
testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo); testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo);
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId); ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTime);
testBroadcastEntityStateChangeEventTime(entityId, tenantId, cntTime); testBroadcastEntityStateChangeEventTime(entityId, tenantId, cntTime);
Mockito.reset(tbClusterService, auditLogService); Mockito.reset(tbClusterService, auditLogService);
} }
@ -241,7 +238,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
testNotificationMsgToEdgeServiceNeverWithActionType(entityId, actionType); testNotificationMsgToEdgeServiceNeverWithActionType(entityId, actionType);
testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo); testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo);
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId); ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTime);
testBroadcastEntityStateChangeEventTime(entityId, tenantId, cntTime); testBroadcastEntityStateChangeEventTime(entityId, tenantId, cntTime);
Mockito.reset(tbClusterService, auditLogService); Mockito.reset(tbClusterService, auditLogService);
} }
@ -263,7 +260,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId); argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId);
testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName, actionType, cntTime, testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName, actionType, cntTime,
cntAdditionalInfo); cntAdditionalInfo);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTimeRuleEngine); testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTimeRuleEngine);
testBroadcastEntityStateChangeEventTime(entityId, tenantId, cntTime); testBroadcastEntityStateChangeEventTime(entityId, tenantId, cntTime);
} }
@ -313,22 +310,6 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
Mockito.reset(tbClusterService, auditLogService); 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) { private void testNotificationMsgToEdgeServiceNeverWithActionType(EntityId entityId, ActionType actionType) {
EdgeEventActionType edgeEventActionType = ActionType.CREDENTIALS_UPDATED.equals(actionType) ? EdgeEventActionType edgeEventActionType = ActionType.CREDENTIALS_UPDATED.equals(actionType) ?
EdgeEventActionType.CREDENTIALS_UPDATED : edgeTypeByActionType(actionType); EdgeEventActionType.CREDENTIALS_UPDATED : edgeTypeByActionType(actionType);
@ -359,7 +340,8 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
Mockito.any(entityId.getClass()), Mockito.any(ComponentLifecycleEvent.class)); Mockito.any(entityId.getClass()), Mockito.any(ComponentLifecycleEvent.class));
} }
private void testPushMsgToRuleEngineTime(ArgumentMatcher<EntityId> matcherOriginatorId, TenantId tenantId, int cntTime) { private void testPushMsgToRuleEngineTime(ArgumentMatcher<EntityId> matcherOriginatorId, TenantId tenantId, HasName entity, int cntTime) {
tenantId = tenantId.isNullUid() && ((HasTenantId) entity).getTenantId() != null ? ((HasTenantId) entity).getTenantId() : tenantId;
Mockito.verify(tbClusterService, times(cntTime)).pushMsgToRuleEngine(Mockito.eq(tenantId), Mockito.verify(tbClusterService, times(cntTime)).pushMsgToRuleEngine(Mockito.eq(tenantId),
Mockito.argThat(matcherOriginatorId), Mockito.any(TbMsg.class), Mockito.isNull()); Mockito.argThat(matcherOriginatorId), Mockito.any(TbMsg.class), Mockito.isNull());
} }
@ -535,12 +517,14 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
CustomerId customerId, UserId userId, String userName, ActionType actionType, CustomerId customerId, UserId userId, String userName, ActionType actionType,
int cntTime, ArgumentMatcher<Exception> matcherError, int cntTime, ArgumentMatcher<Exception> matcherError,
List<ArgumentMatcher<Object>> matcherAdditionalInfos) { List<ArgumentMatcher<Object>> matcherAdditionalInfos) {
ArgumentMatcher<UserId> matcherUserId = userId == null ? argument -> argument.getClass().equals(UserId.class) :
argument -> argument.equals(userId);
switch (matcherAdditionalInfos.size()) { switch (matcherAdditionalInfos.size()) {
case 1: case 1:
Mockito.verify(auditLogService, times(cntTime)) Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId), .logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId), Mockito.eq(customerId),
Mockito.eq(userId), Mockito.argThat(matcherUserId),
Mockito.eq(userName), Mockito.eq(userName),
Mockito.eq(originatorId), Mockito.eq(originatorId),
Mockito.argThat(matcherEntity), Mockito.argThat(matcherEntity),
@ -552,7 +536,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
Mockito.verify(auditLogService, times(cntTime)) Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId), .logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId), Mockito.eq(customerId),
Mockito.eq(userId), Mockito.argThat(matcherUserId),
Mockito.eq(userName), Mockito.eq(userName),
Mockito.eq(originatorId), Mockito.eq(originatorId),
Mockito.argThat(matcherEntity), Mockito.argThat(matcherEntity),
@ -564,7 +548,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
Mockito.verify(auditLogService, times(cntTime)) Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId), .logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId), Mockito.eq(customerId),
Mockito.eq(userId), Mockito.argThat(matcherUserId),
Mockito.eq(userName), Mockito.eq(userName),
Mockito.eq(originatorId), Mockito.eq(originatorId),
Mockito.argThat(matcherEntity), Mockito.argThat(matcherEntity),
@ -578,7 +562,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
Mockito.verify(auditLogService, times(cntTime)) Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId), .logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId), Mockito.eq(customerId),
Mockito.eq(userId), Mockito.argThat(matcherUserId),
Mockito.eq(userName), Mockito.eq(userName),
Mockito.eq(originatorId), Mockito.eq(originatorId),
Mockito.argThat(matcherEntity), Mockito.argThat(matcherEntity),

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

@ -422,12 +422,6 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
} }
protected DeviceProfile createDeviceProfile(String name, DeviceProfileTransportConfiguration deviceProfileTransportConfiguration) { protected DeviceProfile createDeviceProfile(String name, DeviceProfileTransportConfiguration deviceProfileTransportConfiguration) {
return createDeviceProfile(name, deviceProfileTransportConfiguration, null);
}
protected DeviceProfile createDeviceProfile(String name,
DeviceProfileTransportConfiguration deviceProfileTransportConfiguration,
QueueId defaultQueueId) {
DeviceProfile deviceProfile = new DeviceProfile(); DeviceProfile deviceProfile = new DeviceProfile();
deviceProfile.setName(name); deviceProfile.setName(name);
deviceProfile.setType(DeviceProfileType.DEFAULT); deviceProfile.setType(DeviceProfileType.DEFAULT);
@ -445,7 +439,6 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
deviceProfile.setProfileData(deviceProfileData); deviceProfile.setProfileData(deviceProfileData);
deviceProfile.setDefault(false); deviceProfile.setDefault(false);
deviceProfile.setDefaultRuleChainId(null); deviceProfile.setDefaultRuleChainId(null);
deviceProfile.setDefaultQueueId(defaultQueueId);
return deviceProfile; return deviceProfile;
} }

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

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

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

@ -27,6 +27,7 @@ import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.common.util.ThingsBoardExecutors;
import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
@ -52,6 +53,7 @@ import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.exception.DeviceCredentialsValidationException; import org.thingsboard.server.dao.exception.DeviceCredentialsValidationException;
import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.service.gateway_device.GatewayNotificationsService;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -59,14 +61,15 @@ import java.util.concurrent.TimeUnit;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE;
import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE; import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE;
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
public abstract class BaseDeviceControllerTest extends AbstractControllerTest { public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
static final TypeReference<PageData<Device>> PAGE_DATA_DEVICE_TYPE_REF = new TypeReference<>() { static final TypeReference<PageData<Device>> PAGE_DATA_DEVICE_TYPE_REF = new TypeReference<>() {};
};
ListeningExecutorService executor; ListeningExecutorService executor;
@ -76,6 +79,9 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
private Tenant savedTenant; private Tenant savedTenant;
private User tenantAdmin; private User tenantAdmin;
@SpyBean
private GatewayNotificationsService gatewayNotificationsService;
@Before @Before
public void beforeTest() throws Exception { public void beforeTest() throws Exception {
executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(8, getClass())); executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(8, getClass()));
@ -1181,4 +1187,20 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
Assert.assertEquals(0, pageData.getData().size()); Assert.assertEquals(0, pageData.getData().size());
} }
protected void testNotificationUpdateGatewayOneTime(Device device, Device oldDevice) {
Mockito.verify(gatewayNotificationsService, times(1)).onDeviceUpdated(Mockito.eq(device), Mockito.eq(oldDevice));
}
protected void testNotificationUpdateGatewayNever() {
Mockito.verify(gatewayNotificationsService, never()).onDeviceUpdated(Mockito.any(Device.class), Mockito.any(Device.class));
}
protected void testNotificationDeleteGatewayOneTime(Device device) {
Mockito.verify(gatewayNotificationsService, times(1)).onDeviceDeleted(device);
}
protected void testNotificationDeleteGatewayNever() {
Mockito.verify(gatewayNotificationsService, never()).onDeviceDeleted(Mockito.any(Device.class));
}
} }

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

@ -39,7 +39,6 @@ import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.OtaPackageInfo; import org.thingsboard.server.common.data.OtaPackageInfo;
import org.thingsboard.server.common.data.SaveOtaPackageInfoRequest; import org.thingsboard.server.common.data.SaveOtaPackageInfoRequest;
import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration;
@ -49,15 +48,8 @@ import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadCo
import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.queue.ProcessingStrategy;
import org.thingsboard.server.common.data.queue.ProcessingStrategyType;
import org.thingsboard.server.common.data.queue.Queue;
import org.thingsboard.server.common.data.queue.SubmitStrategy;
import org.thingsboard.server.common.data.queue.SubmitStrategyType;
import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.tenant.profile.TenantProfileData;
import org.thingsboard.server.common.data.tenant.profile.TenantProfileQueueConfiguration;
import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.DataValidationException;
import java.util.ArrayList; import java.util.ArrayList;
@ -389,55 +381,6 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
.andExpect(statusReason(containsString("Can't assign dashboard from different tenant!"))); .andExpect(statusReason(containsString("Can't assign dashboard from different tenant!")));
} }
@Test
public void testSaveDeviceProfileWithQueueFromDifferentTenant() throws Exception {
loginDifferentTenant();
loginSysAdmin();
TenantProfile tenantProfile = new TenantProfile();
tenantProfile.setDefault(false);
tenantProfile.setName("Isolated TB Rule Engine");
tenantProfile.setDescription("Isolated TB Rule Engine tenant profile");
tenantProfile.setIsolatedTbCore(false);
tenantProfile.setIsolatedTbRuleEngine(true);
TenantProfileQueueConfiguration mainQueueConfiguration = new TenantProfileQueueConfiguration();
mainQueueConfiguration.setName("Main");
mainQueueConfiguration.setTopic("tb_rule_engine.main");
mainQueueConfiguration.setPollInterval(25);
mainQueueConfiguration.setPartitions(10);
mainQueueConfiguration.setConsumerPerPartition(true);
mainQueueConfiguration.setPackProcessingTimeout(2000);
SubmitStrategy mainQueueSubmitStrategy = new SubmitStrategy();
mainQueueSubmitStrategy.setType(SubmitStrategyType.BURST);
mainQueueSubmitStrategy.setBatchSize(1000);
mainQueueConfiguration.setSubmitStrategy(mainQueueSubmitStrategy);
ProcessingStrategy mainQueueProcessingStrategy = new ProcessingStrategy();
mainQueueProcessingStrategy.setType(ProcessingStrategyType.SKIP_ALL_FAILURES);
mainQueueProcessingStrategy.setRetries(3);
mainQueueProcessingStrategy.setFailurePercentage(0);
mainQueueProcessingStrategy.setPauseBetweenRetries(3);
mainQueueProcessingStrategy.setMaxPauseBetweenRetries(3);
mainQueueConfiguration.setProcessingStrategy(mainQueueProcessingStrategy);
TenantProfileData profileData = tenantProfile.getProfileData();
profileData.setQueueConfiguration(Collections.singletonList(mainQueueConfiguration));
tenantProfile.setProfileData(profileData);
TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class);
savedDifferentTenant.setTenantProfileId(savedTenantProfile.getId());
savedDifferentTenant = doPost("/api/tenant", savedDifferentTenant, Tenant.class);
loginDifferentTenant();
PageLink pageLink = new PageLink(1);
PageData<Queue> pageData = doGetTypedWithPageLink("/api/queues?serviceType=TB_RULE_ENGINE&",
new TypeReference<>() {}, pageLink);
Queue differentQueue = pageData.getData().get(0);
loginTenantAdmin();
DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
deviceProfile.setDefaultQueueId(differentQueue.getId());
doPost("/api/deviceProfile", deviceProfile).andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Can't assign queue from different tenant!")));
}
@Test @Test
public void testSaveDeviceProfileWithFirmwareFromDifferentTenant() throws Exception { public void testSaveDeviceProfileWithFirmwareFromDifferentTenant() throws Exception {
loginDifferentTenant(); loginDifferentTenant();

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

@ -66,7 +66,6 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController
Assert.assertEquals(tenantProfile.getDescription(), savedTenantProfile.getDescription()); Assert.assertEquals(tenantProfile.getDescription(), savedTenantProfile.getDescription());
Assert.assertEquals(tenantProfile.getProfileData(), savedTenantProfile.getProfileData()); Assert.assertEquals(tenantProfile.getProfileData(), savedTenantProfile.getProfileData());
Assert.assertEquals(tenantProfile.isDefault(), savedTenantProfile.isDefault()); Assert.assertEquals(tenantProfile.isDefault(), savedTenantProfile.isDefault());
Assert.assertEquals(tenantProfile.isIsolatedTbCore(), savedTenantProfile.isIsolatedTbCore());
Assert.assertEquals(tenantProfile.isIsolatedTbRuleEngine(), savedTenantProfile.isIsolatedTbRuleEngine()); Assert.assertEquals(tenantProfile.isIsolatedTbRuleEngine(), savedTenantProfile.isIsolatedTbRuleEngine());
testBroadcastEntityStateChangeEventTimeManyTimeTenantProfile(savedTenantProfile, ComponentLifecycleEvent.CREATED, 1); testBroadcastEntityStateChangeEventTimeManyTimeTenantProfile(savedTenantProfile, ComponentLifecycleEvent.CREATED, 1);
@ -182,22 +181,6 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController
testBroadcastEntityStateChangeEventNeverTenantProfile(); testBroadcastEntityStateChangeEventNeverTenantProfile();
} }
@Test
public void testSaveSameTenantProfileWithDifferentIsolatedTbCore() throws Exception {
loginSysAdmin();
TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile");
TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class);
Mockito.reset(tbClusterService);
savedTenantProfile.setIsolatedTbCore(true);
doPost("/api/tenantProfile", savedTenantProfile)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Can't update isolatedTbCore property")));
testBroadcastEntityStateChangeEventNeverTenantProfile();
}
@Test @Test
public void testDeleteTenantProfileWithExistingTenant() throws Exception { public void testDeleteTenantProfileWithExistingTenant() throws Exception {
loginSysAdmin(); loginSysAdmin();
@ -352,7 +335,6 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController
tenantProfileData.setConfiguration(new DefaultTenantProfileConfiguration()); tenantProfileData.setConfiguration(new DefaultTenantProfileConfiguration());
tenantProfile.setProfileData(tenantProfileData); tenantProfile.setProfileData(tenantProfileData);
tenantProfile.setDefault(false); tenantProfile.setDefault(false);
tenantProfile.setIsolatedTbCore(false);
tenantProfile.setIsolatedTbRuleEngine(false); tenantProfile.setIsolatedTbRuleEngine(false);
return tenantProfile; return tenantProfile;
} }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -273,7 +273,6 @@ message GetTenantRoutingInfoRequestMsg {
} }
message GetTenantRoutingInfoResponseMsg { message GetTenantRoutingInfoResponseMsg {
bool isolatedTbCore = 1;
bool isolatedTbRuleEngine = 2; bool isolatedTbRuleEngine = 2;
} }

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

@ -74,12 +74,11 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
private RuleChainId defaultRuleChainId; private RuleChainId defaultRuleChainId;
@ApiModelProperty(position = 6, value = "Reference to the dashboard. Used in the mobile application to open the default dashboard when user navigates to device details.") @ApiModelProperty(position = 6, value = "Reference to the dashboard. Used in the mobile application to open the default dashboard when user navigates to device details.")
private DashboardId defaultDashboardId; private DashboardId defaultDashboardId;
@NoXss @NoXss
@ApiModelProperty(position = 8, value = "Reference to the rule engine queue. " + @ApiModelProperty(position = 8, value = "Rule engine queue name. " +
"If present, the specified queue will be used to store all unprocessed messages related to device, including telemetry, attribute updates, etc. " + "If present, the specified queue will be used to store all unprocessed messages related to device, including telemetry, attribute updates, etc. " +
"Otherwise, the 'Main' queue will be used to store those messages.") "Otherwise, the 'Main' queue will be used to store those messages.")
private QueueId defaultQueueId;
private String defaultQueueName; private String defaultQueueName;
@Valid @Valid
private transient DeviceProfileData profileData; private transient DeviceProfileData profileData;
@ -113,7 +112,7 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
this.isDefault = deviceProfile.isDefault(); this.isDefault = deviceProfile.isDefault();
this.defaultRuleChainId = deviceProfile.getDefaultRuleChainId(); this.defaultRuleChainId = deviceProfile.getDefaultRuleChainId();
this.defaultDashboardId = deviceProfile.getDefaultDashboardId(); this.defaultDashboardId = deviceProfile.getDefaultDashboardId();
this.defaultQueueId = deviceProfile.getDefaultQueueId(); this.defaultQueueName = deviceProfile.getDefaultQueueName();
this.setProfileData(deviceProfile.getProfileData()); this.setProfileData(deviceProfile.getProfileData());
this.provisionDeviceKey = deviceProfile.getProvisionDeviceKey(); this.provisionDeviceKey = deviceProfile.getProvisionDeviceKey();
this.firmwareId = deviceProfile.getFirmwareId(); this.firmwareId = deviceProfile.getFirmwareId();
@ -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;
}
} }

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

@ -53,13 +53,10 @@ public class TenantProfile extends SearchTextBased<TenantProfileId> implements H
private String description; private String description;
@ApiModelProperty(position = 5, value = "Default Tenant profile to be used.", example = "true") @ApiModelProperty(position = 5, value = "Default Tenant profile to be used.", example = "true")
private boolean isDefault; private boolean isDefault;
@ApiModelProperty(position = 6, value = "If enabled, will push all messages related to this tenant and processed by core platform services into separate queue. " + @ApiModelProperty(position = 6, value = "If enabled, will push all messages related to this tenant and processed by the rule engine into separate queue. " +
"Useful for complex microservices deployments, to isolate processing of the data for specific tenants", example = "true")
private boolean isolatedTbCore;
@ApiModelProperty(position = 7, value = "If enabled, will push all messages related to this tenant and processed by the rule engine into separate queue. " +
"Useful for complex microservices deployments, to isolate processing of the data for specific tenants", example = "true") "Useful for complex microservices deployments, to isolate processing of the data for specific tenants", example = "true")
private boolean isolatedTbRuleEngine; private boolean isolatedTbRuleEngine;
@ApiModelProperty(position = 8, value = "Complex JSON object that contains profile settings: queue configs, max devices, max assets, rate limits, etc.") @ApiModelProperty(position = 7, value = "Complex JSON object that contains profile settings: queue configs, max devices, max assets, rate limits, etc.")
private transient TenantProfileData profileData; private transient TenantProfileData profileData;
@JsonIgnore @JsonIgnore
private byte[] profileDataBytes; private byte[] profileDataBytes;
@ -77,7 +74,6 @@ public class TenantProfile extends SearchTextBased<TenantProfileId> implements H
this.name = tenantProfile.getName(); this.name = tenantProfile.getName();
this.description = tenantProfile.getDescription(); this.description = tenantProfile.getDescription();
this.isDefault = tenantProfile.isDefault(); this.isDefault = tenantProfile.isDefault();
this.isolatedTbCore = tenantProfile.isIsolatedTbCore();
this.isolatedTbRuleEngine = tenantProfile.isIsolatedTbRuleEngine(); this.isolatedTbRuleEngine = tenantProfile.isIsolatedTbRuleEngine();
this.setProfileData(tenantProfile.getProfileData()); this.setProfileData(tenantProfile.getProfileData());
} }

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

@ -216,8 +216,6 @@ message DeviceProfileUpdateMsg {
bytes profileDataBytes = 13; bytes profileDataBytes = 13;
optional string provisionDeviceKey = 14; optional string provisionDeviceKey = 14;
optional bytes image = 15; optional bytes image = 15;
int64 defaultQueueIdMSB = 16;
int64 defaultQueueIdLSB = 17;
} }
message DeviceCredentialsUpdateMsg { message DeviceCredentialsUpdateMsg {

48
common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java

@ -43,7 +43,7 @@ import java.util.UUID;
@Slf4j @Slf4j
public final class TbMsg implements Serializable { public final class TbMsg implements Serializable {
private final QueueId queueId; private final String queueName;
private final UUID id; private final UUID id;
private final long ts; private final long ts;
private final String type; private final String type;
@ -67,12 +67,12 @@ public final class TbMsg implements Serializable {
return ctx.getAndIncrementRuleNodeCounter(); return ctx.getAndIncrementRuleNodeCounter();
} }
public static TbMsg newMsg(QueueId queueId, String type, EntityId originator, TbMsgMetaData metaData, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { public static TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) {
return newMsg(queueId, type, originator, null, metaData, data, ruleChainId, ruleNodeId); return newMsg(queueName, type, originator, null, metaData, data, ruleChainId, ruleNodeId);
} }
public static TbMsg newMsg(QueueId queueId, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { public static TbMsg newMsg(String queueName, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) {
return new TbMsg(queueId, UUID.randomUUID(), System.currentTimeMillis(), type, originator, customerId, return new TbMsg(queueName, UUID.randomUUID(), System.currentTimeMillis(), type, originator, customerId,
metaData.copy(), TbMsgDataType.JSON, data, ruleChainId, ruleNodeId, null, TbMsgCallback.EMPTY); metaData.copy(), TbMsgDataType.JSON, data, ruleChainId, ruleNodeId, null, TbMsgCallback.EMPTY);
} }
@ -87,12 +87,12 @@ public final class TbMsg implements Serializable {
// REALLY NEW MSG // REALLY NEW MSG
public static TbMsg newMsg(QueueId queueId, String type, EntityId originator, TbMsgMetaData metaData, String data) { public static TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data) {
return newMsg(queueId, type, originator, null, metaData, data); return newMsg(queueName, type, originator, null, metaData, data);
} }
public static TbMsg newMsg(QueueId queueId, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) { public static TbMsg newMsg(String queueName, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) {
return new TbMsg(queueId, UUID.randomUUID(), System.currentTimeMillis(), type, originator, customerId, return new TbMsg(queueName, UUID.randomUUID(), System.currentTimeMillis(), type, originator, customerId,
metaData.copy(), TbMsgDataType.JSON, data, null, null, null, TbMsgCallback.EMPTY); metaData.copy(), TbMsgDataType.JSON, data, null, null, null, TbMsgCallback.EMPTY);
} }
@ -118,40 +118,40 @@ public final class TbMsg implements Serializable {
} }
public static TbMsg transformMsg(TbMsg tbMsg, String type, EntityId originator, TbMsgMetaData metaData, String data) { public static TbMsg transformMsg(TbMsg tbMsg, String type, EntityId originator, TbMsgMetaData metaData, String data) {
return new TbMsg(tbMsg.queueId, tbMsg.id, tbMsg.ts, type, originator, tbMsg.customerId, metaData.copy(), tbMsg.dataType, return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, type, originator, tbMsg.customerId, metaData.copy(), tbMsg.dataType,
data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.ctx.copy(), tbMsg.callback); data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.ctx.copy(), tbMsg.callback);
} }
public static TbMsg transformMsg(TbMsg tbMsg, CustomerId customerId) { public static TbMsg transformMsg(TbMsg tbMsg, CustomerId customerId) {
return new TbMsg(tbMsg.queueId, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, customerId, tbMsg.metaData, tbMsg.dataType, return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, customerId, tbMsg.metaData, tbMsg.dataType,
tbMsg.data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.ctx.copy(), tbMsg.getCallback()); tbMsg.data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.ctx.copy(), tbMsg.getCallback());
} }
public static TbMsg transformMsg(TbMsg tbMsg, RuleChainId ruleChainId) { public static TbMsg transformMsg(TbMsg tbMsg, RuleChainId ruleChainId) {
return new TbMsg(tbMsg.queueId, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType, return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType,
tbMsg.data, ruleChainId, null, tbMsg.ctx.copy(), tbMsg.getCallback()); tbMsg.data, ruleChainId, null, tbMsg.ctx.copy(), tbMsg.getCallback());
} }
public static TbMsg transformMsg(TbMsg tbMsg, QueueId queueId) { public static TbMsg transformMsg(TbMsg tbMsg, String queueName) {
return new TbMsg(queueId, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType, return new TbMsg(queueName, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType,
tbMsg.data, tbMsg.getRuleChainId(), null, tbMsg.ctx.copy(), tbMsg.getCallback()); tbMsg.data, tbMsg.getRuleChainId(), null, tbMsg.ctx.copy(), tbMsg.getCallback());
} }
public static TbMsg transformMsg(TbMsg tbMsg, RuleChainId ruleChainId, QueueId queueId) { public static TbMsg transformMsg(TbMsg tbMsg, RuleChainId ruleChainId, String queueName) {
return new TbMsg(queueId, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType, return new TbMsg(queueName, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType,
tbMsg.data, ruleChainId, null, tbMsg.ctx.copy(), tbMsg.getCallback()); tbMsg.data, ruleChainId, null, tbMsg.ctx.copy(), tbMsg.getCallback());
} }
//used for enqueueForTellNext //used for enqueueForTellNext
public static TbMsg newMsg(TbMsg tbMsg, QueueId queueId, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { public static TbMsg newMsg(TbMsg tbMsg, String queueName, RuleChainId ruleChainId, RuleNodeId ruleNodeId) {
return new TbMsg(queueId, UUID.randomUUID(), tbMsg.getTs(), tbMsg.getType(), tbMsg.getOriginator(), tbMsg.customerId, tbMsg.getMetaData().copy(), return new TbMsg(queueName, UUID.randomUUID(), tbMsg.getTs(), tbMsg.getType(), tbMsg.getOriginator(), tbMsg.customerId, tbMsg.getMetaData().copy(),
tbMsg.getDataType(), tbMsg.getData(), ruleChainId, ruleNodeId, tbMsg.ctx.copy(), TbMsgCallback.EMPTY); tbMsg.getDataType(), tbMsg.getData(), ruleChainId, ruleNodeId, tbMsg.ctx.copy(), TbMsgCallback.EMPTY);
} }
private TbMsg(QueueId queueId, UUID id, long ts, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, TbMsgDataType dataType, String data, private TbMsg(String queueName, UUID id, long ts, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, TbMsgDataType dataType, String data,
RuleChainId ruleChainId, RuleNodeId ruleNodeId, TbMsgProcessingCtx ctx, TbMsgCallback callback) { RuleChainId ruleChainId, RuleNodeId ruleNodeId, TbMsgProcessingCtx ctx, TbMsgCallback callback) {
this.id = id; this.id = id;
this.queueId = queueId; this.queueName = queueName;
if (ts > 0) { if (ts > 0) {
this.ts = ts; this.ts = ts;
} else { } else {
@ -220,7 +220,7 @@ public final class TbMsg implements Serializable {
return builder.build().toByteArray(); return builder.build().toByteArray();
} }
public static TbMsg fromBytes(QueueId queueId, byte[] data, TbMsgCallback callback) { public static TbMsg fromBytes(String queueName, byte[] data, TbMsgCallback callback) {
try { try {
MsgProtos.TbMsgProto proto = MsgProtos.TbMsgProto.parseFrom(data); MsgProtos.TbMsgProto proto = MsgProtos.TbMsgProto.parseFrom(data);
TbMsgMetaData metaData = new TbMsgMetaData(proto.getMetaData().getDataMap()); TbMsgMetaData metaData = new TbMsgMetaData(proto.getMetaData().getDataMap());
@ -247,7 +247,7 @@ public final class TbMsg implements Serializable {
} }
TbMsgDataType dataType = TbMsgDataType.values()[proto.getDataType()]; TbMsgDataType dataType = TbMsgDataType.values()[proto.getDataType()];
return new TbMsg(queueId, UUID.fromString(proto.getId()), proto.getTs(), proto.getType(), entityId, customerId, return new TbMsg(queueName, UUID.fromString(proto.getId()), proto.getTs(), proto.getType(), entityId, customerId,
metaData, dataType, proto.getData(), ruleChainId, ruleNodeId, ctx, callback); metaData, dataType, proto.getData(), ruleChainId, ruleNodeId, ctx, callback);
} catch (InvalidProtocolBufferException e) { } catch (InvalidProtocolBufferException e) {
throw new IllegalStateException("Could not parse protobuf for TbMsg", e); throw new IllegalStateException("Could not parse protobuf for TbMsg", e);
@ -259,12 +259,12 @@ public final class TbMsg implements Serializable {
} }
public TbMsg copyWithRuleChainId(RuleChainId ruleChainId, UUID msgId) { public TbMsg copyWithRuleChainId(RuleChainId ruleChainId, UUID msgId) {
return new TbMsg(this.queueId, msgId, this.ts, this.type, this.originator, this.customerId, return new TbMsg(this.queueName, msgId, this.ts, this.type, this.originator, this.customerId,
this.metaData, this.dataType, this.data, ruleChainId, null, this.ctx, callback); this.metaData, this.dataType, this.data, ruleChainId, null, this.ctx, callback);
} }
public TbMsg copyWithRuleNodeId(RuleChainId ruleChainId, RuleNodeId ruleNodeId, UUID msgId) { public TbMsg copyWithRuleNodeId(RuleChainId ruleChainId, RuleNodeId ruleNodeId, UUID msgId) {
return new TbMsg(this.queueId, msgId, this.ts, this.type, this.originator, this.customerId, return new TbMsg(this.queueName, msgId, this.ts, this.type, this.originator, this.customerId,
this.metaData, this.dataType, this.data, ruleChainId, ruleNodeId, this.ctx, callback); this.metaData, this.dataType, this.data, ruleChainId, ruleNodeId, this.ctx, callback);
} }

38
common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java

@ -68,8 +68,6 @@ public class HashPartitionService implements PartitionService {
private final TenantRoutingInfoService tenantRoutingInfoService; private final TenantRoutingInfoService tenantRoutingInfoService;
private final QueueRoutingInfoService queueRoutingInfoService; private final QueueRoutingInfoService queueRoutingInfoService;
private final ConcurrentMap<QueueId, QueueRoutingInfo> queuesById = new ConcurrentHashMap<>();
private ConcurrentMap<QueueKey, List<Integer>> myPartitions = new ConcurrentHashMap<>(); private ConcurrentMap<QueueKey, List<Integer>> myPartitions = new ConcurrentHashMap<>();
private final ConcurrentMap<QueueKey, String> partitionTopicsMap = new ConcurrentHashMap<>(); private final ConcurrentMap<QueueKey, String> partitionTopicsMap = new ConcurrentHashMap<>();
@ -121,7 +119,6 @@ public class HashPartitionService implements PartitionService {
QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, queue); QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, queue);
partitionTopicsMap.put(queueKey, queue.getQueueTopic()); partitionTopicsMap.put(queueKey, queue.getQueueTopic());
partitionSizesMap.put(queueKey, queue.getPartitions()); partitionSizesMap.put(queueKey, queue.getPartitions());
queuesById.put(queue.getQueueId(), queue);
}); });
} }
@ -165,8 +162,6 @@ public class HashPartitionService implements PartitionService {
public void updateQueue(TransportProtos.QueueUpdateMsg queueUpdateMsg) { public void updateQueue(TransportProtos.QueueUpdateMsg queueUpdateMsg) {
TenantId tenantId = new TenantId(new UUID(queueUpdateMsg.getTenantIdMSB(), queueUpdateMsg.getTenantIdLSB())); TenantId tenantId = new TenantId(new UUID(queueUpdateMsg.getTenantIdMSB(), queueUpdateMsg.getTenantIdLSB()));
QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, queueUpdateMsg.getQueueName(), tenantId); QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, queueUpdateMsg.getQueueName(), tenantId);
QueueRoutingInfo queue = new QueueRoutingInfo(queueUpdateMsg);
queuesById.put(queue.getQueueId(), queue);
partitionTopicsMap.put(queueKey, queueUpdateMsg.getQueueTopic()); partitionTopicsMap.put(queueKey, queueUpdateMsg.getQueueTopic());
partitionSizesMap.put(queueKey, queueUpdateMsg.getPartitions()); partitionSizesMap.put(queueKey, queueUpdateMsg.getPartitions());
myPartitions.remove(queueKey); myPartitions.remove(queueKey);
@ -176,7 +171,6 @@ public class HashPartitionService implements PartitionService {
public void removeQueue(TransportProtos.QueueDeleteMsg queueDeleteMsg) { public void removeQueue(TransportProtos.QueueDeleteMsg queueDeleteMsg) {
TenantId tenantId = new TenantId(new UUID(queueDeleteMsg.getTenantIdMSB(), queueDeleteMsg.getTenantIdLSB())); TenantId tenantId = new TenantId(new UUID(queueDeleteMsg.getTenantIdMSB(), queueDeleteMsg.getTenantIdLSB()));
QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, queueDeleteMsg.getQueueName(), tenantId); QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, queueDeleteMsg.getQueueName(), tenantId);
queuesById.remove(new QueueId(new UUID(queueDeleteMsg.getQueueIdMSB(), queueDeleteMsg.getQueueIdLSB())));
myPartitions.remove(queueKey); myPartitions.remove(queueKey);
partitionTopicsMap.remove(queueKey); partitionTopicsMap.remove(queueKey);
partitionSizesMap.remove(queueKey); partitionSizesMap.remove(queueKey);
@ -185,9 +179,7 @@ public class HashPartitionService implements PartitionService {
} }
@Override @Override
@Deprecated public TopicPartitionInfo resolve(ServiceType serviceType, String queueName, TenantId tenantId, EntityId entityId) {
public TopicPartitionInfo resolve(ServiceType serviceType, TenantId tenantId, EntityId entityId, String queueName) {
log.warn("This method is deprecated and will be removed!!!");
TenantId isolatedOrSystemTenantId = getIsolatedOrSystemTenantId(serviceType, tenantId); TenantId isolatedOrSystemTenantId = getIsolatedOrSystemTenantId(serviceType, tenantId);
QueueKey queueKey = new QueueKey(serviceType, queueName, isolatedOrSystemTenantId); QueueKey queueKey = new QueueKey(serviceType, queueName, isolatedOrSystemTenantId);
if (!partitionSizesMap.containsKey(queueKey)) { if (!partitionSizesMap.containsKey(queueKey)) {
@ -201,32 +193,6 @@ public class HashPartitionService implements PartitionService {
return resolve(serviceType, null, tenantId, entityId); return resolve(serviceType, null, tenantId, entityId);
} }
@Override
public TopicPartitionInfo resolve(ServiceType serviceType, QueueId queueId, TenantId tenantId, EntityId entityId) {
QueueKey queueKey;
if (queueId == null) {
queueKey = getMainQueueKey(serviceType, tenantId);
} else {
QueueRoutingInfo queueRoutingInfo = queuesById.get(queueId);
if (queueRoutingInfo == null) {
log.debug("Queue was removed but still used in CheckPoint rule node. [{}][{}]", tenantId, entityId);
queueKey = getMainQueueKey(serviceType, tenantId);
} else if (!queueRoutingInfo.getTenantId().equals(getIsolatedOrSystemTenantId(serviceType, tenantId))) {
log.debug("Tenant profile was changed but CheckPoint rule node still uses the queue from system level. [{}][{}]", tenantId, entityId);
queueKey = getMainQueueKey(serviceType, tenantId);
} else {
queueKey = new QueueKey(serviceType, queueRoutingInfo);
}
}
return resolve(queueKey, entityId);
}
private QueueKey getMainQueueKey(ServiceType serviceType, TenantId tenantId) {
return new QueueKey(serviceType, getIsolatedOrSystemTenantId(serviceType, tenantId));
}
private TopicPartitionInfo resolve(QueueKey queueKey, EntityId entityId) { private TopicPartitionInfo resolve(QueueKey queueKey, EntityId entityId) {
int hash = hashFunction.newHasher() int hash = hashFunction.newHasher()
.putLong(entityId.getId().getMostSignificantBits()) .putLong(entityId.getId().getMostSignificantBits())
@ -399,8 +365,6 @@ public class HashPartitionService implements PartitionService {
throw new RuntimeException("Tenant not found!"); throw new RuntimeException("Tenant not found!");
} }
switch (serviceType) { switch (serviceType) {
case TB_CORE:
return routingInfo.isIsolatedTbCore();
case TB_RULE_ENGINE: case TB_RULE_ENGINE:
return routingInfo.isIsolatedTbRuleEngine(); return routingInfo.isIsolatedTbRuleEngine();
default: default:

5
common/queue/src/main/java/org/thingsboard/server/queue/discovery/PartitionService.java

@ -32,13 +32,10 @@ import java.util.UUID;
*/ */
public interface PartitionService { public interface PartitionService {
@Deprecated TopicPartitionInfo resolve(ServiceType serviceType, String queueName, TenantId tenantId, EntityId entityId);
TopicPartitionInfo resolve(ServiceType serviceType, TenantId tenantId, EntityId entityId, String queueName);
TopicPartitionInfo resolve(ServiceType serviceType, TenantId tenantId, EntityId entityId); TopicPartitionInfo resolve(ServiceType serviceType, TenantId tenantId, EntityId entityId);
TopicPartitionInfo resolve(ServiceType serviceType, QueueId queueId, TenantId tenantId, EntityId entityId);
/** /**
* Received from the Discovery service when network topology is changed. * Received from the Discovery service when network topology is changed.
* @param currentService - current service information {@link org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo} * @param currentService - current service information {@link org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo}

1
common/queue/src/main/java/org/thingsboard/server/queue/discovery/TenantRoutingInfo.java

@ -21,6 +21,5 @@ import org.thingsboard.server.common.data.id.TenantId;
@Data @Data
public class TenantRoutingInfo { public class TenantRoutingInfo {
private final TenantId tenantId; private final TenantId tenantId;
private final boolean isolatedTbCore;
private final boolean isolatedTbRuleEngine; private final boolean isolatedTbRuleEngine;
} }

10
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java

@ -1088,7 +1088,7 @@ public class DefaultTransportService implements TransportService {
} }
private void sendToRuleEngine(TenantId tenantId, TbMsg tbMsg, TbQueueCallback callback) { private void sendToRuleEngine(TenantId tenantId, TbMsg tbMsg, TbQueueCallback callback) {
TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, tbMsg.getQueueId(), tenantId, tbMsg.getOriginator()); TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, tbMsg.getQueueName(), tenantId, tbMsg.getOriginator());
if (log.isTraceEnabled()) { if (log.isTraceEnabled()) {
log.trace("[{}][{}] Pushing to topic {} message {}", tenantId, tbMsg.getOriginator(), tpi.getFullTopicName(), tbMsg); log.trace("[{}][{}] Pushing to topic {} message {}", tenantId, tbMsg.getOriginator(), tpi.getFullTopicName(), tbMsg);
} }
@ -1105,18 +1105,18 @@ public class DefaultTransportService implements TransportService {
DeviceProfileId deviceProfileId = new DeviceProfileId(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB())); DeviceProfileId deviceProfileId = new DeviceProfileId(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB()));
DeviceProfile deviceProfile = deviceProfileCache.get(deviceProfileId); DeviceProfile deviceProfile = deviceProfileCache.get(deviceProfileId);
RuleChainId ruleChainId; RuleChainId ruleChainId;
QueueId queueId; String queueName;
if (deviceProfile == null) { if (deviceProfile == null) {
log.warn("[{}] Device profile is null!", deviceProfileId); log.warn("[{}] Device profile is null!", deviceProfileId);
ruleChainId = null; ruleChainId = null;
queueId = null; queueName = null;
} else { } else {
ruleChainId = deviceProfile.getDefaultRuleChainId(); ruleChainId = deviceProfile.getDefaultRuleChainId();
queueId = deviceProfile.getDefaultQueueId(); queueName = deviceProfile.getDefaultQueueName();
} }
TbMsg tbMsg = TbMsg.newMsg(queueId, sessionMsgType.name(), deviceId, customerId, metaData, gson.toJson(json), ruleChainId, null); TbMsg tbMsg = TbMsg.newMsg(queueName, sessionMsgType.name(), deviceId, customerId, metaData, gson.toJson(json), ruleChainId, null);
sendToRuleEngine(tenantId, tbMsg, callback); sendToRuleEngine(tenantId, tbMsg, callback);
} }

2
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/TransportTenantRoutingInfoService.java

@ -38,7 +38,7 @@ public class TransportTenantRoutingInfoService implements TenantRoutingInfoServi
@Override @Override
public TenantRoutingInfo getRoutingInfo(TenantId tenantId) { public TenantRoutingInfo getRoutingInfo(TenantId tenantId) {
TenantProfile profile = tenantProfileCache.get(tenantId); TenantProfile profile = tenantProfileCache.get(tenantId);
return new TenantRoutingInfo(tenantId, profile.isIsolatedTbCore(), profile.isIsolatedTbRuleEngine()); return new TenantRoutingInfo(tenantId, profile.isIsolatedTbRuleEngine());
} }
} }

4
common/util/src/main/java/org/thingsboard/common/util/AbstractListeningExecutor.java

@ -49,6 +49,10 @@ public abstract class AbstractListeningExecutor implements ListeningExecutor {
return service.submit(task); return service.submit(task);
} }
public ListenableFuture<?> executeAsync(Runnable task) {
return service.submit(task);
}
@Override @Override
public void execute(Runnable command) { public void execute(Runnable command) {
service.execute(command); service.execute(command);

11
common/util/src/main/java/org/thingsboard/common/util/ListeningExecutor.java

@ -24,8 +24,19 @@ public interface ListeningExecutor extends Executor {
<T> ListenableFuture<T> executeAsync(Callable<T> task); <T> ListenableFuture<T> executeAsync(Callable<T> task);
default ListenableFuture<?> executeAsync(Runnable task) {
return executeAsync(() -> {
task.run();
return null;
});
}
default <T> ListenableFuture<T> submit(Callable<T> task) { default <T> ListenableFuture<T> submit(Callable<T> task) {
return executeAsync(task); return executeAsync(task);
} }
default ListenableFuture<?> submit(Runnable task) {
return executeAsync(task);
}
} }

3
common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultClusterVersionControlService.java

@ -526,7 +526,8 @@ public class DefaultClusterVersionControlService extends TbApplicationEventListe
.setRequestIdLSB(ctx.getRequestId().getLeastSignificantBits()); .setRequestIdLSB(ctx.getRequestId().getLeastSignificantBits());
if (e.isPresent()) { if (e.isPresent()) {
log.debug("[{}][{}] Failed to process task", ctx.getTenantId(), ctx.getRequestId(), e.get()); log.debug("[{}][{}] Failed to process task", ctx.getTenantId(), ctx.getRequestId(), e.get());
builder.setError(e.get().getMessage()); var message = e.get().getMessage();
builder.setError(message != null ? message : e.get().getClass().getSimpleName());
} else { } else {
if (enrichFunction != null) { if (enrichFunction != null) {
builder = enrichFunction.apply(builder); builder = enrichFunction.apply(builder);

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

@ -120,12 +120,6 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
DeviceProfile oldDeviceProfile = deviceProfileValidator.validate(deviceProfile, DeviceProfile::getTenantId); DeviceProfile oldDeviceProfile = deviceProfileValidator.validate(deviceProfile, DeviceProfile::getTenantId);
DeviceProfile savedDeviceProfile; DeviceProfile savedDeviceProfile;
try { try {
if (deviceProfile.getDefaultQueueId() == null && StringUtils.isNotEmpty(deviceProfile.getDefaultQueueName())) {
Queue existing = queueService.findQueueByTenantIdAndName(deviceProfile.getTenantId(), deviceProfile.getDefaultQueueName());
if (existing != null) {
deviceProfile.setDefaultQueueId(existing.getId());
}
}
savedDeviceProfile = deviceProfileDao.saveAndFlush(deviceProfile.getTenantId(), deviceProfile); savedDeviceProfile = deviceProfileDao.saveAndFlush(deviceProfile.getTenantId(), deviceProfile);
publishEvictEvent(new DeviceProfileEvictEvent(savedDeviceProfile.getTenantId(), savedDeviceProfile.getName(), publishEvictEvent(new DeviceProfileEvictEvent(savedDeviceProfile.getTenantId(), savedDeviceProfile.getName(),
oldDeviceProfile != null ? oldDeviceProfile.getName() : null, savedDeviceProfile.getId(), savedDeviceProfile.isDefault())); oldDeviceProfile != null ? oldDeviceProfile.getName() : null, savedDeviceProfile.getId(), savedDeviceProfile.isDefault()));

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

@ -181,6 +181,7 @@ public class ModelConstants {
public static final String DEVICE_PROFILE_DEFAULT_RULE_CHAIN_ID_PROPERTY = "default_rule_chain_id"; public static final String DEVICE_PROFILE_DEFAULT_RULE_CHAIN_ID_PROPERTY = "default_rule_chain_id";
public static final String DEVICE_PROFILE_DEFAULT_DASHBOARD_ID_PROPERTY = "default_dashboard_id"; public static final String DEVICE_PROFILE_DEFAULT_DASHBOARD_ID_PROPERTY = "default_dashboard_id";
public static final String DEVICE_PROFILE_DEFAULT_QUEUE_ID_PROPERTY = "default_queue_id"; public static final String DEVICE_PROFILE_DEFAULT_QUEUE_ID_PROPERTY = "default_queue_id";
public static final String DEVICE_PROFILE_DEFAULT_QUEUE_NAME_PROPERTY = "default_queue_name";
public static final String DEVICE_PROFILE_PROVISION_DEVICE_KEY = "provision_device_key"; public static final String DEVICE_PROFILE_PROVISION_DEVICE_KEY = "provision_device_key";
public static final String DEVICE_PROFILE_FIRMWARE_ID_PROPERTY = "firmware_id"; public static final String DEVICE_PROFILE_FIRMWARE_ID_PROPERTY = "firmware_id";
public static final String DEVICE_PROFILE_SOFTWARE_ID_PROPERTY = "software_id"; public static final String DEVICE_PROFILE_SOFTWARE_ID_PROPERTY = "software_id";

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

@ -88,8 +88,8 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl
@Column(name = ModelConstants.DEVICE_PROFILE_DEFAULT_DASHBOARD_ID_PROPERTY) @Column(name = ModelConstants.DEVICE_PROFILE_DEFAULT_DASHBOARD_ID_PROPERTY)
private UUID defaultDashboardId; private UUID defaultDashboardId;
@Column(name = ModelConstants.DEVICE_PROFILE_DEFAULT_QUEUE_ID_PROPERTY) @Column(name = ModelConstants.DEVICE_PROFILE_DEFAULT_QUEUE_NAME_PROPERTY)
private UUID defaultQueueId; private String defaultQueueName;
@Type(type = "jsonb") @Type(type = "jsonb")
@Column(name = ModelConstants.DEVICE_PROFILE_PROFILE_DATA_PROPERTY, columnDefinition = "jsonb") @Column(name = ModelConstants.DEVICE_PROFILE_PROFILE_DATA_PROPERTY, columnDefinition = "jsonb")
@ -133,9 +133,7 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl
if (deviceProfile.getDefaultDashboardId() != null) { if (deviceProfile.getDefaultDashboardId() != null) {
this.defaultDashboardId = deviceProfile.getDefaultDashboardId().getId(); this.defaultDashboardId = deviceProfile.getDefaultDashboardId().getId();
} }
if (deviceProfile.getDefaultQueueId() != null) { this.defaultQueueName = deviceProfile.getDefaultQueueName();
this.defaultQueueId = deviceProfile.getDefaultQueueId().getId();
}
this.provisionDeviceKey = deviceProfile.getProvisionDeviceKey(); this.provisionDeviceKey = deviceProfile.getProvisionDeviceKey();
if (deviceProfile.getFirmwareId() != null) { if (deviceProfile.getFirmwareId() != null) {
this.firmwareId = deviceProfile.getFirmwareId().getId(); this.firmwareId = deviceProfile.getFirmwareId().getId();
@ -176,6 +174,7 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl
deviceProfile.setProvisionType(provisionType); deviceProfile.setProvisionType(provisionType);
deviceProfile.setDescription(description); deviceProfile.setDescription(description);
deviceProfile.setDefault(isDefault); deviceProfile.setDefault(isDefault);
deviceProfile.setDefaultQueueName(defaultQueueName);
deviceProfile.setProfileData(JacksonUtil.convertValue(profileData, DeviceProfileData.class)); deviceProfile.setProfileData(JacksonUtil.convertValue(profileData, DeviceProfileData.class));
if (defaultRuleChainId != null) { if (defaultRuleChainId != null) {
deviceProfile.setDefaultRuleChainId(new RuleChainId(defaultRuleChainId)); deviceProfile.setDefaultRuleChainId(new RuleChainId(defaultRuleChainId));
@ -183,15 +182,11 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl
if (defaultDashboardId != null) { if (defaultDashboardId != null) {
deviceProfile.setDefaultDashboardId(new DashboardId(defaultDashboardId)); deviceProfile.setDefaultDashboardId(new DashboardId(defaultDashboardId));
} }
if (defaultQueueId != null) {
deviceProfile.setDefaultQueueId(new QueueId(defaultQueueId));
}
deviceProfile.setProvisionDeviceKey(provisionDeviceKey); deviceProfile.setProvisionDeviceKey(provisionDeviceKey);
if (firmwareId != null) { if (firmwareId != null) {
deviceProfile.setFirmwareId(new OtaPackageId(firmwareId)); deviceProfile.setFirmwareId(new OtaPackageId(firmwareId));
} }
if (softwareId != null) { if (softwareId != null) {
deviceProfile.setSoftwareId(new OtaPackageId(softwareId)); deviceProfile.setSoftwareId(new OtaPackageId(softwareId));
} }

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

@ -53,9 +53,6 @@ public final class TenantProfileEntity extends BaseSqlEntity<TenantProfile> impl
@Column(name = ModelConstants.TENANT_PROFILE_IS_DEFAULT_PROPERTY) @Column(name = ModelConstants.TENANT_PROFILE_IS_DEFAULT_PROPERTY)
private boolean isDefault; private boolean isDefault;
@Column(name = ModelConstants.TENANT_PROFILE_ISOLATED_TB_CORE)
private boolean isolatedTbCore;
@Column(name = ModelConstants.TENANT_PROFILE_ISOLATED_TB_RULE_ENGINE) @Column(name = ModelConstants.TENANT_PROFILE_ISOLATED_TB_RULE_ENGINE)
private boolean isolatedTbRuleEngine; private boolean isolatedTbRuleEngine;
@ -75,7 +72,6 @@ public final class TenantProfileEntity extends BaseSqlEntity<TenantProfile> impl
this.name = tenantProfile.getName(); this.name = tenantProfile.getName();
this.description = tenantProfile.getDescription(); this.description = tenantProfile.getDescription();
this.isDefault = tenantProfile.isDefault(); this.isDefault = tenantProfile.isDefault();
this.isolatedTbCore = tenantProfile.isIsolatedTbCore();
this.isolatedTbRuleEngine = tenantProfile.isIsolatedTbRuleEngine(); this.isolatedTbRuleEngine = tenantProfile.isIsolatedTbRuleEngine();
this.profileData = JacksonUtil.convertValue(tenantProfile.getProfileData(), ObjectNode.class); this.profileData = JacksonUtil.convertValue(tenantProfile.getProfileData(), ObjectNode.class);
} }
@ -101,7 +97,6 @@ public final class TenantProfileEntity extends BaseSqlEntity<TenantProfile> impl
tenantProfile.setName(name); tenantProfile.setName(name);
tenantProfile.setDescription(description); tenantProfile.setDescription(description);
tenantProfile.setDefault(isDefault); tenantProfile.setDefault(isDefault);
tenantProfile.setIsolatedTbCore(isolatedTbCore);
tenantProfile.setIsolatedTbRuleEngine(isolatedTbRuleEngine); tenantProfile.setIsolatedTbRuleEngine(isolatedTbRuleEngine);
tenantProfile.setProfileData(JacksonUtil.convertValue(profileData, TenantProfileData.class)); tenantProfile.setProfileData(JacksonUtil.convertValue(profileData, TenantProfileData.class));
return tenantProfile; return tenantProfile;

15
dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java

@ -84,7 +84,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
private static final int DEFAULT_PAGE_SIZE = 1000; private static final int DEFAULT_PAGE_SIZE = 1000;
public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
public static final String TB_RULE_CHAIN_INPUT_NODE = "org.thingsboard.rule.engine.flow.TbRuleChainInputNode";
@Autowired @Autowired
private RuleChainDao ruleChainDao; private RuleChainDao ruleChainDao;
@ -566,6 +566,19 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
} }
} }
} }
if (!CollectionUtils.isEmpty(metaData.getNodes())) {
metaData.getNodes().stream()
.filter(ruleNode -> ruleNode.getType().equals(TB_RULE_CHAIN_INPUT_NODE))
.forEach(ruleNode -> {
ObjectNode configuration = (ObjectNode) ruleNode.getConfiguration();
if (configuration.has("ruleChainId")) {
if (configuration.get("ruleChainId").asText().equals(oldRuleChainId.toString())) {
configuration.put("ruleChainId", newRuleChainId.toString());
ruleNode.setConfiguration(configuration);
}
}
});
}
} }
} }

9
dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java

@ -126,16 +126,11 @@ public class DeviceProfileDataValidator extends AbstractHasOtaPackageValidator<D
throw new DataValidationException("Another default device profile is present in scope of current tenant!"); throw new DataValidationException("Another default device profile is present in scope of current tenant!");
} }
} }
if (deviceProfile.getDefaultQueueId() != null) { if (StringUtils.isNotEmpty(deviceProfile.getDefaultQueueName())) {
Queue queue = queueService.findQueueById(tenantId, deviceProfile.getDefaultQueueId()); Queue queue = queueService.findQueueByTenantIdAndName(tenantId, deviceProfile.getDefaultQueueName());
if (queue == null) { if (queue == null) {
throw new DataValidationException("Device profile is referencing to non-existent queue!"); throw new DataValidationException("Device profile is referencing to non-existent queue!");
} }
TenantProfile tenantProfile = tenantProfileCache.get(deviceProfile.getTenantId());
if ((tenantProfile.isIsolatedTbRuleEngine() && !queue.getTenantId().equals(deviceProfile.getTenantId()))
|| (!tenantProfile.isIsolatedTbRuleEngine() && !queue.getTenantId().isNullUid())) {
throw new DataValidationException("Can't assign queue from different tenant!");
}
} }
if (deviceProfile.getProvisionType() == null) { if (deviceProfile.getProvisionType() == null) {
deviceProfile.setProvisionType(DeviceProfileProvisionType.DISABLED); deviceProfile.setProvisionType(DeviceProfileProvisionType.DISABLED);

2
dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantProfileDataValidator.java

@ -100,8 +100,6 @@ public class TenantProfileDataValidator extends DataValidator<TenantProfile> {
throw new DataValidationException("Can't update non existing tenant profile!"); throw new DataValidationException("Can't update non existing tenant profile!");
} else if (old.isIsolatedTbRuleEngine() != tenantProfile.isIsolatedTbRuleEngine()) { } else if (old.isIsolatedTbRuleEngine() != tenantProfile.isIsolatedTbRuleEngine()) {
throw new DataValidationException("Can't update isolatedTbRuleEngine property!"); throw new DataValidationException("Can't update isolatedTbRuleEngine property!");
} else if (old.isIsolatedTbCore() != tenantProfile.isIsolatedTbCore()) {
throw new DataValidationException("Can't update isolatedTbCore property!");
} }
return old; return old;
} }

1
dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java

@ -153,7 +153,6 @@ public class TenantProfileServiceImpl extends AbstractCachedEntityService<Tenant
profileData.setConfiguration(new DefaultTenantProfileConfiguration()); profileData.setConfiguration(new DefaultTenantProfileConfiguration());
defaultTenantProfile.setProfileData(profileData); defaultTenantProfile.setProfileData(profileData);
defaultTenantProfile.setDescription("Default tenant profile"); defaultTenantProfile.setDescription("Default tenant profile");
defaultTenantProfile.setIsolatedTbCore(false);
defaultTenantProfile.setIsolatedTbRuleEngine(false); defaultTenantProfile.setIsolatedTbRuleEngine(false);
defaultTenantProfile = saveTenantProfile(tenantId, defaultTenantProfile); defaultTenantProfile = saveTenantProfile(tenantId, defaultTenantProfile);
} }

1
dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java

@ -171,7 +171,6 @@ public class TenantServiceImpl extends AbstractCachedEntityService<TenantId, Ten
} }
@Override @Override
@Transactional(timeout = 60 * 60)
public void deleteTenant(TenantId tenantId) { public void deleteTenant(TenantId tenantId) {
log.trace("Executing deleteTenant [{}]", tenantId); log.trace("Executing deleteTenant [{}]", tenantId);
Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId);

4
dao/src/main/resources/sql/schema-entities-idx.sql

@ -70,4 +70,6 @@ CREATE INDEX IF NOT EXISTS idx_widgets_bundle_external_id ON widgets_bundle(tena
CREATE INDEX IF NOT EXISTS idx_rule_node_external_id ON rule_node(rule_chain_id, external_id); CREATE INDEX IF NOT EXISTS idx_rule_node_external_id ON rule_node(rule_chain_id, external_id);
CREATE INDEX IF NOT EXISTS idx_rule_node_type ON rule_node(type); CREATE INDEX IF NOT EXISTS idx_rule_node_type ON rule_node(type);
CREATE INDEX IF NOT EXISTS idx_api_usage_state_entity_id ON api_usage_state(entity_id);

3
dao/src/main/resources/sql/schema-entities.sql

@ -253,14 +253,13 @@ CREATE TABLE IF NOT EXISTS device_profile (
software_id uuid, software_id uuid,
default_rule_chain_id uuid, default_rule_chain_id uuid,
default_dashboard_id uuid, default_dashboard_id uuid,
default_queue_id uuid, default_queue_name varchar(255),
provision_device_key varchar, provision_device_key varchar,
external_id uuid, external_id uuid,
CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name), CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name),
CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key), CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key),
CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id), CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id),
CONSTRAINT fk_default_dashboard_device_profile FOREIGN KEY (default_dashboard_id) REFERENCES dashboard(id), CONSTRAINT fk_default_dashboard_device_profile FOREIGN KEY (default_dashboard_id) REFERENCES dashboard(id),
CONSTRAINT fk_default_queue_device_profile FOREIGN KEY (default_queue_id) REFERENCES queue(id),
CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES ota_package(id), CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES ota_package(id),
CONSTRAINT fk_software_device_profile FOREIGN KEY (software_id) REFERENCES ota_package(id) CONSTRAINT fk_software_device_profile FOREIGN KEY (software_id) REFERENCES ota_package(id)
); );

1
dao/src/test/java/org/thingsboard/server/dao/service/BaseQueueServiceTest.java

@ -51,7 +51,6 @@ public abstract class BaseQueueServiceTest extends AbstractServiceTest {
tenantProfile.setDefault(false); tenantProfile.setDefault(false);
tenantProfile.setName("Isolated TB Rule Engine"); tenantProfile.setName("Isolated TB Rule Engine");
tenantProfile.setDescription("Isolated TB Rule Engine tenant profile"); tenantProfile.setDescription("Isolated TB Rule Engine tenant profile");
tenantProfile.setIsolatedTbCore(false);
tenantProfile.setIsolatedTbRuleEngine(true); tenantProfile.setIsolatedTbRuleEngine(true);
TenantProfileQueueConfiguration mainQueueConfiguration = new TenantProfileQueueConfiguration(); TenantProfileQueueConfiguration mainQueueConfiguration = new TenantProfileQueueConfiguration();

10
dao/src/test/java/org/thingsboard/server/dao/service/BaseTenantProfileServiceTest.java

@ -85,7 +85,6 @@ public abstract class BaseTenantProfileServiceTest extends AbstractServiceTest {
Assert.assertEquals(tenantProfile.getDescription(), savedTenantProfile.getDescription()); Assert.assertEquals(tenantProfile.getDescription(), savedTenantProfile.getDescription());
Assert.assertEquals(tenantProfile.getProfileData(), savedTenantProfile.getProfileData()); Assert.assertEquals(tenantProfile.getProfileData(), savedTenantProfile.getProfileData());
Assert.assertEquals(tenantProfile.isDefault(), savedTenantProfile.isDefault()); Assert.assertEquals(tenantProfile.isDefault(), savedTenantProfile.isDefault());
Assert.assertEquals(tenantProfile.isIsolatedTbCore(), savedTenantProfile.isIsolatedTbCore());
Assert.assertEquals(tenantProfile.isIsolatedTbRuleEngine(), savedTenantProfile.isIsolatedTbRuleEngine()); Assert.assertEquals(tenantProfile.isIsolatedTbRuleEngine(), savedTenantProfile.isIsolatedTbRuleEngine());
savedTenantProfile.setName("New tenant profile"); savedTenantProfile.setName("New tenant profile");
@ -177,14 +176,6 @@ public abstract class BaseTenantProfileServiceTest extends AbstractServiceTest {
tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, savedTenantProfile); tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, savedTenantProfile);
} }
@Test(expected = DataValidationException.class)
public void testSaveSameTenantProfileWithDifferentIsolatedTbCore() {
TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile");
TenantProfile savedTenantProfile = tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, tenantProfile);
savedTenantProfile.setIsolatedTbCore(true);
tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, savedTenantProfile);
}
@Test(expected = DataValidationException.class) @Test(expected = DataValidationException.class)
public void testDeleteTenantProfileWithExistingTenant() { public void testDeleteTenantProfileWithExistingTenant() {
TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile"); TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile");
@ -298,7 +289,6 @@ public abstract class BaseTenantProfileServiceTest extends AbstractServiceTest {
profileData.setConfiguration(new DefaultTenantProfileConfiguration()); profileData.setConfiguration(new DefaultTenantProfileConfiguration());
tenantProfile.setProfileData(profileData); tenantProfile.setProfileData(profileData);
tenantProfile.setDefault(false); tenantProfile.setDefault(false);
tenantProfile.setIsolatedTbCore(false);
tenantProfile.setIsolatedTbRuleEngine(false); tenantProfile.setIsolatedTbRuleEngine(false);
return tenantProfile; return tenantProfile;
} }

1
dao/src/test/java/org/thingsboard/server/dao/service/BaseTenantServiceTest.java

@ -309,7 +309,6 @@ public abstract class BaseTenantServiceTest extends AbstractServiceTest {
profileData.setConfiguration(new DefaultTenantProfileConfiguration()); profileData.setConfiguration(new DefaultTenantProfileConfiguration());
tenantProfile.setProfileData(profileData); tenantProfile.setProfileData(profileData);
tenantProfile.setDefault(false); tenantProfile.setDefault(false);
tenantProfile.setIsolatedTbCore(true);
tenantProfile.setIsolatedTbRuleEngine(true); tenantProfile.setIsolatedTbRuleEngine(true);
TenantProfile isolatedTenantProfile = tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, tenantProfile); TenantProfile isolatedTenantProfile = tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, tenantProfile);

2
dao/src/test/resources/application-test.properties

@ -5,8 +5,6 @@ zk.zk_dir=/thingsboard
updates.enabled=false updates.enabled=false
audit-log.enabled=true audit-log.enabled=true
audit-log.by_tenant_partitioning=MONTHS
audit-log.default_query_period=30
audit-log.sink.type=none audit-log.sink.type=none
cache.type=caffeine cache.type=caffeine

2
msa/vc-executor/src/main/java/org/thingsboard/server/vc/service/VersionControlTenantRoutingInfoService.java

@ -25,6 +25,6 @@ public class VersionControlTenantRoutingInfoService implements TenantRoutingInfo
@Override @Override
public TenantRoutingInfo getRoutingInfo(TenantId tenantId) { public TenantRoutingInfo getRoutingInfo(TenantId tenantId) {
//This dummy implementation is ok since Version Control service does not produce any rule engine messages. //This dummy implementation is ok since Version Control service does not produce any rule engine messages.
return new TenantRoutingInfo(tenantId, false, false); return new TenantRoutingInfo(tenantId, false);
} }
} }

3
rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/MailService.java

@ -47,8 +47,7 @@ public interface MailService {
void sendTwoFaVerificationEmail(String email, String verificationCode, int expirationTimeSeconds) throws ThingsboardException; void sendTwoFaVerificationEmail(String email, String verificationCode, int expirationTimeSeconds) throws ThingsboardException;
void send(TenantId tenantId, CustomerId customerId, TbEmail tbEmail) throws ThingsboardException; void send(TenantId tenantId, CustomerId customerId, TbEmail tbEmail) throws ThingsboardException;
void send(TenantId tenantId, CustomerId customerId, TbEmail tbEmail, JavaMailSender javaMailSender, long timeout) throws ThingsboardException;
void send(TenantId tenantId, CustomerId customerId, TbEmail tbEmail, JavaMailSender javaMailSender) throws ThingsboardException;
void sendApiFeatureStateEmail(ApiFeature apiFeature, ApiUsageStateValue stateValue, String email, ApiUsageStateMailMessage msg) throws ThingsboardException; void sendApiFeatureStateEmail(ApiFeature apiFeature, ApiUsageStateValue stateValue, String email, ApiUsageStateMailMessage msg) throws ThingsboardException;

13
rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java

@ -142,15 +142,12 @@ public interface TbContext {
*/ */
void output(TbMsg msg, String relationType); void output(TbMsg msg, String relationType);
@Deprecated
void enqueue(TbMsg tbMsg, String queueName, Runnable onSuccess, Consumer<Throwable> onFailure);
/** /**
* Puts new message to custom queue for processing * Puts new message to custom queue for processing
* *
* @param msg - message * @param msg - message
*/ */
void enqueue(TbMsg msg, QueueId queueId, Runnable onSuccess, Consumer<Throwable> onFailure); void enqueue(TbMsg msg, String queueName, Runnable onSuccess, Consumer<Throwable> onFailure);
void enqueueForTellFailure(TbMsg msg, String failureMessage); void enqueueForTellFailure(TbMsg msg, String failureMessage);
@ -162,15 +159,15 @@ public interface TbContext {
void enqueueForTellNext(TbMsg msg, Set<String> relationTypes, Runnable onSuccess, Consumer<Throwable> onFailure); void enqueueForTellNext(TbMsg msg, Set<String> relationTypes, Runnable onSuccess, Consumer<Throwable> onFailure);
void enqueueForTellNext(TbMsg msg, QueueId queueId, String relationType, Runnable onSuccess, Consumer<Throwable> onFailure); void enqueueForTellNext(TbMsg msg, String queueName, String relationType, Runnable onSuccess, Consumer<Throwable> onFailure);
void enqueueForTellNext(TbMsg msg, QueueId queueId, Set<String> relationTypes, Runnable onSuccess, Consumer<Throwable> onFailure); void enqueueForTellNext(TbMsg msg, String queueName, Set<String> relationTypes, Runnable onSuccess, Consumer<Throwable> onFailure);
void ack(TbMsg tbMsg); void ack(TbMsg tbMsg);
TbMsg newMsg(QueueId queueId, String type, EntityId originator, TbMsgMetaData metaData, String data); TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data);
TbMsg newMsg(QueueId queueId, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data); TbMsg newMsg(String queueName, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data);
TbMsg transformMsg(TbMsg origMsg, String type, EntityId originator, TbMsgMetaData metaData, String data); TbMsg transformMsg(TbMsg origMsg, String type, EntityId originator, TbMsgMetaData metaData, String data);

2
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java

@ -138,7 +138,7 @@ public class TbCopyAttributesToEntityViewNode implements TbNode {
} }
private void transformAndTellNext(TbContext ctx, TbMsg msg, EntityView entityView) { private void transformAndTellNext(TbContext ctx, TbMsg msg, EntityView entityView) {
ctx.enqueueForTellNext(ctx.newMsg(msg.getQueueId(), msg.getType(), entityView.getId(), msg.getCustomerId(), msg.getMetaData(), msg.getData()), SUCCESS); ctx.enqueueForTellNext(ctx.newMsg(msg.getQueueName(), msg.getType(), entityView.getId(), msg.getCustomerId(), msg.getMetaData(), msg.getData()), SUCCESS);
} }
private boolean attributeContainsInEntityView(String scope, String attrKey, EntityView entityView) { private boolean attributeContainsInEntityView(String scope, String attrKey, EntityView entityView) {

2
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java

@ -77,7 +77,7 @@ public class TbMsgCountNode implements TbNode {
TbMsgMetaData metaData = new TbMsgMetaData(); TbMsgMetaData metaData = new TbMsgMetaData();
metaData.putValue("delta", Long.toString(System.currentTimeMillis() - lastScheduledTs + delay)); metaData.putValue("delta", Long.toString(System.currentTimeMillis() - lastScheduledTs + delay));
TbMsg tbMsg = TbMsg.newMsg(msg.getQueueId(), SessionMsgType.POST_TELEMETRY_REQUEST.name(), ctx.getTenantId(), msg.getCustomerId(), metaData, gson.toJson(telemetryJson)); TbMsg tbMsg = TbMsg.newMsg(msg.getQueueName(), SessionMsgType.POST_TELEMETRY_REQUEST.name(), ctx.getTenantId(), msg.getCustomerId(), metaData, gson.toJson(telemetryJson));
ctx.enqueueForTellNext(tbMsg, SUCCESS); ctx.enqueueForTellNext(tbMsg, SUCCESS);
scheduleTickMsg(ctx, tbMsg); scheduleTickMsg(ctx, tbMsg);
} else { } else {

6
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbCheckpointNode.java

@ -41,17 +41,17 @@ import java.util.UUID;
) )
public class TbCheckpointNode implements TbNode { public class TbCheckpointNode implements TbNode {
private QueueId queueId; private String queueName;
@Override @Override
public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
TbCheckpointNodeConfiguration config = TbNodeUtils.convert(configuration, TbCheckpointNodeConfiguration.class); TbCheckpointNodeConfiguration config = TbNodeUtils.convert(configuration, TbCheckpointNodeConfiguration.class);
this.queueId = new QueueId(UUID.fromString(config.getQueueId())); this.queueName = config.getQueueName();
} }
@Override @Override
public void onMsg(TbContext ctx, TbMsg msg) { public void onMsg(TbContext ctx, TbMsg msg) {
ctx.enqueueForTellNext(msg, queueId, TbRelationTypes.SUCCESS, () -> ctx.ack(msg), error -> ctx.tellFailure(msg, error)); ctx.enqueueForTellNext(msg, queueName, TbRelationTypes.SUCCESS, () -> ctx.ack(msg), error -> ctx.tellFailure(msg, error));
} }
@Override @Override

2
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbCheckpointNodeConfiguration.java

@ -22,7 +22,7 @@ import org.thingsboard.server.common.data.id.QueueId;
@Data @Data
public class TbCheckpointNodeConfiguration implements NodeConfiguration<TbCheckpointNodeConfiguration> { public class TbCheckpointNodeConfiguration implements NodeConfiguration<TbCheckpointNodeConfiguration> {
private String queueId; private String queueName;
@Override @Override
public TbCheckpointNodeConfiguration defaultConfiguration() { public TbCheckpointNodeConfiguration defaultConfiguration() {

2
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNode.java

@ -88,7 +88,7 @@ public class TbSendEmailNode implements TbNode {
if (this.config.isUseSystemSmtpSettings()) { if (this.config.isUseSystemSmtpSettings()) {
ctx.getMailService(true).send(ctx.getTenantId(), msg.getCustomerId(), email); ctx.getMailService(true).send(ctx.getTenantId(), msg.getCustomerId(), email);
} else { } else {
ctx.getMailService(false).send(ctx.getTenantId(), msg.getCustomerId(), email, this.mailSender); ctx.getMailService(false).send(ctx.getTenantId(), msg.getCustomerId(), email, this.mailSender, config.getTimeout());
} }
} }

6
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmState.java

@ -60,7 +60,7 @@ class AlarmState {
private volatile Alarm currentAlarm; private volatile Alarm currentAlarm;
private volatile boolean initialFetchDone; private volatile boolean initialFetchDone;
private volatile TbMsgMetaData lastMsgMetaData; private volatile TbMsgMetaData lastMsgMetaData;
private volatile QueueId lastMsgQueueId; private volatile String lastMsgQueueName;
private volatile DataSnapshot dataSnapshot; private volatile DataSnapshot dataSnapshot;
private final DynamicPredicateValueCtx dynamicPredicateValueCtx; private final DynamicPredicateValueCtx dynamicPredicateValueCtx;
@ -74,7 +74,7 @@ class AlarmState {
public boolean process(TbContext ctx, TbMsg msg, DataSnapshot data, SnapshotUpdate update) throws ExecutionException, InterruptedException { public boolean process(TbContext ctx, TbMsg msg, DataSnapshot data, SnapshotUpdate update) throws ExecutionException, InterruptedException {
initCurrentAlarm(ctx); initCurrentAlarm(ctx);
lastMsgMetaData = msg.getMetaData(); lastMsgMetaData = msg.getMetaData();
lastMsgQueueId = msg.getQueueId(); lastMsgQueueName = msg.getQueueName();
this.dataSnapshot = data; this.dataSnapshot = data;
try { try {
return createOrClearAlarms(ctx, msg, data, update, AlarmRuleState::eval); return createOrClearAlarms(ctx, msg, data, update, AlarmRuleState::eval);
@ -195,7 +195,7 @@ class AlarmState {
metaData.putValue(DataConstants.IS_CLEARED_ALARM, Boolean.TRUE.toString()); metaData.putValue(DataConstants.IS_CLEARED_ALARM, Boolean.TRUE.toString());
} }
setAlarmConditionMetadata(ruleState, metaData); setAlarmConditionMetadata(ruleState, metaData);
TbMsg newMsg = ctx.newMsg(lastMsgQueueId != null ? lastMsgQueueId : null, "ALARM", TbMsg newMsg = ctx.newMsg(lastMsgQueueName != null ? lastMsgQueueName : null, "ALARM",
originator, msg != null ? msg.getCustomerId() : null, metaData, data); originator, msg != null ? msg.getCustomerId() : null, metaData, data);
ctx.enqueueForTellNext(newMsg, relationType); ctx.enqueueForTellNext(newMsg, relationType);
} }

4
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNode.java

@ -116,10 +116,10 @@ public class TbSendRPCRequestNode implements TbNode {
ctx.getRpcService().sendRpcRequestToDevice(request, ruleEngineDeviceRpcResponse -> { ctx.getRpcService().sendRpcRequestToDevice(request, ruleEngineDeviceRpcResponse -> {
if (ruleEngineDeviceRpcResponse.getError().isEmpty()) { if (ruleEngineDeviceRpcResponse.getError().isEmpty()) {
TbMsg next = ctx.newMsg(msg.getQueueId(), msg.getType(), msg.getOriginator(), msg.getCustomerId(), msg.getMetaData(), ruleEngineDeviceRpcResponse.getResponse().orElse("{}")); TbMsg next = ctx.newMsg(msg.getQueueName(), msg.getType(), msg.getOriginator(), msg.getCustomerId(), msg.getMetaData(), ruleEngineDeviceRpcResponse.getResponse().orElse("{}"));
ctx.enqueueForTellNext(next, TbRelationTypes.SUCCESS); ctx.enqueueForTellNext(next, TbRelationTypes.SUCCESS);
} else { } else {
TbMsg next = ctx.newMsg(msg.getQueueId(), msg.getType(), msg.getOriginator(), msg.getCustomerId(), msg.getMetaData(), wrap("error", ruleEngineDeviceRpcResponse.getError().get().name())); TbMsg next = ctx.newMsg(msg.getQueueName(), msg.getType(), msg.getOriginator(), msg.getCustomerId(), msg.getMetaData(), wrap("error", ruleEngineDeviceRpcResponse.getError().get().name()));
ctx.enqueueForTellFailure(next, ruleEngineDeviceRpcResponse.getError().get().name()); ctx.enqueueForTellFailure(next, ruleEngineDeviceRpcResponse.getError().get().name());
} }
}); });

2
rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js

File diff suppressed because one or more lines are too long

2
transport/mqtt/src/main/java/org/thingsboard/server/mqtt/ThingsboardMqttTransportApplication.java

@ -20,12 +20,14 @@ import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import java.util.Arrays; import java.util.Arrays;
@SpringBootConfiguration @SpringBootConfiguration
@EnableAsync @EnableAsync
@EnableAutoConfiguration @EnableAutoConfiguration
@EnableScheduling
@ComponentScan({"org.thingsboard.server.mqtt", "org.thingsboard.server.common", "org.thingsboard.server.transport.mqtt", "org.thingsboard.server.queue", "org.thingsboard.server.cache"}) @ComponentScan({"org.thingsboard.server.mqtt", "org.thingsboard.server.common", "org.thingsboard.server.transport.mqtt", "org.thingsboard.server.queue", "org.thingsboard.server.cache"})
public class ThingsboardMqttTransportApplication { public class ThingsboardMqttTransportApplication {

4
ui-ngx/src/app/core/http/queue.service.ts

@ -35,6 +35,10 @@ export class QueueService {
return this.http.get<QueueInfo>(`/api/queues/${queueId}`, defaultHttpOptionsFromConfig(config)); return this.http.get<QueueInfo>(`/api/queues/${queueId}`, defaultHttpOptionsFromConfig(config));
} }
public getQueueByName(queueName: string, config?: RequestConfig): Observable<QueueInfo> {
return this.http.get<QueueInfo>(`/api/queues/name/${queueName}`, defaultHttpOptionsFromConfig(config));
}
public getTenantQueuesByServiceType(pageLink: PageLink, public getTenantQueuesByServiceType(pageLink: PageLink,
serviceType: ServiceType, serviceType: ServiceType,
config?: RequestConfig): Observable<PageData<QueueInfo>> { config?: RequestConfig): Observable<PageData<QueueInfo>> {

5
ui-ngx/src/app/core/services/dashboard-utils.service.ts

@ -30,7 +30,7 @@ import {
WidgetLayout WidgetLayout
} from '@shared/models/dashboard.models'; } from '@shared/models/dashboard.models';
import { isDefined, isString, isUndefined } from '@core/utils'; import { isDefined, isString, isUndefined } from '@core/utils';
import { Datasource, DatasourceType, Widget } from '@app/shared/models/widget.models'; import { Datasource, DatasourceType, Widget, widgetType } from '@app/shared/models/widget.models';
import { EntityType } from '@shared/models/entity-type.models'; import { EntityType } from '@shared/models/entity-type.models';
import { AliasFilterType, EntityAlias, EntityAliasFilter } from '@app/shared/models/alias.models'; import { AliasFilterType, EntityAlias, EntityAliasFilter } from '@app/shared/models/alias.models';
import { EntityId } from '@app/shared/models/id/entity-id'; import { EntityId } from '@app/shared/models/id/entity-id';
@ -106,7 +106,8 @@ export class DashboardUtilsService {
const targetDevicesByAliasId: {[aliasId: string]: Array<Array<string>>} = {}; const targetDevicesByAliasId: {[aliasId: string]: Array<Array<string>>} = {};
for (const widgetId of Object.keys(dashboard.configuration.widgets)) { for (const widgetId of Object.keys(dashboard.configuration.widgets)) {
const widget = dashboard.configuration.widgets[widgetId]; const widget = dashboard.configuration.widgets[widgetId];
widget.config.datasources.forEach((datasource) => { const datasources = widget.type === widgetType.alarm ? [widget.config.alarmSource] : widget.config.datasources;
datasources.forEach((datasource) => {
if (datasource.entityAliasId) { if (datasource.entityAliasId) {
const aliasId = datasource.entityAliasId; const aliasId = datasource.entityAliasId;
let aliasDatasources = datasourcesByAliasId[aliasId]; let aliasDatasources = datasourcesByAliasId[aliasId];

34
ui-ngx/src/app/core/services/item-buffer.service.ts

@ -17,7 +17,14 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Dashboard, DashboardLayoutId } from '@app/shared/models/dashboard.models'; import { Dashboard, DashboardLayoutId } from '@app/shared/models/dashboard.models';
import { AliasesInfo, EntityAlias, EntityAliases, EntityAliasInfo } from '@shared/models/alias.models'; import { AliasesInfo, EntityAlias, EntityAliases, EntityAliasInfo } from '@shared/models/alias.models';
import { DatasourceType, Widget, WidgetPosition, WidgetSize } from '@shared/models/widget.models'; import {
Datasource,
DatasourceType,
Widget,
WidgetPosition,
WidgetSize,
widgetType
} from '@shared/models/widget.models';
import { DashboardUtilsService } from '@core/services/dashboard-utils.service'; import { DashboardUtilsService } from '@core/services/dashboard-utils.service';
import { deepClone, isEqual } from '@core/utils'; import { deepClone, isEqual } from '@core/utils';
import { UtilsService } from '@core/services/utils.service'; import { UtilsService } from '@core/services/utils.service';
@ -87,12 +94,13 @@ export class ItemBufferService {
}; };
const originalColumns = this.getOriginalColumns(dashboard, sourceState, sourceLayout); const originalColumns = this.getOriginalColumns(dashboard, sourceState, sourceLayout);
const originalSize = this.getOriginalSize(dashboard, sourceState, sourceLayout, widget); const originalSize = this.getOriginalSize(dashboard, sourceState, sourceLayout, widget);
const datasources: Datasource[] = widget.type === widgetType.alarm ? [widget.config.alarmSource] : widget.config.datasources;
if (widget.config && dashboard.configuration if (widget.config && dashboard.configuration
&& dashboard.configuration.entityAliases) { && dashboard.configuration.entityAliases) {
let entityAlias: EntityAlias; let entityAlias: EntityAlias;
if (widget.config.datasources) { if (datasources) {
for (let i = 0; i < widget.config.datasources.length; i++) { for (let i = 0; i < datasources.length; i++) {
const datasource = widget.config.datasources[i]; const datasource = datasources[i];
if ((datasource.type === DatasourceType.entity || datasource.type === DatasourceType.entityCount) && datasource.entityAliasId) { if ((datasource.type === DatasourceType.entity || datasource.type === DatasourceType.entityCount) && datasource.entityAliasId) {
entityAlias = dashboard.configuration.entityAliases[datasource.entityAliasId]; entityAlias = dashboard.configuration.entityAliases[datasource.entityAliasId];
if (entityAlias) { if (entityAlias) {
@ -116,9 +124,9 @@ export class ItemBufferService {
if (widget.config && dashboard.configuration if (widget.config && dashboard.configuration
&& dashboard.configuration.filters) { && dashboard.configuration.filters) {
let filter: Filter; let filter: Filter;
if (widget.config.datasources) { if (datasources) {
for (let i = 0; i < widget.config.datasources.length; i++) { for (let i = 0; i < datasources.length; i++) {
const datasource = widget.config.datasources[i]; const datasource = datasources[i];
if ((datasource.type === DatasourceType.entity || datasource.type === DatasourceType.entityCount) && datasource.filterId) { if ((datasource.type === DatasourceType.entity || datasource.type === DatasourceType.entityCount) && datasource.filterId) {
filter = dashboard.configuration.filters[datasource.filterId]; filter = dashboard.configuration.filters[datasource.filterId];
if (filter) { if (filter) {
@ -438,7 +446,11 @@ export class ItemBufferService {
const datasourceIndex = Number(datasourceIndexStr); const datasourceIndex = Number(datasourceIndexStr);
aliasInfo = aliasesInfo.datasourceAliases[datasourceIndex]; aliasInfo = aliasesInfo.datasourceAliases[datasourceIndex];
newAliasId = this.getEntityAliasId(entityAliases, aliasInfo); newAliasId = this.getEntityAliasId(entityAliases, aliasInfo);
widget.config.datasources[datasourceIndex].entityAliasId = newAliasId; if (widget.type === widgetType.alarm) {
widget.config.alarmSource.entityAliasId = newAliasId;
} else {
widget.config.datasources[datasourceIndex].entityAliasId = newAliasId;
}
} }
for (const targetDeviceAliasIndexStr of Object.keys(aliasesInfo.targetDeviceAliases)) { for (const targetDeviceAliasIndexStr of Object.keys(aliasesInfo.targetDeviceAliases)) {
const targetDeviceAliasIndex = Number(targetDeviceAliasIndexStr); const targetDeviceAliasIndex = Number(targetDeviceAliasIndexStr);
@ -457,7 +469,11 @@ export class ItemBufferService {
const datasourceIndex = Number(datasourceIndexStr); const datasourceIndex = Number(datasourceIndexStr);
filterInfo = filtersInfo.datasourceFilters[datasourceIndex]; filterInfo = filtersInfo.datasourceFilters[datasourceIndex];
newFilterId = this.getFilterId(filters, filterInfo); newFilterId = this.getFilterId(filters, filterInfo);
widget.config.datasources[datasourceIndex].filterId = newFilterId; if (widget.type === widgetType.alarm) {
widget.config.alarmSource.filterId = newFilterId;
} else {
widget.config.datasources[datasourceIndex].filterId = newFilterId;
}
} }
return filters; return filters;
} }

1
ui-ngx/src/app/modules/home/components/import-export/import-export.service.ts

@ -631,7 +631,6 @@ export class ImportExportService {
private validateImportedTenantProfile(tenantProfile: TenantProfile): boolean { private validateImportedTenantProfile(tenantProfile: TenantProfile): boolean {
return isDefined(tenantProfile.name) return isDefined(tenantProfile.name)
&& isDefined(tenantProfile.profileData) && isDefined(tenantProfile.profileData)
&& isDefined(tenantProfile.isolatedTbCore)
&& isDefined(tenantProfile.isolatedTbRuleEngine); && isDefined(tenantProfile.isolatedTbRuleEngine);
} }

2
ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html

@ -56,7 +56,7 @@
</tb-dashboard-autocomplete> </tb-dashboard-autocomplete>
<tb-queue-autocomplete <tb-queue-autocomplete
[queueType]="serviceType" [queueType]="serviceType"
formControlName="defaultQueueId"> formControlName="defaultQueueName">
</tb-queue-autocomplete> </tb-queue-autocomplete>
<mat-form-field fxHide class="mat-block"> <mat-form-field fxHide class="mat-block">
<mat-label translate>device-profile.type</mat-label> <mat-label translate>device-profile.type</mat-label>

7
ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts

@ -50,7 +50,6 @@ import { StepperSelectionEvent } from '@angular/cdk/stepper';
import { deepTrim } from '@core/utils'; import { deepTrim } from '@core/utils';
import { ServiceType } from '@shared/models/queue.models'; import { ServiceType } from '@shared/models/queue.models';
import { DashboardId } from '@shared/models/id/dashboard-id'; import { DashboardId } from '@shared/models/id/dashboard-id';
import { QueueId } from '@shared/models/id/queue-id';
export interface AddDeviceProfileDialogData { export interface AddDeviceProfileDialogData {
deviceProfileName: string; deviceProfileName: string;
@ -111,7 +110,7 @@ export class AddDeviceProfileDialogComponent extends
image: [null, []], image: [null, []],
defaultRuleChainId: [null, []], defaultRuleChainId: [null, []],
defaultDashboardId: [null, []], defaultDashboardId: [null, []],
defaultQueueId: [null, []], defaultQueueName: [null, []],
description: ['', []] description: ['', []]
} }
); );
@ -188,6 +187,7 @@ export class AddDeviceProfileDialogComponent extends
name: this.deviceProfileDetailsFormGroup.get('name').value, name: this.deviceProfileDetailsFormGroup.get('name').value,
type: this.deviceProfileDetailsFormGroup.get('type').value, type: this.deviceProfileDetailsFormGroup.get('type').value,
image: this.deviceProfileDetailsFormGroup.get('image').value, image: this.deviceProfileDetailsFormGroup.get('image').value,
defaultQueueName: this.deviceProfileDetailsFormGroup.get('defaultQueueName').value,
transportType: this.transportConfigFormGroup.get('transportType').value, transportType: this.transportConfigFormGroup.get('transportType').value,
provisionType: deviceProvisionConfiguration.type, provisionType: deviceProvisionConfiguration.type,
provisionDeviceKey, provisionDeviceKey,
@ -205,9 +205,6 @@ export class AddDeviceProfileDialogComponent extends
if (this.deviceProfileDetailsFormGroup.get('defaultDashboardId').value) { if (this.deviceProfileDetailsFormGroup.get('defaultDashboardId').value) {
deviceProfile.defaultDashboardId = new DashboardId(this.deviceProfileDetailsFormGroup.get('defaultDashboardId').value); deviceProfile.defaultDashboardId = new DashboardId(this.deviceProfileDetailsFormGroup.get('defaultDashboardId').value);
} }
if (this.deviceProfileDetailsFormGroup.get('defaultQueueId').value) {
deviceProfile.defaultQueueId = new QueueId(this.deviceProfileDetailsFormGroup.get('defaultQueueId').value);
}
this.deviceProfileService.saveDeviceProfile(deepTrim(deviceProfile)).subscribe( this.deviceProfileService.saveDeviceProfile(deepTrim(deviceProfile)).subscribe(
(savedDeviceProfile) => { (savedDeviceProfile) => {
this.dialogRef.close(savedDeviceProfile); this.dialogRef.close(savedDeviceProfile);

2
ui-ngx/src/app/modules/home/components/profile/device-profile.component.html

@ -75,7 +75,7 @@
</tb-dashboard-autocomplete> </tb-dashboard-autocomplete>
<tb-queue-autocomplete <tb-queue-autocomplete
[queueType]="serviceType" [queueType]="serviceType"
formControlName="defaultQueueId"> formControlName="defaultQueueName">
</tb-queue-autocomplete> </tb-queue-autocomplete>
<tb-ota-package-autocomplete <tb-ota-package-autocomplete
[useFullEntityId]="true" [useFullEntityId]="true"

8
ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts

@ -42,7 +42,6 @@ import { ServiceType } from '@shared/models/queue.models';
import { EntityId } from '@shared/models/id/entity-id'; import { EntityId } from '@shared/models/id/entity-id';
import { OtaUpdateType } from '@shared/models/ota-package.models'; import { OtaUpdateType } from '@shared/models/ota-package.models';
import { DashboardId } from '@shared/models/id/dashboard-id'; import { DashboardId } from '@shared/models/id/dashboard-id';
import { QueueId } from '@shared/models/id/queue-id';
@Component({ @Component({
selector: 'tb-device-profile', selector: 'tb-device-profile',
@ -118,7 +117,7 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
}), }),
defaultRuleChainId: [entity && entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null, []], defaultRuleChainId: [entity && entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null, []],
defaultDashboardId: [entity && entity.defaultDashboardId ? entity.defaultDashboardId.id : null, []], defaultDashboardId: [entity && entity.defaultDashboardId ? entity.defaultDashboardId.id : null, []],
defaultQueueId: [entity && entity.defaultQueueId ? entity.defaultQueueId.id : null, []], defaultQueueName: [entity ? entity.defaultQueueName : null, []],
firmwareId: [entity ? entity.firmwareId : null], firmwareId: [entity ? entity.firmwareId : null],
softwareId: [entity ? entity.softwareId : null], softwareId: [entity ? entity.softwareId : null],
description: [entity ? entity.description : '', []], description: [entity ? entity.description : '', []],
@ -198,7 +197,7 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
}}, {emitEvent: false}); }}, {emitEvent: false});
this.entityForm.patchValue({defaultRuleChainId: entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null}, {emitEvent: false}); this.entityForm.patchValue({defaultRuleChainId: entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null}, {emitEvent: false});
this.entityForm.patchValue({defaultDashboardId: entity.defaultDashboardId ? entity.defaultDashboardId.id : null}, {emitEvent: false}); this.entityForm.patchValue({defaultDashboardId: entity.defaultDashboardId ? entity.defaultDashboardId.id : null}, {emitEvent: false});
this.entityForm.patchValue({defaultQueueId: entity.defaultQueueId ? entity.defaultQueueId.id : null}, {emitEvent: false}); this.entityForm.patchValue({defaultQueueName: entity.defaultQueueName}, {emitEvent: false});
this.entityForm.patchValue({firmwareId: entity.firmwareId}, {emitEvent: false}); this.entityForm.patchValue({firmwareId: entity.firmwareId}, {emitEvent: false});
this.entityForm.patchValue({softwareId: entity.softwareId}, {emitEvent: false}); this.entityForm.patchValue({softwareId: entity.softwareId}, {emitEvent: false});
this.entityForm.patchValue({description: entity.description}, {emitEvent: false}); this.entityForm.patchValue({description: entity.description}, {emitEvent: false});
@ -211,9 +210,6 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
if (formValue.defaultDashboardId) { if (formValue.defaultDashboardId) {
formValue.defaultDashboardId = new DashboardId(formValue.defaultDashboardId); formValue.defaultDashboardId = new DashboardId(formValue.defaultDashboardId);
} }
if (formValue.defaultQueueId) {
formValue.defaultQueueId = new QueueId(formValue.defaultQueueId);
}
const deviceProvisionConfiguration: DeviceProvisionConfiguration = formValue.profileData.provisionConfiguration; const deviceProvisionConfiguration: DeviceProvisionConfiguration = formValue.profileData.provisionConfiguration;
formValue.provisionType = deviceProvisionConfiguration.type; formValue.provisionType = deviceProvisionConfiguration.type;
formValue.provisionDeviceKey = deviceProvisionConfiguration.provisionDeviceKey; formValue.provisionDeviceKey = deviceProvisionConfiguration.provisionDeviceKey;

4
ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.html

@ -59,10 +59,6 @@
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<div fxLayout="column"> <div fxLayout="column">
<mat-checkbox class="hinted-checkbox" formControlName="isolatedTbCore">
<div>{{ 'tenant.isolated-tb-core' | translate }}</div>
<div class="tb-hint">{{'tenant.isolated-tb-core-details' | translate}}</div>
</mat-checkbox>
<mat-checkbox class="hinted-checkbox" formControlName="isolatedTbRuleEngine"> <mat-checkbox class="hinted-checkbox" formControlName="isolatedTbRuleEngine">
<div>{{ 'tenant.isolated-tb-rule-engine' | translate }}</div> <div>{{ 'tenant.isolated-tb-rule-engine' | translate }}</div>
<div class="tb-hint">{{'tenant.isolated-tb-rule-engine-details' | translate}}</div> <div class="tb-hint">{{'tenant.isolated-tb-rule-engine-details' | translate}}</div>

3
ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.ts

@ -81,7 +81,6 @@ export class TenantProfileComponent extends EntityComponent<TenantProfile> {
const formGroup = this.fb.group( const formGroup = this.fb.group(
{ {
name: [entity ? entity.name : '', [Validators.required, Validators.maxLength(255)]], name: [entity ? entity.name : '', [Validators.required, Validators.maxLength(255)]],
isolatedTbCore: [entity ? entity.isolatedTbCore : false, []],
isolatedTbRuleEngine: [entity ? entity.isolatedTbRuleEngine : false, []], isolatedTbRuleEngine: [entity ? entity.isolatedTbRuleEngine : false, []],
profileData: this.fb.group({ profileData: this.fb.group({
configuration: [entity && !this.isAdd ? entity?.profileData.configuration configuration: [entity && !this.isAdd ? entity?.profileData.configuration
@ -107,7 +106,6 @@ export class TenantProfileComponent extends EntityComponent<TenantProfile> {
updateForm(entity: TenantProfile) { updateForm(entity: TenantProfile) {
this.entityForm.patchValue({name: entity.name}, {emitEvent: false}); this.entityForm.patchValue({name: entity.name}, {emitEvent: false});
this.entityForm.patchValue({isolatedTbCore: entity.isolatedTbCore}, {emitEvent: false});
this.entityForm.patchValue({isolatedTbRuleEngine: entity.isolatedTbRuleEngine}, {emitEvent: false}); this.entityForm.patchValue({isolatedTbRuleEngine: entity.isolatedTbRuleEngine}, {emitEvent: false});
this.entityForm.get('profileData').patchValue({ this.entityForm.get('profileData').patchValue({
configuration: !this.isAdd ? entity.profileData?.configuration : createTenantProfileConfiguration(TenantProfileType.DEFAULT) configuration: !this.isAdd ? entity.profileData?.configuration : createTenantProfileConfiguration(TenantProfileType.DEFAULT)
@ -121,7 +119,6 @@ export class TenantProfileComponent extends EntityComponent<TenantProfile> {
if (this.isEditValue) { if (this.isEditValue) {
this.entityForm.enable({emitEvent: false}); this.entityForm.enable({emitEvent: false});
if (!this.isAdd) { if (!this.isAdd) {
this.entityForm.get('isolatedTbCore').disable({emitEvent: false});
this.entityForm.get('isolatedTbRuleEngine').disable({emitEvent: false}); this.entityForm.get('isolatedTbRuleEngine').disable({emitEvent: false});
} }
} else { } else {

18
ui-ngx/src/app/modules/home/components/widget/legend-config.component.html

@ -37,23 +37,27 @@
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
</div> </div>
<div fxLayout.xs="column" fxLayoutAlign.xs="center" fxLayout="row wrap" fxLayoutAlign="space-between center" fxLayoutGap="8px"> <div fxFlex fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px">
<mat-checkbox formControlName="showMin" fxFlex="48"> <mat-checkbox formControlName="showMin" fxFlex>
{{ 'legend.show-min' | translate }} {{ 'legend.show-min' | translate }}
</mat-checkbox> </mat-checkbox>
<mat-checkbox formControlName="showMax" fxFlex="48"> <mat-checkbox formControlName="showMax" fxFlex>
{{ 'legend.show-max' | translate }} {{ 'legend.show-max' | translate }}
</mat-checkbox> </mat-checkbox>
<mat-checkbox formControlName="showAvg" fxFlex="48"> </div>
<div fxFlex fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px">
<mat-checkbox formControlName="showAvg" fxFlex>
{{ 'legend.show-avg' | translate }} {{ 'legend.show-avg' | translate }}
</mat-checkbox> </mat-checkbox>
<mat-checkbox formControlName="showTotal" fxFlex="48"> <mat-checkbox formControlName="showTotal" fxFlex>
{{ 'legend.show-total' | translate }} {{ 'legend.show-total' | translate }}
</mat-checkbox> </mat-checkbox>
<mat-checkbox formControlName="showLatest" fxFlex="48"> </div>
<div fxFlex fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px">
<mat-checkbox formControlName="showLatest" fxFlex>
{{ 'legend.show-latest' | translate }} {{ 'legend.show-latest' | translate }}
</mat-checkbox> </mat-checkbox>
<mat-checkbox formControlName="sortDataKeys" fxFlex="48"> <mat-checkbox formControlName="sortDataKeys" fxFlex>
{{ 'legend.sort-legend' | translate }} {{ 'legend.sort-legend' | translate }}
</mat-checkbox> </mat-checkbox>
</div> </div>

4
ui-ngx/src/app/modules/home/components/widget/lib/maps/common-maps-utils.ts

@ -116,11 +116,11 @@ const linkActionRegex = /<link-act name=['"]([^['"]*)['"]>([^<]*)<\/link-act>/g;
const buttonActionRegex = /<button-act name=['"]([^['"]*)['"]>([^<]*)<\/button-act>/g; const buttonActionRegex = /<button-act name=['"]([^['"]*)['"]>([^<]*)<\/button-act>/g;
function createLinkElement(actionName: string, actionText: string): string { function createLinkElement(actionName: string, actionText: string): string {
return `<a href="javascript:void(0);" class="tb-custom-action" data-action-name=${actionName}>${actionText}</a>`; return `<a href="javascript:void(0);" class="tb-custom-action" data-action-name="${actionName}">${actionText}</a>`;
} }
function createButtonElement(actionName: string, actionText: string) { function createButtonElement(actionName: string, actionText: string) {
return `<button mat-button class="tb-custom-action" data-action-name=${actionName}>${actionText}</button>`; return `<button mat-button class="tb-custom-action" data-action-name="${actionName}">${actionText}</button>`;
} }
function parseTemplate(template: string, data: FormattedData, function parseTemplate(template: string, data: FormattedData,

3
ui-ngx/src/app/modules/home/components/widget/widget-container.component.html

@ -25,7 +25,8 @@
'tb-highlighted': isHighlighted(widget), 'tb-highlighted': isHighlighted(widget),
'tb-not-highlighted': isNotHighlighted(widget), 'tb-not-highlighted': isNotHighlighted(widget),
'mat-elevation-z4': widget.dropShadow, 'mat-elevation-z4': widget.dropShadow,
'tb-has-timewindow': widget.hasTimewindow 'tb-has-timewindow': widget.hasTimewindow,
'tb-edit': isEdit
}" }"
[ngStyle]="widget.style" [ngStyle]="widget.style"
(mousedown)="onMouseDown($event)" (mousedown)="onMouseDown($event)"

4
ui-ngx/src/app/modules/home/components/widget/widget-container.component.scss

@ -114,4 +114,8 @@ div.tb-widget {
&.tb-not-highlighted { &.tb-not-highlighted {
opacity: .5; opacity: .5;
} }
&.tb-edit {
cursor: pointer;
}
} }

2
ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html

@ -95,7 +95,7 @@
<tb-queue-autocomplete <tb-queue-autocomplete
[ngClass]="{invisible: deviceWizardFormGroup.get('addProfileType').value !== 1}" [ngClass]="{invisible: deviceWizardFormGroup.get('addProfileType').value !== 1}"
[queueType]="serviceType" [queueType]="serviceType"
formControlName="defaultQueueId"> formControlName="defaultQueueName">
</tb-queue-autocomplete> </tb-queue-autocomplete>
</div> </div>
</div> </div>

11
ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.ts

@ -49,7 +49,6 @@ import { MediaBreakpoints } from '@shared/models/constants';
import { RuleChainId } from '@shared/models/id/rule-chain-id'; import { RuleChainId } from '@shared/models/id/rule-chain-id';
import { ServiceType } from '@shared/models/queue.models'; import { ServiceType } from '@shared/models/queue.models';
import { deepTrim } from '@core/utils'; import { deepTrim } from '@core/utils';
import { QueueId } from '@shared/models/id/queue-id';
@Component({ @Component({
selector: 'tb-device-wizard', selector: 'tb-device-wizard',
@ -114,7 +113,7 @@ export class DeviceWizardDialogComponent extends
deviceProfileId: [null, Validators.required], deviceProfileId: [null, Validators.required],
newDeviceProfileTitle: [{value: null, disabled: true}], newDeviceProfileTitle: [{value: null, disabled: true}],
defaultRuleChainId: [{value: null, disabled: true}], defaultRuleChainId: [{value: null, disabled: true}],
defaultQueueId: [{value: null, disabled: true}], defaultQueueName: [{value: null, disabled: true}],
description: [''] description: ['']
} }
); );
@ -127,7 +126,7 @@ export class DeviceWizardDialogComponent extends
this.deviceWizardFormGroup.get('newDeviceProfileTitle').setValidators(null); this.deviceWizardFormGroup.get('newDeviceProfileTitle').setValidators(null);
this.deviceWizardFormGroup.get('newDeviceProfileTitle').disable(); this.deviceWizardFormGroup.get('newDeviceProfileTitle').disable();
this.deviceWizardFormGroup.get('defaultRuleChainId').disable(); this.deviceWizardFormGroup.get('defaultRuleChainId').disable();
this.deviceWizardFormGroup.get('defaultQueueId').disable(); this.deviceWizardFormGroup.get('defaultQueueName').disable();
this.deviceWizardFormGroup.updateValueAndValidity(); this.deviceWizardFormGroup.updateValueAndValidity();
this.createProfile = false; this.createProfile = false;
} else { } else {
@ -136,7 +135,7 @@ export class DeviceWizardDialogComponent extends
this.deviceWizardFormGroup.get('newDeviceProfileTitle').setValidators([Validators.required]); this.deviceWizardFormGroup.get('newDeviceProfileTitle').setValidators([Validators.required]);
this.deviceWizardFormGroup.get('newDeviceProfileTitle').enable(); this.deviceWizardFormGroup.get('newDeviceProfileTitle').enable();
this.deviceWizardFormGroup.get('defaultRuleChainId').enable(); this.deviceWizardFormGroup.get('defaultRuleChainId').enable();
this.deviceWizardFormGroup.get('defaultQueueId').enable(); this.deviceWizardFormGroup.get('defaultQueueName').enable();
this.deviceWizardFormGroup.updateValueAndValidity(); this.deviceWizardFormGroup.updateValueAndValidity();
this.createProfile = true; this.createProfile = true;
@ -298,6 +297,7 @@ export class DeviceWizardDialogComponent extends
const deviceProfile: DeviceProfile = { const deviceProfile: DeviceProfile = {
name: this.deviceWizardFormGroup.get('newDeviceProfileTitle').value, name: this.deviceWizardFormGroup.get('newDeviceProfileTitle').value,
type: DeviceProfileType.DEFAULT, type: DeviceProfileType.DEFAULT,
defaultQueueName: this.deviceWizardFormGroup.get('defaultQueueName').value,
transportType: this.transportConfigFormGroup.get('transportType').value, transportType: this.transportConfigFormGroup.get('transportType').value,
provisionType: deviceProvisionConfiguration.type, provisionType: deviceProvisionConfiguration.type,
provisionDeviceKey, provisionDeviceKey,
@ -311,9 +311,6 @@ export class DeviceWizardDialogComponent extends
if (this.deviceWizardFormGroup.get('defaultRuleChainId').value) { if (this.deviceWizardFormGroup.get('defaultRuleChainId').value) {
deviceProfile.defaultRuleChainId = new RuleChainId(this.deviceWizardFormGroup.get('defaultRuleChainId').value); deviceProfile.defaultRuleChainId = new RuleChainId(this.deviceWizardFormGroup.get('defaultRuleChainId').value);
} }
if (this.deviceWizardFormGroup.get('defaultQueueId').value) {
deviceProfile.defaultQueueId = new QueueId(this.deviceWizardFormGroup.get('defaultQueueId').value);
}
return this.deviceProfileService.saveDeviceProfile(deepTrim(deviceProfile)).pipe( return this.deviceProfileService.saveDeviceProfile(deepTrim(deviceProfile)).pipe(
tap((profile) => { tap((profile) => {
this.currentDeviceProfileTransportType = profile.transportType; this.currentDeviceProfileTransportType = profile.transportType;

2
ui-ngx/src/app/modules/home/models/dashboard-component.models.ts

@ -428,7 +428,7 @@ export class DashboardWidget implements GridsterItem, IDashboardWidget {
this.hasAggregation = this.widget.type === widgetType.timeseries; this.hasAggregation = this.widget.type === widgetType.timeseries;
this.style = {cursor: 'pointer', this.style = {
color: this.color, color: this.color,
backgroundColor: this.backgroundColor, backgroundColor: this.backgroundColor,
padding: this.padding, padding: this.padding,

5
ui-ngx/src/app/shared/components/markdown.component.ts

@ -126,11 +126,11 @@ export class TbMarkdownComponent implements OnChanges {
this.handlePlugins(this.tbMarkdownInstanceComponentRef.location.nativeElement); this.handlePlugins(this.tbMarkdownInstanceComponentRef.location.nativeElement);
this.markdownService.highlight(this.tbMarkdownInstanceComponentRef.location.nativeElement); this.markdownService.highlight(this.tbMarkdownInstanceComponentRef.location.nativeElement);
readyObservable = this.handleImages(this.tbMarkdownInstanceComponentRef.location.nativeElement); readyObservable = this.handleImages(this.tbMarkdownInstanceComponentRef.location.nativeElement);
this.cd.detectChanges();
this.error = null; this.error = null;
} catch (error) { } catch (error) {
readyObservable = this.handleError(compiled, error); readyObservable = this.handleError(compiled, error);
} }
this.cd.detectChanges();
readyObservable.subscribe(() => { readyObservable.subscribe(() => {
this.ready.emit(); this.ready.emit();
}); });
@ -146,9 +146,8 @@ export class TbMarkdownComponent implements OnChanges {
private handleError(template: string, error): Observable<void> { private handleError(template: string, error): Observable<void> {
this.error = (error ? error + '' : 'Failed to render markdown!').replace(/\n/g, '<br>'); this.error = (error ? error + '' : 'Failed to render markdown!').replace(/\n/g, '<br>');
this.destroyMarkdownInstanceResources(); this.markdownContainer.clear();
if (this.fallbackToPlainMarkdownValue) { if (this.fallbackToPlainMarkdownValue) {
this.markdownContainer.clear();
const element = this.fallbackElement.nativeElement; const element = this.fallbackElement.nativeElement;
element.innerHTML = template; element.innerHTML = template;
this.handlePlugins(element); this.handlePlugins(element);

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

Loading…
Cancel
Save