From df21ce8043c03cf6131556abeeceae72534d48c4 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 11 Nov 2020 19:02:01 +0200 Subject: [PATCH 01/18] Fix validate credentials method --- .../server/service/transport/DefaultTransportApiService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java index c7cbea8200..5ceb9b3cd6 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java @@ -165,7 +165,7 @@ public class DefaultTransportApiService implements TransportApiService { private ListenableFuture validateCredentials(TransportProtos.ValidateBasicMqttCredRequestMsg mqtt) { DeviceCredentials credentials = null; - if (mqtt.getUserName() != null) { + if (!StringUtils.isEmpty(mqtt.getUserName())) { credentials = deviceCredentialsService.findDeviceCredentialsByCredentialsId(mqtt.getUserName()); if (credentials != null) { if (credentials.getCredentialsType() == DeviceCredentialsType.ACCESS_TOKEN) { From 671dfa7668a5b6279080030552b7367b9c2a17f6 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 11 Nov 2020 19:53:45 +0200 Subject: [PATCH 02/18] Add validator to unique MQTT device topic filter --- ...e-profile-transport-configuration.component.html | 3 +++ ...ice-profile-transport-configuration.component.ts | 13 +++++++++++-- ui-ngx/src/assets/locale/locale.constant-en_US.json | 1 + 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.html index 00ff4760ab..5b3f3d8abf 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.html @@ -63,6 +63,9 @@ + + {{ 'device-profile.mqtt-device-topic-filters-unique' | translate }} +
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.ts index 18dc1b2bf4..c36d685fec 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.ts @@ -51,7 +51,6 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control mqttTransportPayloadTypeTranslations = mqttTransportPayloadTypeTranslationMap; - mqttDeviceProfileTransportConfigurationFormGroup: FormGroup; private requiredValue: boolean; @@ -87,7 +86,7 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control deviceAttributesTopic: [null, [Validators.required, this.validationMQTTTopic()]], deviceTelemetryTopic: [null, [Validators.required, this.validationMQTTTopic()]], transportPayloadType: [MqttTransportPayloadType.JSON, Validators.required] - }) + }, {validator: this.uniqueDeviceTopicValidator}) }); this.mqttDeviceProfileTransportConfigurationFormGroup.valueChanges.subscribe(() => { this.updateModel(); @@ -147,4 +146,14 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control return null; }; } + + private uniqueDeviceTopicValidator(control: FormGroup): { [key: string]: boolean } | null { + if (control.value) { + const formValue = control.value as MqttDeviceProfileTransportConfiguration; + if (formValue.deviceAttributesTopic === formValue.deviceTelemetryTopic) { + return {unique: true}; + } + } + return null; + } } diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index f0d426be66..76799b73fd 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -885,6 +885,7 @@ "no-device-profiles-found": "No device profiles found.", "create-new-device-profile": "Create a new one!", "mqtt-device-topic-filters": "MQTT device topic filters", + "mqtt-device-topic-filters-unique": "MQTT device topic filters need to be unique.", "mqtt-device-payload-type": "MQTT device payload", "mqtt-device-payload-type-json": "JSON", "mqtt-device-payload-type-proto": "Protobuf", From 0dd9450b92a04c230fb83c9b73fabfcfc2df76ee Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Thu, 12 Nov 2020 09:55:18 +0200 Subject: [PATCH 03/18] Fix for ApiStats security checks --- .../server/dao/sql/query/DefaultEntityQueryRepository.java | 5 ++++- .../thingsboard/server/dao/sql/query/EntityKeyMapping.java | 2 +- ui-ngx/src/app/core/http/entity.service.ts | 3 +++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java index db61e6ab05..8edb1917ba 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java @@ -202,6 +202,9 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { " THEN (select additional_info from entity_view where id = entity_id)" + " END as additional_info"; + 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, " + + "(select title from tenant where id = aus.tenant_id) as name from api_usage_state as aus)"; + static { entityTableMap.put(EntityType.ASSET, "asset"); entityTableMap.put(EntityType.DEVICE, "device"); @@ -210,7 +213,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { entityTableMap.put(EntityType.CUSTOMER, "customer"); entityTableMap.put(EntityType.USER, "tb_user"); entityTableMap.put(EntityType.TENANT, "tenant"); - entityTableMap.put(EntityType.API_USAGE_STATE, "api_usage_state"); + entityTableMap.put(EntityType.API_USAGE_STATE, SELECT_API_USAGE_STATE); } public static EntityType[] RELATION_QUERY_ENTITY_TYPES = new EntityType[]{ diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/query/EntityKeyMapping.java b/dao/src/main/java/org/thingsboard/server/dao/sql/query/EntityKeyMapping.java index 7332de4b49..017306c47c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/query/EntityKeyMapping.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/query/EntityKeyMapping.java @@ -80,7 +80,7 @@ public class EntityKeyMapping { public static final List labeledEntityFields = Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME, TYPE, LABEL, ADDITIONAL_INFO); public static final List contactBasedEntityFields = Arrays.asList(CREATED_TIME, ENTITY_TYPE, EMAIL, TITLE, COUNTRY, STATE, CITY, ADDRESS, ADDRESS_2, ZIP, PHONE, ADDITIONAL_INFO); - public static final Set apiUsageStateEntityFields = Collections.singleton(CREATED_TIME); + public static final Set apiUsageStateEntityFields = new HashSet<>(Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME)); public static final Set commonEntityFieldsSet = new HashSet<>(commonEntityFields); public static final Set 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)); diff --git a/ui-ngx/src/app/core/http/entity.service.ts b/ui-ngx/src/app/core/http/entity.service.ts index acd89e3c1b..9263838ffa 100644 --- a/ui-ngx/src/app/core/http/entity.service.ts +++ b/ui-ngx/src/app/core/http/entity.service.ts @@ -630,6 +630,9 @@ export class EntityService { case EntityType.DASHBOARD: entityFieldKeys.push(entityFields.title.keyName); break; + case EntityType.API_USAGE_STATE: + entityFieldKeys.push(entityFields.name.keyName); + break; } return query ? entityFieldKeys.filter((entityField) => entityField.toLowerCase().indexOf(query) === 0) : entityFieldKeys; } From 590f3a0a80ad7918b829545a83b39498afddd7be Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 12 Nov 2020 10:24:08 +0200 Subject: [PATCH 04/18] Improve tenant creation from OAuth mapper --- .../auth/oauth2/AbstractOAuth2ClientMapper.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java index f76521ac48..ea6e7d4f19 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java @@ -35,14 +35,17 @@ import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.oauth2.OAuth2User; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.service.install.InstallScripts; +import org.thingsboard.server.service.queue.TbClusterService; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.UserPrincipal; @@ -76,6 +79,12 @@ public abstract class AbstractOAuth2ClientMapper { @Autowired private InstallScripts installScripts; + @Autowired + protected TbTenantProfileCache tenantProfileCache; + + @Autowired + protected TbClusterService tbClusterService; + private final Lock userCreationLock = new ReentrantLock(); protected SecurityUser getOrCreateSecurityUserFromOAuth2User(OAuth2User oauth2User, OAuth2ClientRegistrationInfo clientRegistration) { @@ -162,6 +171,10 @@ public abstract class AbstractOAuth2ClientMapper { tenant.setTitle(tenantName); tenant = tenantService.saveTenant(tenant); installScripts.createDefaultRuleChains(tenant.getId()); + tenantProfileCache.evict(tenant.getId()); + tbClusterService.onTenantChange(tenant, null); + tbClusterService.onEntityStateChange(tenant.getId(), tenant.getId(), + ComponentLifecycleEvent.CREATED); } else { tenant = tenants.get(0); } From 31620008890eb6e8a474c63f8b09fd4e8c55adb9 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 12 Nov 2020 11:37:54 +0200 Subject: [PATCH 05/18] Use baseUrl from system security service for OAuth --- .../oauth2/Oauth2AuthenticationFailureHandler.java | 14 +++++++++++++- .../oauth2/Oauth2AuthenticationSuccessHandler.java | 10 ++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationFailureHandler.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationFailureHandler.java index 984936874f..27a4962a0f 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationFailureHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationFailureHandler.java @@ -15,10 +15,15 @@ */ package org.thingsboard.server.service.security.auth.oauth2; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.service.security.system.SystemSecurityService; import org.thingsboard.server.utils.MiscUtils; import javax.servlet.ServletException; @@ -32,11 +37,18 @@ import java.nio.charset.StandardCharsets; @ConditionalOnProperty(prefix = "security.oauth2", value = "enabled", havingValue = "true") public class Oauth2AuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler { + private final SystemSecurityService systemSecurityService; + + @Autowired + public Oauth2AuthenticationFailureHandler(final SystemSecurityService systemSecurityService) { + this.systemSecurityService = systemSecurityService; + } + @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { - String baseUrl = MiscUtils.constructBaseUrl(request); + String baseUrl = this.systemSecurityService.getBaseUrl(TenantId.SYS_TENANT_ID, new CustomerId(EntityId.NULL_UUID), request); getRedirectStrategy().sendRedirect(request, response, baseUrl + "/login?loginError=" + URLEncoder.encode(exception.getMessage(), StandardCharsets.UTF_8.toString())); } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java index 8a65eadedc..2e2eeeee61 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java @@ -22,12 +22,16 @@ import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; import org.thingsboard.server.dao.oauth2.OAuth2Service; import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.token.JwtToken; import org.thingsboard.server.service.security.model.token.JwtTokenFactory; +import org.thingsboard.server.service.security.system.SystemSecurityService; import org.thingsboard.server.utils.MiscUtils; import javax.servlet.http.HttpServletRequest; @@ -45,25 +49,27 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS private final OAuth2ClientMapperProvider oauth2ClientMapperProvider; private final OAuth2Service oAuth2Service; private final OAuth2AuthorizedClientService oAuth2AuthorizedClientService; + private final SystemSecurityService systemSecurityService; @Autowired public Oauth2AuthenticationSuccessHandler(final JwtTokenFactory tokenFactory, final RefreshTokenRepository refreshTokenRepository, final OAuth2ClientMapperProvider oauth2ClientMapperProvider, final OAuth2Service oAuth2Service, - final OAuth2AuthorizedClientService oAuth2AuthorizedClientService) { + final OAuth2AuthorizedClientService oAuth2AuthorizedClientService, final SystemSecurityService systemSecurityService) { this.tokenFactory = tokenFactory; this.refreshTokenRepository = refreshTokenRepository; this.oauth2ClientMapperProvider = oauth2ClientMapperProvider; this.oAuth2Service = oAuth2Service; this.oAuth2AuthorizedClientService = oAuth2AuthorizedClientService; + this.systemSecurityService = systemSecurityService; } @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { - String baseUrl = MiscUtils.constructBaseUrl(request); + String baseUrl = this.systemSecurityService.getBaseUrl(TenantId.SYS_TENANT_ID, new CustomerId(EntityId.NULL_UUID), request); try { OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) authentication; From 7a13083851cc9afe40702d37f9a28982fb92602f Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 12 Nov 2020 13:06:55 +0200 Subject: [PATCH 06/18] BaseUrl check --- .../security/system/DefaultSystemSecurityService.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/security/system/DefaultSystemSecurityService.java b/application/src/main/java/org/thingsboard/server/service/security/system/DefaultSystemSecurityService.java index 4219dbc609..9bd22c88f5 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/system/DefaultSystemSecurityService.java +++ b/application/src/main/java/org/thingsboard/server/service/security/system/DefaultSystemSecurityService.java @@ -202,16 +202,19 @@ public class DefaultSystemSecurityService implements SystemSecurityService { @Override public String getBaseUrl(TenantId tenantId, CustomerId customerId, HttpServletRequest httpServletRequest) { - String baseUrl; + String baseUrl = null; AdminSettings generalSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "general"); JsonNode prohibitDifferentUrl = generalSettings.getJsonValue().get("prohibitDifferentUrl"); if (prohibitDifferentUrl != null && prohibitDifferentUrl.asBoolean()) { baseUrl = generalSettings.getJsonValue().get("baseUrl").asText(); - } else { + } + + if (StringUtils.isEmpty(baseUrl)) { baseUrl = MiscUtils.constructBaseUrl(httpServletRequest); } + return baseUrl; } From eece6418c36e99fa810f4527324519648e7fa24e Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Thu, 12 Nov 2020 13:22:32 +0200 Subject: [PATCH 07/18] updated versions --- application/pom.xml | 4 --- pom.xml | 30 +++++++++++----------- rule-engine/rule-engine-components/pom.xml | 5 ++++ tools/pom.xml | 1 - 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/application/pom.xml b/application/pom.xml index 1730d7a5a5..1783d9f4c5 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -145,10 +145,6 @@ org.springframework.boot spring-boot-starter-websocket - - org.springframework.cloud - spring-cloud-starter-oauth2 - org.springframework.security spring-security-oauth2-client diff --git a/pom.xml b/pom.xml index 335e6c7eba..3495f83971 100755 --- a/pom.xml +++ b/pom.xml @@ -36,11 +36,10 @@ ${project.name} /var/log/${pkg.name} /usr/share/${pkg.name} - 2.2.6.RELEASE - 2.1.2.RELEASE - 5.2.6.RELEASE - 5.2.3.RELEASE - 2.2.4.RELEASE + 2.3.5.RELEASE + 5.3.1 + 5.4.1 + 2.4.1 3.1.0 0.7.0 2.2.0 @@ -51,15 +50,16 @@ 0.10 3.6.0 3.5.0.1 + 3.11.9 1.2.7 28.2-jre 2.6.1 3.4 2.5 1.4 - 2.10.2 - 2.10.2 - 2.10.2 + 2.11.3 + 2.11.3 + 2.11.3 2.2.6 1.0.2 2.6.2 @@ -72,7 +72,7 @@ 1.22.1 1.16.18 1.1.0 - 4.1.49.Final + 4.1.53.Final 1.5.0 4.8.0 2.19.1 @@ -96,7 +96,7 @@ 4.1.1 2.57 2.7.7 - 1.25 + 1.27 1.3.10 1.11.747 1.105.0 @@ -867,11 +867,6 @@ spring-boot-starter-security ${spring-boot.version} - - org.springframework.cloud - spring-cloud-starter-oauth2 - ${spring-oauth2.version} - org.springframework.security spring-security-oauth2-client @@ -1214,6 +1209,11 @@ ${cassandra-unit.version} test + + org.apache.cassandra + cassandra-all + ${cassandra-all.version} + junit junit diff --git a/rule-engine/rule-engine-components/pom.xml b/rule-engine/rule-engine-components/pom.xml index 00d8101e5a..5bb14db250 100644 --- a/rule-engine/rule-engine-components/pom.xml +++ b/rule-engine/rule-engine-components/pom.xml @@ -120,6 +120,11 @@ org.locationtech.jts jts-core + + com.sun.mail + javax.mail + provided + junit junit diff --git a/tools/pom.xml b/tools/pom.xml index 91bf319b0e..10bfa3cc5b 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -54,7 +54,6 @@ org.apache.cassandra cassandra-all - 3.11.6 com.datastax.cassandra From 72a0126e23cd325fe95022911a5dcf592f7b99e1 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Thu, 12 Nov 2020 17:07:07 +0200 Subject: [PATCH 08/18] AlarmState improvements --- .../java/org/thingsboard/rule/engine/profile/AlarmState.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmState.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmState.java index c59dbb75ca..535294d94c 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmState.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmState.java @@ -67,6 +67,7 @@ class AlarmState { initCurrentAlarm(ctx); lastMsgMetaData = msg.getMetaData(); lastMsgQueueName = msg.getQueueName(); + this.dataSnapshot = data; return createOrClearAlarms(ctx, data, update, AlarmRuleState::eval); } @@ -190,7 +191,7 @@ class AlarmState { } } - private TbAlarmResult calculateAlarmResult(TbContext ctx, AlarmRuleState ruleState) { + private TbAlarmResult calculateAlarmResult(TbContext ctx, AlarmRuleState ruleState) { AlarmSeverity severity = ruleState.getSeverity(); if (currentAlarm != null) { // 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 +231,7 @@ class AlarmState { } } - private JsonNode createDetails(AlarmRuleState ruleState) { + private JsonNode createDetails(AlarmRuleState ruleState) { ObjectNode details = JacksonUtil.OBJECT_MAPPER.createObjectNode(); String alarmDetails = ruleState.getAlarmRule().getAlarmDetails(); From 5850fa0cd92ebf8e1deec1bd3875dd3378bae5f8 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Thu, 12 Nov 2020 18:58:07 +0200 Subject: [PATCH 09/18] updated jedis version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3495f83971..00968c525a 100755 --- a/pom.xml +++ b/pom.xml @@ -40,7 +40,7 @@ 5.3.1 5.4.1 2.4.1 - 3.1.0 + 3.3.0 0.7.0 2.2.0 4.12 From cb41548b22bd11f64e3965cce5d9cec1108bacde Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Thu, 12 Nov 2020 19:32:45 +0200 Subject: [PATCH 10/18] Add cleanup of clear alarm state on non matching condition --- .../data/plugin/ComponentLifecycleEvent.java | 2 +- .../rule/engine/profile/AlarmState.java | 22 ++++++++++--------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentLifecycleEvent.java b/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentLifecycleEvent.java index 1f242a0f88..87adc31713 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentLifecycleEvent.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentLifecycleEvent.java @@ -21,5 +21,5 @@ import java.io.Serializable; * @author Andrew Shvayka */ public enum ComponentLifecycleEvent implements Serializable { - CREATED, STARTED, ACTIVATED, SUSPENDED, UPDATED, STOPPED, DELETED, ADDED_TO_ALLOW_LIST, ADDED_TO_DENY_LIST + CREATED, STARTED, ACTIVATED, SUSPENDED, UPDATED, STOPPED, DELETED } \ No newline at end of file diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmState.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmState.java index c59dbb75ca..390a0d22d6 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmState.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmState.java @@ -90,8 +90,7 @@ class AlarmState { resultState = state; break; } else if (AlarmEvalResult.FALSE.equals(evalResult)) { - state.clear(); - stateUpdate |= state.checkUpdate(); + stateUpdate = clearAlarmState(stateUpdate, state); } } if (resultState != null) { @@ -99,6 +98,7 @@ class AlarmState { if (result != null) { pushMsg(ctx, result); } + stateUpdate = clearAlarmState(stateUpdate, clearState); } else if (currentAlarm != null && clearState != null) { if (!validateUpdate(update, clearState)) { log.debug("[{}] Update is not valid for current clear state", alarmDefinition.getId()); @@ -106,23 +106,26 @@ class AlarmState { } AlarmEvalResult evalResult = evalFunction.apply(clearState, data); if (AlarmEvalResult.TRUE.equals(evalResult)) { - clearState.clear(); - stateUpdate |= clearState.checkUpdate(); + stateUpdate = clearAlarmState(stateUpdate, clearState); for (AlarmRuleState state : createRulesSortedBySeverityDesc) { - state.clear(); - stateUpdate |= state.checkUpdate(); + stateUpdate = clearAlarmState(stateUpdate, state); } ctx.getAlarmService().clearAlarm(ctx.getTenantId(), currentAlarm.getId(), JacksonUtil.OBJECT_MAPPER.createObjectNode(), System.currentTimeMillis()); pushMsg(ctx, new TbAlarmResult(false, false, true, currentAlarm)); currentAlarm = null; } else if (AlarmEvalResult.FALSE.equals(evalResult)) { - clearState.clear(); - stateUpdate |= clearState.checkUpdate(); + stateUpdate = clearAlarmState(stateUpdate, clearState); } } return stateUpdate; } + public boolean clearAlarmState(boolean stateUpdate, AlarmRuleState state) { + state.clear(); + stateUpdate |= state.checkUpdate(); + return stateUpdate; + } + public boolean validateUpdate(SnapshotUpdate update, AlarmRuleState state) { if (update != null) { //Check that the update type and that keys match. @@ -273,8 +276,7 @@ class AlarmState { if (currentAlarm != null && currentAlarm.getId().equals(alarmNf.getId())) { currentAlarm = null; for (AlarmRuleState state : createRulesSortedBySeverityDesc) { - state.clear(); - updated |= state.checkUpdate(); + updated = clearAlarmState(updated, state); } } return updated; From 627c0577b08452308f925cecb3860e35292c649e Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Thu, 12 Nov 2020 19:44:49 +0200 Subject: [PATCH 11/18] Fix for ws reconnect issues --- .../service/subscription/DefaultTbLocalSubscriptionService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java index 89233d2f4f..4e62628851 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java @@ -108,7 +108,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer * Since number of subscriptions is usually much less then number of devices that are pushing data. */ subscriptionsBySessionId.values().forEach(map -> map.values() - .forEach(sub -> pushSubscriptionToManagerService(sub, false))); + .forEach(sub -> pushSubscriptionToManagerService(sub, true))); } } From 57c14f1c93274f1f4013f3308363f5307a4dde7e Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Fri, 13 Nov 2020 10:17:46 +0200 Subject: [PATCH 12/18] changed spring version to 5.2.10.RELEASE --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 00968c525a..03b815635d 100755 --- a/pom.xml +++ b/pom.xml @@ -37,7 +37,7 @@ /var/log/${pkg.name} /usr/share/${pkg.name} 2.3.5.RELEASE - 5.3.1 + 5.2.10.RELEASE 5.4.1 2.4.1 3.3.0 From ba1b000adbfc9253fd1fc3e984480a2dd7a60fb8 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Fri, 13 Nov 2020 11:55:31 +0200 Subject: [PATCH 13/18] added maxCustomers, maxUsers, maxDashboards, maxRuleChains for TenantProfile --- .../DefaultTenantProfileConfiguration.java | 4 ++ .../server/dao/asset/BaseAssetService.java | 2 +- .../server/dao/customer/CustomerDao.java | 2 + .../dao/customer/CustomerServiceImpl.java | 16 +++++++ .../server/dao/dashboard/DashboardDao.java | 1 + .../dao/dashboard/DashboardServiceImpl.java | 20 ++++++++ .../server/dao/device/DeviceServiceImpl.java | 2 +- .../server/dao/rule/BaseRuleChainService.java | 20 ++++++++ .../server/dao/rule/RuleChainDao.java | 2 + .../dao/sql/customer/CustomerRepository.java | 1 + .../dao/sql/customer/JpaCustomerDao.java | 6 +++ .../sql/dashboard/DashboardRepository.java | 2 + .../dao/sql/dashboard/JpaDashboardDao.java | 6 +++ .../server/dao/sql/rule/JpaRuleChainDao.java | 5 ++ .../dao/sql/rule/RuleChainRepository.java | 1 + .../server/dao/sql/user/JpaUserDao.java | 5 ++ .../server/dao/sql/user/UserRepository.java | 1 + .../thingsboard/server/dao/user/UserDao.java | 3 +- .../server/dao/user/UserServiceImpl.java | 20 ++++++++ ...enant-profile-configuration.component.html | 48 +++++++++++++++++++ ...-tenant-profile-configuration.component.ts | 4 ++ .../assets/locale/locale.constant-en_US.json | 12 +++++ 22 files changed, 180 insertions(+), 3 deletions(-) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java index 250f9c75a5..10df66ece5 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java @@ -24,6 +24,10 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura private long maxDevices; private long maxAssets; + private long maxCustomers; + private long maxUsers; + private long maxDashboards; + private long maxRuleChains; private String transportTenantMsgRateLimit; private String transportTenantTelemetryMsgRateLimit; diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java index 923baa1df4..97cdb31f5d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java @@ -332,7 +332,7 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ long maxAssets = profileConfiguration.getMaxAssets(); if (maxAssets > 0) { long currentAssetsCount = assetDao.countAssetsByTenantId(tenantId); - if (maxAssets >= currentAssetsCount) { + if (currentAssetsCount >= maxAssets) { throw new DataValidationException("Can't create assets more then " + maxAssets); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDao.java b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDao.java index 11fa5cb7cd..5ecfd2fe62 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDao.java @@ -54,5 +54,7 @@ public interface CustomerDao extends Dao { * @return the optional customer object */ Optional findCustomersByTenantIdAndTitle(UUID tenantId, String title); + + Long countCustomersByTenantId(TenantId tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java index 14d591e7e8..b649200e23 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java @@ -21,6 +21,7 @@ import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Tenant; @@ -28,6 +29,7 @@ import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.device.DeviceService; @@ -38,6 +40,7 @@ import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.Validator; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantDao; import org.thingsboard.server.dao.user.UserService; @@ -75,6 +78,10 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom @Autowired private DashboardService dashboardService; + @Autowired + @Lazy + private TbTenantProfileCache tenantProfileCache; + @Override public Customer findCustomerById(TenantId tenantId, CustomerId customerId) { log.trace("Executing findCustomerById [{}]", customerId); @@ -162,6 +169,15 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom @Override protected void validateCreate(TenantId tenantId, Customer customer) { + DefaultTenantProfileConfiguration profileConfiguration = + (DefaultTenantProfileConfiguration)tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); + long maxCustomers = profileConfiguration.getMaxCustomers(); + if (maxCustomers > 0) { + long currentCustomersCount = customerDao.countCustomersByTenantId(tenantId); + if (currentCustomersCount >= maxCustomers) { + throw new DataValidationException("Can't create customers more then " + maxCustomers); + } + } customerDao.findCustomersByTenantIdAndTitle(customer.getTenantId().getId(), customer.getTitle()).ifPresent( c -> { throw new DataValidationException("Customer with such title already exists!"); diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java index 0429c6d469..0c013b4d33 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java @@ -32,4 +32,5 @@ public interface DashboardDao extends Dao { */ Dashboard save(TenantId tenantId, Dashboard dashboard); + Long countDashboardsByTenantId(TenantId tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java index 112f998cd4..f9ec7755d4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java @@ -19,6 +19,7 @@ import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Dashboard; @@ -31,12 +32,14 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.dao.customer.CustomerDao; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.Validator; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantDao; import java.util.concurrent.ExecutionException; @@ -61,6 +64,10 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb @Autowired private CustomerDao customerDao; + @Autowired + @Lazy + private TbTenantProfileCache tenantProfileCache; + @Override public Dashboard findDashboardById(TenantId tenantId, DashboardId dashboardId) { log.trace("Executing findDashboardById [{}]", dashboardId); @@ -214,6 +221,19 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb private DataValidator dashboardValidator = new DataValidator() { + @Override + protected void validateCreate(TenantId tenantId, Dashboard data) { + DefaultTenantProfileConfiguration profileConfiguration = + (DefaultTenantProfileConfiguration)tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); + long maxDashboards = profileConfiguration.getMaxDashboards(); + if (maxDashboards > 0) { + long currentDashboardsCount = dashboardDao.countDashboardsByTenantId(tenantId); + if (currentDashboardsCount >= maxDashboards) { + throw new DataValidationException("Can't create dashboards more then " + maxDashboards); + } + } + } + @Override protected void validateDataImpl(TenantId tenantId, Dashboard dashboard) { if (StringUtils.isEmpty(dashboard.getTitle())) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java index 24c597b4df..5728259ad7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java @@ -532,7 +532,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe long maxDevices = profileConfiguration.getMaxDevices(); if (maxDevices > 0) { long currentDevicesCount = deviceDao.countDevicesByTenantId(tenantId); - if (maxDevices >= currentDevicesCount) { + if (currentDevicesCount >= maxDevices) { throw new DataValidationException("Can't create devices more then " + maxDevices); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java index 5997839119..aaa5040f6e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java @@ -24,6 +24,7 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.hibernate.exception.ConstraintViolationException; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.EntityType; @@ -44,11 +45,13 @@ import org.thingsboard.server.common.data.rule.RuleChainData; import org.thingsboard.server.common.data.rule.RuleChainImportResult; import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.Validator; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantDao; import java.util.ArrayList; @@ -81,6 +84,10 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC @Autowired private TenantDao tenantDao; + @Autowired + @Lazy + private TbTenantProfileCache tenantProfileCache; + @Override public RuleChain saveRuleChain(RuleChain ruleChain) { ruleChainValidator.validate(ruleChain, RuleChain::getTenantId); @@ -580,6 +587,19 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC private DataValidator ruleChainValidator = new DataValidator() { + @Override + protected void validateCreate(TenantId tenantId, RuleChain data) { + DefaultTenantProfileConfiguration profileConfiguration = + (DefaultTenantProfileConfiguration)tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); + long maxRuleChains = profileConfiguration.getMaxRuleChains(); + if (maxRuleChains > 0) { + long currentRuleChainsCount = ruleChainDao.countRuleChainsByTenantId(tenantId); + if (currentRuleChainsCount >= maxRuleChains) { + throw new DataValidationException("Can't create rule chains more then " + maxRuleChains); + } + } + } + @Override protected void validateDataImpl(TenantId tenantId, RuleChain ruleChain) { if (StringUtils.isEmpty(ruleChain.getName())) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java index c3214425fe..9f792200fc 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.dao.rule; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.rule.RuleChain; @@ -36,4 +37,5 @@ public interface RuleChainDao extends Dao { */ PageData findRuleChainsByTenantId(UUID tenantId, PageLink pageLink); + Long countRuleChainsByTenantId(TenantId tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/customer/CustomerRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/customer/CustomerRepository.java index bf2a845ec0..4b5a8e0791 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/customer/CustomerRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/customer/CustomerRepository.java @@ -37,4 +37,5 @@ public interface CustomerRepository extends PagingAndSortingRepository { + + Long countByTenantId(UUID tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardDao.java index 8d637f0cbe..a8cf3136ff 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardDao.java @@ -19,6 +19,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.dashboard.DashboardDao; import org.thingsboard.server.dao.model.sql.DashboardEntity; import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; @@ -43,4 +44,9 @@ public class JpaDashboardDao extends JpaAbstractSearchTextDao getCrudRepository() { return dashboardRepository; } + + @Override + public Long countDashboardsByTenantId(TenantId tenantId) { + return dashboardRepository.countByTenantId(tenantId.getId()); + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java index 57d1545a94..051d13f805 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java @@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.rule.RuleChain; @@ -56,4 +57,8 @@ public class JpaRuleChainDao extends JpaAbstractSearchTextDao imple DaoUtil.toPageable(pageLink))); } + + @Override + public Long countUsersByTenantId(TenantId tenantId) { + return userRepository.countByTenantId(tenantId.getId()); + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java index 1d512a7944..ceaf09ec59 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java @@ -47,4 +47,5 @@ public interface UserRepository extends PagingAndSortingRepository { * @return the list of user entities */ PageData findCustomerUsers(UUID tenantId, UUID customerId, PageLink pageLink); - + + Long countUsersByTenantId(TenantId tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java index 314684c3fe..acf9c7b1b0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java @@ -24,6 +24,7 @@ import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Tenant; @@ -36,6 +37,7 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.UserCredentials; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.dao.customer.CustomerDao; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; @@ -43,6 +45,7 @@ import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantDao; import java.util.HashMap; @@ -84,6 +87,10 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic @Autowired private CustomerDao customerDao; + @Autowired + @Lazy + private TbTenantProfileCache tenantProfileCache; + @Override public User findUserByEmail(TenantId tenantId, String email) { log.trace("Executing findUserByEmail [{}]", email); @@ -364,6 +371,19 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic private DataValidator userValidator = new DataValidator() { + @Override + protected void validateCreate(TenantId tenantId, User data) { + DefaultTenantProfileConfiguration profileConfiguration = + (DefaultTenantProfileConfiguration)tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); + long maxUsers = profileConfiguration.getMaxUsers(); + if (maxUsers > 0) { + long currentUsersCount = userDao.countUsersByTenantId(tenantId); + if (currentUsersCount >= maxUsers) { + throw new DataValidationException("Can't create users more then " + maxUsers); + } + } + } + @Override protected void validateDataImpl(TenantId requestTenantId, User user) { if (StringUtils.isEmpty(user.getEmail())) { diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html index 12258ffd5f..ae9fd8ea9c 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html @@ -40,6 +40,54 @@ {{ 'tenant-profile.maximum-assets-range' | translate}} + + tenant-profile.maximum-customers + + + {{ 'tenant-profile.maximum-customers-required' | translate}} + + + {{ 'tenant-profile.maximum-customers-range' | translate}} + + + + tenant-profile.maximum-users + + + {{ 'tenant-profile.maximum-users-required' | translate}} + + + {{ 'tenant-profile.maximum-users-range' | translate}} + + + + tenant-profile.maximum-dashboards + + + {{ 'tenant-profile.maximum-dashboards-required' | translate}} + + + {{ 'tenant-profile.maximum-dashboards-range' | translate}} + + + + tenant-profile.maximum-rule-chains + + + {{ 'tenant-profile.maximum-rule-chains-required' | translate}} + + + {{ 'tenant-profile.maximum-rule-chains-range' | translate}} + + tenant-profile.max-transport-messages Date: Fri, 13 Nov 2020 12:02:09 +0200 Subject: [PATCH 14/18] Add getEntityTimeseries method to Service Completion --- .../models/ace/service-completion.models.ts | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/ui-ngx/src/app/shared/models/ace/service-completion.models.ts b/ui-ngx/src/app/shared/models/ace/service-completion.models.ts index f9b42f12f5..27a42680e5 100644 --- a/ui-ngx/src/app/shared/models/ace/service-completion.models.ts +++ b/ui-ngx/src/app/shared/models/ace/service-completion.models.ts @@ -56,6 +56,12 @@ export const customerHref = 'Attribute Data'; +export const timeseriesDataHref = 'Timeseries Data'; + +export const aggregationTypeHref = 'Aggregation Type'; + +export const dataSortOrderHref = 'Data Sort Order'; + export const userHref = 'User'; export const entityDataHref = 'Entity data'; @@ -1080,6 +1086,23 @@ export const serviceCompletions: TbEditorCompletions = { ], return: observableReturnTypeVariable('any') }, + getEntityTimeseries: { + description: 'Get entity timeseries', + meta: 'function', + args: [ + {name: 'entityId', type: entityIdHref, description: 'Id of the entity'}, + {name: 'keys', type: `Array<string>`, description: 'Array of the keys'}, + {name: 'startTs', type: 'number', description: 'Start time in milliseconds'}, + {name: 'endTs', type: 'number', description: 'End time in milliseconds'}, + {name: 'limit', type: 'number', description: 'Limit of values to receive for each key'}, + {name: 'agg', type: aggregationTypeHref, description: 'Aggregation type'}, + {name: 'interval', type: 'number', description: 'Aggregation interval'}, + {name: 'orderBy', type: dataSortOrderHref, description: 'Data order by time'}, + {name: 'useStrictDataTypes', type: 'boolean', description: 'If "false" all values will be returned as strings'}, + requestConfigArg + ], + return: observableReturnTypeVariable(timeseriesDataHref) + }, } }, entityService: { From 4eccf7f9165bf03b5905981c0a2bea60ed823b78 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Fri, 13 Nov 2020 13:00:33 +0200 Subject: [PATCH 15/18] fix validateCreate from UserServiceImpl --- .../server/dao/user/UserServiceImpl.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java index acf9c7b1b0..0f13cdf3c6 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java @@ -372,14 +372,16 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic private DataValidator userValidator = new DataValidator() { @Override - protected void validateCreate(TenantId tenantId, User data) { - DefaultTenantProfileConfiguration profileConfiguration = - (DefaultTenantProfileConfiguration)tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); - long maxUsers = profileConfiguration.getMaxUsers(); - if (maxUsers > 0) { - long currentUsersCount = userDao.countUsersByTenantId(tenantId); - if (currentUsersCount >= maxUsers) { - throw new DataValidationException("Can't create users more then " + maxUsers); + protected void validateCreate(TenantId tenantId, User user) { + if (!user.getTenantId().getId().equals(ModelConstants.NULL_UUID)) { + DefaultTenantProfileConfiguration profileConfiguration = + (DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); + long maxUsers = profileConfiguration.getMaxUsers(); + if (maxUsers > 0) { + long currentUsersCount = userDao.countUsersByTenantId(tenantId); + if (currentUsersCount >= maxUsers) { + throw new DataValidationException("Can't create users more then " + maxUsers); + } } } } From 9f2bae98158e63dd1d3a61a31a0723b05fc63b6a Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Fri, 13 Nov 2020 13:29:23 +0200 Subject: [PATCH 16/18] Improvements to API usage statistics collection --- .../transport/service/DefaultTransportService.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java index 4af723f3a0..9b2e3022a9 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java @@ -390,7 +390,8 @@ public class DefaultTransportService implements TransportService { metaData.putValue("deviceName", sessionInfo.getDeviceName()); metaData.putValue("deviceType", sessionInfo.getDeviceType()); metaData.putValue("notifyDevice", "false"); - sendToRuleEngine(tenantId, deviceId, sessionInfo, json, metaData, SessionMsgType.POST_ATTRIBUTES_REQUEST, new TransportTbQueueCallback(callback)); + sendToRuleEngine(tenantId, deviceId, sessionInfo, json, metaData, SessionMsgType.POST_ATTRIBUTES_REQUEST, + new TransportTbQueueCallback(new ApiStatsProxyCallback<>(tenantId, msg.getKvList().size(), callback))); } } @@ -399,7 +400,7 @@ public class DefaultTransportService implements TransportService { if (checkLimits(sessionInfo, msg, callback)) { reportActivityInternal(sessionInfo); sendToDeviceActor(sessionInfo, TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) - .setGetAttributes(msg).build(), callback); + .setGetAttributes(msg).build(), new ApiStatsProxyCallback<>(getTenantId(sessionInfo), 1, callback)); } } @@ -409,7 +410,7 @@ public class DefaultTransportService implements TransportService { SessionMetaData sessionMetaData = reportActivityInternal(sessionInfo); sessionMetaData.setSubscribedToAttributes(!msg.getUnsubscribe()); sendToDeviceActor(sessionInfo, TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) - .setSubscribeToAttributes(msg).build(), callback); + .setSubscribeToAttributes(msg).build(), new ApiStatsProxyCallback<>(getTenantId(sessionInfo), 1, callback)); } } @@ -419,7 +420,7 @@ public class DefaultTransportService implements TransportService { SessionMetaData sessionMetaData = reportActivityInternal(sessionInfo); sessionMetaData.setSubscribedToRPC(!msg.getUnsubscribe()); sendToDeviceActor(sessionInfo, TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) - .setSubscribeToRPC(msg).build(), callback); + .setSubscribeToRPC(msg).build(), new ApiStatsProxyCallback<>(getTenantId(sessionInfo), 1, callback)); } } @@ -428,7 +429,7 @@ public class DefaultTransportService implements TransportService { if (checkLimits(sessionInfo, msg, callback)) { reportActivityInternal(sessionInfo); sendToDeviceActor(sessionInfo, TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) - .setToDeviceRPCCallResponse(msg).build(), callback); + .setToDeviceRPCCallResponse(msg).build(), new ApiStatsProxyCallback<>(getTenantId(sessionInfo), 1, callback)); } } @@ -805,7 +806,7 @@ public class DefaultTransportService implements TransportService { @Override public void onFailure(Throwable t) { - callback.onError(t); + DefaultTransportService.this.transportCallbackExecutor.submit(() -> callback.onError(t)); } } From a1d9ce6e45a709104328cc4724590c872e18749c Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Fri, 13 Nov 2020 15:32:49 +0200 Subject: [PATCH 17/18] created validateNumberOfEntitiesPerTenant --- .../server/dao/TenantEntityDao.java | 8 ++++++ .../server/dao/asset/AssetDao.java | 5 ++-- .../server/dao/asset/BaseAssetService.java | 7 +---- .../server/dao/customer/CustomerDao.java | 5 ++-- .../dao/customer/CustomerServiceImpl.java | 9 +++---- .../server/dao/dashboard/DashboardDao.java | 5 ++-- .../dao/dashboard/DashboardServiceImpl.java | 8 ++---- .../server/dao/device/DeviceDao.java | 5 ++-- .../server/dao/device/DeviceServiceImpl.java | 7 +---- .../dao/entity/AbstractEntityService.java | 5 ++-- .../server/dao/rule/BaseRuleChainService.java | 7 +---- .../server/dao/rule/RuleChainDao.java | 6 ++--- .../server/dao/service/DataValidator.java | 15 +++++++++++ .../server/dao/sql/asset/JpaAssetDao.java | 3 +-- .../dao/sql/customer/JpaCustomerDao.java | 2 +- .../dao/sql/dashboard/JpaDashboardDao.java | 2 +- .../dao/sql/device/DeviceRepository.java | 26 +++++++++---------- .../server/dao/sql/device/JpaDeviceDao.java | 2 +- .../server/dao/sql/rule/JpaRuleChainDao.java | 2 +- .../server/dao/sql/user/JpaUserDao.java | 2 +- .../thingsboard/server/dao/user/UserDao.java | 9 +++---- .../server/dao/user/UserServiceImpl.java | 8 ++---- 22 files changed, 68 insertions(+), 80 deletions(-) create mode 100644 dao/src/main/java/org/thingsboard/server/dao/TenantEntityDao.java diff --git a/dao/src/main/java/org/thingsboard/server/dao/TenantEntityDao.java b/dao/src/main/java/org/thingsboard/server/dao/TenantEntityDao.java new file mode 100644 index 0000000000..0ac51a0dba --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/TenantEntityDao.java @@ -0,0 +1,8 @@ +package org.thingsboard.server.dao; + +import org.thingsboard.server.common.data.id.TenantId; + +public interface TenantEntityDao { + + Long countByTenantId(TenantId tenantId); +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java index 1366df3ac2..ae8bf5706c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java @@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; +import org.thingsboard.server.dao.TenantEntityDao; import java.util.List; import java.util.Optional; @@ -32,7 +33,7 @@ import java.util.UUID; * The Interface AssetDao. * */ -public interface AssetDao extends Dao { +public interface AssetDao extends Dao, TenantEntityDao { /** * Find asset info by id. @@ -166,6 +167,4 @@ public interface AssetDao extends Dao { */ ListenableFuture> findTenantAssetTypesAsync(UUID tenantId); - Long countAssetsByTenantId(TenantId tenantId); - } diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java index 97cdb31f5d..29eaad31fb 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java @@ -330,12 +330,7 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ DefaultTenantProfileConfiguration profileConfiguration = (DefaultTenantProfileConfiguration)tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); long maxAssets = profileConfiguration.getMaxAssets(); - if (maxAssets > 0) { - long currentAssetsCount = assetDao.countAssetsByTenantId(tenantId); - if (currentAssetsCount >= maxAssets) { - throw new DataValidationException("Can't create assets more then " + maxAssets); - } - } + validateNumberOfEntitiesPerTenant(tenantId, assetDao, maxAssets, EntityType.ASSET); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDao.java b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDao.java index 5ecfd2fe62..98622c8b83 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDao.java @@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; +import org.thingsboard.server.dao.TenantEntityDao; import java.util.Optional; import java.util.UUID; @@ -27,7 +28,7 @@ import java.util.UUID; /** * The Interface CustomerDao. */ -public interface CustomerDao extends Dao { +public interface CustomerDao extends Dao, TenantEntityDao { /** * Save or update customer object @@ -55,6 +56,4 @@ public interface CustomerDao extends Dao { */ Optional findCustomersByTenantIdAndTitle(UUID tenantId, String title); - Long countCustomersByTenantId(TenantId tenantId); - } diff --git a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java index b649200e23..d97b4e9c34 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java @@ -24,6 +24,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; @@ -172,12 +173,8 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom DefaultTenantProfileConfiguration profileConfiguration = (DefaultTenantProfileConfiguration)tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); long maxCustomers = profileConfiguration.getMaxCustomers(); - if (maxCustomers > 0) { - long currentCustomersCount = customerDao.countCustomersByTenantId(tenantId); - if (currentCustomersCount >= maxCustomers) { - throw new DataValidationException("Can't create customers more then " + maxCustomers); - } - } + + validateNumberOfEntitiesPerTenant(tenantId, customerDao, maxCustomers, EntityType.CUSTOMER); customerDao.findCustomersByTenantIdAndTitle(customer.getTenantId().getId(), customer.getTitle()).ifPresent( c -> { throw new DataValidationException("Customer with such title already exists!"); diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java index 0c013b4d33..0beb89ef06 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java @@ -18,11 +18,12 @@ package org.thingsboard.server.dao.dashboard; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.Dao; +import org.thingsboard.server.dao.TenantEntityDao; /** * The Interface DashboardDao. */ -public interface DashboardDao extends Dao { +public interface DashboardDao extends Dao, TenantEntityDao { /** * Save or update dashboard object @@ -31,6 +32,4 @@ public interface DashboardDao extends Dao { * @return saved dashboard object */ Dashboard save(TenantId tenantId, Dashboard dashboard); - - Long countDashboardsByTenantId(TenantId tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java index f9ec7755d4..55bf31e321 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java @@ -24,6 +24,7 @@ import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.DashboardInfo; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DashboardId; @@ -226,12 +227,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb DefaultTenantProfileConfiguration profileConfiguration = (DefaultTenantProfileConfiguration)tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); long maxDashboards = profileConfiguration.getMaxDashboards(); - if (maxDashboards > 0) { - long currentDashboardsCount = dashboardDao.countDashboardsByTenantId(tenantId); - if (currentDashboardsCount >= maxDashboards) { - throw new DataValidationException("Can't create dashboards more then " + maxDashboards); - } - } + validateNumberOfEntitiesPerTenant(tenantId, dashboardDao, maxDashboards, EntityType.DASHBOARD); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java index 2f321ff499..3e8f6445a2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java @@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; +import org.thingsboard.server.dao.TenantEntityDao; import java.util.List; import java.util.Optional; @@ -32,7 +33,7 @@ import java.util.UUID; * The Interface DeviceDao. * */ -public interface DeviceDao extends Dao { +public interface DeviceDao extends Dao, TenantEntityDao { /** * Find device info by id. @@ -203,8 +204,6 @@ public interface DeviceDao extends Dao { */ ListenableFuture findDeviceByTenantIdAndIdAsync(TenantId tenantId, UUID id); - Long countDevicesByTenantId(TenantId tenantId); - Long countDevicesByDeviceProfileId(TenantId tenantId, UUID deviceProfileId); /** diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java index 5728259ad7..a919f86dfe 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java @@ -530,12 +530,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe DefaultTenantProfileConfiguration profileConfiguration = (DefaultTenantProfileConfiguration)tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); long maxDevices = profileConfiguration.getMaxDevices(); - if (maxDevices > 0) { - long currentDevicesCount = deviceDao.countDevicesByTenantId(tenantId); - if (currentDevicesCount >= maxDevices) { - throw new DataValidationException("Can't create devices more then " + maxDevices); - } - } + validateNumberOfEntitiesPerTenant(tenantId, deviceDao, maxDevices, EntityType.DEVICE); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java b/dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java index a0f03b2a32..42d02addcd 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java @@ -37,12 +37,11 @@ public abstract class AbstractEntityService { protected Optional extractConstraintViolationException(Exception t) { if (t instanceof ConstraintViolationException) { - return Optional.of ((ConstraintViolationException) t); + return Optional.of((ConstraintViolationException) t); } else if (t.getCause() instanceof ConstraintViolationException) { - return Optional.of ((ConstraintViolationException) (t.getCause())); + return Optional.of((ConstraintViolationException) (t.getCause())); } else { return Optional.empty(); } } - } diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java index aaa5040f6e..263f01570e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java @@ -592,12 +592,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC DefaultTenantProfileConfiguration profileConfiguration = (DefaultTenantProfileConfiguration)tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); long maxRuleChains = profileConfiguration.getMaxRuleChains(); - if (maxRuleChains > 0) { - long currentRuleChainsCount = ruleChainDao.countRuleChainsByTenantId(tenantId); - if (currentRuleChainsCount >= maxRuleChains) { - throw new DataValidationException("Can't create rule chains more then " + maxRuleChains); - } - } + validateNumberOfEntitiesPerTenant(tenantId, ruleChainDao, maxRuleChains, EntityType.RULE_CHAIN); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java index 9f792200fc..87ab9d26b6 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java @@ -15,18 +15,18 @@ */ package org.thingsboard.server.dao.rule; -import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.dao.Dao; +import org.thingsboard.server.dao.TenantEntityDao; import java.util.UUID; /** * Created by igor on 3/12/18. */ -public interface RuleChainDao extends Dao { +public interface RuleChainDao extends Dao, TenantEntityDao { /** * Find rule chains by tenantId and page link. @@ -36,6 +36,4 @@ public interface RuleChainDao extends Dao { * @return the list of rule chain objects */ PageData findRuleChainsByTenantId(UUID tenantId, PageLink pageLink); - - Long countRuleChainsByTenantId(TenantId tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java index d78654164f..0bd0e7b030 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java @@ -18,7 +18,9 @@ package org.thingsboard.server.dao.service; import com.fasterxml.jackson.databind.JsonNode; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.BaseData; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.TenantEntityDao; import org.thingsboard.server.dao.exception.DataValidationException; import java.util.HashSet; @@ -79,6 +81,19 @@ public abstract class DataValidator> { return emailMatcher.matches(); } + protected void validateNumberOfEntitiesPerTenant(TenantId tenantId, + TenantEntityDao tenantEntityDao, + long maxEntities, + EntityType entityType) { + if (maxEntities > 0) { + long currentEntitiesCount = tenantEntityDao.countByTenantId(tenantId); + if (currentEntitiesCount >= maxEntities) { + throw new DataValidationException(String.format("Can't create more then %d %ss!", + maxEntities, entityType.name().toLowerCase().replaceAll("_", " "))); + } + } + } + protected static void validateJsonStructure(JsonNode expectedNode, JsonNode actualNode) { Set expectedFields = new HashSet<>(); Iterator fieldsIterator = expectedNode.fieldNames(); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java index 698183ea0a..d2a5167b43 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java @@ -178,8 +178,7 @@ public class JpaAssetDao extends JpaAbstractSearchTextDao im } @Override - public Long countAssetsByTenantId(TenantId tenantId) { + public Long countByTenantId(TenantId tenantId) { return assetRepository.countByTenantId(tenantId.getId()); - } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/customer/JpaCustomerDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/customer/JpaCustomerDao.java index 53c8b66038..fdfc12bf5a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/customer/JpaCustomerDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/customer/JpaCustomerDao.java @@ -65,7 +65,7 @@ public class JpaCustomerDao extends JpaAbstractSearchTextDao findByTenantIdAndProfileId(@Param("tenantId") UUID tenantId, - @Param("profileId") UUID profileId, - @Param("searchText") String searchText, - Pageable pageable); + @Param("profileId") UUID profileId, + @Param("searchText") String searchText, + Pageable pageable); @Query("SELECT new org.thingsboard.server.dao.model.sql.DeviceInfoEntity(d, c.title, c.additionalInfo, p.name) " + "FROM DeviceEntity d " + @@ -62,9 +62,9 @@ public interface DeviceRepository extends PagingAndSortingRepository findDeviceInfosByTenantIdAndCustomerId(@Param("tenantId") UUID tenantId, - @Param("customerId") UUID customerId, - @Param("searchText") String searchText, - Pageable pageable); + @Param("customerId") UUID customerId, + @Param("searchText") String searchText, + Pageable pageable); @Query("SELECT d FROM DeviceEntity d WHERE d.tenantId = :tenantId") Page findByTenantId(@Param("tenantId") UUID tenantId, @@ -102,9 +102,9 @@ public interface DeviceRepository extends PagingAndSortingRepository findDeviceInfosByTenantIdAndType(@Param("tenantId") UUID tenantId, - @Param("type") String type, - @Param("textSearch") String textSearch, - Pageable pageable); + @Param("type") String type, + @Param("textSearch") String textSearch, + Pageable pageable); @Query("SELECT new org.thingsboard.server.dao.model.sql.DeviceInfoEntity(d, c.title, c.additionalInfo, p.name) " + "FROM DeviceEntity d " + @@ -137,10 +137,10 @@ public interface DeviceRepository extends PagingAndSortingRepository findDeviceInfosByTenantIdAndCustomerIdAndType(@Param("tenantId") UUID tenantId, - @Param("customerId") UUID customerId, - @Param("type") String type, - @Param("textSearch") String textSearch, - Pageable pageable); + @Param("customerId") UUID customerId, + @Param("type") String type, + @Param("textSearch") String textSearch, + Pageable pageable); @Query("SELECT new org.thingsboard.server.dao.model.sql.DeviceInfoEntity(d, c.title, c.additionalInfo, p.name) " + "FROM DeviceEntity d " + diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java index fceefcadf9..7ce59bda0d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java @@ -220,7 +220,7 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao } @Override - public Long countDevicesByTenantId(TenantId tenantId) { + public Long countByTenantId(TenantId tenantId) { return deviceRepository.countByTenantId(tenantId.getId()); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java index 051d13f805..3331f57e00 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java @@ -58,7 +58,7 @@ public class JpaRuleChainDao extends JpaAbstractSearchTextDao imple } @Override - public Long countUsersByTenantId(TenantId tenantId) { + public Long countByTenantId(TenantId tenantId) { return userRepository.countByTenantId(tenantId.getId()); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java index c2b9e7ab6c..4cffadd595 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java @@ -20,10 +20,11 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; +import org.thingsboard.server.dao.TenantEntityDao; import java.util.UUID; -public interface UserDao extends Dao { +public interface UserDao extends Dao, TenantEntityDao { /** * Save or update user object @@ -49,7 +50,7 @@ public interface UserDao extends Dao { * @return the list of user entities */ PageData findByTenantId(UUID tenantId, PageLink pageLink); - + /** * Find tenant admin users by tenantId and page link. * @@ -58,7 +59,7 @@ public interface UserDao extends Dao { * @return the list of user entities */ PageData findTenantAdmins(UUID tenantId, PageLink pageLink); - + /** * Find customer users by tenantId, customerId and page link. * @@ -68,6 +69,4 @@ public interface UserDao extends Dao { * @return the list of user entities */ PageData findCustomerUsers(UUID tenantId, UUID customerId, PageLink pageLink); - - Long countUsersByTenantId(TenantId tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java index 0f13cdf3c6..a19ec57f48 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java @@ -27,6 +27,7 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.id.CustomerId; @@ -377,12 +378,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic DefaultTenantProfileConfiguration profileConfiguration = (DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); long maxUsers = profileConfiguration.getMaxUsers(); - if (maxUsers > 0) { - long currentUsersCount = userDao.countUsersByTenantId(tenantId); - if (currentUsersCount >= maxUsers) { - throw new DataValidationException("Can't create users more then " + maxUsers); - } - } + validateNumberOfEntitiesPerTenant(tenantId, userDao, maxUsers, EntityType.USER); } } From 2df72b4ec130f472697032340c32b3cd21b80fef Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Fri, 13 Nov 2020 15:51:28 +0200 Subject: [PATCH 18/18] license format --- .../thingsboard/server/dao/TenantEntityDao.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/dao/src/main/java/org/thingsboard/server/dao/TenantEntityDao.java b/dao/src/main/java/org/thingsboard/server/dao/TenantEntityDao.java index 0ac51a0dba..e47dd1e301 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/TenantEntityDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/TenantEntityDao.java @@ -1,3 +1,18 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.thingsboard.server.dao; import org.thingsboard.server.common.data.id.TenantId;