Commit 10e6b9a458d7d49596f3fc89ead24bbec6f3c74b

Authored by Andrii Shvaika
2 parents a87a3d68 c1737d0d

Merge branch 'master' of github.com:thingsboard/thingsboard

Showing 42 changed files with 292 additions and 78 deletions
@@ -146,10 +146,6 @@ @@ -146,10 +146,6 @@
146 <artifactId>spring-boot-starter-websocket</artifactId> 146 <artifactId>spring-boot-starter-websocket</artifactId>
147 </dependency> 147 </dependency>
148 <dependency> 148 <dependency>
149 - <groupId>org.springframework.cloud</groupId>  
150 - <artifactId>spring-cloud-starter-oauth2</artifactId>  
151 - </dependency>  
152 - <dependency>  
153 <groupId>org.springframework.security</groupId> 149 <groupId>org.springframework.security</groupId>
154 <artifactId>spring-security-oauth2-client</artifactId> 150 <artifactId>spring-security-oauth2-client</artifactId>
155 </dependency> 151 </dependency>
@@ -24,6 +24,10 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura @@ -24,6 +24,10 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura
24 24
25 private long maxDevices; 25 private long maxDevices;
26 private long maxAssets; 26 private long maxAssets;
  27 + private long maxCustomers;
  28 + private long maxUsers;
  29 + private long maxDashboards;
  30 + private long maxRuleChains;
27 31
28 private String transportTenantMsgRateLimit; 32 private String transportTenantMsgRateLimit;
29 private String transportTenantTelemetryMsgRateLimit; 33 private String transportTenantTelemetryMsgRateLimit;
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao;
  17 +
  18 +import org.thingsboard.server.common.data.id.TenantId;
  19 +
  20 +public interface TenantEntityDao {
  21 +
  22 + Long countByTenantId(TenantId tenantId);
  23 +}
@@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.id.TenantId; @@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.id.TenantId;
23 import org.thingsboard.server.common.data.page.PageData; 23 import org.thingsboard.server.common.data.page.PageData;
24 import org.thingsboard.server.common.data.page.PageLink; 24 import org.thingsboard.server.common.data.page.PageLink;
25 import org.thingsboard.server.dao.Dao; 25 import org.thingsboard.server.dao.Dao;
  26 +import org.thingsboard.server.dao.TenantEntityDao;
26 27
27 import java.util.List; 28 import java.util.List;
28 import java.util.Optional; 29 import java.util.Optional;
@@ -32,7 +33,7 @@ import java.util.UUID; @@ -32,7 +33,7 @@ import java.util.UUID;
32 * The Interface AssetDao. 33 * The Interface AssetDao.
33 * 34 *
34 */ 35 */
35 -public interface AssetDao extends Dao<Asset> { 36 +public interface AssetDao extends Dao<Asset>, TenantEntityDao {
36 37
37 /** 38 /**
38 * Find asset info by id. 39 * Find asset info by id.
@@ -166,6 +167,4 @@ public interface AssetDao extends Dao<Asset> { @@ -166,6 +167,4 @@ public interface AssetDao extends Dao<Asset> {
166 */ 167 */
167 ListenableFuture<List<EntitySubtype>> findTenantAssetTypesAsync(UUID tenantId); 168 ListenableFuture<List<EntitySubtype>> findTenantAssetTypesAsync(UUID tenantId);
168 169
169 - Long countAssetsByTenantId(TenantId tenantId);  
170 -  
171 } 170 }
@@ -330,12 +330,7 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ @@ -330,12 +330,7 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
330 DefaultTenantProfileConfiguration profileConfiguration = 330 DefaultTenantProfileConfiguration profileConfiguration =
331 (DefaultTenantProfileConfiguration)tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); 331 (DefaultTenantProfileConfiguration)tenantProfileCache.get(tenantId).getProfileData().getConfiguration();
332 long maxAssets = profileConfiguration.getMaxAssets(); 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 - } 333 + validateNumberOfEntitiesPerTenant(tenantId, assetDao, maxAssets, EntityType.ASSET);
339 } 334 }
340 335
341 @Override 336 @Override
@@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.id.TenantId; @@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.id.TenantId;
20 import org.thingsboard.server.common.data.page.PageData; 20 import org.thingsboard.server.common.data.page.PageData;
21 import org.thingsboard.server.common.data.page.PageLink; 21 import org.thingsboard.server.common.data.page.PageLink;
22 import org.thingsboard.server.dao.Dao; 22 import org.thingsboard.server.dao.Dao;
  23 +import org.thingsboard.server.dao.TenantEntityDao;
23 24
24 import java.util.Optional; 25 import java.util.Optional;
25 import java.util.UUID; 26 import java.util.UUID;
@@ -27,7 +28,7 @@ import java.util.UUID; @@ -27,7 +28,7 @@ import java.util.UUID;
27 /** 28 /**
28 * The Interface CustomerDao. 29 * The Interface CustomerDao.
29 */ 30 */
30 -public interface CustomerDao extends Dao<Customer> { 31 +public interface CustomerDao extends Dao<Customer>, TenantEntityDao {
31 32
32 /** 33 /**
33 * Save or update customer object 34 * Save or update customer object
@@ -54,5 +55,5 @@ public interface CustomerDao extends Dao<Customer> { @@ -54,5 +55,5 @@ public interface CustomerDao extends Dao<Customer> {
54 * @return the optional customer object 55 * @return the optional customer object
55 */ 56 */
56 Optional<Customer> findCustomersByTenantIdAndTitle(UUID tenantId, String title); 57 Optional<Customer> findCustomersByTenantIdAndTitle(UUID tenantId, String title);
57 - 58 +
58 } 59 }
@@ -21,13 +21,16 @@ import com.google.common.util.concurrent.ListenableFuture; @@ -21,13 +21,16 @@ import com.google.common.util.concurrent.ListenableFuture;
21 import lombok.extern.slf4j.Slf4j; 21 import lombok.extern.slf4j.Slf4j;
22 import org.apache.commons.lang3.StringUtils; 22 import org.apache.commons.lang3.StringUtils;
23 import org.springframework.beans.factory.annotation.Autowired; 23 import org.springframework.beans.factory.annotation.Autowired;
  24 +import org.springframework.context.annotation.Lazy;
24 import org.springframework.stereotype.Service; 25 import org.springframework.stereotype.Service;
25 import org.thingsboard.server.common.data.Customer; 26 import org.thingsboard.server.common.data.Customer;
  27 +import org.thingsboard.server.common.data.EntityType;
26 import org.thingsboard.server.common.data.Tenant; 28 import org.thingsboard.server.common.data.Tenant;
27 import org.thingsboard.server.common.data.id.CustomerId; 29 import org.thingsboard.server.common.data.id.CustomerId;
28 import org.thingsboard.server.common.data.id.TenantId; 30 import org.thingsboard.server.common.data.id.TenantId;
29 import org.thingsboard.server.common.data.page.PageData; 31 import org.thingsboard.server.common.data.page.PageData;
30 import org.thingsboard.server.common.data.page.PageLink; 32 import org.thingsboard.server.common.data.page.PageLink;
  33 +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
31 import org.thingsboard.server.dao.asset.AssetService; 34 import org.thingsboard.server.dao.asset.AssetService;
32 import org.thingsboard.server.dao.dashboard.DashboardService; 35 import org.thingsboard.server.dao.dashboard.DashboardService;
33 import org.thingsboard.server.dao.device.DeviceService; 36 import org.thingsboard.server.dao.device.DeviceService;
@@ -38,6 +41,7 @@ import org.thingsboard.server.dao.exception.IncorrectParameterException; @@ -38,6 +41,7 @@ import org.thingsboard.server.dao.exception.IncorrectParameterException;
38 import org.thingsboard.server.dao.service.DataValidator; 41 import org.thingsboard.server.dao.service.DataValidator;
39 import org.thingsboard.server.dao.service.PaginatedRemover; 42 import org.thingsboard.server.dao.service.PaginatedRemover;
40 import org.thingsboard.server.dao.service.Validator; 43 import org.thingsboard.server.dao.service.Validator;
  44 +import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
41 import org.thingsboard.server.dao.tenant.TenantDao; 45 import org.thingsboard.server.dao.tenant.TenantDao;
42 import org.thingsboard.server.dao.user.UserService; 46 import org.thingsboard.server.dao.user.UserService;
43 47
@@ -75,6 +79,10 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom @@ -75,6 +79,10 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom
75 @Autowired 79 @Autowired
76 private DashboardService dashboardService; 80 private DashboardService dashboardService;
77 81
  82 + @Autowired
  83 + @Lazy
  84 + private TbTenantProfileCache tenantProfileCache;
  85 +
78 @Override 86 @Override
79 public Customer findCustomerById(TenantId tenantId, CustomerId customerId) { 87 public Customer findCustomerById(TenantId tenantId, CustomerId customerId) {
80 log.trace("Executing findCustomerById [{}]", customerId); 88 log.trace("Executing findCustomerById [{}]", customerId);
@@ -162,6 +170,11 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom @@ -162,6 +170,11 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom
162 170
163 @Override 171 @Override
164 protected void validateCreate(TenantId tenantId, Customer customer) { 172 protected void validateCreate(TenantId tenantId, Customer customer) {
  173 + DefaultTenantProfileConfiguration profileConfiguration =
  174 + (DefaultTenantProfileConfiguration)tenantProfileCache.get(tenantId).getProfileData().getConfiguration();
  175 + long maxCustomers = profileConfiguration.getMaxCustomers();
  176 +
  177 + validateNumberOfEntitiesPerTenant(tenantId, customerDao, maxCustomers, EntityType.CUSTOMER);
165 customerDao.findCustomersByTenantIdAndTitle(customer.getTenantId().getId(), customer.getTitle()).ifPresent( 178 customerDao.findCustomersByTenantIdAndTitle(customer.getTenantId().getId(), customer.getTitle()).ifPresent(
166 c -> { 179 c -> {
167 throw new DataValidationException("Customer with such title already exists!"); 180 throw new DataValidationException("Customer with such title already exists!");
@@ -18,11 +18,12 @@ package org.thingsboard.server.dao.dashboard; @@ -18,11 +18,12 @@ package org.thingsboard.server.dao.dashboard;
18 import org.thingsboard.server.common.data.Dashboard; 18 import org.thingsboard.server.common.data.Dashboard;
19 import org.thingsboard.server.common.data.id.TenantId; 19 import org.thingsboard.server.common.data.id.TenantId;
20 import org.thingsboard.server.dao.Dao; 20 import org.thingsboard.server.dao.Dao;
  21 +import org.thingsboard.server.dao.TenantEntityDao;
21 22
22 /** 23 /**
23 * The Interface DashboardDao. 24 * The Interface DashboardDao.
24 */ 25 */
25 -public interface DashboardDao extends Dao<Dashboard> { 26 +public interface DashboardDao extends Dao<Dashboard>, TenantEntityDao {
26 27
27 /** 28 /**
28 * Save or update dashboard object 29 * Save or update dashboard object
@@ -31,5 +32,4 @@ public interface DashboardDao extends Dao<Dashboard> { @@ -31,5 +32,4 @@ public interface DashboardDao extends Dao<Dashboard> {
31 * @return saved dashboard object 32 * @return saved dashboard object
32 */ 33 */
33 Dashboard save(TenantId tenantId, Dashboard dashboard); 34 Dashboard save(TenantId tenantId, Dashboard dashboard);
34 -  
35 } 35 }
@@ -19,10 +19,12 @@ import com.google.common.util.concurrent.ListenableFuture; @@ -19,10 +19,12 @@ import com.google.common.util.concurrent.ListenableFuture;
19 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
20 import org.apache.commons.lang3.StringUtils; 20 import org.apache.commons.lang3.StringUtils;
21 import org.springframework.beans.factory.annotation.Autowired; 21 import org.springframework.beans.factory.annotation.Autowired;
  22 +import org.springframework.context.annotation.Lazy;
22 import org.springframework.stereotype.Service; 23 import org.springframework.stereotype.Service;
23 import org.thingsboard.server.common.data.Customer; 24 import org.thingsboard.server.common.data.Customer;
24 import org.thingsboard.server.common.data.Dashboard; 25 import org.thingsboard.server.common.data.Dashboard;
25 import org.thingsboard.server.common.data.DashboardInfo; 26 import org.thingsboard.server.common.data.DashboardInfo;
  27 +import org.thingsboard.server.common.data.EntityType;
26 import org.thingsboard.server.common.data.Tenant; 28 import org.thingsboard.server.common.data.Tenant;
27 import org.thingsboard.server.common.data.id.CustomerId; 29 import org.thingsboard.server.common.data.id.CustomerId;
28 import org.thingsboard.server.common.data.id.DashboardId; 30 import org.thingsboard.server.common.data.id.DashboardId;
@@ -31,12 +33,14 @@ import org.thingsboard.server.common.data.page.PageData; @@ -31,12 +33,14 @@ import org.thingsboard.server.common.data.page.PageData;
31 import org.thingsboard.server.common.data.page.PageLink; 33 import org.thingsboard.server.common.data.page.PageLink;
32 import org.thingsboard.server.common.data.relation.EntityRelation; 34 import org.thingsboard.server.common.data.relation.EntityRelation;
33 import org.thingsboard.server.common.data.relation.RelationTypeGroup; 35 import org.thingsboard.server.common.data.relation.RelationTypeGroup;
  36 +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
34 import org.thingsboard.server.dao.customer.CustomerDao; 37 import org.thingsboard.server.dao.customer.CustomerDao;
35 import org.thingsboard.server.dao.entity.AbstractEntityService; 38 import org.thingsboard.server.dao.entity.AbstractEntityService;
36 import org.thingsboard.server.dao.exception.DataValidationException; 39 import org.thingsboard.server.dao.exception.DataValidationException;
37 import org.thingsboard.server.dao.service.DataValidator; 40 import org.thingsboard.server.dao.service.DataValidator;
38 import org.thingsboard.server.dao.service.PaginatedRemover; 41 import org.thingsboard.server.dao.service.PaginatedRemover;
39 import org.thingsboard.server.dao.service.Validator; 42 import org.thingsboard.server.dao.service.Validator;
  43 +import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
40 import org.thingsboard.server.dao.tenant.TenantDao; 44 import org.thingsboard.server.dao.tenant.TenantDao;
41 45
42 import java.util.concurrent.ExecutionException; 46 import java.util.concurrent.ExecutionException;
@@ -61,6 +65,10 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb @@ -61,6 +65,10 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
61 @Autowired 65 @Autowired
62 private CustomerDao customerDao; 66 private CustomerDao customerDao;
63 67
  68 + @Autowired
  69 + @Lazy
  70 + private TbTenantProfileCache tenantProfileCache;
  71 +
64 @Override 72 @Override
65 public Dashboard findDashboardById(TenantId tenantId, DashboardId dashboardId) { 73 public Dashboard findDashboardById(TenantId tenantId, DashboardId dashboardId) {
66 log.trace("Executing findDashboardById [{}]", dashboardId); 74 log.trace("Executing findDashboardById [{}]", dashboardId);
@@ -215,6 +223,14 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb @@ -215,6 +223,14 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
215 private DataValidator<Dashboard> dashboardValidator = 223 private DataValidator<Dashboard> dashboardValidator =
216 new DataValidator<Dashboard>() { 224 new DataValidator<Dashboard>() {
217 @Override 225 @Override
  226 + protected void validateCreate(TenantId tenantId, Dashboard data) {
  227 + DefaultTenantProfileConfiguration profileConfiguration =
  228 + (DefaultTenantProfileConfiguration)tenantProfileCache.get(tenantId).getProfileData().getConfiguration();
  229 + long maxDashboards = profileConfiguration.getMaxDashboards();
  230 + validateNumberOfEntitiesPerTenant(tenantId, dashboardDao, maxDashboards, EntityType.DASHBOARD);
  231 + }
  232 +
  233 + @Override
218 protected void validateDataImpl(TenantId tenantId, Dashboard dashboard) { 234 protected void validateDataImpl(TenantId tenantId, Dashboard dashboard) {
219 if (StringUtils.isEmpty(dashboard.getTitle())) { 235 if (StringUtils.isEmpty(dashboard.getTitle())) {
220 throw new DataValidationException("Dashboard title should be specified!"); 236 throw new DataValidationException("Dashboard title should be specified!");
@@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.id.TenantId; @@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.id.TenantId;
23 import org.thingsboard.server.common.data.page.PageData; 23 import org.thingsboard.server.common.data.page.PageData;
24 import org.thingsboard.server.common.data.page.PageLink; 24 import org.thingsboard.server.common.data.page.PageLink;
25 import org.thingsboard.server.dao.Dao; 25 import org.thingsboard.server.dao.Dao;
  26 +import org.thingsboard.server.dao.TenantEntityDao;
26 27
27 import java.util.List; 28 import java.util.List;
28 import java.util.Optional; 29 import java.util.Optional;
@@ -32,7 +33,7 @@ import java.util.UUID; @@ -32,7 +33,7 @@ import java.util.UUID;
32 * The Interface DeviceDao. 33 * The Interface DeviceDao.
33 * 34 *
34 */ 35 */
35 -public interface DeviceDao extends Dao<Device> { 36 +public interface DeviceDao extends Dao<Device>, TenantEntityDao {
36 37
37 /** 38 /**
38 * Find device info by id. 39 * Find device info by id.
@@ -203,8 +204,6 @@ public interface DeviceDao extends Dao<Device> { @@ -203,8 +204,6 @@ public interface DeviceDao extends Dao<Device> {
203 */ 204 */
204 ListenableFuture<Device> findDeviceByTenantIdAndIdAsync(TenantId tenantId, UUID id); 205 ListenableFuture<Device> findDeviceByTenantIdAndIdAsync(TenantId tenantId, UUID id);
205 206
206 - Long countDevicesByTenantId(TenantId tenantId);  
207 -  
208 Long countDevicesByDeviceProfileId(TenantId tenantId, UUID deviceProfileId); 207 Long countDevicesByDeviceProfileId(TenantId tenantId, UUID deviceProfileId);
209 208
210 /** 209 /**
@@ -530,12 +530,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe @@ -530,12 +530,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
530 DefaultTenantProfileConfiguration profileConfiguration = 530 DefaultTenantProfileConfiguration profileConfiguration =
531 (DefaultTenantProfileConfiguration)tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); 531 (DefaultTenantProfileConfiguration)tenantProfileCache.get(tenantId).getProfileData().getConfiguration();
532 long maxDevices = profileConfiguration.getMaxDevices(); 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 - } 533 + validateNumberOfEntitiesPerTenant(tenantId, deviceDao, maxDevices, EntityType.DEVICE);
539 } 534 }
540 535
541 @Override 536 @Override
@@ -37,12 +37,11 @@ public abstract class AbstractEntityService { @@ -37,12 +37,11 @@ public abstract class AbstractEntityService {
37 37
38 protected Optional<ConstraintViolationException> extractConstraintViolationException(Exception t) { 38 protected Optional<ConstraintViolationException> extractConstraintViolationException(Exception t) {
39 if (t instanceof ConstraintViolationException) { 39 if (t instanceof ConstraintViolationException) {
40 - return Optional.of ((ConstraintViolationException) t); 40 + return Optional.of((ConstraintViolationException) t);
41 } else if (t.getCause() instanceof ConstraintViolationException) { 41 } else if (t.getCause() instanceof ConstraintViolationException) {
42 - return Optional.of ((ConstraintViolationException) (t.getCause())); 42 + return Optional.of((ConstraintViolationException) (t.getCause()));
43 } else { 43 } else {
44 return Optional.empty(); 44 return Optional.empty();
45 } 45 }
46 } 46 }
47 -  
48 } 47 }
@@ -24,6 +24,7 @@ import org.apache.commons.collections.CollectionUtils; @@ -24,6 +24,7 @@ import org.apache.commons.collections.CollectionUtils;
24 import org.apache.commons.lang3.StringUtils; 24 import org.apache.commons.lang3.StringUtils;
25 import org.hibernate.exception.ConstraintViolationException; 25 import org.hibernate.exception.ConstraintViolationException;
26 import org.springframework.beans.factory.annotation.Autowired; 26 import org.springframework.beans.factory.annotation.Autowired;
  27 +import org.springframework.context.annotation.Lazy;
27 import org.springframework.stereotype.Service; 28 import org.springframework.stereotype.Service;
28 import org.thingsboard.server.common.data.BaseData; 29 import org.thingsboard.server.common.data.BaseData;
29 import org.thingsboard.server.common.data.EntityType; 30 import org.thingsboard.server.common.data.EntityType;
@@ -44,11 +45,13 @@ import org.thingsboard.server.common.data.rule.RuleChainData; @@ -44,11 +45,13 @@ import org.thingsboard.server.common.data.rule.RuleChainData;
44 import org.thingsboard.server.common.data.rule.RuleChainImportResult; 45 import org.thingsboard.server.common.data.rule.RuleChainImportResult;
45 import org.thingsboard.server.common.data.rule.RuleChainMetaData; 46 import org.thingsboard.server.common.data.rule.RuleChainMetaData;
46 import org.thingsboard.server.common.data.rule.RuleNode; 47 import org.thingsboard.server.common.data.rule.RuleNode;
  48 +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
47 import org.thingsboard.server.dao.entity.AbstractEntityService; 49 import org.thingsboard.server.dao.entity.AbstractEntityService;
48 import org.thingsboard.server.dao.exception.DataValidationException; 50 import org.thingsboard.server.dao.exception.DataValidationException;
49 import org.thingsboard.server.dao.service.DataValidator; 51 import org.thingsboard.server.dao.service.DataValidator;
50 import org.thingsboard.server.dao.service.PaginatedRemover; 52 import org.thingsboard.server.dao.service.PaginatedRemover;
51 import org.thingsboard.server.dao.service.Validator; 53 import org.thingsboard.server.dao.service.Validator;
  54 +import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
52 import org.thingsboard.server.dao.tenant.TenantDao; 55 import org.thingsboard.server.dao.tenant.TenantDao;
53 56
54 import java.util.ArrayList; 57 import java.util.ArrayList;
@@ -81,6 +84,10 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC @@ -81,6 +84,10 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
81 @Autowired 84 @Autowired
82 private TenantDao tenantDao; 85 private TenantDao tenantDao;
83 86
  87 + @Autowired
  88 + @Lazy
  89 + private TbTenantProfileCache tenantProfileCache;
  90 +
84 @Override 91 @Override
85 public RuleChain saveRuleChain(RuleChain ruleChain) { 92 public RuleChain saveRuleChain(RuleChain ruleChain) {
86 ruleChainValidator.validate(ruleChain, RuleChain::getTenantId); 93 ruleChainValidator.validate(ruleChain, RuleChain::getTenantId);
@@ -581,6 +588,14 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC @@ -581,6 +588,14 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
581 private DataValidator<RuleChain> ruleChainValidator = 588 private DataValidator<RuleChain> ruleChainValidator =
582 new DataValidator<RuleChain>() { 589 new DataValidator<RuleChain>() {
583 @Override 590 @Override
  591 + protected void validateCreate(TenantId tenantId, RuleChain data) {
  592 + DefaultTenantProfileConfiguration profileConfiguration =
  593 + (DefaultTenantProfileConfiguration)tenantProfileCache.get(tenantId).getProfileData().getConfiguration();
  594 + long maxRuleChains = profileConfiguration.getMaxRuleChains();
  595 + validateNumberOfEntitiesPerTenant(tenantId, ruleChainDao, maxRuleChains, EntityType.RULE_CHAIN);
  596 + }
  597 +
  598 + @Override
584 protected void validateDataImpl(TenantId tenantId, RuleChain ruleChain) { 599 protected void validateDataImpl(TenantId tenantId, RuleChain ruleChain) {
585 if (StringUtils.isEmpty(ruleChain.getName())) { 600 if (StringUtils.isEmpty(ruleChain.getName())) {
586 throw new DataValidationException("Rule chain name should be specified!."); 601 throw new DataValidationException("Rule chain name should be specified!.");
@@ -19,13 +19,14 @@ import org.thingsboard.server.common.data.page.PageData; @@ -19,13 +19,14 @@ import org.thingsboard.server.common.data.page.PageData;
19 import org.thingsboard.server.common.data.page.PageLink; 19 import org.thingsboard.server.common.data.page.PageLink;
20 import org.thingsboard.server.common.data.rule.RuleChain; 20 import org.thingsboard.server.common.data.rule.RuleChain;
21 import org.thingsboard.server.dao.Dao; 21 import org.thingsboard.server.dao.Dao;
  22 +import org.thingsboard.server.dao.TenantEntityDao;
22 23
23 import java.util.UUID; 24 import java.util.UUID;
24 25
25 /** 26 /**
26 * Created by igor on 3/12/18. 27 * Created by igor on 3/12/18.
27 */ 28 */
28 -public interface RuleChainDao extends Dao<RuleChain> { 29 +public interface RuleChainDao extends Dao<RuleChain>, TenantEntityDao {
29 30
30 /** 31 /**
31 * Find rule chains by tenantId and page link. 32 * Find rule chains by tenantId and page link.
@@ -35,5 +36,4 @@ public interface RuleChainDao extends Dao<RuleChain> { @@ -35,5 +36,4 @@ public interface RuleChainDao extends Dao<RuleChain> {
35 * @return the list of rule chain objects 36 * @return the list of rule chain objects
36 */ 37 */
37 PageData<RuleChain> findRuleChainsByTenantId(UUID tenantId, PageLink pageLink); 38 PageData<RuleChain> findRuleChainsByTenantId(UUID tenantId, PageLink pageLink);
38 -  
39 } 39 }
@@ -18,7 +18,9 @@ package org.thingsboard.server.dao.service; @@ -18,7 +18,9 @@ package org.thingsboard.server.dao.service;
18 import com.fasterxml.jackson.databind.JsonNode; 18 import com.fasterxml.jackson.databind.JsonNode;
19 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
20 import org.thingsboard.server.common.data.BaseData; 20 import org.thingsboard.server.common.data.BaseData;
  21 +import org.thingsboard.server.common.data.EntityType;
21 import org.thingsboard.server.common.data.id.TenantId; 22 import org.thingsboard.server.common.data.id.TenantId;
  23 +import org.thingsboard.server.dao.TenantEntityDao;
22 import org.thingsboard.server.dao.exception.DataValidationException; 24 import org.thingsboard.server.dao.exception.DataValidationException;
23 25
24 import java.util.HashSet; 26 import java.util.HashSet;
@@ -79,6 +81,19 @@ public abstract class DataValidator<D extends BaseData<?>> { @@ -79,6 +81,19 @@ public abstract class DataValidator<D extends BaseData<?>> {
79 return emailMatcher.matches(); 81 return emailMatcher.matches();
80 } 82 }
81 83
  84 + protected void validateNumberOfEntitiesPerTenant(TenantId tenantId,
  85 + TenantEntityDao tenantEntityDao,
  86 + long maxEntities,
  87 + EntityType entityType) {
  88 + if (maxEntities > 0) {
  89 + long currentEntitiesCount = tenantEntityDao.countByTenantId(tenantId);
  90 + if (currentEntitiesCount >= maxEntities) {
  91 + throw new DataValidationException(String.format("Can't create more then %d %ss!",
  92 + maxEntities, entityType.name().toLowerCase().replaceAll("_", " ")));
  93 + }
  94 + }
  95 + }
  96 +
82 protected static void validateJsonStructure(JsonNode expectedNode, JsonNode actualNode) { 97 protected static void validateJsonStructure(JsonNode expectedNode, JsonNode actualNode) {
83 Set<String> expectedFields = new HashSet<>(); 98 Set<String> expectedFields = new HashSet<>();
84 Iterator<String> fieldsIterator = expectedNode.fieldNames(); 99 Iterator<String> fieldsIterator = expectedNode.fieldNames();
@@ -178,8 +178,7 @@ public class JpaAssetDao extends JpaAbstractSearchTextDao<AssetEntity, Asset> im @@ -178,8 +178,7 @@ public class JpaAssetDao extends JpaAbstractSearchTextDao<AssetEntity, Asset> im
178 } 178 }
179 179
180 @Override 180 @Override
181 - public Long countAssetsByTenantId(TenantId tenantId) { 181 + public Long countByTenantId(TenantId tenantId) {
182 return assetRepository.countByTenantId(tenantId.getId()); 182 return assetRepository.countByTenantId(tenantId.getId());
183 -  
184 } 183 }
185 } 184 }
@@ -37,4 +37,5 @@ public interface CustomerRepository extends PagingAndSortingRepository<CustomerE @@ -37,4 +37,5 @@ public interface CustomerRepository extends PagingAndSortingRepository<CustomerE
37 37
38 CustomerEntity findByTenantIdAndTitle(UUID tenantId, String title); 38 CustomerEntity findByTenantIdAndTitle(UUID tenantId, String title);
39 39
  40 + Long countByTenantId(UUID tenantId);
40 } 41 }
@@ -19,6 +19,7 @@ import org.springframework.beans.factory.annotation.Autowired; @@ -19,6 +19,7 @@ import org.springframework.beans.factory.annotation.Autowired;
19 import org.springframework.data.repository.CrudRepository; 19 import org.springframework.data.repository.CrudRepository;
20 import org.springframework.stereotype.Component; 20 import org.springframework.stereotype.Component;
21 import org.thingsboard.server.common.data.Customer; 21 import org.thingsboard.server.common.data.Customer;
  22 +import org.thingsboard.server.common.data.id.TenantId;
22 import org.thingsboard.server.common.data.page.PageData; 23 import org.thingsboard.server.common.data.page.PageData;
23 import org.thingsboard.server.common.data.page.PageLink; 24 import org.thingsboard.server.common.data.page.PageLink;
24 import org.thingsboard.server.dao.DaoUtil; 25 import org.thingsboard.server.dao.DaoUtil;
@@ -62,4 +63,9 @@ public class JpaCustomerDao extends JpaAbstractSearchTextDao<CustomerEntity, Cus @@ -62,4 +63,9 @@ public class JpaCustomerDao extends JpaAbstractSearchTextDao<CustomerEntity, Cus
62 Customer customer = DaoUtil.getData(customerRepository.findByTenantIdAndTitle(tenantId, title)); 63 Customer customer = DaoUtil.getData(customerRepository.findByTenantIdAndTitle(tenantId, title));
63 return Optional.ofNullable(customer); 64 return Optional.ofNullable(customer);
64 } 65 }
  66 +
  67 + @Override
  68 + public Long countByTenantId(TenantId tenantId) {
  69 + return customerRepository.countByTenantId(tenantId.getId());
  70 + }
65 } 71 }
@@ -24,4 +24,6 @@ import java.util.UUID; @@ -24,4 +24,6 @@ import java.util.UUID;
24 * Created by Valerii Sosliuk on 5/6/2017. 24 * Created by Valerii Sosliuk on 5/6/2017.
25 */ 25 */
26 public interface DashboardRepository extends CrudRepository<DashboardEntity, UUID> { 26 public interface DashboardRepository extends CrudRepository<DashboardEntity, UUID> {
  27 +
  28 + Long countByTenantId(UUID tenantId);
27 } 29 }
@@ -19,6 +19,7 @@ import org.springframework.beans.factory.annotation.Autowired; @@ -19,6 +19,7 @@ import org.springframework.beans.factory.annotation.Autowired;
19 import org.springframework.data.repository.CrudRepository; 19 import org.springframework.data.repository.CrudRepository;
20 import org.springframework.stereotype.Component; 20 import org.springframework.stereotype.Component;
21 import org.thingsboard.server.common.data.Dashboard; 21 import org.thingsboard.server.common.data.Dashboard;
  22 +import org.thingsboard.server.common.data.id.TenantId;
22 import org.thingsboard.server.dao.dashboard.DashboardDao; 23 import org.thingsboard.server.dao.dashboard.DashboardDao;
23 import org.thingsboard.server.dao.model.sql.DashboardEntity; 24 import org.thingsboard.server.dao.model.sql.DashboardEntity;
24 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; 25 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
@@ -43,4 +44,9 @@ public class JpaDashboardDao extends JpaAbstractSearchTextDao<DashboardEntity, D @@ -43,4 +44,9 @@ public class JpaDashboardDao extends JpaAbstractSearchTextDao<DashboardEntity, D
43 protected CrudRepository<DashboardEntity, UUID> getCrudRepository() { 44 protected CrudRepository<DashboardEntity, UUID> getCrudRepository() {
44 return dashboardRepository; 45 return dashboardRepository;
45 } 46 }
  47 +
  48 + @Override
  49 + public Long countByTenantId(TenantId tenantId) {
  50 + return dashboardRepository.countByTenantId(tenantId.getId());
  51 + }
46 } 52 }
@@ -50,9 +50,9 @@ public interface DeviceRepository extends PagingAndSortingRepository<DeviceEntit @@ -50,9 +50,9 @@ public interface DeviceRepository extends PagingAndSortingRepository<DeviceEntit
50 "AND d.deviceProfileId = :profileId " + 50 "AND d.deviceProfileId = :profileId " +
51 "AND LOWER(d.searchText) LIKE LOWER(CONCAT(:searchText, '%'))") 51 "AND LOWER(d.searchText) LIKE LOWER(CONCAT(:searchText, '%'))")
52 Page<DeviceEntity> findByTenantIdAndProfileId(@Param("tenantId") UUID tenantId, 52 Page<DeviceEntity> findByTenantIdAndProfileId(@Param("tenantId") UUID tenantId,
53 - @Param("profileId") UUID profileId,  
54 - @Param("searchText") String searchText,  
55 - Pageable pageable); 53 + @Param("profileId") UUID profileId,
  54 + @Param("searchText") String searchText,
  55 + Pageable pageable);
56 56
57 @Query("SELECT new org.thingsboard.server.dao.model.sql.DeviceInfoEntity(d, c.title, c.additionalInfo, p.name) " + 57 @Query("SELECT new org.thingsboard.server.dao.model.sql.DeviceInfoEntity(d, c.title, c.additionalInfo, p.name) " +
58 "FROM DeviceEntity d " + 58 "FROM DeviceEntity d " +
@@ -62,9 +62,9 @@ public interface DeviceRepository extends PagingAndSortingRepository<DeviceEntit @@ -62,9 +62,9 @@ public interface DeviceRepository extends PagingAndSortingRepository<DeviceEntit
62 "AND d.customerId = :customerId " + 62 "AND d.customerId = :customerId " +
63 "AND LOWER(d.searchText) LIKE LOWER(CONCAT(:searchText, '%'))") 63 "AND LOWER(d.searchText) LIKE LOWER(CONCAT(:searchText, '%'))")
64 Page<DeviceInfoEntity> findDeviceInfosByTenantIdAndCustomerId(@Param("tenantId") UUID tenantId, 64 Page<DeviceInfoEntity> findDeviceInfosByTenantIdAndCustomerId(@Param("tenantId") UUID tenantId,
65 - @Param("customerId") UUID customerId,  
66 - @Param("searchText") String searchText,  
67 - Pageable pageable); 65 + @Param("customerId") UUID customerId,
  66 + @Param("searchText") String searchText,
  67 + Pageable pageable);
68 68
69 @Query("SELECT d FROM DeviceEntity d WHERE d.tenantId = :tenantId") 69 @Query("SELECT d FROM DeviceEntity d WHERE d.tenantId = :tenantId")
70 Page<DeviceEntity> findByTenantId(@Param("tenantId") UUID tenantId, 70 Page<DeviceEntity> findByTenantId(@Param("tenantId") UUID tenantId,
@@ -102,9 +102,9 @@ public interface DeviceRepository extends PagingAndSortingRepository<DeviceEntit @@ -102,9 +102,9 @@ public interface DeviceRepository extends PagingAndSortingRepository<DeviceEntit
102 "AND d.type = :type " + 102 "AND d.type = :type " +
103 "AND LOWER(d.searchText) LIKE LOWER(CONCAT(:textSearch, '%'))") 103 "AND LOWER(d.searchText) LIKE LOWER(CONCAT(:textSearch, '%'))")
104 Page<DeviceInfoEntity> findDeviceInfosByTenantIdAndType(@Param("tenantId") UUID tenantId, 104 Page<DeviceInfoEntity> findDeviceInfosByTenantIdAndType(@Param("tenantId") UUID tenantId,
105 - @Param("type") String type,  
106 - @Param("textSearch") String textSearch,  
107 - Pageable pageable); 105 + @Param("type") String type,
  106 + @Param("textSearch") String textSearch,
  107 + Pageable pageable);
108 108
109 @Query("SELECT new org.thingsboard.server.dao.model.sql.DeviceInfoEntity(d, c.title, c.additionalInfo, p.name) " + 109 @Query("SELECT new org.thingsboard.server.dao.model.sql.DeviceInfoEntity(d, c.title, c.additionalInfo, p.name) " +
110 "FROM DeviceEntity d " + 110 "FROM DeviceEntity d " +
@@ -137,10 +137,10 @@ public interface DeviceRepository extends PagingAndSortingRepository<DeviceEntit @@ -137,10 +137,10 @@ public interface DeviceRepository extends PagingAndSortingRepository<DeviceEntit
137 "AND d.type = :type " + 137 "AND d.type = :type " +
138 "AND LOWER(d.searchText) LIKE LOWER(CONCAT(:textSearch, '%'))") 138 "AND LOWER(d.searchText) LIKE LOWER(CONCAT(:textSearch, '%'))")
139 Page<DeviceInfoEntity> findDeviceInfosByTenantIdAndCustomerIdAndType(@Param("tenantId") UUID tenantId, 139 Page<DeviceInfoEntity> findDeviceInfosByTenantIdAndCustomerIdAndType(@Param("tenantId") UUID tenantId,
140 - @Param("customerId") UUID customerId,  
141 - @Param("type") String type,  
142 - @Param("textSearch") String textSearch,  
143 - Pageable pageable); 140 + @Param("customerId") UUID customerId,
  141 + @Param("type") String type,
  142 + @Param("textSearch") String textSearch,
  143 + Pageable pageable);
144 144
145 @Query("SELECT new org.thingsboard.server.dao.model.sql.DeviceInfoEntity(d, c.title, c.additionalInfo, p.name) " + 145 @Query("SELECT new org.thingsboard.server.dao.model.sql.DeviceInfoEntity(d, c.title, c.additionalInfo, p.name) " +
146 "FROM DeviceEntity d " + 146 "FROM DeviceEntity d " +
@@ -220,7 +220,7 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao<DeviceEntity, Device> @@ -220,7 +220,7 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao<DeviceEntity, Device>
220 } 220 }
221 221
222 @Override 222 @Override
223 - public Long countDevicesByTenantId(TenantId tenantId) { 223 + public Long countByTenantId(TenantId tenantId) {
224 return deviceRepository.countByTenantId(tenantId.getId()); 224 return deviceRepository.countByTenantId(tenantId.getId());
225 } 225 }
226 226
@@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j; @@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j;
19 import org.springframework.beans.factory.annotation.Autowired; 19 import org.springframework.beans.factory.annotation.Autowired;
20 import org.springframework.data.repository.CrudRepository; 20 import org.springframework.data.repository.CrudRepository;
21 import org.springframework.stereotype.Component; 21 import org.springframework.stereotype.Component;
  22 +import org.thingsboard.server.common.data.id.TenantId;
22 import org.thingsboard.server.common.data.page.PageData; 23 import org.thingsboard.server.common.data.page.PageData;
23 import org.thingsboard.server.common.data.page.PageLink; 24 import org.thingsboard.server.common.data.page.PageLink;
24 import org.thingsboard.server.common.data.rule.RuleChain; 25 import org.thingsboard.server.common.data.rule.RuleChain;
@@ -56,4 +57,8 @@ public class JpaRuleChainDao extends JpaAbstractSearchTextDao<RuleChainEntity, R @@ -56,4 +57,8 @@ public class JpaRuleChainDao extends JpaAbstractSearchTextDao<RuleChainEntity, R
56 DaoUtil.toPageable(pageLink))); 57 DaoUtil.toPageable(pageLink)));
57 } 58 }
58 59
  60 + @Override
  61 + public Long countByTenantId(TenantId tenantId) {
  62 + return ruleChainRepository.countByTenantId(tenantId.getId());
  63 + }
59 } 64 }
@@ -32,4 +32,5 @@ public interface RuleChainRepository extends PagingAndSortingRepository<RuleChai @@ -32,4 +32,5 @@ public interface RuleChainRepository extends PagingAndSortingRepository<RuleChai
32 @Param("searchText") String searchText, 32 @Param("searchText") String searchText,
33 Pageable pageable); 33 Pageable pageable);
34 34
  35 + Long countByTenantId(UUID tenantId);
35 } 36 }
@@ -91,4 +91,9 @@ public class JpaUserDao extends JpaAbstractSearchTextDao<UserEntity, User> imple @@ -91,4 +91,9 @@ public class JpaUserDao extends JpaAbstractSearchTextDao<UserEntity, User> imple
91 DaoUtil.toPageable(pageLink))); 91 DaoUtil.toPageable(pageLink)));
92 92
93 } 93 }
  94 +
  95 + @Override
  96 + public Long countByTenantId(TenantId tenantId) {
  97 + return userRepository.countByTenantId(tenantId.getId());
  98 + }
94 } 99 }
@@ -47,4 +47,5 @@ public interface UserRepository extends PagingAndSortingRepository<UserEntity, U @@ -47,4 +47,5 @@ public interface UserRepository extends PagingAndSortingRepository<UserEntity, U
47 @Param("searchText") String searchText, 47 @Param("searchText") String searchText,
48 Pageable pageable); 48 Pageable pageable);
49 49
  50 + Long countByTenantId(UUID tenantId);
50 } 51 }
@@ -20,10 +20,11 @@ import org.thingsboard.server.common.data.id.TenantId; @@ -20,10 +20,11 @@ import org.thingsboard.server.common.data.id.TenantId;
20 import org.thingsboard.server.common.data.page.PageData; 20 import org.thingsboard.server.common.data.page.PageData;
21 import org.thingsboard.server.common.data.page.PageLink; 21 import org.thingsboard.server.common.data.page.PageLink;
22 import org.thingsboard.server.dao.Dao; 22 import org.thingsboard.server.dao.Dao;
  23 +import org.thingsboard.server.dao.TenantEntityDao;
23 24
24 import java.util.UUID; 25 import java.util.UUID;
25 26
26 -public interface UserDao extends Dao<User> { 27 +public interface UserDao extends Dao<User>, TenantEntityDao {
27 28
28 /** 29 /**
29 * Save or update user object 30 * Save or update user object
@@ -49,7 +50,7 @@ public interface UserDao extends Dao<User> { @@ -49,7 +50,7 @@ public interface UserDao extends Dao<User> {
49 * @return the list of user entities 50 * @return the list of user entities
50 */ 51 */
51 PageData<User> findByTenantId(UUID tenantId, PageLink pageLink); 52 PageData<User> findByTenantId(UUID tenantId, PageLink pageLink);
52 - 53 +
53 /** 54 /**
54 * Find tenant admin users by tenantId and page link. 55 * Find tenant admin users by tenantId and page link.
55 * 56 *
@@ -58,7 +59,7 @@ public interface UserDao extends Dao<User> { @@ -58,7 +59,7 @@ public interface UserDao extends Dao<User> {
58 * @return the list of user entities 59 * @return the list of user entities
59 */ 60 */
60 PageData<User> findTenantAdmins(UUID tenantId, PageLink pageLink); 61 PageData<User> findTenantAdmins(UUID tenantId, PageLink pageLink);
61 - 62 +
62 /** 63 /**
63 * Find customer users by tenantId, customerId and page link. 64 * Find customer users by tenantId, customerId and page link.
64 * 65 *
@@ -68,5 +69,4 @@ public interface UserDao extends Dao<User> { @@ -68,5 +69,4 @@ public interface UserDao extends Dao<User> {
68 * @return the list of user entities 69 * @return the list of user entities
69 */ 70 */
70 PageData<User> findCustomerUsers(UUID tenantId, UUID customerId, PageLink pageLink); 71 PageData<User> findCustomerUsers(UUID tenantId, UUID customerId, PageLink pageLink);
71 -  
72 } 72 }
@@ -24,8 +24,10 @@ import org.apache.commons.lang3.RandomStringUtils; @@ -24,8 +24,10 @@ import org.apache.commons.lang3.RandomStringUtils;
24 import org.apache.commons.lang3.StringUtils; 24 import org.apache.commons.lang3.StringUtils;
25 import org.springframework.beans.factory.annotation.Autowired; 25 import org.springframework.beans.factory.annotation.Autowired;
26 import org.springframework.beans.factory.annotation.Value; 26 import org.springframework.beans.factory.annotation.Value;
  27 +import org.springframework.context.annotation.Lazy;
27 import org.springframework.stereotype.Service; 28 import org.springframework.stereotype.Service;
28 import org.thingsboard.server.common.data.Customer; 29 import org.thingsboard.server.common.data.Customer;
  30 +import org.thingsboard.server.common.data.EntityType;
29 import org.thingsboard.server.common.data.Tenant; 31 import org.thingsboard.server.common.data.Tenant;
30 import org.thingsboard.server.common.data.User; 32 import org.thingsboard.server.common.data.User;
31 import org.thingsboard.server.common.data.id.CustomerId; 33 import org.thingsboard.server.common.data.id.CustomerId;
@@ -36,6 +38,7 @@ import org.thingsboard.server.common.data.page.PageData; @@ -36,6 +38,7 @@ import org.thingsboard.server.common.data.page.PageData;
36 import org.thingsboard.server.common.data.page.PageLink; 38 import org.thingsboard.server.common.data.page.PageLink;
37 import org.thingsboard.server.common.data.security.Authority; 39 import org.thingsboard.server.common.data.security.Authority;
38 import org.thingsboard.server.common.data.security.UserCredentials; 40 import org.thingsboard.server.common.data.security.UserCredentials;
  41 +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
39 import org.thingsboard.server.dao.customer.CustomerDao; 42 import org.thingsboard.server.dao.customer.CustomerDao;
40 import org.thingsboard.server.dao.entity.AbstractEntityService; 43 import org.thingsboard.server.dao.entity.AbstractEntityService;
41 import org.thingsboard.server.dao.exception.DataValidationException; 44 import org.thingsboard.server.dao.exception.DataValidationException;
@@ -43,6 +46,7 @@ import org.thingsboard.server.dao.exception.IncorrectParameterException; @@ -43,6 +46,7 @@ import org.thingsboard.server.dao.exception.IncorrectParameterException;
43 import org.thingsboard.server.dao.model.ModelConstants; 46 import org.thingsboard.server.dao.model.ModelConstants;
44 import org.thingsboard.server.dao.service.DataValidator; 47 import org.thingsboard.server.dao.service.DataValidator;
45 import org.thingsboard.server.dao.service.PaginatedRemover; 48 import org.thingsboard.server.dao.service.PaginatedRemover;
  49 +import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
46 import org.thingsboard.server.dao.tenant.TenantDao; 50 import org.thingsboard.server.dao.tenant.TenantDao;
47 51
48 import java.util.HashMap; 52 import java.util.HashMap;
@@ -84,6 +88,10 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic @@ -84,6 +88,10 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic
84 @Autowired 88 @Autowired
85 private CustomerDao customerDao; 89 private CustomerDao customerDao;
86 90
  91 + @Autowired
  92 + @Lazy
  93 + private TbTenantProfileCache tenantProfileCache;
  94 +
87 @Override 95 @Override
88 public User findUserByEmail(TenantId tenantId, String email) { 96 public User findUserByEmail(TenantId tenantId, String email) {
89 log.trace("Executing findUserByEmail [{}]", email); 97 log.trace("Executing findUserByEmail [{}]", email);
@@ -365,6 +373,16 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic @@ -365,6 +373,16 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic
365 private DataValidator<User> userValidator = 373 private DataValidator<User> userValidator =
366 new DataValidator<User>() { 374 new DataValidator<User>() {
367 @Override 375 @Override
  376 + protected void validateCreate(TenantId tenantId, User user) {
  377 + if (!user.getTenantId().getId().equals(ModelConstants.NULL_UUID)) {
  378 + DefaultTenantProfileConfiguration profileConfiguration =
  379 + (DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration();
  380 + long maxUsers = profileConfiguration.getMaxUsers();
  381 + validateNumberOfEntitiesPerTenant(tenantId, userDao, maxUsers, EntityType.USER);
  382 + }
  383 + }
  384 +
  385 + @Override
368 protected void validateDataImpl(TenantId requestTenantId, User user) { 386 protected void validateDataImpl(TenantId requestTenantId, User user) {
369 if (StringUtils.isEmpty(user.getEmail())) { 387 if (StringUtils.isEmpty(user.getEmail())) {
370 throw new DataValidationException("User email should be specified!"); 388 throw new DataValidationException("User email should be specified!");
@@ -36,12 +36,11 @@ @@ -36,12 +36,11 @@
36 <pkg.implementationTitle>${project.name}</pkg.implementationTitle> 36 <pkg.implementationTitle>${project.name}</pkg.implementationTitle>
37 <pkg.unixLogFolder>/var/log/${pkg.name}</pkg.unixLogFolder> 37 <pkg.unixLogFolder>/var/log/${pkg.name}</pkg.unixLogFolder>
38 <pkg.installFolder>/usr/share/${pkg.name}</pkg.installFolder> 38 <pkg.installFolder>/usr/share/${pkg.name}</pkg.installFolder>
39 - <spring-boot.version>2.2.6.RELEASE</spring-boot.version>  
40 - <spring-oauth2.version>2.1.2.RELEASE</spring-oauth2.version>  
41 - <spring.version>5.2.6.RELEASE</spring.version>  
42 - <spring-security.version>5.2.3.RELEASE</spring-security.version>  
43 - <spring-data-redis.version>2.2.4.RELEASE</spring-data-redis.version>  
44 - <jedis.version>3.1.0</jedis.version> 39 + <spring-boot.version>2.3.5.RELEASE</spring-boot.version>
  40 + <spring.version>5.2.10.RELEASE</spring.version>
  41 + <spring-security.version>5.4.1</spring-security.version>
  42 + <spring-data-redis.version>2.4.1</spring-data-redis.version>
  43 + <jedis.version>3.3.0</jedis.version>
45 <jjwt.version>0.7.0</jjwt.version> 44 <jjwt.version>0.7.0</jjwt.version>
46 <json-path.version>2.2.0</json-path.version> 45 <json-path.version>2.2.0</json-path.version>
47 <junit.version>4.12</junit.version> 46 <junit.version>4.12</junit.version>
@@ -52,15 +51,16 @@ @@ -52,15 +51,16 @@
52 <cassandra.version>4.6.0</cassandra.version> 51 <cassandra.version>4.6.0</cassandra.version>
53 <metrics.version>4.0.5</metrics.version> 52 <metrics.version>4.0.5</metrics.version>
54 <cassandra-unit.version>4.3.1.0</cassandra-unit.version> 53 <cassandra-unit.version>4.3.1.0</cassandra-unit.version>
  54 + <cassandra-all.version>3.11.9</cassandra-all.version>
55 <takari-cpsuite.version>1.2.7</takari-cpsuite.version> 55 <takari-cpsuite.version>1.2.7</takari-cpsuite.version>
56 <guava.version>28.2-jre</guava.version> 56 <guava.version>28.2-jre</guava.version>
57 <caffeine.version>2.6.1</caffeine.version> 57 <caffeine.version>2.6.1</caffeine.version>
58 <commons-lang3.version>3.4</commons-lang3.version> 58 <commons-lang3.version>3.4</commons-lang3.version>
59 <commons-io.version>2.5</commons-io.version> 59 <commons-io.version>2.5</commons-io.version>
60 <commons-csv.version>1.4</commons-csv.version> 60 <commons-csv.version>1.4</commons-csv.version>
61 - <jackson.version>2.10.2</jackson.version>  
62 - <jackson-annotations.version>2.10.2</jackson-annotations.version>  
63 - <jackson-core.version>2.10.2</jackson-core.version> 61 + <jackson.version>2.11.3</jackson.version>
  62 + <jackson-annotations.version>2.11.3</jackson-annotations.version>
  63 + <jackson-core.version>2.11.3</jackson-core.version>
64 <json-schema-validator.version>2.2.6</json-schema-validator.version> 64 <json-schema-validator.version>2.2.6</json-schema-validator.version>
65 <californium.version>1.0.2</californium.version> 65 <californium.version>1.0.2</californium.version>
66 <gson.version>2.6.2</gson.version> 66 <gson.version>2.6.2</gson.version>
@@ -72,7 +72,7 @@ @@ -72,7 +72,7 @@
72 <grpc.version>1.22.1</grpc.version> 72 <grpc.version>1.22.1</grpc.version>
73 <lombok.version>1.16.18</lombok.version> 73 <lombok.version>1.16.18</lombok.version>
74 <paho.client.version>1.2.4</paho.client.version> 74 <paho.client.version>1.2.4</paho.client.version>
75 - <netty.version>4.1.49.Final</netty.version> 75 + <netty.version>4.1.53.Final</netty.version>
76 <os-maven-plugin.version>1.5.0</os-maven-plugin.version> 76 <os-maven-plugin.version>1.5.0</os-maven-plugin.version>
77 <rabbitmq.version>4.8.0</rabbitmq.version> 77 <rabbitmq.version>4.8.0</rabbitmq.version>
78 <surfire.version>2.19.1</surfire.version> 78 <surfire.version>2.19.1</surfire.version>
@@ -96,7 +96,7 @@ @@ -96,7 +96,7 @@
96 <bucket4j.version>4.1.1</bucket4j.version> 96 <bucket4j.version>4.1.1</bucket4j.version>
97 <fst.version>2.57</fst.version> 97 <fst.version>2.57</fst.version>
98 <antlr.version>2.7.7</antlr.version> 98 <antlr.version>2.7.7</antlr.version>
99 - <snakeyaml.version>1.25</snakeyaml.version> 99 + <snakeyaml.version>1.27</snakeyaml.version>
100 <amazonaws.sqs.version>1.11.747</amazonaws.sqs.version> 100 <amazonaws.sqs.version>1.11.747</amazonaws.sqs.version>
101 <pubsub.client.version>1.105.0</pubsub.client.version> 101 <pubsub.client.version>1.105.0</pubsub.client.version>
102 <azure-servicebus.version>3.2.0</azure-servicebus.version> 102 <azure-servicebus.version>3.2.0</azure-servicebus.version>
@@ -873,11 +873,6 @@ @@ -873,11 +873,6 @@
873 <version>${spring-boot.version}</version> 873 <version>${spring-boot.version}</version>
874 </dependency> 874 </dependency>
875 <dependency> 875 <dependency>
876 - <groupId>org.springframework.cloud</groupId>  
877 - <artifactId>spring-cloud-starter-oauth2</artifactId>  
878 - <version>${spring-oauth2.version}</version>  
879 - </dependency>  
880 - <dependency>  
881 <groupId>org.springframework.security</groupId> 876 <groupId>org.springframework.security</groupId>
882 <artifactId>spring-security-oauth2-client</artifactId> 877 <artifactId>spring-security-oauth2-client</artifactId>
883 <version>${spring-security.version}</version> 878 <version>${spring-security.version}</version>
@@ -1201,6 +1196,11 @@ @@ -1201,6 +1196,11 @@
1201 <scope>test</scope> 1196 <scope>test</scope>
1202 </dependency> 1197 </dependency>
1203 <dependency> 1198 <dependency>
  1199 + <groupId>org.apache.cassandra</groupId>
  1200 + <artifactId>cassandra-all</artifactId>
  1201 + <version>${cassandra-all.version}</version>
  1202 + </dependency>
  1203 + <dependency>
1204 <groupId>junit</groupId> 1204 <groupId>junit</groupId>
1205 <artifactId>junit</artifactId> 1205 <artifactId>junit</artifactId>
1206 <version>${junit.version}</version> 1206 <version>${junit.version}</version>
@@ -121,6 +121,11 @@ @@ -121,6 +121,11 @@
121 <artifactId>jts-core</artifactId> 121 <artifactId>jts-core</artifactId>
122 </dependency> 122 </dependency>
123 <dependency> 123 <dependency>
  124 + <groupId>com.sun.mail</groupId>
  125 + <artifactId>javax.mail</artifactId>
  126 + <scope>provided</scope>
  127 + </dependency>
  128 + <dependency>
124 <groupId>junit</groupId> 129 <groupId>junit</groupId>
125 <artifactId>junit</artifactId> 130 <artifactId>junit</artifactId>
126 <version>${junit.version}</version> 131 <version>${junit.version}</version>
@@ -54,7 +54,6 @@ @@ -54,7 +54,6 @@
54 <dependency> 54 <dependency>
55 <groupId>org.apache.cassandra</groupId> 55 <groupId>org.apache.cassandra</groupId>
56 <artifactId>cassandra-all</artifactId> 56 <artifactId>cassandra-all</artifactId>
57 - <version>3.11.6</version>  
58 </dependency> 57 </dependency>
59 <dependency> 58 <dependency>
60 <groupId>com.datastax.oss</groupId> 59 <groupId>com.datastax.oss</groupId>
@@ -41,6 +41,54 @@ @@ -41,6 +41,54 @@
41 </mat-error> 41 </mat-error>
42 </mat-form-field> 42 </mat-form-field>
43 <mat-form-field class="mat-block"> 43 <mat-form-field class="mat-block">
  44 + <mat-label translate>tenant-profile.maximum-customers</mat-label>
  45 + <input matInput required min="0" step="1"
  46 + formControlName="maxCustomers"
  47 + type="number">
  48 + <mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxCustomers').hasError('required')">
  49 + {{ 'tenant-profile.maximum-customers-required' | translate}}
  50 + </mat-error>
  51 + <mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxCustomers').hasError('min')">
  52 + {{ 'tenant-profile.maximum-customers-range' | translate}}
  53 + </mat-error>
  54 + </mat-form-field>
  55 + <mat-form-field class="mat-block">
  56 + <mat-label translate>tenant-profile.maximum-users</mat-label>
  57 + <input matInput required min="0" step="1"
  58 + formControlName="maxUsers"
  59 + type="number">
  60 + <mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxUsers').hasError('required')">
  61 + {{ 'tenant-profile.maximum-users-required' | translate}}
  62 + </mat-error>
  63 + <mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxUsers').hasError('min')">
  64 + {{ 'tenant-profile.maximum-users-range' | translate}}
  65 + </mat-error>
  66 + </mat-form-field>
  67 + <mat-form-field class="mat-block">
  68 + <mat-label translate>tenant-profile.maximum-dashboards</mat-label>
  69 + <input matInput required min="0" step="1"
  70 + formControlName="maxDashboards"
  71 + type="number">
  72 + <mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxDashboards').hasError('required')">
  73 + {{ 'tenant-profile.maximum-dashboards-required' | translate}}
  74 + </mat-error>
  75 + <mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxDashboards').hasError('min')">
  76 + {{ 'tenant-profile.maximum-dashboards-range' | translate}}
  77 + </mat-error>
  78 + </mat-form-field>
  79 + <mat-form-field class="mat-block">
  80 + <mat-label translate>tenant-profile.maximum-rule-chains</mat-label>
  81 + <input matInput required min="0" step="1"
  82 + formControlName="maxRuleChains"
  83 + type="number">
  84 + <mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxRuleChains').hasError('required')">
  85 + {{ 'tenant-profile.maximum-rule-chains-required' | translate}}
  86 + </mat-error>
  87 + <mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxRuleChains').hasError('min')">
  88 + {{ 'tenant-profile.maximum-rule-chains-range' | translate}}
  89 + </mat-error>
  90 + </mat-form-field>
  91 + <mat-form-field class="mat-block">
44 <mat-label translate>tenant-profile.max-transport-messages</mat-label> 92 <mat-label translate>tenant-profile.max-transport-messages</mat-label>
45 <input matInput required min="0" step="1" 93 <input matInput required min="0" step="1"
46 formControlName="maxTransportMessages" 94 formControlName="maxTransportMessages"
@@ -55,6 +55,10 @@ export class DefaultTenantProfileConfigurationComponent implements ControlValueA @@ -55,6 +55,10 @@ export class DefaultTenantProfileConfigurationComponent implements ControlValueA
55 this.defaultTenantProfileConfigurationFormGroup = this.fb.group({ 55 this.defaultTenantProfileConfigurationFormGroup = this.fb.group({
56 maxDevices: [null, [Validators.required, Validators.min(0)]], 56 maxDevices: [null, [Validators.required, Validators.min(0)]],
57 maxAssets: [null, [Validators.required, Validators.min(0)]], 57 maxAssets: [null, [Validators.required, Validators.min(0)]],
  58 + maxCustomers: [null, [Validators.required, Validators.min(0)]],
  59 + maxUsers: [null, [Validators.required, Validators.min(0)]],
  60 + maxDashboards: [null, [Validators.required, Validators.min(0)]],
  61 + maxRuleChains: [null, [Validators.required, Validators.min(0)]],
58 transportTenantMsgRateLimit: [null, []], 62 transportTenantMsgRateLimit: [null, []],
59 transportTenantTelemetryMsgRateLimit: [null, []], 63 transportTenantTelemetryMsgRateLimit: [null, []],
60 transportTenantTelemetryDataPointsRateLimit: [null, []], 64 transportTenantTelemetryDataPointsRateLimit: [null, []],
@@ -487,7 +487,8 @@ export default abstract class LeafletMap { @@ -487,7 +487,8 @@ export default abstract class LeafletMap {
487 } 487 }
488 488
489 const mapBounds = this.map.getBounds(); 489 const mapBounds = this.map.getBounds();
490 - if (bounds.isValid() && (!this.bounds || !this.bounds.isValid() || !this.bounds.equals(bounds) && !mapBounds.contains(bounds))) { 490 + if (bounds.isValid() && (!this.bounds || !this.bounds.isValid() || !this.bounds.equals(bounds)
  491 + && this.options.fitMapBounds ? !mapBounds.contains(bounds) : false)) {
491 this.bounds = bounds; 492 this.bounds = bounds;
492 this.fitBounds(bounds); 493 this.fitBounds(bounds);
493 } 494 }
@@ -20,6 +20,7 @@ import { EntityType } from '@shared/models/entity-type.models'; @@ -20,6 +20,7 @@ import { EntityType } from '@shared/models/entity-type.models';
20 import tinycolor from 'tinycolor2'; 20 import tinycolor from 'tinycolor2';
21 21
22 export const DEFAULT_MAP_PAGE_SIZE = 16384; 22 export const DEFAULT_MAP_PAGE_SIZE = 16384;
  23 +export const DEFAULT_ZOOM_LEVEL = 8;
23 24
24 export type GenericFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string; 25 export type GenericFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string;
25 export type MarkerImageFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string; 26 export type MarkerImageFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string;
@@ -229,7 +230,6 @@ export const defaultSettings: any = { @@ -229,7 +230,6 @@ export const defaultSettings: any = {
229 strokeWeight: 2, 230 strokeWeight: 2,
230 strokeOpacity: 1.0, 231 strokeOpacity: 1.0,
231 initCallback: () => { }, 232 initCallback: () => { },
232 - defaultZoomLevel: 8,  
233 disableScrollZooming: false, 233 disableScrollZooming: false,
234 minZoomLevel: 16, 234 minZoomLevel: 16,
235 credentials: '', 235 credentials: '',
@@ -17,7 +17,7 @@ @@ -17,7 +17,7 @@
17 17
18 import L from 'leaflet'; 18 import L from 'leaflet';
19 import LeafletMap from '../leaflet-map'; 19 import LeafletMap from '../leaflet-map';
20 -import { UnitedMapSettings } from '../map-models'; 20 +import { DEFAULT_ZOOM_LEVEL, UnitedMapSettings } from '../map-models';
21 import 'leaflet.gridlayer.googlemutant'; 21 import 'leaflet.gridlayer.googlemutant';
22 import { ResourcesService } from '@core/services/resources.service'; 22 import { ResourcesService } from '@core/services/resources.service';
23 import { WidgetContext } from '@home/models/widget-component.models'; 23 import { WidgetContext } from '@home/models/widget-component.models';
@@ -39,7 +39,7 @@ export class GoogleMap extends LeafletMap { @@ -39,7 +39,7 @@ export class GoogleMap extends LeafletMap {
39 const map = L.map($container, { 39 const map = L.map($container, {
40 attributionControl: false, 40 attributionControl: false,
41 editable: !!options.editablePolygon 41 editable: !!options.editablePolygon
42 - }).setView(options?.defaultCenterPosition, options?.defaultZoomLevel); 42 + }).setView(options?.defaultCenterPosition, options?.defaultZoomLevel || DEFAULT_ZOOM_LEVEL);
43 (L.gridLayer as any).googleMutant({ 43 (L.gridLayer as any).googleMutant({
44 type: options?.gmDefaultMapType || 'roadmap' 44 type: options?.gmDefaultMapType || 'roadmap'
45 }).addTo(map); 45 }).addTo(map);
@@ -16,7 +16,7 @@ @@ -16,7 +16,7 @@
16 16
17 import L from 'leaflet'; 17 import L from 'leaflet';
18 import LeafletMap from '../leaflet-map'; 18 import LeafletMap from '../leaflet-map';
19 -import { UnitedMapSettings } from '../map-models'; 19 +import { DEFAULT_ZOOM_LEVEL, UnitedMapSettings } from '../map-models';
20 import { WidgetContext } from '@home/models/widget-component.models'; 20 import { WidgetContext } from '@home/models/widget-component.models';
21 21
22 export class HEREMap extends LeafletMap { 22 export class HEREMap extends LeafletMap {
@@ -24,7 +24,7 @@ export class HEREMap extends LeafletMap { @@ -24,7 +24,7 @@ export class HEREMap extends LeafletMap {
24 super(ctx, $container, options); 24 super(ctx, $container, options);
25 const map = L.map($container, { 25 const map = L.map($container, {
26 editable: !!options.editablePolygon 26 editable: !!options.editablePolygon
27 - }).setView(options?.defaultCenterPosition, options?.defaultZoomLevel); 27 + }).setView(options?.defaultCenterPosition, options?.defaultZoomLevel || DEFAULT_ZOOM_LEVEL);
28 const tileLayer = (L.tileLayer as any).provider(options.mapProviderHere || 'HERE.normalDay', options.credentials); 28 const tileLayer = (L.tileLayer as any).provider(options.mapProviderHere || 'HERE.normalDay', options.credentials);
29 tileLayer.addTo(map); 29 tileLayer.addTo(map);
30 super.initSettings(options); 30 super.initSettings(options);
@@ -16,7 +16,7 @@ @@ -16,7 +16,7 @@
16 16
17 import L from 'leaflet'; 17 import L from 'leaflet';
18 import LeafletMap from '../leaflet-map'; 18 import LeafletMap from '../leaflet-map';
19 -import { UnitedMapSettings } from '../map-models'; 19 +import { DEFAULT_ZOOM_LEVEL, UnitedMapSettings } from '../map-models';
20 import { WidgetContext } from '@home/models/widget-component.models'; 20 import { WidgetContext } from '@home/models/widget-component.models';
21 21
22 export class OpenStreetMap extends LeafletMap { 22 export class OpenStreetMap extends LeafletMap {
@@ -24,7 +24,7 @@ export class OpenStreetMap extends LeafletMap { @@ -24,7 +24,7 @@ export class OpenStreetMap extends LeafletMap {
24 super(ctx, $container, options); 24 super(ctx, $container, options);
25 const map = L.map($container, { 25 const map = L.map($container, {
26 editable: !!options.editablePolygon 26 editable: !!options.editablePolygon
27 - }).setView(options?.defaultCenterPosition, options?.defaultZoomLevel); 27 + }).setView(options?.defaultCenterPosition, options?.defaultZoomLevel || DEFAULT_ZOOM_LEVEL);
28 let tileLayer; 28 let tileLayer;
29 if (options.useCustomProvider) { 29 if (options.useCustomProvider) {
30 tileLayer = L.tileLayer(options.customProviderTileUrl); 30 tileLayer = L.tileLayer(options.customProviderTileUrl);
@@ -17,7 +17,7 @@ @@ -17,7 +17,7 @@
17 17
18 import L from 'leaflet'; 18 import L from 'leaflet';
19 import LeafletMap from '../leaflet-map'; 19 import LeafletMap from '../leaflet-map';
20 -import { UnitedMapSettings } from '../map-models'; 20 +import { DEFAULT_ZOOM_LEVEL, UnitedMapSettings } from '../map-models';
21 import { WidgetContext } from '@home/models/widget-component.models'; 21 import { WidgetContext } from '@home/models/widget-component.models';
22 22
23 export class TencentMap extends LeafletMap { 23 export class TencentMap extends LeafletMap {
@@ -26,7 +26,7 @@ export class TencentMap extends LeafletMap { @@ -26,7 +26,7 @@ export class TencentMap extends LeafletMap {
26 const txUrl = 'http://rt{s}.map.gtimg.com/realtimerender?z={z}&x={x}&y={y}&type=vector&style=0'; 26 const txUrl = 'http://rt{s}.map.gtimg.com/realtimerender?z={z}&x={x}&y={y}&type=vector&style=0';
27 const map = L.map($container, { 27 const map = L.map($container, {
28 editable: !!options.editablePolygon 28 editable: !!options.editablePolygon
29 - }).setView(options?.defaultCenterPosition, options?.defaultZoomLevel); 29 + }).setView(options?.defaultCenterPosition, options?.defaultZoomLevel || DEFAULT_ZOOM_LEVEL);
30 const txLayer = L.tileLayer(txUrl, { 30 const txLayer = L.tileLayer(txUrl, {
31 subdomains: '0123', 31 subdomains: '0123',
32 tms: true, 32 tms: true,
@@ -56,6 +56,12 @@ export const customerHref = '<a href="https://github.com/thingsboard/thingsboard @@ -56,6 +56,12 @@ export const customerHref = '<a href="https://github.com/thingsboard/thingsboard
56 56
57 export const attributeDataHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts#L76">Attribute Data</a>'; 57 export const attributeDataHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts#L76">Attribute Data</a>';
58 58
  59 +export const timeseriesDataHref = '<a href="https://github.com/thingsboard/thingsboard/blob/627c0577b08452308f925cecb3860e35292c649e/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts#L91">Timeseries Data</a>';
  60 +
  61 +export const aggregationTypeHref = '<a href="https://github.com/thingsboard/thingsboard/blob/a8ea887eacf7729e603ace13ce2d7d89dae82931/ui-ngx/src/app/shared/models/time/time.models.ts#L54">Aggregation Type</a>';
  62 +
  63 +export const dataSortOrderHref = '<a href="https://github.com/thingsboard/thingsboard/blob/627c0577b08452308f925cecb3860e35292c649e/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts#L95">Data Sort Order</a>';
  64 +
59 export const userHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/user.model.ts#L23">User</a>'; 65 export const userHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/user.model.ts#L23">User</a>';
60 66
61 export const entityDataHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/shared/models/query/query.models.ts#L567">Entity data</a>'; 67 export const entityDataHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/shared/models/query/query.models.ts#L567">Entity data</a>';
@@ -1080,6 +1086,23 @@ export const serviceCompletions: TbEditorCompletions = { @@ -1080,6 +1086,23 @@ export const serviceCompletions: TbEditorCompletions = {
1080 ], 1086 ],
1081 return: observableReturnTypeVariable('any') 1087 return: observableReturnTypeVariable('any')
1082 }, 1088 },
  1089 + getEntityTimeseries: {
  1090 + description: 'Get entity timeseries',
  1091 + meta: 'function',
  1092 + args: [
  1093 + {name: 'entityId', type: entityIdHref, description: 'Id of the entity'},
  1094 + {name: 'keys', type: `Array&lt;string&gt;`, description: 'Array of the keys'},
  1095 + {name: 'startTs', type: 'number', description: 'Start time in milliseconds'},
  1096 + {name: 'endTs', type: 'number', description: 'End time in milliseconds'},
  1097 + {name: 'limit', type: 'number', description: 'Limit of values to receive for each key'},
  1098 + {name: 'agg', type: aggregationTypeHref, description: 'Aggregation type'},
  1099 + {name: 'interval', type: 'number', description: 'Aggregation interval'},
  1100 + {name: 'orderBy', type: dataSortOrderHref, description: 'Data order by time'},
  1101 + {name: 'useStrictDataTypes', type: 'boolean', description: 'If "false" all values will be returned as strings'},
  1102 + requestConfigArg
  1103 + ],
  1104 + return: observableReturnTypeVariable(timeseriesDataHref)
  1105 + },
1083 } 1106 }
1084 }, 1107 },
1085 entityService: { 1108 entityService: {
@@ -26,6 +26,10 @@ export enum TenantProfileType { @@ -26,6 +26,10 @@ export enum TenantProfileType {
26 export interface DefaultTenantProfileConfiguration { 26 export interface DefaultTenantProfileConfiguration {
27 maxDevices: number; 27 maxDevices: number;
28 maxAssets: number; 28 maxAssets: number;
  29 + maxCustomers: number;
  30 + maxUsers: number;
  31 + maxDashboards: number;
  32 + maxRuleChains: number;
29 33
30 transportTenantMsgRateLimit?: string; 34 transportTenantMsgRateLimit?: string;
31 transportTenantTelemetryMsgRateLimit?: string; 35 transportTenantTelemetryMsgRateLimit?: string;
@@ -56,6 +60,10 @@ export function createTenantProfileConfiguration(type: TenantProfileType): Tenan @@ -56,6 +60,10 @@ export function createTenantProfileConfiguration(type: TenantProfileType): Tenan
56 const defaultConfiguration: DefaultTenantProfileConfiguration = { 60 const defaultConfiguration: DefaultTenantProfileConfiguration = {
57 maxDevices: 0, 61 maxDevices: 0,
58 maxAssets: 0, 62 maxAssets: 0,
  63 + maxCustomers: 0,
  64 + maxUsers: 0,
  65 + maxDashboards: 0,
  66 + maxRuleChains: 0,
59 maxTransportMessages: 0, 67 maxTransportMessages: 0,
60 maxTransportDataPoints: 0, 68 maxTransportDataPoints: 0,
61 maxREExecutions: 0, 69 maxREExecutions: 0,
@@ -1946,6 +1946,18 @@ @@ -1946,6 +1946,18 @@
1946 "maximum-assets": "Maximum number of assets (0 - unlimited)", 1946 "maximum-assets": "Maximum number of assets (0 - unlimited)",
1947 "maximum-assets-required": "Maximum number of assets is required.", 1947 "maximum-assets-required": "Maximum number of assets is required.",
1948 "maximum-assets-range": "Maximum number of assets can't be negative", 1948 "maximum-assets-range": "Maximum number of assets can't be negative",
  1949 + "maximum-customers": "Maximum number of customers (0 - unlimited)",
  1950 + "maximum-customers-required": "Maximum number of customers is required.",
  1951 + "maximum-customers-range": "Maximum number of customers can't be negative",
  1952 + "maximum-users": "Maximum number of users (0 - unlimited)",
  1953 + "maximum-users-required": "Maximum number of users is required.",
  1954 + "maximum-users-range": "Maximum number of users can't be negative",
  1955 + "maximum-dashboards": "Maximum number of dashboards (0 - unlimited)",
  1956 + "maximum-dashboards-required": "Maximum number of dashboards is required.",
  1957 + "maximum-dashboards-range": "Maximum number of dashboards can't be negative",
  1958 + "maximum-rule-chains": "Maximum number of rule chains (0 - unlimited)",
  1959 + "maximum-rule-chains-required": "Maximum number of rule chains is required.",
  1960 + "maximum-rule-chains-range": "Maximum number of rule chains can't be negative",
1949 "transport-tenant-msg-rate-limit": "Transport tenant messages rate limit.", 1961 "transport-tenant-msg-rate-limit": "Transport tenant messages rate limit.",
1950 "transport-tenant-telemetry-msg-rate-limit": "Transport tenant telemetry messages rate limit.", 1962 "transport-tenant-telemetry-msg-rate-limit": "Transport tenant telemetry messages rate limit.",
1951 "transport-tenant-telemetry-data-points-rate-limit": "Transport tenant telemetry data points rate limit.", 1963 "transport-tenant-telemetry-data-points-rate-limit": "Transport tenant telemetry data points rate limit.",