diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java index 250f9c75a5..10df66ece5 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java @@ -24,6 +24,10 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura private long maxDevices; private long maxAssets; + private long maxCustomers; + private long maxUsers; + private long maxDashboards; + private long maxRuleChains; private String transportTenantMsgRateLimit; private String transportTenantTelemetryMsgRateLimit; diff --git a/dao/src/main/java/org/thingsboard/server/dao/TenantEntityDao.java b/dao/src/main/java/org/thingsboard/server/dao/TenantEntityDao.java new file mode 100644 index 0000000000..e47dd1e301 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/TenantEntityDao.java @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao; + +import org.thingsboard.server.common.data.id.TenantId; + +public interface TenantEntityDao { + + Long countByTenantId(TenantId tenantId); +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java index 1366df3ac2..ae8bf5706c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java @@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; +import org.thingsboard.server.dao.TenantEntityDao; import java.util.List; import java.util.Optional; @@ -32,7 +33,7 @@ import java.util.UUID; * The Interface AssetDao. * */ -public interface AssetDao extends Dao { +public interface AssetDao extends Dao, TenantEntityDao { /** * Find asset info by id. @@ -166,6 +167,4 @@ public interface AssetDao extends Dao { */ ListenableFuture> findTenantAssetTypesAsync(UUID tenantId); - Long countAssetsByTenantId(TenantId tenantId); - } diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java index 923baa1df4..29eaad31fb 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java @@ -330,12 +330,7 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ DefaultTenantProfileConfiguration profileConfiguration = (DefaultTenantProfileConfiguration)tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); long maxAssets = profileConfiguration.getMaxAssets(); - if (maxAssets > 0) { - long currentAssetsCount = assetDao.countAssetsByTenantId(tenantId); - if (maxAssets >= currentAssetsCount) { - throw new DataValidationException("Can't create assets more then " + maxAssets); - } - } + validateNumberOfEntitiesPerTenant(tenantId, assetDao, maxAssets, EntityType.ASSET); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDao.java b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDao.java index 11fa5cb7cd..98622c8b83 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDao.java @@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; +import org.thingsboard.server.dao.TenantEntityDao; import java.util.Optional; import java.util.UUID; @@ -27,7 +28,7 @@ import java.util.UUID; /** * The Interface CustomerDao. */ -public interface CustomerDao extends Dao { +public interface CustomerDao extends Dao, TenantEntityDao { /** * Save or update customer object @@ -54,5 +55,5 @@ public interface CustomerDao extends Dao { * @return the optional customer object */ Optional findCustomersByTenantIdAndTitle(UUID tenantId, String title); - + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java index 14d591e7e8..d97b4e9c34 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java @@ -21,13 +21,16 @@ import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.device.DeviceService; @@ -38,6 +41,7 @@ import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.Validator; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantDao; import org.thingsboard.server.dao.user.UserService; @@ -75,6 +79,10 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom @Autowired private DashboardService dashboardService; + @Autowired + @Lazy + private TbTenantProfileCache tenantProfileCache; + @Override public Customer findCustomerById(TenantId tenantId, CustomerId customerId) { log.trace("Executing findCustomerById [{}]", customerId); @@ -162,6 +170,11 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom @Override protected void validateCreate(TenantId tenantId, Customer customer) { + DefaultTenantProfileConfiguration profileConfiguration = + (DefaultTenantProfileConfiguration)tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); + long maxCustomers = profileConfiguration.getMaxCustomers(); + + validateNumberOfEntitiesPerTenant(tenantId, customerDao, maxCustomers, EntityType.CUSTOMER); customerDao.findCustomersByTenantIdAndTitle(customer.getTenantId().getId(), customer.getTitle()).ifPresent( c -> { throw new DataValidationException("Customer with such title already exists!"); diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java index 0429c6d469..0beb89ef06 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java @@ -18,11 +18,12 @@ package org.thingsboard.server.dao.dashboard; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.Dao; +import org.thingsboard.server.dao.TenantEntityDao; /** * The Interface DashboardDao. */ -public interface DashboardDao extends Dao { +public interface DashboardDao extends Dao, TenantEntityDao { /** * Save or update dashboard object @@ -31,5 +32,4 @@ public interface DashboardDao extends Dao { * @return saved dashboard object */ Dashboard save(TenantId tenantId, Dashboard dashboard); - } diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java index 112f998cd4..55bf31e321 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java @@ -19,10 +19,12 @@ import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.DashboardInfo; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DashboardId; @@ -31,12 +33,14 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.dao.customer.CustomerDao; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.Validator; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantDao; import java.util.concurrent.ExecutionException; @@ -61,6 +65,10 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb @Autowired private CustomerDao customerDao; + @Autowired + @Lazy + private TbTenantProfileCache tenantProfileCache; + @Override public Dashboard findDashboardById(TenantId tenantId, DashboardId dashboardId) { log.trace("Executing findDashboardById [{}]", dashboardId); @@ -214,6 +222,14 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb private DataValidator dashboardValidator = new DataValidator() { + @Override + protected void validateCreate(TenantId tenantId, Dashboard data) { + DefaultTenantProfileConfiguration profileConfiguration = + (DefaultTenantProfileConfiguration)tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); + long maxDashboards = profileConfiguration.getMaxDashboards(); + validateNumberOfEntitiesPerTenant(tenantId, dashboardDao, maxDashboards, EntityType.DASHBOARD); + } + @Override protected void validateDataImpl(TenantId tenantId, Dashboard dashboard) { if (StringUtils.isEmpty(dashboard.getTitle())) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java index 2f321ff499..3e8f6445a2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java @@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; +import org.thingsboard.server.dao.TenantEntityDao; import java.util.List; import java.util.Optional; @@ -32,7 +33,7 @@ import java.util.UUID; * The Interface DeviceDao. * */ -public interface DeviceDao extends Dao { +public interface DeviceDao extends Dao, TenantEntityDao { /** * Find device info by id. @@ -203,8 +204,6 @@ public interface DeviceDao extends Dao { */ ListenableFuture findDeviceByTenantIdAndIdAsync(TenantId tenantId, UUID id); - Long countDevicesByTenantId(TenantId tenantId); - Long countDevicesByDeviceProfileId(TenantId tenantId, UUID deviceProfileId); /** diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java index 24c597b4df..a919f86dfe 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java @@ -530,12 +530,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe DefaultTenantProfileConfiguration profileConfiguration = (DefaultTenantProfileConfiguration)tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); long maxDevices = profileConfiguration.getMaxDevices(); - if (maxDevices > 0) { - long currentDevicesCount = deviceDao.countDevicesByTenantId(tenantId); - if (maxDevices >= currentDevicesCount) { - throw new DataValidationException("Can't create devices more then " + maxDevices); - } - } + validateNumberOfEntitiesPerTenant(tenantId, deviceDao, maxDevices, EntityType.DEVICE); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java b/dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java index a0f03b2a32..42d02addcd 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java @@ -37,12 +37,11 @@ public abstract class AbstractEntityService { protected Optional extractConstraintViolationException(Exception t) { if (t instanceof ConstraintViolationException) { - return Optional.of ((ConstraintViolationException) t); + return Optional.of((ConstraintViolationException) t); } else if (t.getCause() instanceof ConstraintViolationException) { - return Optional.of ((ConstraintViolationException) (t.getCause())); + return Optional.of((ConstraintViolationException) (t.getCause())); } else { return Optional.empty(); } } - } diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java index 5997839119..263f01570e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java @@ -24,6 +24,7 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.hibernate.exception.ConstraintViolationException; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.EntityType; @@ -44,11 +45,13 @@ import org.thingsboard.server.common.data.rule.RuleChainData; import org.thingsboard.server.common.data.rule.RuleChainImportResult; import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.Validator; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantDao; import java.util.ArrayList; @@ -81,6 +84,10 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC @Autowired private TenantDao tenantDao; + @Autowired + @Lazy + private TbTenantProfileCache tenantProfileCache; + @Override public RuleChain saveRuleChain(RuleChain ruleChain) { ruleChainValidator.validate(ruleChain, RuleChain::getTenantId); @@ -580,6 +587,14 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC private DataValidator ruleChainValidator = new DataValidator() { + @Override + protected void validateCreate(TenantId tenantId, RuleChain data) { + DefaultTenantProfileConfiguration profileConfiguration = + (DefaultTenantProfileConfiguration)tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); + long maxRuleChains = profileConfiguration.getMaxRuleChains(); + validateNumberOfEntitiesPerTenant(tenantId, ruleChainDao, maxRuleChains, EntityType.RULE_CHAIN); + } + @Override protected void validateDataImpl(TenantId tenantId, RuleChain ruleChain) { if (StringUtils.isEmpty(ruleChain.getName())) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java index c3214425fe..87ab9d26b6 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java @@ -19,13 +19,14 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.dao.Dao; +import org.thingsboard.server.dao.TenantEntityDao; import java.util.UUID; /** * Created by igor on 3/12/18. */ -public interface RuleChainDao extends Dao { +public interface RuleChainDao extends Dao, TenantEntityDao { /** * Find rule chains by tenantId and page link. @@ -35,5 +36,4 @@ public interface RuleChainDao extends Dao { * @return the list of rule chain objects */ PageData findRuleChainsByTenantId(UUID tenantId, PageLink pageLink); - } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java index d78654164f..0bd0e7b030 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java @@ -18,7 +18,9 @@ package org.thingsboard.server.dao.service; import com.fasterxml.jackson.databind.JsonNode; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.BaseData; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.TenantEntityDao; import org.thingsboard.server.dao.exception.DataValidationException; import java.util.HashSet; @@ -79,6 +81,19 @@ public abstract class DataValidator> { return emailMatcher.matches(); } + protected void validateNumberOfEntitiesPerTenant(TenantId tenantId, + TenantEntityDao tenantEntityDao, + long maxEntities, + EntityType entityType) { + if (maxEntities > 0) { + long currentEntitiesCount = tenantEntityDao.countByTenantId(tenantId); + if (currentEntitiesCount >= maxEntities) { + throw new DataValidationException(String.format("Can't create more then %d %ss!", + maxEntities, entityType.name().toLowerCase().replaceAll("_", " "))); + } + } + } + protected static void validateJsonStructure(JsonNode expectedNode, JsonNode actualNode) { Set expectedFields = new HashSet<>(); Iterator fieldsIterator = expectedNode.fieldNames(); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java index 698183ea0a..d2a5167b43 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java @@ -178,8 +178,7 @@ public class JpaAssetDao extends JpaAbstractSearchTextDao im } @Override - public Long countAssetsByTenantId(TenantId tenantId) { + public Long countByTenantId(TenantId tenantId) { return assetRepository.countByTenantId(tenantId.getId()); - } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/customer/CustomerRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/customer/CustomerRepository.java index bf2a845ec0..4b5a8e0791 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/customer/CustomerRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/customer/CustomerRepository.java @@ -37,4 +37,5 @@ public interface CustomerRepository extends PagingAndSortingRepository { + + Long countByTenantId(UUID tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardDao.java index 8d637f0cbe..0f083cf6b9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardDao.java @@ -19,6 +19,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.dashboard.DashboardDao; import org.thingsboard.server.dao.model.sql.DashboardEntity; import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; @@ -43,4 +44,9 @@ public class JpaDashboardDao extends JpaAbstractSearchTextDao getCrudRepository() { return dashboardRepository; } + + @Override + public Long countByTenantId(TenantId tenantId) { + return dashboardRepository.countByTenantId(tenantId.getId()); + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java index 9f3cc5b5a7..9519b169e2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java @@ -50,9 +50,9 @@ public interface DeviceRepository extends PagingAndSortingRepository findByTenantIdAndProfileId(@Param("tenantId") UUID tenantId, - @Param("profileId") UUID profileId, - @Param("searchText") String searchText, - Pageable pageable); + @Param("profileId") UUID profileId, + @Param("searchText") String searchText, + Pageable pageable); @Query("SELECT new org.thingsboard.server.dao.model.sql.DeviceInfoEntity(d, c.title, c.additionalInfo, p.name) " + "FROM DeviceEntity d " + @@ -62,9 +62,9 @@ public interface DeviceRepository extends PagingAndSortingRepository findDeviceInfosByTenantIdAndCustomerId(@Param("tenantId") UUID tenantId, - @Param("customerId") UUID customerId, - @Param("searchText") String searchText, - Pageable pageable); + @Param("customerId") UUID customerId, + @Param("searchText") String searchText, + Pageable pageable); @Query("SELECT d FROM DeviceEntity d WHERE d.tenantId = :tenantId") Page findByTenantId(@Param("tenantId") UUID tenantId, @@ -102,9 +102,9 @@ public interface DeviceRepository extends PagingAndSortingRepository findDeviceInfosByTenantIdAndType(@Param("tenantId") UUID tenantId, - @Param("type") String type, - @Param("textSearch") String textSearch, - Pageable pageable); + @Param("type") String type, + @Param("textSearch") String textSearch, + Pageable pageable); @Query("SELECT new org.thingsboard.server.dao.model.sql.DeviceInfoEntity(d, c.title, c.additionalInfo, p.name) " + "FROM DeviceEntity d " + @@ -137,10 +137,10 @@ public interface DeviceRepository extends PagingAndSortingRepository findDeviceInfosByTenantIdAndCustomerIdAndType(@Param("tenantId") UUID tenantId, - @Param("customerId") UUID customerId, - @Param("type") String type, - @Param("textSearch") String textSearch, - Pageable pageable); + @Param("customerId") UUID customerId, + @Param("type") String type, + @Param("textSearch") String textSearch, + Pageable pageable); @Query("SELECT new org.thingsboard.server.dao.model.sql.DeviceInfoEntity(d, c.title, c.additionalInfo, p.name) " + "FROM DeviceEntity d " + diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java index fceefcadf9..7ce59bda0d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java @@ -220,7 +220,7 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao } @Override - public Long countDevicesByTenantId(TenantId tenantId) { + public Long countByTenantId(TenantId tenantId) { return deviceRepository.countByTenantId(tenantId.getId()); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java index 57d1545a94..3331f57e00 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java @@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.rule.RuleChain; @@ -56,4 +57,8 @@ public class JpaRuleChainDao extends JpaAbstractSearchTextDao imple DaoUtil.toPageable(pageLink))); } + + @Override + public Long countByTenantId(TenantId tenantId) { + return userRepository.countByTenantId(tenantId.getId()); + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java index 1d512a7944..ceaf09ec59 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java @@ -47,4 +47,5 @@ public interface UserRepository extends PagingAndSortingRepository { +public interface UserDao extends Dao, TenantEntityDao { /** * Save or update user object @@ -49,7 +50,7 @@ public interface UserDao extends Dao { * @return the list of user entities */ PageData findByTenantId(UUID tenantId, PageLink pageLink); - + /** * Find tenant admin users by tenantId and page link. * @@ -58,7 +59,7 @@ public interface UserDao extends Dao { * @return the list of user entities */ PageData findTenantAdmins(UUID tenantId, PageLink pageLink); - + /** * Find customer users by tenantId, customerId and page link. * @@ -68,5 +69,4 @@ public interface UserDao extends Dao { * @return the list of user entities */ PageData findCustomerUsers(UUID tenantId, UUID customerId, PageLink pageLink); - } diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java index 314684c3fe..a19ec57f48 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java @@ -24,8 +24,10 @@ import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.id.CustomerId; @@ -36,6 +38,7 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.UserCredentials; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.dao.customer.CustomerDao; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; @@ -43,6 +46,7 @@ import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantDao; import java.util.HashMap; @@ -84,6 +88,10 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic @Autowired private CustomerDao customerDao; + @Autowired + @Lazy + private TbTenantProfileCache tenantProfileCache; + @Override public User findUserByEmail(TenantId tenantId, String email) { log.trace("Executing findUserByEmail [{}]", email); @@ -364,6 +372,16 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic private DataValidator userValidator = new DataValidator() { + @Override + protected void validateCreate(TenantId tenantId, User user) { + if (!user.getTenantId().getId().equals(ModelConstants.NULL_UUID)) { + DefaultTenantProfileConfiguration profileConfiguration = + (DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); + long maxUsers = profileConfiguration.getMaxUsers(); + validateNumberOfEntitiesPerTenant(tenantId, userDao, maxUsers, EntityType.USER); + } + } + @Override protected void validateDataImpl(TenantId requestTenantId, User user) { if (StringUtils.isEmpty(user.getEmail())) { diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html index 12258ffd5f..ae9fd8ea9c 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html @@ -40,6 +40,54 @@ {{ 'tenant-profile.maximum-assets-range' | translate}} + + tenant-profile.maximum-customers + + + {{ 'tenant-profile.maximum-customers-required' | translate}} + + + {{ 'tenant-profile.maximum-customers-range' | translate}} + + + + tenant-profile.maximum-users + + + {{ 'tenant-profile.maximum-users-required' | translate}} + + + {{ 'tenant-profile.maximum-users-range' | translate}} + + + + tenant-profile.maximum-dashboards + + + {{ 'tenant-profile.maximum-dashboards-required' | translate}} + + + {{ 'tenant-profile.maximum-dashboards-range' | translate}} + + + + tenant-profile.maximum-rule-chains + + + {{ 'tenant-profile.maximum-rule-chains-required' | translate}} + + + {{ 'tenant-profile.maximum-rule-chains-range' | translate}} + + tenant-profile.max-transport-messages