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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -28,15 +28,15 @@ import java.util.Map;
@Component
@ConditionalOnProperty(prefix = "queue", value = "type", havingValue = "kafka")
public class TbKafkaTopicConfigs {
@Value("${queue.kafka.topic-properties.core}")
@Value("${queue.kafka.topic-properties.core:}")
private String coreProperties;
@Value("${queue.kafka.topic-properties.rule-engine}")
@Value("${queue.kafka.topic-properties.rule-engine:}")
private String ruleEngineProperties;
@Value("${queue.kafka.topic-properties.transport-api}")
@Value("${queue.kafka.topic-properties.transport-api:}")
private String transportApiProperties;
@Value("${queue.kafka.topic-properties.notifications}")
@Value("${queue.kafka.topic-properties.notifications:}")
private String notificationsProperties;
@Value("${queue.kafka.topic-properties.js-executor}")
@Value("${queue.kafka.topic-properties.js-executor:}")
private String jsExecutorProperties;
@Value("${queue.kafka.topic-properties.ota-updates:}")
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 transportApiAdmin;
private final TbQueueAdmin notificationAdmin;
private final TbQueueAdmin otaAdmin;
private final TbQueueAdmin vcAdmin;
public AwsSqsMonolithQueueFactory(NotificationsTopicService notificationsTopicService, TbQueueCoreSettings coreSettings,
@ -102,6 +103,7 @@ public class AwsSqsMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng
this.jsExecutorAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getJsExecutorAttributes());
this.transportApiAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getTransportApiAttributes());
this.notificationAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getNotificationsAttributes());
this.otaAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getOtaAttributes());
this.vcAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getVcAttributes());
}
@ -210,13 +212,13 @@ public class AwsSqsMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng
@Override
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()));
}
@Override
public TbQueueProducer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgProducer() {
return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getOtaPackageTopic());
return new TbAwsSqsProducerTemplate<>(otaAdmin, sqsSettings, coreSettings.getOtaPackageTopic());
}
@Override
@ -241,6 +243,9 @@ public class AwsSqsMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng
if (notificationAdmin != null) {
notificationAdmin.destroy();
}
if (otaAdmin != null) {
otaAdmin.destroy();
}
if (vcAdmin != null) {
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.TbQueueTransportApiSettings;
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.TbAwsSqsConsumerTemplate;
import org.thingsboard.server.queue.sqs.TbAwsSqsProducerTemplate;
@ -66,18 +67,22 @@ public class AwsSqsTbCoreQueueFactory implements TbCoreQueueFactory {
private final TbServiceInfoProvider serviceInfoProvider;
private final TbQueueRemoteJsInvokeSettings jsInvokeSettings;
private final TbQueueTransportNotificationSettings transportNotificationSettings;
private final TbQueueVersionControlSettings vcSettings;
private final TbQueueAdmin coreAdmin;
private final TbQueueAdmin ruleEngineAdmin;
private final TbQueueAdmin jsExecutorAdmin;
private final TbQueueAdmin transportApiAdmin;
private final TbQueueAdmin notificationAdmin;
private final TbQueueAdmin otaAdmin;
private final TbQueueAdmin vcAdmin;
public AwsSqsTbCoreQueueFactory(TbAwsSqsSettings sqsSettings,
TbQueueCoreSettings coreSettings,
TbQueueTransportApiSettings transportApiSettings,
TbQueueRuleEngineSettings ruleEngineSettings,
NotificationsTopicService notificationsTopicService,
TbQueueVersionControlSettings vcSettings,
TbServiceInfoProvider serviceInfoProvider,
TbQueueRemoteJsInvokeSettings jsInvokeSettings,
TbAwsSqsQueueAttributes sqsQueueAttributes,
@ -90,12 +95,15 @@ public class AwsSqsTbCoreQueueFactory implements TbCoreQueueFactory {
this.serviceInfoProvider = serviceInfoProvider;
this.jsInvokeSettings = jsInvokeSettings;
this.transportNotificationSettings = transportNotificationSettings;
this.vcSettings = vcSettings;
this.coreAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getCoreAttributes());
this.ruleEngineAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getRuleEngineAttributes());
this.jsExecutorAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getJsExecutorAttributes());
this.transportApiAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getTransportApiAttributes());
this.notificationAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getNotificationsAttributes());
this.otaAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getOtaAttributes());
this.vcAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getVcAttributes());
}
@Override
@ -183,19 +191,18 @@ public class AwsSqsTbCoreQueueFactory implements TbCoreQueueFactory {
@Override
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()));
}
@Override
public TbQueueProducer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgProducer() {
return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getOtaPackageTopic());
return new TbAwsSqsProducerTemplate<>(otaAdmin, sqsSettings, coreSettings.getOtaPackageTopic());
}
@Override
public TbQueueProducer<TbProtoQueueMsg<TransportProtos.ToVersionControlServiceMsg>> createVersionControlMsgProducer() {
//TODO: version-control
return null;
return new TbAwsSqsProducerTemplate<>(vcAdmin, sqsSettings, vcSettings.getTopic());
}
@PreDestroy
@ -215,5 +222,11 @@ public class AwsSqsTbCoreQueueFactory implements TbCoreQueueFactory {
if (notificationAdmin != null) {
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.TbQueueRuleEngineSettings;
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.TbAwsSqsConsumerTemplate;
import org.thingsboard.server.queue.sqs.TbAwsSqsProducerTemplate;
@ -64,6 +65,7 @@ public class AwsSqsTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory
private final TbQueueAdmin ruleEngineAdmin;
private final TbQueueAdmin jsExecutorAdmin;
private final TbQueueAdmin notificationAdmin;
private final TbQueueAdmin otaAdmin;
public AwsSqsTbRuleEngineQueueFactory(NotificationsTopicService notificationsTopicService, TbQueueCoreSettings coreSettings,
TbQueueRuleEngineSettings ruleEngineSettings,
@ -84,6 +86,7 @@ public class AwsSqsTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory
this.ruleEngineAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getRuleEngineAttributes());
this.jsExecutorAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getJsExecutorAttributes());
this.notificationAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getNotificationsAttributes());
this.otaAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getOtaAttributes());
}
@Override
@ -154,7 +157,7 @@ public class AwsSqsTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory
@Override
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) {
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.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.StringUtils;
import javax.annotation.PostConstruct;
import java.util.HashMap;
@ -27,7 +28,7 @@ import java.util.Map;
@Component
@ConditionalOnExpression("'${queue.type:null}'=='pubsub'")
public class TbPubSubSubscriptionSettings {
@Value("${queue.pubsub.queue-properties.core}")
@Value("${queue.pubsub.queue-properties.core:}")
private String coreProperties;
@Value("${queue.pubsub.queue-properties.rule-engine}")
private String ruleEngineProperties;
@ -65,11 +66,13 @@ public class TbPubSubSubscriptionSettings {
private Map<String, String> getSettings(String properties) {
Map<String, String> configs = new HashMap<>();
for (String property : properties.split(";")) {
int delimiterPosition = property.indexOf(":");
String key = property.substring(0, delimiterPosition);
String value = property.substring(delimiterPosition + 1);
configs.put(key, value);
if (StringUtils.isNotEmpty(properties)) {
for (String property : properties.split(";")) {
int delimiterPosition = property.indexOf(":");
String key = property.substring(0, delimiterPosition);
String value = property.substring(delimiterPosition + 1);
configs.put(key, value);
}
}
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.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.StringUtils;
import javax.annotation.PostConstruct;
import java.util.HashMap;
@ -28,15 +29,15 @@ import java.util.regex.Pattern;
@Component
@ConditionalOnExpression("'${queue.type:null}'=='rabbitmq'")
public class TbRabbitMqQueueArguments {
@Value("${queue.rabbitmq.queue-properties.core}")
@Value("${queue.rabbitmq.queue-properties.core:}")
private String coreProperties;
@Value("${queue.rabbitmq.queue-properties.rule-engine}")
@Value("${queue.rabbitmq.queue-properties.rule-engine:}")
private String ruleEngineProperties;
@Value("${queue.rabbitmq.queue-properties.transport-api}")
@Value("${queue.rabbitmq.queue-properties.transport-api:}")
private String transportApiProperties;
@Value("${queue.rabbitmq.queue-properties.notifications}")
@Value("${queue.rabbitmq.queue-properties.notifications:}")
private String notificationsProperties;
@Value("${queue.rabbitmq.queue-properties.js-executor}")
@Value("${queue.rabbitmq.queue-properties.js-executor:}")
private String jsExecutorProperties;
@Value("${queue.rabbitmq.queue-properties.version-control:}")
private String vcProperties;
@ -66,11 +67,13 @@ public class TbRabbitMqQueueArguments {
private Map<String, Object> getArgs(String properties) {
Map<String, Object> configs = new HashMap<>();
for (String property : properties.split(";")) {
int delimiterPosition = property.indexOf(":");
String key = property.substring(0, delimiterPosition);
String strValue = property.substring(delimiterPosition + 1);
configs.put(key, getObjectValue(strValue));
if (StringUtils.isNotEmpty(properties)) {
for (String property : properties.split(";")) {
int delimiterPosition = property.indexOf(":");
String key = property.substring(0, delimiterPosition);
String strValue = property.substring(delimiterPosition + 1);
configs.put(key, getObjectValue(strValue));
}
}
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.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.StringUtils;
import javax.annotation.PostConstruct;
import java.util.HashMap;
@ -28,16 +29,18 @@ import java.util.Map;
@Component
@ConditionalOnExpression("'${queue.type:null}'=='aws-sqs'")
public class TbAwsSqsQueueAttributes {
@Value("${queue.aws-sqs.queue-properties.core}")
@Value("${queue.aws-sqs.queue-properties.core:}")
private String coreProperties;
@Value("${queue.aws-sqs.queue-properties.rule-engine}")
@Value("${queue.aws-sqs.queue-properties.rule-engine:}")
private String ruleEngineProperties;
@Value("${queue.aws-sqs.queue-properties.transport-api}")
@Value("${queue.aws-sqs.queue-properties.transport-api:}")
private String transportApiProperties;
@Value("${queue.aws-sqs.queue-properties.notifications}")
@Value("${queue.aws-sqs.queue-properties.notifications:}")
private String notificationsProperties;
@Value("${queue.aws-sqs.queue-properties.js-executor}")
@Value("${queue.aws-sqs.queue-properties.js-executor:}")
private String jsExecutorProperties;
@Value("${queue.aws-sqs.queue-properties.ota-updates:}")
private String otaProperties;
@Value("${queue.aws-sqs.queue-properties.version-control:}")
private String vcProperties;
@ -52,6 +55,8 @@ public class TbAwsSqsQueueAttributes {
@Getter
private Map<String, String> jsExecutorAttributes;
@Getter
private Map<String, String> otaAttributes;
@Getter
private Map<String, String> vcAttributes;
private final Map<String, String> defaultAttributes = new HashMap<>();
@ -65,19 +70,21 @@ public class TbAwsSqsQueueAttributes {
transportApiAttributes = getConfigs(transportApiProperties);
notificationsAttributes = getConfigs(notificationsProperties);
jsExecutorAttributes = getConfigs(jsExecutorProperties);
otaAttributes = getConfigs(otaProperties);
vcAttributes = getConfigs(vcProperties);
}
private Map<String, String> getConfigs(String properties) {
Map<String, String> configs = new HashMap<>();
for (String property : properties.split(";")) {
int delimiterPosition = property.indexOf(":");
String key = property.substring(0, delimiterPosition);
String value = property.substring(delimiterPosition + 1);
validateAttributeName(key);
configs.put(key, value);
Map<String, String> configs = new HashMap<>(defaultAttributes);
if (StringUtils.isNotEmpty(properties)) {
for (String property : properties.split(";")) {
int delimiterPosition = property.indexOf(":");
String key = property.substring(0, delimiterPosition);
String value = property.substring(delimiterPosition + 1);
validateAttributeName(key);
configs.put(key, value);
}
}
configs.putAll(defaultAttributes);
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) {
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.setDeviceData(syncDeviceData(deviceProfile, device.getDeviceData()));
@ -530,6 +533,7 @@ public class DeviceServiceImpl extends AbstractCachedEntityService<DeviceCacheKe
device.setTenantId(tenantId);
device.setCustomerId(null);
device.setDeviceProfileId(null);
Device savedDevice = doSaveDevice(device, null, true);
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.Device;
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.id.CustomerId;
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.dao.customer.CustomerDao;
import org.thingsboard.server.dao.device.DeviceDao;
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.TenantService;
@ -41,7 +37,7 @@ import java.util.Optional;
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
@Component
public class DeviceDataValidator extends DataValidator<Device> {
public class DeviceDataValidator extends AbstractHasOtaPackageValidator<Device> {
@Autowired
private DeviceDao deviceDao;
@ -56,9 +52,6 @@ public class DeviceDataValidator extends DataValidator<Device> {
@Lazy
private TbTenantProfileCache tenantProfileCache;
@Autowired
private OtaPackageService otaPackageService;
@Override
protected void validateCreate(TenantId tenantId, Device device) {
DefaultTenantProfileConfiguration profileConfiguration =
@ -103,36 +96,6 @@ public class DeviceDataValidator extends DataValidator<Device> {
.flatMap(deviceData -> Optional.ofNullable(deviceData.getTransportConfiguration()))
.ifPresent(DeviceTransportConfiguration::validate);
if (device.getFirmwareId() != null) {
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!");
}
}
validateOtaPackage(tenantId, device, device.getDeviceProfileId());
}
}

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.DeviceProfile;
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.TenantProfile;
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.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.X509LwM2MBootstrapServerCredential;
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.rule.RuleChain;
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.exception.DataValidationException;
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.rule.RuleChainService;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
import org.thingsboard.server.dao.tenant.TenantService;
import java.util.HashSet;
@ -74,7 +72,7 @@ import java.util.Set;
import java.util.stream.Collectors;
@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 String ATTRIBUTES_PROTO_SCHEMA = "attributes proto schema";
@ -94,11 +92,11 @@ public class DeviceProfileDataValidator extends DataValidator<DeviceProfile> {
@Autowired
private QueueService queueService;
@Autowired
private OtaPackageService otaPackageService;
@Autowired
private RuleChainService ruleChainService;
@Autowired
private DashboardService dashboardService;
@Autowired
private TbTenantProfileCache tenantProfileCache;
private static String invalidSchemaProvidedMessage(String schemaName) {
return "[Transport Configuration] invalid " + schemaName + " provided!";
@ -133,6 +131,11 @@ public class DeviceProfileDataValidator extends DataValidator<DeviceProfile> {
if (queue == null) {
throw new DataValidationException("Device profile is referencing to non-existent queue!");
}
TenantProfile tenantProfile = tenantProfileCache.get(deviceProfile.getTenantId());
if ((tenantProfile.isIsolatedTbRuleEngine() && !queue.getTenantId().equals(deviceProfile.getTenantId()))
|| (!tenantProfile.isIsolatedTbRuleEngine() && !queue.getTenantId().isNullUid())) {
throw new DataValidationException("Can't assign queue from different tenant!");
}
}
if (deviceProfile.getProvisionType() == null) {
deviceProfile.setProvisionType(DeviceProfileProvisionType.DISABLED);
@ -192,6 +195,9 @@ public class DeviceProfileDataValidator extends DataValidator<DeviceProfile> {
if (ruleChain == null) {
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) {
@ -199,39 +205,12 @@ public class DeviceProfileDataValidator extends DataValidator<DeviceProfile> {
if (dashboard == null) {
throw new DataValidationException("Can't assign non-existent dashboard!");
}
}
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 (!dashboard.getTenantId().equals(deviceProfile.getTenantId())) {
throw new DataValidationException("Can't assign dashboard from different tenant!");
}
}
if (deviceProfile.getSoftwareId() != null) {
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!");
}
}
validateOtaPackage(tenantId, deviceProfile, deviceProfile.getId());
}
@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;
@Autowired
@Lazy
private OtaPackageService otaPackageService;
@Autowired

5
docker/.env

@ -25,4 +25,7 @@ DATABASE=postgres
LOAD_BALANCER_NAME=haproxy-certbot
# 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_TYPE: tb-core
EDGES_ENABLED: "true"
JAVA_OPTS: "${JAVA_OPTS}"
env_file:
- tb-node.env
volumes:
@ -73,6 +74,7 @@ services:
TB_SERVICE_ID: tb-core2
TB_SERVICE_TYPE: tb-core
EDGES_ENABLED: "true"
JAVA_OPTS: "${JAVA_OPTS}"
env_file:
- tb-node.env
volumes:
@ -96,6 +98,7 @@ services:
environment:
TB_SERVICE_ID: tb-rule-engine1
TB_SERVICE_TYPE: tb-rule-engine
JAVA_OPTS: "${JAVA_OPTS}"
env_file:
- tb-node.env
volumes:
@ -117,6 +120,7 @@ services:
environment:
TB_SERVICE_ID: tb-rule-engine2
TB_SERVICE_TYPE: tb-rule-engine
JAVA_OPTS: "${JAVA_OPTS}"
env_file:
- tb-node.env
volumes:
@ -132,6 +136,7 @@ services:
- "1883"
environment:
TB_SERVICE_ID: tb-mqtt-transport1
JAVA_OPTS: "${JAVA_OPTS}"
env_file:
- tb-mqtt-transport.env
volumes:
@ -148,6 +153,7 @@ services:
- "1883"
environment:
TB_SERVICE_ID: tb-mqtt-transport2
JAVA_OPTS: "${JAVA_OPTS}"
env_file:
- tb-mqtt-transport.env
volumes:
@ -164,6 +170,7 @@ services:
- "8081"
environment:
TB_SERVICE_ID: tb-http-transport1
JAVA_OPTS: "${JAVA_OPTS}"
env_file:
- tb-http-transport.env
volumes:
@ -180,6 +187,7 @@ services:
- "8081"
environment:
TB_SERVICE_ID: tb-http-transport2
JAVA_OPTS: "${JAVA_OPTS}"
env_file:
- tb-http-transport.env
volumes:
@ -196,6 +204,7 @@ services:
- "5683:5683/udp"
environment:
TB_SERVICE_ID: tb-coap-transport
JAVA_OPTS: "${JAVA_OPTS}"
env_file:
- tb-coap-transport.env
volumes:
@ -212,6 +221,7 @@ services:
- "5685:5685/udp"
environment:
TB_SERVICE_ID: tb-lwm2m-transport
JAVA_OPTS: "${JAVA_OPTS}"
env_file:
- tb-lwm2m-transport.env
volumes:
@ -226,6 +236,7 @@ services:
image: "${DOCKER_REPO}/${SNMP_TRANSPORT_DOCKER_NAME}:${TB_VERSION}"
environment:
TB_SERVICE_ID: tb-snmp-transport
JAVA_OPTS: "${JAVA_OPTS}"
env_file:
- tb-snmp-transport.env
volumes:
@ -256,6 +267,7 @@ services:
- "8081"
environment:
TB_SERVICE_ID: tb-vc-executor1
JAVA_OPTS: "${JAVA_OPTS}"
env_file:
- tb-vc-executor.env
volumes:
@ -272,6 +284,7 @@ services:
- "8081"
environment:
TB_SERVICE_ID: tb-vc-executor2
JAVA_OPTS: "${JAVA_OPTS}"
env_file:
- tb-vc-executor.env
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.testcontainers.containers.DockerComposeContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.thingsboard.server.common.data.StringUtils;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import static org.hamcrest.CoreMatchers.containsString;
@ -44,10 +49,13 @@ import static org.junit.Assert.fail;
public class ContainerTestSuite {
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 String QUEUE_TYPE = System.getProperty("blackBoxTests.queue", "kafka");
private static final String SOURCE_DIR = "./../../docker/";
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 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;
@ -66,6 +74,8 @@ public class ContainerTestSuite {
FileUtils.copyDirectory(new File(SOURCE_DIR), new File(targetDir));
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> {
public DockerComposeContainerImpl(List<File> composeFiles) {
super(composeFiles);
@ -81,17 +91,33 @@ public class ContainerTestSuite {
List<File> composeFiles = new ArrayList<>(Arrays.asList(
new File(targetDir + "docker-compose.yml"),
new File(targetDir + "docker-compose.volumes.yml"),
IS_HYBRID_MODE
? new File(targetDir + "docker-compose.hybrid.yml")
: new File(targetDir + "docker-compose.postgres.yml"),
new File(targetDir + (IS_HYBRID_MODE ? "docker-compose.hybrid.yml" : "docker-compose.postgres.yml")),
new File(targetDir + "docker-compose.postgres.volumes.yml"),
new File(targetDir + "docker-compose.kafka.yml"),
IS_REDIS_CLUSTER
? new File(targetDir + "docker-compose.redis-cluster.yml")
: new File(targetDir + "docker-compose.redis.yml"),
IS_REDIS_CLUSTER
? new File(targetDir + "docker-compose.redis-cluster.volumes.yml")
: new File(targetDir + "docker-compose.redis.volumes.yml")));
new File(targetDir + "docker-compose." + QUEUE_TYPE + ".yml"),
new File(targetDir + (IS_REDIS_CLUSTER ? "docker-compose.redis-cluster.yml" : "docker-compose.redis.yml")),
new File(targetDir + (IS_HYBRID_MODE ? "docker-compose.redis-cluster.volumes.yml" : "docker-compose.redis.volumes.yml"))
));
Map<String, String> queueEnv = new HashMap<>();
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) {
composeFiles.add(new File(targetDir + "docker-compose.cassandra.volumes.yml"));
@ -102,16 +128,18 @@ public class ContainerTestSuite {
.withLocalCompose(true)
.withTailChildContainers(!skipTailChildContainers)
.withEnv(installTb.getEnv())
.withEnv(queueEnv)
.withEnv("LOAD_BALANCER_NAME", "")
.withExposedService("haproxy", 80, Wait.forHttp("/swagger-ui.html").withStartupTimeout(Duration.ofSeconds(400)))
.waitingFor("tb-core1", Wait.forLogMessage(TB_CORE_LOG_REGEXP, 1).withStartupTimeout(Duration.ofSeconds(400)))
.waitingFor("tb-core2", Wait.forLogMessage(TB_CORE_LOG_REGEXP, 1).withStartupTimeout(Duration.ofSeconds(400)))
.waitingFor("tb-http-transport1", Wait.forLogMessage(TRANSPORTS_LOG_REGEXP, 1).withStartupTimeout(Duration.ofSeconds(400)))
.waitingFor("tb-http-transport2", Wait.forLogMessage(TRANSPORTS_LOG_REGEXP, 1).withStartupTimeout(Duration.ofSeconds(400)))
.waitingFor("tb-mqtt-transport1", Wait.forLogMessage(TRANSPORTS_LOG_REGEXP, 1).withStartupTimeout(Duration.ofSeconds(400)))
.waitingFor("tb-mqtt-transport2", Wait.forLogMessage(TRANSPORTS_LOG_REGEXP, 1).withStartupTimeout(Duration.ofSeconds(400)))
.waitingFor("tb-vc-executor1", Wait.forLogMessage(TB_VC_LOG_REGEXP, 1).withStartupTimeout(Duration.ofSeconds(400)))
.waitingFor("tb-vc-executor2", Wait.forLogMessage(TB_VC_LOG_REGEXP, 1).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(CONTAINER_STARTUP_TIMEOUT))
.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(CONTAINER_STARTUP_TIMEOUT))
.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(CONTAINER_STARTUP_TIMEOUT))
.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(CONTAINER_STARTUP_TIMEOUT))
.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) {
log.error("Failed to create test container", e);
fail("Failed to create test container");
@ -120,6 +148,23 @@ public class ContainerTestSuite {
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) {
try {
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.
* 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...
* */
*/
private static void replaceInFile(String sourceFilename, String target, String replacement, String verifyPhrase) {
try {
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_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 JAVA_OPTS = "-Xmx384m -Xss256k";
private final DockerComposeExecutor dockerCompose;
@ -102,6 +103,7 @@ public class ThingsBoardDbInstaller extends ExternalResource {
dockerCompose = new DockerComposeExecutor(composeFiles, project);
env = new HashMap<>();
env.put("JAVA_OPTS", JAVA_OPTS);
env.put("POSTGRES_DATA_VOLUME", postgresDataVolume);
if (IS_HYBRID_MODE) {
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 { _logger} from '../config/logger';
import http from 'http';
import { Socket } from 'net';
export class HttpServer {
private logger = _logger('httpServer');
private app = express();
private server;
private server: http.Server | null;
private connections: Socket[] = [];
constructor(httpPort: number) {
this.app.get('/livenessProbe', async (req, res) => {
@ -32,15 +35,31 @@ export class HttpServer {
})
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) => {
this.logger.error(error);
});
}
stop() {
this.server.close(() => {
this.logger.info('Http server stop');
this.server.on('connection', connection => {
this.connections.push(connection);
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",
"express": "^4.18.1",
"js-yaml": "^4.1.0",
"kafkajs": "^2.0.2",
"kafkajs": "^2.1.0",
"long": "^5.2.0",
"uuid-parse": "^1.1.0",
"uuid-random": "^1.3.2",

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

@ -54,82 +54,76 @@ export class AwsSqsTemplate implements IQueue {
FifoQueue: 'true'
};
name = 'AWS SQS';
constructor() {
}
async init() {
try {
this.logger.info('Starting ThingsBoard JavaScript Executor Microservice...');
this.sqsClient = new SQSClient({
apiVersion: '2012-11-05',
credentials: {
accessKeyId: this.accessKeyId,
secretAccessKey: this.secretAccessKey
},
region: this.region
});
this.sqsClient = new SQSClient({
apiVersion: '2012-11-05',
credentials: {
accessKeyId: this.accessKeyId,
secretAccessKey: this.secretAccessKey
},
region: this.region
});
const queues = await this.getQueues();
const queues = await this.getQueues();
if (queues.QueueUrls) {
queues.QueueUrls.forEach(queueUrl => {
const delimiterPosition = queueUrl.lastIndexOf('/');
const queueName = queueUrl.substring(delimiterPosition + 1);
this.queueUrls.set(queueName, queueUrl);
});
}
if (queues.QueueUrls) {
queues.QueueUrls.forEach(queueUrl => {
const delimiterPosition = queueUrl.lastIndexOf('/');
const queueName = queueUrl.substring(delimiterPosition + 1);
this.queueUrls.set(queueName, queueUrl);
});
}
this.parseQueueProperties();
this.parseQueueProperties();
this.requestQueueURL = this.queueUrls.get(AwsSqsTemplate.topicToSqsQueueName(this.requestTopic)) || '';
if (!this.requestQueueURL) {
this.requestQueueURL = await this.createQueue(this.requestTopic);
}
this.requestQueueURL = this.queueUrls.get(AwsSqsTemplate.topicToSqsQueueName(this.requestTopic)) || '';
if (!this.requestQueueURL) {
this.requestQueueURL = await this.createQueue(this.requestTopic);
}
const messageProcessor = new JsInvokeMessageProcessor(this);
const messageProcessor = new JsInvokeMessageProcessor(this);
const params: ReceiveMessageRequest = {
MaxNumberOfMessages: 10,
QueueUrl: this.requestQueueURL,
WaitTimeSeconds: this.pollInterval / 1000
};
while (!this.stopped) {
let pollStartTs = new Date().getTime();
const messagesResponse: ReceiveMessageResult = await this.sqsClient.send(new ReceiveMessageCommand(params));
const messages = messagesResponse.Messages;
if (messages && messages.length > 0) {
const entries: DeleteMessageBatchRequestEntry[] = [];
messages.forEach(message => {
entries.push({
Id: message.MessageId,
ReceiptHandle: message.ReceiptHandle
});
messageProcessor.onJsInvokeMessage(JSON.parse(message.Body || ''));
const params: ReceiveMessageRequest = {
MaxNumberOfMessages: 10,
QueueUrl: this.requestQueueURL,
WaitTimeSeconds: this.pollInterval / 1000
};
while (!this.stopped) {
let pollStartTs = new Date().getTime();
const messagesResponse: ReceiveMessageResult = await this.sqsClient.send(new ReceiveMessageCommand(params));
const messages = messagesResponse.Messages;
if (messages && messages.length > 0) {
const entries: DeleteMessageBatchRequestEntry[] = [];
messages.forEach(message => {
entries.push({
Id: message.MessageId,
ReceiptHandle: message.ReceiptHandle
});
messageProcessor.onJsInvokeMessage(JSON.parse(message.Body || ''));
});
const deleteBatch: DeleteMessageBatchRequest = {
QueueUrl: this.requestQueueURL,
Entries: entries
};
try {
await this.sqsClient.send(new DeleteMessageBatchCommand(deleteBatch))
} catch (err: any) {
this.logger.error("Failed to delete messages from queue.", err.message);
}
} else {
let pollDuration = new Date().getTime() - pollStartTs;
if (pollDuration < this.pollInterval) {
await sleep(this.pollInterval - pollDuration);
}
const deleteBatch: DeleteMessageBatchRequest = {
QueueUrl: this.requestQueueURL,
Entries: entries
};
try {
await this.sqsClient.send(new DeleteMessageBatchCommand(deleteBatch))
} catch (err: any) {
this.logger.error("Failed to delete messages from queue.", err.message);
}
} else {
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);
}
}
@ -187,29 +181,21 @@ export class AwsSqsTemplate implements IQueue {
return result.QueueUrl || '';
}
static async build(): Promise<AwsSqsTemplate> {
const queue = new AwsSqsTemplate();
await queue.init();
return queue;
}
async exit(status: number) {
async destroy(): Promise<void> {
this.stopped = true;
this.logger.info('Exiting with status: %d ...', status);
this.logger.info('Stopping AWS SQS resources...');
if (this.sqsClient) {
this.logger.info('Stopping Aws Sqs client.')
this.logger.info('Stopping AWS SQS client...');
try {
this.sqsClient.destroy();
const _sqsClient = this.sqsClient;
// @ts-ignore
delete this.sqsClient;
this.logger.info('Aws Sqs client stopped.')
process.exit(status);
_sqsClient.destroy();
this.logger.info('AWS SQS client stopped.');
} catch (e: any) {
this.logger.info('Aws Sqs client stop error.');
process.exit(status);
this.logger.info('AWS SQS client stop error.');
}
} 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 sendLoopInstance: NodeJS.Timeout;
name = 'Kafka';
constructor() {
}
async init(): Promise<void> {
try {
this.logger.info('Starting ThingsBoard JavaScript Executor Microservice...');
const kafkaBootstrapServers: string = config.get('kafka.bootstrap.servers');
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 Requests Topic: %s', requestTopic);
this.logger.info('Kafka Bootstrap Servers: %s', kafkaBootstrapServers);
this.logger.info('Kafka Requests Topic: %s', requestTopic);
let kafkaConfig: KafkaConfig = {
brokers: kafkaBootstrapServers.split(','),
logLevel: logLevel.INFO,
logCreator: KafkaJsWinstonLogCreator
};
let kafkaConfig: KafkaConfig = {
brokers: kafkaBootstrapServers.split(','),
logLevel: logLevel.INFO,
logCreator: KafkaJsWinstonLogCreator
};
if (this.kafkaClientId) {
kafkaConfig['clientId'] = this.kafkaClientId;
} else {
this.logger.warn('KAFKA_CLIENT_ID is undefined. Consider to define the env variable KAFKA_CLIENT_ID');
}
if (this.kafkaClientId) {
kafkaConfig['clientId'] = this.kafkaClientId;
} else {
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) {
kafkaConfig['sasl'] = {
mechanism: config.get('kafka.confluent.sasl.mechanism') as any,
username: config.get('kafka.confluent.username'),
password: config.get('kafka.confluent.password')
};
kafkaConfig['ssl'] = true;
}
if (useConfluent) {
kafkaConfig['sasl'] = {
mechanism: config.get('kafka.confluent.sasl.mechanism') as any,
username: config.get('kafka.confluent.username'),
password: config.get('kafka.confluent.password')
};
kafkaConfig['ssl'] = true;
}
this.parseTopicProperties();
this.parseTopicProperties();
this.kafkaClient = new Kafka(kafkaConfig);
this.kafkaAdmin = this.kafkaClient.admin();
await this.kafkaAdmin.connect();
this.kafkaClient = new Kafka(kafkaConfig);
this.kafkaAdmin = this.kafkaClient.admin();
await this.kafkaAdmin.connect();
let partitions = 1;
let partitions = 1;
for (let i = 0; i < this.configEntries.length; i++) {
let param = this.configEntries[i];
if (param.name === 'partitions') {
partitions = param.value;
this.configEntries.splice(i, 1);
break;
}
for (let i = 0; i < this.configEntries.length; i++) {
let param = this.configEntries[i];
if (param.name === 'partitions') {
partitions = param.value;
this.configEntries.splice(i, 1);
break;
}
}
let topics = await this.kafkaAdmin.listTopics();
let topics = await this.kafkaAdmin.listTopics();
if (!topics.includes(requestTopic)) {
let createRequestTopicResult = await this.createTopic(requestTopic, partitions);
if (createRequestTopicResult) {
this.logger.info('Created new topic: %s', requestTopic);
}
if (!topics.includes(requestTopic)) {
let createRequestTopicResult = await this.createTopic(requestTopic, partitions);
if (createRequestTopicResult) {
this.logger.info('Created new topic: %s', requestTopic);
}
}
this.consumer = this.kafkaClient.consumer({groupId: 'js-executor-group'});
this.producer = this.kafkaClient.producer({createPartitioner: Partitioners.DefaultPartitioner});
const {CRASH} = this.consumer.events;
this.consumer = this.kafkaClient.consumer({groupId: 'js-executor-group'});
this.producer = this.kafkaClient.producer({createPartitioner: Partitioners.DefaultPartitioner});
this.consumer.on(CRASH, e => {
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 {CRASH} = this.consumer.events;
const messageProcessor = new JsInvokeMessageProcessor(this);
await this.consumer.connect();
await this.producer.connect();
this.sendLoopWithLinger();
await this.consumer.subscribe({topic: requestTopic});
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);
},
});
this.consumer.on(CRASH, async (e) => {
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!');
await this.destroy();
}
});
} 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);
}
}
const messageProcessor = new JsInvokeMessageProcessor(this);
await this.consumer.connect();
await this.producer.connect();
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> {
this.logger.debug('Pending queue response, scriptId: [%s]', scriptId);
@ -235,41 +227,33 @@ export class KafkaTemplate implements IQueue {
}, this.linger);
}
static async build(): Promise<KafkaTemplate> {
const queue = new KafkaTemplate();
await queue.init();
return queue;
}
async exit(status: number): Promise<void> {
this.logger.info('Exiting with status: %d ...', status);
async destroy(): Promise<void> {
this.logger.info('Stopping Kafka resources...');
if (this.kafkaAdmin) {
this.logger.info('Stopping Kafka Admin...');
await this.kafkaAdmin.disconnect();
const _kafkaAdmin = this.kafkaAdmin;
// @ts-ignore
delete this.kafkaAdmin;
await _kafkaAdmin.disconnect();
this.logger.info('Kafka Admin stopped.');
}
if (this.consumer) {
this.logger.info('Stopping Kafka Consumer...');
try {
await this.consumer.disconnect();
const _consumer = this.consumer;
// @ts-ignore
delete this.consumer;
await _consumer.disconnect();
this.logger.info('Kafka Consumer stopped.');
await this.disconnectProducer();
process.exit(status);
} catch (e: any) {
this.logger.info('Kafka Consumer stop error.');
await this.disconnectProducer();
process.exit(status);
}
} else {
process.exit(status);
}
this.logger.info('Kafka resources stopped.');
}
private async disconnectProducer(): Promise<void> {
@ -279,13 +263,15 @@ export class KafkaTemplate implements IQueue {
this.logger.info('Stopping loop...');
clearTimeout(this.sendLoopInstance);
await this.sendMessagesAsBatch();
await this.producer.disconnect();
const _producer = this.producer;
// @ts-ignore
delete this.producer;
await _producer.disconnect();
this.logger.info('Kafka Producer stopped.');
} catch (e) {
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 subscriptions: string[] = [];
name = 'Pub/Sub';
constructor() {
}
async init() {
try {
this.logger.info('Starting ThingsBoard JavaScript Executor Microservice...');
this.pubSubClient = new PubSub({
projectId: this.projectId,
credentials: this.credentials
});
this.parseQueueProperties();
this.pubSubClient = new PubSub({
projectId: this.projectId,
credentials: this.credentials
});
const topicList = await this.pubSubClient.getTopics();
this.parseQueueProperties();
if (topicList) {
topicList[0].forEach(topic => {
this.topics.push(PubSubTemplate.getName(topic.name));
});
}
const topicList = await this.pubSubClient.getTopics();
const subscriptionList = await this.pubSubClient.getSubscriptions();
if (topicList) {
topicList[0].forEach(topic => {
this.topics.push(PubSubTemplate.getName(topic.name));
});
}
if (subscriptionList) {
topicList[0].forEach(sub => {
this.subscriptions.push(PubSubTemplate.getName(sub.name));
});
}
const subscriptionList = await this.pubSubClient.getSubscriptions();
if (!(this.subscriptions.includes(this.requestTopic) && this.topics.includes(this.requestTopic))) {
await this.createTopic(this.requestTopic);
await this.createSubscription(this.requestTopic);
}
if (subscriptionList) {
topicList[0].forEach(sub => {
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) => {
messageProcessor.onJsInvokeMessage(JSON.parse(message.data.toString('utf8')));
message.ack();
};
const messageProcessor = new JsInvokeMessageProcessor(this);
subscription.on('message', messageHandler);
const messageHandler = (message: Message) => {
messageProcessor.onJsInvokeMessage(JSON.parse(message.data.toString('utf8')));
message.ack();
};
} 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);
}
subscription.on('message', messageHandler);
}
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> {
const queue = new PubSubTemplate();
await queue.init();
return queue;
}
async exit(status: number): Promise<void> {
this.logger.info('Exiting with status: %d ...', status);
async destroy(): Promise<void> {
this.logger.info('Stopping Pub/Sub resources...');
if (this.pubSubClient) {
this.logger.info('Stopping Pub/Sub client.')
this.logger.info('Stopping Pub/Sub client...');
try {
await this.pubSubClient.close();
const _pubSubClient = this.pubSubClient;
// @ts-ignore
delete this.pubSubClient;
this.logger.info('Pub/Sub client stopped.')
process.exit(status);
await _pubSubClient.close();
this.logger.info('Pub/Sub client stopped.');
} catch (e) {
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 {
name: string;
init(): Promise<void>;
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 amqp, { ConfirmChannel, Connection } from 'amqplib';
import { Options, Replies } from 'amqplib/properties';
import { sleep } from '../api/utils';
export class RabbitMqTemplate implements IQueue {
@ -32,7 +31,6 @@ export class RabbitMqTemplate implements IQueue {
private username = config.get('rabbitmq.username');
private password = config.get('rabbitmq.password');
private queueProperties: string = config.get('rabbitmq.queue_properties');
private pollInterval = Number(config.get('js.response_poll_interval'));
private queueOptions: Options.AssertQueue = {
durable: false,
@ -41,45 +39,30 @@ export class RabbitMqTemplate implements IQueue {
};
private connection: Connection;
private channel: ConfirmChannel;
private stopped = false;
private topics: string[] = [];
name = 'RabbitMQ';
constructor() {
}
async init(): Promise<void> {
try {
this.logger.info('Starting ThingsBoard JavaScript Executor Microservice...');
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();
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();
await this.createQueue(this.requestTopic);
this.parseQueueProperties();
const messageProcessor = new JsInvokeMessageProcessor(this);
await this.createQueue(this.requestTopic);
while (!this.stopped) {
let pollStartTs = new Date().getTime();
let message = await this.channel.get(this.requestTopic);
const messageProcessor = new JsInvokeMessageProcessor(this);
if (message) {
messageProcessor.onJsInvokeMessage(JSON.parse(message.content.toString('utf8')));
this.channel.ack(message);
} else {
let pollDuration = new Date().getTime() - pollStartTs;
if (pollDuration < this.pollInterval) {
await sleep(this.pollInterval - pollDuration);
}
}
await this.channel.consume(this.requestTopic, (message) => {
if (message) {
messageProcessor.onJsInvokeMessage(JSON.parse(message.content.toString('utf8')));
this.channel.ack(message);
}
} 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> {
@ -114,38 +97,31 @@ export class RabbitMqTemplate implements IQueue {
return this.channel.assertQueue(topic, this.queueOptions);
}
static async build(): Promise<RabbitMqTemplate> {
const queue = new RabbitMqTemplate();
await queue.init();
return queue;
}
async exit(status: number) {
this.logger.info('Exiting with status: %d ...', status);
async destroy() {
this.logger.info('Stopping RabbitMQ resources...');
if (this.channel) {
this.logger.info('Stopping RabbitMq chanel.')
await this.channel.close();
this.logger.info('Stopping RabbitMQ chanel...');
const _channel = this.channel;
// @ts-ignore
delete this.channel;
this.logger.info('RabbitMq chanel stopped');
await _channel.close();
this.logger.info('RabbitMQ chanel stopped');
}
if (this.connection) {
this.logger.info('Stopping RabbitMq connection.')
this.logger.info('Stopping RabbitMQ connection...')
try {
await this.connection.close();
const _connection = this.connection;
// @ts-ignore
delete this.connection;
this.logger.info('RabbitMq client connection.')
process.exit(status);
await _connection.close();
this.logger.info('RabbitMQ client connection.');
} catch (e) {
this.logger.info('RabbitMq connection stop error.');
process.exit(status);
this.logger.info('RabbitMQ connection stop error.');
}
} 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 senderMap = new Map<string, ServiceBusSender>();
name = 'Azure Service Bus';
constructor() {
}
async init() {
try {
this.logger.info('Starting ThingsBoard JavaScript Executor Microservice...');
const connectionString = `Endpoint=sb://${this.namespaceName}.servicebus.windows.net/;SharedAccessKeyName=${this.sasKeyName};SharedAccessKey=${this.sasKey}`;
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.sbClient = new ServiceBusClient(connectionString)
this.serviceBusService = new ServiceBusAdministrationClient(connectionString);
this.parseQueueProperties();
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();
for await (const queue of listQueues) {
this.queues.push(queue.name);
}
if (!this.queues.includes(this.requestTopic)) {
await this.createQueueIfNotExist(this.requestTopic);
this.queues.push(this.requestTopic);
}
if (!this.queues.includes(this.requestTopic)) {
await this.createQueueIfNotExist(this.requestTopic);
this.queues.push(this.requestTopic);
}
this.receiver = this.sbClient.createReceiver(this.requestTopic, {receiveMode: 'peekLock'});
this.receiver = this.sbClient.createReceiver(this.requestTopic, {receiveMode: 'peekLock'});
const messageProcessor = new JsInvokeMessageProcessor(this);
const messageHandler = async (message: ServiceBusReceivedMessage) => {
if (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);
};
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);
}
const messageProcessor = new JsInvokeMessageProcessor(this);
const messageHandler = async (message: ServiceBusReceivedMessage) => {
if (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);
};
this.receiver.subscribe({processMessage: messageHandler, processError: errorHandler})
}
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> {
const queue = new ServiceBusTemplate();
await queue.init();
return queue;
}
async exit(status: number) {
this.logger.info('Exiting with status: %d ...', status);
async destroy() {
this.logger.info('Stopping Azure Service Bus resources...')
if (this.receiver) {
this.logger.info('Stopping Service Bus Receiver...');
try {
await this.receiver.close();
const _receiver = this.receiver;
// @ts-ignore
delete this.receiver;
await _receiver.close();
this.logger.info('Service Bus Receiver stopped.');
} catch (e) {
this.logger.info('Service Bus Receiver stop error.');
}
}
this.senderMap.forEach(k => {
try {
k.close();
} catch (e) {
}
this.logger.info('Stopping Service Bus Senders...');
const senders: Promise<void>[] = [];
this.senderMap.forEach((sender) => {
senders.push(sender.close());
});
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) {
this.logger.info('Stopping Service Bus Client...');
try {
await this.sbClient.close();
const _sbClient = this.sbClient;
// @ts-ignore
delete this.sbClient;
await _sbClient.close();
this.logger.info('Service Bus Client stopped.');
} catch (e) {
this.logger.info('Service Bus Client stop error.');
}
}
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('===CONFIG END===');
const serviceType = config.get('queue_type');
const serviceType: string = config.get('queue_type');
const httpPort = Number(config.get('http_port'));
let queues: IQueue;
let httpServer: HttpServer;
let queues: IQueue | null;
let httpServer: HttpServer | null;
(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) {
case 'kafka':
logger.info('Starting kafka template.');
queues = await KafkaTemplate.build();
logger.info('kafka template started.');
break;
return new KafkaTemplate();
case 'pubsub':
logger.info('Starting Pub/Sub template.')
queues = await PubSubTemplate.build();
logger.info('Pub/Sub template started.')
break;
return new PubSubTemplate();
case 'aws-sqs':
logger.info('Starting Aws Sqs template.')
queues = await AwsSqsTemplate.build();
logger.info('Aws Sqs template started.')
break;
return new AwsSqsTemplate();
case 'rabbitmq':
logger.info('Starting RabbitMq template.')
queues = await RabbitMqTemplate.build();
logger.info('RabbitMq template started.')
break;
return new RabbitMqTemplate();
case 'service-bus':
logger.info('Starting Azure Service Bus template.')
queues = await ServiceBusTemplate.build();
logger.info('Azure Service Bus template started.')
break;
return new ServiceBusTemplate();
default:
logger.error('Unknown service type: ', serviceType);
process.exit(-1);
throw new Error('Unknown service type: ' + serviceType);
}
}
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', () => {
logger.info('SIGTERM signal received');
process.exit(0);
process.on('exit', (code: number) => {
logger.info(`ThingsBoard JavaScript Executor Microservice has been stopped. Exit code: ${code}.`);
});
process.on('exit', async () => {
async function exit(status: number) {
logger.info('Exiting with status: %d ...', status);
if (httpServer) {
httpServer.stop();
const _httpServer = httpServer;
httpServer = null;
await _httpServer.stop();
}
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"
safe-buffer "^5.0.1"
kafkajs@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/kafkajs/-/kafkajs-2.0.2.tgz#cdfc8f57aa4fd69f6d9ca1cce4ee89bbc2a3a1f9"
integrity sha512-g6CM3fAenofOjR1bfOAqeZUEaSGhNtBscNokybSdW1rmIKYNwBPC9xQzwulFJm36u/xcxXUiCl/L/qfslapihA==
kafkajs@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/kafkajs/-/kafkajs-2.1.0.tgz#32ede4e8080cc75586c5e4406eeb582fa73f7b1e"
integrity sha512-6IYiOdGWvFPbSbVB+AV3feT+A7vzw5sXm7Ze4QTfP7FRNdY8pGcpiNPvD2lfgYFD8Dm9KbMgBgTt2mf8KaIkzw==
keyv@^3.0.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
value: "${TB_QUEUE_KAFKA_SESSION_TIMEOUT_MS:10000}" # (10 seconds)
topic-properties:
rule-engine: "${TB_QUEUE_KAFKA_RE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100;min.insync.replicas:1}"
ota-updates: "${TB_QUEUE_KAFKA_OTA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:10;min.insync.replicas:1}"
version-control: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
consumer-stats:
enabled: "${TB_QUEUE_KAFKA_CONSUMER_STATS_ENABLED:true}"
print-interval-ms: "${TB_QUEUE_KAFKA_CONSUMER_STATS_MIN_PRINT_INTERVAL_MS:60000}"
@ -104,11 +101,9 @@ queue:
region: "${TB_QUEUE_AWS_SQS_REGION:YOUR_REGION}"
threads_per_topic: "${TB_QUEUE_AWS_SQS_THREADS_PER_TOPIC:1}"
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}"
transport-api: "${TB_QUEUE_AWS_SQS_TA_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
notifications: "${TB_QUEUE_AWS_SQS_NOTIFICATIONS_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
js-executor: "${TB_QUEUE_AWS_SQS_JE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
version-control: "${TB_QUEUE_AWS_SQS_VC_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
# VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds
pubsub:
project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}"
@ -116,22 +111,18 @@ queue:
max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" #in bytes
max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}"
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}"
transport-api: "${TB_QUEUE_PUBSUB_TA_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
notifications: "${TB_QUEUE_PUBSUB_NOTIFICATIONS_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
js-executor: "${TB_QUEUE_PUBSUB_JE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
version-control: "${TB_QUEUE_PUBSUB_VC_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
service_bus:
namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}"
sas_key_name: "${TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME:YOUR_SAS_KEY_NAME}"
sas_key: "${TB_QUEUE_SERVICE_BUS_SAS_KEY:YOUR_SAS_KEY}"
max_messages: "${TB_QUEUE_SERVICE_BUS_MAX_MESSAGES:1000}"
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}"
transport-api: "${TB_QUEUE_SERVICE_BUS_TA_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
notifications: "${TB_QUEUE_SERVICE_BUS_NOTIFICATIONS_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
js-executor: "${TB_QUEUE_SERVICE_BUS_JE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
version-control: "${TB_QUEUE_SERVICE_BUS_VC_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
rabbitmq:
exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}"
host: "${TB_QUEUE_RABBIT_MQ_HOST:localhost}"
@ -143,11 +134,9 @@ queue:
connection_timeout: "${TB_QUEUE_RABBIT_MQ_CONNECTION_TIMEOUT:60000}"
handshake_timeout: "${TB_QUEUE_RABBIT_MQ_HANDSHAKE_TIMEOUT:10000}"
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}"
transport-api: "${TB_QUEUE_RABBIT_MQ_TA_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
notifications: "${TB_QUEUE_RABBIT_MQ_NOTIFICATIONS_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
js-executor: "${TB_QUEUE_RABBIT_MQ_JE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
version-control: "${TB_QUEUE_RABBIT_MQ_VC_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
partitions:
hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256
core:

2
msa/web-ui/server.ts

@ -123,7 +123,7 @@ let connections: Socket[] = [];
})();
[`SIGINT`, `SIGUSR1`, `SIGUSR2`, `uncaughtException`, `SIGTERM`].forEach((eventType) => {
process.on(eventType, async () => {
process.once(eventType, async () => {
logger.info(`${eventType} signal received`);
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}"
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}"
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:
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}"
@ -186,7 +185,6 @@ queue:
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}"
js-executor: "${TB_QUEUE_AWS_SQS_JE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
# VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds
pubsub:
project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}"
@ -198,7 +196,6 @@ queue:
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}"
js-executor: "${TB_QUEUE_PUBSUB_JE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
service_bus:
namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}"
sas_key_name: "${TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME:YOUR_SAS_KEY_NAME}"
@ -209,7 +206,6 @@ queue:
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}"
js-executor: "${TB_QUEUE_SERVICE_BUS_JE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
rabbitmq:
exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}"
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}"
transport-api: "${TB_QUEUE_RABBIT_MQ_TA_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
notifications: "${TB_QUEUE_RABBIT_MQ_NOTIFICATIONS_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
js-executor: "${TB_QUEUE_RABBIT_MQ_JE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
partitions:
hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}"
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}"
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}"
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:
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}"
@ -173,7 +172,6 @@ queue:
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}"
js-executor: "${TB_QUEUE_AWS_SQS_JE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
# VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds
pubsub:
project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}"
@ -185,7 +183,6 @@ queue:
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}"
js-executor: "${TB_QUEUE_PUBSUB_JE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
service_bus:
namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}"
sas_key_name: "${TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME:YOUR_SAS_KEY_NAME}"
@ -196,7 +193,6 @@ queue:
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}"
js-executor: "${TB_QUEUE_SERVICE_BUS_JE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
rabbitmq:
exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}"
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}"
transport-api: "${TB_QUEUE_RABBIT_MQ_TA_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
notifications: "${TB_QUEUE_RABBIT_MQ_NOTIFICATIONS_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
js-executor: "${TB_QUEUE_RABBIT_MQ_JE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
partitions:
hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}"
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}"
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}"
js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100}"
aws_sqs:
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}"
@ -251,7 +250,6 @@ queue:
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}"
js-executor: "${TB_QUEUE_AWS_SQS_JE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
# VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds
pubsub:
project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}"
@ -263,7 +261,6 @@ queue:
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}"
js-executor: "${TB_QUEUE_PUBSUB_JE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
service_bus:
namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}"
sas_key_name: "${TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME:YOUR_SAS_KEY_NAME}"
@ -274,7 +271,6 @@ queue:
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}"
js-executor: "${TB_QUEUE_SERVICE_BUS_JE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
rabbitmq:
exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}"
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}"
transport-api: "${TB_QUEUE_RABBIT_MQ_TA_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
notifications: "${TB_QUEUE_RABBIT_MQ_NOTIFICATIONS_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
js-executor: "${TB_QUEUE_RABBIT_MQ_JE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
partitions:
hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}"
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}"
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}"
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:
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}"
@ -203,7 +202,6 @@ queue:
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}"
js-executor: "${TB_QUEUE_AWS_SQS_JE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
# VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds
pubsub:
project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}"
@ -215,7 +213,6 @@ queue:
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}"
js-executor: "${TB_QUEUE_PUBSUB_JE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
service_bus:
namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}"
sas_key_name: "${TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME:YOUR_SAS_KEY_NAME}"
@ -226,7 +223,6 @@ queue:
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}"
js-executor: "${TB_QUEUE_SERVICE_BUS_JE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
rabbitmq:
exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}"
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}"
transport-api: "${TB_QUEUE_RABBIT_MQ_TA_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
notifications: "${TB_QUEUE_RABBIT_MQ_NOTIFICATIONS_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
js-executor: "${TB_QUEUE_RABBIT_MQ_JE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
partitions:
hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}"
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}"
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}"
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:
enabled: "${TB_QUEUE_KAFKA_CONSUMER_STATS_ENABLED:true}"
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}"
transport-api: "${TB_QUEUE_AWS_SQS_TA_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
notifications: "${TB_QUEUE_AWS_SQS_NOTIFICATIONS_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
js-executor: "${TB_QUEUE_AWS_SQS_JE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}"
# VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds
pubsub:
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}"
transport-api: "${TB_QUEUE_PUBSUB_TA_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
notifications: "${TB_QUEUE_PUBSUB_NOTIFICATIONS_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
js-executor: "${TB_QUEUE_PUBSUB_JE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}"
service_bus:
namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}"
sas_key_name: "${TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME:YOUR_SAS_KEY_NAME}"
@ -176,7 +173,6 @@ queue:
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}"
js-executor: "${TB_QUEUE_SERVICE_BUS_JE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}"
rabbitmq:
exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}"
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}"
transport-api: "${TB_QUEUE_RABBIT_MQ_TA_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
notifications: "${TB_QUEUE_RABBIT_MQ_NOTIFICATIONS_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
js-executor: "${TB_QUEUE_RABBIT_MQ_JE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}"
partitions:
hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}"
virtual_nodes_size: "${TB_QUEUE_PARTITIONS_VIRTUAL_NODES_SIZE:16}"

Loading…
Cancel
Save