Commit 861fd651096ac37a4d72a2ebdfe77fd59b4776fe

Authored by ShvaykaD
2 parents 4a187813 35a21079

merge with master

Showing 49 changed files with 360 additions and 98 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>
@@ -35,14 +35,17 @@ import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; @@ -35,14 +35,17 @@ import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo;
35 import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; 35 import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig;
36 import org.thingsboard.server.common.data.page.PageData; 36 import org.thingsboard.server.common.data.page.PageData;
37 import org.thingsboard.server.common.data.page.PageLink; 37 import org.thingsboard.server.common.data.page.PageLink;
  38 +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
38 import org.thingsboard.server.common.data.security.Authority; 39 import org.thingsboard.server.common.data.security.Authority;
39 import org.thingsboard.server.common.data.security.UserCredentials; 40 import org.thingsboard.server.common.data.security.UserCredentials;
40 import org.thingsboard.server.dao.customer.CustomerService; 41 import org.thingsboard.server.dao.customer.CustomerService;
41 import org.thingsboard.server.dao.dashboard.DashboardService; 42 import org.thingsboard.server.dao.dashboard.DashboardService;
42 import org.thingsboard.server.dao.oauth2.OAuth2User; 43 import org.thingsboard.server.dao.oauth2.OAuth2User;
  44 +import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
43 import org.thingsboard.server.dao.tenant.TenantService; 45 import org.thingsboard.server.dao.tenant.TenantService;
44 import org.thingsboard.server.dao.user.UserService; 46 import org.thingsboard.server.dao.user.UserService;
45 import org.thingsboard.server.service.install.InstallScripts; 47 import org.thingsboard.server.service.install.InstallScripts;
  48 +import org.thingsboard.server.service.queue.TbClusterService;
46 import org.thingsboard.server.service.security.model.SecurityUser; 49 import org.thingsboard.server.service.security.model.SecurityUser;
47 import org.thingsboard.server.service.security.model.UserPrincipal; 50 import org.thingsboard.server.service.security.model.UserPrincipal;
48 51
@@ -76,6 +79,12 @@ public abstract class AbstractOAuth2ClientMapper { @@ -76,6 +79,12 @@ public abstract class AbstractOAuth2ClientMapper {
76 @Autowired 79 @Autowired
77 private InstallScripts installScripts; 80 private InstallScripts installScripts;
78 81
  82 + @Autowired
  83 + protected TbTenantProfileCache tenantProfileCache;
  84 +
  85 + @Autowired
  86 + protected TbClusterService tbClusterService;
  87 +
79 private final Lock userCreationLock = new ReentrantLock(); 88 private final Lock userCreationLock = new ReentrantLock();
80 89
81 protected SecurityUser getOrCreateSecurityUserFromOAuth2User(OAuth2User oauth2User, OAuth2ClientRegistrationInfo clientRegistration) { 90 protected SecurityUser getOrCreateSecurityUserFromOAuth2User(OAuth2User oauth2User, OAuth2ClientRegistrationInfo clientRegistration) {
@@ -162,6 +171,10 @@ public abstract class AbstractOAuth2ClientMapper { @@ -162,6 +171,10 @@ public abstract class AbstractOAuth2ClientMapper {
162 tenant.setTitle(tenantName); 171 tenant.setTitle(tenantName);
163 tenant = tenantService.saveTenant(tenant); 172 tenant = tenantService.saveTenant(tenant);
164 installScripts.createDefaultRuleChains(tenant.getId()); 173 installScripts.createDefaultRuleChains(tenant.getId());
  174 + tenantProfileCache.evict(tenant.getId());
  175 + tbClusterService.onTenantChange(tenant, null);
  176 + tbClusterService.onEntityStateChange(tenant.getId(), tenant.getId(),
  177 + ComponentLifecycleEvent.CREATED);
165 } else { 178 } else {
166 tenant = tenants.get(0); 179 tenant = tenants.get(0);
167 } 180 }
@@ -15,10 +15,15 @@ @@ -15,10 +15,15 @@
15 */ 15 */
16 package org.thingsboard.server.service.security.auth.oauth2; 16 package org.thingsboard.server.service.security.auth.oauth2;
17 17
  18 +import org.springframework.beans.factory.annotation.Autowired;
18 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 19 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
19 import org.springframework.security.core.AuthenticationException; 20 import org.springframework.security.core.AuthenticationException;
20 import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; 21 import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
21 import org.springframework.stereotype.Component; 22 import org.springframework.stereotype.Component;
  23 +import org.thingsboard.server.common.data.id.CustomerId;
  24 +import org.thingsboard.server.common.data.id.EntityId;
  25 +import org.thingsboard.server.common.data.id.TenantId;
  26 +import org.thingsboard.server.service.security.system.SystemSecurityService;
22 import org.thingsboard.server.utils.MiscUtils; 27 import org.thingsboard.server.utils.MiscUtils;
23 28
24 import javax.servlet.ServletException; 29 import javax.servlet.ServletException;
@@ -32,11 +37,18 @@ import java.nio.charset.StandardCharsets; @@ -32,11 +37,18 @@ import java.nio.charset.StandardCharsets;
32 @ConditionalOnProperty(prefix = "security.oauth2", value = "enabled", havingValue = "true") 37 @ConditionalOnProperty(prefix = "security.oauth2", value = "enabled", havingValue = "true")
33 public class Oauth2AuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler { 38 public class Oauth2AuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
34 39
  40 + private final SystemSecurityService systemSecurityService;
  41 +
  42 + @Autowired
  43 + public Oauth2AuthenticationFailureHandler(final SystemSecurityService systemSecurityService) {
  44 + this.systemSecurityService = systemSecurityService;
  45 + }
  46 +
35 @Override 47 @Override
36 public void onAuthenticationFailure(HttpServletRequest request, 48 public void onAuthenticationFailure(HttpServletRequest request,
37 HttpServletResponse response, AuthenticationException exception) 49 HttpServletResponse response, AuthenticationException exception)
38 throws IOException, ServletException { 50 throws IOException, ServletException {
39 - String baseUrl = MiscUtils.constructBaseUrl(request); 51 + String baseUrl = this.systemSecurityService.getBaseUrl(TenantId.SYS_TENANT_ID, new CustomerId(EntityId.NULL_UUID), request);
40 getRedirectStrategy().sendRedirect(request, response, baseUrl + "/login?loginError=" + 52 getRedirectStrategy().sendRedirect(request, response, baseUrl + "/login?loginError=" +
41 URLEncoder.encode(exception.getMessage(), StandardCharsets.UTF_8.toString())); 53 URLEncoder.encode(exception.getMessage(), StandardCharsets.UTF_8.toString()));
42 } 54 }
@@ -22,12 +22,16 @@ import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; @@ -22,12 +22,16 @@ import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
22 import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; 22 import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
23 import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; 23 import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
24 import org.springframework.stereotype.Component; 24 import org.springframework.stereotype.Component;
  25 +import org.thingsboard.server.common.data.id.CustomerId;
  26 +import org.thingsboard.server.common.data.id.EntityId;
  27 +import org.thingsboard.server.common.data.id.TenantId;
25 import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; 28 import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo;
26 import org.thingsboard.server.dao.oauth2.OAuth2Service; 29 import org.thingsboard.server.dao.oauth2.OAuth2Service;
27 import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository; 30 import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository;
28 import org.thingsboard.server.service.security.model.SecurityUser; 31 import org.thingsboard.server.service.security.model.SecurityUser;
29 import org.thingsboard.server.service.security.model.token.JwtToken; 32 import org.thingsboard.server.service.security.model.token.JwtToken;
30 import org.thingsboard.server.service.security.model.token.JwtTokenFactory; 33 import org.thingsboard.server.service.security.model.token.JwtTokenFactory;
  34 +import org.thingsboard.server.service.security.system.SystemSecurityService;
31 import org.thingsboard.server.utils.MiscUtils; 35 import org.thingsboard.server.utils.MiscUtils;
32 36
33 import javax.servlet.http.HttpServletRequest; 37 import javax.servlet.http.HttpServletRequest;
@@ -45,25 +49,27 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS @@ -45,25 +49,27 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS
45 private final OAuth2ClientMapperProvider oauth2ClientMapperProvider; 49 private final OAuth2ClientMapperProvider oauth2ClientMapperProvider;
46 private final OAuth2Service oAuth2Service; 50 private final OAuth2Service oAuth2Service;
47 private final OAuth2AuthorizedClientService oAuth2AuthorizedClientService; 51 private final OAuth2AuthorizedClientService oAuth2AuthorizedClientService;
  52 + private final SystemSecurityService systemSecurityService;
48 53
49 @Autowired 54 @Autowired
50 public Oauth2AuthenticationSuccessHandler(final JwtTokenFactory tokenFactory, 55 public Oauth2AuthenticationSuccessHandler(final JwtTokenFactory tokenFactory,
51 final RefreshTokenRepository refreshTokenRepository, 56 final RefreshTokenRepository refreshTokenRepository,
52 final OAuth2ClientMapperProvider oauth2ClientMapperProvider, 57 final OAuth2ClientMapperProvider oauth2ClientMapperProvider,
53 final OAuth2Service oAuth2Service, 58 final OAuth2Service oAuth2Service,
54 - final OAuth2AuthorizedClientService oAuth2AuthorizedClientService) { 59 + final OAuth2AuthorizedClientService oAuth2AuthorizedClientService, final SystemSecurityService systemSecurityService) {
55 this.tokenFactory = tokenFactory; 60 this.tokenFactory = tokenFactory;
56 this.refreshTokenRepository = refreshTokenRepository; 61 this.refreshTokenRepository = refreshTokenRepository;
57 this.oauth2ClientMapperProvider = oauth2ClientMapperProvider; 62 this.oauth2ClientMapperProvider = oauth2ClientMapperProvider;
58 this.oAuth2Service = oAuth2Service; 63 this.oAuth2Service = oAuth2Service;
59 this.oAuth2AuthorizedClientService = oAuth2AuthorizedClientService; 64 this.oAuth2AuthorizedClientService = oAuth2AuthorizedClientService;
  65 + this.systemSecurityService = systemSecurityService;
60 } 66 }
61 67
62 @Override 68 @Override
63 public void onAuthenticationSuccess(HttpServletRequest request, 69 public void onAuthenticationSuccess(HttpServletRequest request,
64 HttpServletResponse response, 70 HttpServletResponse response,
65 Authentication authentication) throws IOException { 71 Authentication authentication) throws IOException {
66 - String baseUrl = MiscUtils.constructBaseUrl(request); 72 + String baseUrl = this.systemSecurityService.getBaseUrl(TenantId.SYS_TENANT_ID, new CustomerId(EntityId.NULL_UUID), request);
67 try { 73 try {
68 OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) authentication; 74 OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) authentication;
69 75
@@ -202,16 +202,19 @@ public class DefaultSystemSecurityService implements SystemSecurityService { @@ -202,16 +202,19 @@ public class DefaultSystemSecurityService implements SystemSecurityService {
202 202
203 @Override 203 @Override
204 public String getBaseUrl(TenantId tenantId, CustomerId customerId, HttpServletRequest httpServletRequest) { 204 public String getBaseUrl(TenantId tenantId, CustomerId customerId, HttpServletRequest httpServletRequest) {
205 - String baseUrl; 205 + String baseUrl = null;
206 AdminSettings generalSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "general"); 206 AdminSettings generalSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "general");
207 207
208 JsonNode prohibitDifferentUrl = generalSettings.getJsonValue().get("prohibitDifferentUrl"); 208 JsonNode prohibitDifferentUrl = generalSettings.getJsonValue().get("prohibitDifferentUrl");
209 209
210 if (prohibitDifferentUrl != null && prohibitDifferentUrl.asBoolean()) { 210 if (prohibitDifferentUrl != null && prohibitDifferentUrl.asBoolean()) {
211 baseUrl = generalSettings.getJsonValue().get("baseUrl").asText(); 211 baseUrl = generalSettings.getJsonValue().get("baseUrl").asText();
212 - } else { 212 + }
  213 +
  214 + if (StringUtils.isEmpty(baseUrl)) {
213 baseUrl = MiscUtils.constructBaseUrl(httpServletRequest); 215 baseUrl = MiscUtils.constructBaseUrl(httpServletRequest);
214 } 216 }
  217 +
215 return baseUrl; 218 return baseUrl;
216 } 219 }
217 220
@@ -108,7 +108,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer @@ -108,7 +108,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer
108 * Since number of subscriptions is usually much less then number of devices that are pushing data. 108 * Since number of subscriptions is usually much less then number of devices that are pushing data.
109 */ 109 */
110 subscriptionsBySessionId.values().forEach(map -> map.values() 110 subscriptionsBySessionId.values().forEach(map -> map.values()
111 - .forEach(sub -> pushSubscriptionToManagerService(sub, false))); 111 + .forEach(sub -> pushSubscriptionToManagerService(sub, true)));
112 } 112 }
113 } 113 }
114 114
@@ -165,7 +165,7 @@ public class DefaultTransportApiService implements TransportApiService { @@ -165,7 +165,7 @@ public class DefaultTransportApiService implements TransportApiService {
165 165
166 private ListenableFuture<TransportApiResponseMsg> validateCredentials(TransportProtos.ValidateBasicMqttCredRequestMsg mqtt) { 166 private ListenableFuture<TransportApiResponseMsg> validateCredentials(TransportProtos.ValidateBasicMqttCredRequestMsg mqtt) {
167 DeviceCredentials credentials = null; 167 DeviceCredentials credentials = null;
168 - if (mqtt.getUserName() != null) { 168 + if (!StringUtils.isEmpty(mqtt.getUserName())) {
169 credentials = deviceCredentialsService.findDeviceCredentialsByCredentialsId(mqtt.getUserName()); 169 credentials = deviceCredentialsService.findDeviceCredentialsByCredentialsId(mqtt.getUserName());
170 if (credentials != null) { 170 if (credentials != null) {
171 if (credentials.getCredentialsType() == DeviceCredentialsType.ACCESS_TOKEN) { 171 if (credentials.getCredentialsType() == DeviceCredentialsType.ACCESS_TOKEN) {
@@ -21,5 +21,5 @@ import java.io.Serializable; @@ -21,5 +21,5 @@ import java.io.Serializable;
21 * @author Andrew Shvayka 21 * @author Andrew Shvayka
22 */ 22 */
23 public enum ComponentLifecycleEvent implements Serializable { 23 public enum ComponentLifecycleEvent implements Serializable {
24 - CREATED, STARTED, ACTIVATED, SUSPENDED, UPDATED, STOPPED, DELETED, ADDED_TO_ALLOW_LIST, ADDED_TO_DENY_LIST 24 + CREATED, STARTED, ACTIVATED, SUSPENDED, UPDATED, STOPPED, DELETED
25 } 25 }
@@ -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;
@@ -390,7 +390,8 @@ public class DefaultTransportService implements TransportService { @@ -390,7 +390,8 @@ public class DefaultTransportService implements TransportService {
390 metaData.putValue("deviceName", sessionInfo.getDeviceName()); 390 metaData.putValue("deviceName", sessionInfo.getDeviceName());
391 metaData.putValue("deviceType", sessionInfo.getDeviceType()); 391 metaData.putValue("deviceType", sessionInfo.getDeviceType());
392 metaData.putValue("notifyDevice", "false"); 392 metaData.putValue("notifyDevice", "false");
393 - sendToRuleEngine(tenantId, deviceId, sessionInfo, json, metaData, SessionMsgType.POST_ATTRIBUTES_REQUEST, new TransportTbQueueCallback(callback)); 393 + sendToRuleEngine(tenantId, deviceId, sessionInfo, json, metaData, SessionMsgType.POST_ATTRIBUTES_REQUEST,
  394 + new TransportTbQueueCallback(new ApiStatsProxyCallback<>(tenantId, msg.getKvList().size(), callback)));
394 } 395 }
395 } 396 }
396 397
@@ -399,7 +400,7 @@ public class DefaultTransportService implements TransportService { @@ -399,7 +400,7 @@ public class DefaultTransportService implements TransportService {
399 if (checkLimits(sessionInfo, msg, callback)) { 400 if (checkLimits(sessionInfo, msg, callback)) {
400 reportActivityInternal(sessionInfo); 401 reportActivityInternal(sessionInfo);
401 sendToDeviceActor(sessionInfo, TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) 402 sendToDeviceActor(sessionInfo, TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo)
402 - .setGetAttributes(msg).build(), callback); 403 + .setGetAttributes(msg).build(), new ApiStatsProxyCallback<>(getTenantId(sessionInfo), 1, callback));
403 } 404 }
404 } 405 }
405 406
@@ -409,7 +410,7 @@ public class DefaultTransportService implements TransportService { @@ -409,7 +410,7 @@ public class DefaultTransportService implements TransportService {
409 SessionMetaData sessionMetaData = reportActivityInternal(sessionInfo); 410 SessionMetaData sessionMetaData = reportActivityInternal(sessionInfo);
410 sessionMetaData.setSubscribedToAttributes(!msg.getUnsubscribe()); 411 sessionMetaData.setSubscribedToAttributes(!msg.getUnsubscribe());
411 sendToDeviceActor(sessionInfo, TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) 412 sendToDeviceActor(sessionInfo, TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo)
412 - .setSubscribeToAttributes(msg).build(), callback); 413 + .setSubscribeToAttributes(msg).build(), new ApiStatsProxyCallback<>(getTenantId(sessionInfo), 1, callback));
413 } 414 }
414 } 415 }
415 416
@@ -419,7 +420,7 @@ public class DefaultTransportService implements TransportService { @@ -419,7 +420,7 @@ public class DefaultTransportService implements TransportService {
419 SessionMetaData sessionMetaData = reportActivityInternal(sessionInfo); 420 SessionMetaData sessionMetaData = reportActivityInternal(sessionInfo);
420 sessionMetaData.setSubscribedToRPC(!msg.getUnsubscribe()); 421 sessionMetaData.setSubscribedToRPC(!msg.getUnsubscribe());
421 sendToDeviceActor(sessionInfo, TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) 422 sendToDeviceActor(sessionInfo, TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo)
422 - .setSubscribeToRPC(msg).build(), callback); 423 + .setSubscribeToRPC(msg).build(), new ApiStatsProxyCallback<>(getTenantId(sessionInfo), 1, callback));
423 } 424 }
424 } 425 }
425 426
@@ -428,7 +429,7 @@ public class DefaultTransportService implements TransportService { @@ -428,7 +429,7 @@ public class DefaultTransportService implements TransportService {
428 if (checkLimits(sessionInfo, msg, callback)) { 429 if (checkLimits(sessionInfo, msg, callback)) {
429 reportActivityInternal(sessionInfo); 430 reportActivityInternal(sessionInfo);
430 sendToDeviceActor(sessionInfo, TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) 431 sendToDeviceActor(sessionInfo, TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo)
431 - .setToDeviceRPCCallResponse(msg).build(), callback); 432 + .setToDeviceRPCCallResponse(msg).build(), new ApiStatsProxyCallback<>(getTenantId(sessionInfo), 1, callback));
432 } 433 }
433 } 434 }
434 435
@@ -805,7 +806,7 @@ public class DefaultTransportService implements TransportService { @@ -805,7 +806,7 @@ public class DefaultTransportService implements TransportService {
805 806
806 @Override 807 @Override
807 public void onFailure(Throwable t) { 808 public void onFailure(Throwable t) {
808 - callback.onError(t); 809 + DefaultTransportService.this.transportCallbackExecutor.submit(() -> callback.onError(t));
809 } 810 }
810 } 811 }
811 812
  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
@@ -202,6 +202,9 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { @@ -202,6 +202,9 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
202 " THEN (select additional_info from entity_view where id = entity_id)" + 202 " THEN (select additional_info from entity_view where id = entity_id)" +
203 " END as additional_info"; 203 " END as additional_info";
204 204
  205 + private static final String SELECT_API_USAGE_STATE = "(select aus.id, aus.created_time, aus.tenant_id, '13814000-1dd2-11b2-8080-808080808080'::uuid as customer_id, " +
  206 + "(select title from tenant where id = aus.tenant_id) as name from api_usage_state as aus)";
  207 +
205 static { 208 static {
206 entityTableMap.put(EntityType.ASSET, "asset"); 209 entityTableMap.put(EntityType.ASSET, "asset");
207 entityTableMap.put(EntityType.DEVICE, "device"); 210 entityTableMap.put(EntityType.DEVICE, "device");
@@ -210,7 +213,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { @@ -210,7 +213,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
210 entityTableMap.put(EntityType.CUSTOMER, "customer"); 213 entityTableMap.put(EntityType.CUSTOMER, "customer");
211 entityTableMap.put(EntityType.USER, "tb_user"); 214 entityTableMap.put(EntityType.USER, "tb_user");
212 entityTableMap.put(EntityType.TENANT, "tenant"); 215 entityTableMap.put(EntityType.TENANT, "tenant");
213 - entityTableMap.put(EntityType.API_USAGE_STATE, "api_usage_state"); 216 + entityTableMap.put(EntityType.API_USAGE_STATE, SELECT_API_USAGE_STATE);
214 } 217 }
215 218
216 public static EntityType[] RELATION_QUERY_ENTITY_TYPES = new EntityType[]{ 219 public static EntityType[] RELATION_QUERY_ENTITY_TYPES = new EntityType[]{
@@ -80,7 +80,7 @@ public class EntityKeyMapping { @@ -80,7 +80,7 @@ public class EntityKeyMapping {
80 public static final List<String> labeledEntityFields = Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME, TYPE, LABEL, ADDITIONAL_INFO); 80 public static final List<String> labeledEntityFields = Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME, TYPE, LABEL, ADDITIONAL_INFO);
81 public static final List<String> contactBasedEntityFields = Arrays.asList(CREATED_TIME, ENTITY_TYPE, EMAIL, TITLE, COUNTRY, STATE, CITY, ADDRESS, ADDRESS_2, ZIP, PHONE, ADDITIONAL_INFO); 81 public static final List<String> contactBasedEntityFields = Arrays.asList(CREATED_TIME, ENTITY_TYPE, EMAIL, TITLE, COUNTRY, STATE, CITY, ADDRESS, ADDRESS_2, ZIP, PHONE, ADDITIONAL_INFO);
82 82
83 - public static final Set<String> apiUsageStateEntityFields = Collections.singleton(CREATED_TIME); 83 + public static final Set<String> apiUsageStateEntityFields = new HashSet<>(Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME));
84 public static final Set<String> commonEntityFieldsSet = new HashSet<>(commonEntityFields); 84 public static final Set<String> commonEntityFieldsSet = new HashSet<>(commonEntityFields);
85 public static final Set<String> relationQueryEntityFieldsSet = new HashSet<>(Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME, TYPE, LABEL, FIRST_NAME, LAST_NAME, EMAIL, REGION, TITLE, COUNTRY, STATE, CITY, ADDRESS, ADDRESS_2, ZIP, PHONE, ADDITIONAL_INFO)); 85 public static final Set<String> relationQueryEntityFieldsSet = new HashSet<>(Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME, TYPE, LABEL, FIRST_NAME, LAST_NAME, EMAIL, REGION, TITLE, COUNTRY, STATE, CITY, ADDRESS, ADDRESS_2, ZIP, PHONE, ADDITIONAL_INFO));
86 86
@@ -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>
@@ -875,11 +875,6 @@ @@ -875,11 +875,6 @@
875 <version>${spring-boot.version}</version> 875 <version>${spring-boot.version}</version>
876 </dependency> 876 </dependency>
877 <dependency> 877 <dependency>
878 - <groupId>org.springframework.cloud</groupId>  
879 - <artifactId>spring-cloud-starter-oauth2</artifactId>  
880 - <version>${spring-oauth2.version}</version>  
881 - </dependency>  
882 - <dependency>  
883 <groupId>org.springframework.security</groupId> 878 <groupId>org.springframework.security</groupId>
884 <artifactId>spring-security-oauth2-client</artifactId> 879 <artifactId>spring-security-oauth2-client</artifactId>
885 <version>${spring-security.version}</version> 880 <version>${spring-security.version}</version>
@@ -1203,6 +1198,11 @@ @@ -1203,6 +1198,11 @@
1203 <scope>test</scope> 1198 <scope>test</scope>
1204 </dependency> 1199 </dependency>
1205 <dependency> 1200 <dependency>
  1201 + <groupId>org.apache.cassandra</groupId>
  1202 + <artifactId>cassandra-all</artifactId>
  1203 + <version>${cassandra-all.version}</version>
  1204 + </dependency>
  1205 + <dependency>
1206 <groupId>junit</groupId> 1206 <groupId>junit</groupId>
1207 <artifactId>junit</artifactId> 1207 <artifactId>junit</artifactId>
1208 <version>${junit.version}</version> 1208 <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>
@@ -67,6 +67,7 @@ class AlarmState { @@ -67,6 +67,7 @@ class AlarmState {
67 initCurrentAlarm(ctx); 67 initCurrentAlarm(ctx);
68 lastMsgMetaData = msg.getMetaData(); 68 lastMsgMetaData = msg.getMetaData();
69 lastMsgQueueName = msg.getQueueName(); 69 lastMsgQueueName = msg.getQueueName();
  70 + this.dataSnapshot = data;
70 return createOrClearAlarms(ctx, data, update, AlarmRuleState::eval); 71 return createOrClearAlarms(ctx, data, update, AlarmRuleState::eval);
71 } 72 }
72 73
@@ -90,8 +91,7 @@ class AlarmState { @@ -90,8 +91,7 @@ class AlarmState {
90 resultState = state; 91 resultState = state;
91 break; 92 break;
92 } else if (AlarmEvalResult.FALSE.equals(evalResult)) { 93 } else if (AlarmEvalResult.FALSE.equals(evalResult)) {
93 - state.clear();  
94 - stateUpdate |= state.checkUpdate(); 94 + stateUpdate = clearAlarmState(stateUpdate, state);
95 } 95 }
96 } 96 }
97 if (resultState != null) { 97 if (resultState != null) {
@@ -99,6 +99,7 @@ class AlarmState { @@ -99,6 +99,7 @@ class AlarmState {
99 if (result != null) { 99 if (result != null) {
100 pushMsg(ctx, result); 100 pushMsg(ctx, result);
101 } 101 }
  102 + stateUpdate = clearAlarmState(stateUpdate, clearState);
102 } else if (currentAlarm != null && clearState != null) { 103 } else if (currentAlarm != null && clearState != null) {
103 if (!validateUpdate(update, clearState)) { 104 if (!validateUpdate(update, clearState)) {
104 log.debug("[{}] Update is not valid for current clear state", alarmDefinition.getId()); 105 log.debug("[{}] Update is not valid for current clear state", alarmDefinition.getId());
@@ -106,23 +107,26 @@ class AlarmState { @@ -106,23 +107,26 @@ class AlarmState {
106 } 107 }
107 AlarmEvalResult evalResult = evalFunction.apply(clearState, data); 108 AlarmEvalResult evalResult = evalFunction.apply(clearState, data);
108 if (AlarmEvalResult.TRUE.equals(evalResult)) { 109 if (AlarmEvalResult.TRUE.equals(evalResult)) {
109 - clearState.clear();  
110 - stateUpdate |= clearState.checkUpdate(); 110 + stateUpdate = clearAlarmState(stateUpdate, clearState);
111 for (AlarmRuleState state : createRulesSortedBySeverityDesc) { 111 for (AlarmRuleState state : createRulesSortedBySeverityDesc) {
112 - state.clear();  
113 - stateUpdate |= state.checkUpdate(); 112 + stateUpdate = clearAlarmState(stateUpdate, state);
114 } 113 }
115 ctx.getAlarmService().clearAlarm(ctx.getTenantId(), currentAlarm.getId(), JacksonUtil.OBJECT_MAPPER.createObjectNode(), System.currentTimeMillis()); 114 ctx.getAlarmService().clearAlarm(ctx.getTenantId(), currentAlarm.getId(), JacksonUtil.OBJECT_MAPPER.createObjectNode(), System.currentTimeMillis());
116 pushMsg(ctx, new TbAlarmResult(false, false, true, currentAlarm)); 115 pushMsg(ctx, new TbAlarmResult(false, false, true, currentAlarm));
117 currentAlarm = null; 116 currentAlarm = null;
118 } else if (AlarmEvalResult.FALSE.equals(evalResult)) { 117 } else if (AlarmEvalResult.FALSE.equals(evalResult)) {
119 - clearState.clear();  
120 - stateUpdate |= clearState.checkUpdate(); 118 + stateUpdate = clearAlarmState(stateUpdate, clearState);
121 } 119 }
122 } 120 }
123 return stateUpdate; 121 return stateUpdate;
124 } 122 }
125 123
  124 + public boolean clearAlarmState(boolean stateUpdate, AlarmRuleState state) {
  125 + state.clear();
  126 + stateUpdate |= state.checkUpdate();
  127 + return stateUpdate;
  128 + }
  129 +
126 public boolean validateUpdate(SnapshotUpdate update, AlarmRuleState state) { 130 public boolean validateUpdate(SnapshotUpdate update, AlarmRuleState state) {
127 if (update != null) { 131 if (update != null) {
128 //Check that the update type and that keys match. 132 //Check that the update type and that keys match.
@@ -190,7 +194,7 @@ class AlarmState { @@ -190,7 +194,7 @@ class AlarmState {
190 } 194 }
191 } 195 }
192 196
193 - private <T> TbAlarmResult calculateAlarmResult(TbContext ctx, AlarmRuleState ruleState) { 197 + private TbAlarmResult calculateAlarmResult(TbContext ctx, AlarmRuleState ruleState) {
194 AlarmSeverity severity = ruleState.getSeverity(); 198 AlarmSeverity severity = ruleState.getSeverity();
195 if (currentAlarm != null) { 199 if (currentAlarm != null) {
196 // TODO: In some extremely rare cases, we might miss the event of alarm clear (If one use in-mem queue and restarted the server) or (if one manipulated the rule chain). 200 // TODO: In some extremely rare cases, we might miss the event of alarm clear (If one use in-mem queue and restarted the server) or (if one manipulated the rule chain).
@@ -230,7 +234,7 @@ class AlarmState { @@ -230,7 +234,7 @@ class AlarmState {
230 } 234 }
231 } 235 }
232 236
233 - private <T> JsonNode createDetails(AlarmRuleState ruleState) { 237 + private JsonNode createDetails(AlarmRuleState ruleState) {
234 ObjectNode details = JacksonUtil.OBJECT_MAPPER.createObjectNode(); 238 ObjectNode details = JacksonUtil.OBJECT_MAPPER.createObjectNode();
235 String alarmDetails = ruleState.getAlarmRule().getAlarmDetails(); 239 String alarmDetails = ruleState.getAlarmRule().getAlarmDetails();
236 240
@@ -273,8 +277,7 @@ class AlarmState { @@ -273,8 +277,7 @@ class AlarmState {
273 if (currentAlarm != null && currentAlarm.getId().equals(alarmNf.getId())) { 277 if (currentAlarm != null && currentAlarm.getId().equals(alarmNf.getId())) {
274 currentAlarm = null; 278 currentAlarm = null;
275 for (AlarmRuleState state : createRulesSortedBySeverityDesc) { 279 for (AlarmRuleState state : createRulesSortedBySeverityDesc) {
276 - state.clear();  
277 - updated |= state.checkUpdate(); 280 + updated = clearAlarmState(updated, state);
278 } 281 }
279 } 282 }
280 return updated; 283 return updated;
@@ -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>
@@ -630,6 +630,9 @@ export class EntityService { @@ -630,6 +630,9 @@ export class EntityService {
630 case EntityType.DASHBOARD: 630 case EntityType.DASHBOARD:
631 entityFieldKeys.push(entityFields.title.keyName); 631 entityFieldKeys.push(entityFields.title.keyName);
632 break; 632 break;
  633 + case EntityType.API_USAGE_STATE:
  634 + entityFieldKeys.push(entityFields.name.keyName);
  635 + break;
633 } 636 }
634 return query ? entityFieldKeys.filter((entityField) => entityField.toLowerCase().indexOf(query) === 0) : entityFieldKeys; 637 return query ? entityFieldKeys.filter((entityField) => entityField.toLowerCase().indexOf(query) === 0) : entityFieldKeys;
635 } 638 }
@@ -52,6 +52,9 @@ @@ -52,6 +52,9 @@
52 </mat-error> 52 </mat-error>
53 </mat-form-field> 53 </mat-form-field>
54 </div> 54 </div>
  55 + <mat-error *ngIf="mqttDeviceProfileTransportConfigurationFormGroup.get('configuration').hasError('unique')">
  56 + {{ 'device-profile.mqtt-device-topic-filters-unique' | translate }}
  57 + </mat-error>
55 <div class="tb-hint" innerHTML="{{ 'device-profile.support-level-wildcards' | translate }}"></div> 58 <div class="tb-hint" innerHTML="{{ 'device-profile.support-level-wildcards' | translate }}"></div>
56 <div class="tb-hint" innerHTML="{{ 'device-profile.single-level-wildcards-hint' | translate }}"></div> 59 <div class="tb-hint" innerHTML="{{ 'device-profile.single-level-wildcards-hint' | translate }}"></div>
57 <div class="tb-hint" innerHTML="{{ 'device-profile.multi-level-wildcards-hint' | translate }}"></div> 60 <div class="tb-hint" innerHTML="{{ 'device-profile.multi-level-wildcards-hint' | translate }}"></div>
@@ -52,7 +52,6 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control @@ -52,7 +52,6 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control
52 52
53 mqttTransportPayloadTypeTranslations = mqttTransportPayloadTypeTranslationMap; 53 mqttTransportPayloadTypeTranslations = mqttTransportPayloadTypeTranslationMap;
54 54
55 -  
56 mqttDeviceProfileTransportConfigurationFormGroup: FormGroup; 55 mqttDeviceProfileTransportConfigurationFormGroup: FormGroup;
57 56
58 private requiredValue: boolean; 57 private requiredValue: boolean;
@@ -90,7 +89,7 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control @@ -90,7 +89,7 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control
90 transportPayloadTypeConfiguration: this.fb.group({ 89 transportPayloadTypeConfiguration: this.fb.group({
91 transportPayloadType: [MqttTransportPayloadType.JSON, Validators.required] 90 transportPayloadType: [MqttTransportPayloadType.JSON, Validators.required]
92 }) 91 })
93 - }) 92 + }, {validator: this.uniqueDeviceTopicValidator})
94 }); 93 });
95 this.mqttDeviceProfileTransportConfigurationFormGroup.get('configuration.transportPayloadTypeConfiguration.transportPayloadType').valueChanges.subscribe(payloadType => { 94 this.mqttDeviceProfileTransportConfigurationFormGroup.get('configuration.transportPayloadTypeConfiguration.transportPayloadType').valueChanges.subscribe(payloadType => {
96 this.updateTransportPayloadBasedControls(payloadType); 95 this.updateTransportPayloadBasedControls(payloadType);
@@ -171,4 +170,14 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control @@ -171,4 +170,14 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control
171 return null; 170 return null;
172 }; 171 };
173 } 172 }
  173 +
  174 + private uniqueDeviceTopicValidator(control: FormGroup): { [key: string]: boolean } | null {
  175 + if (control.value) {
  176 + const formValue = control.value as MqttDeviceProfileTransportConfiguration;
  177 + if (formValue.deviceAttributesTopic === formValue.deviceTelemetryTopic) {
  178 + return {unique: true};
  179 + }
  180 + }
  181 + return null;
  182 + }
174 } 183 }
@@ -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, []],
@@ -56,6 +56,12 @@ export const customerHref = '<a href="https://github.com/thingsboard/thingsboard @@ -56,6 +56,12 @@ export const customerHref = '<a href="https://github.com/thingsboard/thingsboard
56 56
57 export const attributeDataHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts#L76">Attribute Data</a>'; 57 export const attributeDataHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts#L76">Attribute Data</a>';
58 58
  59 +export const timeseriesDataHref = '<a href="https://github.com/thingsboard/thingsboard/blob/627c0577b08452308f925cecb3860e35292c649e/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts#L91">Timeseries Data</a>';
  60 +
  61 +export const aggregationTypeHref = '<a href="https://github.com/thingsboard/thingsboard/blob/a8ea887eacf7729e603ace13ce2d7d89dae82931/ui-ngx/src/app/shared/models/time/time.models.ts#L54">Aggregation Type</a>';
  62 +
  63 +export const dataSortOrderHref = '<a href="https://github.com/thingsboard/thingsboard/blob/627c0577b08452308f925cecb3860e35292c649e/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts#L95">Data Sort Order</a>';
  64 +
59 export const userHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/user.model.ts#L23">User</a>'; 65 export const userHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/user.model.ts#L23">User</a>';
60 66
61 export const entityDataHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/shared/models/query/query.models.ts#L567">Entity data</a>'; 67 export const entityDataHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/shared/models/query/query.models.ts#L567">Entity data</a>';
@@ -1080,6 +1086,23 @@ export const serviceCompletions: TbEditorCompletions = { @@ -1080,6 +1086,23 @@ export const serviceCompletions: TbEditorCompletions = {
1080 ], 1086 ],
1081 return: observableReturnTypeVariable('any') 1087 return: observableReturnTypeVariable('any')
1082 }, 1088 },
  1089 + getEntityTimeseries: {
  1090 + description: 'Get entity timeseries',
  1091 + meta: 'function',
  1092 + args: [
  1093 + {name: 'entityId', type: entityIdHref, description: 'Id of the entity'},
  1094 + {name: 'keys', type: `Array&lt;string&gt;`, description: 'Array of the keys'},
  1095 + {name: 'startTs', type: 'number', description: 'Start time in milliseconds'},
  1096 + {name: 'endTs', type: 'number', description: 'End time in milliseconds'},
  1097 + {name: 'limit', type: 'number', description: 'Limit of values to receive for each key'},
  1098 + {name: 'agg', type: aggregationTypeHref, description: 'Aggregation type'},
  1099 + {name: 'interval', type: 'number', description: 'Aggregation interval'},
  1100 + {name: 'orderBy', type: dataSortOrderHref, description: 'Data order by time'},
  1101 + {name: 'useStrictDataTypes', type: 'boolean', description: 'If "false" all values will be returned as strings'},
  1102 + requestConfigArg
  1103 + ],
  1104 + return: observableReturnTypeVariable(timeseriesDataHref)
  1105 + },
1083 } 1106 }
1084 }, 1107 },
1085 entityService: { 1108 entityService: {
@@ -885,6 +885,7 @@ @@ -885,6 +885,7 @@
885 "no-device-profiles-found": "No device profiles found.", 885 "no-device-profiles-found": "No device profiles found.",
886 "create-new-device-profile": "Create a new one!", 886 "create-new-device-profile": "Create a new one!",
887 "mqtt-device-topic-filters": "MQTT device topic filters", 887 "mqtt-device-topic-filters": "MQTT device topic filters",
  888 + "mqtt-device-topic-filters-unique": "MQTT device topic filters need to be unique.",
888 "mqtt-device-payload-type": "MQTT device payload", 889 "mqtt-device-payload-type": "MQTT device payload",
889 "mqtt-device-payload-type-json": "JSON", 890 "mqtt-device-payload-type-json": "JSON",
890 "mqtt-device-payload-type-proto": "Protobuf", 891 "mqtt-device-payload-type-proto": "Protobuf",
@@ -1949,6 +1950,18 @@ @@ -1949,6 +1950,18 @@
1949 "maximum-assets": "Maximum number of assets (0 - unlimited)", 1950 "maximum-assets": "Maximum number of assets (0 - unlimited)",
1950 "maximum-assets-required": "Maximum number of assets is required.", 1951 "maximum-assets-required": "Maximum number of assets is required.",
1951 "maximum-assets-range": "Maximum number of assets can't be negative", 1952 "maximum-assets-range": "Maximum number of assets can't be negative",
  1953 + "maximum-customers": "Maximum number of customers (0 - unlimited)",
  1954 + "maximum-customers-required": "Maximum number of customers is required.",
  1955 + "maximum-customers-range": "Maximum number of customers can't be negative",
  1956 + "maximum-users": "Maximum number of users (0 - unlimited)",
  1957 + "maximum-users-required": "Maximum number of users is required.",
  1958 + "maximum-users-range": "Maximum number of users can't be negative",
  1959 + "maximum-dashboards": "Maximum number of dashboards (0 - unlimited)",
  1960 + "maximum-dashboards-required": "Maximum number of dashboards is required.",
  1961 + "maximum-dashboards-range": "Maximum number of dashboards can't be negative",
  1962 + "maximum-rule-chains": "Maximum number of rule chains (0 - unlimited)",
  1963 + "maximum-rule-chains-required": "Maximum number of rule chains is required.",
  1964 + "maximum-rule-chains-range": "Maximum number of rule chains can't be negative",
1952 "transport-tenant-msg-rate-limit": "Transport tenant messages rate limit.", 1965 "transport-tenant-msg-rate-limit": "Transport tenant messages rate limit.",
1953 "transport-tenant-telemetry-msg-rate-limit": "Transport tenant telemetry messages rate limit.", 1966 "transport-tenant-telemetry-msg-rate-limit": "Transport tenant telemetry messages rate limit.",
1954 "transport-tenant-telemetry-data-points-rate-limit": "Transport tenant telemetry data points rate limit.", 1967 "transport-tenant-telemetry-data-points-rate-limit": "Transport tenant telemetry data points rate limit.",