Commit 10e6b9a458d7d49596f3fc89ead24bbec6f3c74b
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<string>`, 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.", |