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
);
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);
}
public TopicPartitionInfo resolve(ServiceType serviceType, QueueId queueId, TenantId tenantId, EntityId entityId) {
return partitionService.resolve(serviceType, queueId, tenantId, entityId);
}
@Deprecated
public TopicPartitionInfo resolve(ServiceType serviceType, String queueName, TenantId tenantId, EntityId entityId) {
return partitionService.resolve(serviceType, tenantId, entityId, queueName);
return partitionService.resolve(serviceType, queueName, tenantId, entityId);
}
public String getServiceId() {

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

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

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

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

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));
}
@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')")
@RequestMapping(value = "/queues", params = {"serviceType"}, method = RequestMethod.POST)
@ResponseBody

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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.SaveOtaPackageInfoRequest;
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.audit.ActionType;
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.page.PageData;
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.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 java.util.ArrayList;
@ -389,55 +381,6 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
.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
public void testSaveDeviceProfileWithFirmwareFromDifferentTenant() throws Exception {
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.getProfileData(), savedTenantProfile.getProfileData());
Assert.assertEquals(tenantProfile.isDefault(), savedTenantProfile.isDefault());
Assert.assertEquals(tenantProfile.isIsolatedTbCore(), savedTenantProfile.isIsolatedTbCore());
Assert.assertEquals(tenantProfile.isIsolatedTbRuleEngine(), savedTenantProfile.isIsolatedTbRuleEngine());
testBroadcastEntityStateChangeEventTimeManyTimeTenantProfile(savedTenantProfile, ComponentLifecycleEvent.CREATED, 1);
@ -182,22 +181,6 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController
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
public void testDeleteTenantProfileWithExistingTenant() throws Exception {
loginSysAdmin();
@ -352,7 +335,6 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController
tenantProfileData.setConfiguration(new DefaultTenantProfileConfiguration());
tenantProfile.setProfileData(tenantProfileData);
tenantProfile.setDefault(false);
tenantProfile.setIsolatedTbCore(false);
tenantProfile.setIsolatedTbRuleEngine(false);
return tenantProfile;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -273,7 +273,6 @@ message GetTenantRoutingInfoRequestMsg {
}
message GetTenantRoutingInfoResponseMsg {
bool isolatedTbCore = 1;
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;
@ApiModelProperty(position = 6, value = "Reference to the dashboard. Used in the mobile application to open the default dashboard when user navigates to device details.")
private DashboardId defaultDashboardId;
@NoXss
@ApiModelProperty(position = 8, value = "Reference to the rule engine queue. " +
@ApiModelProperty(position = 8, value = "Rule engine queue name. " +
"If present, the specified queue will be used to store all unprocessed messages related to device, including telemetry, attribute updates, etc. " +
"Otherwise, the 'Main' queue will be used to store those messages.")
private QueueId defaultQueueId;
private String defaultQueueName;
@Valid
private transient DeviceProfileData profileData;
@ -113,7 +112,7 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
this.isDefault = deviceProfile.isDefault();
this.defaultRuleChainId = deviceProfile.getDefaultRuleChainId();
this.defaultDashboardId = deviceProfile.getDefaultDashboardId();
this.defaultQueueId = deviceProfile.getDefaultQueueId();
this.defaultQueueName = deviceProfile.getDefaultQueueName();
this.setProfileData(deviceProfile.getProfileData());
this.provisionDeviceKey = deviceProfile.getProvisionDeviceKey();
this.firmwareId = deviceProfile.getFirmwareId();
@ -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;
@ApiModelProperty(position = 5, value = "Default Tenant profile to be used.", example = "true")
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. " +
"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. " +
@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 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;
@JsonIgnore
private byte[] profileDataBytes;
@ -77,7 +74,6 @@ public class TenantProfile extends SearchTextBased<TenantProfileId> implements H
this.name = tenantProfile.getName();
this.description = tenantProfile.getDescription();
this.isDefault = tenantProfile.isDefault();
this.isolatedTbCore = tenantProfile.isIsolatedTbCore();
this.isolatedTbRuleEngine = tenantProfile.isIsolatedTbRuleEngine();
this.setProfileData(tenantProfile.getProfileData());
}

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

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

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

@ -43,7 +43,7 @@ import java.util.UUID;
@Slf4j
public final class TbMsg implements Serializable {
private final QueueId queueId;
private final String queueName;
private final UUID id;
private final long ts;
private final String type;
@ -67,12 +67,12 @@ public final class TbMsg implements Serializable {
return ctx.getAndIncrementRuleNodeCounter();
}
public static TbMsg newMsg(QueueId queueId, String type, EntityId originator, TbMsgMetaData metaData, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) {
return newMsg(queueId, type, originator, null, metaData, data, ruleChainId, ruleNodeId);
public static TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data, RuleChainId ruleChainId, RuleNodeId 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) {
return new TbMsg(queueId, UUID.randomUUID(), System.currentTimeMillis(), type, originator, customerId,
public static TbMsg newMsg(String queueName, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) {
return new TbMsg(queueName, UUID.randomUUID(), System.currentTimeMillis(), type, originator, customerId,
metaData.copy(), TbMsgDataType.JSON, data, ruleChainId, ruleNodeId, null, TbMsgCallback.EMPTY);
}
@ -87,12 +87,12 @@ public final class TbMsg implements Serializable {
// REALLY NEW MSG
public static TbMsg newMsg(QueueId queueId, String type, EntityId originator, TbMsgMetaData metaData, String data) {
return newMsg(queueId, type, originator, null, metaData, data);
public static TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String 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) {
return new TbMsg(queueId, UUID.randomUUID(), System.currentTimeMillis(), type, originator, customerId,
public static TbMsg newMsg(String queueName, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) {
return new TbMsg(queueName, UUID.randomUUID(), System.currentTimeMillis(), type, originator, customerId,
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) {
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);
}
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());
}
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());
}
public static TbMsg transformMsg(TbMsg tbMsg, QueueId queueId) {
return new TbMsg(queueId, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType,
public static TbMsg transformMsg(TbMsg tbMsg, String queueName) {
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());
}
public static TbMsg transformMsg(TbMsg tbMsg, RuleChainId ruleChainId, QueueId queueId) {
return new TbMsg(queueId, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType,
public static TbMsg transformMsg(TbMsg tbMsg, RuleChainId ruleChainId, String queueName) {
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());
}
//used for enqueueForTellNext
public static TbMsg newMsg(TbMsg tbMsg, QueueId queueId, RuleChainId ruleChainId, RuleNodeId ruleNodeId) {
return new TbMsg(queueId, UUID.randomUUID(), tbMsg.getTs(), tbMsg.getType(), tbMsg.getOriginator(), tbMsg.customerId, tbMsg.getMetaData().copy(),
public static TbMsg newMsg(TbMsg tbMsg, String queueName, RuleChainId ruleChainId, RuleNodeId ruleNodeId) {
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);
}
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) {
this.id = id;
this.queueId = queueId;
this.queueName = queueName;
if (ts > 0) {
this.ts = ts;
} else {
@ -220,7 +220,7 @@ public final class TbMsg implements Serializable {
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 {
MsgProtos.TbMsgProto proto = MsgProtos.TbMsgProto.parseFrom(data);
TbMsgMetaData metaData = new TbMsgMetaData(proto.getMetaData().getDataMap());
@ -247,7 +247,7 @@ public final class TbMsg implements Serializable {
}
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);
} catch (InvalidProtocolBufferException 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) {
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);
}
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);
}

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 QueueRoutingInfoService queueRoutingInfoService;
private final ConcurrentMap<QueueId, QueueRoutingInfo> queuesById = new ConcurrentHashMap<>();
private ConcurrentMap<QueueKey, List<Integer>> myPartitions = 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);
partitionTopicsMap.put(queueKey, queue.getQueueTopic());
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) {
TenantId tenantId = new TenantId(new UUID(queueUpdateMsg.getTenantIdMSB(), queueUpdateMsg.getTenantIdLSB()));
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());
partitionSizesMap.put(queueKey, queueUpdateMsg.getPartitions());
myPartitions.remove(queueKey);
@ -176,7 +171,6 @@ public class HashPartitionService implements PartitionService {
public void removeQueue(TransportProtos.QueueDeleteMsg queueDeleteMsg) {
TenantId tenantId = new TenantId(new UUID(queueDeleteMsg.getTenantIdMSB(), queueDeleteMsg.getTenantIdLSB()));
QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, queueDeleteMsg.getQueueName(), tenantId);
queuesById.remove(new QueueId(new UUID(queueDeleteMsg.getQueueIdMSB(), queueDeleteMsg.getQueueIdLSB())));
myPartitions.remove(queueKey);
partitionTopicsMap.remove(queueKey);
partitionSizesMap.remove(queueKey);
@ -185,9 +179,7 @@ public class HashPartitionService implements PartitionService {
}
@Override
@Deprecated
public TopicPartitionInfo resolve(ServiceType serviceType, TenantId tenantId, EntityId entityId, String queueName) {
log.warn("This method is deprecated and will be removed!!!");
public TopicPartitionInfo resolve(ServiceType serviceType, String queueName, TenantId tenantId, EntityId entityId) {
TenantId isolatedOrSystemTenantId = getIsolatedOrSystemTenantId(serviceType, tenantId);
QueueKey queueKey = new QueueKey(serviceType, queueName, isolatedOrSystemTenantId);
if (!partitionSizesMap.containsKey(queueKey)) {
@ -201,32 +193,6 @@ public class HashPartitionService implements PartitionService {
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) {
int hash = hashFunction.newHasher()
.putLong(entityId.getId().getMostSignificantBits())
@ -399,8 +365,6 @@ public class HashPartitionService implements PartitionService {
throw new RuntimeException("Tenant not found!");
}
switch (serviceType) {
case TB_CORE:
return routingInfo.isIsolatedTbCore();
case TB_RULE_ENGINE:
return routingInfo.isIsolatedTbRuleEngine();
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 {
@Deprecated
TopicPartitionInfo resolve(ServiceType serviceType, TenantId tenantId, EntityId entityId, String queueName);
TopicPartitionInfo resolve(ServiceType serviceType, String queueName, 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.
* @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
public class TenantRoutingInfo {
private final TenantId tenantId;
private final boolean isolatedTbCore;
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) {
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()) {
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()));
DeviceProfile deviceProfile = deviceProfileCache.get(deviceProfileId);
RuleChainId ruleChainId;
QueueId queueId;
String queueName;
if (deviceProfile == null) {
log.warn("[{}] Device profile is null!", deviceProfileId);
ruleChainId = null;
queueId = null;
queueName = null;
} else {
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);
}

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
public TenantRoutingInfo getRoutingInfo(TenantId 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);
}
public ListenableFuture<?> executeAsync(Runnable task) {
return service.submit(task);
}
@Override
public void execute(Runnable 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);
default ListenableFuture<?> executeAsync(Runnable task) {
return executeAsync(() -> {
task.run();
return null;
});
}
default <T> ListenableFuture<T> submit(Callable<T> 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());
if (e.isPresent()) {
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 {
if (enrichFunction != null) {
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 savedDeviceProfile;
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);
publishEvictEvent(new DeviceProfileEvictEvent(savedDeviceProfile.getTenantId(), savedDeviceProfile.getName(),
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_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_NAME_PROPERTY = "default_queue_name";
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_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)
private UUID defaultDashboardId;
@Column(name = ModelConstants.DEVICE_PROFILE_DEFAULT_QUEUE_ID_PROPERTY)
private UUID defaultQueueId;
@Column(name = ModelConstants.DEVICE_PROFILE_DEFAULT_QUEUE_NAME_PROPERTY)
private String defaultQueueName;
@Type(type = "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) {
this.defaultDashboardId = deviceProfile.getDefaultDashboardId().getId();
}
if (deviceProfile.getDefaultQueueId() != null) {
this.defaultQueueId = deviceProfile.getDefaultQueueId().getId();
}
this.defaultQueueName = deviceProfile.getDefaultQueueName();
this.provisionDeviceKey = deviceProfile.getProvisionDeviceKey();
if (deviceProfile.getFirmwareId() != null) {
this.firmwareId = deviceProfile.getFirmwareId().getId();
@ -176,6 +174,7 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl
deviceProfile.setProvisionType(provisionType);
deviceProfile.setDescription(description);
deviceProfile.setDefault(isDefault);
deviceProfile.setDefaultQueueName(defaultQueueName);
deviceProfile.setProfileData(JacksonUtil.convertValue(profileData, DeviceProfileData.class));
if (defaultRuleChainId != null) {
deviceProfile.setDefaultRuleChainId(new RuleChainId(defaultRuleChainId));
@ -183,15 +182,11 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl
if (defaultDashboardId != null) {
deviceProfile.setDefaultDashboardId(new DashboardId(defaultDashboardId));
}
if (defaultQueueId != null) {
deviceProfile.setDefaultQueueId(new QueueId(defaultQueueId));
}
deviceProfile.setProvisionDeviceKey(provisionDeviceKey);
if (firmwareId != null) {
deviceProfile.setFirmwareId(new OtaPackageId(firmwareId));
}
if (softwareId != null) {
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)
private boolean isDefault;
@Column(name = ModelConstants.TENANT_PROFILE_ISOLATED_TB_CORE)
private boolean isolatedTbCore;
@Column(name = ModelConstants.TENANT_PROFILE_ISOLATED_TB_RULE_ENGINE)
private boolean isolatedTbRuleEngine;
@ -75,7 +72,6 @@ public final class TenantProfileEntity extends BaseSqlEntity<TenantProfile> impl
this.name = tenantProfile.getName();
this.description = tenantProfile.getDescription();
this.isDefault = tenantProfile.isDefault();
this.isolatedTbCore = tenantProfile.isIsolatedTbCore();
this.isolatedTbRuleEngine = tenantProfile.isIsolatedTbRuleEngine();
this.profileData = JacksonUtil.convertValue(tenantProfile.getProfileData(), ObjectNode.class);
}
@ -101,7 +97,6 @@ public final class TenantProfileEntity extends BaseSqlEntity<TenantProfile> impl
tenantProfile.setName(name);
tenantProfile.setDescription(description);
tenantProfile.setDefault(isDefault);
tenantProfile.setIsolatedTbCore(isolatedTbCore);
tenantProfile.setIsolatedTbRuleEngine(isolatedTbRuleEngine);
tenantProfile.setProfileData(JacksonUtil.convertValue(profileData, TenantProfileData.class));
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;
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
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!");
}
}
if (deviceProfile.getDefaultQueueId() != null) {
Queue queue = queueService.findQueueById(tenantId, deviceProfile.getDefaultQueueId());
if (StringUtils.isNotEmpty(deviceProfile.getDefaultQueueName())) {
Queue queue = queueService.findQueueByTenantIdAndName(tenantId, deviceProfile.getDefaultQueueName());
if (queue == null) {
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) {
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!");
} else if (old.isIsolatedTbRuleEngine() != tenantProfile.isIsolatedTbRuleEngine()) {
throw new DataValidationException("Can't update isolatedTbRuleEngine property!");
} else if (old.isIsolatedTbCore() != tenantProfile.isIsolatedTbCore()) {
throw new DataValidationException("Can't update isolatedTbCore property!");
}
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());
defaultTenantProfile.setProfileData(profileData);
defaultTenantProfile.setDescription("Default tenant profile");
defaultTenantProfile.setIsolatedTbCore(false);
defaultTenantProfile.setIsolatedTbRuleEngine(false);
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
@Transactional(timeout = 60 * 60)
public void deleteTenant(TenantId tenantId) {
log.trace("Executing deleteTenant [{}]", 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_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,
default_rule_chain_id uuid,
default_dashboard_id uuid,
default_queue_id uuid,
default_queue_name varchar(255),
provision_device_key varchar,
external_id uuid,
CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name),
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_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_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.setName("Isolated TB Rule Engine");
tenantProfile.setDescription("Isolated TB Rule Engine tenant profile");
tenantProfile.setIsolatedTbCore(false);
tenantProfile.setIsolatedTbRuleEngine(true);
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.getProfileData(), savedTenantProfile.getProfileData());
Assert.assertEquals(tenantProfile.isDefault(), savedTenantProfile.isDefault());
Assert.assertEquals(tenantProfile.isIsolatedTbCore(), savedTenantProfile.isIsolatedTbCore());
Assert.assertEquals(tenantProfile.isIsolatedTbRuleEngine(), savedTenantProfile.isIsolatedTbRuleEngine());
savedTenantProfile.setName("New tenant profile");
@ -177,14 +176,6 @@ public abstract class BaseTenantProfileServiceTest extends AbstractServiceTest {
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)
public void testDeleteTenantProfileWithExistingTenant() {
TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile");
@ -298,7 +289,6 @@ public abstract class BaseTenantProfileServiceTest extends AbstractServiceTest {
profileData.setConfiguration(new DefaultTenantProfileConfiguration());
tenantProfile.setProfileData(profileData);
tenantProfile.setDefault(false);
tenantProfile.setIsolatedTbCore(false);
tenantProfile.setIsolatedTbRuleEngine(false);
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());
tenantProfile.setProfileData(profileData);
tenantProfile.setDefault(false);
tenantProfile.setIsolatedTbCore(true);
tenantProfile.setIsolatedTbRuleEngine(true);
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
audit-log.enabled=true
audit-log.by_tenant_partitioning=MONTHS
audit-log.default_query_period=30
audit-log.sink.type=none
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
public TenantRoutingInfo getRoutingInfo(TenantId tenantId) {
//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 send(TenantId tenantId, CustomerId customerId, TbEmail tbEmail) throws ThingsboardException;
void send(TenantId tenantId, CustomerId customerId, TbEmail tbEmail, JavaMailSender javaMailSender) throws ThingsboardException;
void send(TenantId tenantId, CustomerId customerId, TbEmail tbEmail, JavaMailSender javaMailSender, long timeout) 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);
@Deprecated
void enqueue(TbMsg tbMsg, String queueName, Runnable onSuccess, Consumer<Throwable> onFailure);
/**
* Puts new message to custom queue for processing
*
* @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);
@ -162,15 +159,15 @@ public interface TbContext {
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);
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);

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) {
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) {

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();
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);
scheduleTickMsg(ctx, tbMsg);
} 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 {
private QueueId queueId;
private String queueName;
@Override
public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
TbCheckpointNodeConfiguration config = TbNodeUtils.convert(configuration, TbCheckpointNodeConfiguration.class);
this.queueId = new QueueId(UUID.fromString(config.getQueueId()));
this.queueName = config.getQueueName();
}
@Override
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

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
public class TbCheckpointNodeConfiguration implements NodeConfiguration<TbCheckpointNodeConfiguration> {
private String queueId;
private String queueName;
@Override
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()) {
ctx.getMailService(true).send(ctx.getTenantId(), msg.getCustomerId(), email);
} 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 boolean initialFetchDone;
private volatile TbMsgMetaData lastMsgMetaData;
private volatile QueueId lastMsgQueueId;
private volatile String lastMsgQueueName;
private volatile DataSnapshot dataSnapshot;
private final DynamicPredicateValueCtx dynamicPredicateValueCtx;
@ -74,7 +74,7 @@ class AlarmState {
public boolean process(TbContext ctx, TbMsg msg, DataSnapshot data, SnapshotUpdate update) throws ExecutionException, InterruptedException {
initCurrentAlarm(ctx);
lastMsgMetaData = msg.getMetaData();
lastMsgQueueId = msg.getQueueId();
lastMsgQueueName = msg.getQueueName();
this.dataSnapshot = data;
try {
return createOrClearAlarms(ctx, msg, data, update, AlarmRuleState::eval);
@ -195,7 +195,7 @@ class AlarmState {
metaData.putValue(DataConstants.IS_CLEARED_ALARM, Boolean.TRUE.toString());
}
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);
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 -> {
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);
} 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());
}
});

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.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import java.util.Arrays;
@SpringBootConfiguration
@EnableAsync
@EnableAutoConfiguration
@EnableScheduling
@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 {

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));
}
public getQueueByName(queueName: string, config?: RequestConfig): Observable<QueueInfo> {
return this.http.get<QueueInfo>(`/api/queues/name/${queueName}`, defaultHttpOptionsFromConfig(config));
}
public getTenantQueuesByServiceType(pageLink: PageLink,
serviceType: ServiceType,
config?: RequestConfig): Observable<PageData<QueueInfo>> {

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

@ -30,7 +30,7 @@ import {
WidgetLayout
} from '@shared/models/dashboard.models';
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 { AliasFilterType, EntityAlias, EntityAliasFilter } from '@app/shared/models/alias.models';
import { EntityId } from '@app/shared/models/id/entity-id';
@ -106,7 +106,8 @@ export class DashboardUtilsService {
const targetDevicesByAliasId: {[aliasId: string]: Array<Array<string>>} = {};
for (const widgetId of Object.keys(dashboard.configuration.widgets)) {
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) {
const aliasId = datasource.entityAliasId;
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 { Dashboard, DashboardLayoutId } from '@app/shared/models/dashboard.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 { deepClone, isEqual } from '@core/utils';
import { UtilsService } from '@core/services/utils.service';
@ -87,12 +94,13 @@ export class ItemBufferService {
};
const originalColumns = this.getOriginalColumns(dashboard, sourceState, sourceLayout);
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
&& dashboard.configuration.entityAliases) {
let entityAlias: EntityAlias;
if (widget.config.datasources) {
for (let i = 0; i < widget.config.datasources.length; i++) {
const datasource = widget.config.datasources[i];
if (datasources) {
for (let i = 0; i < datasources.length; i++) {
const datasource = datasources[i];
if ((datasource.type === DatasourceType.entity || datasource.type === DatasourceType.entityCount) && datasource.entityAliasId) {
entityAlias = dashboard.configuration.entityAliases[datasource.entityAliasId];
if (entityAlias) {
@ -116,9 +124,9 @@ export class ItemBufferService {
if (widget.config && dashboard.configuration
&& dashboard.configuration.filters) {
let filter: Filter;
if (widget.config.datasources) {
for (let i = 0; i < widget.config.datasources.length; i++) {
const datasource = widget.config.datasources[i];
if (datasources) {
for (let i = 0; i < datasources.length; i++) {
const datasource = datasources[i];
if ((datasource.type === DatasourceType.entity || datasource.type === DatasourceType.entityCount) && datasource.filterId) {
filter = dashboard.configuration.filters[datasource.filterId];
if (filter) {
@ -438,7 +446,11 @@ export class ItemBufferService {
const datasourceIndex = Number(datasourceIndexStr);
aliasInfo = aliasesInfo.datasourceAliases[datasourceIndex];
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)) {
const targetDeviceAliasIndex = Number(targetDeviceAliasIndexStr);
@ -457,7 +469,11 @@ export class ItemBufferService {
const datasourceIndex = Number(datasourceIndexStr);
filterInfo = filtersInfo.datasourceFilters[datasourceIndex];
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;
}

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 {
return isDefined(tenantProfile.name)
&& isDefined(tenantProfile.profileData)
&& isDefined(tenantProfile.isolatedTbCore)
&& 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-queue-autocomplete
[queueType]="serviceType"
formControlName="defaultQueueId">
formControlName="defaultQueueName">
</tb-queue-autocomplete>
<mat-form-field fxHide class="mat-block">
<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 { ServiceType } from '@shared/models/queue.models';
import { DashboardId } from '@shared/models/id/dashboard-id';
import { QueueId } from '@shared/models/id/queue-id';
export interface AddDeviceProfileDialogData {
deviceProfileName: string;
@ -111,7 +110,7 @@ export class AddDeviceProfileDialogComponent extends
image: [null, []],
defaultRuleChainId: [null, []],
defaultDashboardId: [null, []],
defaultQueueId: [null, []],
defaultQueueName: [null, []],
description: ['', []]
}
);
@ -188,6 +187,7 @@ export class AddDeviceProfileDialogComponent extends
name: this.deviceProfileDetailsFormGroup.get('name').value,
type: this.deviceProfileDetailsFormGroup.get('type').value,
image: this.deviceProfileDetailsFormGroup.get('image').value,
defaultQueueName: this.deviceProfileDetailsFormGroup.get('defaultQueueName').value,
transportType: this.transportConfigFormGroup.get('transportType').value,
provisionType: deviceProvisionConfiguration.type,
provisionDeviceKey,
@ -205,9 +205,6 @@ export class AddDeviceProfileDialogComponent extends
if (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(
(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-queue-autocomplete
[queueType]="serviceType"
formControlName="defaultQueueId">
formControlName="defaultQueueName">
</tb-queue-autocomplete>
<tb-ota-package-autocomplete
[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 { OtaUpdateType } from '@shared/models/ota-package.models';
import { DashboardId } from '@shared/models/id/dashboard-id';
import { QueueId } from '@shared/models/id/queue-id';
@Component({
selector: 'tb-device-profile',
@ -118,7 +117,7 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
}),
defaultRuleChainId: [entity && entity.defaultRuleChainId ? entity.defaultRuleChainId.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],
softwareId: [entity ? entity.softwareId : null],
description: [entity ? entity.description : '', []],
@ -198,7 +197,7 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
}}, {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({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({softwareId: entity.softwareId}, {emitEvent: false});
this.entityForm.patchValue({description: entity.description}, {emitEvent: false});
@ -211,9 +210,6 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
if (formValue.defaultDashboardId) {
formValue.defaultDashboardId = new DashboardId(formValue.defaultDashboardId);
}
if (formValue.defaultQueueId) {
formValue.defaultQueueId = new QueueId(formValue.defaultQueueId);
}
const deviceProvisionConfiguration: DeviceProvisionConfiguration = formValue.profileData.provisionConfiguration;
formValue.provisionType = deviceProvisionConfiguration.type;
formValue.provisionDeviceKey = deviceProvisionConfiguration.provisionDeviceKey;

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

@ -59,10 +59,6 @@
</mat-error>
</mat-form-field>
<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">
<div>{{ 'tenant.isolated-tb-rule-engine' | 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(
{
name: [entity ? entity.name : '', [Validators.required, Validators.maxLength(255)]],
isolatedTbCore: [entity ? entity.isolatedTbCore : false, []],
isolatedTbRuleEngine: [entity ? entity.isolatedTbRuleEngine : false, []],
profileData: this.fb.group({
configuration: [entity && !this.isAdd ? entity?.profileData.configuration
@ -107,7 +106,6 @@ export class TenantProfileComponent extends EntityComponent<TenantProfile> {
updateForm(entity: TenantProfile) {
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.get('profileData').patchValue({
configuration: !this.isAdd ? entity.profileData?.configuration : createTenantProfileConfiguration(TenantProfileType.DEFAULT)
@ -121,7 +119,6 @@ export class TenantProfileComponent extends EntityComponent<TenantProfile> {
if (this.isEditValue) {
this.entityForm.enable({emitEvent: false});
if (!this.isAdd) {
this.entityForm.get('isolatedTbCore').disable({emitEvent: false});
this.entityForm.get('isolatedTbRuleEngine').disable({emitEvent: false});
}
} else {

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

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

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

@ -25,7 +25,8 @@
'tb-highlighted': isHighlighted(widget),
'tb-not-highlighted': isNotHighlighted(widget),
'mat-elevation-z4': widget.dropShadow,
'tb-has-timewindow': widget.hasTimewindow
'tb-has-timewindow': widget.hasTimewindow,
'tb-edit': isEdit
}"
[ngStyle]="widget.style"
(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 {
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
[ngClass]="{invisible: deviceWizardFormGroup.get('addProfileType').value !== 1}"
[queueType]="serviceType"
formControlName="defaultQueueId">
formControlName="defaultQueueName">
</tb-queue-autocomplete>
</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 { ServiceType } from '@shared/models/queue.models';
import { deepTrim } from '@core/utils';
import { QueueId } from '@shared/models/id/queue-id';
@Component({
selector: 'tb-device-wizard',
@ -114,7 +113,7 @@ export class DeviceWizardDialogComponent extends
deviceProfileId: [null, Validators.required],
newDeviceProfileTitle: [{value: null, disabled: true}],
defaultRuleChainId: [{value: null, disabled: true}],
defaultQueueId: [{value: null, disabled: true}],
defaultQueueName: [{value: null, disabled: true}],
description: ['']
}
);
@ -127,7 +126,7 @@ export class DeviceWizardDialogComponent extends
this.deviceWizardFormGroup.get('newDeviceProfileTitle').setValidators(null);
this.deviceWizardFormGroup.get('newDeviceProfileTitle').disable();
this.deviceWizardFormGroup.get('defaultRuleChainId').disable();
this.deviceWizardFormGroup.get('defaultQueueId').disable();
this.deviceWizardFormGroup.get('defaultQueueName').disable();
this.deviceWizardFormGroup.updateValueAndValidity();
this.createProfile = false;
} else {
@ -136,7 +135,7 @@ export class DeviceWizardDialogComponent extends
this.deviceWizardFormGroup.get('newDeviceProfileTitle').setValidators([Validators.required]);
this.deviceWizardFormGroup.get('newDeviceProfileTitle').enable();
this.deviceWizardFormGroup.get('defaultRuleChainId').enable();
this.deviceWizardFormGroup.get('defaultQueueId').enable();
this.deviceWizardFormGroup.get('defaultQueueName').enable();
this.deviceWizardFormGroup.updateValueAndValidity();
this.createProfile = true;
@ -298,6 +297,7 @@ export class DeviceWizardDialogComponent extends
const deviceProfile: DeviceProfile = {
name: this.deviceWizardFormGroup.get('newDeviceProfileTitle').value,
type: DeviceProfileType.DEFAULT,
defaultQueueName: this.deviceWizardFormGroup.get('defaultQueueName').value,
transportType: this.transportConfigFormGroup.get('transportType').value,
provisionType: deviceProvisionConfiguration.type,
provisionDeviceKey,
@ -311,9 +311,6 @@ export class DeviceWizardDialogComponent extends
if (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(
tap((profile) => {
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.style = {cursor: 'pointer',
this.style = {
color: this.color,
backgroundColor: this.backgroundColor,
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.markdownService.highlight(this.tbMarkdownInstanceComponentRef.location.nativeElement);
readyObservable = this.handleImages(this.tbMarkdownInstanceComponentRef.location.nativeElement);
this.cd.detectChanges();
this.error = null;
} catch (error) {
readyObservable = this.handleError(compiled, error);
}
this.cd.detectChanges();
readyObservable.subscribe(() => {
this.ready.emit();
});
@ -146,9 +146,8 @@ export class TbMarkdownComponent implements OnChanges {
private handleError(template: string, error): Observable<void> {
this.error = (error ? error + '' : 'Failed to render markdown!').replace(/\n/g, '<br>');
this.destroyMarkdownInstanceResources();
this.markdownContainer.clear();
if (this.fallbackToPlainMarkdownValue) {
this.markdownContainer.clear();
const element = this.fallbackElement.nativeElement;
element.innerHTML = template;
this.handlePlugins(element);

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

Loading…
Cancel
Save