Browse Source

Merge remote-tracking branch 'upstream/develop/3.4' into js-timeout-fix

pull/6905/head
Volodymyr Babak 4 years ago
parent
commit
5ec38f9bcc
  1. 6
      application/src/main/resources/thingsboard.yml
  2. 172
      application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java
  3. 2
      application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java
  4. 1
      application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java
  5. 75
      application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java
  6. 142
      application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java
  7. 6
      application/src/test/java/org/thingsboard/server/controller/BaseEntityRelationControllerTest.java
  8. 152
      application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java
  9. 108
      application/src/test/java/org/thingsboard/server/controller/BaseOtaPackageControllerTest.java
  10. 68
      application/src/test/java/org/thingsboard/server/controller/BaseRuleChainControllerTest.java
  11. 78
      application/src/test/java/org/thingsboard/server/controller/BaseTbResourceControllerTest.java
  12. 82
      application/src/test/java/org/thingsboard/server/controller/BaseTenantControllerTest.java
  13. 112
      application/src/test/java/org/thingsboard/server/controller/BaseTenantProfileControllerTest.java
  14. 2
      application/src/test/java/org/thingsboard/server/queue/discovery/HashPartitionServiceTest.java
  15. 23
      common/queue/src/main/java/org/thingsboard/server/queue/azure/servicebus/TbServiceBusQueueConfigs.java
  16. 10
      common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaTopicConfigs.java
  17. 9
      common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsMonolithQueueFactory.java
  18. 21
      common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbCoreQueueFactory.java
  19. 8
      common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbRuleEngineQueueFactory.java
  20. 15
      common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubSubscriptionSettings.java
  21. 23
      common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqQueueArguments.java
  22. 33
      common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsQueueAttributes.java
  23. 4
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
  24. 66
      dao/src/main/java/org/thingsboard/server/dao/service/validator/AbstractHasOtaPackageValidator.java
  25. 41
      dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceDataValidator.java
  26. 53
      dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java
  27. 1
      dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java
  28. 5
      docker/.env
  29. 13
      docker/docker-compose.yml
  30. 85
      msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ContainerTestSuite.java
  31. 2
      msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ThingsBoardDbInstaller.java
  32. 69
      msa/black-box-tests/src/test/resources/docker-compose.rabbitmq-server.yml
  33. 31
      msa/js-executor/api/httpServer.ts
  34. 2
      msa/js-executor/package.json
  35. 144
      msa/js-executor/queue/awsSqsTemplate.ts
  36. 186
      msa/js-executor/queue/kafkaTemplate.ts
  37. 88
      msa/js-executor/queue/pubSubTemplate.ts
  38. 3
      msa/js-executor/queue/queue.models.ts
  39. 74
      msa/js-executor/queue/rabbitmqTemplate.ts
  40. 97
      msa/js-executor/queue/serviceBusTemplate.ts
  41. 79
      msa/js-executor/server.ts
  42. 8
      msa/js-executor/yarn.lock
  43. 21
      msa/vc-executor/src/main/resources/tb-vc-executor.yml
  44. 2
      msa/web-ui/server.ts
  45. 5
      transport/coap/src/main/resources/tb-coap-transport.yml
  46. 5
      transport/http/src/main/resources/tb-http-transport.yml
  47. 5
      transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml
  48. 5
      transport/mqtt/src/main/resources/tb-mqtt-transport.yml
  49. 5
      transport/snmp/src/main/resources/tb-snmp-transport.yml

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

@ -949,6 +949,7 @@ queue:
notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100;min.insync.replicas:1}" js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100;min.insync.replicas:1}"
ota-updates: "${TB_QUEUE_KAFKA_OTA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:10;min.insync.replicas:1}" ota-updates: "${TB_QUEUE_KAFKA_OTA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:10;min.insync.replicas:1}"
version-control: "${TB_QUEUE_KAFKA_VC_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:10;min.insync.replicas:1}"
consumer-stats: consumer-stats:
enabled: "${TB_QUEUE_KAFKA_CONSUMER_STATS_ENABLED:true}" enabled: "${TB_QUEUE_KAFKA_CONSUMER_STATS_ENABLED:true}"
print-interval-ms: "${TB_QUEUE_KAFKA_CONSUMER_STATS_MIN_PRINT_INTERVAL_MS:60000}" print-interval-ms: "${TB_QUEUE_KAFKA_CONSUMER_STATS_MIN_PRINT_INTERVAL_MS:60000}"
@ -965,6 +966,8 @@ queue:
transport-api: "${TB_QUEUE_AWS_SQS_TA_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" transport-api: "${TB_QUEUE_AWS_SQS_TA_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
notifications: "${TB_QUEUE_AWS_SQS_NOTIFICATIONS_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" notifications: "${TB_QUEUE_AWS_SQS_NOTIFICATIONS_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
js-executor: "${TB_QUEUE_AWS_SQS_JE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" js-executor: "${TB_QUEUE_AWS_SQS_JE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
ota-updates: "${TB_QUEUE_AWS_SQS_OTA_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
version-control: "${TB_QUEUE_AWS_SQS_VC_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
# VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds # VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds
pubsub: pubsub:
project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}" project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}"
@ -977,6 +980,7 @@ queue:
transport-api: "${TB_QUEUE_PUBSUB_TA_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" transport-api: "${TB_QUEUE_PUBSUB_TA_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
notifications: "${TB_QUEUE_PUBSUB_NOTIFICATIONS_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" notifications: "${TB_QUEUE_PUBSUB_NOTIFICATIONS_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
js-executor: "${TB_QUEUE_PUBSUB_JE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" js-executor: "${TB_QUEUE_PUBSUB_JE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
version-control: "${TB_QUEUE_PUBSUB_VC_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
service_bus: service_bus:
namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}" namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}"
sas_key_name: "${TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME:YOUR_SAS_KEY_NAME}" sas_key_name: "${TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME:YOUR_SAS_KEY_NAME}"
@ -988,6 +992,7 @@ queue:
transport-api: "${TB_QUEUE_SERVICE_BUS_TA_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" transport-api: "${TB_QUEUE_SERVICE_BUS_TA_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
notifications: "${TB_QUEUE_SERVICE_BUS_NOTIFICATIONS_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" notifications: "${TB_QUEUE_SERVICE_BUS_NOTIFICATIONS_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
js-executor: "${TB_QUEUE_SERVICE_BUS_JE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" js-executor: "${TB_QUEUE_SERVICE_BUS_JE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
version-control: "${TB_QUEUE_SERVICE_BUS_VC_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
rabbitmq: rabbitmq:
exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}" exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}"
host: "${TB_QUEUE_RABBIT_MQ_HOST:localhost}" host: "${TB_QUEUE_RABBIT_MQ_HOST:localhost}"
@ -1004,6 +1009,7 @@ queue:
transport-api: "${TB_QUEUE_RABBIT_MQ_TA_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" transport-api: "${TB_QUEUE_RABBIT_MQ_TA_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
notifications: "${TB_QUEUE_RABBIT_MQ_NOTIFICATIONS_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" notifications: "${TB_QUEUE_RABBIT_MQ_NOTIFICATIONS_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
js-executor: "${TB_QUEUE_RABBIT_MQ_JE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" js-executor: "${TB_QUEUE_RABBIT_MQ_JE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
version-control: "${TB_QUEUE_RABBIT_MQ_VC_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
partitions: partitions:
hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256
transport_api: transport_api:

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

@ -63,13 +63,14 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
protected final String msgErrorPermission = "You don't have permission to perform this operation!"; protected final String msgErrorPermission = "You don't have permission to perform this operation!";
protected final String msgErrorShouldBeSpecified = "should be specified"; protected final String msgErrorShouldBeSpecified = "should be specified";
protected final String msgErrorNotFound = "Requested item wasn't found!";
protected void testNotifyEntityAllOneTime(HasName entity, EntityId entityId, EntityId originatorId, protected void testNotifyEntityAllOneTime(HasName entity, EntityId entityId, EntityId originatorId,
TenantId tenantId, CustomerId customerId, UserId userId, String userName, TenantId tenantId, CustomerId customerId, UserId userId, String userName,
ActionType actionType, Object... additionalInfo) { ActionType actionType, Object... additionalInfo) {
int cntTime = 1; int cntTime = 1;
testSendNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime); testNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime);
testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo); testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo);
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId); ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime);
@ -85,25 +86,33 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
Mockito.eq(edgeTypeByActionType(actionType))); Mockito.eq(edgeTypeByActionType(actionType)));
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(relation.getTo()); ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(relation.getTo());
ArgumentMatcher<HasName> matcherEntityClassEquals = Objects::isNull; ArgumentMatcher<HasName> matcherEntityClassEquals = Objects::isNull;
testLogEntityActionAdditionalInfo(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, userName, actionType, cntTime, ArgumentMatcher<CustomerId> matcherCustomerId = customerId == null ?
argument -> argument.getClass().equals(CustomerId.class) : argument -> argument.equals(customerId);
ArgumentMatcher<UserId> matcherUserId = userId == null ?
argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId);
testLogEntityActionAdditionalInfo(matcherEntityClassEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName, actionType, cntTime,
extractMatcherAdditionalInfo(additionalInfo)); extractMatcherAdditionalInfo(additionalInfo));
testPushMsgToRuleEngineNever(relation.getTo()); testPushMsgToRuleEngineNever(relation.getTo());
matcherOriginatorId = argument -> argument.equals(relation.getFrom()); matcherOriginatorId = argument -> argument.equals(relation.getFrom());
testLogEntityActionAdditionalInfo(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, userName, actionType, cntTime, testLogEntityActionAdditionalInfo(matcherEntityClassEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName, actionType, cntTime,
extractMatcherAdditionalInfo(additionalInfo)); extractMatcherAdditionalInfo(additionalInfo));
testPushMsgToRuleEngineNever(relation.getFrom()); testPushMsgToRuleEngineNever(relation.getFrom());
Mockito.reset(tbClusterService, auditLogService); Mockito.reset(tbClusterService, auditLogService);
} }
protected void testNotifyEntityAllManyRelation(EntityRelation relation, protected void testNotifyEntityAllManyRelation(EntityRelation relation,
TenantId tenantId, CustomerId customerId, UserId userId, String userName, TenantId tenantId, CustomerId customerId, UserId userId, String userName,
ActionType actionType, int cntTime) { ActionType actionType, int cntTime) {
Mockito.verify(tbClusterService, times(cntTime)).sendNotificationMsgToEdge(Mockito.eq(tenantId), Mockito.verify(tbClusterService, times(cntTime)).sendNotificationMsgToEdge(Mockito.eq(tenantId),
Mockito.isNull(), Mockito.isNull(), Mockito.any(), Mockito.eq(EdgeEventType.RELATION), Mockito.isNull(), Mockito.isNull(), Mockito.any(), Mockito.eq(EdgeEventType.RELATION),
Mockito.eq(edgeTypeByActionType(actionType))); Mockito.eq(edgeTypeByActionType(actionType)));
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.getClass().equals(relation.getFrom().getClass()); ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.getClass().equals(relation.getFrom().getClass());
ArgumentMatcher<HasName> matcherEntityClassEquals = Objects::isNull; ArgumentMatcher<HasName> matcherEntityClassEquals = Objects::isNull;
testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, ArgumentMatcher<CustomerId> matcherCustomerId = customerId == null ?
argument -> argument.getClass().equals(CustomerId.class) : argument -> argument.equals(customerId);
ArgumentMatcher<UserId> matcherUserId = userId == null ?
argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId);
testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId,
userName, actionType, cntTime * 2, 1); userName, actionType, cntTime * 2, 1);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime);
Mockito.reset(tbClusterService, auditLogService); Mockito.reset(tbClusterService, auditLogService);
@ -113,7 +122,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
TenantId tenantId, CustomerId customerId, UserId userId, String userName, TenantId tenantId, CustomerId customerId, UserId userId, String userName,
ActionType actionType, Object... additionalInfo) { ActionType actionType, Object... additionalInfo) {
int cntTime = 1; int cntTime = 1;
testSendNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime); testNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime);
testLogEntityActionEntityEqClass(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo); testLogEntityActionEntityEqClass(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo);
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId); ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime);
@ -122,7 +131,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
protected void testNotifyEntityNeverMsgToEdgeServiceOneTime(HasName entity, EntityId entityId, TenantId tenantId, protected void testNotifyEntityNeverMsgToEdgeServiceOneTime(HasName entity, EntityId entityId, TenantId tenantId,
ActionType actionType) { ActionType actionType) {
testSendNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, 1); testNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, 1);
testLogEntityActionNever(entityId, entity); testLogEntityActionNever(entityId, entity);
testPushMsgToRuleEngineNever(entityId); testPushMsgToRuleEngineNever(entityId);
Mockito.reset(tbClusterService, auditLogService); Mockito.reset(tbClusterService, auditLogService);
@ -132,7 +141,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
TenantId tenantId, CustomerId customerId, UserId userId, TenantId tenantId, CustomerId customerId, UserId userId,
String userName, ActionType actionType, Object... additionalInfo) { String userName, ActionType actionType, Object... additionalInfo) {
int cntTime = 1; int cntTime = 1;
testNotificationMsgToEdgeServiceNever(entityId); testNotificationMsgToEdgeServiceNeverWithActionType(entityId, actionType);
testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo); testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo);
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId); ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId);
if (ActionType.RELATIONS_DELETED.equals(actionType)) { if (ActionType.RELATIONS_DELETED.equals(actionType)) {
@ -148,10 +157,14 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
ActionType actionType, int cntTime, Object... additionalInfo) { ActionType actionType, int cntTime, Object... additionalInfo) {
EntityId entityId = createEntityId_NULL_UUID(entity); EntityId entityId = createEntityId_NULL_UUID(entity);
EntityId originatorId = createEntityId_NULL_UUID(originator); EntityId originatorId = createEntityId_NULL_UUID(originator);
testNotificationMsgToEdgeServiceNever(entityId); testNotificationMsgToEdgeServiceNeverWithActionType(entityId, actionType);
ArgumentMatcher<HasName> matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass()); ArgumentMatcher<HasName> matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass());
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass()); ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass());
testLogEntityActionAdditionalInfo(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, userName, actionType, cntTime, ArgumentMatcher<CustomerId> matcherCustomerId = customerId == null ?
argument -> argument.getClass().equals(CustomerId.class) : argument -> argument.equals(customerId);
ArgumentMatcher<UserId> matcherUserId = userId == null ?
argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId);
testLogEntityActionAdditionalInfo(matcherEntityClassEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName, actionType, cntTime,
extractMatcherAdditionalInfo(additionalInfo)); extractMatcherAdditionalInfo(additionalInfo));
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime);
Mockito.reset(tbClusterService, auditLogService); Mockito.reset(tbClusterService, auditLogService);
@ -165,10 +178,13 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
testSendNotificationMsgToEdgeServiceTimeEntityEqAny(tenantId, actionTypeEdge, cntTimeEdge); testSendNotificationMsgToEdgeServiceTimeEntityEqAny(tenantId, actionTypeEdge, cntTimeEdge);
ArgumentMatcher<HasName> matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass()); ArgumentMatcher<HasName> matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass());
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass()); ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass());
testLogEntityActionAdditionalInfo(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, userName, actionType, cntTime, ArgumentMatcher<CustomerId> matcherCustomerId = customerId == null ?
argument -> argument.getClass().equals(CustomerId.class) : argument -> argument.equals(customerId);
ArgumentMatcher<UserId> matcherUserId = userId == null ?
argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId);
testLogEntityActionAdditionalInfo(matcherEntityClassEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName, actionType, cntTime,
extractMatcherAdditionalInfoClass(additionalInfo)); extractMatcherAdditionalInfoClass(additionalInfo));
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTimeRuleEngine); testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTimeRuleEngine);
Mockito.reset(tbClusterService, auditLogService);
} }
protected void testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAnyAdditionalInfoAny(HasName entity, HasName originator, protected void testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAnyAdditionalInfoAny(HasName entity, HasName originator,
@ -178,7 +194,11 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
testSendNotificationMsgToEdgeServiceTimeEntityEqAny(tenantId, actionTypeEdge, cntTimeEdge); testSendNotificationMsgToEdgeServiceTimeEntityEqAny(tenantId, actionTypeEdge, cntTimeEdge);
ArgumentMatcher<HasName> matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass()); ArgumentMatcher<HasName> matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass());
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass()); ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass());
testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, userName, actionType, cntTime, ArgumentMatcher<CustomerId> matcherCustomerId = customerId == null ?
argument -> argument.getClass().equals(CustomerId.class) : argument -> argument.equals(customerId);
ArgumentMatcher<UserId> matcherUserId = userId == null ?
argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId);
testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName, actionType, cntTime,
cntAdditionalInfo); cntAdditionalInfo);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTimeEdge); testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTimeEdge);
Mockito.reset(tbClusterService, auditLogService); Mockito.reset(tbClusterService, auditLogService);
@ -189,10 +209,14 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
ActionType actionType, int cntTime, int cntAdditionalInfo) { ActionType actionType, int cntTime, int cntAdditionalInfo) {
EntityId entityId = createEntityId_NULL_UUID(entity); EntityId entityId = createEntityId_NULL_UUID(entity);
EntityId originatorId = createEntityId_NULL_UUID(originator); EntityId originatorId = createEntityId_NULL_UUID(originator);
testNotificationMsgToEdgeServiceNever(entityId); testNotificationMsgToEdgeServiceNeverWithActionType(entityId, actionType);
ArgumentMatcher<HasName> matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass()); ArgumentMatcher<HasName> matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass());
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass()); ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass());
testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, userName, actionType, cntTime, ArgumentMatcher<CustomerId> matcherCustomerId = customerId == null ?
argument -> argument.getClass().equals(CustomerId.class) : argument -> argument.equals(customerId);
ArgumentMatcher<UserId> matcherUserId = userId == null ?
argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId);
testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName, actionType, cntTime,
cntAdditionalInfo); cntAdditionalInfo);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime);
Mockito.reset(tbClusterService, auditLogService); Mockito.reset(tbClusterService, auditLogService);
@ -202,7 +226,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
TenantId tenantId, CustomerId customerId, UserId userId, String userName, TenantId tenantId, CustomerId customerId, UserId userId, String userName,
ActionType actionType, Object... additionalInfo) { ActionType actionType, Object... additionalInfo) {
int cntTime = 1; int cntTime = 1;
testSendNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime); testNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime);
testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo); testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo);
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId); ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime);
@ -214,7 +238,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
TenantId tenantId, CustomerId customerId, UserId userId, String userName, TenantId tenantId, CustomerId customerId, UserId userId, String userName,
ActionType actionType, Object... additionalInfo) { ActionType actionType, Object... additionalInfo) {
int cntTime = 1; int cntTime = 1;
testNotificationMsgToEdgeServiceNever(entityId); testNotificationMsgToEdgeServiceNeverWithActionType(entityId, actionType);
testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo); testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo);
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId); ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime);
@ -222,26 +246,32 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
Mockito.reset(tbClusterService, auditLogService); Mockito.reset(tbClusterService, auditLogService);
} }
protected void testNotifyEntityBroadcastEntityStateChangeEventManyMsgToEdgeServiceNever(HasName entity, HasName originator, protected void testNotifyEntityBroadcastEntityStateChangeEventMany(HasName entity, HasName originator,
TenantId tenantId, CustomerId customerId, UserId userId, String userName, TenantId tenantId, CustomerId customerId,
ActionType actionType, int cntTime, int cntAdditionalInfo) { UserId userId, String userName, ActionType actionType,
ActionType actionTypeEdge,
int cntTime, int cntTimeEdge, int cntTimeRuleEngine,
int cntAdditionalInfo) {
EntityId entityId = createEntityId_NULL_UUID(entity); EntityId entityId = createEntityId_NULL_UUID(entity);
EntityId originatorId = createEntityId_NULL_UUID(originator); EntityId originatorId = createEntityId_NULL_UUID(originator);
testNotificationMsgToEdgeServiceNever(entityId); testNotificationMsgToEdgeServiceTime(entityId, tenantId, actionTypeEdge, cntTimeEdge);
ArgumentMatcher<HasName> matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass()); ArgumentMatcher<HasName> matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass());
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass()); ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass());
testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, userName, actionType, cntTime, ArgumentMatcher<CustomerId> matcherCustomerId = customerId == null ?
argument -> argument.getClass().equals(CustomerId.class) : argument -> argument.equals(customerId);
ArgumentMatcher<UserId> matcherUserId = userId == null ?
argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId);
testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName, actionType, cntTime,
cntAdditionalInfo); cntAdditionalInfo);
testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTimeRuleEngine);
testBroadcastEntityStateChangeEventTime(entityId, tenantId, cntTime); testBroadcastEntityStateChangeEventTime(entityId, tenantId, cntTime);
Mockito.reset(tbClusterService, auditLogService);
} }
protected void testNotifyEntityMsgToEdgePushMsgToCoreOneTime(HasName entity, EntityId entityId, EntityId originatorId, protected void testNotifyEntityMsgToEdgePushMsgToCoreOneTime(HasName entity, EntityId entityId, EntityId originatorId,
TenantId tenantId, CustomerId customerId, UserId userId, String userName, TenantId tenantId, CustomerId customerId, UserId userId, String userName,
ActionType actionType, Object... additionalInfo) { ActionType actionType, Object... additionalInfo) {
int cntTime = 1; int cntTime = 1;
testSendNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime); testNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime);
testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo); testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo);
tesPushMsgToCoreTime(cntTime); tesPushMsgToCoreTime(cntTime);
Mockito.reset(tbClusterService, auditLogService); Mockito.reset(tbClusterService, auditLogService);
@ -252,7 +282,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
Object... additionalInfo) { Object... additionalInfo) {
CustomerId customer_NULL_UUID = (CustomerId) EntityIdFactory.getByTypeAndUuid(EntityType.CUSTOMER, ModelConstants.NULL_UUID); CustomerId customer_NULL_UUID = (CustomerId) EntityIdFactory.getByTypeAndUuid(EntityType.CUSTOMER, ModelConstants.NULL_UUID);
EntityId entity_originator_NULL_UUID = createEntityId_NULL_UUID(entity); EntityId entity_originator_NULL_UUID = createEntityId_NULL_UUID(entity);
testNotificationMsgToEdgeServiceNever(entity_originator_NULL_UUID); testNotificationMsgToEdgeServiceNeverWithActionType(entity_originator_NULL_UUID, actionType);
ArgumentMatcher<HasName> matcherEntityEquals = argument -> argument.getClass().equals(entity.getClass()); ArgumentMatcher<HasName> matcherEntityEquals = argument -> argument.getClass().equals(entity.getClass());
ArgumentMatcher<Exception> matcherError = argument -> argument.getMessage().contains(exp.getMessage()) ArgumentMatcher<Exception> matcherError = argument -> argument.getMessage().contains(exp.getMessage())
& argument.getClass().equals(exp.getClass()); & argument.getClass().equals(exp.getClass());
@ -266,7 +296,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
Object... additionalInfo) { Object... additionalInfo) {
CustomerId customer_NULL_UUID = (CustomerId) EntityIdFactory.getByTypeAndUuid(EntityType.CUSTOMER, ModelConstants.NULL_UUID); CustomerId customer_NULL_UUID = (CustomerId) EntityIdFactory.getByTypeAndUuid(EntityType.CUSTOMER, ModelConstants.NULL_UUID);
EntityId entity_originator_NULL_UUID = createEntityId_NULL_UUID(entity); EntityId entity_originator_NULL_UUID = createEntityId_NULL_UUID(entity);
testNotificationMsgToEdgeServiceNever(entity_originator_NULL_UUID); testNotificationMsgToEdgeServiceNeverWithActionType(entity_originator_NULL_UUID, actionType);
ArgumentMatcher<HasName> matcherEntityIsNull = Objects::isNull; ArgumentMatcher<HasName> matcherEntityIsNull = Objects::isNull;
ArgumentMatcher<Exception> matcherError = argument -> argument.getMessage().contains(exp.getMessage()) & ArgumentMatcher<Exception> matcherError = argument -> argument.getMessage().contains(exp.getMessage()) &
argument.getClass().equals(exp.getClass()); argument.getClass().equals(exp.getClass());
@ -299,6 +329,13 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
Mockito.verify(gatewayNotificationsService, never()).onDeviceDeleted(Mockito.any(Device.class)); Mockito.verify(gatewayNotificationsService, never()).onDeviceDeleted(Mockito.any(Device.class));
} }
private void testNotificationMsgToEdgeServiceNeverWithActionType(EntityId entityId, ActionType actionType) {
EdgeEventActionType edgeEventActionType = ActionType.CREDENTIALS_UPDATED.equals(actionType) ?
EdgeEventActionType.CREDENTIALS_UPDATED : edgeTypeByActionType(actionType);
Mockito.verify(tbClusterService, never()).sendNotificationMsgToEdge(Mockito.any(),
Mockito.any(), Mockito.any(entityId.getClass()), Mockito.any(), Mockito.any(), Mockito.eq(edgeEventActionType));
}
private void testNotificationMsgToEdgeServiceNever(EntityId entityId) { private void testNotificationMsgToEdgeServiceNever(EntityId entityId) {
Mockito.verify(tbClusterService, never()).sendNotificationMsgToEdge(Mockito.any(), Mockito.verify(tbClusterService, never()).sendNotificationMsgToEdge(Mockito.any(),
Mockito.any(), Mockito.any(entityId.getClass()), Mockito.any(), Mockito.any(), Mockito.any()); Mockito.any(), Mockito.any(entityId.getClass()), Mockito.any(), Mockito.any(), Mockito.any());
@ -317,16 +354,23 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
Mockito.any(entityId.getClass()), Mockito.any(), Mockito.any()); Mockito.any(entityId.getClass()), Mockito.any(), Mockito.any());
} }
protected void testBroadcastEntityStateChangeEventNever(EntityId entityId) {
Mockito.verify(tbClusterService, never()).broadcastEntityStateChangeEvent(Mockito.any(),
Mockito.any(entityId.getClass()), Mockito.any(ComponentLifecycleEvent.class));
}
private void testPushMsgToRuleEngineTime(ArgumentMatcher<EntityId> matcherOriginatorId, TenantId tenantId, int cntTime) { private void testPushMsgToRuleEngineTime(ArgumentMatcher<EntityId> matcherOriginatorId, TenantId tenantId, int cntTime) {
Mockito.verify(tbClusterService, times(cntTime)).pushMsgToRuleEngine(Mockito.eq(tenantId), Mockito.verify(tbClusterService, times(cntTime)).pushMsgToRuleEngine(Mockito.eq(tenantId),
Mockito.argThat(matcherOriginatorId), Mockito.any(TbMsg.class), Mockito.isNull()); Mockito.argThat(matcherOriginatorId), Mockito.any(TbMsg.class), Mockito.isNull());
} }
private void testSendNotificationMsgToEdgeServiceTime(EntityId entityId, TenantId tenantId, ActionType actionType, int cntTime) { private void testNotificationMsgToEdgeServiceTime(EntityId entityId, TenantId tenantId, ActionType actionType, int cntTime) {
EdgeEventActionType edgeEventActionType = ActionType.CREDENTIALS_UPDATED.equals(actionType) ? EdgeEventActionType edgeEventActionType = ActionType.CREDENTIALS_UPDATED.equals(actionType) ?
EdgeEventActionType.CREDENTIALS_UPDATED : edgeTypeByActionType(actionType); EdgeEventActionType.CREDENTIALS_UPDATED : edgeTypeByActionType(actionType);
ArgumentMatcher<EntityId> matcherEntityId = cntTime == 1 ? argument -> argument.equals(entityId) :
argument -> argument.getClass().equals(entityId.getClass());
Mockito.verify(tbClusterService, times(cntTime)).sendNotificationMsgToEdge(Mockito.eq(tenantId), Mockito.verify(tbClusterService, times(cntTime)).sendNotificationMsgToEdge(Mockito.eq(tenantId),
Mockito.any(), Mockito.eq(entityId), Mockito.any(), Mockito.isNull(), Mockito.any(), Mockito.argThat(matcherEntityId), Mockito.any(), Mockito.isNull(),
Mockito.eq(edgeEventActionType)); Mockito.eq(edgeEventActionType));
} }
@ -336,8 +380,10 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
Mockito.eq(edgeTypeByActionType(actionType))); Mockito.eq(edgeTypeByActionType(actionType)));
} }
private void testBroadcastEntityStateChangeEventTime(EntityId entityId, TenantId tenantId, int cntTime) { protected void testBroadcastEntityStateChangeEventTime(EntityId entityId, TenantId tenantId, int cntTime) {
Mockito.verify(tbClusterService, times(cntTime)).broadcastEntityStateChangeEvent(Mockito.eq(tenantId), ArgumentMatcher<TenantId> matcherTenantIdId = cntTime > 1 || tenantId == null ? argument -> argument.getClass().equals(TenantId.class) :
argument -> argument.equals(tenantId) ;
Mockito.verify(tbClusterService, times(cntTime)).broadcastEntityStateChangeEvent(Mockito.argThat(matcherTenantIdId),
Mockito.any(entityId.getClass()), Mockito.any(ComponentLifecycleEvent.class)); Mockito.any(entityId.getClass()), Mockito.any(ComponentLifecycleEvent.class));
} }
@ -348,9 +394,13 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
private void testLogEntityAction(HasName entity, EntityId originatorId, TenantId tenantId, private void testLogEntityAction(HasName entity, EntityId originatorId, TenantId tenantId,
CustomerId customerId, UserId userId, String userName, CustomerId customerId, UserId userId, String userName,
ActionType actionType, int cntTime, Object... additionalInfo) { ActionType actionType, int cntTime, Object... additionalInfo) {
ArgumentMatcher<HasName> matcherEntityEquals = entity == null ? Objects::isNull : argument -> argument.equals(entity); ArgumentMatcher<HasName> matcherEntityEquals = entity == null ? Objects::isNull : argument -> argument.toString().equals(entity.toString());
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId); ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId);
testLogEntityActionAdditionalInfo(matcherEntityEquals, matcherOriginatorId, tenantId, customerId, userId, userName, ArgumentMatcher<CustomerId> matcherCustomerId = customerId == null ?
argument -> argument.getClass().equals(CustomerId.class) : argument -> argument.equals(customerId);
ArgumentMatcher<UserId> matcherUserId = userId == null ?
argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId);
testLogEntityActionAdditionalInfo(matcherEntityEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName,
actionType, cntTime, extractMatcherAdditionalInfo(additionalInfo)); actionType, cntTime, extractMatcherAdditionalInfo(additionalInfo));
} }
@ -359,19 +409,24 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
ActionType actionType, int cntTime, Object... additionalInfo) { ActionType actionType, int cntTime, Object... additionalInfo) {
ArgumentMatcher<HasName> matcherEntityEquals = argument -> argument.getClass().equals(entity.getClass()); ArgumentMatcher<HasName> matcherEntityEquals = argument -> argument.getClass().equals(entity.getClass());
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId); ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId);
testLogEntityActionAdditionalInfo(matcherEntityEquals, matcherOriginatorId, tenantId, customerId, userId, userName, ArgumentMatcher<CustomerId> matcherCustomerId = customerId == null ?
argument -> argument.getClass().equals(CustomerId.class) : argument -> argument.equals(customerId);
ArgumentMatcher<UserId> matcherUserId = userId == null ?
argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId);
testLogEntityActionAdditionalInfo(matcherEntityEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName,
actionType, cntTime, extractMatcherAdditionalInfo(additionalInfo)); actionType, cntTime, extractMatcherAdditionalInfo(additionalInfo));
} }
private void testLogEntityActionAdditionalInfo(ArgumentMatcher<HasName> matcherEntity, ArgumentMatcher<EntityId> matcherOriginatorId, private void testLogEntityActionAdditionalInfo(ArgumentMatcher<HasName> matcherEntity, ArgumentMatcher<EntityId> matcherOriginatorId,
TenantId tenantId, CustomerId customerId, UserId userId, String userName, TenantId tenantId, ArgumentMatcher<CustomerId> matcherCustomerId,
ActionType actionType, int cntTime, List<ArgumentMatcher<Object>> matcherAdditionalInfos) { ArgumentMatcher<UserId> matcherUserId, String userName, ActionType actionType,
int cntTime, List<ArgumentMatcher<Object>> matcherAdditionalInfos) {
switch (matcherAdditionalInfos.size()) { switch (matcherAdditionalInfos.size()) {
case 1: case 1:
Mockito.verify(auditLogService, times(cntTime)) Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId), .logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId), Mockito.argThat(matcherCustomerId),
Mockito.eq(userId), Mockito.argThat(matcherUserId),
Mockito.eq(userName), Mockito.eq(userName),
Mockito.argThat(matcherOriginatorId), Mockito.argThat(matcherOriginatorId),
Mockito.argThat(matcherEntity), Mockito.argThat(matcherEntity),
@ -382,8 +437,8 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
case 2: case 2:
Mockito.verify(auditLogService, times(cntTime)) Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId), .logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId), Mockito.argThat(matcherCustomerId),
Mockito.eq(userId), Mockito.argThat(matcherUserId),
Mockito.eq(userName), Mockito.eq(userName),
Mockito.argThat(matcherOriginatorId), Mockito.argThat(matcherOriginatorId),
Mockito.argThat(matcherEntity), Mockito.argThat(matcherEntity),
@ -395,8 +450,8 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
case 3: case 3:
Mockito.verify(auditLogService, times(cntTime)) Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId), .logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId), Mockito.argThat(matcherCustomerId),
Mockito.eq(userId), Mockito.argThat(matcherUserId),
Mockito.eq(userName), Mockito.eq(userName),
Mockito.argThat(matcherOriginatorId), Mockito.argThat(matcherOriginatorId),
Mockito.argThat(matcherEntity), Mockito.argThat(matcherEntity),
@ -409,8 +464,8 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
default: default:
Mockito.verify(auditLogService, times(cntTime)) Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId), .logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId), Mockito.argThat(matcherCustomerId),
Mockito.eq(userId), Mockito.argThat(matcherUserId),
Mockito.eq(userName), Mockito.eq(userName),
Mockito.argThat(matcherOriginatorId), Mockito.argThat(matcherOriginatorId),
Mockito.argThat(matcherEntity), Mockito.argThat(matcherEntity),
@ -420,14 +475,15 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
} }
private void testLogEntityActionAdditionalInfoAny(ArgumentMatcher<HasName> matcherEntity, ArgumentMatcher<EntityId> matcherOriginatorId, private void testLogEntityActionAdditionalInfoAny(ArgumentMatcher<HasName> matcherEntity, ArgumentMatcher<EntityId> matcherOriginatorId,
TenantId tenantId, CustomerId customerId, UserId userId, String userName, TenantId tenantId, ArgumentMatcher<CustomerId> matcherCustomerId,
ArgumentMatcher<UserId> matcherUserId, String userName,
ActionType actionType, int cntTime, int cntAdditionalInfo) { ActionType actionType, int cntTime, int cntAdditionalInfo) {
switch (cntAdditionalInfo) { switch (cntAdditionalInfo) {
case 1: case 1:
Mockito.verify(auditLogService, times(cntTime)) Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId), .logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId), Mockito.argThat(matcherCustomerId),
Mockito.eq(userId), Mockito.argThat(matcherUserId),
Mockito.eq(userName), Mockito.eq(userName),
Mockito.argThat(matcherOriginatorId), Mockito.argThat(matcherOriginatorId),
Mockito.argThat(matcherEntity), Mockito.argThat(matcherEntity),
@ -438,8 +494,8 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
case 2: case 2:
Mockito.verify(auditLogService, times(cntTime)) Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId), .logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId), Mockito.argThat(matcherCustomerId),
Mockito.eq(userId), Mockito.argThat(matcherUserId),
Mockito.eq(userName), Mockito.eq(userName),
Mockito.argThat(matcherOriginatorId), Mockito.argThat(matcherOriginatorId),
Mockito.argThat(matcherEntity), Mockito.argThat(matcherEntity),
@ -451,8 +507,8 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
case 3: case 3:
Mockito.verify(auditLogService, times(cntTime)) Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId), .logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId), Mockito.argThat(matcherCustomerId),
Mockito.eq(userId), Mockito.argThat(matcherUserId),
Mockito.eq(userName), Mockito.eq(userName),
Mockito.argThat(matcherOriginatorId), Mockito.argThat(matcherOriginatorId),
Mockito.argThat(matcherEntity), Mockito.argThat(matcherEntity),
@ -465,8 +521,8 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
default: default:
Mockito.verify(auditLogService, times(cntTime)) Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId), .logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId), Mockito.argThat(matcherCustomerId),
Mockito.eq(userId), Mockito.argThat(matcherUserId),
Mockito.eq(userName), Mockito.eq(userName),
Mockito.argThat(matcherOriginatorId), Mockito.argThat(matcherOriginatorId),
Mockito.argThat(matcherEntity), Mockito.argThat(matcherEntity),
@ -558,7 +614,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
return result; return result;
} }
private EntityId createEntityId_NULL_UUID(HasName entity) { protected EntityId createEntityId_NULL_UUID(HasName entity) {
return EntityIdFactory.getByTypeAndUuid(entityClassToEntityTypeName(entity), ModelConstants.NULL_UUID); return EntityIdFactory.getByTypeAndUuid(entityClassToEntityTypeName(entity), ModelConstants.NULL_UUID);
} }
@ -571,6 +627,12 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
} }
private String entityClassToEntityTypeName(HasName entity) { private String entityClassToEntityTypeName(HasName entity) {
String entityType = entityClassToString(entity);
return "SAVE_OTA_PACKAGE_INFO_REQUEST".equals(entityType) || "OTA_PACKAGE_INFO".equals(entityType)?
EntityType.OTA_PACKAGE.name().toUpperCase(Locale.ENGLISH) : entityType;
}
private String entityClassToString(HasName entity) {
String className = entity.getClass().toString() String className = entity.getClass().toString()
.substring(entity.getClass().toString().lastIndexOf(".") + 1); .substring(entity.getClass().toString().lastIndexOf(".") + 1);
List str = className.chars() List str = className.chars()

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

@ -285,7 +285,7 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
protected void loginDifferentTenant() throws Exception { protected void loginDifferentTenant() throws Exception {
if (savedDifferentTenant != null) { if (savedDifferentTenant != null) {
login(savedDifferentTenant.getEmail(), TENANT_ADMIN_PASSWORD); login(DIFFERENT_TENANT_ADMIN_EMAIL, DIFFERENT_TENANT_ADMIN_PASSWORD);
} else { } else {
loginSysAdmin(); loginSysAdmin();

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

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

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

@ -30,7 +30,10 @@ import org.mockito.Mockito;
import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.common.util.ThingsBoardExecutors;
import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.OtaPackageInfo;
import org.thingsboard.server.common.data.SaveOtaPackageInfoRequest;
import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.audit.ActionType;
@ -57,6 +60,8 @@ import java.util.concurrent.TimeUnit;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE;
import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE;
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
public abstract class BaseDeviceControllerTest extends AbstractControllerTest { public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
@ -113,6 +118,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
Device savedDevice = doPost("/api/device", device, Device.class); Device savedDevice = doPost("/api/device", device, Device.class);
Device oldDevice = new Device(savedDevice); Device oldDevice = new Device(savedDevice);
testNotifyEntityOneTimeMsgToEdgeServiceNever(savedDevice, savedDevice.getId(), savedDevice.getId(), testNotifyEntityOneTimeMsgToEdgeServiceNever(savedDevice, savedDevice.getId(), savedDevice.getId(),
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.ADDED); ActionType.ADDED);
@ -203,7 +209,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
String savedDeviceIdStr = savedDevice.getId().getId().toString(); String savedDeviceIdStr = savedDevice.getId().getId().toString();
doPost("/api/device", savedDevice) doPost("/api/device", savedDevice)
.andExpect( status().isNotFound()) .andExpect(status().isNotFound())
.andExpect(statusReason(containsString(msgErrorNoFound("Device", savedDeviceIdStr)))); .andExpect(statusReason(containsString(msgErrorNoFound("Device", savedDeviceIdStr))));
testNotifyEntityNever(savedDevice.getId(), savedDevice); testNotifyEntityNever(savedDevice.getId(), savedDevice);
@ -221,6 +227,66 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
deleteDifferentTenant(); deleteDifferentTenant();
} }
@Test
public void testSaveDeviceWithProfileFromDifferentTenant() throws Exception {
loginDifferentTenant();
DeviceProfile differentProfile = createDeviceProfile("Different profile");
differentProfile = doPost("/api/deviceProfile", differentProfile, DeviceProfile.class);
loginTenantAdmin();
Device device = new Device();
device.setName("My device");
device.setDeviceProfileId(differentProfile.getId());
doPost("/api/device", device).andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Device can`t be referencing to device profile from different tenant!")));
}
@Test
public void testSaveDeviceWithFirmwareFromDifferentTenant() throws Exception {
loginDifferentTenant();
DeviceProfile differentProfile = createDeviceProfile("Different profile");
differentProfile = doPost("/api/deviceProfile", differentProfile, DeviceProfile.class);
SaveOtaPackageInfoRequest firmwareInfo = new SaveOtaPackageInfoRequest();
firmwareInfo.setDeviceProfileId(differentProfile.getId());
firmwareInfo.setType(FIRMWARE);
firmwareInfo.setTitle("title");
firmwareInfo.setVersion("1.0");
firmwareInfo.setUrl("test.url");
firmwareInfo.setUsesUrl(true);
OtaPackageInfo savedFw = doPost("/api/otaPackage", firmwareInfo, OtaPackageInfo.class);
loginTenantAdmin();
Device device = new Device();
device.setName("My device");
device.setType("default");
device.setFirmwareId(savedFw.getId());
doPost("/api/device", device).andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Can't assign firmware from different tenant!")));
}
@Test
public void testSaveDeviceWithSoftwareFromDifferentTenant() throws Exception {
loginDifferentTenant();
DeviceProfile differentProfile = createDeviceProfile("Different profile");
differentProfile = doPost("/api/deviceProfile", differentProfile, DeviceProfile.class);
SaveOtaPackageInfoRequest softwareInfo = new SaveOtaPackageInfoRequest();
softwareInfo.setDeviceProfileId(differentProfile.getId());
softwareInfo.setType(SOFTWARE);
softwareInfo.setTitle("title");
softwareInfo.setVersion("1.0");
softwareInfo.setUrl("test.url");
softwareInfo.setUsesUrl(true);
OtaPackageInfo savedSw = doPost("/api/otaPackage", softwareInfo, OtaPackageInfo.class);
loginTenantAdmin();
Device device = new Device();
device.setName("My device");
device.setType("default");
device.setSoftwareId(savedSw.getId());
doPost("/api/device", device).andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Can't assign software from different tenant!")));
}
@Test @Test
public void testFindDeviceById() throws Exception { public void testFindDeviceById() throws Exception {
Device device = new Device(); Device device = new Device();
@ -248,8 +314,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
} }
testNotifyManyEntityManyTimeMsgToEdgeServiceNever(new Device(), new Device(), testNotifyManyEntityManyTimeMsgToEdgeServiceNever(new Device(), new Device(),
savedTenant.getId(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.ADDED, cntEntity); ActionType.ADDED, cntEntity);
testNotificationUpdateGatewayNever(); testNotificationUpdateGatewayNever();
@ -813,7 +878,9 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new Device(), new Device(), testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new Device(), new Device(),
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.ADDED, ActionType.ASSIGNED_TO_CUSTOMER, cntEntity, cntEntity, cntEntity * 2); ActionType.ADDED, ActionType.ASSIGNED_TO_CUSTOMER, cntEntity, cntEntity, cntEntity * 2);
Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService);
testNotificationUpdateGatewayNever(); testNotificationUpdateGatewayNever();
Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService);
List<Device> loadedDevices = new ArrayList<>(cntEntity); List<Device> loadedDevices = new ArrayList<>(cntEntity);
PageLink pageLink = new PageLink(23); PageLink pageLink = new PageLink(23);
@ -828,8 +895,6 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
assertThat(devices).containsExactlyInAnyOrderElementsOf(loadedDevices); assertThat(devices).containsExactlyInAnyOrderElementsOf(loadedDevices);
Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService);
deleteEntitiesAsync("/api/customer/device/", loadedDevices, executor).get(TIMEOUT, TimeUnit.SECONDS); deleteEntitiesAsync("/api/customer/device/", loadedDevices, executor).get(TIMEOUT, TimeUnit.SECONDS);
testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAnyAdditionalInfoAny(new Device(), new Device(), testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAnyAdditionalInfoAny(new Device(), new Device(),

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

@ -29,13 +29,17 @@ import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceProfileInfo; import org.thingsboard.server.common.data.DeviceProfileInfo;
import org.thingsboard.server.common.data.DeviceProfileProvisionType; import org.thingsboard.server.common.data.DeviceProfileProvisionType;
import org.thingsboard.server.common.data.DeviceProfileType; import org.thingsboard.server.common.data.DeviceProfileType;
import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.OtaPackageInfo;
import org.thingsboard.server.common.data.SaveOtaPackageInfoRequest;
import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration;
@ -45,7 +49,15 @@ import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadCo
import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.queue.ProcessingStrategy;
import org.thingsboard.server.common.data.queue.ProcessingStrategyType;
import org.thingsboard.server.common.data.queue.Queue;
import org.thingsboard.server.common.data.queue.SubmitStrategy;
import org.thingsboard.server.common.data.queue.SubmitStrategyType;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.tenant.profile.TenantProfileData;
import org.thingsboard.server.common.data.tenant.profile.TenantProfileQueueConfiguration;
import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.DataValidationException;
import java.util.ArrayList; import java.util.ArrayList;
@ -59,6 +71,8 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE;
import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE;
public abstract class BaseDeviceProfileControllerTest extends AbstractControllerTest { public abstract class BaseDeviceProfileControllerTest extends AbstractControllerTest {
@ -345,6 +359,129 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
testNotifyEntityNever(savedDeviceProfile.getId(), savedDeviceProfile); testNotifyEntityNever(savedDeviceProfile.getId(), savedDeviceProfile);
} }
@Test
public void testSaveDeviceProfileWithRuleChainFromDifferentTenant() throws Exception {
loginDifferentTenant();
RuleChain ruleChain = new RuleChain();
ruleChain.setName("Different rule chain");
RuleChain savedRuleChain = doPost("/api/ruleChain", ruleChain, RuleChain.class);
loginTenantAdmin();
DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
deviceProfile.setDefaultRuleChainId(savedRuleChain.getId());
doPost("/api/deviceProfile", deviceProfile).andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Can't assign rule chain from different tenant!")));
}
@Test
public void testSaveDeviceProfileWithDashboardFromDifferentTenant() throws Exception {
loginDifferentTenant();
Dashboard dashboard = new Dashboard();
dashboard.setTitle("Different dashboard");
Dashboard savedDashboard = doPost("/api/dashboard", dashboard, Dashboard.class);
loginTenantAdmin();
DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
deviceProfile.setDefaultDashboardId(savedDashboard.getId());
doPost("/api/deviceProfile", deviceProfile).andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Can't assign dashboard from different tenant!")));
}
@Test
public void 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();
DeviceProfile differentProfile = createDeviceProfile("Different profile");
differentProfile = doPost("/api/deviceProfile", differentProfile, DeviceProfile.class);
SaveOtaPackageInfoRequest firmwareInfo = new SaveOtaPackageInfoRequest();
firmwareInfo.setDeviceProfileId(differentProfile.getId());
firmwareInfo.setType(FIRMWARE);
firmwareInfo.setTitle("title");
firmwareInfo.setVersion("1.0");
firmwareInfo.setUrl("test.url");
firmwareInfo.setUsesUrl(true);
OtaPackageInfo savedFw = doPost("/api/otaPackage", firmwareInfo, OtaPackageInfo.class);
loginTenantAdmin();
DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
deviceProfile.setFirmwareId(savedFw.getId());
doPost("/api/deviceProfile", deviceProfile).andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Can't assign firmware from different tenant!")));
}
@Test
public void testSaveDeviceProfileWithSoftwareFromDifferentTenant() throws Exception {
loginDifferentTenant();
DeviceProfile differentProfile = createDeviceProfile("Different profile");
differentProfile = doPost("/api/deviceProfile", differentProfile, DeviceProfile.class);
SaveOtaPackageInfoRequest softwareInfo = new SaveOtaPackageInfoRequest();
softwareInfo.setDeviceProfileId(differentProfile.getId());
softwareInfo.setType(SOFTWARE);
softwareInfo.setTitle("title");
softwareInfo.setVersion("1.0");
softwareInfo.setUrl("test.url");
softwareInfo.setUsesUrl(true);
OtaPackageInfo savedSw = doPost("/api/otaPackage", softwareInfo, OtaPackageInfo.class);
loginTenantAdmin();
DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
deviceProfile.setSoftwareId(savedSw.getId());
doPost("/api/deviceProfile", deviceProfile).andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Can't assign software from different tenant!")));
}
@Test @Test
public void testDeleteDeviceProfile() throws Exception { public void testDeleteDeviceProfile() throws Exception {
DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"); DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
@ -387,6 +524,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new DeviceProfile(), new DeviceProfile(), testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new DeviceProfile(), new DeviceProfile(),
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.ADDED, ActionType.ADDED, cntEntity, cntEntity, cntEntity); ActionType.ADDED, ActionType.ADDED, cntEntity, cntEntity, cntEntity);
Mockito.reset(tbClusterService, auditLogService);
List<DeviceProfile> loadedDeviceProfiles = new ArrayList<>(); List<DeviceProfile> loadedDeviceProfiles = new ArrayList<>();
pageLink = new PageLink(17); pageLink = new PageLink(17);
@ -1008,7 +1146,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
.andExpect(status().isBadRequest()) .andExpect(status().isBadRequest())
.andExpect(statusReason(containsString(errorMsg))); .andExpect(statusReason(containsString(errorMsg)));
testNotifyEntityEqualsOneTimeServiceNeverError(deviceProfile,savedTenant.getId(), testNotifyEntityEqualsOneTimeServiceNeverError(deviceProfile, savedTenant.getId(),
tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(errorMsg)); tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(errorMsg));
} }
@ -1023,7 +1161,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
.andExpect(status().isBadRequest()) .andExpect(status().isBadRequest())
.andExpect(statusReason(containsString(errorMsg))); .andExpect(statusReason(containsString(errorMsg)));
testNotifyEntityEqualsOneTimeServiceNeverError(deviceProfile,savedTenant.getId(), testNotifyEntityEqualsOneTimeServiceNeverError(deviceProfile, savedTenant.getId(),
tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(errorMsg)); tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(errorMsg));
} }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2
application/src/test/java/org/thingsboard/server/queue/discovery/HashPartitionServiceTest.java

@ -112,7 +112,7 @@ public class HashPartitionServiceTest {
map.put(partition, map.getOrDefault(partition, 0) + 1); map.put(partition, map.getOrDefault(partition, 0) + 1);
} }
checkDispersion(start, map, ITERATIONS, 1.0); checkDispersion(start, map, ITERATIONS, 5.0);
} }
@SneakyThrows @SneakyThrows

23
common/queue/src/main/java/org/thingsboard/server/queue/azure/servicebus/TbServiceBusQueueConfigs.java

@ -19,6 +19,7 @@ import lombok.Getter;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.StringUtils;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import java.util.HashMap; import java.util.HashMap;
@ -27,15 +28,15 @@ import java.util.Map;
@Component @Component
@ConditionalOnExpression("'${queue.type:null}'=='service-bus'") @ConditionalOnExpression("'${queue.type:null}'=='service-bus'")
public class TbServiceBusQueueConfigs { public class TbServiceBusQueueConfigs {
@Value("${queue.service-bus.queue-properties.core}") @Value("${queue.service-bus.queue-properties.core:}")
private String coreProperties; private String coreProperties;
@Value("${queue.service-bus.queue-properties.rule-engine}") @Value("${queue.service-bus.queue-properties.rule-engine:}")
private String ruleEngineProperties; private String ruleEngineProperties;
@Value("${queue.service-bus.queue-properties.transport-api}") @Value("${queue.service-bus.queue-properties.transport-api:}")
private String transportApiProperties; private String transportApiProperties;
@Value("${queue.service-bus.queue-properties.notifications}") @Value("${queue.service-bus.queue-properties.notifications:}")
private String notificationsProperties; private String notificationsProperties;
@Value("${queue.service-bus.queue-properties.js-executor}") @Value("${queue.service-bus.queue-properties.js-executor:}")
private String jsExecutorProperties; private String jsExecutorProperties;
@Value("${queue.service-bus.queue-properties.version-control:}") @Value("${queue.service-bus.queue-properties.version-control:}")
private String vcProperties; private String vcProperties;
@ -64,11 +65,13 @@ public class TbServiceBusQueueConfigs {
private Map<String, String> getConfigs(String properties) { private Map<String, String> getConfigs(String properties) {
Map<String, String> configs = new HashMap<>(); Map<String, String> configs = new HashMap<>();
for (String property : properties.split(";")) { if (StringUtils.isNotEmpty(properties)) {
int delimiterPosition = property.indexOf(":"); for (String property : properties.split(";")) {
String key = property.substring(0, delimiterPosition); int delimiterPosition = property.indexOf(":");
String value = property.substring(delimiterPosition + 1); String key = property.substring(0, delimiterPosition);
configs.put(key, value); String value = property.substring(delimiterPosition + 1);
configs.put(key, value);
}
} }
return configs; return configs;
} }

10
common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaTopicConfigs.java

@ -28,15 +28,15 @@ import java.util.Map;
@Component @Component
@ConditionalOnProperty(prefix = "queue", value = "type", havingValue = "kafka") @ConditionalOnProperty(prefix = "queue", value = "type", havingValue = "kafka")
public class TbKafkaTopicConfigs { public class TbKafkaTopicConfigs {
@Value("${queue.kafka.topic-properties.core}") @Value("${queue.kafka.topic-properties.core:}")
private String coreProperties; private String coreProperties;
@Value("${queue.kafka.topic-properties.rule-engine}") @Value("${queue.kafka.topic-properties.rule-engine:}")
private String ruleEngineProperties; private String ruleEngineProperties;
@Value("${queue.kafka.topic-properties.transport-api}") @Value("${queue.kafka.topic-properties.transport-api:}")
private String transportApiProperties; private String transportApiProperties;
@Value("${queue.kafka.topic-properties.notifications}") @Value("${queue.kafka.topic-properties.notifications:}")
private String notificationsProperties; private String notificationsProperties;
@Value("${queue.kafka.topic-properties.js-executor}") @Value("${queue.kafka.topic-properties.js-executor:}")
private String jsExecutorProperties; private String jsExecutorProperties;
@Value("${queue.kafka.topic-properties.ota-updates:}") @Value("${queue.kafka.topic-properties.ota-updates:}")
private String fwUpdatesProperties; private String fwUpdatesProperties;

9
common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsMonolithQueueFactory.java

@ -76,6 +76,7 @@ public class AwsSqsMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng
private final TbQueueAdmin jsExecutorAdmin; private final TbQueueAdmin jsExecutorAdmin;
private final TbQueueAdmin transportApiAdmin; private final TbQueueAdmin transportApiAdmin;
private final TbQueueAdmin notificationAdmin; private final TbQueueAdmin notificationAdmin;
private final TbQueueAdmin otaAdmin;
private final TbQueueAdmin vcAdmin; private final TbQueueAdmin vcAdmin;
public AwsSqsMonolithQueueFactory(NotificationsTopicService notificationsTopicService, TbQueueCoreSettings coreSettings, public AwsSqsMonolithQueueFactory(NotificationsTopicService notificationsTopicService, TbQueueCoreSettings coreSettings,
@ -102,6 +103,7 @@ public class AwsSqsMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng
this.jsExecutorAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getJsExecutorAttributes()); this.jsExecutorAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getJsExecutorAttributes());
this.transportApiAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getTransportApiAttributes()); this.transportApiAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getTransportApiAttributes());
this.notificationAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getNotificationsAttributes()); this.notificationAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getNotificationsAttributes());
this.otaAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getOtaAttributes());
this.vcAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getVcAttributes()); this.vcAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getVcAttributes());
} }
@ -210,13 +212,13 @@ public class AwsSqsMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng
@Override @Override
public TbQueueConsumer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgConsumer() { public TbQueueConsumer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgConsumer() {
return new TbAwsSqsConsumerTemplate<>(coreAdmin, sqsSettings, coreSettings.getOtaPackageTopic(), return new TbAwsSqsConsumerTemplate<>(otaAdmin, sqsSettings, coreSettings.getOtaPackageTopic(),
msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
} }
@Override @Override
public TbQueueProducer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgProducer() { public TbQueueProducer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgProducer() {
return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getOtaPackageTopic()); return new TbAwsSqsProducerTemplate<>(otaAdmin, sqsSettings, coreSettings.getOtaPackageTopic());
} }
@Override @Override
@ -241,6 +243,9 @@ public class AwsSqsMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng
if (notificationAdmin != null) { if (notificationAdmin != null) {
notificationAdmin.destroy(); notificationAdmin.destroy();
} }
if (otaAdmin != null) {
otaAdmin.destroy();
}
if (vcAdmin != null) { if (vcAdmin != null) {
vcAdmin.destroy(); vcAdmin.destroy();
} }

21
common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbCoreQueueFactory.java

@ -45,6 +45,7 @@ import org.thingsboard.server.queue.settings.TbQueueRemoteJsInvokeSettings;
import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings;
import org.thingsboard.server.queue.settings.TbQueueTransportApiSettings; import org.thingsboard.server.queue.settings.TbQueueTransportApiSettings;
import org.thingsboard.server.queue.settings.TbQueueTransportNotificationSettings; import org.thingsboard.server.queue.settings.TbQueueTransportNotificationSettings;
import org.thingsboard.server.queue.settings.TbQueueVersionControlSettings;
import org.thingsboard.server.queue.sqs.TbAwsSqsAdmin; import org.thingsboard.server.queue.sqs.TbAwsSqsAdmin;
import org.thingsboard.server.queue.sqs.TbAwsSqsConsumerTemplate; import org.thingsboard.server.queue.sqs.TbAwsSqsConsumerTemplate;
import org.thingsboard.server.queue.sqs.TbAwsSqsProducerTemplate; import org.thingsboard.server.queue.sqs.TbAwsSqsProducerTemplate;
@ -66,18 +67,22 @@ public class AwsSqsTbCoreQueueFactory implements TbCoreQueueFactory {
private final TbServiceInfoProvider serviceInfoProvider; private final TbServiceInfoProvider serviceInfoProvider;
private final TbQueueRemoteJsInvokeSettings jsInvokeSettings; private final TbQueueRemoteJsInvokeSettings jsInvokeSettings;
private final TbQueueTransportNotificationSettings transportNotificationSettings; private final TbQueueTransportNotificationSettings transportNotificationSettings;
private final TbQueueVersionControlSettings vcSettings;
private final TbQueueAdmin coreAdmin; private final TbQueueAdmin coreAdmin;
private final TbQueueAdmin ruleEngineAdmin; private final TbQueueAdmin ruleEngineAdmin;
private final TbQueueAdmin jsExecutorAdmin; private final TbQueueAdmin jsExecutorAdmin;
private final TbQueueAdmin transportApiAdmin; private final TbQueueAdmin transportApiAdmin;
private final TbQueueAdmin notificationAdmin; private final TbQueueAdmin notificationAdmin;
private final TbQueueAdmin otaAdmin;
private final TbQueueAdmin vcAdmin;
public AwsSqsTbCoreQueueFactory(TbAwsSqsSettings sqsSettings, public AwsSqsTbCoreQueueFactory(TbAwsSqsSettings sqsSettings,
TbQueueCoreSettings coreSettings, TbQueueCoreSettings coreSettings,
TbQueueTransportApiSettings transportApiSettings, TbQueueTransportApiSettings transportApiSettings,
TbQueueRuleEngineSettings ruleEngineSettings, TbQueueRuleEngineSettings ruleEngineSettings,
NotificationsTopicService notificationsTopicService, NotificationsTopicService notificationsTopicService,
TbQueueVersionControlSettings vcSettings,
TbServiceInfoProvider serviceInfoProvider, TbServiceInfoProvider serviceInfoProvider,
TbQueueRemoteJsInvokeSettings jsInvokeSettings, TbQueueRemoteJsInvokeSettings jsInvokeSettings,
TbAwsSqsQueueAttributes sqsQueueAttributes, TbAwsSqsQueueAttributes sqsQueueAttributes,
@ -90,12 +95,15 @@ public class AwsSqsTbCoreQueueFactory implements TbCoreQueueFactory {
this.serviceInfoProvider = serviceInfoProvider; this.serviceInfoProvider = serviceInfoProvider;
this.jsInvokeSettings = jsInvokeSettings; this.jsInvokeSettings = jsInvokeSettings;
this.transportNotificationSettings = transportNotificationSettings; this.transportNotificationSettings = transportNotificationSettings;
this.vcSettings = vcSettings;
this.coreAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getCoreAttributes()); this.coreAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getCoreAttributes());
this.ruleEngineAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getRuleEngineAttributes()); this.ruleEngineAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getRuleEngineAttributes());
this.jsExecutorAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getJsExecutorAttributes()); this.jsExecutorAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getJsExecutorAttributes());
this.transportApiAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getTransportApiAttributes()); this.transportApiAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getTransportApiAttributes());
this.notificationAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getNotificationsAttributes()); this.notificationAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getNotificationsAttributes());
this.otaAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getOtaAttributes());
this.vcAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getVcAttributes());
} }
@Override @Override
@ -183,19 +191,18 @@ public class AwsSqsTbCoreQueueFactory implements TbCoreQueueFactory {
@Override @Override
public TbQueueConsumer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgConsumer() { public TbQueueConsumer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgConsumer() {
return new TbAwsSqsConsumerTemplate<>(coreAdmin, sqsSettings, coreSettings.getOtaPackageTopic(), return new TbAwsSqsConsumerTemplate<>(otaAdmin, sqsSettings, coreSettings.getOtaPackageTopic(),
msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
} }
@Override @Override
public TbQueueProducer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgProducer() { public TbQueueProducer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgProducer() {
return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getOtaPackageTopic()); return new TbAwsSqsProducerTemplate<>(otaAdmin, sqsSettings, coreSettings.getOtaPackageTopic());
} }
@Override @Override
public TbQueueProducer<TbProtoQueueMsg<TransportProtos.ToVersionControlServiceMsg>> createVersionControlMsgProducer() { public TbQueueProducer<TbProtoQueueMsg<TransportProtos.ToVersionControlServiceMsg>> createVersionControlMsgProducer() {
//TODO: version-control return new TbAwsSqsProducerTemplate<>(vcAdmin, sqsSettings, vcSettings.getTopic());
return null;
} }
@PreDestroy @PreDestroy
@ -215,5 +222,11 @@ public class AwsSqsTbCoreQueueFactory implements TbCoreQueueFactory {
if (notificationAdmin != null) { if (notificationAdmin != null) {
notificationAdmin.destroy(); notificationAdmin.destroy();
} }
if (otaAdmin != null) {
otaAdmin.destroy();
}
if (vcAdmin != null) {
vcAdmin.destroy();
}
} }
} }

8
common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbRuleEngineQueueFactory.java

@ -39,6 +39,7 @@ import org.thingsboard.server.queue.settings.TbQueueCoreSettings;
import org.thingsboard.server.queue.settings.TbQueueRemoteJsInvokeSettings; import org.thingsboard.server.queue.settings.TbQueueRemoteJsInvokeSettings;
import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings;
import org.thingsboard.server.queue.settings.TbQueueTransportNotificationSettings; import org.thingsboard.server.queue.settings.TbQueueTransportNotificationSettings;
import org.thingsboard.server.queue.settings.TbQueueVersionControlSettings;
import org.thingsboard.server.queue.sqs.TbAwsSqsAdmin; import org.thingsboard.server.queue.sqs.TbAwsSqsAdmin;
import org.thingsboard.server.queue.sqs.TbAwsSqsConsumerTemplate; import org.thingsboard.server.queue.sqs.TbAwsSqsConsumerTemplate;
import org.thingsboard.server.queue.sqs.TbAwsSqsProducerTemplate; import org.thingsboard.server.queue.sqs.TbAwsSqsProducerTemplate;
@ -64,6 +65,7 @@ public class AwsSqsTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory
private final TbQueueAdmin ruleEngineAdmin; private final TbQueueAdmin ruleEngineAdmin;
private final TbQueueAdmin jsExecutorAdmin; private final TbQueueAdmin jsExecutorAdmin;
private final TbQueueAdmin notificationAdmin; private final TbQueueAdmin notificationAdmin;
private final TbQueueAdmin otaAdmin;
public AwsSqsTbRuleEngineQueueFactory(NotificationsTopicService notificationsTopicService, TbQueueCoreSettings coreSettings, public AwsSqsTbRuleEngineQueueFactory(NotificationsTopicService notificationsTopicService, TbQueueCoreSettings coreSettings,
TbQueueRuleEngineSettings ruleEngineSettings, TbQueueRuleEngineSettings ruleEngineSettings,
@ -84,6 +86,7 @@ public class AwsSqsTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory
this.ruleEngineAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getRuleEngineAttributes()); this.ruleEngineAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getRuleEngineAttributes());
this.jsExecutorAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getJsExecutorAttributes()); this.jsExecutorAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getJsExecutorAttributes());
this.notificationAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getNotificationsAttributes()); this.notificationAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getNotificationsAttributes());
this.otaAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getOtaAttributes());
} }
@Override @Override
@ -154,7 +157,7 @@ public class AwsSqsTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory
@Override @Override
public TbQueueProducer<TbProtoQueueMsg<TransportProtos.ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgProducer() { public TbQueueProducer<TbProtoQueueMsg<TransportProtos.ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgProducer() {
return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getOtaPackageTopic()); return new TbAwsSqsProducerTemplate<>(otaAdmin, sqsSettings, coreSettings.getOtaPackageTopic());
} }
@ -172,6 +175,9 @@ public class AwsSqsTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory
if (notificationAdmin != null) { if (notificationAdmin != null) {
notificationAdmin.destroy(); notificationAdmin.destroy();
} }
if (otaAdmin != null) {
otaAdmin.destroy();
}
} }
} }

15
common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubSubscriptionSettings.java

@ -19,6 +19,7 @@ import lombok.Getter;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.StringUtils;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import java.util.HashMap; import java.util.HashMap;
@ -27,7 +28,7 @@ import java.util.Map;
@Component @Component
@ConditionalOnExpression("'${queue.type:null}'=='pubsub'") @ConditionalOnExpression("'${queue.type:null}'=='pubsub'")
public class TbPubSubSubscriptionSettings { public class TbPubSubSubscriptionSettings {
@Value("${queue.pubsub.queue-properties.core}") @Value("${queue.pubsub.queue-properties.core:}")
private String coreProperties; private String coreProperties;
@Value("${queue.pubsub.queue-properties.rule-engine}") @Value("${queue.pubsub.queue-properties.rule-engine}")
private String ruleEngineProperties; private String ruleEngineProperties;
@ -65,11 +66,13 @@ public class TbPubSubSubscriptionSettings {
private Map<String, String> getSettings(String properties) { private Map<String, String> getSettings(String properties) {
Map<String, String> configs = new HashMap<>(); Map<String, String> configs = new HashMap<>();
for (String property : properties.split(";")) { if (StringUtils.isNotEmpty(properties)) {
int delimiterPosition = property.indexOf(":"); for (String property : properties.split(";")) {
String key = property.substring(0, delimiterPosition); int delimiterPosition = property.indexOf(":");
String value = property.substring(delimiterPosition + 1); String key = property.substring(0, delimiterPosition);
configs.put(key, value); String value = property.substring(delimiterPosition + 1);
configs.put(key, value);
}
} }
return configs; return configs;
} }

23
common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqQueueArguments.java

@ -19,6 +19,7 @@ import lombok.Getter;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.StringUtils;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import java.util.HashMap; import java.util.HashMap;
@ -28,15 +29,15 @@ import java.util.regex.Pattern;
@Component @Component
@ConditionalOnExpression("'${queue.type:null}'=='rabbitmq'") @ConditionalOnExpression("'${queue.type:null}'=='rabbitmq'")
public class TbRabbitMqQueueArguments { public class TbRabbitMqQueueArguments {
@Value("${queue.rabbitmq.queue-properties.core}") @Value("${queue.rabbitmq.queue-properties.core:}")
private String coreProperties; private String coreProperties;
@Value("${queue.rabbitmq.queue-properties.rule-engine}") @Value("${queue.rabbitmq.queue-properties.rule-engine:}")
private String ruleEngineProperties; private String ruleEngineProperties;
@Value("${queue.rabbitmq.queue-properties.transport-api}") @Value("${queue.rabbitmq.queue-properties.transport-api:}")
private String transportApiProperties; private String transportApiProperties;
@Value("${queue.rabbitmq.queue-properties.notifications}") @Value("${queue.rabbitmq.queue-properties.notifications:}")
private String notificationsProperties; private String notificationsProperties;
@Value("${queue.rabbitmq.queue-properties.js-executor}") @Value("${queue.rabbitmq.queue-properties.js-executor:}")
private String jsExecutorProperties; private String jsExecutorProperties;
@Value("${queue.rabbitmq.queue-properties.version-control:}") @Value("${queue.rabbitmq.queue-properties.version-control:}")
private String vcProperties; private String vcProperties;
@ -66,11 +67,13 @@ public class TbRabbitMqQueueArguments {
private Map<String, Object> getArgs(String properties) { private Map<String, Object> getArgs(String properties) {
Map<String, Object> configs = new HashMap<>(); Map<String, Object> configs = new HashMap<>();
for (String property : properties.split(";")) { if (StringUtils.isNotEmpty(properties)) {
int delimiterPosition = property.indexOf(":"); for (String property : properties.split(";")) {
String key = property.substring(0, delimiterPosition); int delimiterPosition = property.indexOf(":");
String strValue = property.substring(delimiterPosition + 1); String key = property.substring(0, delimiterPosition);
configs.put(key, getObjectValue(strValue)); String strValue = property.substring(delimiterPosition + 1);
configs.put(key, getObjectValue(strValue));
}
} }
return configs; return configs;
} }

33
common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsQueueAttributes.java

@ -20,6 +20,7 @@ import lombok.Getter;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.StringUtils;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import java.util.HashMap; import java.util.HashMap;
@ -28,16 +29,18 @@ import java.util.Map;
@Component @Component
@ConditionalOnExpression("'${queue.type:null}'=='aws-sqs'") @ConditionalOnExpression("'${queue.type:null}'=='aws-sqs'")
public class TbAwsSqsQueueAttributes { public class TbAwsSqsQueueAttributes {
@Value("${queue.aws-sqs.queue-properties.core}") @Value("${queue.aws-sqs.queue-properties.core:}")
private String coreProperties; private String coreProperties;
@Value("${queue.aws-sqs.queue-properties.rule-engine}") @Value("${queue.aws-sqs.queue-properties.rule-engine:}")
private String ruleEngineProperties; private String ruleEngineProperties;
@Value("${queue.aws-sqs.queue-properties.transport-api}") @Value("${queue.aws-sqs.queue-properties.transport-api:}")
private String transportApiProperties; private String transportApiProperties;
@Value("${queue.aws-sqs.queue-properties.notifications}") @Value("${queue.aws-sqs.queue-properties.notifications:}")
private String notificationsProperties; private String notificationsProperties;
@Value("${queue.aws-sqs.queue-properties.js-executor}") @Value("${queue.aws-sqs.queue-properties.js-executor:}")
private String jsExecutorProperties; private String jsExecutorProperties;
@Value("${queue.aws-sqs.queue-properties.ota-updates:}")
private String otaProperties;
@Value("${queue.aws-sqs.queue-properties.version-control:}") @Value("${queue.aws-sqs.queue-properties.version-control:}")
private String vcProperties; private String vcProperties;
@ -52,6 +55,8 @@ public class TbAwsSqsQueueAttributes {
@Getter @Getter
private Map<String, String> jsExecutorAttributes; private Map<String, String> jsExecutorAttributes;
@Getter @Getter
private Map<String, String> otaAttributes;
@Getter
private Map<String, String> vcAttributes; private Map<String, String> vcAttributes;
private final Map<String, String> defaultAttributes = new HashMap<>(); private final Map<String, String> defaultAttributes = new HashMap<>();
@ -65,19 +70,21 @@ public class TbAwsSqsQueueAttributes {
transportApiAttributes = getConfigs(transportApiProperties); transportApiAttributes = getConfigs(transportApiProperties);
notificationsAttributes = getConfigs(notificationsProperties); notificationsAttributes = getConfigs(notificationsProperties);
jsExecutorAttributes = getConfigs(jsExecutorProperties); jsExecutorAttributes = getConfigs(jsExecutorProperties);
otaAttributes = getConfigs(otaProperties);
vcAttributes = getConfigs(vcProperties); vcAttributes = getConfigs(vcProperties);
} }
private Map<String, String> getConfigs(String properties) { private Map<String, String> getConfigs(String properties) {
Map<String, String> configs = new HashMap<>(); Map<String, String> configs = new HashMap<>(defaultAttributes);
for (String property : properties.split(";")) { if (StringUtils.isNotEmpty(properties)) {
int delimiterPosition = property.indexOf(":"); for (String property : properties.split(";")) {
String key = property.substring(0, delimiterPosition); int delimiterPosition = property.indexOf(":");
String value = property.substring(delimiterPosition + 1); String key = property.substring(0, delimiterPosition);
validateAttributeName(key); String value = property.substring(delimiterPosition + 1);
configs.put(key, value); validateAttributeName(key);
configs.put(key, value);
}
} }
configs.putAll(defaultAttributes);
return configs; return configs;
} }

4
dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java

@ -226,6 +226,9 @@ public class DeviceServiceImpl extends AbstractCachedEntityService<DeviceCacheKe
if (deviceProfile == null) { if (deviceProfile == null) {
throw new DataValidationException("Device is referencing non existing device profile!"); throw new DataValidationException("Device is referencing non existing device profile!");
} }
if (!deviceProfile.getTenantId().equals(device.getTenantId())) {
throw new DataValidationException("Device can`t be referencing to device profile from different tenant!");
}
} }
device.setType(deviceProfile.getName()); device.setType(deviceProfile.getName());
device.setDeviceData(syncDeviceData(deviceProfile, device.getDeviceData())); device.setDeviceData(syncDeviceData(deviceProfile, device.getDeviceData()));
@ -530,6 +533,7 @@ public class DeviceServiceImpl extends AbstractCachedEntityService<DeviceCacheKe
device.setTenantId(tenantId); device.setTenantId(tenantId);
device.setCustomerId(null); device.setCustomerId(null);
device.setDeviceProfileId(null);
Device savedDevice = doSaveDevice(device, null, true); Device savedDevice = doSaveDevice(device, null, true);
DeviceCacheEvictEvent oldTenantEvent = new DeviceCacheEvictEvent(oldTenantId, device.getId(), device.getName(), null); DeviceCacheEvictEvent oldTenantEvent = new DeviceCacheEvictEvent(oldTenantId, device.getId(), device.getName(), null);

66
dao/src/main/java/org/thingsboard/server/dao/service/validator/AbstractHasOtaPackageValidator.java

@ -0,0 +1,66 @@
/**
* 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.dao.service.validator;
import org.springframework.beans.factory.annotation.Autowired;
import org.thingsboard.server.common.data.BaseData;
import org.thingsboard.server.common.data.HasOtaPackage;
import org.thingsboard.server.common.data.OtaPackage;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.ota.OtaPackageType;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.ota.OtaPackageService;
import org.thingsboard.server.dao.service.DataValidator;
public abstract class AbstractHasOtaPackageValidator<D extends BaseData<?>> extends DataValidator<D> {
@Autowired
private OtaPackageService otaPackageService;
protected <T extends HasOtaPackage> void validateOtaPackage(TenantId tenantId, T entity, DeviceProfileId deviceProfileId) {
if (entity.getFirmwareId() != null) {
OtaPackage firmware = otaPackageService.findOtaPackageById(tenantId, entity.getFirmwareId());
validateOtaPackage(tenantId, OtaPackageType.FIRMWARE, deviceProfileId, firmware);
}
if (entity.getSoftwareId() != null) {
OtaPackage software = otaPackageService.findOtaPackageById(tenantId, entity.getSoftwareId());
validateOtaPackage(tenantId, OtaPackageType.SOFTWARE, deviceProfileId, software);
}
}
private void validateOtaPackage(TenantId tenantId, OtaPackageType type, DeviceProfileId deviceProfileId, OtaPackage otaPackage) {
if (otaPackage == null) {
throw new DataValidationException(prepareMsg("Can't assign non-existent %s!", type));
}
if (!otaPackage.getTenantId().equals(tenantId)) {
throw new DataValidationException(prepareMsg("Can't assign %s from different tenant!", type));
}
if (!otaPackage.getType().equals(type)) {
throw new DataValidationException(prepareMsg("Can't assign %s with type: " + otaPackage.getType(), type));
}
if (otaPackage.getData() == null && !otaPackage.hasUrl()) {
throw new DataValidationException(prepareMsg("Can't assign %s with empty data!", type));
}
if (!otaPackage.getDeviceProfileId().equals(deviceProfileId)) {
throw new DataValidationException(prepareMsg("Can't assign %s with different deviceProfile!", type));
}
}
private String prepareMsg(String msg, OtaPackageType type) {
return String.format(msg, type.name().toLowerCase());
}
}

41
dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceDataValidator.java

@ -22,17 +22,13 @@ import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.OtaPackage;
import org.thingsboard.server.common.data.device.data.DeviceTransportConfiguration; import org.thingsboard.server.common.data.device.data.DeviceTransportConfiguration;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.ota.OtaPackageType;
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
import org.thingsboard.server.dao.customer.CustomerDao; import org.thingsboard.server.dao.customer.CustomerDao;
import org.thingsboard.server.dao.device.DeviceDao; import org.thingsboard.server.dao.device.DeviceDao;
import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.ota.OtaPackageService;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.tenant.TenantService;
@ -41,7 +37,7 @@ import java.util.Optional;
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
@Component @Component
public class DeviceDataValidator extends DataValidator<Device> { public class DeviceDataValidator extends AbstractHasOtaPackageValidator<Device> {
@Autowired @Autowired
private DeviceDao deviceDao; private DeviceDao deviceDao;
@ -56,9 +52,6 @@ public class DeviceDataValidator extends DataValidator<Device> {
@Lazy @Lazy
private TbTenantProfileCache tenantProfileCache; private TbTenantProfileCache tenantProfileCache;
@Autowired
private OtaPackageService otaPackageService;
@Override @Override
protected void validateCreate(TenantId tenantId, Device device) { protected void validateCreate(TenantId tenantId, Device device) {
DefaultTenantProfileConfiguration profileConfiguration = DefaultTenantProfileConfiguration profileConfiguration =
@ -103,36 +96,6 @@ public class DeviceDataValidator extends DataValidator<Device> {
.flatMap(deviceData -> Optional.ofNullable(deviceData.getTransportConfiguration())) .flatMap(deviceData -> Optional.ofNullable(deviceData.getTransportConfiguration()))
.ifPresent(DeviceTransportConfiguration::validate); .ifPresent(DeviceTransportConfiguration::validate);
if (device.getFirmwareId() != null) { validateOtaPackage(tenantId, device, device.getDeviceProfileId());
OtaPackage firmware = otaPackageService.findOtaPackageById(tenantId, device.getFirmwareId());
if (firmware == null) {
throw new DataValidationException("Can't assign non-existent firmware!");
}
if (!firmware.getType().equals(OtaPackageType.FIRMWARE)) {
throw new DataValidationException("Can't assign firmware with type: " + firmware.getType());
}
if (firmware.getData() == null && !firmware.hasUrl()) {
throw new DataValidationException("Can't assign firmware with empty data!");
}
if (!firmware.getDeviceProfileId().equals(device.getDeviceProfileId())) {
throw new DataValidationException("Can't assign firmware with different deviceProfile!");
}
}
if (device.getSoftwareId() != null) {
OtaPackage software = otaPackageService.findOtaPackageById(tenantId, device.getSoftwareId());
if (software == null) {
throw new DataValidationException("Can't assign non-existent software!");
}
if (!software.getType().equals(OtaPackageType.SOFTWARE)) {
throw new DataValidationException("Can't assign software with type: " + software.getType());
}
if (software.getData() == null && !software.hasUrl()) {
throw new DataValidationException("Can't assign software with empty data!");
}
if (!software.getDeviceProfileId().equals(device.getDeviceProfileId())) {
throw new DataValidationException("Can't assign firmware with different deviceProfile!");
}
}
} }
} }

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

@ -35,8 +35,8 @@ import org.springframework.util.CollectionUtils;
import org.thingsboard.server.common.data.DashboardInfo; import org.thingsboard.server.common.data.DashboardInfo;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceProfileProvisionType; import org.thingsboard.server.common.data.DeviceProfileProvisionType;
import org.thingsboard.server.common.data.OtaPackage;
import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode; import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode;
import org.thingsboard.server.common.data.device.profile.CoapDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.CoapDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.CoapDeviceTypeConfiguration; import org.thingsboard.server.common.data.device.profile.CoapDeviceTypeConfiguration;
@ -52,7 +52,6 @@ import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MBo
import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.RPKLwM2MBootstrapServerCredential; import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.RPKLwM2MBootstrapServerCredential;
import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.X509LwM2MBootstrapServerCredential; import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.X509LwM2MBootstrapServerCredential;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.ota.OtaPackageType;
import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.common.data.queue.Queue;
import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.msg.EncryptionUtil; import org.thingsboard.server.common.msg.EncryptionUtil;
@ -62,10 +61,9 @@ import org.thingsboard.server.dao.device.DeviceProfileDao;
import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.dao.device.DeviceProfileService;
import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.exception.DeviceCredentialsValidationException; import org.thingsboard.server.dao.exception.DeviceCredentialsValidationException;
import org.thingsboard.server.dao.ota.OtaPackageService;
import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.queue.QueueService;
import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.tenant.TenantService;
import java.util.HashSet; import java.util.HashSet;
@ -74,7 +72,7 @@ import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Component @Component
public class DeviceProfileDataValidator extends DataValidator<DeviceProfile> { public class DeviceProfileDataValidator extends AbstractHasOtaPackageValidator<DeviceProfile> {
private static final Location LOCATION = new Location("", "", -1, -1); private static final Location LOCATION = new Location("", "", -1, -1);
private static final String ATTRIBUTES_PROTO_SCHEMA = "attributes proto schema"; private static final String ATTRIBUTES_PROTO_SCHEMA = "attributes proto schema";
@ -94,11 +92,11 @@ public class DeviceProfileDataValidator extends DataValidator<DeviceProfile> {
@Autowired @Autowired
private QueueService queueService; private QueueService queueService;
@Autowired @Autowired
private OtaPackageService otaPackageService;
@Autowired
private RuleChainService ruleChainService; private RuleChainService ruleChainService;
@Autowired @Autowired
private DashboardService dashboardService; private DashboardService dashboardService;
@Autowired
private TbTenantProfileCache tenantProfileCache;
private static String invalidSchemaProvidedMessage(String schemaName) { private static String invalidSchemaProvidedMessage(String schemaName) {
return "[Transport Configuration] invalid " + schemaName + " provided!"; return "[Transport Configuration] invalid " + schemaName + " provided!";
@ -133,6 +131,11 @@ public class DeviceProfileDataValidator extends DataValidator<DeviceProfile> {
if (queue == null) { if (queue == null) {
throw new DataValidationException("Device profile is referencing to non-existent queue!"); throw new DataValidationException("Device profile is referencing to non-existent queue!");
} }
TenantProfile tenantProfile = tenantProfileCache.get(deviceProfile.getTenantId());
if ((tenantProfile.isIsolatedTbRuleEngine() && !queue.getTenantId().equals(deviceProfile.getTenantId()))
|| (!tenantProfile.isIsolatedTbRuleEngine() && !queue.getTenantId().isNullUid())) {
throw new DataValidationException("Can't assign queue from different tenant!");
}
} }
if (deviceProfile.getProvisionType() == null) { if (deviceProfile.getProvisionType() == null) {
deviceProfile.setProvisionType(DeviceProfileProvisionType.DISABLED); deviceProfile.setProvisionType(DeviceProfileProvisionType.DISABLED);
@ -192,6 +195,9 @@ public class DeviceProfileDataValidator extends DataValidator<DeviceProfile> {
if (ruleChain == null) { if (ruleChain == null) {
throw new DataValidationException("Can't assign non-existent rule chain!"); throw new DataValidationException("Can't assign non-existent rule chain!");
} }
if (!ruleChain.getTenantId().equals(deviceProfile.getTenantId())) {
throw new DataValidationException("Can't assign rule chain from different tenant!");
}
} }
if (deviceProfile.getDefaultDashboardId() != null) { if (deviceProfile.getDefaultDashboardId() != null) {
@ -199,39 +205,12 @@ public class DeviceProfileDataValidator extends DataValidator<DeviceProfile> {
if (dashboard == null) { if (dashboard == null) {
throw new DataValidationException("Can't assign non-existent dashboard!"); throw new DataValidationException("Can't assign non-existent dashboard!");
} }
} if (!dashboard.getTenantId().equals(deviceProfile.getTenantId())) {
throw new DataValidationException("Can't assign dashboard from different tenant!");
if (deviceProfile.getFirmwareId() != null) {
OtaPackage firmware = otaPackageService.findOtaPackageById(tenantId, deviceProfile.getFirmwareId());
if (firmware == null) {
throw new DataValidationException("Can't assign non-existent firmware!");
}
if (!firmware.getType().equals(OtaPackageType.FIRMWARE)) {
throw new DataValidationException("Can't assign firmware with type: " + firmware.getType());
}
if (firmware.getData() == null && !firmware.hasUrl()) {
throw new DataValidationException("Can't assign firmware with empty data!");
}
if (!firmware.getDeviceProfileId().equals(deviceProfile.getId())) {
throw new DataValidationException("Can't assign firmware with different deviceProfile!");
} }
} }
if (deviceProfile.getSoftwareId() != null) { validateOtaPackage(tenantId, deviceProfile, deviceProfile.getId());
OtaPackage software = otaPackageService.findOtaPackageById(tenantId, deviceProfile.getSoftwareId());
if (software == null) {
throw new DataValidationException("Can't assign non-existent software!");
}
if (!software.getType().equals(OtaPackageType.SOFTWARE)) {
throw new DataValidationException("Can't assign software with type: " + software.getType());
}
if (software.getData() == null && !software.hasUrl()) {
throw new DataValidationException("Can't assign software with empty data!");
}
if (!software.getDeviceProfileId().equals(deviceProfile.getId())) {
throw new DataValidationException("Can't assign firmware with different deviceProfile!");
}
}
} }
@Override @Override

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

@ -99,6 +99,7 @@ public class TenantServiceImpl extends AbstractCachedEntityService<TenantId, Ten
private ResourceService resourceService; private ResourceService resourceService;
@Autowired @Autowired
@Lazy
private OtaPackageService otaPackageService; private OtaPackageService otaPackageService;
@Autowired @Autowired

5
docker/.env

@ -25,4 +25,7 @@ DATABASE=postgres
LOAD_BALANCER_NAME=haproxy-certbot LOAD_BALANCER_NAME=haproxy-certbot
# If enabled Prometheus and Grafana containers are deployed along with other containers # If enabled Prometheus and Grafana containers are deployed along with other containers
MONITORING_ENABLED=false MONITORING_ENABLED=false
# Limit memory usage for each Java application
# JAVA_OPTS=-Xmx2048M -Xms2048M -Xss384k -XX:+AlwaysPreTouch

13
docker/docker-compose.yml

@ -48,6 +48,7 @@ services:
TB_SERVICE_ID: tb-core1 TB_SERVICE_ID: tb-core1
TB_SERVICE_TYPE: tb-core TB_SERVICE_TYPE: tb-core
EDGES_ENABLED: "true" EDGES_ENABLED: "true"
JAVA_OPTS: "${JAVA_OPTS}"
env_file: env_file:
- tb-node.env - tb-node.env
volumes: volumes:
@ -73,6 +74,7 @@ services:
TB_SERVICE_ID: tb-core2 TB_SERVICE_ID: tb-core2
TB_SERVICE_TYPE: tb-core TB_SERVICE_TYPE: tb-core
EDGES_ENABLED: "true" EDGES_ENABLED: "true"
JAVA_OPTS: "${JAVA_OPTS}"
env_file: env_file:
- tb-node.env - tb-node.env
volumes: volumes:
@ -96,6 +98,7 @@ services:
environment: environment:
TB_SERVICE_ID: tb-rule-engine1 TB_SERVICE_ID: tb-rule-engine1
TB_SERVICE_TYPE: tb-rule-engine TB_SERVICE_TYPE: tb-rule-engine
JAVA_OPTS: "${JAVA_OPTS}"
env_file: env_file:
- tb-node.env - tb-node.env
volumes: volumes:
@ -117,6 +120,7 @@ services:
environment: environment:
TB_SERVICE_ID: tb-rule-engine2 TB_SERVICE_ID: tb-rule-engine2
TB_SERVICE_TYPE: tb-rule-engine TB_SERVICE_TYPE: tb-rule-engine
JAVA_OPTS: "${JAVA_OPTS}"
env_file: env_file:
- tb-node.env - tb-node.env
volumes: volumes:
@ -132,6 +136,7 @@ services:
- "1883" - "1883"
environment: environment:
TB_SERVICE_ID: tb-mqtt-transport1 TB_SERVICE_ID: tb-mqtt-transport1
JAVA_OPTS: "${JAVA_OPTS}"
env_file: env_file:
- tb-mqtt-transport.env - tb-mqtt-transport.env
volumes: volumes:
@ -148,6 +153,7 @@ services:
- "1883" - "1883"
environment: environment:
TB_SERVICE_ID: tb-mqtt-transport2 TB_SERVICE_ID: tb-mqtt-transport2
JAVA_OPTS: "${JAVA_OPTS}"
env_file: env_file:
- tb-mqtt-transport.env - tb-mqtt-transport.env
volumes: volumes:
@ -164,6 +170,7 @@ services:
- "8081" - "8081"
environment: environment:
TB_SERVICE_ID: tb-http-transport1 TB_SERVICE_ID: tb-http-transport1
JAVA_OPTS: "${JAVA_OPTS}"
env_file: env_file:
- tb-http-transport.env - tb-http-transport.env
volumes: volumes:
@ -180,6 +187,7 @@ services:
- "8081" - "8081"
environment: environment:
TB_SERVICE_ID: tb-http-transport2 TB_SERVICE_ID: tb-http-transport2
JAVA_OPTS: "${JAVA_OPTS}"
env_file: env_file:
- tb-http-transport.env - tb-http-transport.env
volumes: volumes:
@ -196,6 +204,7 @@ services:
- "5683:5683/udp" - "5683:5683/udp"
environment: environment:
TB_SERVICE_ID: tb-coap-transport TB_SERVICE_ID: tb-coap-transport
JAVA_OPTS: "${JAVA_OPTS}"
env_file: env_file:
- tb-coap-transport.env - tb-coap-transport.env
volumes: volumes:
@ -212,6 +221,7 @@ services:
- "5685:5685/udp" - "5685:5685/udp"
environment: environment:
TB_SERVICE_ID: tb-lwm2m-transport TB_SERVICE_ID: tb-lwm2m-transport
JAVA_OPTS: "${JAVA_OPTS}"
env_file: env_file:
- tb-lwm2m-transport.env - tb-lwm2m-transport.env
volumes: volumes:
@ -226,6 +236,7 @@ services:
image: "${DOCKER_REPO}/${SNMP_TRANSPORT_DOCKER_NAME}:${TB_VERSION}" image: "${DOCKER_REPO}/${SNMP_TRANSPORT_DOCKER_NAME}:${TB_VERSION}"
environment: environment:
TB_SERVICE_ID: tb-snmp-transport TB_SERVICE_ID: tb-snmp-transport
JAVA_OPTS: "${JAVA_OPTS}"
env_file: env_file:
- tb-snmp-transport.env - tb-snmp-transport.env
volumes: volumes:
@ -256,6 +267,7 @@ services:
- "8081" - "8081"
environment: environment:
TB_SERVICE_ID: tb-vc-executor1 TB_SERVICE_ID: tb-vc-executor1
JAVA_OPTS: "${JAVA_OPTS}"
env_file: env_file:
- tb-vc-executor.env - tb-vc-executor.env
volumes: volumes:
@ -272,6 +284,7 @@ services:
- "8081" - "8081"
environment: environment:
TB_SERVICE_ID: tb-vc-executor2 TB_SERVICE_ID: tb-vc-executor2
JAVA_OPTS: "${JAVA_OPTS}"
env_file: env_file:
- tb-vc-executor.env - tb-vc-executor.env
volumes: volumes:

85
msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ContainerTestSuite.java

@ -22,14 +22,19 @@ import org.junit.extensions.cpsuite.ClasspathSuite;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.testcontainers.containers.DockerComposeContainer; import org.testcontainers.containers.DockerComposeContainer;
import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.containers.wait.strategy.Wait;
import org.thingsboard.server.common.data.StringUtils;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration; import java.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.UUID; import java.util.UUID;
import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.containsString;
@ -44,10 +49,13 @@ import static org.junit.Assert.fail;
public class ContainerTestSuite { public class ContainerTestSuite {
final static boolean IS_REDIS_CLUSTER = Boolean.parseBoolean(System.getProperty("blackBoxTests.redisCluster")); final static boolean IS_REDIS_CLUSTER = Boolean.parseBoolean(System.getProperty("blackBoxTests.redisCluster"));
final static boolean IS_HYBRID_MODE = Boolean.parseBoolean(System.getProperty("blackBoxTests.hybridMode")); final static boolean IS_HYBRID_MODE = Boolean.parseBoolean(System.getProperty("blackBoxTests.hybridMode"));
final static String QUEUE_TYPE = System.getProperty("blackBoxTests.queue", "kafka");
private static final String SOURCE_DIR = "./../../docker/"; private static final String SOURCE_DIR = "./../../docker/";
private static final String TB_CORE_LOG_REGEXP = ".*Starting polling for events.*"; private static final String TB_CORE_LOG_REGEXP = ".*Starting polling for events.*";
private static final String TRANSPORTS_LOG_REGEXP = ".*Going to recalculate partitions.*"; private static final String TRANSPORTS_LOG_REGEXP = ".*Going to recalculate partitions.*";
private static final String TB_VC_LOG_REGEXP = TRANSPORTS_LOG_REGEXP; private static final String TB_VC_LOG_REGEXP = TRANSPORTS_LOG_REGEXP;
private static final String TB_JS_EXECUTOR_LOG_REGEXP = ".*template started.*";
private static final Duration CONTAINER_STARTUP_TIMEOUT = Duration.ofSeconds(400);
private static DockerComposeContainer<?> testContainer; private static DockerComposeContainer<?> testContainer;
@ -66,6 +74,8 @@ public class ContainerTestSuite {
FileUtils.copyDirectory(new File(SOURCE_DIR), new File(targetDir)); FileUtils.copyDirectory(new File(SOURCE_DIR), new File(targetDir));
replaceInFile(targetDir + "docker-compose.yml", " container_name: \"${LOAD_BALANCER_NAME}\"", "", "container_name"); replaceInFile(targetDir + "docker-compose.yml", " container_name: \"${LOAD_BALANCER_NAME}\"", "", "container_name");
FileUtils.copyDirectory(new File("src/test/resources"), new File(targetDir));
class DockerComposeContainerImpl<SELF extends DockerComposeContainer<SELF>> extends DockerComposeContainer<SELF> { class DockerComposeContainerImpl<SELF extends DockerComposeContainer<SELF>> extends DockerComposeContainer<SELF> {
public DockerComposeContainerImpl(List<File> composeFiles) { public DockerComposeContainerImpl(List<File> composeFiles) {
super(composeFiles); super(composeFiles);
@ -81,17 +91,33 @@ public class ContainerTestSuite {
List<File> composeFiles = new ArrayList<>(Arrays.asList( List<File> composeFiles = new ArrayList<>(Arrays.asList(
new File(targetDir + "docker-compose.yml"), new File(targetDir + "docker-compose.yml"),
new File(targetDir + "docker-compose.volumes.yml"), new File(targetDir + "docker-compose.volumes.yml"),
IS_HYBRID_MODE new File(targetDir + (IS_HYBRID_MODE ? "docker-compose.hybrid.yml" : "docker-compose.postgres.yml")),
? new File(targetDir + "docker-compose.hybrid.yml")
: new File(targetDir + "docker-compose.postgres.yml"),
new File(targetDir + "docker-compose.postgres.volumes.yml"), new File(targetDir + "docker-compose.postgres.volumes.yml"),
new File(targetDir + "docker-compose.kafka.yml"), new File(targetDir + "docker-compose." + QUEUE_TYPE + ".yml"),
IS_REDIS_CLUSTER new File(targetDir + (IS_REDIS_CLUSTER ? "docker-compose.redis-cluster.yml" : "docker-compose.redis.yml")),
? new File(targetDir + "docker-compose.redis-cluster.yml") new File(targetDir + (IS_HYBRID_MODE ? "docker-compose.redis-cluster.volumes.yml" : "docker-compose.redis.volumes.yml"))
: new File(targetDir + "docker-compose.redis.yml"), ));
IS_REDIS_CLUSTER
? new File(targetDir + "docker-compose.redis-cluster.volumes.yml") Map<String, String> queueEnv = new HashMap<>();
: new File(targetDir + "docker-compose.redis.volumes.yml"))); queueEnv.put("TB_QUEUE_TYPE", QUEUE_TYPE);
switch (QUEUE_TYPE) {
case "kafka":
composeFiles.add(new File(targetDir + "docker-compose.kafka.yml"));
break;
case "aws-sqs":
replaceInFile(targetDir, "queue-aws-sqs.env",
Map.of("YOUR_KEY", getSysProp("blackBoxTests.awsKey"),
"YOUR_SECRET", "blackBoxTests.awsSecret",
"YOUR_REGION", "blackBoxTests.awsRegion"));
break;
case "rabbitmq":
composeFiles.add(new File(targetDir + "docker-compose.rabbitmq-server.yml"));
replaceInFile(targetDir, "queue-rabbitmq.env",
Map.of("localhost", "rabbitmq"));
break;
default:
throw new RuntimeException("Unsupported queue type: " + QUEUE_TYPE);
}
if (IS_HYBRID_MODE) { if (IS_HYBRID_MODE) {
composeFiles.add(new File(targetDir + "docker-compose.cassandra.volumes.yml")); composeFiles.add(new File(targetDir + "docker-compose.cassandra.volumes.yml"));
@ -102,16 +128,18 @@ public class ContainerTestSuite {
.withLocalCompose(true) .withLocalCompose(true)
.withTailChildContainers(!skipTailChildContainers) .withTailChildContainers(!skipTailChildContainers)
.withEnv(installTb.getEnv()) .withEnv(installTb.getEnv())
.withEnv(queueEnv)
.withEnv("LOAD_BALANCER_NAME", "") .withEnv("LOAD_BALANCER_NAME", "")
.withExposedService("haproxy", 80, Wait.forHttp("/swagger-ui.html").withStartupTimeout(Duration.ofSeconds(400))) .withExposedService("haproxy", 80, Wait.forHttp("/swagger-ui.html").withStartupTimeout(CONTAINER_STARTUP_TIMEOUT))
.waitingFor("tb-core1", Wait.forLogMessage(TB_CORE_LOG_REGEXP, 1).withStartupTimeout(Duration.ofSeconds(400))) .waitingFor("tb-core1", Wait.forLogMessage(TB_CORE_LOG_REGEXP, 1).withStartupTimeout(CONTAINER_STARTUP_TIMEOUT))
.waitingFor("tb-core2", Wait.forLogMessage(TB_CORE_LOG_REGEXP, 1).withStartupTimeout(Duration.ofSeconds(400))) .waitingFor("tb-core2", Wait.forLogMessage(TB_CORE_LOG_REGEXP, 1).withStartupTimeout(CONTAINER_STARTUP_TIMEOUT))
.waitingFor("tb-http-transport1", Wait.forLogMessage(TRANSPORTS_LOG_REGEXP, 1).withStartupTimeout(Duration.ofSeconds(400))) .waitingFor("tb-http-transport1", Wait.forLogMessage(TRANSPORTS_LOG_REGEXP, 1).withStartupTimeout(CONTAINER_STARTUP_TIMEOUT))
.waitingFor("tb-http-transport2", Wait.forLogMessage(TRANSPORTS_LOG_REGEXP, 1).withStartupTimeout(Duration.ofSeconds(400))) .waitingFor("tb-http-transport2", Wait.forLogMessage(TRANSPORTS_LOG_REGEXP, 1).withStartupTimeout(CONTAINER_STARTUP_TIMEOUT))
.waitingFor("tb-mqtt-transport1", Wait.forLogMessage(TRANSPORTS_LOG_REGEXP, 1).withStartupTimeout(Duration.ofSeconds(400))) .waitingFor("tb-mqtt-transport1", Wait.forLogMessage(TRANSPORTS_LOG_REGEXP, 1).withStartupTimeout(CONTAINER_STARTUP_TIMEOUT))
.waitingFor("tb-mqtt-transport2", Wait.forLogMessage(TRANSPORTS_LOG_REGEXP, 1).withStartupTimeout(Duration.ofSeconds(400))) .waitingFor("tb-mqtt-transport2", Wait.forLogMessage(TRANSPORTS_LOG_REGEXP, 1).withStartupTimeout(CONTAINER_STARTUP_TIMEOUT))
.waitingFor("tb-vc-executor1", Wait.forLogMessage(TB_VC_LOG_REGEXP, 1).withStartupTimeout(Duration.ofSeconds(400))) .waitingFor("tb-vc-executor1", Wait.forLogMessage(TB_VC_LOG_REGEXP, 1).withStartupTimeout(CONTAINER_STARTUP_TIMEOUT))
.waitingFor("tb-vc-executor2", Wait.forLogMessage(TB_VC_LOG_REGEXP, 1).withStartupTimeout(Duration.ofSeconds(400))); .waitingFor("tb-vc-executor2", Wait.forLogMessage(TB_VC_LOG_REGEXP, 1).withStartupTimeout(CONTAINER_STARTUP_TIMEOUT))
.waitingFor("tb-js-executor", Wait.forLogMessage(TB_JS_EXECUTOR_LOG_REGEXP, 1).withStartupTimeout(CONTAINER_STARTUP_TIMEOUT));
} catch (Exception e) { } catch (Exception e) {
log.error("Failed to create test container", e); log.error("Failed to create test container", e);
fail("Failed to create test container"); fail("Failed to create test container");
@ -120,6 +148,23 @@ public class ContainerTestSuite {
return testContainer; return testContainer;
} }
private static void replaceInFile(String targetDir, String fileName, Map<String, String> replacements) throws IOException {
Path envFilePath = Path.of(targetDir, fileName);
String data = Files.readString(envFilePath);
for (var entry : replacements.entrySet()) {
data = data.replace(entry.getKey(), entry.getValue());
}
Files.write(envFilePath, data.getBytes(StandardCharsets.UTF_8));
}
private static String getSysProp(String propertyName) {
var value = System.getProperty(propertyName);
if (StringUtils.isEmpty(value)) {
throw new RuntimeException("Please define system property: " + propertyName + "!");
}
return value;
}
private static void tryDeleteDir(String targetDir) { private static void tryDeleteDir(String targetDir) {
try { try {
log.info("Trying to delete temp dir {}", targetDir); log.info("Trying to delete temp dir {}", targetDir);
@ -135,7 +180,7 @@ public class ContainerTestSuite {
* docker-compose files which contain container_name are not supported and the creation of DockerComposeContainer fails due to IllegalStateException. * docker-compose files which contain container_name are not supported and the creation of DockerComposeContainer fails due to IllegalStateException.
* This has been introduced in #1151 as a quick fix for unintuitive feedback. https://github.com/testcontainers/testcontainers-java/issues/1151 * This has been introduced in #1151 as a quick fix for unintuitive feedback. https://github.com/testcontainers/testcontainers-java/issues/1151
* Using the latest testcontainers and waiting for the fix... * Using the latest testcontainers and waiting for the fix...
* */ */
private static void replaceInFile(String sourceFilename, String target, String replacement, String verifyPhrase) { private static void replaceInFile(String sourceFilename, String target, String replacement, String verifyPhrase) {
try { try {
File file = new File(sourceFilename); File file = new File(sourceFilename);

2
msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ThingsBoardDbInstaller.java

@ -46,6 +46,7 @@ public class ThingsBoardDbInstaller extends ExternalResource {
private final static String TB_MQTT_TRANSPORT_LOG_VOLUME = "tb-mqtt-transport-log-test-volume"; private final static String TB_MQTT_TRANSPORT_LOG_VOLUME = "tb-mqtt-transport-log-test-volume";
private final static String TB_SNMP_TRANSPORT_LOG_VOLUME = "tb-snmp-transport-log-test-volume"; private final static String TB_SNMP_TRANSPORT_LOG_VOLUME = "tb-snmp-transport-log-test-volume";
private final static String TB_VC_EXECUTOR_LOG_VOLUME = "tb-vc-executor-log-test-volume"; private final static String TB_VC_EXECUTOR_LOG_VOLUME = "tb-vc-executor-log-test-volume";
private final static String JAVA_OPTS = "-Xmx384m -Xss256k";
private final DockerComposeExecutor dockerCompose; private final DockerComposeExecutor dockerCompose;
@ -102,6 +103,7 @@ public class ThingsBoardDbInstaller extends ExternalResource {
dockerCompose = new DockerComposeExecutor(composeFiles, project); dockerCompose = new DockerComposeExecutor(composeFiles, project);
env = new HashMap<>(); env = new HashMap<>();
env.put("JAVA_OPTS", JAVA_OPTS);
env.put("POSTGRES_DATA_VOLUME", postgresDataVolume); env.put("POSTGRES_DATA_VOLUME", postgresDataVolume);
if (IS_HYBRID_MODE) { if (IS_HYBRID_MODE) {
env.put("CASSANDRA_DATA_VOLUME", cassandraDataVolume); env.put("CASSANDRA_DATA_VOLUME", cassandraDataVolume);

69
msa/black-box-tests/src/test/resources/docker-compose.rabbitmq-server.yml

@ -0,0 +1,69 @@
#
# 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.
#
version: '2.2'
services:
rabbitmq:
restart: always
image: rabbitmq:3
ports:
- '5672:5672'
environment:
RABBITMQ_DEFAULT_USER: YOUR_USERNAME
RABBITMQ_DEFAULT_PASS: YOUR_PASSWORD
tb-js-executor:
depends_on:
- rabbitmq
tb-core1:
depends_on:
- rabbitmq
tb-core2:
depends_on:
- rabbitmq
tb-rule-engine1:
depends_on:
- rabbitmq
tb-rule-engine2:
depends_on:
- rabbitmq
tb-mqtt-transport1:
depends_on:
- rabbitmq
tb-mqtt-transport2:
depends_on:
- rabbitmq
tb-http-transport1:
depends_on:
- rabbitmq
tb-http-transport2:
depends_on:
- rabbitmq
tb-coap-transport:
depends_on:
- rabbitmq
tb-lwm2m-transport:
depends_on:
- rabbitmq
tb-snmp-transport:
depends_on:
- rabbitmq
tb-vc-executor1:
depends_on:
- rabbitmq
tb-vc-executor2:
depends_on:
- rabbitmq

31
msa/js-executor/api/httpServer.ts

@ -16,12 +16,15 @@
import express from 'express'; import express from 'express';
import { _logger} from '../config/logger'; import { _logger} from '../config/logger';
import http from 'http';
import { Socket } from 'net';
export class HttpServer { export class HttpServer {
private logger = _logger('httpServer'); private logger = _logger('httpServer');
private app = express(); private app = express();
private server; private server: http.Server | null;
private connections: Socket[] = [];
constructor(httpPort: number) { constructor(httpPort: number) {
this.app.get('/livenessProbe', async (req, res) => { this.app.get('/livenessProbe', async (req, res) => {
@ -32,15 +35,31 @@ export class HttpServer {
}) })
this.server = this.app.listen(httpPort, () => { this.server = this.app.listen(httpPort, () => {
this.logger.info('Started http endpoint on port %s. Please, use /livenessProbe !', httpPort); this.logger.info('Started HTTP endpoint on port %s. Please, use /livenessProbe !', httpPort);
}).on('error', (error) => { }).on('error', (error) => {
this.logger.error(error); this.logger.error(error);
}); });
}
stop() { this.server.on('connection', connection => {
this.server.close(() => { this.connections.push(connection);
this.logger.info('Http server stop'); connection.on('close', () => this.connections = this.connections.filter(curr => curr !== connection));
}); });
} }
async stop() {
if (this.server) {
this.logger.info('Stopping HTTP Server...');
const _server = this.server;
this.server = null;
this.connections.forEach(curr => curr.end(() => curr.destroy()));
await new Promise<void>(
(resolve, reject) => {
_server.close((err) => {
this.logger.info('HTTP Server stopped.');
resolve();
});
}
);
}
}
} }

2
msa/js-executor/package.json

@ -20,7 +20,7 @@
"config": "^3.3.7", "config": "^3.3.7",
"express": "^4.18.1", "express": "^4.18.1",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"kafkajs": "^2.0.2", "kafkajs": "^2.1.0",
"long": "^5.2.0", "long": "^5.2.0",
"uuid-parse": "^1.1.0", "uuid-parse": "^1.1.0",
"uuid-random": "^1.3.2", "uuid-random": "^1.3.2",

144
msa/js-executor/queue/awsSqsTemplate.ts

@ -54,82 +54,76 @@ export class AwsSqsTemplate implements IQueue {
FifoQueue: 'true' FifoQueue: 'true'
}; };
name = 'AWS SQS';
constructor() { constructor() {
} }
async init() { async init() {
try { this.sqsClient = new SQSClient({
this.logger.info('Starting ThingsBoard JavaScript Executor Microservice...'); apiVersion: '2012-11-05',
credentials: {
this.sqsClient = new SQSClient({ accessKeyId: this.accessKeyId,
apiVersion: '2012-11-05', secretAccessKey: this.secretAccessKey
credentials: { },
accessKeyId: this.accessKeyId, region: this.region
secretAccessKey: this.secretAccessKey });
},
region: this.region
});
const queues = await this.getQueues(); const queues = await this.getQueues();
if (queues.QueueUrls) { if (queues.QueueUrls) {
queues.QueueUrls.forEach(queueUrl => { queues.QueueUrls.forEach(queueUrl => {
const delimiterPosition = queueUrl.lastIndexOf('/'); const delimiterPosition = queueUrl.lastIndexOf('/');
const queueName = queueUrl.substring(delimiterPosition + 1); const queueName = queueUrl.substring(delimiterPosition + 1);
this.queueUrls.set(queueName, queueUrl); this.queueUrls.set(queueName, queueUrl);
}); });
} }
this.parseQueueProperties(); this.parseQueueProperties();
this.requestQueueURL = this.queueUrls.get(AwsSqsTemplate.topicToSqsQueueName(this.requestTopic)) || ''; this.requestQueueURL = this.queueUrls.get(AwsSqsTemplate.topicToSqsQueueName(this.requestTopic)) || '';
if (!this.requestQueueURL) { if (!this.requestQueueURL) {
this.requestQueueURL = await this.createQueue(this.requestTopic); this.requestQueueURL = await this.createQueue(this.requestTopic);
} }
const messageProcessor = new JsInvokeMessageProcessor(this);
const messageProcessor = new JsInvokeMessageProcessor(this); const params: ReceiveMessageRequest = {
MaxNumberOfMessages: 10,
const params: ReceiveMessageRequest = { QueueUrl: this.requestQueueURL,
MaxNumberOfMessages: 10, WaitTimeSeconds: this.pollInterval / 1000
QueueUrl: this.requestQueueURL, };
WaitTimeSeconds: this.pollInterval / 1000 while (!this.stopped) {
}; let pollStartTs = new Date().getTime();
while (!this.stopped) { const messagesResponse: ReceiveMessageResult = await this.sqsClient.send(new ReceiveMessageCommand(params));
let pollStartTs = new Date().getTime(); const messages = messagesResponse.Messages;
const messagesResponse: ReceiveMessageResult = await this.sqsClient.send(new ReceiveMessageCommand(params));
const messages = messagesResponse.Messages; if (messages && messages.length > 0) {
const entries: DeleteMessageBatchRequestEntry[] = [];
if (messages && messages.length > 0) {
const entries: DeleteMessageBatchRequestEntry[] = []; messages.forEach(message => {
entries.push({
messages.forEach(message => { Id: message.MessageId,
entries.push({ ReceiptHandle: message.ReceiptHandle
Id: message.MessageId,
ReceiptHandle: message.ReceiptHandle
});
messageProcessor.onJsInvokeMessage(JSON.parse(message.Body || ''));
}); });
messageProcessor.onJsInvokeMessage(JSON.parse(message.Body || ''));
});
const deleteBatch: DeleteMessageBatchRequest = { const deleteBatch: DeleteMessageBatchRequest = {
QueueUrl: this.requestQueueURL, QueueUrl: this.requestQueueURL,
Entries: entries Entries: entries
}; };
try { try {
await this.sqsClient.send(new DeleteMessageBatchCommand(deleteBatch)) await this.sqsClient.send(new DeleteMessageBatchCommand(deleteBatch))
} catch (err: any) { } catch (err: any) {
this.logger.error("Failed to delete messages from queue.", err.message); this.logger.error("Failed to delete messages from queue.", err.message);
} }
} else { } else {
let pollDuration = new Date().getTime() - pollStartTs; let pollDuration = new Date().getTime() - pollStartTs;
if (pollDuration < this.pollInterval) { if (pollDuration < this.pollInterval) {
await sleep(this.pollInterval - pollDuration); await sleep(this.pollInterval - pollDuration);
}
} }
} }
} catch (e: any) {
this.logger.error('Failed to start ThingsBoard JavaScript Executor Microservice: %s', e.message);
this.logger.error(e.stack);
await this.exit(-1);
} }
} }
@ -187,29 +181,21 @@ export class AwsSqsTemplate implements IQueue {
return result.QueueUrl || ''; return result.QueueUrl || '';
} }
static async build(): Promise<AwsSqsTemplate> { async destroy(): Promise<void> {
const queue = new AwsSqsTemplate();
await queue.init();
return queue;
}
async exit(status: number) {
this.stopped = true; this.stopped = true;
this.logger.info('Exiting with status: %d ...', status); this.logger.info('Stopping AWS SQS resources...');
if (this.sqsClient) { if (this.sqsClient) {
this.logger.info('Stopping Aws Sqs client.') this.logger.info('Stopping AWS SQS client...');
try { try {
this.sqsClient.destroy(); const _sqsClient = this.sqsClient;
// @ts-ignore // @ts-ignore
delete this.sqsClient; delete this.sqsClient;
this.logger.info('Aws Sqs client stopped.') _sqsClient.destroy();
process.exit(status); this.logger.info('AWS SQS client stopped.');
} catch (e: any) { } catch (e: any) {
this.logger.info('Aws Sqs client stop error.'); this.logger.info('AWS SQS client stop error.');
process.exit(status);
} }
} else {
process.exit(status);
} }
this.logger.info('AWS SQS resources stopped.')
} }
} }

186
msa/js-executor/queue/kafkaTemplate.ts

@ -51,111 +51,103 @@ export class KafkaTemplate implements IQueue {
private batchMessages: TopicMessages[] = []; private batchMessages: TopicMessages[] = [];
private sendLoopInstance: NodeJS.Timeout; private sendLoopInstance: NodeJS.Timeout;
name = 'Kafka';
constructor() { constructor() {
} }
async init(): Promise<void> { async init(): Promise<void> {
try { const kafkaBootstrapServers: string = config.get('kafka.bootstrap.servers');
this.logger.info('Starting ThingsBoard JavaScript Executor Microservice...'); const requestTopic: string = config.get('request_topic');
const useConfluent = config.get('kafka.use_confluent_cloud');
const kafkaBootstrapServers: string = config.get('kafka.bootstrap.servers');
const requestTopic: string = config.get('request_topic');
const useConfluent = config.get('kafka.use_confluent_cloud');
this.logger.info('Kafka Bootstrap Servers: %s', kafkaBootstrapServers); this.logger.info('Kafka Bootstrap Servers: %s', kafkaBootstrapServers);
this.logger.info('Kafka Requests Topic: %s', requestTopic); this.logger.info('Kafka Requests Topic: %s', requestTopic);
let kafkaConfig: KafkaConfig = { let kafkaConfig: KafkaConfig = {
brokers: kafkaBootstrapServers.split(','), brokers: kafkaBootstrapServers.split(','),
logLevel: logLevel.INFO, logLevel: logLevel.INFO,
logCreator: KafkaJsWinstonLogCreator logCreator: KafkaJsWinstonLogCreator
}; };
if (this.kafkaClientId) { if (this.kafkaClientId) {
kafkaConfig['clientId'] = this.kafkaClientId; kafkaConfig['clientId'] = this.kafkaClientId;
} else { } else {
this.logger.warn('KAFKA_CLIENT_ID is undefined. Consider to define the env variable KAFKA_CLIENT_ID'); this.logger.warn('KAFKA_CLIENT_ID is undefined. Consider to define the env variable KAFKA_CLIENT_ID');
} }
kafkaConfig['requestTimeout'] = this.requestTimeout; kafkaConfig['requestTimeout'] = this.requestTimeout;
if (useConfluent) { if (useConfluent) {
kafkaConfig['sasl'] = { kafkaConfig['sasl'] = {
mechanism: config.get('kafka.confluent.sasl.mechanism') as any, mechanism: config.get('kafka.confluent.sasl.mechanism') as any,
username: config.get('kafka.confluent.username'), username: config.get('kafka.confluent.username'),
password: config.get('kafka.confluent.password') password: config.get('kafka.confluent.password')
}; };
kafkaConfig['ssl'] = true; kafkaConfig['ssl'] = true;
} }
this.parseTopicProperties(); this.parseTopicProperties();
this.kafkaClient = new Kafka(kafkaConfig); this.kafkaClient = new Kafka(kafkaConfig);
this.kafkaAdmin = this.kafkaClient.admin(); this.kafkaAdmin = this.kafkaClient.admin();
await this.kafkaAdmin.connect(); await this.kafkaAdmin.connect();
let partitions = 1; let partitions = 1;
for (let i = 0; i < this.configEntries.length; i++) { for (let i = 0; i < this.configEntries.length; i++) {
let param = this.configEntries[i]; let param = this.configEntries[i];
if (param.name === 'partitions') { if (param.name === 'partitions') {
partitions = param.value; partitions = param.value;
this.configEntries.splice(i, 1); this.configEntries.splice(i, 1);
break; break;
}
} }
}
let topics = await this.kafkaAdmin.listTopics(); let topics = await this.kafkaAdmin.listTopics();
if (!topics.includes(requestTopic)) { if (!topics.includes(requestTopic)) {
let createRequestTopicResult = await this.createTopic(requestTopic, partitions); let createRequestTopicResult = await this.createTopic(requestTopic, partitions);
if (createRequestTopicResult) { if (createRequestTopicResult) {
this.logger.info('Created new topic: %s', requestTopic); this.logger.info('Created new topic: %s', requestTopic);
}
} }
}
this.consumer = this.kafkaClient.consumer({groupId: 'js-executor-group'}); this.consumer = this.kafkaClient.consumer({groupId: 'js-executor-group'});
this.producer = this.kafkaClient.producer({createPartitioner: Partitioners.DefaultPartitioner}); this.producer = this.kafkaClient.producer({createPartitioner: Partitioners.DefaultPartitioner});
const {CRASH} = this.consumer.events;
this.consumer.on(CRASH, e => { const {CRASH} = this.consumer.events;
this.logger.error(`Got consumer CRASH event, should restart: ${e.payload.restart}`);
if (!e.payload.restart) {
this.logger.error('Going to exit due to not retryable error!');
this.exit(-1);
}
});
const messageProcessor = new JsInvokeMessageProcessor(this); this.consumer.on(CRASH, async (e) => {
await this.consumer.connect(); this.logger.error(`Got consumer CRASH event, should restart: ${e.payload.restart}`);
await this.producer.connect(); if (!e.payload.restart) {
this.sendLoopWithLinger(); this.logger.error('Going to exit due to not retryable error!');
await this.consumer.subscribe({topic: requestTopic}); await this.destroy();
}
this.logger.info('Started ThingsBoard JavaScript Executor Microservice.'); });
await this.consumer.run({
partitionsConsumedConcurrently: this.partitionsConsumedConcurrently,
eachMessage: async ({topic, partition, message}) => {
let headers = message.headers;
let key = message.key || new Buffer([]);
let msg = {
key: key.toString('utf8'),
data: message.value,
headers: {
data: headers
}
};
messageProcessor.onJsInvokeMessage(msg);
},
});
} catch (e: any) { const messageProcessor = new JsInvokeMessageProcessor(this);
this.logger.error('Failed to start ThingsBoard JavaScript Executor Microservice: %s', e.message); await this.consumer.connect();
this.logger.error(e.stack); await this.producer.connect();
await this.exit(-1); this.sendLoopWithLinger();
} await this.consumer.subscribe({topic: requestTopic});
}
await this.consumer.run({
partitionsConsumedConcurrently: this.partitionsConsumedConcurrently,
eachMessage: async ({topic, partition, message}) => {
let headers = message.headers;
let key = message.key || new Buffer([]);
let msg = {
key: key.toString('utf8'),
data: message.value,
headers: {
data: headers
}
};
messageProcessor.onJsInvokeMessage(msg);
},
});
}
async send(responseTopic: string, scriptId: string, rawResponse: Buffer, headers: any): Promise<any> { async send(responseTopic: string, scriptId: string, rawResponse: Buffer, headers: any): Promise<any> {
this.logger.debug('Pending queue response, scriptId: [%s]', scriptId); this.logger.debug('Pending queue response, scriptId: [%s]', scriptId);
@ -235,41 +227,33 @@ export class KafkaTemplate implements IQueue {
}, this.linger); }, this.linger);
} }
static async build(): Promise<KafkaTemplate> { async destroy(): Promise<void> {
const queue = new KafkaTemplate(); this.logger.info('Stopping Kafka resources...');
await queue.init();
return queue;
}
async exit(status: number): Promise<void> {
this.logger.info('Exiting with status: %d ...', status);
if (this.kafkaAdmin) { if (this.kafkaAdmin) {
this.logger.info('Stopping Kafka Admin...'); this.logger.info('Stopping Kafka Admin...');
await this.kafkaAdmin.disconnect(); const _kafkaAdmin = this.kafkaAdmin;
// @ts-ignore // @ts-ignore
delete this.kafkaAdmin; delete this.kafkaAdmin;
await _kafkaAdmin.disconnect();
this.logger.info('Kafka Admin stopped.'); this.logger.info('Kafka Admin stopped.');
} }
if (this.consumer) { if (this.consumer) {
this.logger.info('Stopping Kafka Consumer...'); this.logger.info('Stopping Kafka Consumer...');
try { try {
await this.consumer.disconnect(); const _consumer = this.consumer;
// @ts-ignore // @ts-ignore
delete this.consumer; delete this.consumer;
await _consumer.disconnect();
this.logger.info('Kafka Consumer stopped.'); this.logger.info('Kafka Consumer stopped.');
await this.disconnectProducer(); await this.disconnectProducer();
process.exit(status);
} catch (e: any) { } catch (e: any) {
this.logger.info('Kafka Consumer stop error.'); this.logger.info('Kafka Consumer stop error.');
await this.disconnectProducer(); await this.disconnectProducer();
process.exit(status);
} }
} else {
process.exit(status);
} }
this.logger.info('Kafka resources stopped.');
} }
private async disconnectProducer(): Promise<void> { private async disconnectProducer(): Promise<void> {
@ -279,13 +263,15 @@ export class KafkaTemplate implements IQueue {
this.logger.info('Stopping loop...'); this.logger.info('Stopping loop...');
clearTimeout(this.sendLoopInstance); clearTimeout(this.sendLoopInstance);
await this.sendMessagesAsBatch(); await this.sendMessagesAsBatch();
await this.producer.disconnect(); const _producer = this.producer;
// @ts-ignore // @ts-ignore
delete this.producer; delete this.producer;
await _producer.disconnect();
this.logger.info('Kafka Producer stopped.'); this.logger.info('Kafka Producer stopped.');
} catch (e) { } catch (e) {
this.logger.info('Kafka Producer stop error.'); this.logger.info('Kafka Producer stop error.');
} }
} }
} }
} }

88
msa/js-executor/queue/pubSubTemplate.ts

@ -34,56 +34,50 @@ export class PubSubTemplate implements IQueue {
private topics: string[] = []; private topics: string[] = [];
private subscriptions: string[] = []; private subscriptions: string[] = [];
name = 'Pub/Sub';
constructor() { constructor() {
} }
async init() { async init() {
try { this.pubSubClient = new PubSub({
this.logger.info('Starting ThingsBoard JavaScript Executor Microservice...'); projectId: this.projectId,
this.pubSubClient = new PubSub({ credentials: this.credentials
projectId: this.projectId, });
credentials: this.credentials
});
this.parseQueueProperties();
const topicList = await this.pubSubClient.getTopics(); this.parseQueueProperties();
if (topicList) { const topicList = await this.pubSubClient.getTopics();
topicList[0].forEach(topic => {
this.topics.push(PubSubTemplate.getName(topic.name));
});
}
const subscriptionList = await this.pubSubClient.getSubscriptions(); if (topicList) {
topicList[0].forEach(topic => {
this.topics.push(PubSubTemplate.getName(topic.name));
});
}
if (subscriptionList) { const subscriptionList = await this.pubSubClient.getSubscriptions();
topicList[0].forEach(sub => {
this.subscriptions.push(PubSubTemplate.getName(sub.name));
});
}
if (!(this.subscriptions.includes(this.requestTopic) && this.topics.includes(this.requestTopic))) { if (subscriptionList) {
await this.createTopic(this.requestTopic); topicList[0].forEach(sub => {
await this.createSubscription(this.requestTopic); this.subscriptions.push(PubSubTemplate.getName(sub.name));
} });
}
const subscription = this.pubSubClient.subscription(this.requestTopic); if (!(this.subscriptions.includes(this.requestTopic) && this.topics.includes(this.requestTopic))) {
await this.createTopic(this.requestTopic);
await this.createSubscription(this.requestTopic);
}
const messageProcessor = new JsInvokeMessageProcessor(this); const subscription = this.pubSubClient.subscription(this.requestTopic);
const messageHandler = (message: Message) => { const messageProcessor = new JsInvokeMessageProcessor(this);
messageProcessor.onJsInvokeMessage(JSON.parse(message.data.toString('utf8')));
message.ack();
};
subscription.on('message', messageHandler); const messageHandler = (message: Message) => {
messageProcessor.onJsInvokeMessage(JSON.parse(message.data.toString('utf8')));
message.ack();
};
} catch (e: any) { subscription.on('message', messageHandler);
this.logger.error('Failed to start ThingsBoard JavaScript Executor Microservice: %s', e.message);
this.logger.error(e.stack);
await this.exit(-1);
}
} }
async send(responseTopic: string, scriptId: string, rawResponse: Buffer, headers: any): Promise<any> { async send(responseTopic: string, scriptId: string, rawResponse: Buffer, headers: any): Promise<any> {
@ -147,29 +141,21 @@ export class PubSubTemplate implements IQueue {
} }
} }
static async build(): Promise<PubSubTemplate> { async destroy(): Promise<void> {
const queue = new PubSubTemplate(); this.logger.info('Stopping Pub/Sub resources...');
await queue.init();
return queue;
}
async exit(status: number): Promise<void> {
this.logger.info('Exiting with status: %d ...', status);
if (this.pubSubClient) { if (this.pubSubClient) {
this.logger.info('Stopping Pub/Sub client.') this.logger.info('Stopping Pub/Sub client...');
try { try {
await this.pubSubClient.close(); const _pubSubClient = this.pubSubClient;
// @ts-ignore // @ts-ignore
delete this.pubSubClient; delete this.pubSubClient;
this.logger.info('Pub/Sub client stopped.') await _pubSubClient.close();
process.exit(status); this.logger.info('Pub/Sub client stopped.');
} catch (e) { } catch (e) {
this.logger.info('Pub/Sub client stop error.'); this.logger.info('Pub/Sub client stop error.');
process.exit(status);
} }
} else {
process.exit(status);
} }
this.logger.info('Pub/Sub resources stopped.');
} }
} }

3
msa/js-executor/queue/queue.models.ts

@ -15,7 +15,8 @@
/// ///
export interface IQueue { export interface IQueue {
name: string;
init(): Promise<void>; init(): Promise<void>;
send(responseTopic: string, scriptId: string, rawResponse: Buffer, headers: any): Promise<any>; send(responseTopic: string, scriptId: string, rawResponse: Buffer, headers: any): Promise<any>;
exit(status: number): Promise<void>; destroy(): Promise<void>;
} }

74
msa/js-executor/queue/rabbitmqTemplate.ts

@ -20,7 +20,6 @@ import { JsInvokeMessageProcessor } from '../api/jsInvokeMessageProcessor'
import { IQueue } from './queue.models'; import { IQueue } from './queue.models';
import amqp, { ConfirmChannel, Connection } from 'amqplib'; import amqp, { ConfirmChannel, Connection } from 'amqplib';
import { Options, Replies } from 'amqplib/properties'; import { Options, Replies } from 'amqplib/properties';
import { sleep } from '../api/utils';
export class RabbitMqTemplate implements IQueue { export class RabbitMqTemplate implements IQueue {
@ -32,7 +31,6 @@ export class RabbitMqTemplate implements IQueue {
private username = config.get('rabbitmq.username'); private username = config.get('rabbitmq.username');
private password = config.get('rabbitmq.password'); private password = config.get('rabbitmq.password');
private queueProperties: string = config.get('rabbitmq.queue_properties'); private queueProperties: string = config.get('rabbitmq.queue_properties');
private pollInterval = Number(config.get('js.response_poll_interval'));
private queueOptions: Options.AssertQueue = { private queueOptions: Options.AssertQueue = {
durable: false, durable: false,
@ -41,45 +39,30 @@ export class RabbitMqTemplate implements IQueue {
}; };
private connection: Connection; private connection: Connection;
private channel: ConfirmChannel; private channel: ConfirmChannel;
private stopped = false;
private topics: string[] = []; private topics: string[] = [];
name = 'RabbitMQ';
constructor() { constructor() {
} }
async init(): Promise<void> { async init(): Promise<void> {
try { const url = `amqp://${this.username}:${this.password}@${this.host}:${this.port}${this.vhost}`;
this.logger.info('Starting ThingsBoard JavaScript Executor Microservice...'); this.connection = await amqp.connect(url);
this.channel = await this.connection.createConfirmChannel();
const url = `amqp://${this.username}:${this.password}@${this.host}:${this.port}${this.vhost}`;
this.connection = await amqp.connect(url);
this.channel = await this.connection.createConfirmChannel();
this.parseQueueProperties();
await this.createQueue(this.requestTopic); this.parseQueueProperties();
const messageProcessor = new JsInvokeMessageProcessor(this); await this.createQueue(this.requestTopic);
while (!this.stopped) { const messageProcessor = new JsInvokeMessageProcessor(this);
let pollStartTs = new Date().getTime();
let message = await this.channel.get(this.requestTopic);
if (message) { await this.channel.consume(this.requestTopic, (message) => {
messageProcessor.onJsInvokeMessage(JSON.parse(message.content.toString('utf8'))); if (message) {
this.channel.ack(message); messageProcessor.onJsInvokeMessage(JSON.parse(message.content.toString('utf8')));
} else { this.channel.ack(message);
let pollDuration = new Date().getTime() - pollStartTs;
if (pollDuration < this.pollInterval) {
await sleep(this.pollInterval - pollDuration);
}
}
} }
} catch (e: any) { })
this.logger.error('Failed to start ThingsBoard JavaScript Executor Microservice: %s', e.message);
this.logger.error(e.stack);
await this.exit(-1);
}
} }
async send(responseTopic: string, scriptId: string, rawResponse: Buffer, headers: any): Promise<any> { async send(responseTopic: string, scriptId: string, rawResponse: Buffer, headers: any): Promise<any> {
@ -114,38 +97,31 @@ export class RabbitMqTemplate implements IQueue {
return this.channel.assertQueue(topic, this.queueOptions); return this.channel.assertQueue(topic, this.queueOptions);
} }
static async build(): Promise<RabbitMqTemplate> { async destroy() {
const queue = new RabbitMqTemplate(); this.logger.info('Stopping RabbitMQ resources...');
await queue.init();
return queue;
}
async exit(status: number) {
this.logger.info('Exiting with status: %d ...', status);
if (this.channel) { if (this.channel) {
this.logger.info('Stopping RabbitMq chanel.') this.logger.info('Stopping RabbitMQ chanel...');
await this.channel.close(); const _channel = this.channel;
// @ts-ignore // @ts-ignore
delete this.channel; delete this.channel;
this.logger.info('RabbitMq chanel stopped'); await _channel.close();
this.logger.info('RabbitMQ chanel stopped');
} }
if (this.connection) { if (this.connection) {
this.logger.info('Stopping RabbitMq connection.') this.logger.info('Stopping RabbitMQ connection...')
try { try {
await this.connection.close(); const _connection = this.connection;
// @ts-ignore // @ts-ignore
delete this.connection; delete this.connection;
this.logger.info('RabbitMq client connection.') await _connection.close();
process.exit(status); this.logger.info('RabbitMQ client connection.');
} catch (e) { } catch (e) {
this.logger.info('RabbitMq connection stop error.'); this.logger.info('RabbitMQ connection stop error.');
process.exit(status);
} }
} else {
process.exit(status);
} }
this.logger.info('RabbitMQ resources stopped.')
} }
} }

97
msa/js-executor/queue/serviceBusTemplate.ts

@ -44,48 +44,42 @@ export class ServiceBusTemplate implements IQueue {
private receiver: ServiceBusReceiver; private receiver: ServiceBusReceiver;
private senderMap = new Map<string, ServiceBusSender>(); private senderMap = new Map<string, ServiceBusSender>();
name = 'Azure Service Bus';
constructor() { constructor() {
} }
async init() { async init() {
try { const connectionString = `Endpoint=sb://${this.namespaceName}.servicebus.windows.net/;SharedAccessKeyName=${this.sasKeyName};SharedAccessKey=${this.sasKey}`;
this.logger.info('Starting ThingsBoard JavaScript Executor Microservice...'); this.sbClient = new ServiceBusClient(connectionString)
this.serviceBusService = new ServiceBusAdministrationClient(connectionString);
const connectionString = `Endpoint=sb://${this.namespaceName}.servicebus.windows.net/;SharedAccessKeyName=${this.sasKeyName};SharedAccessKey=${this.sasKey}`; this.parseQueueProperties();
this.sbClient = new ServiceBusClient(connectionString)
this.serviceBusService = new ServiceBusAdministrationClient(connectionString);
this.parseQueueProperties(); const listQueues = await this.serviceBusService.listQueues();
for await (const queue of listQueues) {
this.queues.push(queue.name);
}
const listQueues = await this.serviceBusService.listQueues(); if (!this.queues.includes(this.requestTopic)) {
for await (const queue of listQueues) { await this.createQueueIfNotExist(this.requestTopic);
this.queues.push(queue.name); this.queues.push(this.requestTopic);
} }
if (!this.queues.includes(this.requestTopic)) { this.receiver = this.sbClient.createReceiver(this.requestTopic, {receiveMode: 'peekLock'});
await this.createQueueIfNotExist(this.requestTopic);
this.queues.push(this.requestTopic);
}
this.receiver = this.sbClient.createReceiver(this.requestTopic, {receiveMode: 'peekLock'}); const messageProcessor = new JsInvokeMessageProcessor(this);
const messageProcessor = new JsInvokeMessageProcessor(this); const messageHandler = async (message: ServiceBusReceivedMessage) => {
if (message) {
const messageHandler = async (message: ServiceBusReceivedMessage) => { messageProcessor.onJsInvokeMessage(message.body);
if (message) { await this.receiver.completeMessage(message);
messageProcessor.onJsInvokeMessage(message.body); }
await this.receiver.completeMessage(message); };
} const errorHandler = async (error: ProcessErrorArgs) => {
}; this.logger.error('Failed to receive message from queue.', error);
const errorHandler = async (error: ProcessErrorArgs) => { };
this.logger.error('Failed to receive message from queue.', error); this.receiver.subscribe({processMessage: messageHandler, processError: errorHandler})
};
this.receiver.subscribe({processMessage: messageHandler, processError: errorHandler})
} catch (e: any) {
this.logger.error('Failed to start ThingsBoard JavaScript Executor Microservice: %s', e.message);
this.logger.error(e.stack);
await this.exit(-1);
}
} }
async send(responseTopic: string, scriptId: string, rawResponse: Buffer, headers: any): Promise<any> { async send(responseTopic: string, scriptId: string, rawResponse: Buffer, headers: any): Promise<any> {
@ -135,41 +129,46 @@ export class ServiceBusTemplate implements IQueue {
} }
} }
static async build(): Promise<ServiceBusTemplate> { async destroy() {
const queue = new ServiceBusTemplate();
await queue.init();
return queue;
}
async exit(status: number) {
this.logger.info('Exiting with status: %d ...', status);
this.logger.info('Stopping Azure Service Bus resources...') this.logger.info('Stopping Azure Service Bus resources...')
if (this.receiver) { if (this.receiver) {
this.logger.info('Stopping Service Bus Receiver...');
try { try {
await this.receiver.close(); const _receiver = this.receiver;
// @ts-ignore // @ts-ignore
delete this.receiver; delete this.receiver;
await _receiver.close();
this.logger.info('Service Bus Receiver stopped.');
} catch (e) { } catch (e) {
this.logger.info('Service Bus Receiver stop error.');
} }
} }
this.senderMap.forEach(k => { this.logger.info('Stopping Service Bus Senders...');
try { const senders: Promise<void>[] = [];
k.close(); this.senderMap.forEach((sender) => {
} catch (e) { senders.push(sender.close());
}
}); });
this.senderMap.clear(); this.senderMap.clear();
try {
await Promise.all(senders);
this.logger.info('Service Bus Senders stopped.');
} catch (e) {
this.logger.info('Service Bus Senders stop error.');
}
if (this.sbClient) { if (this.sbClient) {
this.logger.info('Stopping Service Bus Client...');
try { try {
await this.sbClient.close(); const _sbClient = this.sbClient;
// @ts-ignore // @ts-ignore
delete this.sbClient; delete this.sbClient;
await _sbClient.close();
this.logger.info('Service Bus Client stopped.');
} catch (e) { } catch (e) {
this.logger.info('Service Bus Client stop error.');
} }
} }
this.logger.info('Azure Service Bus resources stopped.') this.logger.info('Azure Service Bus resources stopped.')
process.exit(status);
} }
} }

79
msa/js-executor/server.ts

@ -30,57 +30,66 @@ logger.info('===CONFIG BEGIN===');
logger.info(JSON.stringify(config, null, 4)); logger.info(JSON.stringify(config, null, 4));
logger.info('===CONFIG END==='); logger.info('===CONFIG END===');
const serviceType = config.get('queue_type'); const serviceType: string = config.get('queue_type');
const httpPort = Number(config.get('http_port')); const httpPort = Number(config.get('http_port'));
let queues: IQueue; let queues: IQueue | null;
let httpServer: HttpServer; let httpServer: HttpServer | null;
(async () => { (async () => {
logger.info('Starting ThingsBoard JavaScript Executor Microservice...');
try {
queues = await createQueue(serviceType);
logger.info(`Starting ${queues.name} template...`);
await queues.init();
logger.info(`${queues.name} template started.`);
httpServer = new HttpServer(httpPort);
} catch (e: any) {
logger.error('Failed to start ThingsBoard JavaScript Executor Microservice: %s', e.message);
logger.error(e.stack);
await exit(-1);
}
})();
async function createQueue(serviceType: string): Promise<IQueue> {
switch (serviceType) { switch (serviceType) {
case 'kafka': case 'kafka':
logger.info('Starting kafka template.'); return new KafkaTemplate();
queues = await KafkaTemplate.build();
logger.info('kafka template started.');
break;
case 'pubsub': case 'pubsub':
logger.info('Starting Pub/Sub template.') return new PubSubTemplate();
queues = await PubSubTemplate.build();
logger.info('Pub/Sub template started.')
break;
case 'aws-sqs': case 'aws-sqs':
logger.info('Starting Aws Sqs template.') return new AwsSqsTemplate();
queues = await AwsSqsTemplate.build();
logger.info('Aws Sqs template started.')
break;
case 'rabbitmq': case 'rabbitmq':
logger.info('Starting RabbitMq template.') return new RabbitMqTemplate();
queues = await RabbitMqTemplate.build();
logger.info('RabbitMq template started.')
break;
case 'service-bus': case 'service-bus':
logger.info('Starting Azure Service Bus template.') return new ServiceBusTemplate();
queues = await ServiceBusTemplate.build();
logger.info('Azure Service Bus template started.')
break;
default: default:
logger.error('Unknown service type: ', serviceType); throw new Error('Unknown service type: ' + serviceType);
process.exit(-1);
} }
}
httpServer = new HttpServer(httpPort); [`SIGINT`, `SIGUSR1`, `SIGUSR2`, `uncaughtException`, `SIGTERM`].forEach((eventType) => {
})(); process.once(eventType, async () => {
logger.info(`${eventType} signal received`);
await exit(0);
})
})
process.on('SIGTERM', () => { process.on('exit', (code: number) => {
logger.info('SIGTERM signal received'); logger.info(`ThingsBoard JavaScript Executor Microservice has been stopped. Exit code: ${code}.`);
process.exit(0);
}); });
process.on('exit', async () => { async function exit(status: number) {
logger.info('Exiting with status: %d ...', status);
if (httpServer) { if (httpServer) {
httpServer.stop(); const _httpServer = httpServer;
httpServer = null;
await _httpServer.stop();
} }
if (queues) { if (queues) {
queues.exit(0); const _queues = queues;
queues = null;
await _queues.destroy();
} }
logger.info('JavaScript Executor Microservice has been stopped.'); process.exit(status);
}); }

8
msa/js-executor/yarn.lock

@ -2670,10 +2670,10 @@ jws@^4.0.0:
jwa "^2.0.0" jwa "^2.0.0"
safe-buffer "^5.0.1" safe-buffer "^5.0.1"
kafkajs@^2.0.2: kafkajs@^2.1.0:
version "2.0.2" version "2.1.0"
resolved "https://registry.yarnpkg.com/kafkajs/-/kafkajs-2.0.2.tgz#cdfc8f57aa4fd69f6d9ca1cce4ee89bbc2a3a1f9" resolved "https://registry.yarnpkg.com/kafkajs/-/kafkajs-2.1.0.tgz#32ede4e8080cc75586c5e4406eeb582fa73f7b1e"
integrity sha512-g6CM3fAenofOjR1bfOAqeZUEaSGhNtBscNokybSdW1rmIKYNwBPC9xQzwulFJm36u/xcxXUiCl/L/qfslapihA== integrity sha512-6IYiOdGWvFPbSbVB+AV3feT+A7vzw5sXm7Ze4QTfP7FRNdY8pGcpiNPvD2lfgYFD8Dm9KbMgBgTt2mf8KaIkzw==
keyv@^3.0.0: keyv@^3.0.0:
version "3.1.0" version "3.1.0"

21
msa/vc-executor/src/main/resources/tb-vc-executor.yml

@ -87,12 +87,9 @@ queue:
- key: "session.timeout.ms" # refer to https://docs.confluent.io/platform/current/installation/configuration/consumer-configs.html#consumerconfigs_session.timeout.ms - key: "session.timeout.ms" # refer to https://docs.confluent.io/platform/current/installation/configuration/consumer-configs.html#consumerconfigs_session.timeout.ms
value: "${TB_QUEUE_KAFKA_SESSION_TIMEOUT_MS:10000}" # (10 seconds) value: "${TB_QUEUE_KAFKA_SESSION_TIMEOUT_MS:10000}" # (10 seconds)
topic-properties: topic-properties:
rule-engine: "${TB_QUEUE_KAFKA_RE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100;min.insync.replicas:1}" version-control: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
ota-updates: "${TB_QUEUE_KAFKA_OTA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:10;min.insync.replicas:1}"
consumer-stats: consumer-stats:
enabled: "${TB_QUEUE_KAFKA_CONSUMER_STATS_ENABLED:true}" enabled: "${TB_QUEUE_KAFKA_CONSUMER_STATS_ENABLED:true}"
print-interval-ms: "${TB_QUEUE_KAFKA_CONSUMER_STATS_MIN_PRINT_INTERVAL_MS:60000}" print-interval-ms: "${TB_QUEUE_KAFKA_CONSUMER_STATS_MIN_PRINT_INTERVAL_MS:60000}"
@ -104,11 +101,9 @@ queue:
region: "${TB_QUEUE_AWS_SQS_REGION:YOUR_REGION}" region: "${TB_QUEUE_AWS_SQS_REGION:YOUR_REGION}"
threads_per_topic: "${TB_QUEUE_AWS_SQS_THREADS_PER_TOPIC:1}" threads_per_topic: "${TB_QUEUE_AWS_SQS_THREADS_PER_TOPIC:1}"
queue-properties: queue-properties:
rule-engine: "${TB_QUEUE_AWS_SQS_RE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
core: "${TB_QUEUE_AWS_SQS_CORE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" core: "${TB_QUEUE_AWS_SQS_CORE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
transport-api: "${TB_QUEUE_AWS_SQS_TA_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
notifications: "${TB_QUEUE_AWS_SQS_NOTIFICATIONS_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" notifications: "${TB_QUEUE_AWS_SQS_NOTIFICATIONS_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
js-executor: "${TB_QUEUE_AWS_SQS_JE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" version-control: "${TB_QUEUE_AWS_SQS_VC_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
# VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds # VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds
pubsub: pubsub:
project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}" project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}"
@ -116,22 +111,18 @@ queue:
max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" #in bytes max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" #in bytes
max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}"
queue-properties: queue-properties:
rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
core: "${TB_QUEUE_PUBSUB_CORE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" core: "${TB_QUEUE_PUBSUB_CORE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
transport-api: "${TB_QUEUE_PUBSUB_TA_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
notifications: "${TB_QUEUE_PUBSUB_NOTIFICATIONS_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" notifications: "${TB_QUEUE_PUBSUB_NOTIFICATIONS_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
js-executor: "${TB_QUEUE_PUBSUB_JE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" version-control: "${TB_QUEUE_PUBSUB_VC_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
service_bus: service_bus:
namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}" namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}"
sas_key_name: "${TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME:YOUR_SAS_KEY_NAME}" sas_key_name: "${TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME:YOUR_SAS_KEY_NAME}"
sas_key: "${TB_QUEUE_SERVICE_BUS_SAS_KEY:YOUR_SAS_KEY}" sas_key: "${TB_QUEUE_SERVICE_BUS_SAS_KEY:YOUR_SAS_KEY}"
max_messages: "${TB_QUEUE_SERVICE_BUS_MAX_MESSAGES:1000}" max_messages: "${TB_QUEUE_SERVICE_BUS_MAX_MESSAGES:1000}"
queue-properties: queue-properties:
rule-engine: "${TB_QUEUE_SERVICE_BUS_RE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
core: "${TB_QUEUE_SERVICE_BUS_CORE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" core: "${TB_QUEUE_SERVICE_BUS_CORE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
transport-api: "${TB_QUEUE_SERVICE_BUS_TA_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
notifications: "${TB_QUEUE_SERVICE_BUS_NOTIFICATIONS_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" notifications: "${TB_QUEUE_SERVICE_BUS_NOTIFICATIONS_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
js-executor: "${TB_QUEUE_SERVICE_BUS_JE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" version-control: "${TB_QUEUE_SERVICE_BUS_VC_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
rabbitmq: rabbitmq:
exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}" exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}"
host: "${TB_QUEUE_RABBIT_MQ_HOST:localhost}" host: "${TB_QUEUE_RABBIT_MQ_HOST:localhost}"
@ -143,11 +134,9 @@ queue:
connection_timeout: "${TB_QUEUE_RABBIT_MQ_CONNECTION_TIMEOUT:60000}" connection_timeout: "${TB_QUEUE_RABBIT_MQ_CONNECTION_TIMEOUT:60000}"
handshake_timeout: "${TB_QUEUE_RABBIT_MQ_HANDSHAKE_TIMEOUT:10000}" handshake_timeout: "${TB_QUEUE_RABBIT_MQ_HANDSHAKE_TIMEOUT:10000}"
queue-properties: queue-properties:
rule-engine: "${TB_QUEUE_RABBIT_MQ_RE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
core: "${TB_QUEUE_RABBIT_MQ_CORE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" core: "${TB_QUEUE_RABBIT_MQ_CORE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
transport-api: "${TB_QUEUE_RABBIT_MQ_TA_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" transport-api: "${TB_QUEUE_RABBIT_MQ_TA_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
notifications: "${TB_QUEUE_RABBIT_MQ_NOTIFICATIONS_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" version-control: "${TB_QUEUE_RABBIT_MQ_VC_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
js-executor: "${TB_QUEUE_RABBIT_MQ_JE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
partitions: partitions:
hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256
core: core:

2
msa/web-ui/server.ts

@ -123,7 +123,7 @@ let connections: Socket[] = [];
})(); })();
[`SIGINT`, `SIGUSR1`, `SIGUSR2`, `uncaughtException`, `SIGTERM`].forEach((eventType) => { [`SIGINT`, `SIGUSR1`, `SIGUSR2`, `uncaughtException`, `SIGTERM`].forEach((eventType) => {
process.on(eventType, async () => { process.once(eventType, async () => {
logger.info(`${eventType} signal received`); logger.info(`${eventType} signal received`);
await exit(0); await exit(0);
}); });

5
transport/coap/src/main/resources/tb-coap-transport.yml

@ -174,7 +174,6 @@ queue:
core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100;min.insync.replicas:1}"
aws_sqs: aws_sqs:
use_default_credential_provider_chain: "${TB_QUEUE_AWS_SQS_USE_DEFAULT_CREDENTIAL_PROVIDER_CHAIN:false}" use_default_credential_provider_chain: "${TB_QUEUE_AWS_SQS_USE_DEFAULT_CREDENTIAL_PROVIDER_CHAIN:false}"
access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}" access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}"
@ -186,7 +185,6 @@ queue:
core: "${TB_QUEUE_AWS_SQS_CORE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" core: "${TB_QUEUE_AWS_SQS_CORE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
transport-api: "${TB_QUEUE_AWS_SQS_TA_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" transport-api: "${TB_QUEUE_AWS_SQS_TA_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
notifications: "${TB_QUEUE_AWS_SQS_NOTIFICATIONS_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" notifications: "${TB_QUEUE_AWS_SQS_NOTIFICATIONS_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
js-executor: "${TB_QUEUE_AWS_SQS_JE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
# VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds # VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds
pubsub: pubsub:
project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}" project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}"
@ -198,7 +196,6 @@ queue:
core: "${TB_QUEUE_PUBSUB_CORE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" core: "${TB_QUEUE_PUBSUB_CORE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
transport-api: "${TB_QUEUE_PUBSUB_TA_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" transport-api: "${TB_QUEUE_PUBSUB_TA_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
notifications: "${TB_QUEUE_PUBSUB_NOTIFICATIONS_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" notifications: "${TB_QUEUE_PUBSUB_NOTIFICATIONS_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
js-executor: "${TB_QUEUE_PUBSUB_JE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
service_bus: service_bus:
namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}" namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}"
sas_key_name: "${TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME:YOUR_SAS_KEY_NAME}" sas_key_name: "${TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME:YOUR_SAS_KEY_NAME}"
@ -209,7 +206,6 @@ queue:
core: "${TB_QUEUE_SERVICE_BUS_CORE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" core: "${TB_QUEUE_SERVICE_BUS_CORE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
transport-api: "${TB_QUEUE_SERVICE_BUS_TA_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" transport-api: "${TB_QUEUE_SERVICE_BUS_TA_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
notifications: "${TB_QUEUE_SERVICE_BUS_NOTIFICATIONS_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" notifications: "${TB_QUEUE_SERVICE_BUS_NOTIFICATIONS_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
js-executor: "${TB_QUEUE_SERVICE_BUS_JE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
rabbitmq: rabbitmq:
exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}" exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}"
host: "${TB_QUEUE_RABBIT_MQ_HOST:localhost}" host: "${TB_QUEUE_RABBIT_MQ_HOST:localhost}"
@ -225,7 +221,6 @@ queue:
core: "${TB_QUEUE_RABBIT_MQ_CORE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" core: "${TB_QUEUE_RABBIT_MQ_CORE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
transport-api: "${TB_QUEUE_RABBIT_MQ_TA_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" transport-api: "${TB_QUEUE_RABBIT_MQ_TA_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
notifications: "${TB_QUEUE_RABBIT_MQ_NOTIFICATIONS_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" notifications: "${TB_QUEUE_RABBIT_MQ_NOTIFICATIONS_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
js-executor: "${TB_QUEUE_RABBIT_MQ_JE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
partitions: partitions:
hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}"
virtual_nodes_size: "${TB_QUEUE_PARTITIONS_VIRTUAL_NODES_SIZE:16}" virtual_nodes_size: "${TB_QUEUE_PARTITIONS_VIRTUAL_NODES_SIZE:16}"

5
transport/http/src/main/resources/tb-http-transport.yml

@ -161,7 +161,6 @@ queue:
core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100;min.insync.replicas:1}"
aws_sqs: aws_sqs:
use_default_credential_provider_chain: "${TB_QUEUE_AWS_SQS_USE_DEFAULT_CREDENTIAL_PROVIDER_CHAIN:false}" use_default_credential_provider_chain: "${TB_QUEUE_AWS_SQS_USE_DEFAULT_CREDENTIAL_PROVIDER_CHAIN:false}"
access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}" access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}"
@ -173,7 +172,6 @@ queue:
core: "${TB_QUEUE_AWS_SQS_CORE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" core: "${TB_QUEUE_AWS_SQS_CORE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
transport-api: "${TB_QUEUE_AWS_SQS_TA_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" transport-api: "${TB_QUEUE_AWS_SQS_TA_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
notifications: "${TB_QUEUE_AWS_SQS_NOTIFICATIONS_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" notifications: "${TB_QUEUE_AWS_SQS_NOTIFICATIONS_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
js-executor: "${TB_QUEUE_AWS_SQS_JE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
# VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds # VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds
pubsub: pubsub:
project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}" project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}"
@ -185,7 +183,6 @@ queue:
core: "${TB_QUEUE_PUBSUB_CORE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" core: "${TB_QUEUE_PUBSUB_CORE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
transport-api: "${TB_QUEUE_PUBSUB_TA_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" transport-api: "${TB_QUEUE_PUBSUB_TA_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
notifications: "${TB_QUEUE_PUBSUB_NOTIFICATIONS_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" notifications: "${TB_QUEUE_PUBSUB_NOTIFICATIONS_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
js-executor: "${TB_QUEUE_PUBSUB_JE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
service_bus: service_bus:
namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}" namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}"
sas_key_name: "${TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME:YOUR_SAS_KEY_NAME}" sas_key_name: "${TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME:YOUR_SAS_KEY_NAME}"
@ -196,7 +193,6 @@ queue:
core: "${TB_QUEUE_SERVICE_BUS_CORE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" core: "${TB_QUEUE_SERVICE_BUS_CORE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
transport-api: "${TB_QUEUE_SERVICE_BUS_TA_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" transport-api: "${TB_QUEUE_SERVICE_BUS_TA_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
notifications: "${TB_QUEUE_SERVICE_BUS_NOTIFICATIONS_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" notifications: "${TB_QUEUE_SERVICE_BUS_NOTIFICATIONS_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
js-executor: "${TB_QUEUE_SERVICE_BUS_JE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
rabbitmq: rabbitmq:
exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}" exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}"
host: "${TB_QUEUE_RABBIT_MQ_HOST:localhost}" host: "${TB_QUEUE_RABBIT_MQ_HOST:localhost}"
@ -212,7 +208,6 @@ queue:
core: "${TB_QUEUE_RABBIT_MQ_CORE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" core: "${TB_QUEUE_RABBIT_MQ_CORE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
transport-api: "${TB_QUEUE_RABBIT_MQ_TA_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" transport-api: "${TB_QUEUE_RABBIT_MQ_TA_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
notifications: "${TB_QUEUE_RABBIT_MQ_NOTIFICATIONS_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" notifications: "${TB_QUEUE_RABBIT_MQ_NOTIFICATIONS_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
js-executor: "${TB_QUEUE_RABBIT_MQ_JE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
partitions: partitions:
hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}"
virtual_nodes_size: "${TB_QUEUE_PARTITIONS_VIRTUAL_NODES_SIZE:16}" virtual_nodes_size: "${TB_QUEUE_PARTITIONS_VIRTUAL_NODES_SIZE:16}"

5
transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml

@ -239,7 +239,6 @@ queue:
core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}" core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}"
transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}" transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}"
notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}" notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}"
js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100}"
aws_sqs: aws_sqs:
use_default_credential_provider_chain: "${TB_QUEUE_AWS_SQS_USE_DEFAULT_CREDENTIAL_PROVIDER_CHAIN:false}" use_default_credential_provider_chain: "${TB_QUEUE_AWS_SQS_USE_DEFAULT_CREDENTIAL_PROVIDER_CHAIN:false}"
access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}" access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}"
@ -251,7 +250,6 @@ queue:
core: "${TB_QUEUE_AWS_SQS_CORE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" core: "${TB_QUEUE_AWS_SQS_CORE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
transport-api: "${TB_QUEUE_AWS_SQS_TA_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" transport-api: "${TB_QUEUE_AWS_SQS_TA_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
notifications: "${TB_QUEUE_AWS_SQS_NOTIFICATIONS_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" notifications: "${TB_QUEUE_AWS_SQS_NOTIFICATIONS_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
js-executor: "${TB_QUEUE_AWS_SQS_JE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
# VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds # VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds
pubsub: pubsub:
project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}" project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}"
@ -263,7 +261,6 @@ queue:
core: "${TB_QUEUE_PUBSUB_CORE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" core: "${TB_QUEUE_PUBSUB_CORE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
transport-api: "${TB_QUEUE_PUBSUB_TA_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" transport-api: "${TB_QUEUE_PUBSUB_TA_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
notifications: "${TB_QUEUE_PUBSUB_NOTIFICATIONS_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" notifications: "${TB_QUEUE_PUBSUB_NOTIFICATIONS_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
js-executor: "${TB_QUEUE_PUBSUB_JE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
service_bus: service_bus:
namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}" namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}"
sas_key_name: "${TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME:YOUR_SAS_KEY_NAME}" sas_key_name: "${TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME:YOUR_SAS_KEY_NAME}"
@ -274,7 +271,6 @@ queue:
core: "${TB_QUEUE_SERVICE_BUS_CORE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" core: "${TB_QUEUE_SERVICE_BUS_CORE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
transport-api: "${TB_QUEUE_SERVICE_BUS_TA_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" transport-api: "${TB_QUEUE_SERVICE_BUS_TA_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
notifications: "${TB_QUEUE_SERVICE_BUS_NOTIFICATIONS_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" notifications: "${TB_QUEUE_SERVICE_BUS_NOTIFICATIONS_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
js-executor: "${TB_QUEUE_SERVICE_BUS_JE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
rabbitmq: rabbitmq:
exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}" exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}"
host: "${TB_QUEUE_RABBIT_MQ_HOST:localhost}" host: "${TB_QUEUE_RABBIT_MQ_HOST:localhost}"
@ -290,7 +286,6 @@ queue:
core: "${TB_QUEUE_RABBIT_MQ_CORE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" core: "${TB_QUEUE_RABBIT_MQ_CORE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
transport-api: "${TB_QUEUE_RABBIT_MQ_TA_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" transport-api: "${TB_QUEUE_RABBIT_MQ_TA_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
notifications: "${TB_QUEUE_RABBIT_MQ_NOTIFICATIONS_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" notifications: "${TB_QUEUE_RABBIT_MQ_NOTIFICATIONS_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
js-executor: "${TB_QUEUE_RABBIT_MQ_JE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
partitions: partitions:
hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}"
virtual_nodes_size: "${TB_QUEUE_PARTITIONS_VIRTUAL_NODES_SIZE:16}" virtual_nodes_size: "${TB_QUEUE_PARTITIONS_VIRTUAL_NODES_SIZE:16}"

5
transport/mqtt/src/main/resources/tb-mqtt-transport.yml

@ -191,7 +191,6 @@ queue:
core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100;min.insync.replicas:1}"
aws_sqs: aws_sqs:
use_default_credential_provider_chain: "${TB_QUEUE_AWS_SQS_USE_DEFAULT_CREDENTIAL_PROVIDER_CHAIN:false}" use_default_credential_provider_chain: "${TB_QUEUE_AWS_SQS_USE_DEFAULT_CREDENTIAL_PROVIDER_CHAIN:false}"
access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}" access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}"
@ -203,7 +202,6 @@ queue:
core: "${TB_QUEUE_AWS_SQS_CORE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" core: "${TB_QUEUE_AWS_SQS_CORE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
transport-api: "${TB_QUEUE_AWS_SQS_TA_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" transport-api: "${TB_QUEUE_AWS_SQS_TA_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
notifications: "${TB_QUEUE_AWS_SQS_NOTIFICATIONS_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" notifications: "${TB_QUEUE_AWS_SQS_NOTIFICATIONS_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
js-executor: "${TB_QUEUE_AWS_SQS_JE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
# VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds # VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds
pubsub: pubsub:
project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}" project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}"
@ -215,7 +213,6 @@ queue:
core: "${TB_QUEUE_PUBSUB_CORE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" core: "${TB_QUEUE_PUBSUB_CORE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
transport-api: "${TB_QUEUE_PUBSUB_TA_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" transport-api: "${TB_QUEUE_PUBSUB_TA_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
notifications: "${TB_QUEUE_PUBSUB_NOTIFICATIONS_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" notifications: "${TB_QUEUE_PUBSUB_NOTIFICATIONS_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
js-executor: "${TB_QUEUE_PUBSUB_JE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
service_bus: service_bus:
namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}" namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}"
sas_key_name: "${TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME:YOUR_SAS_KEY_NAME}" sas_key_name: "${TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME:YOUR_SAS_KEY_NAME}"
@ -226,7 +223,6 @@ queue:
core: "${TB_QUEUE_SERVICE_BUS_CORE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" core: "${TB_QUEUE_SERVICE_BUS_CORE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
transport-api: "${TB_QUEUE_SERVICE_BUS_TA_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" transport-api: "${TB_QUEUE_SERVICE_BUS_TA_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
notifications: "${TB_QUEUE_SERVICE_BUS_NOTIFICATIONS_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" notifications: "${TB_QUEUE_SERVICE_BUS_NOTIFICATIONS_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
js-executor: "${TB_QUEUE_SERVICE_BUS_JE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
rabbitmq: rabbitmq:
exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}" exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}"
host: "${TB_QUEUE_RABBIT_MQ_HOST:localhost}" host: "${TB_QUEUE_RABBIT_MQ_HOST:localhost}"
@ -242,7 +238,6 @@ queue:
core: "${TB_QUEUE_RABBIT_MQ_CORE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" core: "${TB_QUEUE_RABBIT_MQ_CORE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
transport-api: "${TB_QUEUE_RABBIT_MQ_TA_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" transport-api: "${TB_QUEUE_RABBIT_MQ_TA_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
notifications: "${TB_QUEUE_RABBIT_MQ_NOTIFICATIONS_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" notifications: "${TB_QUEUE_RABBIT_MQ_NOTIFICATIONS_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
js-executor: "${TB_QUEUE_RABBIT_MQ_JE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
partitions: partitions:
hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}"
virtual_nodes_size: "${TB_QUEUE_PARTITIONS_VIRTUAL_NODES_SIZE:16}" virtual_nodes_size: "${TB_QUEUE_PARTITIONS_VIRTUAL_NODES_SIZE:16}"

5
transport/snmp/src/main/resources/tb-snmp-transport.yml

@ -137,7 +137,6 @@ queue:
core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100;min.insync.replicas:1}"
consumer-stats: consumer-stats:
enabled: "${TB_QUEUE_KAFKA_CONSUMER_STATS_ENABLED:true}" enabled: "${TB_QUEUE_KAFKA_CONSUMER_STATS_ENABLED:true}"
print-interval-ms: "${TB_QUEUE_KAFKA_CONSUMER_STATS_MIN_PRINT_INTERVAL_MS:60000}" print-interval-ms: "${TB_QUEUE_KAFKA_CONSUMER_STATS_MIN_PRINT_INTERVAL_MS:60000}"
@ -153,7 +152,6 @@ queue:
core: "${TB_QUEUE_AWS_SQS_CORE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" core: "${TB_QUEUE_AWS_SQS_CORE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
transport-api: "${TB_QUEUE_AWS_SQS_TA_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" transport-api: "${TB_QUEUE_AWS_SQS_TA_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
notifications: "${TB_QUEUE_AWS_SQS_NOTIFICATIONS_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" notifications: "${TB_QUEUE_AWS_SQS_NOTIFICATIONS_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
js-executor: "${TB_QUEUE_AWS_SQS_JE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
# VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds # VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds
pubsub: pubsub:
project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}" project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}"
@ -165,7 +163,6 @@ queue:
core: "${TB_QUEUE_PUBSUB_CORE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" core: "${TB_QUEUE_PUBSUB_CORE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
transport-api: "${TB_QUEUE_PUBSUB_TA_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" transport-api: "${TB_QUEUE_PUBSUB_TA_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
notifications: "${TB_QUEUE_PUBSUB_NOTIFICATIONS_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" notifications: "${TB_QUEUE_PUBSUB_NOTIFICATIONS_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
js-executor: "${TB_QUEUE_PUBSUB_JE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
service_bus: service_bus:
namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}" namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}"
sas_key_name: "${TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME:YOUR_SAS_KEY_NAME}" sas_key_name: "${TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME:YOUR_SAS_KEY_NAME}"
@ -176,7 +173,6 @@ queue:
core: "${TB_QUEUE_SERVICE_BUS_CORE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" core: "${TB_QUEUE_SERVICE_BUS_CORE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
transport-api: "${TB_QUEUE_SERVICE_BUS_TA_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" transport-api: "${TB_QUEUE_SERVICE_BUS_TA_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
notifications: "${TB_QUEUE_SERVICE_BUS_NOTIFICATIONS_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" notifications: "${TB_QUEUE_SERVICE_BUS_NOTIFICATIONS_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
js-executor: "${TB_QUEUE_SERVICE_BUS_JE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
rabbitmq: rabbitmq:
exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}" exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}"
host: "${TB_QUEUE_RABBIT_MQ_HOST:localhost}" host: "${TB_QUEUE_RABBIT_MQ_HOST:localhost}"
@ -192,7 +188,6 @@ queue:
core: "${TB_QUEUE_RABBIT_MQ_CORE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" core: "${TB_QUEUE_RABBIT_MQ_CORE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
transport-api: "${TB_QUEUE_RABBIT_MQ_TA_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" transport-api: "${TB_QUEUE_RABBIT_MQ_TA_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
notifications: "${TB_QUEUE_RABBIT_MQ_NOTIFICATIONS_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" notifications: "${TB_QUEUE_RABBIT_MQ_NOTIFICATIONS_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
js-executor: "${TB_QUEUE_RABBIT_MQ_JE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
partitions: partitions:
hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}"
virtual_nodes_size: "${TB_QUEUE_PARTITIONS_VIRTUAL_NODES_SIZE:16}" virtual_nodes_size: "${TB_QUEUE_PARTITIONS_VIRTUAL_NODES_SIZE:16}"

Loading…
Cancel
Save