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> |
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmState.java
@@ -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<string>`, description: 'Array of the keys'}, | ||
1095 | + {name: 'startTs', type: 'number', description: 'Start time in milliseconds'}, | ||
1096 | + {name: 'endTs', type: 'number', description: 'End time in milliseconds'}, | ||
1097 | + {name: 'limit', type: 'number', description: 'Limit of values to receive for each key'}, | ||
1098 | + {name: 'agg', type: aggregationTypeHref, description: 'Aggregation type'}, | ||
1099 | + {name: 'interval', type: 'number', description: 'Aggregation interval'}, | ||
1100 | + {name: 'orderBy', type: dataSortOrderHref, description: 'Data order by time'}, | ||
1101 | + {name: 'useStrictDataTypes', type: 'boolean', description: 'If "false" all values will be returned as strings'}, | ||
1102 | + requestConfigArg | ||
1103 | + ], | ||
1104 | + return: observableReturnTypeVariable(timeseriesDataHref) | ||
1105 | + }, | ||
1083 | } | 1106 | } |
1084 | }, | 1107 | }, |
1085 | entityService: { | 1108 | entityService: { |
@@ -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.", |