Browse Source

Merge branch 'master' into feature/log-telemetry-updated

pull/3602/head
Viacheslav Kukhtyn 6 years ago
parent
commit
c03356e944
  1. 5
      application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
  2. 10
      application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java
  3. 3
      application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
  4. 1
      application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java
  5. 2
      application/src/main/java/org/thingsboard/server/service/queue/processing/AbstractConsumerService.java
  6. 13
      application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java
  7. 14
      application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationFailureHandler.java
  8. 10
      application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java
  9. 7
      application/src/main/java/org/thingsboard/server/service/security/system/DefaultSystemSecurityService.java
  10. 2
      application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java
  11. 25
      application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java
  12. 1
      application/src/main/resources/thingsboard.yml
  13. 2
      common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentLifecycleEvent.java
  14. 4
      common/queue/src/main/java/org/thingsboard/server/queue/usagestats/DefaultTbApiUsageClient.java
  15. 14
      common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java
  16. 24
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/limits/DefaultTransportRateLimitService.java
  17. 2
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
  18. 5
      dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java
  19. 2
      dao/src/main/java/org/thingsboard/server/dao/sql/query/EntityKeyMapping.java
  20. 14
      dao/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateServiceImpl.java
  21. 2
      docker/README.md
  22. 27
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmState.java
  23. 3
      ui-ngx/src/app/core/http/entity.service.ts
  24. 6
      ui-ngx/src/app/core/services/dashboard-utils.service.ts
  25. 3
      ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.html
  26. 13
      ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.ts
  27. 18
      ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts
  28. 2
      ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts
  29. 22
      ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts
  30. 9
      ui-ngx/src/app/shared/models/entity-type.models.ts
  31. 4
      ui-ngx/src/assets/locale/locale.constant-en_US.json

5
application/src/main/java/org/thingsboard/server/controller/TelemetryController.java

@ -392,6 +392,11 @@ public class TelemetryController extends BaseController {
if (attributes.isEmpty()) { if (attributes.isEmpty()) {
return getImmediateDeferredResult("No attributes data found in request body!", HttpStatus.BAD_REQUEST); return getImmediateDeferredResult("No attributes data found in request body!", HttpStatus.BAD_REQUEST);
} }
for (AttributeKvEntry attributeKvEntry: attributes) {
if (attributeKvEntry.getKey().isEmpty() || attributeKvEntry.getKey().trim().length() == 0) {
return getImmediateDeferredResult("Key cannot be empty or contains only spaces", HttpStatus.BAD_REQUEST);
}
}
SecurityUser user = getCurrentUser(); SecurityUser user = getCurrentUser();
return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.WRITE_ATTRIBUTES, entityIdSrc, (result, tenantId, entityId) -> { return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.WRITE_ATTRIBUTES, entityIdSrc, (result, tenantId, entityId) -> {
tsSubService.saveAndNotify(tenantId, entityId, scope, attributes, new FutureCallback<Void>() { tsSubService.saveAndNotify(tenantId, entityId, scope, attributes, new FutureCallback<Void>() {

10
application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java

@ -221,6 +221,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
@Override @Override
public void onTenantProfileUpdate(TenantProfileId tenantProfileId) { public void onTenantProfileUpdate(TenantProfileId tenantProfileId) {
log.info("[{}] On Tenant Profile Update", tenantProfileId);
TenantProfile tenantProfile = tenantProfileCache.get(tenantProfileId); TenantProfile tenantProfile = tenantProfileCache.get(tenantProfileId);
updateLock.lock(); updateLock.lock();
try { try {
@ -236,6 +237,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
@Override @Override
public void onTenantUpdate(TenantId tenantId) { public void onTenantUpdate(TenantId tenantId) {
log.info("[{}] On Tenant Update.", tenantId);
TenantProfile tenantProfile = tenantProfileCache.get(tenantId); TenantProfile tenantProfile = tenantProfileCache.get(tenantId);
updateLock.lock(); updateLock.lock();
try { try {
@ -248,16 +250,16 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
} }
} }
private void updateTenantState(TenantApiUsageState state, TenantProfile tenantProfile) { private void updateTenantState(TenantApiUsageState state, TenantProfile profile) {
TenantProfileData oldProfileData = state.getTenantProfileData(); TenantProfileData oldProfileData = state.getTenantProfileData();
state.setTenantProfileId(tenantProfile.getId()); state.setTenantProfileId(profile.getId());
state.setTenantProfileData(tenantProfile.getProfileData()); state.setTenantProfileData(profile.getProfileData());
Map<ApiFeature, ApiUsageStateValue> result = state.checkStateUpdatedDueToThresholds(); Map<ApiFeature, ApiUsageStateValue> result = state.checkStateUpdatedDueToThresholds();
if (!result.isEmpty()) { if (!result.isEmpty()) {
persistAndNotify(state, result); persistAndNotify(state, result);
} }
updateProfileThresholds(state.getTenantId(), state.getApiUsageState().getId(), updateProfileThresholds(state.getTenantId(), state.getApiUsageState().getId(),
oldProfileData.getConfiguration(), tenantProfile.getProfileData().getConfiguration()); oldProfileData.getConfiguration(), profile.getProfileData().getConfiguration());
} }
private void updateProfileThresholds(TenantId tenantId, ApiUsageStateId id, private void updateProfileThresholds(TenantId tenantId, ApiUsageStateId id,

3
application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java

@ -134,7 +134,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
TenantProfile isolatedTbCoreProfile = new TenantProfile(); TenantProfile isolatedTbCoreProfile = new TenantProfile();
isolatedTbCoreProfile.setDefault(false); isolatedTbCoreProfile.setDefault(false);
isolatedTbCoreProfile.setName("Isolated TB Core"); isolatedTbCoreProfile.setName("Isolated TB Core");
isolatedTbCoreProfile.setProfileData(new TenantProfileData());
isolatedTbCoreProfile.setDescription("Isolated TB Core tenant profile"); isolatedTbCoreProfile.setDescription("Isolated TB Core tenant profile");
isolatedTbCoreProfile.setIsolatedTbCore(true); isolatedTbCoreProfile.setIsolatedTbCore(true);
isolatedTbCoreProfile.setIsolatedTbRuleEngine(false); isolatedTbCoreProfile.setIsolatedTbRuleEngine(false);
@ -148,7 +147,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
TenantProfile isolatedTbRuleEngineProfile = new TenantProfile(); TenantProfile isolatedTbRuleEngineProfile = new TenantProfile();
isolatedTbRuleEngineProfile.setDefault(false); isolatedTbRuleEngineProfile.setDefault(false);
isolatedTbRuleEngineProfile.setName("Isolated TB Rule Engine"); isolatedTbRuleEngineProfile.setName("Isolated TB Rule Engine");
isolatedTbRuleEngineProfile.setProfileData(new TenantProfileData());
isolatedTbRuleEngineProfile.setDescription("Isolated TB Rule Engine tenant profile"); isolatedTbRuleEngineProfile.setDescription("Isolated TB Rule Engine tenant profile");
isolatedTbRuleEngineProfile.setIsolatedTbCore(false); isolatedTbRuleEngineProfile.setIsolatedTbCore(false);
isolatedTbRuleEngineProfile.setIsolatedTbRuleEngine(true); isolatedTbRuleEngineProfile.setIsolatedTbRuleEngine(true);
@ -163,7 +161,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
TenantProfile isolatedTbCoreAndTbRuleEngineProfile = new TenantProfile(); TenantProfile isolatedTbCoreAndTbRuleEngineProfile = new TenantProfile();
isolatedTbCoreAndTbRuleEngineProfile.setDefault(false); isolatedTbCoreAndTbRuleEngineProfile.setDefault(false);
isolatedTbCoreAndTbRuleEngineProfile.setName("Isolated TB Core and TB Rule Engine"); isolatedTbCoreAndTbRuleEngineProfile.setName("Isolated TB Core and TB Rule Engine");
isolatedTbCoreAndTbRuleEngineProfile.setProfileData(new TenantProfileData());
isolatedTbCoreAndTbRuleEngineProfile.setDescription("Isolated TB Core and TB Rule Engine tenant profile"); isolatedTbCoreAndTbRuleEngineProfile.setDescription("Isolated TB Core and TB Rule Engine tenant profile");
isolatedTbCoreAndTbRuleEngineProfile.setIsolatedTbCore(true); isolatedTbCoreAndTbRuleEngineProfile.setIsolatedTbCore(true);
isolatedTbCoreAndTbRuleEngineProfile.setIsolatedTbRuleEngine(true); isolatedTbCoreAndTbRuleEngineProfile.setIsolatedTbRuleEngine(true);

1
application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java

@ -274,6 +274,7 @@ public class DefaultTbClusterService implements TbClusterService {
TbQueueProducer<TbProtoQueueMsg<ToRuleEngineNotificationMsg>> toRuleEngineProducer = producerProvider.getRuleEngineNotificationsMsgProducer(); TbQueueProducer<TbProtoQueueMsg<ToRuleEngineNotificationMsg>> toRuleEngineProducer = producerProvider.getRuleEngineNotificationsMsgProducer();
Set<String> tbRuleEngineServices = new HashSet<>(partitionService.getAllServiceIds(ServiceType.TB_RULE_ENGINE)); Set<String> tbRuleEngineServices = new HashSet<>(partitionService.getAllServiceIds(ServiceType.TB_RULE_ENGINE));
if (msg.getEntityId().getEntityType().equals(EntityType.TENANT) if (msg.getEntityId().getEntityType().equals(EntityType.TENANT)
|| msg.getEntityId().getEntityType().equals(EntityType.TENANT_PROFILE)
|| msg.getEntityId().getEntityType().equals(EntityType.DEVICE_PROFILE) || msg.getEntityId().getEntityType().equals(EntityType.DEVICE_PROFILE)
|| msg.getEntityId().getEntityType().equals(EntityType.API_USAGE_STATE)) { || msg.getEntityId().getEntityType().equals(EntityType.API_USAGE_STATE)) {
TbQueueProducer<TbProtoQueueMsg<ToCoreNotificationMsg>> toCoreNfProducer = producerProvider.getTbCoreNotificationsMsgProducer(); TbQueueProducer<TbProtoQueueMsg<ToCoreNotificationMsg>> toCoreNfProducer = producerProvider.getTbCoreNotificationsMsgProducer();

2
application/src/main/java/org/thingsboard/server/service/queue/processing/AbstractConsumerService.java

@ -153,6 +153,8 @@ public abstract class AbstractConsumerService<N extends com.google.protobuf.Gene
TbActorMsg actorMsg = actorMsgOpt.get(); TbActorMsg actorMsg = actorMsgOpt.get();
if (actorMsg instanceof ComponentLifecycleMsg) { if (actorMsg instanceof ComponentLifecycleMsg) {
ComponentLifecycleMsg componentLifecycleMsg = (ComponentLifecycleMsg) actorMsg; ComponentLifecycleMsg componentLifecycleMsg = (ComponentLifecycleMsg) actorMsg;
log.info("[{}][{}][{}] Received Lifecycle event: {}", componentLifecycleMsg.getTenantId(), componentLifecycleMsg.getEntityId().getEntityType(),
componentLifecycleMsg.getEntityId(), componentLifecycleMsg.getEvent());
if (EntityType.TENANT_PROFILE.equals(componentLifecycleMsg.getEntityId().getEntityType())) { if (EntityType.TENANT_PROFILE.equals(componentLifecycleMsg.getEntityId().getEntityType())) {
TenantProfileId tenantProfileId = new TenantProfileId(componentLifecycleMsg.getEntityId().getId()); TenantProfileId tenantProfileId = new TenantProfileId(componentLifecycleMsg.getEntityId().getId());
tenantProfileCache.evict(tenantProfileId); tenantProfileCache.evict(tenantProfileId);

13
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.oauth2.OAuth2MapperConfig;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink; 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.Authority;
import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.common.data.security.UserCredentials;
import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.customer.CustomerService;
import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.oauth2.OAuth2User; 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.tenant.TenantService;
import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.service.install.InstallScripts; 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.SecurityUser;
import org.thingsboard.server.service.security.model.UserPrincipal; import org.thingsboard.server.service.security.model.UserPrincipal;
@ -76,6 +79,12 @@ public abstract class AbstractOAuth2ClientMapper {
@Autowired @Autowired
private InstallScripts installScripts; private InstallScripts installScripts;
@Autowired
protected TbTenantProfileCache tenantProfileCache;
@Autowired
protected TbClusterService tbClusterService;
private final Lock userCreationLock = new ReentrantLock(); private final Lock userCreationLock = new ReentrantLock();
protected SecurityUser getOrCreateSecurityUserFromOAuth2User(OAuth2User oauth2User, OAuth2ClientRegistrationInfo clientRegistration) { protected SecurityUser getOrCreateSecurityUserFromOAuth2User(OAuth2User oauth2User, OAuth2ClientRegistrationInfo clientRegistration) {
@ -162,6 +171,10 @@ public abstract class AbstractOAuth2ClientMapper {
tenant.setTitle(tenantName); tenant.setTitle(tenantName);
tenant = tenantService.saveTenant(tenant); tenant = tenantService.saveTenant(tenant);
installScripts.createDefaultRuleChains(tenant.getId()); installScripts.createDefaultRuleChains(tenant.getId());
tenantProfileCache.evict(tenant.getId());
tbClusterService.onTenantChange(tenant, null);
tbClusterService.onEntityStateChange(tenant.getId(), tenant.getId(),
ComponentLifecycleEvent.CREATED);
} else { } else {
tenant = tenants.get(0); tenant = tenants.get(0);
} }

14
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; 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.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.stereotype.Component; 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 org.thingsboard.server.utils.MiscUtils;
import javax.servlet.ServletException; import javax.servlet.ServletException;
@ -32,11 +37,18 @@ import java.nio.charset.StandardCharsets;
@ConditionalOnProperty(prefix = "security.oauth2", value = "enabled", havingValue = "true") @ConditionalOnProperty(prefix = "security.oauth2", value = "enabled", havingValue = "true")
public class Oauth2AuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler { public class Oauth2AuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
private final SystemSecurityService systemSecurityService;
@Autowired
public Oauth2AuthenticationFailureHandler(final SystemSecurityService systemSecurityService) {
this.systemSecurityService = systemSecurityService;
}
@Override @Override
public void onAuthenticationFailure(HttpServletRequest request, public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response, AuthenticationException exception) HttpServletResponse response, AuthenticationException exception)
throws IOException, ServletException { 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=" + getRedirectStrategy().sendRedirect(request, response, baseUrl + "/login?loginError=" +
URLEncoder.encode(exception.getMessage(), StandardCharsets.UTF_8.toString())); URLEncoder.encode(exception.getMessage(), StandardCharsets.UTF_8.toString()));
} }

10
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.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.stereotype.Component; 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.common.data.oauth2.OAuth2ClientRegistrationInfo;
import org.thingsboard.server.dao.oauth2.OAuth2Service; import org.thingsboard.server.dao.oauth2.OAuth2Service;
import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository; import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository;
import org.thingsboard.server.service.security.model.SecurityUser; 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.JwtToken;
import org.thingsboard.server.service.security.model.token.JwtTokenFactory; import org.thingsboard.server.service.security.model.token.JwtTokenFactory;
import org.thingsboard.server.service.security.system.SystemSecurityService;
import org.thingsboard.server.utils.MiscUtils; import org.thingsboard.server.utils.MiscUtils;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -45,25 +49,27 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS
private final OAuth2ClientMapperProvider oauth2ClientMapperProvider; private final OAuth2ClientMapperProvider oauth2ClientMapperProvider;
private final OAuth2Service oAuth2Service; private final OAuth2Service oAuth2Service;
private final OAuth2AuthorizedClientService oAuth2AuthorizedClientService; private final OAuth2AuthorizedClientService oAuth2AuthorizedClientService;
private final SystemSecurityService systemSecurityService;
@Autowired @Autowired
public Oauth2AuthenticationSuccessHandler(final JwtTokenFactory tokenFactory, public Oauth2AuthenticationSuccessHandler(final JwtTokenFactory tokenFactory,
final RefreshTokenRepository refreshTokenRepository, final RefreshTokenRepository refreshTokenRepository,
final OAuth2ClientMapperProvider oauth2ClientMapperProvider, final OAuth2ClientMapperProvider oauth2ClientMapperProvider,
final OAuth2Service oAuth2Service, final OAuth2Service oAuth2Service,
final OAuth2AuthorizedClientService oAuth2AuthorizedClientService) { final OAuth2AuthorizedClientService oAuth2AuthorizedClientService, final SystemSecurityService systemSecurityService) {
this.tokenFactory = tokenFactory; this.tokenFactory = tokenFactory;
this.refreshTokenRepository = refreshTokenRepository; this.refreshTokenRepository = refreshTokenRepository;
this.oauth2ClientMapperProvider = oauth2ClientMapperProvider; this.oauth2ClientMapperProvider = oauth2ClientMapperProvider;
this.oAuth2Service = oAuth2Service; this.oAuth2Service = oAuth2Service;
this.oAuth2AuthorizedClientService = oAuth2AuthorizedClientService; this.oAuth2AuthorizedClientService = oAuth2AuthorizedClientService;
this.systemSecurityService = systemSecurityService;
} }
@Override @Override
public void onAuthenticationSuccess(HttpServletRequest request, public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, HttpServletResponse response,
Authentication authentication) throws IOException { 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 { try {
OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) authentication; OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) authentication;

7
application/src/main/java/org/thingsboard/server/service/security/system/DefaultSystemSecurityService.java

@ -202,16 +202,19 @@ public class DefaultSystemSecurityService implements SystemSecurityService {
@Override @Override
public String getBaseUrl(TenantId tenantId, CustomerId customerId, HttpServletRequest httpServletRequest) { public String getBaseUrl(TenantId tenantId, CustomerId customerId, HttpServletRequest httpServletRequest) {
String baseUrl; String baseUrl = null;
AdminSettings generalSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "general"); AdminSettings generalSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "general");
JsonNode prohibitDifferentUrl = generalSettings.getJsonValue().get("prohibitDifferentUrl"); JsonNode prohibitDifferentUrl = generalSettings.getJsonValue().get("prohibitDifferentUrl");
if (prohibitDifferentUrl != null && prohibitDifferentUrl.asBoolean()) { if (prohibitDifferentUrl != null && prohibitDifferentUrl.asBoolean()) {
baseUrl = generalSettings.getJsonValue().get("baseUrl").asText(); baseUrl = generalSettings.getJsonValue().get("baseUrl").asText();
} else { }
if (StringUtils.isEmpty(baseUrl)) {
baseUrl = MiscUtils.constructBaseUrl(httpServletRequest); baseUrl = MiscUtils.constructBaseUrl(httpServletRequest);
} }
return baseUrl; return baseUrl;
} }

2
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. * Since number of subscriptions is usually much less then number of devices that are pushing data.
*/ */
subscriptionsBySessionId.values().forEach(map -> map.values() subscriptionsBySessionId.values().forEach(map -> map.values()
.forEach(sub -> pushSubscriptionToManagerService(sub, false))); .forEach(sub -> pushSubscriptionToManagerService(sub, true)));
} }
} }

25
application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java

@ -164,22 +164,25 @@ public class DefaultTransportApiService implements TransportApiService {
} }
private ListenableFuture<TransportApiResponseMsg> validateCredentials(TransportProtos.ValidateBasicMqttCredRequestMsg mqtt) { private ListenableFuture<TransportApiResponseMsg> validateCredentials(TransportProtos.ValidateBasicMqttCredRequestMsg mqtt) {
DeviceCredentials credentials = deviceCredentialsService.findDeviceCredentialsByCredentialsId(mqtt.getUserName()); DeviceCredentials credentials = null;
if (credentials != null) { if (!StringUtils.isEmpty(mqtt.getUserName())) {
if (credentials.getCredentialsType() == DeviceCredentialsType.ACCESS_TOKEN) { credentials = deviceCredentialsService.findDeviceCredentialsByCredentialsId(mqtt.getUserName());
return getDeviceInfo(credentials.getDeviceId(), credentials); if (credentials != null) {
} else if (credentials.getCredentialsType() == DeviceCredentialsType.MQTT_BASIC) { if (credentials.getCredentialsType() == DeviceCredentialsType.ACCESS_TOKEN) {
if (!checkMqttCredentials(mqtt, credentials)) { return getDeviceInfo(credentials.getDeviceId(), credentials);
credentials = null; } else if (credentials.getCredentialsType() == DeviceCredentialsType.MQTT_BASIC) {
if (!checkMqttCredentials(mqtt, credentials)) {
credentials = null;
}
} }
} }
}
if (credentials == null) {
credentials = checkMqttCredentials(mqtt, EncryptionUtil.getSha3Hash("|", mqtt.getClientId(), mqtt.getUserName()));
if (credentials == null) { if (credentials == null) {
credentials = checkMqttCredentials(mqtt, EncryptionUtil.getSha3Hash(mqtt.getClientId())); credentials = checkMqttCredentials(mqtt, EncryptionUtil.getSha3Hash("|", mqtt.getClientId(), mqtt.getUserName()));
} }
} }
if (credentials == null) {
credentials = checkMqttCredentials(mqtt, EncryptionUtil.getSha3Hash(mqtt.getClientId()));
}
if (credentials != null) { if (credentials != null) {
return getDeviceInfo(credentials.getDeviceId(), credentials); return getDeviceInfo(credentials.getDeviceId(), credentials);
} else { } else {

1
application/src/main/resources/thingsboard.yml

@ -766,7 +766,6 @@ metrics:
# Metrics percentiles returned by actuator for timer metrics. List of double values (divided by ,). # Metrics percentiles returned by actuator for timer metrics. List of double values (divided by ,).
percentiles: "${METRICS_TIMER_PERCENTILES:0.5}" percentiles: "${METRICS_TIMER_PERCENTILES:0.5}"
management: management:
endpoints: endpoints:
web: web:

2
common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentLifecycleEvent.java

@ -21,5 +21,5 @@ import java.io.Serializable;
* @author Andrew Shvayka * @author Andrew Shvayka
*/ */
public enum ComponentLifecycleEvent implements Serializable { 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
} }

4
common/queue/src/main/java/org/thingsboard/server/queue/usagestats/DefaultTbApiUsageClient.java

@ -94,7 +94,9 @@ public class DefaultTbApiUsageClient implements TbApiUsageClient {
TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, tenantId).newByTopic(msgProducer.getDefaultTopic()); TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, tenantId).newByTopic(msgProducer.getDefaultTopic());
msgProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), builder.build()), null); msgProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), builder.build()), null);
})); }));
log.info("Report statistics for: {} tenants", report.size()); if (!report.isEmpty()) {
log.info("Report statistics for: {} tenants", report.size());
}
} catch (Exception e) { } catch (Exception e) {
log.warn("Failed to report statistics: ", e); log.warn("Failed to report statistics: ", e);
} }

14
common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java

@ -122,7 +122,13 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
log.trace("[{}] Processing msg: {}", sessionId, msg); log.trace("[{}] Processing msg: {}", sessionId, msg);
try { try {
if (msg instanceof MqttMessage) { if (msg instanceof MqttMessage) {
processMqttMsg(ctx, (MqttMessage) msg); MqttMessage message = (MqttMessage) msg;
if (message.decoderResult().isSuccess()) {
processMqttMsg(ctx, message);
} else {
log.error("[{}] Message processing failed: {}", sessionId, message.decoderResult().cause().getMessage());
ctx.close();
}
} else { } else {
ctx.close(); ctx.close();
} }
@ -464,8 +470,10 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
String userName = msg.payload().userName(); String userName = msg.payload().userName();
log.info("[{}] Processing connect msg for client with user name: {}!", sessionId, userName); log.info("[{}] Processing connect msg for client with user name: {}!", sessionId, userName);
TransportProtos.ValidateBasicMqttCredRequestMsg.Builder request = TransportProtos.ValidateBasicMqttCredRequestMsg.newBuilder() TransportProtos.ValidateBasicMqttCredRequestMsg.Builder request = TransportProtos.ValidateBasicMqttCredRequestMsg.newBuilder()
.setClientId(msg.payload().clientIdentifier()) .setClientId(msg.payload().clientIdentifier());
.setUserName(userName); if (userName != null) {
request.setUserName(userName);
}
String password = msg.payload().password(); String password = msg.payload().password();
if (password != null) { if (password != null) {
request.setPassword(password); request.setPassword(password);

24
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/limits/DefaultTransportRateLimitService.java

@ -21,6 +21,7 @@ import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; import org.thingsboard.server.common.data.tenant.profile.TenantProfileData;
@ -77,6 +78,7 @@ public class DefaultTransportRateLimitService implements TransportRateLimitServi
@Override @Override
public void update(TenantProfileUpdateResult update) { public void update(TenantProfileUpdateResult update) {
log.info("Received tenant profile update: {}", update.getProfile());
EntityTransportRateLimits tenantRateLimitPrototype = createRateLimits(update.getProfile(), true); EntityTransportRateLimits tenantRateLimitPrototype = createRateLimits(update.getProfile(), true);
EntityTransportRateLimits deviceRateLimitPrototype = createRateLimits(update.getProfile(), false); EntityTransportRateLimits deviceRateLimitPrototype = createRateLimits(update.getProfile(), false);
for (TenantId tenantId : update.getAffectedTenants()) { for (TenantId tenantId : update.getAffectedTenants()) {
@ -114,16 +116,26 @@ public class DefaultTransportRateLimitService implements TransportRateLimitServi
tenantAllowed.put(tenantId, allowed); tenantAllowed.put(tenantId, allowed);
} }
private <T> void mergeLimits(T deviceId, EntityTransportRateLimits newRateLimits, private <T extends EntityId> void mergeLimits(T entityId, EntityTransportRateLimits newRateLimits,
Function<T, EntityTransportRateLimits> getFunction, Function<T, EntityTransportRateLimits> getFunction,
BiConsumer<T, EntityTransportRateLimits> putFunction) { BiConsumer<T, EntityTransportRateLimits> putFunction) {
EntityTransportRateLimits oldRateLimits = getFunction.apply(deviceId); EntityTransportRateLimits oldRateLimits = getFunction.apply(entityId);
if (oldRateLimits == null) { if (oldRateLimits == null) {
putFunction.accept(deviceId, newRateLimits); if (EntityType.TENANT.equals(entityId.getEntityType())) {
log.info("[{}] New rate limits: {}", entityId, newRateLimits);
} else {
log.debug("[{}] New rate limits: {}", entityId, newRateLimits);
}
putFunction.accept(entityId, newRateLimits);
} else { } else {
EntityTransportRateLimits updated = merge(oldRateLimits, newRateLimits); EntityTransportRateLimits updated = merge(oldRateLimits, newRateLimits);
if (updated != null) { if (updated != null) {
putFunction.accept(deviceId, updated); if (EntityType.TENANT.equals(entityId.getEntityType())) {
log.info("[{}] Updated rate limits: {}", entityId, updated);
} else {
log.debug("[{}] Updated rate limits: {}", entityId, updated);
}
putFunction.accept(entityId, updated);
} }
} }
} }

2
dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java

@ -548,7 +548,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
@Override @Override
protected void validateDataImpl(TenantId tenantId, Device device) { protected void validateDataImpl(TenantId tenantId, Device device) {
if (StringUtils.isEmpty(device.getName())) { if (StringUtils.isEmpty(device.getName()) || device.getName().trim().length() == 0) {
throw new DataValidationException("Device name should be specified!"); throw new DataValidationException("Device name should be specified!");
} }
if (device.getTenantId() == null) { if (device.getTenantId() == null) {

5
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)" + " THEN (select additional_info from entity_view where id = entity_id)" +
" END as additional_info"; " 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 { static {
entityTableMap.put(EntityType.ASSET, "asset"); entityTableMap.put(EntityType.ASSET, "asset");
entityTableMap.put(EntityType.DEVICE, "device"); entityTableMap.put(EntityType.DEVICE, "device");
@ -210,7 +213,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
entityTableMap.put(EntityType.CUSTOMER, "customer"); entityTableMap.put(EntityType.CUSTOMER, "customer");
entityTableMap.put(EntityType.USER, "tb_user"); entityTableMap.put(EntityType.USER, "tb_user");
entityTableMap.put(EntityType.TENANT, "tenant"); entityTableMap.put(EntityType.TENANT, "tenant");
entityTableMap.put(EntityType.API_USAGE_STATE, "(select aus.id, aus.created_time, aus.tenant_id, '' as name, '' as additional_info from api_usage_state as aus)"); entityTableMap.put(EntityType.API_USAGE_STATE, SELECT_API_USAGE_STATE);
} }
public static EntityType[] RELATION_QUERY_ENTITY_TYPES = new EntityType[]{ public static EntityType[] RELATION_QUERY_ENTITY_TYPES = new EntityType[]{

2
dao/src/main/java/org/thingsboard/server/dao/sql/query/EntityKeyMapping.java

@ -80,6 +80,7 @@ public class EntityKeyMapping {
public static final List<String> labeledEntityFields = Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME, TYPE, LABEL, ADDITIONAL_INFO); public static final List<String> labeledEntityFields = Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME, TYPE, LABEL, ADDITIONAL_INFO);
public static final List<String> contactBasedEntityFields = Arrays.asList(CREATED_TIME, ENTITY_TYPE, EMAIL, TITLE, COUNTRY, STATE, CITY, ADDRESS, ADDRESS_2, ZIP, PHONE, ADDITIONAL_INFO); public static final List<String> contactBasedEntityFields = Arrays.asList(CREATED_TIME, ENTITY_TYPE, EMAIL, TITLE, COUNTRY, STATE, CITY, ADDRESS, ADDRESS_2, ZIP, PHONE, ADDITIONAL_INFO);
public static final Set<String> apiUsageStateEntityFields = new HashSet<>(Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME));
public static final Set<String> commonEntityFieldsSet = new HashSet<>(commonEntityFields); public static final Set<String> commonEntityFieldsSet = new HashSet<>(commonEntityFields);
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)); 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));
@ -99,6 +100,7 @@ public class EntityKeyMapping {
allowedEntityFieldMap.put(EntityType.RULE_NODE, new HashSet<>(commonEntityFields)); allowedEntityFieldMap.put(EntityType.RULE_NODE, new HashSet<>(commonEntityFields));
allowedEntityFieldMap.put(EntityType.WIDGET_TYPE, new HashSet<>(widgetEntityFields)); allowedEntityFieldMap.put(EntityType.WIDGET_TYPE, new HashSet<>(widgetEntityFields));
allowedEntityFieldMap.put(EntityType.WIDGETS_BUNDLE, new HashSet<>(widgetEntityFields)); allowedEntityFieldMap.put(EntityType.WIDGETS_BUNDLE, new HashSet<>(widgetEntityFields));
allowedEntityFieldMap.put(EntityType.API_USAGE_STATE, apiUsageStateEntityFields);
entityFieldColumnMap.put(CREATED_TIME, ModelConstants.CREATED_TIME_PROPERTY); entityFieldColumnMap.put(CREATED_TIME, ModelConstants.CREATED_TIME_PROPERTY);
entityFieldColumnMap.put(ENTITY_TYPE, ModelConstants.ENTITY_TYPE_PROPERTY); entityFieldColumnMap.put(ENTITY_TYPE, ModelConstants.ENTITY_TYPE_PROPERTY);

14
dao/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateServiceImpl.java

@ -17,6 +17,7 @@ package org.thingsboard.server.dao.usagerecord;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.ApiFeature;
import org.thingsboard.server.common.data.ApiUsageRecordKey; import org.thingsboard.server.common.data.ApiUsageRecordKey;
import org.thingsboard.server.common.data.ApiUsageState; import org.thingsboard.server.common.data.ApiUsageState;
import org.thingsboard.server.common.data.ApiUsageStateValue; import org.thingsboard.server.common.data.ApiUsageStateValue;
@ -27,6 +28,7 @@ import org.thingsboard.server.common.data.id.ApiUsageStateId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.common.data.kv.LongDataEntry;
import org.thingsboard.server.common.data.kv.StringDataEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.tenant.profile.TenantProfileConfiguration; import org.thingsboard.server.common.data.tenant.profile.TenantProfileConfiguration;
import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.entity.AbstractEntityService;
@ -83,7 +85,19 @@ public class ApiUsageStateServiceImpl extends AbstractEntityService implements A
Tenant tenant = tenantDao.findById(tenantId, tenantId.getId()); Tenant tenant = tenantDao.findById(tenantId, tenantId.getId());
TenantProfile tenantProfile = tenantProfileDao.findById(tenantId, tenant.getTenantProfileId().getId()); TenantProfile tenantProfile = tenantProfileDao.findById(tenantId, tenant.getTenantProfileId().getId());
TenantProfileConfiguration configuration = tenantProfile.getProfileData().getConfiguration(); TenantProfileConfiguration configuration = tenantProfile.getProfileData().getConfiguration();
List<TsKvEntry> apiUsageStates = new ArrayList<>();
apiUsageStates.add(new BasicTsKvEntry(saved.getCreatedTime(),
new StringDataEntry(ApiFeature.TRANSPORT.getApiStateKey(), ApiUsageStateValue.ENABLED.name())));
apiUsageStates.add(new BasicTsKvEntry(saved.getCreatedTime(),
new StringDataEntry(ApiFeature.DB.getApiStateKey(), ApiUsageStateValue.ENABLED.name())));
apiUsageStates.add(new BasicTsKvEntry(saved.getCreatedTime(),
new StringDataEntry(ApiFeature.RE.getApiStateKey(), ApiUsageStateValue.ENABLED.name())));
apiUsageStates.add(new BasicTsKvEntry(saved.getCreatedTime(),
new StringDataEntry(ApiFeature.JS.getApiStateKey(), ApiUsageStateValue.ENABLED.name())));
tsService.save(tenantId, saved.getId(), apiUsageStates, 0L);
List<TsKvEntry> profileThresholds = new ArrayList<>(); List<TsKvEntry> profileThresholds = new ArrayList<>();
for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) { for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) {
profileThresholds.add(new BasicTsKvEntry(saved.getCreatedTime(), new LongDataEntry(key.getApiLimitKey(), configuration.getProfileThreshold(key)))); profileThresholds.add(new BasicTsKvEntry(saved.getCreatedTime(), new LongDataEntry(key.getApiLimitKey(), configuration.getProfileThreshold(key))));
} }

2
docker/README.md

@ -58,7 +58,7 @@ In case of any issues you can examine service logs for errors.
For example to see ThingsBoard node logs execute the following command: For example to see ThingsBoard node logs execute the following command:
` `
$ docker-compose logs -f tb-core1 tb-rule-engine1 $ docker-compose logs -f tb-core1 tb-core2 tb-rule-engine1 tb-rule-engine2 tb-mqtt-transport1 tb-mqtt-transport2
` `
Or use `docker-compose ps` to see the state of all the containers. Or use `docker-compose ps` to see the state of all the containers.

27
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmState.java

@ -67,6 +67,7 @@ class AlarmState {
initCurrentAlarm(ctx); initCurrentAlarm(ctx);
lastMsgMetaData = msg.getMetaData(); lastMsgMetaData = msg.getMetaData();
lastMsgQueueName = msg.getQueueName(); lastMsgQueueName = msg.getQueueName();
this.dataSnapshot = data;
return createOrClearAlarms(ctx, data, update, AlarmRuleState::eval); return createOrClearAlarms(ctx, data, update, AlarmRuleState::eval);
} }
@ -90,8 +91,7 @@ class AlarmState {
resultState = state; resultState = state;
break; break;
} else if (AlarmEvalResult.FALSE.equals(evalResult)) { } else if (AlarmEvalResult.FALSE.equals(evalResult)) {
state.clear(); stateUpdate = clearAlarmState(stateUpdate, state);
stateUpdate |= state.checkUpdate();
} }
} }
if (resultState != null) { if (resultState != null) {
@ -99,6 +99,7 @@ class AlarmState {
if (result != null) { if (result != null) {
pushMsg(ctx, result); pushMsg(ctx, result);
} }
stateUpdate = clearAlarmState(stateUpdate, clearState);
} else if (currentAlarm != null && clearState != null) { } else if (currentAlarm != null && clearState != null) {
if (!validateUpdate(update, clearState)) { if (!validateUpdate(update, clearState)) {
log.debug("[{}] Update is not valid for current clear state", alarmDefinition.getId()); log.debug("[{}] Update is not valid for current clear state", alarmDefinition.getId());
@ -106,23 +107,26 @@ class AlarmState {
} }
AlarmEvalResult evalResult = evalFunction.apply(clearState, data); AlarmEvalResult evalResult = evalFunction.apply(clearState, data);
if (AlarmEvalResult.TRUE.equals(evalResult)) { if (AlarmEvalResult.TRUE.equals(evalResult)) {
clearState.clear(); stateUpdate = clearAlarmState(stateUpdate, clearState);
stateUpdate |= clearState.checkUpdate();
for (AlarmRuleState state : createRulesSortedBySeverityDesc) { for (AlarmRuleState state : createRulesSortedBySeverityDesc) {
state.clear(); stateUpdate = clearAlarmState(stateUpdate, state);
stateUpdate |= state.checkUpdate();
} }
ctx.getAlarmService().clearAlarm(ctx.getTenantId(), currentAlarm.getId(), JacksonUtil.OBJECT_MAPPER.createObjectNode(), System.currentTimeMillis()); ctx.getAlarmService().clearAlarm(ctx.getTenantId(), currentAlarm.getId(), JacksonUtil.OBJECT_MAPPER.createObjectNode(), System.currentTimeMillis());
pushMsg(ctx, new TbAlarmResult(false, false, true, currentAlarm)); pushMsg(ctx, new TbAlarmResult(false, false, true, currentAlarm));
currentAlarm = null; currentAlarm = null;
} else if (AlarmEvalResult.FALSE.equals(evalResult)) { } else if (AlarmEvalResult.FALSE.equals(evalResult)) {
clearState.clear(); stateUpdate = clearAlarmState(stateUpdate, clearState);
stateUpdate |= clearState.checkUpdate();
} }
} }
return stateUpdate; return stateUpdate;
} }
public boolean clearAlarmState(boolean stateUpdate, AlarmRuleState state) {
state.clear();
stateUpdate |= state.checkUpdate();
return stateUpdate;
}
public boolean validateUpdate(SnapshotUpdate update, AlarmRuleState state) { public boolean validateUpdate(SnapshotUpdate update, AlarmRuleState state) {
if (update != null) { if (update != null) {
//Check that the update type and that keys match. //Check that the update type and that keys match.
@ -190,7 +194,7 @@ class AlarmState {
} }
} }
private <T> TbAlarmResult calculateAlarmResult(TbContext ctx, AlarmRuleState ruleState) { private TbAlarmResult calculateAlarmResult(TbContext ctx, AlarmRuleState ruleState) {
AlarmSeverity severity = ruleState.getSeverity(); AlarmSeverity severity = ruleState.getSeverity();
if (currentAlarm != null) { 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). // 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 {
} }
} }
private <T> JsonNode createDetails(AlarmRuleState ruleState) { private JsonNode createDetails(AlarmRuleState ruleState) {
ObjectNode details = JacksonUtil.OBJECT_MAPPER.createObjectNode(); ObjectNode details = JacksonUtil.OBJECT_MAPPER.createObjectNode();
String alarmDetails = ruleState.getAlarmRule().getAlarmDetails(); String alarmDetails = ruleState.getAlarmRule().getAlarmDetails();
@ -273,8 +277,7 @@ class AlarmState {
if (currentAlarm != null && currentAlarm.getId().equals(alarmNf.getId())) { if (currentAlarm != null && currentAlarm.getId().equals(alarmNf.getId())) {
currentAlarm = null; currentAlarm = null;
for (AlarmRuleState state : createRulesSortedBySeverityDesc) { for (AlarmRuleState state : createRulesSortedBySeverityDesc) {
state.clear(); updated = clearAlarmState(updated, state);
updated |= state.checkUpdate();
} }
} }
return updated; return updated;

3
ui-ngx/src/app/core/http/entity.service.ts

@ -630,6 +630,9 @@ export class EntityService {
case EntityType.DASHBOARD: case EntityType.DASHBOARD:
entityFieldKeys.push(entityFields.title.keyName); entityFieldKeys.push(entityFields.title.keyName);
break; break;
case EntityType.API_USAGE_STATE:
entityFieldKeys.push(entityFields.name.keyName);
break;
} }
return query ? entityFieldKeys.filter((entityField) => entityField.toLowerCase().indexOf(query) === 0) : entityFieldKeys; return query ? entityFieldKeys.filter((entityField) => entityField.toLowerCase().indexOf(query) === 0) : entityFieldKeys;
} }

6
ui-ngx/src/app/core/services/dashboard-utils.service.ts

@ -422,6 +422,12 @@ export class DashboardUtilsService {
widgetLayout.row = row; widgetLayout.row = row;
widgetLayout.col = 0; widgetLayout.col = 0;
} }
widgetLayout.sizeX = Math.floor(widgetLayout.sizeX);
widgetLayout.sizeY = Math.floor(widgetLayout.sizeY);
widgetLayout.row = Math.floor(widgetLayout.row);
widgetLayout.col = Math.floor(widgetLayout.col);
layout.widgets[widget.id] = widgetLayout; layout.widgets[widget.id] = widgetLayout;
} }

3
ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.html

@ -63,6 +63,9 @@
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<mat-error *ngIf="mqttDeviceProfileTransportConfigurationFormGroup.get('configuration').hasError('unique')">
{{ 'device-profile.mqtt-device-topic-filters-unique' | translate }}
</mat-error>
<div class="tb-hint" innerHTML="{{ 'device-profile.support-level-wildcards' | translate }}"></div> <div class="tb-hint" innerHTML="{{ 'device-profile.support-level-wildcards' | translate }}"></div>
<div class="tb-hint" innerHTML="{{ 'device-profile.single-level-wildcards-hint' | translate }}"></div> <div class="tb-hint" innerHTML="{{ 'device-profile.single-level-wildcards-hint' | translate }}"></div>
<div class="tb-hint" innerHTML="{{ 'device-profile.multi-level-wildcards-hint' | translate }}"></div> <div class="tb-hint" innerHTML="{{ 'device-profile.multi-level-wildcards-hint' | translate }}"></div>

13
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; mqttTransportPayloadTypeTranslations = mqttTransportPayloadTypeTranslationMap;
mqttDeviceProfileTransportConfigurationFormGroup: FormGroup; mqttDeviceProfileTransportConfigurationFormGroup: FormGroup;
private requiredValue: boolean; private requiredValue: boolean;
@ -87,7 +86,7 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control
deviceAttributesTopic: [null, [Validators.required, this.validationMQTTTopic()]], deviceAttributesTopic: [null, [Validators.required, this.validationMQTTTopic()]],
deviceTelemetryTopic: [null, [Validators.required, this.validationMQTTTopic()]], deviceTelemetryTopic: [null, [Validators.required, this.validationMQTTTopic()]],
transportPayloadType: [MqttTransportPayloadType.JSON, Validators.required] transportPayloadType: [MqttTransportPayloadType.JSON, Validators.required]
}) }, {validator: this.uniqueDeviceTopicValidator})
}); });
this.mqttDeviceProfileTransportConfigurationFormGroup.valueChanges.subscribe(() => { this.mqttDeviceProfileTransportConfigurationFormGroup.valueChanges.subscribe(() => {
this.updateModel(); this.updateModel();
@ -147,4 +146,14 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control
return null; 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;
}
} }

18
ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts

@ -131,10 +131,13 @@ export default abstract class LeafletMap {
tooltipAnchor: [16, -28], tooltipAnchor: [16, -28],
shadowSize: [41, 41] shadowSize: [41, 41]
}); });
const customLatLng = this.convertToCustomFormat(mousePositionOnMap);
mousePositionOnMap.lat = customLatLng[this.options.latKeyName];
mousePositionOnMap.lng = customLatLng[this.options.lngKeyName];
const newMarker = L.marker(mousePositionOnMap, { icon }).addTo(this.map); const newMarker = L.marker(mousePositionOnMap, { icon }).addTo(this.map);
this.addMarkers.push(newMarker); this.addMarkers.push(newMarker);
const datasourcesList = document.createElement('div'); const datasourcesList = document.createElement('div');
const customLatLng = this.convertToCustomFormat(mousePositionOnMap);
const header = document.createElement('p'); const header = document.createElement('p');
header.appendChild(document.createTextNode('Select entity:')); header.appendChild(document.createTextNode('Select entity:'));
header.setAttribute('style', 'font-size: 14px; margin: 8px 0'); header.setAttribute('style', 'font-size: 14px; margin: 8px 0');
@ -410,10 +413,15 @@ export default abstract class LeafletMap {
} }
convertToCustomFormat(position: L.LatLng): object { convertToCustomFormat(position: L.LatLng): object {
return { if (position.lng > 180) {
[this.options.latKeyName]: position.lat % 90, position.lng = 180;
[this.options.lngKeyName]: position.lng % 180 } else if (position.lng < -180) {
}; position.lng = -180;
}
return {
[this.options.latKeyName]: position.lat,
[this.options.lngKeyName]: position.lng
};
} }
convertToPolygonFormat(points: Array<any>): Array<any> { convertToPolygonFormat(points: Array<any>): Array<any> {

2
ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts

@ -94,7 +94,7 @@ export class Marker {
} }
updateMarkerPosition(position: L.LatLng) { updateMarkerPosition(position: L.LatLng) {
if (!this.location.equals(position)) { if (!this.leafletMarker.getLatLng().equals(position)) {
this.location = position; this.location = position;
this.leafletMarker.setLatLng(position); this.leafletMarker.setLatLng(position);
} }

22
ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts

@ -259,9 +259,27 @@ export class ImageMap extends LeafletMap {
convertToCustomFormat(position: L.LatLng, width = this.width, height = this.height): object { convertToCustomFormat(position: L.LatLng, width = this.width, height = this.height): object {
const point = this.latLngToPoint(position); const point = this.latLngToPoint(position);
const customX = calculateNewPointCoordinate(point.x, width);
const customY = calculateNewPointCoordinate(point.y, height);
if (customX === 0) {
point.x = 0;
} else if (customX === 1) {
point.x = width;
}
if (customY === 0) {
point.y = 0;
} else if (customY === 1) {
point.y = height;
}
const customLatLng = this.pointToLatLng(point.x, point.y);
return { return {
[this.options.xPosKeyName]: calculateNewPointCoordinate(point.x, width), [this.options.xPosKeyName]: customX,
[this.options.yPosKeyName]: calculateNewPointCoordinate(point.y, height) [this.options.yPosKeyName]: customY,
[this.options.latKeyName]: customLatLng.lat,
[this.options.lngKeyName]: customLatLng.lng
}; };
} }

9
ui-ngx/src/app/shared/models/entity-type.models.ts

@ -47,7 +47,8 @@ export enum EntityType {
RULE_NODE = 'RULE_NODE', RULE_NODE = 'RULE_NODE',
ENTITY_VIEW = 'ENTITY_VIEW', ENTITY_VIEW = 'ENTITY_VIEW',
WIDGETS_BUNDLE = 'WIDGETS_BUNDLE', WIDGETS_BUNDLE = 'WIDGETS_BUNDLE',
WIDGET_TYPE = 'WIDGET_TYPE' WIDGET_TYPE = 'WIDGET_TYPE',
API_USAGE_STATE = 'API_USAGE_STATE'
} }
export enum AliasEntityType { export enum AliasEntityType {
@ -238,6 +239,12 @@ export const entityTypeTranslations = new Map<EntityType | AliasEntityType, Enti
selectedEntities: 'alarm.selected-alarms' selectedEntities: 'alarm.selected-alarms'
} }
], ],
[
EntityType.API_USAGE_STATE,
{
type: 'entity.type-api-usage-state'
}
],
[ [
EntityType.WIDGETS_BUNDLE, EntityType.WIDGETS_BUNDLE,
{ {

4
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -885,6 +885,7 @@
"no-device-profiles-found": "No device profiles found.", "no-device-profiles-found": "No device profiles found.",
"create-new-device-profile": "Create a new one!", "create-new-device-profile": "Create a new one!",
"mqtt-device-topic-filters": "MQTT device topic filters", "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": "MQTT device payload",
"mqtt-device-payload-type-json": "JSON", "mqtt-device-payload-type-json": "JSON",
"mqtt-device-payload-type-proto": "Protobuf", "mqtt-device-payload-type-proto": "Protobuf",
@ -1099,7 +1100,8 @@
"details": "Entity details", "details": "Entity details",
"no-entities-prompt": "No entities found", "no-entities-prompt": "No entities found",
"no-data": "No data to display", "no-data": "No data to display",
"columns-to-display": "Columns to Display" "columns-to-display": "Columns to Display",
"type-api-usage-state": "Api Usage State"
}, },
"entity-field": { "entity-field": {
"created-time": "Created time", "created-time": "Created time",

Loading…
Cancel
Save