Commit e981edd83c6ddf5cfa0ec137a240b3ab2194790e

Authored by Andrii Shvaika
2 parents f157d07c 67ae7ef3

Merge remote-tracking branch 'origin/master'

Showing 25 changed files with 130 additions and 33 deletions
@@ -73,7 +73,7 @@ import org.thingsboard.server.service.executors.ExternalCallExecutorService; @@ -73,7 +73,7 @@ import org.thingsboard.server.service.executors.ExternalCallExecutorService;
73 import org.thingsboard.server.service.executors.SharedEventLoopGroupService; 73 import org.thingsboard.server.service.executors.SharedEventLoopGroupService;
74 import org.thingsboard.server.service.mail.MailExecutorService; 74 import org.thingsboard.server.service.mail.MailExecutorService;
75 import org.thingsboard.server.service.profile.TbDeviceProfileCache; 75 import org.thingsboard.server.service.profile.TbDeviceProfileCache;
76 -import org.thingsboard.server.service.profile.TbTenantProfileCache; 76 +import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
77 import org.thingsboard.server.service.queue.TbClusterService; 77 import org.thingsboard.server.service.queue.TbClusterService;
78 import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService; 78 import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService;
79 import org.thingsboard.server.service.rpc.TbRuleEngineDeviceRpcService; 79 import org.thingsboard.server.service.rpc.TbRuleEngineDeviceRpcService;
@@ -30,7 +30,6 @@ import org.thingsboard.server.common.data.Tenant; @@ -30,7 +30,6 @@ import org.thingsboard.server.common.data.Tenant;
30 import org.thingsboard.server.common.data.TenantProfile; 30 import org.thingsboard.server.common.data.TenantProfile;
31 import org.thingsboard.server.common.data.id.EntityId; 31 import org.thingsboard.server.common.data.id.EntityId;
32 import org.thingsboard.server.common.data.id.TenantId; 32 import org.thingsboard.server.common.data.id.TenantId;
33 -import org.thingsboard.server.common.data.id.TenantProfileId;  
34 import org.thingsboard.server.common.data.page.PageDataIterable; 33 import org.thingsboard.server.common.data.page.PageDataIterable;
35 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; 34 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
36 import org.thingsboard.server.common.msg.MsgType; 35 import org.thingsboard.server.common.msg.MsgType;
@@ -41,7 +40,7 @@ import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg; @@ -41,7 +40,7 @@ import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg;
41 import org.thingsboard.server.common.msg.queue.RuleEngineException; 40 import org.thingsboard.server.common.msg.queue.RuleEngineException;
42 import org.thingsboard.server.common.msg.queue.ServiceType; 41 import org.thingsboard.server.common.msg.queue.ServiceType;
43 import org.thingsboard.server.dao.tenant.TenantService; 42 import org.thingsboard.server.dao.tenant.TenantService;
44 -import org.thingsboard.server.service.profile.TbTenantProfileCache; 43 +import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
45 import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper; 44 import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
46 45
47 import java.util.HashSet; 46 import java.util.HashSet;
@@ -35,7 +35,6 @@ import org.thingsboard.server.common.data.asset.AssetInfo; @@ -35,7 +35,6 @@ import org.thingsboard.server.common.data.asset.AssetInfo;
35 import org.thingsboard.server.common.data.audit.ActionType; 35 import org.thingsboard.server.common.data.audit.ActionType;
36 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; 36 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
37 import org.thingsboard.server.common.data.exception.ThingsboardException; 37 import org.thingsboard.server.common.data.exception.ThingsboardException;
38 -import org.thingsboard.server.common.data.id.*;  
39 import org.thingsboard.server.common.data.id.AlarmId; 38 import org.thingsboard.server.common.data.id.AlarmId;
40 import org.thingsboard.server.common.data.id.AssetId; 39 import org.thingsboard.server.common.data.id.AssetId;
41 import org.thingsboard.server.common.data.id.CustomerId; 40 import org.thingsboard.server.common.data.id.CustomerId;
@@ -94,7 +93,7 @@ import org.thingsboard.server.queue.provider.TbQueueProducerProvider; @@ -94,7 +93,7 @@ import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
94 import org.thingsboard.server.queue.util.TbCoreComponent; 93 import org.thingsboard.server.queue.util.TbCoreComponent;
95 import org.thingsboard.server.service.component.ComponentDiscoveryService; 94 import org.thingsboard.server.service.component.ComponentDiscoveryService;
96 import org.thingsboard.server.service.profile.TbDeviceProfileCache; 95 import org.thingsboard.server.service.profile.TbDeviceProfileCache;
97 -import org.thingsboard.server.service.profile.TbTenantProfileCache; 96 +import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
98 import org.thingsboard.server.service.queue.TbClusterService; 97 import org.thingsboard.server.service.queue.TbClusterService;
99 import org.thingsboard.server.service.security.model.SecurityUser; 98 import org.thingsboard.server.service.security.model.SecurityUser;
100 import org.thingsboard.server.service.security.permission.AccessControlService; 99 import org.thingsboard.server.service.security.permission.AccessControlService;
@@ -392,6 +392,11 @@ public class TelemetryController extends BaseController { @@ -392,6 +392,11 @@ public class TelemetryController extends BaseController {
392 if (attributes.isEmpty()) { 392 if (attributes.isEmpty()) {
393 return getImmediateDeferredResult("No attributes data found in request body!", HttpStatus.BAD_REQUEST); 393 return getImmediateDeferredResult("No attributes data found in request body!", HttpStatus.BAD_REQUEST);
394 } 394 }
  395 + for (AttributeKvEntry attributeKvEntry: attributes) {
  396 + if (attributeKvEntry.getKey().isEmpty() || attributeKvEntry.getKey().trim().length() == 0) {
  397 + return getImmediateDeferredResult("Key cannot be empty or contains only spaces", HttpStatus.BAD_REQUEST);
  398 + }
  399 + }
395 SecurityUser user = getCurrentUser(); 400 SecurityUser user = getCurrentUser();
396 return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.WRITE_ATTRIBUTES, entityIdSrc, (result, tenantId, entityId) -> { 401 return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.WRITE_ATTRIBUTES, entityIdSrc, (result, tenantId, entityId) -> {
397 tsSubService.saveAndNotify(tenantId, entityId, scope, attributes, new FutureCallback<Void>() { 402 tsSubService.saveAndNotify(tenantId, entityId, scope, attributes, new FutureCallback<Void>() {
@@ -55,7 +55,7 @@ import org.thingsboard.server.queue.discovery.PartitionChangeEvent; @@ -55,7 +55,7 @@ import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
55 import org.thingsboard.server.queue.discovery.PartitionService; 55 import org.thingsboard.server.queue.discovery.PartitionService;
56 import org.thingsboard.server.queue.scheduler.SchedulerComponent; 56 import org.thingsboard.server.queue.scheduler.SchedulerComponent;
57 import org.thingsboard.server.queue.util.TbCoreComponent; 57 import org.thingsboard.server.queue.util.TbCoreComponent;
58 -import org.thingsboard.server.service.profile.TbTenantProfileCache; 58 +import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
59 import org.thingsboard.server.service.queue.TbClusterService; 59 import org.thingsboard.server.service.queue.TbClusterService;
60 import org.thingsboard.server.service.telemetry.InternalTelemetryService; 60 import org.thingsboard.server.service.telemetry.InternalTelemetryService;
61 61
@@ -55,7 +55,7 @@ import org.thingsboard.server.queue.provider.TbCoreQueueFactory; @@ -55,7 +55,7 @@ import org.thingsboard.server.queue.provider.TbCoreQueueFactory;
55 import org.thingsboard.server.queue.util.TbCoreComponent; 55 import org.thingsboard.server.queue.util.TbCoreComponent;
56 import org.thingsboard.server.service.apiusage.TbApiUsageStateService; 56 import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
57 import org.thingsboard.server.service.profile.TbDeviceProfileCache; 57 import org.thingsboard.server.service.profile.TbDeviceProfileCache;
58 -import org.thingsboard.server.service.profile.TbTenantProfileCache; 58 +import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
59 import org.thingsboard.server.service.queue.processing.AbstractConsumerService; 59 import org.thingsboard.server.service.queue.processing.AbstractConsumerService;
60 import org.thingsboard.server.service.rpc.FromDeviceRpcResponse; 60 import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
61 import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService; 61 import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService;
@@ -45,7 +45,7 @@ import org.thingsboard.server.queue.settings.TbRuleEngineQueueConfiguration; @@ -45,7 +45,7 @@ import org.thingsboard.server.queue.settings.TbRuleEngineQueueConfiguration;
45 import org.thingsboard.server.queue.util.TbRuleEngineComponent; 45 import org.thingsboard.server.queue.util.TbRuleEngineComponent;
46 import org.thingsboard.server.service.apiusage.TbApiUsageStateService; 46 import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
47 import org.thingsboard.server.service.profile.TbDeviceProfileCache; 47 import org.thingsboard.server.service.profile.TbDeviceProfileCache;
48 -import org.thingsboard.server.service.profile.TbTenantProfileCache; 48 +import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
49 import org.thingsboard.server.service.queue.processing.AbstractConsumerService; 49 import org.thingsboard.server.service.queue.processing.AbstractConsumerService;
50 import org.thingsboard.server.service.queue.processing.TbRuleEngineProcessingDecision; 50 import org.thingsboard.server.service.queue.processing.TbRuleEngineProcessingDecision;
51 import org.thingsboard.server.service.queue.processing.TbRuleEngineProcessingResult; 51 import org.thingsboard.server.service.queue.processing.TbRuleEngineProcessingResult;
@@ -21,11 +21,10 @@ import org.springframework.stereotype.Service; @@ -21,11 +21,10 @@ import org.springframework.stereotype.Service;
21 import org.thingsboard.server.common.data.Tenant; 21 import org.thingsboard.server.common.data.Tenant;
22 import org.thingsboard.server.common.data.TenantProfile; 22 import org.thingsboard.server.common.data.TenantProfile;
23 import org.thingsboard.server.common.data.id.TenantId; 23 import org.thingsboard.server.common.data.id.TenantId;
24 -import org.thingsboard.server.dao.tenant.TenantProfileService;  
25 import org.thingsboard.server.dao.tenant.TenantService; 24 import org.thingsboard.server.dao.tenant.TenantService;
26 import org.thingsboard.server.queue.discovery.TenantRoutingInfo; 25 import org.thingsboard.server.queue.discovery.TenantRoutingInfo;
27 import org.thingsboard.server.queue.discovery.TenantRoutingInfoService; 26 import org.thingsboard.server.queue.discovery.TenantRoutingInfoService;
28 -import org.thingsboard.server.service.profile.TbTenantProfileCache; 27 +import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
29 28
30 @Slf4j 29 @Slf4j
31 @Service 30 @Service
@@ -38,7 +38,7 @@ import org.thingsboard.server.queue.discovery.PartitionChangeEvent; @@ -38,7 +38,7 @@ import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
38 import org.thingsboard.server.common.transport.util.DataDecodingEncodingService; 38 import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
39 import org.thingsboard.server.service.apiusage.TbApiUsageStateService; 39 import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
40 import org.thingsboard.server.service.profile.TbDeviceProfileCache; 40 import org.thingsboard.server.service.profile.TbDeviceProfileCache;
41 -import org.thingsboard.server.service.profile.TbTenantProfileCache; 41 +import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
42 import org.thingsboard.server.service.queue.TbPackCallback; 42 import org.thingsboard.server.service.queue.TbPackCallback;
43 import org.thingsboard.server.service.queue.TbPackProcessingContext; 43 import org.thingsboard.server.service.queue.TbPackProcessingContext;
44 44
@@ -71,7 +71,7 @@ import org.thingsboard.server.dao.device.provision.ProvisionFailedException; @@ -71,7 +71,7 @@ import org.thingsboard.server.dao.device.provision.ProvisionFailedException;
71 import org.thingsboard.server.service.apiusage.TbApiUsageStateService; 71 import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
72 import org.thingsboard.server.service.executors.DbCallbackExecutorService; 72 import org.thingsboard.server.service.executors.DbCallbackExecutorService;
73 import org.thingsboard.server.service.profile.TbDeviceProfileCache; 73 import org.thingsboard.server.service.profile.TbDeviceProfileCache;
74 -import org.thingsboard.server.service.profile.TbTenantProfileCache; 74 +import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
75 import org.thingsboard.server.service.queue.TbClusterService; 75 import org.thingsboard.server.service.queue.TbClusterService;
76 import org.thingsboard.server.service.state.DeviceStateService; 76 import org.thingsboard.server.service.state.DeviceStateService;
77 77
common/dao-api/src/main/java/org/thingsboard/server/dao/tenant/TbTenantProfileCache.java renamed from application/src/main/java/org/thingsboard/server/service/profile/TbTenantProfileCache.java
@@ -13,7 +13,7 @@ @@ -13,7 +13,7 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.service.profile; 16 +package org.thingsboard.server.dao.tenant;
17 17
18 import org.thingsboard.server.common.data.TenantProfile; 18 import org.thingsboard.server.common.data.TenantProfile;
19 import org.thingsboard.server.common.data.id.TenantId; 19 import org.thingsboard.server.common.data.id.TenantId;
@@ -87,8 +87,9 @@ public class TbAwsSqsProducerTemplate<T extends TbQueueMsg> implements TbQueuePr @@ -87,8 +87,9 @@ public class TbAwsSqsProducerTemplate<T extends TbQueueMsg> implements TbQueuePr
87 sendMsgRequest.withQueueUrl(getQueueUrl(tpi.getFullTopicName())); 87 sendMsgRequest.withQueueUrl(getQueueUrl(tpi.getFullTopicName()));
88 sendMsgRequest.withMessageBody(gson.toJson(new DefaultTbQueueMsg(msg))); 88 sendMsgRequest.withMessageBody(gson.toJson(new DefaultTbQueueMsg(msg)));
89 89
90 - sendMsgRequest.withMessageGroupId(tpi.getTopic());  
91 - sendMsgRequest.withMessageDeduplicationId(UUID.randomUUID().toString()); 90 + String sqsMsgId = UUID.randomUUID().toString();
  91 + sendMsgRequest.withMessageGroupId(sqsMsgId);
  92 + sendMsgRequest.withMessageDeduplicationId(sqsMsgId);
92 93
93 ListenableFuture<SendMessageResult> future = producerExecutor.submit(() -> sqsClient.sendMessage(sendMsgRequest)); 94 ListenableFuture<SendMessageResult> future = producerExecutor.submit(() -> sqsClient.sendMessage(sendMsgRequest));
94 95
@@ -122,7 +122,13 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -122,7 +122,13 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
122 log.trace("[{}] Processing msg: {}", sessionId, msg); 122 log.trace("[{}] Processing msg: {}", sessionId, msg);
123 try { 123 try {
124 if (msg instanceof MqttMessage) { 124 if (msg instanceof MqttMessage) {
125 - processMqttMsg(ctx, (MqttMessage) msg); 125 + MqttMessage message = (MqttMessage) msg;
  126 + if (message.decoderResult().isSuccess()) {
  127 + processMqttMsg(ctx, message);
  128 + } else {
  129 + log.error("[{}] Message processing failed: {}", sessionId, message.decoderResult().cause().getMessage());
  130 + ctx.close();
  131 + }
126 } else { 132 } else {
127 ctx.close(); 133 ctx.close();
128 } 134 }
@@ -166,4 +166,6 @@ public interface AssetDao extends Dao<Asset> { @@ -166,4 +166,6 @@ public interface AssetDao extends Dao<Asset> {
166 */ 166 */
167 ListenableFuture<List<EntitySubtype>> findTenantAssetTypesAsync(UUID tenantId); 167 ListenableFuture<List<EntitySubtype>> findTenantAssetTypesAsync(UUID tenantId);
168 168
  169 + Long countAssetsByTenantId(TenantId tenantId);
  170 +
169 } 171 }
@@ -26,6 +26,7 @@ import org.springframework.cache.Cache; @@ -26,6 +26,7 @@ import org.springframework.cache.Cache;
26 import org.springframework.cache.CacheManager; 26 import org.springframework.cache.CacheManager;
27 import org.springframework.cache.annotation.CacheEvict; 27 import org.springframework.cache.annotation.CacheEvict;
28 import org.springframework.cache.annotation.Cacheable; 28 import org.springframework.cache.annotation.Cacheable;
  29 +import org.springframework.context.annotation.Lazy;
29 import org.springframework.stereotype.Service; 30 import org.springframework.stereotype.Service;
30 import org.springframework.util.StringUtils; 31 import org.springframework.util.StringUtils;
31 import org.thingsboard.server.common.data.Customer; 32 import org.thingsboard.server.common.data.Customer;
@@ -44,12 +45,14 @@ import org.thingsboard.server.common.data.page.PageData; @@ -44,12 +45,14 @@ import org.thingsboard.server.common.data.page.PageData;
44 import org.thingsboard.server.common.data.page.PageLink; 45 import org.thingsboard.server.common.data.page.PageLink;
45 import org.thingsboard.server.common.data.relation.EntityRelation; 46 import org.thingsboard.server.common.data.relation.EntityRelation;
46 import org.thingsboard.server.common.data.relation.EntitySearchDirection; 47 import org.thingsboard.server.common.data.relation.EntitySearchDirection;
  48 +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
47 import org.thingsboard.server.dao.customer.CustomerDao; 49 import org.thingsboard.server.dao.customer.CustomerDao;
48 import org.thingsboard.server.dao.entity.AbstractEntityService; 50 import org.thingsboard.server.dao.entity.AbstractEntityService;
49 import org.thingsboard.server.dao.entityview.EntityViewService; 51 import org.thingsboard.server.dao.entityview.EntityViewService;
50 import org.thingsboard.server.dao.exception.DataValidationException; 52 import org.thingsboard.server.dao.exception.DataValidationException;
51 import org.thingsboard.server.dao.service.DataValidator; 53 import org.thingsboard.server.dao.service.DataValidator;
52 import org.thingsboard.server.dao.service.PaginatedRemover; 54 import org.thingsboard.server.dao.service.PaginatedRemover;
  55 +import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
53 import org.thingsboard.server.dao.tenant.TenantDao; 56 import org.thingsboard.server.dao.tenant.TenantDao;
54 57
55 import java.util.ArrayList; 58 import java.util.ArrayList;
@@ -90,6 +93,10 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ @@ -90,6 +93,10 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
90 @Autowired 93 @Autowired
91 private CacheManager cacheManager; 94 private CacheManager cacheManager;
92 95
  96 + @Autowired
  97 + @Lazy
  98 + private TbTenantProfileCache tenantProfileCache;
  99 +
93 @Override 100 @Override
94 public AssetInfo findAssetInfoById(TenantId tenantId, AssetId assetId) { 101 public AssetInfo findAssetInfoById(TenantId tenantId, AssetId assetId) {
95 log.trace("Executing findAssetInfoById [{}]", assetId); 102 log.trace("Executing findAssetInfoById [{}]", assetId);
@@ -320,6 +327,15 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ @@ -320,6 +327,15 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
320 327
321 @Override 328 @Override
322 protected void validateCreate(TenantId tenantId, Asset asset) { 329 protected void validateCreate(TenantId tenantId, Asset asset) {
  330 + DefaultTenantProfileConfiguration profileConfiguration =
  331 + (DefaultTenantProfileConfiguration)tenantProfileCache.get(tenantId).getProfileData().getConfiguration();
  332 + long maxAssets = profileConfiguration.getMaxAssets();
  333 + if (maxAssets > 0) {
  334 + long currentAssetsCount = assetDao.countAssetsByTenantId(tenantId);
  335 + if (maxAssets >= currentAssetsCount) {
  336 + throw new DataValidationException("Can't create assets more then " + maxAssets);
  337 + }
  338 + }
323 } 339 }
324 340
325 @Override 341 @Override
@@ -203,6 +203,8 @@ public interface DeviceDao extends Dao<Device> { @@ -203,6 +203,8 @@ public interface DeviceDao extends Dao<Device> {
203 */ 203 */
204 ListenableFuture<Device> findDeviceByTenantIdAndIdAsync(TenantId tenantId, UUID id); 204 ListenableFuture<Device> findDeviceByTenantIdAndIdAsync(TenantId tenantId, UUID id);
205 205
  206 + Long countDevicesByTenantId(TenantId tenantId);
  207 +
206 Long countDevicesByDeviceProfileId(TenantId tenantId, UUID deviceProfileId); 208 Long countDevicesByDeviceProfileId(TenantId tenantId, UUID deviceProfileId);
207 209
208 /** 210 /**
@@ -27,6 +27,7 @@ import org.springframework.cache.Cache; @@ -27,6 +27,7 @@ import org.springframework.cache.Cache;
27 import org.springframework.cache.CacheManager; 27 import org.springframework.cache.CacheManager;
28 import org.springframework.cache.annotation.CacheEvict; 28 import org.springframework.cache.annotation.CacheEvict;
29 import org.springframework.cache.annotation.Cacheable; 29 import org.springframework.cache.annotation.Cacheable;
  30 +import org.springframework.context.annotation.Lazy;
30 import org.springframework.stereotype.Service; 31 import org.springframework.stereotype.Service;
31 import org.springframework.transaction.annotation.Transactional; 32 import org.springframework.transaction.annotation.Transactional;
32 import org.springframework.util.CollectionUtils; 33 import org.springframework.util.CollectionUtils;
@@ -57,6 +58,7 @@ import org.thingsboard.server.common.data.relation.EntityRelation; @@ -57,6 +58,7 @@ import org.thingsboard.server.common.data.relation.EntityRelation;
57 import org.thingsboard.server.common.data.relation.EntitySearchDirection; 58 import org.thingsboard.server.common.data.relation.EntitySearchDirection;
58 import org.thingsboard.server.common.data.security.DeviceCredentials; 59 import org.thingsboard.server.common.data.security.DeviceCredentials;
59 import org.thingsboard.server.common.data.security.DeviceCredentialsType; 60 import org.thingsboard.server.common.data.security.DeviceCredentialsType;
  61 +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
60 import org.thingsboard.server.dao.customer.CustomerDao; 62 import org.thingsboard.server.dao.customer.CustomerDao;
61 import org.thingsboard.server.dao.device.provision.ProvisionFailedException; 63 import org.thingsboard.server.dao.device.provision.ProvisionFailedException;
62 import org.thingsboard.server.dao.device.provision.ProvisionRequest; 64 import org.thingsboard.server.dao.device.provision.ProvisionRequest;
@@ -67,6 +69,7 @@ import org.thingsboard.server.dao.event.EventService; @@ -67,6 +69,7 @@ import org.thingsboard.server.dao.event.EventService;
67 import org.thingsboard.server.dao.exception.DataValidationException; 69 import org.thingsboard.server.dao.exception.DataValidationException;
68 import org.thingsboard.server.dao.service.DataValidator; 70 import org.thingsboard.server.dao.service.DataValidator;
69 import org.thingsboard.server.dao.service.PaginatedRemover; 71 import org.thingsboard.server.dao.service.PaginatedRemover;
  72 +import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
70 import org.thingsboard.server.dao.tenant.TenantDao; 73 import org.thingsboard.server.dao.tenant.TenantDao;
71 import org.thingsboard.server.dao.util.mapping.JacksonUtil; 74 import org.thingsboard.server.dao.util.mapping.JacksonUtil;
72 75
@@ -120,6 +123,10 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe @@ -120,6 +123,10 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
120 @Autowired 123 @Autowired
121 private EventService eventService; 124 private EventService eventService;
122 125
  126 + @Autowired
  127 + @Lazy
  128 + private TbTenantProfileCache tenantProfileCache;
  129 +
123 @Override 130 @Override
124 public DeviceInfo findDeviceInfoById(TenantId tenantId, DeviceId deviceId) { 131 public DeviceInfo findDeviceInfoById(TenantId tenantId, DeviceId deviceId) {
125 log.trace("Executing findDeviceInfoById [{}]", deviceId); 132 log.trace("Executing findDeviceInfoById [{}]", deviceId);
@@ -520,6 +527,15 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe @@ -520,6 +527,15 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
520 527
521 @Override 528 @Override
522 protected void validateCreate(TenantId tenantId, Device device) { 529 protected void validateCreate(TenantId tenantId, Device device) {
  530 + DefaultTenantProfileConfiguration profileConfiguration =
  531 + (DefaultTenantProfileConfiguration)tenantProfileCache.get(tenantId).getProfileData().getConfiguration();
  532 + long maxDevices = profileConfiguration.getMaxDevices();
  533 + if (maxDevices > 0) {
  534 + long currentDevicesCount = deviceDao.countDevicesByTenantId(tenantId);
  535 + if (maxDevices >= currentDevicesCount) {
  536 + throw new DataValidationException("Can't create devices more then " + maxDevices);
  537 + }
  538 + }
523 } 539 }
524 540
525 @Override 541 @Override
@@ -532,7 +548,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe @@ -532,7 +548,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
532 548
533 @Override 549 @Override
534 protected void validateDataImpl(TenantId tenantId, Device device) { 550 protected void validateDataImpl(TenantId tenantId, Device device) {
535 - if (StringUtils.isEmpty(device.getName())) { 551 + if (StringUtils.isEmpty(device.getName()) || device.getName().trim().length() == 0) {
536 throw new DataValidationException("Device name should be specified!"); 552 throw new DataValidationException("Device name should be specified!");
537 } 553 }
538 if (device.getTenantId() == null) { 554 if (device.getTenantId() == null) {
@@ -122,4 +122,5 @@ public interface AssetRepository extends PagingAndSortingRepository<AssetEntity, @@ -122,4 +122,5 @@ public interface AssetRepository extends PagingAndSortingRepository<AssetEntity,
122 @Query("SELECT DISTINCT a.type FROM AssetEntity a WHERE a.tenantId = :tenantId") 122 @Query("SELECT DISTINCT a.type FROM AssetEntity a WHERE a.tenantId = :tenantId")
123 List<String> findTenantAssetTypes(@Param("tenantId") UUID tenantId); 123 List<String> findTenantAssetTypes(@Param("tenantId") UUID tenantId);
124 124
  125 + Long countByTenantId(UUID tenantId);
125 } 126 }
@@ -176,4 +176,10 @@ public class JpaAssetDao extends JpaAbstractSearchTextDao<AssetEntity, Asset> im @@ -176,4 +176,10 @@ public class JpaAssetDao extends JpaAbstractSearchTextDao<AssetEntity, Asset> im
176 } 176 }
177 return list; 177 return list;
178 } 178 }
  179 +
  180 + @Override
  181 + public Long countAssetsByTenantId(TenantId tenantId) {
  182 + return assetRepository.countByTenantId(tenantId.getId());
  183 +
  184 + }
179 } 185 }
@@ -168,4 +168,6 @@ public interface DeviceRepository extends PagingAndSortingRepository<DeviceEntit @@ -168,4 +168,6 @@ public interface DeviceRepository extends PagingAndSortingRepository<DeviceEntit
168 DeviceEntity findByTenantIdAndId(UUID tenantId, UUID id); 168 DeviceEntity findByTenantIdAndId(UUID tenantId, UUID id);
169 169
170 Long countByDeviceProfileId(UUID deviceProfileId); 170 Long countByDeviceProfileId(UUID deviceProfileId);
  171 +
  172 + Long countByTenantId(UUID tenantId);
171 } 173 }
@@ -219,6 +219,11 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao<DeviceEntity, Device> @@ -219,6 +219,11 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao<DeviceEntity, Device>
219 return deviceRepository.countByDeviceProfileId(deviceProfileId); 219 return deviceRepository.countByDeviceProfileId(deviceProfileId);
220 } 220 }
221 221
  222 + @Override
  223 + public Long countDevicesByTenantId(TenantId tenantId) {
  224 + return deviceRepository.countByTenantId(tenantId.getId());
  225 + }
  226 +
222 private List<EntitySubtype> convertTenantDeviceTypesToDto(UUID tenantId, List<String> types) { 227 private List<EntitySubtype> convertTenantDeviceTypesToDto(UUID tenantId, List<String> types) {
223 List<EntitySubtype> list = Collections.emptyList(); 228 List<EntitySubtype> list = Collections.emptyList();
224 if (types != null && !types.isEmpty()) { 229 if (types != null && !types.isEmpty()) {
dao/src/main/java/org/thingsboard/server/dao/tenant/DefaultTbTenantProfileCache.java renamed from application/src/main/java/org/thingsboard/server/service/profile/DefaultTbTenantProfileCache.java
@@ -13,20 +13,15 @@ @@ -13,20 +13,15 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.service.profile; 16 +package org.thingsboard.server.dao.tenant;
17 17
18 import lombok.extern.slf4j.Slf4j; 18 import lombok.extern.slf4j.Slf4j;
19 import org.springframework.stereotype.Service; 19 import org.springframework.stereotype.Service;
20 -import org.thingsboard.server.common.data.Device;  
21 -import org.thingsboard.server.common.data.DeviceProfile;  
22 import org.thingsboard.server.common.data.Tenant; 20 import org.thingsboard.server.common.data.Tenant;
23 import org.thingsboard.server.common.data.TenantProfile; 21 import org.thingsboard.server.common.data.TenantProfile;
24 -import org.thingsboard.server.common.data.id.DeviceId;  
25 -import org.thingsboard.server.common.data.id.DeviceProfileId;  
26 import org.thingsboard.server.common.data.id.TenantId; 22 import org.thingsboard.server.common.data.id.TenantId;
27 import org.thingsboard.server.common.data.id.TenantProfileId; 23 import org.thingsboard.server.common.data.id.TenantProfileId;
28 -import org.thingsboard.server.dao.device.DeviceProfileService;  
29 -import org.thingsboard.server.dao.device.DeviceService; 24 +import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
30 import org.thingsboard.server.dao.tenant.TenantProfileService; 25 import org.thingsboard.server.dao.tenant.TenantProfileService;
31 import org.thingsboard.server.dao.tenant.TenantService; 26 import org.thingsboard.server.dao.tenant.TenantService;
32 27
1 TB_QUEUE_TYPE=kafka 1 TB_QUEUE_TYPE=kafka
2 TB_KAFKA_SERVERS=kafka:9092 2 TB_KAFKA_SERVERS=kafka:9092
3 -TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES=retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100  
@@ -52,11 +52,13 @@ function AwsSqsProducer() { @@ -52,11 +52,13 @@ function AwsSqsProducer() {
52 queueUrls.set(responseTopic, responseQueueUrl); 52 queueUrls.set(responseTopic, responseQueueUrl);
53 } 53 }
54 54
  55 + let msgId = uuid();
  56 +
55 let params = { 57 let params = {
56 MessageBody: msgBody, 58 MessageBody: msgBody,
57 QueueUrl: responseQueueUrl, 59 QueueUrl: responseQueueUrl,
58 - MessageGroupId: 'js_eval',  
59 - MessageDeduplicationId: uuid() 60 + MessageGroupId: msgId,
  61 + MessageDeduplicationId: msgId
60 }; 62 };
61 63
62 return new Promise((resolve, reject) => { 64 return new Promise((resolve, reject) => {
@@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
16 package org.thingsboard.rule.engine.profile; 16 package org.thingsboard.rule.engine.profile;
17 17
18 import com.fasterxml.jackson.databind.JsonNode; 18 import com.fasterxml.jackson.databind.JsonNode;
  19 +import com.fasterxml.jackson.databind.node.ObjectNode;
19 import lombok.Data; 20 import lombok.Data;
20 import lombok.extern.slf4j.Slf4j; 21 import lombok.extern.slf4j.Slf4j;
21 import org.thingsboard.rule.engine.action.TbAlarmResult; 22 import org.thingsboard.rule.engine.action.TbAlarmResult;
@@ -29,6 +30,7 @@ import org.thingsboard.server.common.data.alarm.AlarmStatus; @@ -29,6 +30,7 @@ import org.thingsboard.server.common.data.alarm.AlarmStatus;
29 import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; 30 import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm;
30 import org.thingsboard.server.common.data.id.EntityId; 31 import org.thingsboard.server.common.data.id.EntityId;
31 import org.thingsboard.server.common.data.query.EntityKeyType; 32 import org.thingsboard.server.common.data.query.EntityKeyType;
  33 +import org.thingsboard.server.common.data.query.KeyFilter;
32 import org.thingsboard.server.common.msg.TbMsg; 34 import org.thingsboard.server.common.msg.TbMsg;
33 import org.thingsboard.server.common.msg.TbMsgMetaData; 35 import org.thingsboard.server.common.msg.TbMsgMetaData;
34 import org.thingsboard.server.common.msg.queue.ServiceQueue; 36 import org.thingsboard.server.common.msg.queue.ServiceQueue;
@@ -53,6 +55,7 @@ class AlarmState { @@ -53,6 +55,7 @@ class AlarmState {
53 private volatile boolean initialFetchDone; 55 private volatile boolean initialFetchDone;
54 private volatile TbMsgMetaData lastMsgMetaData; 56 private volatile TbMsgMetaData lastMsgMetaData;
55 private volatile String lastMsgQueueName; 57 private volatile String lastMsgQueueName;
  58 + private volatile DataSnapshot dataSnapshot;
56 59
57 AlarmState(ProfileState deviceProfile, EntityId originator, DeviceProfileAlarm alarmDefinition, PersistedAlarmState alarmState) { 60 AlarmState(ProfileState deviceProfile, EntityId originator, DeviceProfileAlarm alarmDefinition, PersistedAlarmState alarmState) {
58 this.deviceProfile = deviceProfile; 61 this.deviceProfile = deviceProfile;
@@ -74,7 +77,7 @@ class AlarmState { @@ -74,7 +77,7 @@ class AlarmState {
74 77
75 public <T> boolean createOrClearAlarms(TbContext ctx, T data, SnapshotUpdate update, BiFunction<AlarmRuleState, T, AlarmEvalResult> evalFunction) { 78 public <T> boolean createOrClearAlarms(TbContext ctx, T data, SnapshotUpdate update, BiFunction<AlarmRuleState, T, AlarmEvalResult> evalFunction) {
76 boolean stateUpdate = false; 79 boolean stateUpdate = false;
77 - AlarmSeverity resultSeverity = null; 80 + AlarmRuleState resultState = null;
78 log.debug("[{}] processing update: {}", alarmDefinition.getId(), data); 81 log.debug("[{}] processing update: {}", alarmDefinition.getId(), data);
79 for (AlarmRuleState state : createRulesSortedBySeverityDesc) { 82 for (AlarmRuleState state : createRulesSortedBySeverityDesc) {
80 if (!validateUpdate(update, state)) { 83 if (!validateUpdate(update, state)) {
@@ -84,15 +87,15 @@ class AlarmState { @@ -84,15 +87,15 @@ class AlarmState {
84 AlarmEvalResult evalResult = evalFunction.apply(state, data); 87 AlarmEvalResult evalResult = evalFunction.apply(state, data);
85 stateUpdate |= state.checkUpdate(); 88 stateUpdate |= state.checkUpdate();
86 if (AlarmEvalResult.TRUE.equals(evalResult)) { 89 if (AlarmEvalResult.TRUE.equals(evalResult)) {
87 - resultSeverity = state.getSeverity(); 90 + resultState = state;
88 break; 91 break;
89 } else if (AlarmEvalResult.FALSE.equals(evalResult)) { 92 } else if (AlarmEvalResult.FALSE.equals(evalResult)) {
90 state.clear(); 93 state.clear();
91 stateUpdate |= state.checkUpdate(); 94 stateUpdate |= state.checkUpdate();
92 } 95 }
93 } 96 }
94 - if (resultSeverity != null) {  
95 - TbAlarmResult result = calculateAlarmResult(ctx, resultSeverity); 97 + if (resultState != null) {
  98 + TbAlarmResult result = calculateAlarmResult(ctx, resultState);
96 if (result != null) { 99 if (result != null) {
97 pushMsg(ctx, result); 100 pushMsg(ctx, result);
98 } 101 }
@@ -187,7 +190,8 @@ class AlarmState { @@ -187,7 +190,8 @@ class AlarmState {
187 } 190 }
188 } 191 }
189 192
190 - private TbAlarmResult calculateAlarmResult(TbContext ctx, AlarmSeverity severity) { 193 + private <T> TbAlarmResult calculateAlarmResult(TbContext ctx, AlarmRuleState ruleState) {
  194 + AlarmSeverity severity = ruleState.getSeverity();
191 if (currentAlarm != null) { 195 if (currentAlarm != null) {
192 // TODO: In some extremely rare cases, we might miss the event of alarm clear (If one use in-mem queue and restarted the server) or (if one manipulated the rule chain). 196 // TODO: In some extremely rare cases, we might miss the event of alarm clear (If one use in-mem queue and restarted the server) or (if one manipulated the rule chain).
193 // Maybe we should fetch alarm every time? 197 // Maybe we should fetch alarm every time?
@@ -213,7 +217,7 @@ class AlarmState { @@ -213,7 +217,7 @@ class AlarmState {
213 currentAlarm.setSeverity(severity); 217 currentAlarm.setSeverity(severity);
214 currentAlarm.setStartTs(System.currentTimeMillis()); 218 currentAlarm.setStartTs(System.currentTimeMillis());
215 currentAlarm.setEndTs(currentAlarm.getStartTs()); 219 currentAlarm.setEndTs(currentAlarm.getStartTs());
216 - currentAlarm.setDetails(JacksonUtil.OBJECT_MAPPER.createObjectNode()); 220 + currentAlarm.setDetails(createDetails(ruleState));
217 currentAlarm.setOriginator(originator); 221 currentAlarm.setOriginator(originator);
218 currentAlarm.setTenantId(ctx.getTenantId()); 222 currentAlarm.setTenantId(ctx.getTenantId());
219 currentAlarm.setPropagate(alarmDefinition.isPropagate()); 223 currentAlarm.setPropagate(alarmDefinition.isPropagate());
@@ -226,6 +230,44 @@ class AlarmState { @@ -226,6 +230,44 @@ class AlarmState {
226 } 230 }
227 } 231 }
228 232
  233 + private <T> JsonNode createDetails(AlarmRuleState ruleState) {
  234 + ObjectNode details = JacksonUtil.OBJECT_MAPPER.createObjectNode();
  235 + String alarmDetails = ruleState.getAlarmRule().getAlarmDetails();
  236 +
  237 + if (alarmDetails != null) {
  238 + for (KeyFilter keyFilter : ruleState.getAlarmRule().getCondition().getCondition()) {
  239 + EntityKeyValue entityKeyValue = dataSnapshot.getValue(keyFilter.getKey());
  240 + alarmDetails = alarmDetails.replaceAll(String.format("\\$\\{%s}", keyFilter.getKey().getKey()), getValueAsString(entityKeyValue));
  241 + }
  242 +
  243 + details.put("data", alarmDetails);
  244 + }
  245 +
  246 + return details;
  247 + }
  248 +
  249 + private static String getValueAsString(EntityKeyValue entityKeyValue) {
  250 + Object result = null;
  251 + switch (entityKeyValue.getDataType()) {
  252 + case STRING:
  253 + result = entityKeyValue.getStrValue();
  254 + break;
  255 + case JSON:
  256 + result = entityKeyValue.getJsonValue();
  257 + break;
  258 + case LONG:
  259 + result = entityKeyValue.getLngValue();
  260 + break;
  261 + case DOUBLE:
  262 + result = entityKeyValue.getDblValue();
  263 + break;
  264 + case BOOLEAN:
  265 + result = entityKeyValue.getBoolValue();
  266 + break;
  267 + }
  268 + return String.valueOf(result);
  269 + }
  270 +
229 public boolean processAlarmClear(TbContext ctx, Alarm alarmNf) { 271 public boolean processAlarmClear(TbContext ctx, Alarm alarmNf) {
230 boolean updated = false; 272 boolean updated = false;
231 if (currentAlarm != null && currentAlarm.getId().equals(alarmNf.getId())) { 273 if (currentAlarm != null && currentAlarm.getId().equals(alarmNf.getId())) {