Commit 35a2107944aaec4f29025e169673548f7bc68f16

Authored by Igor Kulikov
Committed by GitHub
2 parents 573b9a34 2df72b4e

Merge pull request #3717 from YevhenBondarenko/feature/tenant-profile

[WIP]added maxCustomers, maxUsers, maxDashboards, maxRuleChains for Tenant…
Showing 30 changed files with 229 additions and 47 deletions
@@ -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!");
@@ -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, []],
@@ -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.",