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 | 146 | <artifactId>spring-boot-starter-websocket</artifactId> |
147 | 147 | </dependency> |
148 | 148 | <dependency> |
149 | - <groupId>org.springframework.cloud</groupId> | |
150 | - <artifactId>spring-cloud-starter-oauth2</artifactId> | |
151 | - </dependency> | |
152 | - <dependency> | |
153 | 149 | <groupId>org.springframework.security</groupId> |
154 | 150 | <artifactId>spring-security-oauth2-client</artifactId> |
155 | 151 | </dependency> | ... | ... |
... | ... | @@ -24,6 +24,10 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura |
24 | 24 | |
25 | 25 | private long maxDevices; |
26 | 26 | private long maxAssets; |
27 | + private long maxCustomers; | |
28 | + private long maxUsers; | |
29 | + private long maxDashboards; | |
30 | + private long maxRuleChains; | |
27 | 31 | |
28 | 32 | private String transportTenantMsgRateLimit; |
29 | 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 | 23 | import org.thingsboard.server.common.data.page.PageData; |
24 | 24 | import org.thingsboard.server.common.data.page.PageLink; |
25 | 25 | import org.thingsboard.server.dao.Dao; |
26 | +import org.thingsboard.server.dao.TenantEntityDao; | |
26 | 27 | |
27 | 28 | import java.util.List; |
28 | 29 | import java.util.Optional; |
... | ... | @@ -32,7 +33,7 @@ import java.util.UUID; |
32 | 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 | 39 | * Find asset info by id. |
... | ... | @@ -166,6 +167,4 @@ public interface AssetDao extends Dao<Asset> { |
166 | 167 | */ |
167 | 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 | 330 | DefaultTenantProfileConfiguration profileConfiguration = |
331 | 331 | (DefaultTenantProfileConfiguration)tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); |
332 | 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 | 336 | @Override | ... | ... |
... | ... | @@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.id.TenantId; |
20 | 20 | import org.thingsboard.server.common.data.page.PageData; |
21 | 21 | import org.thingsboard.server.common.data.page.PageLink; |
22 | 22 | import org.thingsboard.server.dao.Dao; |
23 | +import org.thingsboard.server.dao.TenantEntityDao; | |
23 | 24 | |
24 | 25 | import java.util.Optional; |
25 | 26 | import java.util.UUID; |
... | ... | @@ -27,7 +28,7 @@ import java.util.UUID; |
27 | 28 | /** |
28 | 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 | 34 | * Save or update customer object |
... | ... | @@ -54,5 +55,5 @@ public interface CustomerDao extends Dao<Customer> { |
54 | 55 | * @return the optional customer object |
55 | 56 | */ |
56 | 57 | Optional<Customer> findCustomersByTenantIdAndTitle(UUID tenantId, String title); |
57 | - | |
58 | + | |
58 | 59 | } | ... | ... |
... | ... | @@ -21,13 +21,16 @@ import com.google.common.util.concurrent.ListenableFuture; |
21 | 21 | import lombok.extern.slf4j.Slf4j; |
22 | 22 | import org.apache.commons.lang3.StringUtils; |
23 | 23 | import org.springframework.beans.factory.annotation.Autowired; |
24 | +import org.springframework.context.annotation.Lazy; | |
24 | 25 | import org.springframework.stereotype.Service; |
25 | 26 | import org.thingsboard.server.common.data.Customer; |
27 | +import org.thingsboard.server.common.data.EntityType; | |
26 | 28 | import org.thingsboard.server.common.data.Tenant; |
27 | 29 | import org.thingsboard.server.common.data.id.CustomerId; |
28 | 30 | import org.thingsboard.server.common.data.id.TenantId; |
29 | 31 | import org.thingsboard.server.common.data.page.PageData; |
30 | 32 | import org.thingsboard.server.common.data.page.PageLink; |
33 | +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; | |
31 | 34 | import org.thingsboard.server.dao.asset.AssetService; |
32 | 35 | import org.thingsboard.server.dao.dashboard.DashboardService; |
33 | 36 | import org.thingsboard.server.dao.device.DeviceService; |
... | ... | @@ -38,6 +41,7 @@ import org.thingsboard.server.dao.exception.IncorrectParameterException; |
38 | 41 | import org.thingsboard.server.dao.service.DataValidator; |
39 | 42 | import org.thingsboard.server.dao.service.PaginatedRemover; |
40 | 43 | import org.thingsboard.server.dao.service.Validator; |
44 | +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; | |
41 | 45 | import org.thingsboard.server.dao.tenant.TenantDao; |
42 | 46 | import org.thingsboard.server.dao.user.UserService; |
43 | 47 | |
... | ... | @@ -75,6 +79,10 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom |
75 | 79 | @Autowired |
76 | 80 | private DashboardService dashboardService; |
77 | 81 | |
82 | + @Autowired | |
83 | + @Lazy | |
84 | + private TbTenantProfileCache tenantProfileCache; | |
85 | + | |
78 | 86 | @Override |
79 | 87 | public Customer findCustomerById(TenantId tenantId, CustomerId customerId) { |
80 | 88 | log.trace("Executing findCustomerById [{}]", customerId); |
... | ... | @@ -162,6 +170,11 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom |
162 | 170 | |
163 | 171 | @Override |
164 | 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 | 178 | customerDao.findCustomersByTenantIdAndTitle(customer.getTenantId().getId(), customer.getTitle()).ifPresent( |
166 | 179 | c -> { |
167 | 180 | throw new DataValidationException("Customer with such title already exists!"); | ... | ... |
... | ... | @@ -18,11 +18,12 @@ package org.thingsboard.server.dao.dashboard; |
18 | 18 | import org.thingsboard.server.common.data.Dashboard; |
19 | 19 | import org.thingsboard.server.common.data.id.TenantId; |
20 | 20 | import org.thingsboard.server.dao.Dao; |
21 | +import org.thingsboard.server.dao.TenantEntityDao; | |
21 | 22 | |
22 | 23 | /** |
23 | 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 | 29 | * Save or update dashboard object |
... | ... | @@ -31,5 +32,4 @@ public interface DashboardDao extends Dao<Dashboard> { |
31 | 32 | * @return saved dashboard object |
32 | 33 | */ |
33 | 34 | Dashboard save(TenantId tenantId, Dashboard dashboard); |
34 | - | |
35 | 35 | } | ... | ... |
... | ... | @@ -19,10 +19,12 @@ import com.google.common.util.concurrent.ListenableFuture; |
19 | 19 | import lombok.extern.slf4j.Slf4j; |
20 | 20 | import org.apache.commons.lang3.StringUtils; |
21 | 21 | import org.springframework.beans.factory.annotation.Autowired; |
22 | +import org.springframework.context.annotation.Lazy; | |
22 | 23 | import org.springframework.stereotype.Service; |
23 | 24 | import org.thingsboard.server.common.data.Customer; |
24 | 25 | import org.thingsboard.server.common.data.Dashboard; |
25 | 26 | import org.thingsboard.server.common.data.DashboardInfo; |
27 | +import org.thingsboard.server.common.data.EntityType; | |
26 | 28 | import org.thingsboard.server.common.data.Tenant; |
27 | 29 | import org.thingsboard.server.common.data.id.CustomerId; |
28 | 30 | import org.thingsboard.server.common.data.id.DashboardId; |
... | ... | @@ -31,12 +33,14 @@ import org.thingsboard.server.common.data.page.PageData; |
31 | 33 | import org.thingsboard.server.common.data.page.PageLink; |
32 | 34 | import org.thingsboard.server.common.data.relation.EntityRelation; |
33 | 35 | import org.thingsboard.server.common.data.relation.RelationTypeGroup; |
36 | +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; | |
34 | 37 | import org.thingsboard.server.dao.customer.CustomerDao; |
35 | 38 | import org.thingsboard.server.dao.entity.AbstractEntityService; |
36 | 39 | import org.thingsboard.server.dao.exception.DataValidationException; |
37 | 40 | import org.thingsboard.server.dao.service.DataValidator; |
38 | 41 | import org.thingsboard.server.dao.service.PaginatedRemover; |
39 | 42 | import org.thingsboard.server.dao.service.Validator; |
43 | +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; | |
40 | 44 | import org.thingsboard.server.dao.tenant.TenantDao; |
41 | 45 | |
42 | 46 | import java.util.concurrent.ExecutionException; |
... | ... | @@ -61,6 +65,10 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb |
61 | 65 | @Autowired |
62 | 66 | private CustomerDao customerDao; |
63 | 67 | |
68 | + @Autowired | |
69 | + @Lazy | |
70 | + private TbTenantProfileCache tenantProfileCache; | |
71 | + | |
64 | 72 | @Override |
65 | 73 | public Dashboard findDashboardById(TenantId tenantId, DashboardId dashboardId) { |
66 | 74 | log.trace("Executing findDashboardById [{}]", dashboardId); |
... | ... | @@ -215,6 +223,14 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb |
215 | 223 | private DataValidator<Dashboard> dashboardValidator = |
216 | 224 | new DataValidator<Dashboard>() { |
217 | 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 | 234 | protected void validateDataImpl(TenantId tenantId, Dashboard dashboard) { |
219 | 235 | if (StringUtils.isEmpty(dashboard.getTitle())) { |
220 | 236 | throw new DataValidationException("Dashboard title should be specified!"); | ... | ... |
... | ... | @@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.id.TenantId; |
23 | 23 | import org.thingsboard.server.common.data.page.PageData; |
24 | 24 | import org.thingsboard.server.common.data.page.PageLink; |
25 | 25 | import org.thingsboard.server.dao.Dao; |
26 | +import org.thingsboard.server.dao.TenantEntityDao; | |
26 | 27 | |
27 | 28 | import java.util.List; |
28 | 29 | import java.util.Optional; |
... | ... | @@ -32,7 +33,7 @@ import java.util.UUID; |
32 | 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 | 39 | * Find device info by id. |
... | ... | @@ -203,8 +204,6 @@ public interface DeviceDao extends Dao<Device> { |
203 | 204 | */ |
204 | 205 | ListenableFuture<Device> findDeviceByTenantIdAndIdAsync(TenantId tenantId, UUID id); |
205 | 206 | |
206 | - Long countDevicesByTenantId(TenantId tenantId); | |
207 | - | |
208 | 207 | Long countDevicesByDeviceProfileId(TenantId tenantId, UUID deviceProfileId); |
209 | 208 | |
210 | 209 | /** | ... | ... |
... | ... | @@ -530,12 +530,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe |
530 | 530 | DefaultTenantProfileConfiguration profileConfiguration = |
531 | 531 | (DefaultTenantProfileConfiguration)tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); |
532 | 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 | 536 | @Override | ... | ... |
... | ... | @@ -37,12 +37,11 @@ public abstract class AbstractEntityService { |
37 | 37 | |
38 | 38 | protected Optional<ConstraintViolationException> extractConstraintViolationException(Exception t) { |
39 | 39 | if (t instanceof ConstraintViolationException) { |
40 | - return Optional.of ((ConstraintViolationException) t); | |
40 | + return Optional.of((ConstraintViolationException) t); | |
41 | 41 | } else if (t.getCause() instanceof ConstraintViolationException) { |
42 | - return Optional.of ((ConstraintViolationException) (t.getCause())); | |
42 | + return Optional.of((ConstraintViolationException) (t.getCause())); | |
43 | 43 | } else { |
44 | 44 | return Optional.empty(); |
45 | 45 | } |
46 | 46 | } |
47 | - | |
48 | 47 | } | ... | ... |
... | ... | @@ -24,6 +24,7 @@ import org.apache.commons.collections.CollectionUtils; |
24 | 24 | import org.apache.commons.lang3.StringUtils; |
25 | 25 | import org.hibernate.exception.ConstraintViolationException; |
26 | 26 | import org.springframework.beans.factory.annotation.Autowired; |
27 | +import org.springframework.context.annotation.Lazy; | |
27 | 28 | import org.springframework.stereotype.Service; |
28 | 29 | import org.thingsboard.server.common.data.BaseData; |
29 | 30 | import org.thingsboard.server.common.data.EntityType; |
... | ... | @@ -44,11 +45,13 @@ import org.thingsboard.server.common.data.rule.RuleChainData; |
44 | 45 | import org.thingsboard.server.common.data.rule.RuleChainImportResult; |
45 | 46 | import org.thingsboard.server.common.data.rule.RuleChainMetaData; |
46 | 47 | import org.thingsboard.server.common.data.rule.RuleNode; |
48 | +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; | |
47 | 49 | import org.thingsboard.server.dao.entity.AbstractEntityService; |
48 | 50 | import org.thingsboard.server.dao.exception.DataValidationException; |
49 | 51 | import org.thingsboard.server.dao.service.DataValidator; |
50 | 52 | import org.thingsboard.server.dao.service.PaginatedRemover; |
51 | 53 | import org.thingsboard.server.dao.service.Validator; |
54 | +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; | |
52 | 55 | import org.thingsboard.server.dao.tenant.TenantDao; |
53 | 56 | |
54 | 57 | import java.util.ArrayList; |
... | ... | @@ -81,6 +84,10 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC |
81 | 84 | @Autowired |
82 | 85 | private TenantDao tenantDao; |
83 | 86 | |
87 | + @Autowired | |
88 | + @Lazy | |
89 | + private TbTenantProfileCache tenantProfileCache; | |
90 | + | |
84 | 91 | @Override |
85 | 92 | public RuleChain saveRuleChain(RuleChain ruleChain) { |
86 | 93 | ruleChainValidator.validate(ruleChain, RuleChain::getTenantId); |
... | ... | @@ -581,6 +588,14 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC |
581 | 588 | private DataValidator<RuleChain> ruleChainValidator = |
582 | 589 | new DataValidator<RuleChain>() { |
583 | 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 | 599 | protected void validateDataImpl(TenantId tenantId, RuleChain ruleChain) { |
585 | 600 | if (StringUtils.isEmpty(ruleChain.getName())) { |
586 | 601 | throw new DataValidationException("Rule chain name should be specified!."); | ... | ... |
... | ... | @@ -19,13 +19,14 @@ import org.thingsboard.server.common.data.page.PageData; |
19 | 19 | import org.thingsboard.server.common.data.page.PageLink; |
20 | 20 | import org.thingsboard.server.common.data.rule.RuleChain; |
21 | 21 | import org.thingsboard.server.dao.Dao; |
22 | +import org.thingsboard.server.dao.TenantEntityDao; | |
22 | 23 | |
23 | 24 | import java.util.UUID; |
24 | 25 | |
25 | 26 | /** |
26 | 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 | 32 | * Find rule chains by tenantId and page link. |
... | ... | @@ -35,5 +36,4 @@ public interface RuleChainDao extends Dao<RuleChain> { |
35 | 36 | * @return the list of rule chain objects |
36 | 37 | */ |
37 | 38 | PageData<RuleChain> findRuleChainsByTenantId(UUID tenantId, PageLink pageLink); |
38 | - | |
39 | 39 | } | ... | ... |
... | ... | @@ -18,7 +18,9 @@ package org.thingsboard.server.dao.service; |
18 | 18 | import com.fasterxml.jackson.databind.JsonNode; |
19 | 19 | import lombok.extern.slf4j.Slf4j; |
20 | 20 | import org.thingsboard.server.common.data.BaseData; |
21 | +import org.thingsboard.server.common.data.EntityType; | |
21 | 22 | import org.thingsboard.server.common.data.id.TenantId; |
23 | +import org.thingsboard.server.dao.TenantEntityDao; | |
22 | 24 | import org.thingsboard.server.dao.exception.DataValidationException; |
23 | 25 | |
24 | 26 | import java.util.HashSet; |
... | ... | @@ -79,6 +81,19 @@ public abstract class DataValidator<D extends BaseData<?>> { |
79 | 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 | 97 | protected static void validateJsonStructure(JsonNode expectedNode, JsonNode actualNode) { |
83 | 98 | Set<String> expectedFields = new HashSet<>(); |
84 | 99 | Iterator<String> fieldsIterator = expectedNode.fieldNames(); | ... | ... |
... | ... | @@ -178,8 +178,7 @@ public class JpaAssetDao extends JpaAbstractSearchTextDao<AssetEntity, Asset> im |
178 | 178 | } |
179 | 179 | |
180 | 180 | @Override |
181 | - public Long countAssetsByTenantId(TenantId tenantId) { | |
181 | + public Long countByTenantId(TenantId tenantId) { | |
182 | 182 | return assetRepository.countByTenantId(tenantId.getId()); |
183 | - | |
184 | 183 | } |
185 | 184 | } | ... | ... |
... | ... | @@ -19,6 +19,7 @@ import org.springframework.beans.factory.annotation.Autowired; |
19 | 19 | import org.springframework.data.repository.CrudRepository; |
20 | 20 | import org.springframework.stereotype.Component; |
21 | 21 | import org.thingsboard.server.common.data.Customer; |
22 | +import org.thingsboard.server.common.data.id.TenantId; | |
22 | 23 | import org.thingsboard.server.common.data.page.PageData; |
23 | 24 | import org.thingsboard.server.common.data.page.PageLink; |
24 | 25 | import org.thingsboard.server.dao.DaoUtil; |
... | ... | @@ -62,4 +63,9 @@ public class JpaCustomerDao extends JpaAbstractSearchTextDao<CustomerEntity, Cus |
62 | 63 | Customer customer = DaoUtil.getData(customerRepository.findByTenantIdAndTitle(tenantId, title)); |
63 | 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 | } | ... | ... |
... | ... | @@ -19,6 +19,7 @@ import org.springframework.beans.factory.annotation.Autowired; |
19 | 19 | import org.springframework.data.repository.CrudRepository; |
20 | 20 | import org.springframework.stereotype.Component; |
21 | 21 | import org.thingsboard.server.common.data.Dashboard; |
22 | +import org.thingsboard.server.common.data.id.TenantId; | |
22 | 23 | import org.thingsboard.server.dao.dashboard.DashboardDao; |
23 | 24 | import org.thingsboard.server.dao.model.sql.DashboardEntity; |
24 | 25 | import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; |
... | ... | @@ -43,4 +44,9 @@ public class JpaDashboardDao extends JpaAbstractSearchTextDao<DashboardEntity, D |
43 | 44 | protected CrudRepository<DashboardEntity, UUID> getCrudRepository() { |
44 | 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 | 50 | "AND d.deviceProfileId = :profileId " + |
51 | 51 | "AND LOWER(d.searchText) LIKE LOWER(CONCAT(:searchText, '%'))") |
52 | 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 | 57 | @Query("SELECT new org.thingsboard.server.dao.model.sql.DeviceInfoEntity(d, c.title, c.additionalInfo, p.name) " + |
58 | 58 | "FROM DeviceEntity d " + |
... | ... | @@ -62,9 +62,9 @@ public interface DeviceRepository extends PagingAndSortingRepository<DeviceEntit |
62 | 62 | "AND d.customerId = :customerId " + |
63 | 63 | "AND LOWER(d.searchText) LIKE LOWER(CONCAT(:searchText, '%'))") |
64 | 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 | 69 | @Query("SELECT d FROM DeviceEntity d WHERE d.tenantId = :tenantId") |
70 | 70 | Page<DeviceEntity> findByTenantId(@Param("tenantId") UUID tenantId, |
... | ... | @@ -102,9 +102,9 @@ public interface DeviceRepository extends PagingAndSortingRepository<DeviceEntit |
102 | 102 | "AND d.type = :type " + |
103 | 103 | "AND LOWER(d.searchText) LIKE LOWER(CONCAT(:textSearch, '%'))") |
104 | 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 | 109 | @Query("SELECT new org.thingsboard.server.dao.model.sql.DeviceInfoEntity(d, c.title, c.additionalInfo, p.name) " + |
110 | 110 | "FROM DeviceEntity d " + |
... | ... | @@ -137,10 +137,10 @@ public interface DeviceRepository extends PagingAndSortingRepository<DeviceEntit |
137 | 137 | "AND d.type = :type " + |
138 | 138 | "AND LOWER(d.searchText) LIKE LOWER(CONCAT(:textSearch, '%'))") |
139 | 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 | 145 | @Query("SELECT new org.thingsboard.server.dao.model.sql.DeviceInfoEntity(d, c.title, c.additionalInfo, p.name) " + |
146 | 146 | "FROM DeviceEntity d " + | ... | ... |
... | ... | @@ -220,7 +220,7 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao<DeviceEntity, Device> |
220 | 220 | } |
221 | 221 | |
222 | 222 | @Override |
223 | - public Long countDevicesByTenantId(TenantId tenantId) { | |
223 | + public Long countByTenantId(TenantId tenantId) { | |
224 | 224 | return deviceRepository.countByTenantId(tenantId.getId()); |
225 | 225 | } |
226 | 226 | ... | ... |
... | ... | @@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j; |
19 | 19 | import org.springframework.beans.factory.annotation.Autowired; |
20 | 20 | import org.springframework.data.repository.CrudRepository; |
21 | 21 | import org.springframework.stereotype.Component; |
22 | +import org.thingsboard.server.common.data.id.TenantId; | |
22 | 23 | import org.thingsboard.server.common.data.page.PageData; |
23 | 24 | import org.thingsboard.server.common.data.page.PageLink; |
24 | 25 | import org.thingsboard.server.common.data.rule.RuleChain; |
... | ... | @@ -56,4 +57,8 @@ public class JpaRuleChainDao extends JpaAbstractSearchTextDao<RuleChainEntity, R |
56 | 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 | } | ... | ... |
... | ... | @@ -91,4 +91,9 @@ public class JpaUserDao extends JpaAbstractSearchTextDao<UserEntity, User> imple |
91 | 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 | } | ... | ... |
... | ... | @@ -20,10 +20,11 @@ import org.thingsboard.server.common.data.id.TenantId; |
20 | 20 | import org.thingsboard.server.common.data.page.PageData; |
21 | 21 | import org.thingsboard.server.common.data.page.PageLink; |
22 | 22 | import org.thingsboard.server.dao.Dao; |
23 | +import org.thingsboard.server.dao.TenantEntityDao; | |
23 | 24 | |
24 | 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 | 30 | * Save or update user object |
... | ... | @@ -49,7 +50,7 @@ public interface UserDao extends Dao<User> { |
49 | 50 | * @return the list of user entities |
50 | 51 | */ |
51 | 52 | PageData<User> findByTenantId(UUID tenantId, PageLink pageLink); |
52 | - | |
53 | + | |
53 | 54 | /** |
54 | 55 | * Find tenant admin users by tenantId and page link. |
55 | 56 | * |
... | ... | @@ -58,7 +59,7 @@ public interface UserDao extends Dao<User> { |
58 | 59 | * @return the list of user entities |
59 | 60 | */ |
60 | 61 | PageData<User> findTenantAdmins(UUID tenantId, PageLink pageLink); |
61 | - | |
62 | + | |
62 | 63 | /** |
63 | 64 | * Find customer users by tenantId, customerId and page link. |
64 | 65 | * |
... | ... | @@ -68,5 +69,4 @@ public interface UserDao extends Dao<User> { |
68 | 69 | * @return the list of user entities |
69 | 70 | */ |
70 | 71 | PageData<User> findCustomerUsers(UUID tenantId, UUID customerId, PageLink pageLink); |
71 | - | |
72 | 72 | } | ... | ... |
... | ... | @@ -24,8 +24,10 @@ import org.apache.commons.lang3.RandomStringUtils; |
24 | 24 | import org.apache.commons.lang3.StringUtils; |
25 | 25 | import org.springframework.beans.factory.annotation.Autowired; |
26 | 26 | import org.springframework.beans.factory.annotation.Value; |
27 | +import org.springframework.context.annotation.Lazy; | |
27 | 28 | import org.springframework.stereotype.Service; |
28 | 29 | import org.thingsboard.server.common.data.Customer; |
30 | +import org.thingsboard.server.common.data.EntityType; | |
29 | 31 | import org.thingsboard.server.common.data.Tenant; |
30 | 32 | import org.thingsboard.server.common.data.User; |
31 | 33 | import org.thingsboard.server.common.data.id.CustomerId; |
... | ... | @@ -36,6 +38,7 @@ import org.thingsboard.server.common.data.page.PageData; |
36 | 38 | import org.thingsboard.server.common.data.page.PageLink; |
37 | 39 | import org.thingsboard.server.common.data.security.Authority; |
38 | 40 | import org.thingsboard.server.common.data.security.UserCredentials; |
41 | +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; | |
39 | 42 | import org.thingsboard.server.dao.customer.CustomerDao; |
40 | 43 | import org.thingsboard.server.dao.entity.AbstractEntityService; |
41 | 44 | import org.thingsboard.server.dao.exception.DataValidationException; |
... | ... | @@ -43,6 +46,7 @@ import org.thingsboard.server.dao.exception.IncorrectParameterException; |
43 | 46 | import org.thingsboard.server.dao.model.ModelConstants; |
44 | 47 | import org.thingsboard.server.dao.service.DataValidator; |
45 | 48 | import org.thingsboard.server.dao.service.PaginatedRemover; |
49 | +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; | |
46 | 50 | import org.thingsboard.server.dao.tenant.TenantDao; |
47 | 51 | |
48 | 52 | import java.util.HashMap; |
... | ... | @@ -84,6 +88,10 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic |
84 | 88 | @Autowired |
85 | 89 | private CustomerDao customerDao; |
86 | 90 | |
91 | + @Autowired | |
92 | + @Lazy | |
93 | + private TbTenantProfileCache tenantProfileCache; | |
94 | + | |
87 | 95 | @Override |
88 | 96 | public User findUserByEmail(TenantId tenantId, String email) { |
89 | 97 | log.trace("Executing findUserByEmail [{}]", email); |
... | ... | @@ -365,6 +373,16 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic |
365 | 373 | private DataValidator<User> userValidator = |
366 | 374 | new DataValidator<User>() { |
367 | 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 | 386 | protected void validateDataImpl(TenantId requestTenantId, User user) { |
369 | 387 | if (StringUtils.isEmpty(user.getEmail())) { |
370 | 388 | throw new DataValidationException("User email should be specified!"); | ... | ... |
... | ... | @@ -36,12 +36,11 @@ |
36 | 36 | <pkg.implementationTitle>${project.name}</pkg.implementationTitle> |
37 | 37 | <pkg.unixLogFolder>/var/log/${pkg.name}</pkg.unixLogFolder> |
38 | 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 | 44 | <jjwt.version>0.7.0</jjwt.version> |
46 | 45 | <json-path.version>2.2.0</json-path.version> |
47 | 46 | <junit.version>4.12</junit.version> |
... | ... | @@ -52,15 +51,16 @@ |
52 | 51 | <cassandra.version>4.6.0</cassandra.version> |
53 | 52 | <metrics.version>4.0.5</metrics.version> |
54 | 53 | <cassandra-unit.version>4.3.1.0</cassandra-unit.version> |
54 | + <cassandra-all.version>3.11.9</cassandra-all.version> | |
55 | 55 | <takari-cpsuite.version>1.2.7</takari-cpsuite.version> |
56 | 56 | <guava.version>28.2-jre</guava.version> |
57 | 57 | <caffeine.version>2.6.1</caffeine.version> |
58 | 58 | <commons-lang3.version>3.4</commons-lang3.version> |
59 | 59 | <commons-io.version>2.5</commons-io.version> |
60 | 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 | 64 | <json-schema-validator.version>2.2.6</json-schema-validator.version> |
65 | 65 | <californium.version>1.0.2</californium.version> |
66 | 66 | <gson.version>2.6.2</gson.version> |
... | ... | @@ -72,7 +72,7 @@ |
72 | 72 | <grpc.version>1.22.1</grpc.version> |
73 | 73 | <lombok.version>1.16.18</lombok.version> |
74 | 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 | 76 | <os-maven-plugin.version>1.5.0</os-maven-plugin.version> |
77 | 77 | <rabbitmq.version>4.8.0</rabbitmq.version> |
78 | 78 | <surfire.version>2.19.1</surfire.version> |
... | ... | @@ -96,7 +96,7 @@ |
96 | 96 | <bucket4j.version>4.1.1</bucket4j.version> |
97 | 97 | <fst.version>2.57</fst.version> |
98 | 98 | <antlr.version>2.7.7</antlr.version> |
99 | - <snakeyaml.version>1.25</snakeyaml.version> | |
99 | + <snakeyaml.version>1.27</snakeyaml.version> | |
100 | 100 | <amazonaws.sqs.version>1.11.747</amazonaws.sqs.version> |
101 | 101 | <pubsub.client.version>1.105.0</pubsub.client.version> |
102 | 102 | <azure-servicebus.version>3.2.0</azure-servicebus.version> |
... | ... | @@ -873,11 +873,6 @@ |
873 | 873 | <version>${spring-boot.version}</version> |
874 | 874 | </dependency> |
875 | 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 | 876 | <groupId>org.springframework.security</groupId> |
882 | 877 | <artifactId>spring-security-oauth2-client</artifactId> |
883 | 878 | <version>${spring-security.version}</version> |
... | ... | @@ -1201,6 +1196,11 @@ |
1201 | 1196 | <scope>test</scope> |
1202 | 1197 | </dependency> |
1203 | 1198 | <dependency> |
1199 | + <groupId>org.apache.cassandra</groupId> | |
1200 | + <artifactId>cassandra-all</artifactId> | |
1201 | + <version>${cassandra-all.version}</version> | |
1202 | + </dependency> | |
1203 | + <dependency> | |
1204 | 1204 | <groupId>junit</groupId> |
1205 | 1205 | <artifactId>junit</artifactId> |
1206 | 1206 | <version>${junit.version}</version> | ... | ... |
... | ... | @@ -121,6 +121,11 @@ |
121 | 121 | <artifactId>jts-core</artifactId> |
122 | 122 | </dependency> |
123 | 123 | <dependency> |
124 | + <groupId>com.sun.mail</groupId> | |
125 | + <artifactId>javax.mail</artifactId> | |
126 | + <scope>provided</scope> | |
127 | + </dependency> | |
128 | + <dependency> | |
124 | 129 | <groupId>junit</groupId> |
125 | 130 | <artifactId>junit</artifactId> |
126 | 131 | <version>${junit.version}</version> | ... | ... |
... | ... | @@ -41,6 +41,54 @@ |
41 | 41 | </mat-error> |
42 | 42 | </mat-form-field> |
43 | 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 | 92 | <mat-label translate>tenant-profile.max-transport-messages</mat-label> |
45 | 93 | <input matInput required min="0" step="1" |
46 | 94 | formControlName="maxTransportMessages" | ... | ... |
... | ... | @@ -55,6 +55,10 @@ export class DefaultTenantProfileConfigurationComponent implements ControlValueA |
55 | 55 | this.defaultTenantProfileConfigurationFormGroup = this.fb.group({ |
56 | 56 | maxDevices: [null, [Validators.required, Validators.min(0)]], |
57 | 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 | 62 | transportTenantMsgRateLimit: [null, []], |
59 | 63 | transportTenantTelemetryMsgRateLimit: [null, []], |
60 | 64 | transportTenantTelemetryDataPointsRateLimit: [null, []], | ... | ... |
... | ... | @@ -487,7 +487,8 @@ export default abstract class LeafletMap { |
487 | 487 | } |
488 | 488 | |
489 | 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 | 492 | this.bounds = bounds; |
492 | 493 | this.fitBounds(bounds); |
493 | 494 | } | ... | ... |
... | ... | @@ -20,6 +20,7 @@ import { EntityType } from '@shared/models/entity-type.models'; |
20 | 20 | import tinycolor from 'tinycolor2'; |
21 | 21 | |
22 | 22 | export const DEFAULT_MAP_PAGE_SIZE = 16384; |
23 | +export const DEFAULT_ZOOM_LEVEL = 8; | |
23 | 24 | |
24 | 25 | export type GenericFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string; |
25 | 26 | export type MarkerImageFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string; |
... | ... | @@ -229,7 +230,6 @@ export const defaultSettings: any = { |
229 | 230 | strokeWeight: 2, |
230 | 231 | strokeOpacity: 1.0, |
231 | 232 | initCallback: () => { }, |
232 | - defaultZoomLevel: 8, | |
233 | 233 | disableScrollZooming: false, |
234 | 234 | minZoomLevel: 16, |
235 | 235 | credentials: '', | ... | ... |
... | ... | @@ -17,7 +17,7 @@ |
17 | 17 | |
18 | 18 | import L from 'leaflet'; |
19 | 19 | import LeafletMap from '../leaflet-map'; |
20 | -import { UnitedMapSettings } from '../map-models'; | |
20 | +import { DEFAULT_ZOOM_LEVEL, UnitedMapSettings } from '../map-models'; | |
21 | 21 | import 'leaflet.gridlayer.googlemutant'; |
22 | 22 | import { ResourcesService } from '@core/services/resources.service'; |
23 | 23 | import { WidgetContext } from '@home/models/widget-component.models'; |
... | ... | @@ -39,7 +39,7 @@ export class GoogleMap extends LeafletMap { |
39 | 39 | const map = L.map($container, { |
40 | 40 | attributionControl: false, |
41 | 41 | editable: !!options.editablePolygon |
42 | - }).setView(options?.defaultCenterPosition, options?.defaultZoomLevel); | |
42 | + }).setView(options?.defaultCenterPosition, options?.defaultZoomLevel || DEFAULT_ZOOM_LEVEL); | |
43 | 43 | (L.gridLayer as any).googleMutant({ |
44 | 44 | type: options?.gmDefaultMapType || 'roadmap' |
45 | 45 | }).addTo(map); | ... | ... |
... | ... | @@ -16,7 +16,7 @@ |
16 | 16 | |
17 | 17 | import L from 'leaflet'; |
18 | 18 | import LeafletMap from '../leaflet-map'; |
19 | -import { UnitedMapSettings } from '../map-models'; | |
19 | +import { DEFAULT_ZOOM_LEVEL, UnitedMapSettings } from '../map-models'; | |
20 | 20 | import { WidgetContext } from '@home/models/widget-component.models'; |
21 | 21 | |
22 | 22 | export class HEREMap extends LeafletMap { |
... | ... | @@ -24,7 +24,7 @@ export class HEREMap extends LeafletMap { |
24 | 24 | super(ctx, $container, options); |
25 | 25 | const map = L.map($container, { |
26 | 26 | editable: !!options.editablePolygon |
27 | - }).setView(options?.defaultCenterPosition, options?.defaultZoomLevel); | |
27 | + }).setView(options?.defaultCenterPosition, options?.defaultZoomLevel || DEFAULT_ZOOM_LEVEL); | |
28 | 28 | const tileLayer = (L.tileLayer as any).provider(options.mapProviderHere || 'HERE.normalDay', options.credentials); |
29 | 29 | tileLayer.addTo(map); |
30 | 30 | super.initSettings(options); | ... | ... |
... | ... | @@ -16,7 +16,7 @@ |
16 | 16 | |
17 | 17 | import L from 'leaflet'; |
18 | 18 | import LeafletMap from '../leaflet-map'; |
19 | -import { UnitedMapSettings } from '../map-models'; | |
19 | +import { DEFAULT_ZOOM_LEVEL, UnitedMapSettings } from '../map-models'; | |
20 | 20 | import { WidgetContext } from '@home/models/widget-component.models'; |
21 | 21 | |
22 | 22 | export class OpenStreetMap extends LeafletMap { |
... | ... | @@ -24,7 +24,7 @@ export class OpenStreetMap extends LeafletMap { |
24 | 24 | super(ctx, $container, options); |
25 | 25 | const map = L.map($container, { |
26 | 26 | editable: !!options.editablePolygon |
27 | - }).setView(options?.defaultCenterPosition, options?.defaultZoomLevel); | |
27 | + }).setView(options?.defaultCenterPosition, options?.defaultZoomLevel || DEFAULT_ZOOM_LEVEL); | |
28 | 28 | let tileLayer; |
29 | 29 | if (options.useCustomProvider) { |
30 | 30 | tileLayer = L.tileLayer(options.customProviderTileUrl); | ... | ... |
... | ... | @@ -17,7 +17,7 @@ |
17 | 17 | |
18 | 18 | import L from 'leaflet'; |
19 | 19 | import LeafletMap from '../leaflet-map'; |
20 | -import { UnitedMapSettings } from '../map-models'; | |
20 | +import { DEFAULT_ZOOM_LEVEL, UnitedMapSettings } from '../map-models'; | |
21 | 21 | import { WidgetContext } from '@home/models/widget-component.models'; |
22 | 22 | |
23 | 23 | export class TencentMap extends LeafletMap { |
... | ... | @@ -26,7 +26,7 @@ export class TencentMap extends LeafletMap { |
26 | 26 | const txUrl = 'http://rt{s}.map.gtimg.com/realtimerender?z={z}&x={x}&y={y}&type=vector&style=0'; |
27 | 27 | const map = L.map($container, { |
28 | 28 | editable: !!options.editablePolygon |
29 | - }).setView(options?.defaultCenterPosition, options?.defaultZoomLevel); | |
29 | + }).setView(options?.defaultCenterPosition, options?.defaultZoomLevel || DEFAULT_ZOOM_LEVEL); | |
30 | 30 | const txLayer = L.tileLayer(txUrl, { |
31 | 31 | subdomains: '0123', |
32 | 32 | tms: true, | ... | ... |
... | ... | @@ -56,6 +56,12 @@ export const customerHref = '<a href="https://github.com/thingsboard/thingsboard |
56 | 56 | |
57 | 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 | 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 | 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 | 1086 | ], |
1081 | 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 | 1108 | entityService: { | ... | ... |
... | ... | @@ -26,6 +26,10 @@ export enum TenantProfileType { |
26 | 26 | export interface DefaultTenantProfileConfiguration { |
27 | 27 | maxDevices: number; |
28 | 28 | maxAssets: number; |
29 | + maxCustomers: number; | |
30 | + maxUsers: number; | |
31 | + maxDashboards: number; | |
32 | + maxRuleChains: number; | |
29 | 33 | |
30 | 34 | transportTenantMsgRateLimit?: string; |
31 | 35 | transportTenantTelemetryMsgRateLimit?: string; |
... | ... | @@ -56,6 +60,10 @@ export function createTenantProfileConfiguration(type: TenantProfileType): Tenan |
56 | 60 | const defaultConfiguration: DefaultTenantProfileConfiguration = { |
57 | 61 | maxDevices: 0, |
58 | 62 | maxAssets: 0, |
63 | + maxCustomers: 0, | |
64 | + maxUsers: 0, | |
65 | + maxDashboards: 0, | |
66 | + maxRuleChains: 0, | |
59 | 67 | maxTransportMessages: 0, |
60 | 68 | maxTransportDataPoints: 0, |
61 | 69 | maxREExecutions: 0, | ... | ... |
... | ... | @@ -1946,6 +1946,18 @@ |
1946 | 1946 | "maximum-assets": "Maximum number of assets (0 - unlimited)", |
1947 | 1947 | "maximum-assets-required": "Maximum number of assets is required.", |
1948 | 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 | 1961 | "transport-tenant-msg-rate-limit": "Transport tenant messages rate limit.", |
1950 | 1962 | "transport-tenant-telemetry-msg-rate-limit": "Transport tenant telemetry messages rate limit.", |
1951 | 1963 | "transport-tenant-telemetry-data-points-rate-limit": "Transport tenant telemetry data points rate limit.", | ... | ... |