Commit c03356e94456c526bc17c56acfc1d9563fb20483
Merge branch 'master' into feature/log-telemetry-updated
Showing
31 changed files
with
206 additions
and
63 deletions
@@ -392,6 +392,11 @@ public class TelemetryController extends BaseController { | @@ -392,6 +392,11 @@ public class TelemetryController extends BaseController { | ||
392 | if (attributes.isEmpty()) { | 392 | if (attributes.isEmpty()) { |
393 | return getImmediateDeferredResult("No attributes data found in request body!", HttpStatus.BAD_REQUEST); | 393 | return getImmediateDeferredResult("No attributes data found in request body!", HttpStatus.BAD_REQUEST); |
394 | } | 394 | } |
395 | + for (AttributeKvEntry attributeKvEntry: attributes) { | ||
396 | + if (attributeKvEntry.getKey().isEmpty() || attributeKvEntry.getKey().trim().length() == 0) { | ||
397 | + return getImmediateDeferredResult("Key cannot be empty or contains only spaces", HttpStatus.BAD_REQUEST); | ||
398 | + } | ||
399 | + } | ||
395 | SecurityUser user = getCurrentUser(); | 400 | SecurityUser user = getCurrentUser(); |
396 | return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.WRITE_ATTRIBUTES, entityIdSrc, (result, tenantId, entityId) -> { | 401 | return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.WRITE_ATTRIBUTES, entityIdSrc, (result, tenantId, entityId) -> { |
397 | tsSubService.saveAndNotify(tenantId, entityId, scope, attributes, new FutureCallback<Void>() { | 402 | tsSubService.saveAndNotify(tenantId, entityId, scope, attributes, new FutureCallback<Void>() { |
application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java
@@ -221,6 +221,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | @@ -221,6 +221,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | ||
221 | 221 | ||
222 | @Override | 222 | @Override |
223 | public void onTenantProfileUpdate(TenantProfileId tenantProfileId) { | 223 | public void onTenantProfileUpdate(TenantProfileId tenantProfileId) { |
224 | + log.info("[{}] On Tenant Profile Update", tenantProfileId); | ||
224 | TenantProfile tenantProfile = tenantProfileCache.get(tenantProfileId); | 225 | TenantProfile tenantProfile = tenantProfileCache.get(tenantProfileId); |
225 | updateLock.lock(); | 226 | updateLock.lock(); |
226 | try { | 227 | try { |
@@ -236,6 +237,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | @@ -236,6 +237,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | ||
236 | 237 | ||
237 | @Override | 238 | @Override |
238 | public void onTenantUpdate(TenantId tenantId) { | 239 | public void onTenantUpdate(TenantId tenantId) { |
240 | + log.info("[{}] On Tenant Update.", tenantId); | ||
239 | TenantProfile tenantProfile = tenantProfileCache.get(tenantId); | 241 | TenantProfile tenantProfile = tenantProfileCache.get(tenantId); |
240 | updateLock.lock(); | 242 | updateLock.lock(); |
241 | try { | 243 | try { |
@@ -248,16 +250,16 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | @@ -248,16 +250,16 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | ||
248 | } | 250 | } |
249 | } | 251 | } |
250 | 252 | ||
251 | - private void updateTenantState(TenantApiUsageState state, TenantProfile tenantProfile) { | 253 | + private void updateTenantState(TenantApiUsageState state, TenantProfile profile) { |
252 | TenantProfileData oldProfileData = state.getTenantProfileData(); | 254 | TenantProfileData oldProfileData = state.getTenantProfileData(); |
253 | - state.setTenantProfileId(tenantProfile.getId()); | ||
254 | - state.setTenantProfileData(tenantProfile.getProfileData()); | 255 | + state.setTenantProfileId(profile.getId()); |
256 | + state.setTenantProfileData(profile.getProfileData()); | ||
255 | Map<ApiFeature, ApiUsageStateValue> result = state.checkStateUpdatedDueToThresholds(); | 257 | Map<ApiFeature, ApiUsageStateValue> result = state.checkStateUpdatedDueToThresholds(); |
256 | if (!result.isEmpty()) { | 258 | if (!result.isEmpty()) { |
257 | persistAndNotify(state, result); | 259 | persistAndNotify(state, result); |
258 | } | 260 | } |
259 | updateProfileThresholds(state.getTenantId(), state.getApiUsageState().getId(), | 261 | updateProfileThresholds(state.getTenantId(), state.getApiUsageState().getId(), |
260 | - oldProfileData.getConfiguration(), tenantProfile.getProfileData().getConfiguration()); | 262 | + oldProfileData.getConfiguration(), profile.getProfileData().getConfiguration()); |
261 | } | 263 | } |
262 | 264 | ||
263 | private void updateProfileThresholds(TenantId tenantId, ApiUsageStateId id, | 265 | private void updateProfileThresholds(TenantId tenantId, ApiUsageStateId id, |
application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
@@ -134,7 +134,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | @@ -134,7 +134,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | ||
134 | TenantProfile isolatedTbCoreProfile = new TenantProfile(); | 134 | TenantProfile isolatedTbCoreProfile = new TenantProfile(); |
135 | isolatedTbCoreProfile.setDefault(false); | 135 | isolatedTbCoreProfile.setDefault(false); |
136 | isolatedTbCoreProfile.setName("Isolated TB Core"); | 136 | isolatedTbCoreProfile.setName("Isolated TB Core"); |
137 | - isolatedTbCoreProfile.setProfileData(new TenantProfileData()); | ||
138 | isolatedTbCoreProfile.setDescription("Isolated TB Core tenant profile"); | 137 | isolatedTbCoreProfile.setDescription("Isolated TB Core tenant profile"); |
139 | isolatedTbCoreProfile.setIsolatedTbCore(true); | 138 | isolatedTbCoreProfile.setIsolatedTbCore(true); |
140 | isolatedTbCoreProfile.setIsolatedTbRuleEngine(false); | 139 | isolatedTbCoreProfile.setIsolatedTbRuleEngine(false); |
@@ -148,7 +147,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | @@ -148,7 +147,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | ||
148 | TenantProfile isolatedTbRuleEngineProfile = new TenantProfile(); | 147 | TenantProfile isolatedTbRuleEngineProfile = new TenantProfile(); |
149 | isolatedTbRuleEngineProfile.setDefault(false); | 148 | isolatedTbRuleEngineProfile.setDefault(false); |
150 | isolatedTbRuleEngineProfile.setName("Isolated TB Rule Engine"); | 149 | isolatedTbRuleEngineProfile.setName("Isolated TB Rule Engine"); |
151 | - isolatedTbRuleEngineProfile.setProfileData(new TenantProfileData()); | ||
152 | isolatedTbRuleEngineProfile.setDescription("Isolated TB Rule Engine tenant profile"); | 150 | isolatedTbRuleEngineProfile.setDescription("Isolated TB Rule Engine tenant profile"); |
153 | isolatedTbRuleEngineProfile.setIsolatedTbCore(false); | 151 | isolatedTbRuleEngineProfile.setIsolatedTbCore(false); |
154 | isolatedTbRuleEngineProfile.setIsolatedTbRuleEngine(true); | 152 | isolatedTbRuleEngineProfile.setIsolatedTbRuleEngine(true); |
@@ -163,7 +161,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | @@ -163,7 +161,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | ||
163 | TenantProfile isolatedTbCoreAndTbRuleEngineProfile = new TenantProfile(); | 161 | TenantProfile isolatedTbCoreAndTbRuleEngineProfile = new TenantProfile(); |
164 | isolatedTbCoreAndTbRuleEngineProfile.setDefault(false); | 162 | isolatedTbCoreAndTbRuleEngineProfile.setDefault(false); |
165 | isolatedTbCoreAndTbRuleEngineProfile.setName("Isolated TB Core and TB Rule Engine"); | 163 | isolatedTbCoreAndTbRuleEngineProfile.setName("Isolated TB Core and TB Rule Engine"); |
166 | - isolatedTbCoreAndTbRuleEngineProfile.setProfileData(new TenantProfileData()); | ||
167 | isolatedTbCoreAndTbRuleEngineProfile.setDescription("Isolated TB Core and TB Rule Engine tenant profile"); | 164 | isolatedTbCoreAndTbRuleEngineProfile.setDescription("Isolated TB Core and TB Rule Engine tenant profile"); |
168 | isolatedTbCoreAndTbRuleEngineProfile.setIsolatedTbCore(true); | 165 | isolatedTbCoreAndTbRuleEngineProfile.setIsolatedTbCore(true); |
169 | isolatedTbCoreAndTbRuleEngineProfile.setIsolatedTbRuleEngine(true); | 166 | isolatedTbCoreAndTbRuleEngineProfile.setIsolatedTbRuleEngine(true); |
@@ -274,6 +274,7 @@ public class DefaultTbClusterService implements TbClusterService { | @@ -274,6 +274,7 @@ public class DefaultTbClusterService implements TbClusterService { | ||
274 | TbQueueProducer<TbProtoQueueMsg<ToRuleEngineNotificationMsg>> toRuleEngineProducer = producerProvider.getRuleEngineNotificationsMsgProducer(); | 274 | TbQueueProducer<TbProtoQueueMsg<ToRuleEngineNotificationMsg>> toRuleEngineProducer = producerProvider.getRuleEngineNotificationsMsgProducer(); |
275 | Set<String> tbRuleEngineServices = new HashSet<>(partitionService.getAllServiceIds(ServiceType.TB_RULE_ENGINE)); | 275 | Set<String> tbRuleEngineServices = new HashSet<>(partitionService.getAllServiceIds(ServiceType.TB_RULE_ENGINE)); |
276 | if (msg.getEntityId().getEntityType().equals(EntityType.TENANT) | 276 | if (msg.getEntityId().getEntityType().equals(EntityType.TENANT) |
277 | + || msg.getEntityId().getEntityType().equals(EntityType.TENANT_PROFILE) | ||
277 | || msg.getEntityId().getEntityType().equals(EntityType.DEVICE_PROFILE) | 278 | || msg.getEntityId().getEntityType().equals(EntityType.DEVICE_PROFILE) |
278 | || msg.getEntityId().getEntityType().equals(EntityType.API_USAGE_STATE)) { | 279 | || msg.getEntityId().getEntityType().equals(EntityType.API_USAGE_STATE)) { |
279 | TbQueueProducer<TbProtoQueueMsg<ToCoreNotificationMsg>> toCoreNfProducer = producerProvider.getTbCoreNotificationsMsgProducer(); | 280 | TbQueueProducer<TbProtoQueueMsg<ToCoreNotificationMsg>> toCoreNfProducer = producerProvider.getTbCoreNotificationsMsgProducer(); |
@@ -153,6 +153,8 @@ public abstract class AbstractConsumerService<N extends com.google.protobuf.Gene | @@ -153,6 +153,8 @@ public abstract class AbstractConsumerService<N extends com.google.protobuf.Gene | ||
153 | TbActorMsg actorMsg = actorMsgOpt.get(); | 153 | TbActorMsg actorMsg = actorMsgOpt.get(); |
154 | if (actorMsg instanceof ComponentLifecycleMsg) { | 154 | if (actorMsg instanceof ComponentLifecycleMsg) { |
155 | ComponentLifecycleMsg componentLifecycleMsg = (ComponentLifecycleMsg) actorMsg; | 155 | ComponentLifecycleMsg componentLifecycleMsg = (ComponentLifecycleMsg) actorMsg; |
156 | + log.info("[{}][{}][{}] Received Lifecycle event: {}", componentLifecycleMsg.getTenantId(), componentLifecycleMsg.getEntityId().getEntityType(), | ||
157 | + componentLifecycleMsg.getEntityId(), componentLifecycleMsg.getEvent()); | ||
156 | if (EntityType.TENANT_PROFILE.equals(componentLifecycleMsg.getEntityId().getEntityType())) { | 158 | if (EntityType.TENANT_PROFILE.equals(componentLifecycleMsg.getEntityId().getEntityType())) { |
157 | TenantProfileId tenantProfileId = new TenantProfileId(componentLifecycleMsg.getEntityId().getId()); | 159 | TenantProfileId tenantProfileId = new TenantProfileId(componentLifecycleMsg.getEntityId().getId()); |
158 | tenantProfileCache.evict(tenantProfileId); | 160 | tenantProfileCache.evict(tenantProfileId); |
@@ -35,14 +35,17 @@ import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; | @@ -35,14 +35,17 @@ import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; | ||
35 | import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; | 35 | import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; |
36 | import org.thingsboard.server.common.data.page.PageData; | 36 | import org.thingsboard.server.common.data.page.PageData; |
37 | import org.thingsboard.server.common.data.page.PageLink; | 37 | import org.thingsboard.server.common.data.page.PageLink; |
38 | +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; | ||
38 | import org.thingsboard.server.common.data.security.Authority; | 39 | import org.thingsboard.server.common.data.security.Authority; |
39 | import org.thingsboard.server.common.data.security.UserCredentials; | 40 | import org.thingsboard.server.common.data.security.UserCredentials; |
40 | import org.thingsboard.server.dao.customer.CustomerService; | 41 | import org.thingsboard.server.dao.customer.CustomerService; |
41 | import org.thingsboard.server.dao.dashboard.DashboardService; | 42 | import org.thingsboard.server.dao.dashboard.DashboardService; |
42 | import org.thingsboard.server.dao.oauth2.OAuth2User; | 43 | import org.thingsboard.server.dao.oauth2.OAuth2User; |
44 | +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; | ||
43 | import org.thingsboard.server.dao.tenant.TenantService; | 45 | import org.thingsboard.server.dao.tenant.TenantService; |
44 | import org.thingsboard.server.dao.user.UserService; | 46 | import org.thingsboard.server.dao.user.UserService; |
45 | import org.thingsboard.server.service.install.InstallScripts; | 47 | import org.thingsboard.server.service.install.InstallScripts; |
48 | +import org.thingsboard.server.service.queue.TbClusterService; | ||
46 | import org.thingsboard.server.service.security.model.SecurityUser; | 49 | import org.thingsboard.server.service.security.model.SecurityUser; |
47 | import org.thingsboard.server.service.security.model.UserPrincipal; | 50 | import org.thingsboard.server.service.security.model.UserPrincipal; |
48 | 51 | ||
@@ -76,6 +79,12 @@ public abstract class AbstractOAuth2ClientMapper { | @@ -76,6 +79,12 @@ public abstract class AbstractOAuth2ClientMapper { | ||
76 | @Autowired | 79 | @Autowired |
77 | private InstallScripts installScripts; | 80 | private InstallScripts installScripts; |
78 | 81 | ||
82 | + @Autowired | ||
83 | + protected TbTenantProfileCache tenantProfileCache; | ||
84 | + | ||
85 | + @Autowired | ||
86 | + protected TbClusterService tbClusterService; | ||
87 | + | ||
79 | private final Lock userCreationLock = new ReentrantLock(); | 88 | private final Lock userCreationLock = new ReentrantLock(); |
80 | 89 | ||
81 | protected SecurityUser getOrCreateSecurityUserFromOAuth2User(OAuth2User oauth2User, OAuth2ClientRegistrationInfo clientRegistration) { | 90 | protected SecurityUser getOrCreateSecurityUserFromOAuth2User(OAuth2User oauth2User, OAuth2ClientRegistrationInfo clientRegistration) { |
@@ -162,6 +171,10 @@ public abstract class AbstractOAuth2ClientMapper { | @@ -162,6 +171,10 @@ public abstract class AbstractOAuth2ClientMapper { | ||
162 | tenant.setTitle(tenantName); | 171 | tenant.setTitle(tenantName); |
163 | tenant = tenantService.saveTenant(tenant); | 172 | tenant = tenantService.saveTenant(tenant); |
164 | installScripts.createDefaultRuleChains(tenant.getId()); | 173 | installScripts.createDefaultRuleChains(tenant.getId()); |
174 | + tenantProfileCache.evict(tenant.getId()); | ||
175 | + tbClusterService.onTenantChange(tenant, null); | ||
176 | + tbClusterService.onEntityStateChange(tenant.getId(), tenant.getId(), | ||
177 | + ComponentLifecycleEvent.CREATED); | ||
165 | } else { | 178 | } else { |
166 | tenant = tenants.get(0); | 179 | tenant = tenants.get(0); |
167 | } | 180 | } |
@@ -15,10 +15,15 @@ | @@ -15,10 +15,15 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.service.security.auth.oauth2; | 16 | package org.thingsboard.server.service.security.auth.oauth2; |
17 | 17 | ||
18 | +import org.springframework.beans.factory.annotation.Autowired; | ||
18 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | 19 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
19 | import org.springframework.security.core.AuthenticationException; | 20 | import org.springframework.security.core.AuthenticationException; |
20 | import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; | 21 | import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; |
21 | import org.springframework.stereotype.Component; | 22 | import org.springframework.stereotype.Component; |
23 | +import org.thingsboard.server.common.data.id.CustomerId; | ||
24 | +import org.thingsboard.server.common.data.id.EntityId; | ||
25 | +import org.thingsboard.server.common.data.id.TenantId; | ||
26 | +import org.thingsboard.server.service.security.system.SystemSecurityService; | ||
22 | import org.thingsboard.server.utils.MiscUtils; | 27 | import org.thingsboard.server.utils.MiscUtils; |
23 | 28 | ||
24 | import javax.servlet.ServletException; | 29 | import javax.servlet.ServletException; |
@@ -32,11 +37,18 @@ import java.nio.charset.StandardCharsets; | @@ -32,11 +37,18 @@ import java.nio.charset.StandardCharsets; | ||
32 | @ConditionalOnProperty(prefix = "security.oauth2", value = "enabled", havingValue = "true") | 37 | @ConditionalOnProperty(prefix = "security.oauth2", value = "enabled", havingValue = "true") |
33 | public class Oauth2AuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler { | 38 | public class Oauth2AuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler { |
34 | 39 | ||
40 | + private final SystemSecurityService systemSecurityService; | ||
41 | + | ||
42 | + @Autowired | ||
43 | + public Oauth2AuthenticationFailureHandler(final SystemSecurityService systemSecurityService) { | ||
44 | + this.systemSecurityService = systemSecurityService; | ||
45 | + } | ||
46 | + | ||
35 | @Override | 47 | @Override |
36 | public void onAuthenticationFailure(HttpServletRequest request, | 48 | public void onAuthenticationFailure(HttpServletRequest request, |
37 | HttpServletResponse response, AuthenticationException exception) | 49 | HttpServletResponse response, AuthenticationException exception) |
38 | throws IOException, ServletException { | 50 | throws IOException, ServletException { |
39 | - String baseUrl = MiscUtils.constructBaseUrl(request); | 51 | + String baseUrl = this.systemSecurityService.getBaseUrl(TenantId.SYS_TENANT_ID, new CustomerId(EntityId.NULL_UUID), request); |
40 | getRedirectStrategy().sendRedirect(request, response, baseUrl + "/login?loginError=" + | 52 | getRedirectStrategy().sendRedirect(request, response, baseUrl + "/login?loginError=" + |
41 | URLEncoder.encode(exception.getMessage(), StandardCharsets.UTF_8.toString())); | 53 | URLEncoder.encode(exception.getMessage(), StandardCharsets.UTF_8.toString())); |
42 | } | 54 | } |
@@ -22,12 +22,16 @@ import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; | @@ -22,12 +22,16 @@ import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; | ||
22 | import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; | 22 | import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; |
23 | import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; | 23 | import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; |
24 | import org.springframework.stereotype.Component; | 24 | import org.springframework.stereotype.Component; |
25 | +import org.thingsboard.server.common.data.id.CustomerId; | ||
26 | +import org.thingsboard.server.common.data.id.EntityId; | ||
27 | +import org.thingsboard.server.common.data.id.TenantId; | ||
25 | import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; | 28 | import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; |
26 | import org.thingsboard.server.dao.oauth2.OAuth2Service; | 29 | import org.thingsboard.server.dao.oauth2.OAuth2Service; |
27 | import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository; | 30 | import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository; |
28 | import org.thingsboard.server.service.security.model.SecurityUser; | 31 | import org.thingsboard.server.service.security.model.SecurityUser; |
29 | import org.thingsboard.server.service.security.model.token.JwtToken; | 32 | import org.thingsboard.server.service.security.model.token.JwtToken; |
30 | import org.thingsboard.server.service.security.model.token.JwtTokenFactory; | 33 | import org.thingsboard.server.service.security.model.token.JwtTokenFactory; |
34 | +import org.thingsboard.server.service.security.system.SystemSecurityService; | ||
31 | import org.thingsboard.server.utils.MiscUtils; | 35 | import org.thingsboard.server.utils.MiscUtils; |
32 | 36 | ||
33 | import javax.servlet.http.HttpServletRequest; | 37 | import javax.servlet.http.HttpServletRequest; |
@@ -45,25 +49,27 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS | @@ -45,25 +49,27 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS | ||
45 | private final OAuth2ClientMapperProvider oauth2ClientMapperProvider; | 49 | private final OAuth2ClientMapperProvider oauth2ClientMapperProvider; |
46 | private final OAuth2Service oAuth2Service; | 50 | private final OAuth2Service oAuth2Service; |
47 | private final OAuth2AuthorizedClientService oAuth2AuthorizedClientService; | 51 | private final OAuth2AuthorizedClientService oAuth2AuthorizedClientService; |
52 | + private final SystemSecurityService systemSecurityService; | ||
48 | 53 | ||
49 | @Autowired | 54 | @Autowired |
50 | public Oauth2AuthenticationSuccessHandler(final JwtTokenFactory tokenFactory, | 55 | public Oauth2AuthenticationSuccessHandler(final JwtTokenFactory tokenFactory, |
51 | final RefreshTokenRepository refreshTokenRepository, | 56 | final RefreshTokenRepository refreshTokenRepository, |
52 | final OAuth2ClientMapperProvider oauth2ClientMapperProvider, | 57 | final OAuth2ClientMapperProvider oauth2ClientMapperProvider, |
53 | final OAuth2Service oAuth2Service, | 58 | final OAuth2Service oAuth2Service, |
54 | - final OAuth2AuthorizedClientService oAuth2AuthorizedClientService) { | 59 | + final OAuth2AuthorizedClientService oAuth2AuthorizedClientService, final SystemSecurityService systemSecurityService) { |
55 | this.tokenFactory = tokenFactory; | 60 | this.tokenFactory = tokenFactory; |
56 | this.refreshTokenRepository = refreshTokenRepository; | 61 | this.refreshTokenRepository = refreshTokenRepository; |
57 | this.oauth2ClientMapperProvider = oauth2ClientMapperProvider; | 62 | this.oauth2ClientMapperProvider = oauth2ClientMapperProvider; |
58 | this.oAuth2Service = oAuth2Service; | 63 | this.oAuth2Service = oAuth2Service; |
59 | this.oAuth2AuthorizedClientService = oAuth2AuthorizedClientService; | 64 | this.oAuth2AuthorizedClientService = oAuth2AuthorizedClientService; |
65 | + this.systemSecurityService = systemSecurityService; | ||
60 | } | 66 | } |
61 | 67 | ||
62 | @Override | 68 | @Override |
63 | public void onAuthenticationSuccess(HttpServletRequest request, | 69 | public void onAuthenticationSuccess(HttpServletRequest request, |
64 | HttpServletResponse response, | 70 | HttpServletResponse response, |
65 | Authentication authentication) throws IOException { | 71 | Authentication authentication) throws IOException { |
66 | - String baseUrl = MiscUtils.constructBaseUrl(request); | 72 | + String baseUrl = this.systemSecurityService.getBaseUrl(TenantId.SYS_TENANT_ID, new CustomerId(EntityId.NULL_UUID), request); |
67 | try { | 73 | try { |
68 | OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) authentication; | 74 | OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) authentication; |
69 | 75 |
@@ -202,16 +202,19 @@ public class DefaultSystemSecurityService implements SystemSecurityService { | @@ -202,16 +202,19 @@ public class DefaultSystemSecurityService implements SystemSecurityService { | ||
202 | 202 | ||
203 | @Override | 203 | @Override |
204 | public String getBaseUrl(TenantId tenantId, CustomerId customerId, HttpServletRequest httpServletRequest) { | 204 | public String getBaseUrl(TenantId tenantId, CustomerId customerId, HttpServletRequest httpServletRequest) { |
205 | - String baseUrl; | 205 | + String baseUrl = null; |
206 | AdminSettings generalSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "general"); | 206 | AdminSettings generalSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "general"); |
207 | 207 | ||
208 | JsonNode prohibitDifferentUrl = generalSettings.getJsonValue().get("prohibitDifferentUrl"); | 208 | JsonNode prohibitDifferentUrl = generalSettings.getJsonValue().get("prohibitDifferentUrl"); |
209 | 209 | ||
210 | if (prohibitDifferentUrl != null && prohibitDifferentUrl.asBoolean()) { | 210 | if (prohibitDifferentUrl != null && prohibitDifferentUrl.asBoolean()) { |
211 | baseUrl = generalSettings.getJsonValue().get("baseUrl").asText(); | 211 | baseUrl = generalSettings.getJsonValue().get("baseUrl").asText(); |
212 | - } else { | 212 | + } |
213 | + | ||
214 | + if (StringUtils.isEmpty(baseUrl)) { | ||
213 | baseUrl = MiscUtils.constructBaseUrl(httpServletRequest); | 215 | baseUrl = MiscUtils.constructBaseUrl(httpServletRequest); |
214 | } | 216 | } |
217 | + | ||
215 | return baseUrl; | 218 | return baseUrl; |
216 | } | 219 | } |
217 | 220 |
@@ -108,7 +108,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer | @@ -108,7 +108,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer | ||
108 | * Since number of subscriptions is usually much less then number of devices that are pushing data. | 108 | * Since number of subscriptions is usually much less then number of devices that are pushing data. |
109 | */ | 109 | */ |
110 | subscriptionsBySessionId.values().forEach(map -> map.values() | 110 | subscriptionsBySessionId.values().forEach(map -> map.values() |
111 | - .forEach(sub -> pushSubscriptionToManagerService(sub, false))); | 111 | + .forEach(sub -> pushSubscriptionToManagerService(sub, true))); |
112 | } | 112 | } |
113 | } | 113 | } |
114 | 114 |
@@ -164,22 +164,25 @@ public class DefaultTransportApiService implements TransportApiService { | @@ -164,22 +164,25 @@ public class DefaultTransportApiService implements TransportApiService { | ||
164 | } | 164 | } |
165 | 165 | ||
166 | private ListenableFuture<TransportApiResponseMsg> validateCredentials(TransportProtos.ValidateBasicMqttCredRequestMsg mqtt) { | 166 | private ListenableFuture<TransportApiResponseMsg> validateCredentials(TransportProtos.ValidateBasicMqttCredRequestMsg mqtt) { |
167 | - DeviceCredentials credentials = deviceCredentialsService.findDeviceCredentialsByCredentialsId(mqtt.getUserName()); | ||
168 | - if (credentials != null) { | ||
169 | - if (credentials.getCredentialsType() == DeviceCredentialsType.ACCESS_TOKEN) { | ||
170 | - return getDeviceInfo(credentials.getDeviceId(), credentials); | ||
171 | - } else if (credentials.getCredentialsType() == DeviceCredentialsType.MQTT_BASIC) { | ||
172 | - if (!checkMqttCredentials(mqtt, credentials)) { | ||
173 | - credentials = null; | 167 | + DeviceCredentials credentials = null; |
168 | + if (!StringUtils.isEmpty(mqtt.getUserName())) { | ||
169 | + credentials = deviceCredentialsService.findDeviceCredentialsByCredentialsId(mqtt.getUserName()); | ||
170 | + if (credentials != null) { | ||
171 | + if (credentials.getCredentialsType() == DeviceCredentialsType.ACCESS_TOKEN) { | ||
172 | + return getDeviceInfo(credentials.getDeviceId(), credentials); | ||
173 | + } else if (credentials.getCredentialsType() == DeviceCredentialsType.MQTT_BASIC) { | ||
174 | + if (!checkMqttCredentials(mqtt, credentials)) { | ||
175 | + credentials = null; | ||
176 | + } | ||
174 | } | 177 | } |
175 | } | 178 | } |
176 | - } | ||
177 | - if (credentials == null) { | ||
178 | - credentials = checkMqttCredentials(mqtt, EncryptionUtil.getSha3Hash("|", mqtt.getClientId(), mqtt.getUserName())); | ||
179 | if (credentials == null) { | 179 | if (credentials == null) { |
180 | - credentials = checkMqttCredentials(mqtt, EncryptionUtil.getSha3Hash(mqtt.getClientId())); | 180 | + credentials = checkMqttCredentials(mqtt, EncryptionUtil.getSha3Hash("|", mqtt.getClientId(), mqtt.getUserName())); |
181 | } | 181 | } |
182 | } | 182 | } |
183 | + if (credentials == null) { | ||
184 | + credentials = checkMqttCredentials(mqtt, EncryptionUtil.getSha3Hash(mqtt.getClientId())); | ||
185 | + } | ||
183 | if (credentials != null) { | 186 | if (credentials != null) { |
184 | return getDeviceInfo(credentials.getDeviceId(), credentials); | 187 | return getDeviceInfo(credentials.getDeviceId(), credentials); |
185 | } else { | 188 | } else { |
@@ -766,7 +766,6 @@ metrics: | @@ -766,7 +766,6 @@ metrics: | ||
766 | # Metrics percentiles returned by actuator for timer metrics. List of double values (divided by ,). | 766 | # Metrics percentiles returned by actuator for timer metrics. List of double values (divided by ,). |
767 | percentiles: "${METRICS_TIMER_PERCENTILES:0.5}" | 767 | percentiles: "${METRICS_TIMER_PERCENTILES:0.5}" |
768 | 768 | ||
769 | - | ||
770 | management: | 769 | management: |
771 | endpoints: | 770 | endpoints: |
772 | web: | 771 | web: |
@@ -21,5 +21,5 @@ import java.io.Serializable; | @@ -21,5 +21,5 @@ import java.io.Serializable; | ||
21 | * @author Andrew Shvayka | 21 | * @author Andrew Shvayka |
22 | */ | 22 | */ |
23 | public enum ComponentLifecycleEvent implements Serializable { | 23 | public enum ComponentLifecycleEvent implements Serializable { |
24 | - CREATED, STARTED, ACTIVATED, SUSPENDED, UPDATED, STOPPED, DELETED, ADDED_TO_ALLOW_LIST, ADDED_TO_DENY_LIST | 24 | + CREATED, STARTED, ACTIVATED, SUSPENDED, UPDATED, STOPPED, DELETED |
25 | } | 25 | } |
@@ -94,7 +94,9 @@ public class DefaultTbApiUsageClient implements TbApiUsageClient { | @@ -94,7 +94,9 @@ public class DefaultTbApiUsageClient implements TbApiUsageClient { | ||
94 | TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, tenantId).newByTopic(msgProducer.getDefaultTopic()); | 94 | TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, tenantId).newByTopic(msgProducer.getDefaultTopic()); |
95 | msgProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), builder.build()), null); | 95 | msgProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), builder.build()), null); |
96 | })); | 96 | })); |
97 | - log.info("Report statistics for: {} tenants", report.size()); | 97 | + if (!report.isEmpty()) { |
98 | + log.info("Report statistics for: {} tenants", report.size()); | ||
99 | + } | ||
98 | } catch (Exception e) { | 100 | } catch (Exception e) { |
99 | log.warn("Failed to report statistics: ", e); | 101 | log.warn("Failed to report statistics: ", e); |
100 | } | 102 | } |
@@ -122,7 +122,13 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | @@ -122,7 +122,13 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | ||
122 | log.trace("[{}] Processing msg: {}", sessionId, msg); | 122 | log.trace("[{}] Processing msg: {}", sessionId, msg); |
123 | try { | 123 | try { |
124 | if (msg instanceof MqttMessage) { | 124 | if (msg instanceof MqttMessage) { |
125 | - processMqttMsg(ctx, (MqttMessage) msg); | 125 | + MqttMessage message = (MqttMessage) msg; |
126 | + if (message.decoderResult().isSuccess()) { | ||
127 | + processMqttMsg(ctx, message); | ||
128 | + } else { | ||
129 | + log.error("[{}] Message processing failed: {}", sessionId, message.decoderResult().cause().getMessage()); | ||
130 | + ctx.close(); | ||
131 | + } | ||
126 | } else { | 132 | } else { |
127 | ctx.close(); | 133 | ctx.close(); |
128 | } | 134 | } |
@@ -464,8 +470,10 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | @@ -464,8 +470,10 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | ||
464 | String userName = msg.payload().userName(); | 470 | String userName = msg.payload().userName(); |
465 | log.info("[{}] Processing connect msg for client with user name: {}!", sessionId, userName); | 471 | log.info("[{}] Processing connect msg for client with user name: {}!", sessionId, userName); |
466 | TransportProtos.ValidateBasicMqttCredRequestMsg.Builder request = TransportProtos.ValidateBasicMqttCredRequestMsg.newBuilder() | 472 | TransportProtos.ValidateBasicMqttCredRequestMsg.Builder request = TransportProtos.ValidateBasicMqttCredRequestMsg.newBuilder() |
467 | - .setClientId(msg.payload().clientIdentifier()) | ||
468 | - .setUserName(userName); | 473 | + .setClientId(msg.payload().clientIdentifier()); |
474 | + if (userName != null) { | ||
475 | + request.setUserName(userName); | ||
476 | + } | ||
469 | String password = msg.payload().password(); | 477 | String password = msg.payload().password(); |
470 | if (password != null) { | 478 | if (password != null) { |
471 | request.setPassword(password); | 479 | request.setPassword(password); |
@@ -21,6 +21,7 @@ import org.springframework.util.StringUtils; | @@ -21,6 +21,7 @@ import org.springframework.util.StringUtils; | ||
21 | import org.thingsboard.server.common.data.EntityType; | 21 | import org.thingsboard.server.common.data.EntityType; |
22 | import org.thingsboard.server.common.data.TenantProfile; | 22 | import org.thingsboard.server.common.data.TenantProfile; |
23 | import org.thingsboard.server.common.data.id.DeviceId; | 23 | import org.thingsboard.server.common.data.id.DeviceId; |
24 | +import org.thingsboard.server.common.data.id.EntityId; | ||
24 | import org.thingsboard.server.common.data.id.TenantId; | 25 | import org.thingsboard.server.common.data.id.TenantId; |
25 | import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; | 26 | import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; |
26 | import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; | 27 | import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; |
@@ -77,6 +78,7 @@ public class DefaultTransportRateLimitService implements TransportRateLimitServi | @@ -77,6 +78,7 @@ public class DefaultTransportRateLimitService implements TransportRateLimitServi | ||
77 | 78 | ||
78 | @Override | 79 | @Override |
79 | public void update(TenantProfileUpdateResult update) { | 80 | public void update(TenantProfileUpdateResult update) { |
81 | + log.info("Received tenant profile update: {}", update.getProfile()); | ||
80 | EntityTransportRateLimits tenantRateLimitPrototype = createRateLimits(update.getProfile(), true); | 82 | EntityTransportRateLimits tenantRateLimitPrototype = createRateLimits(update.getProfile(), true); |
81 | EntityTransportRateLimits deviceRateLimitPrototype = createRateLimits(update.getProfile(), false); | 83 | EntityTransportRateLimits deviceRateLimitPrototype = createRateLimits(update.getProfile(), false); |
82 | for (TenantId tenantId : update.getAffectedTenants()) { | 84 | for (TenantId tenantId : update.getAffectedTenants()) { |
@@ -114,16 +116,26 @@ public class DefaultTransportRateLimitService implements TransportRateLimitServi | @@ -114,16 +116,26 @@ public class DefaultTransportRateLimitService implements TransportRateLimitServi | ||
114 | tenantAllowed.put(tenantId, allowed); | 116 | tenantAllowed.put(tenantId, allowed); |
115 | } | 117 | } |
116 | 118 | ||
117 | - private <T> void mergeLimits(T deviceId, EntityTransportRateLimits newRateLimits, | ||
118 | - Function<T, EntityTransportRateLimits> getFunction, | ||
119 | - BiConsumer<T, EntityTransportRateLimits> putFunction) { | ||
120 | - EntityTransportRateLimits oldRateLimits = getFunction.apply(deviceId); | 119 | + private <T extends EntityId> void mergeLimits(T entityId, EntityTransportRateLimits newRateLimits, |
120 | + Function<T, EntityTransportRateLimits> getFunction, | ||
121 | + BiConsumer<T, EntityTransportRateLimits> putFunction) { | ||
122 | + EntityTransportRateLimits oldRateLimits = getFunction.apply(entityId); | ||
121 | if (oldRateLimits == null) { | 123 | if (oldRateLimits == null) { |
122 | - putFunction.accept(deviceId, newRateLimits); | 124 | + if (EntityType.TENANT.equals(entityId.getEntityType())) { |
125 | + log.info("[{}] New rate limits: {}", entityId, newRateLimits); | ||
126 | + } else { | ||
127 | + log.debug("[{}] New rate limits: {}", entityId, newRateLimits); | ||
128 | + } | ||
129 | + putFunction.accept(entityId, newRateLimits); | ||
123 | } else { | 130 | } else { |
124 | EntityTransportRateLimits updated = merge(oldRateLimits, newRateLimits); | 131 | EntityTransportRateLimits updated = merge(oldRateLimits, newRateLimits); |
125 | if (updated != null) { | 132 | if (updated != null) { |
126 | - putFunction.accept(deviceId, updated); | 133 | + if (EntityType.TENANT.equals(entityId.getEntityType())) { |
134 | + log.info("[{}] Updated rate limits: {}", entityId, updated); | ||
135 | + } else { | ||
136 | + log.debug("[{}] Updated rate limits: {}", entityId, updated); | ||
137 | + } | ||
138 | + putFunction.accept(entityId, updated); | ||
127 | } | 139 | } |
128 | } | 140 | } |
129 | } | 141 | } |
@@ -548,7 +548,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe | @@ -548,7 +548,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe | ||
548 | 548 | ||
549 | @Override | 549 | @Override |
550 | protected void validateDataImpl(TenantId tenantId, Device device) { | 550 | protected void validateDataImpl(TenantId tenantId, Device device) { |
551 | - if (StringUtils.isEmpty(device.getName())) { | 551 | + if (StringUtils.isEmpty(device.getName()) || device.getName().trim().length() == 0) { |
552 | throw new DataValidationException("Device name should be specified!"); | 552 | throw new DataValidationException("Device name should be specified!"); |
553 | } | 553 | } |
554 | if (device.getTenantId() == null) { | 554 | if (device.getTenantId() == null) { |
@@ -202,6 +202,9 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { | @@ -202,6 +202,9 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { | ||
202 | " THEN (select additional_info from entity_view where id = entity_id)" + | 202 | " THEN (select additional_info from entity_view where id = entity_id)" + |
203 | " END as additional_info"; | 203 | " END as additional_info"; |
204 | 204 | ||
205 | + private static final String SELECT_API_USAGE_STATE = "(select aus.id, aus.created_time, aus.tenant_id, '13814000-1dd2-11b2-8080-808080808080'::uuid as customer_id, " + | ||
206 | + "(select title from tenant where id = aus.tenant_id) as name from api_usage_state as aus)"; | ||
207 | + | ||
205 | static { | 208 | static { |
206 | entityTableMap.put(EntityType.ASSET, "asset"); | 209 | entityTableMap.put(EntityType.ASSET, "asset"); |
207 | entityTableMap.put(EntityType.DEVICE, "device"); | 210 | entityTableMap.put(EntityType.DEVICE, "device"); |
@@ -210,7 +213,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { | @@ -210,7 +213,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { | ||
210 | entityTableMap.put(EntityType.CUSTOMER, "customer"); | 213 | entityTableMap.put(EntityType.CUSTOMER, "customer"); |
211 | entityTableMap.put(EntityType.USER, "tb_user"); | 214 | entityTableMap.put(EntityType.USER, "tb_user"); |
212 | entityTableMap.put(EntityType.TENANT, "tenant"); | 215 | entityTableMap.put(EntityType.TENANT, "tenant"); |
213 | - entityTableMap.put(EntityType.API_USAGE_STATE, "(select aus.id, aus.created_time, aus.tenant_id, '' as name, '' as additional_info from api_usage_state as aus)"); | 216 | + entityTableMap.put(EntityType.API_USAGE_STATE, SELECT_API_USAGE_STATE); |
214 | } | 217 | } |
215 | 218 | ||
216 | public static EntityType[] RELATION_QUERY_ENTITY_TYPES = new EntityType[]{ | 219 | public static EntityType[] RELATION_QUERY_ENTITY_TYPES = new EntityType[]{ |
@@ -80,6 +80,7 @@ public class EntityKeyMapping { | @@ -80,6 +80,7 @@ public class EntityKeyMapping { | ||
80 | public static final List<String> labeledEntityFields = Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME, TYPE, LABEL, ADDITIONAL_INFO); | 80 | public static final List<String> labeledEntityFields = Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME, TYPE, LABEL, ADDITIONAL_INFO); |
81 | public static final List<String> contactBasedEntityFields = Arrays.asList(CREATED_TIME, ENTITY_TYPE, EMAIL, TITLE, COUNTRY, STATE, CITY, ADDRESS, ADDRESS_2, ZIP, PHONE, ADDITIONAL_INFO); | 81 | public static final List<String> contactBasedEntityFields = Arrays.asList(CREATED_TIME, ENTITY_TYPE, EMAIL, TITLE, COUNTRY, STATE, CITY, ADDRESS, ADDRESS_2, ZIP, PHONE, ADDITIONAL_INFO); |
82 | 82 | ||
83 | + public static final Set<String> apiUsageStateEntityFields = new HashSet<>(Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME)); | ||
83 | public static final Set<String> commonEntityFieldsSet = new HashSet<>(commonEntityFields); | 84 | public static final Set<String> commonEntityFieldsSet = new HashSet<>(commonEntityFields); |
84 | public static final Set<String> relationQueryEntityFieldsSet = new HashSet<>(Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME, TYPE, LABEL, FIRST_NAME, LAST_NAME, EMAIL, REGION, TITLE, COUNTRY, STATE, CITY, ADDRESS, ADDRESS_2, ZIP, PHONE, ADDITIONAL_INFO)); | 85 | public static final Set<String> relationQueryEntityFieldsSet = new HashSet<>(Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME, TYPE, LABEL, FIRST_NAME, LAST_NAME, EMAIL, REGION, TITLE, COUNTRY, STATE, CITY, ADDRESS, ADDRESS_2, ZIP, PHONE, ADDITIONAL_INFO)); |
85 | 86 | ||
@@ -99,6 +100,7 @@ public class EntityKeyMapping { | @@ -99,6 +100,7 @@ public class EntityKeyMapping { | ||
99 | allowedEntityFieldMap.put(EntityType.RULE_NODE, new HashSet<>(commonEntityFields)); | 100 | allowedEntityFieldMap.put(EntityType.RULE_NODE, new HashSet<>(commonEntityFields)); |
100 | allowedEntityFieldMap.put(EntityType.WIDGET_TYPE, new HashSet<>(widgetEntityFields)); | 101 | allowedEntityFieldMap.put(EntityType.WIDGET_TYPE, new HashSet<>(widgetEntityFields)); |
101 | allowedEntityFieldMap.put(EntityType.WIDGETS_BUNDLE, new HashSet<>(widgetEntityFields)); | 102 | allowedEntityFieldMap.put(EntityType.WIDGETS_BUNDLE, new HashSet<>(widgetEntityFields)); |
103 | + allowedEntityFieldMap.put(EntityType.API_USAGE_STATE, apiUsageStateEntityFields); | ||
102 | 104 | ||
103 | entityFieldColumnMap.put(CREATED_TIME, ModelConstants.CREATED_TIME_PROPERTY); | 105 | entityFieldColumnMap.put(CREATED_TIME, ModelConstants.CREATED_TIME_PROPERTY); |
104 | entityFieldColumnMap.put(ENTITY_TYPE, ModelConstants.ENTITY_TYPE_PROPERTY); | 106 | entityFieldColumnMap.put(ENTITY_TYPE, ModelConstants.ENTITY_TYPE_PROPERTY); |
@@ -17,6 +17,7 @@ package org.thingsboard.server.dao.usagerecord; | @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.usagerecord; | ||
17 | 17 | ||
18 | import lombok.extern.slf4j.Slf4j; | 18 | import lombok.extern.slf4j.Slf4j; |
19 | import org.springframework.stereotype.Service; | 19 | import org.springframework.stereotype.Service; |
20 | +import org.thingsboard.server.common.data.ApiFeature; | ||
20 | import org.thingsboard.server.common.data.ApiUsageRecordKey; | 21 | import org.thingsboard.server.common.data.ApiUsageRecordKey; |
21 | import org.thingsboard.server.common.data.ApiUsageState; | 22 | import org.thingsboard.server.common.data.ApiUsageState; |
22 | import org.thingsboard.server.common.data.ApiUsageStateValue; | 23 | import org.thingsboard.server.common.data.ApiUsageStateValue; |
@@ -27,6 +28,7 @@ import org.thingsboard.server.common.data.id.ApiUsageStateId; | @@ -27,6 +28,7 @@ import org.thingsboard.server.common.data.id.ApiUsageStateId; | ||
27 | import org.thingsboard.server.common.data.id.TenantId; | 28 | import org.thingsboard.server.common.data.id.TenantId; |
28 | import org.thingsboard.server.common.data.kv.BasicTsKvEntry; | 29 | import org.thingsboard.server.common.data.kv.BasicTsKvEntry; |
29 | import org.thingsboard.server.common.data.kv.LongDataEntry; | 30 | import org.thingsboard.server.common.data.kv.LongDataEntry; |
31 | +import org.thingsboard.server.common.data.kv.StringDataEntry; | ||
30 | import org.thingsboard.server.common.data.kv.TsKvEntry; | 32 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
31 | import org.thingsboard.server.common.data.tenant.profile.TenantProfileConfiguration; | 33 | import org.thingsboard.server.common.data.tenant.profile.TenantProfileConfiguration; |
32 | import org.thingsboard.server.dao.entity.AbstractEntityService; | 34 | import org.thingsboard.server.dao.entity.AbstractEntityService; |
@@ -83,7 +85,19 @@ public class ApiUsageStateServiceImpl extends AbstractEntityService implements A | @@ -83,7 +85,19 @@ public class ApiUsageStateServiceImpl extends AbstractEntityService implements A | ||
83 | Tenant tenant = tenantDao.findById(tenantId, tenantId.getId()); | 85 | Tenant tenant = tenantDao.findById(tenantId, tenantId.getId()); |
84 | TenantProfile tenantProfile = tenantProfileDao.findById(tenantId, tenant.getTenantProfileId().getId()); | 86 | TenantProfile tenantProfile = tenantProfileDao.findById(tenantId, tenant.getTenantProfileId().getId()); |
85 | TenantProfileConfiguration configuration = tenantProfile.getProfileData().getConfiguration(); | 87 | TenantProfileConfiguration configuration = tenantProfile.getProfileData().getConfiguration(); |
88 | + List<TsKvEntry> apiUsageStates = new ArrayList<>(); | ||
89 | + apiUsageStates.add(new BasicTsKvEntry(saved.getCreatedTime(), | ||
90 | + new StringDataEntry(ApiFeature.TRANSPORT.getApiStateKey(), ApiUsageStateValue.ENABLED.name()))); | ||
91 | + apiUsageStates.add(new BasicTsKvEntry(saved.getCreatedTime(), | ||
92 | + new StringDataEntry(ApiFeature.DB.getApiStateKey(), ApiUsageStateValue.ENABLED.name()))); | ||
93 | + apiUsageStates.add(new BasicTsKvEntry(saved.getCreatedTime(), | ||
94 | + new StringDataEntry(ApiFeature.RE.getApiStateKey(), ApiUsageStateValue.ENABLED.name()))); | ||
95 | + apiUsageStates.add(new BasicTsKvEntry(saved.getCreatedTime(), | ||
96 | + new StringDataEntry(ApiFeature.JS.getApiStateKey(), ApiUsageStateValue.ENABLED.name()))); | ||
97 | + tsService.save(tenantId, saved.getId(), apiUsageStates, 0L); | ||
98 | + | ||
86 | List<TsKvEntry> profileThresholds = new ArrayList<>(); | 99 | List<TsKvEntry> profileThresholds = new ArrayList<>(); |
100 | + | ||
87 | for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) { | 101 | for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) { |
88 | profileThresholds.add(new BasicTsKvEntry(saved.getCreatedTime(), new LongDataEntry(key.getApiLimitKey(), configuration.getProfileThreshold(key)))); | 102 | profileThresholds.add(new BasicTsKvEntry(saved.getCreatedTime(), new LongDataEntry(key.getApiLimitKey(), configuration.getProfileThreshold(key)))); |
89 | } | 103 | } |
@@ -58,7 +58,7 @@ In case of any issues you can examine service logs for errors. | @@ -58,7 +58,7 @@ In case of any issues you can examine service logs for errors. | ||
58 | For example to see ThingsBoard node logs execute the following command: | 58 | For example to see ThingsBoard node logs execute the following command: |
59 | 59 | ||
60 | ` | 60 | ` |
61 | -$ docker-compose logs -f tb-core1 tb-rule-engine1 | 61 | +$ docker-compose logs -f tb-core1 tb-core2 tb-rule-engine1 tb-rule-engine2 tb-mqtt-transport1 tb-mqtt-transport2 |
62 | ` | 62 | ` |
63 | 63 | ||
64 | Or use `docker-compose ps` to see the state of all the containers. | 64 | Or use `docker-compose ps` to see the state of all the containers. |
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmState.java
@@ -67,6 +67,7 @@ class AlarmState { | @@ -67,6 +67,7 @@ class AlarmState { | ||
67 | initCurrentAlarm(ctx); | 67 | initCurrentAlarm(ctx); |
68 | lastMsgMetaData = msg.getMetaData(); | 68 | lastMsgMetaData = msg.getMetaData(); |
69 | lastMsgQueueName = msg.getQueueName(); | 69 | lastMsgQueueName = msg.getQueueName(); |
70 | + this.dataSnapshot = data; | ||
70 | return createOrClearAlarms(ctx, data, update, AlarmRuleState::eval); | 71 | return createOrClearAlarms(ctx, data, update, AlarmRuleState::eval); |
71 | } | 72 | } |
72 | 73 | ||
@@ -90,8 +91,7 @@ class AlarmState { | @@ -90,8 +91,7 @@ class AlarmState { | ||
90 | resultState = state; | 91 | resultState = state; |
91 | break; | 92 | break; |
92 | } else if (AlarmEvalResult.FALSE.equals(evalResult)) { | 93 | } else if (AlarmEvalResult.FALSE.equals(evalResult)) { |
93 | - state.clear(); | ||
94 | - stateUpdate |= state.checkUpdate(); | 94 | + stateUpdate = clearAlarmState(stateUpdate, state); |
95 | } | 95 | } |
96 | } | 96 | } |
97 | if (resultState != null) { | 97 | if (resultState != null) { |
@@ -99,6 +99,7 @@ class AlarmState { | @@ -99,6 +99,7 @@ class AlarmState { | ||
99 | if (result != null) { | 99 | if (result != null) { |
100 | pushMsg(ctx, result); | 100 | pushMsg(ctx, result); |
101 | } | 101 | } |
102 | + stateUpdate = clearAlarmState(stateUpdate, clearState); | ||
102 | } else if (currentAlarm != null && clearState != null) { | 103 | } else if (currentAlarm != null && clearState != null) { |
103 | if (!validateUpdate(update, clearState)) { | 104 | if (!validateUpdate(update, clearState)) { |
104 | log.debug("[{}] Update is not valid for current clear state", alarmDefinition.getId()); | 105 | log.debug("[{}] Update is not valid for current clear state", alarmDefinition.getId()); |
@@ -106,23 +107,26 @@ class AlarmState { | @@ -106,23 +107,26 @@ class AlarmState { | ||
106 | } | 107 | } |
107 | AlarmEvalResult evalResult = evalFunction.apply(clearState, data); | 108 | AlarmEvalResult evalResult = evalFunction.apply(clearState, data); |
108 | if (AlarmEvalResult.TRUE.equals(evalResult)) { | 109 | if (AlarmEvalResult.TRUE.equals(evalResult)) { |
109 | - clearState.clear(); | ||
110 | - stateUpdate |= clearState.checkUpdate(); | 110 | + stateUpdate = clearAlarmState(stateUpdate, clearState); |
111 | for (AlarmRuleState state : createRulesSortedBySeverityDesc) { | 111 | for (AlarmRuleState state : createRulesSortedBySeverityDesc) { |
112 | - state.clear(); | ||
113 | - stateUpdate |= state.checkUpdate(); | 112 | + stateUpdate = clearAlarmState(stateUpdate, state); |
114 | } | 113 | } |
115 | ctx.getAlarmService().clearAlarm(ctx.getTenantId(), currentAlarm.getId(), JacksonUtil.OBJECT_MAPPER.createObjectNode(), System.currentTimeMillis()); | 114 | ctx.getAlarmService().clearAlarm(ctx.getTenantId(), currentAlarm.getId(), JacksonUtil.OBJECT_MAPPER.createObjectNode(), System.currentTimeMillis()); |
116 | pushMsg(ctx, new TbAlarmResult(false, false, true, currentAlarm)); | 115 | pushMsg(ctx, new TbAlarmResult(false, false, true, currentAlarm)); |
117 | currentAlarm = null; | 116 | currentAlarm = null; |
118 | } else if (AlarmEvalResult.FALSE.equals(evalResult)) { | 117 | } else if (AlarmEvalResult.FALSE.equals(evalResult)) { |
119 | - clearState.clear(); | ||
120 | - stateUpdate |= clearState.checkUpdate(); | 118 | + stateUpdate = clearAlarmState(stateUpdate, clearState); |
121 | } | 119 | } |
122 | } | 120 | } |
123 | return stateUpdate; | 121 | return stateUpdate; |
124 | } | 122 | } |
125 | 123 | ||
124 | + public boolean clearAlarmState(boolean stateUpdate, AlarmRuleState state) { | ||
125 | + state.clear(); | ||
126 | + stateUpdate |= state.checkUpdate(); | ||
127 | + return stateUpdate; | ||
128 | + } | ||
129 | + | ||
126 | public boolean validateUpdate(SnapshotUpdate update, AlarmRuleState state) { | 130 | public boolean validateUpdate(SnapshotUpdate update, AlarmRuleState state) { |
127 | if (update != null) { | 131 | if (update != null) { |
128 | //Check that the update type and that keys match. | 132 | //Check that the update type and that keys match. |
@@ -190,7 +194,7 @@ class AlarmState { | @@ -190,7 +194,7 @@ class AlarmState { | ||
190 | } | 194 | } |
191 | } | 195 | } |
192 | 196 | ||
193 | - private <T> TbAlarmResult calculateAlarmResult(TbContext ctx, AlarmRuleState ruleState) { | 197 | + private TbAlarmResult calculateAlarmResult(TbContext ctx, AlarmRuleState ruleState) { |
194 | AlarmSeverity severity = ruleState.getSeverity(); | 198 | AlarmSeverity severity = ruleState.getSeverity(); |
195 | if (currentAlarm != null) { | 199 | if (currentAlarm != null) { |
196 | // TODO: In some extremely rare cases, we might miss the event of alarm clear (If one use in-mem queue and restarted the server) or (if one manipulated the rule chain). | 200 | // TODO: In some extremely rare cases, we might miss the event of alarm clear (If one use in-mem queue and restarted the server) or (if one manipulated the rule chain). |
@@ -230,7 +234,7 @@ class AlarmState { | @@ -230,7 +234,7 @@ class AlarmState { | ||
230 | } | 234 | } |
231 | } | 235 | } |
232 | 236 | ||
233 | - private <T> JsonNode createDetails(AlarmRuleState ruleState) { | 237 | + private JsonNode createDetails(AlarmRuleState ruleState) { |
234 | ObjectNode details = JacksonUtil.OBJECT_MAPPER.createObjectNode(); | 238 | ObjectNode details = JacksonUtil.OBJECT_MAPPER.createObjectNode(); |
235 | String alarmDetails = ruleState.getAlarmRule().getAlarmDetails(); | 239 | String alarmDetails = ruleState.getAlarmRule().getAlarmDetails(); |
236 | 240 | ||
@@ -273,8 +277,7 @@ class AlarmState { | @@ -273,8 +277,7 @@ class AlarmState { | ||
273 | if (currentAlarm != null && currentAlarm.getId().equals(alarmNf.getId())) { | 277 | if (currentAlarm != null && currentAlarm.getId().equals(alarmNf.getId())) { |
274 | currentAlarm = null; | 278 | currentAlarm = null; |
275 | for (AlarmRuleState state : createRulesSortedBySeverityDesc) { | 279 | for (AlarmRuleState state : createRulesSortedBySeverityDesc) { |
276 | - state.clear(); | ||
277 | - updated |= state.checkUpdate(); | 280 | + updated = clearAlarmState(updated, state); |
278 | } | 281 | } |
279 | } | 282 | } |
280 | return updated; | 283 | return updated; |
@@ -630,6 +630,9 @@ export class EntityService { | @@ -630,6 +630,9 @@ export class EntityService { | ||
630 | case EntityType.DASHBOARD: | 630 | case EntityType.DASHBOARD: |
631 | entityFieldKeys.push(entityFields.title.keyName); | 631 | entityFieldKeys.push(entityFields.title.keyName); |
632 | break; | 632 | break; |
633 | + case EntityType.API_USAGE_STATE: | ||
634 | + entityFieldKeys.push(entityFields.name.keyName); | ||
635 | + break; | ||
633 | } | 636 | } |
634 | return query ? entityFieldKeys.filter((entityField) => entityField.toLowerCase().indexOf(query) === 0) : entityFieldKeys; | 637 | return query ? entityFieldKeys.filter((entityField) => entityField.toLowerCase().indexOf(query) === 0) : entityFieldKeys; |
635 | } | 638 | } |
@@ -422,6 +422,12 @@ export class DashboardUtilsService { | @@ -422,6 +422,12 @@ export class DashboardUtilsService { | ||
422 | widgetLayout.row = row; | 422 | widgetLayout.row = row; |
423 | widgetLayout.col = 0; | 423 | widgetLayout.col = 0; |
424 | } | 424 | } |
425 | + | ||
426 | + widgetLayout.sizeX = Math.floor(widgetLayout.sizeX); | ||
427 | + widgetLayout.sizeY = Math.floor(widgetLayout.sizeY); | ||
428 | + widgetLayout.row = Math.floor(widgetLayout.row); | ||
429 | + widgetLayout.col = Math.floor(widgetLayout.col); | ||
430 | + | ||
425 | layout.widgets[widget.id] = widgetLayout; | 431 | layout.widgets[widget.id] = widgetLayout; |
426 | } | 432 | } |
427 | 433 |
@@ -63,6 +63,9 @@ | @@ -63,6 +63,9 @@ | ||
63 | </mat-error> | 63 | </mat-error> |
64 | </mat-form-field> | 64 | </mat-form-field> |
65 | </div> | 65 | </div> |
66 | + <mat-error *ngIf="mqttDeviceProfileTransportConfigurationFormGroup.get('configuration').hasError('unique')"> | ||
67 | + {{ 'device-profile.mqtt-device-topic-filters-unique' | translate }} | ||
68 | + </mat-error> | ||
66 | <div class="tb-hint" innerHTML="{{ 'device-profile.support-level-wildcards' | translate }}"></div> | 69 | <div class="tb-hint" innerHTML="{{ 'device-profile.support-level-wildcards' | translate }}"></div> |
67 | <div class="tb-hint" innerHTML="{{ 'device-profile.single-level-wildcards-hint' | translate }}"></div> | 70 | <div class="tb-hint" innerHTML="{{ 'device-profile.single-level-wildcards-hint' | translate }}"></div> |
68 | <div class="tb-hint" innerHTML="{{ 'device-profile.multi-level-wildcards-hint' | translate }}"></div> | 71 | <div class="tb-hint" innerHTML="{{ 'device-profile.multi-level-wildcards-hint' | translate }}"></div> |
@@ -51,7 +51,6 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control | @@ -51,7 +51,6 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control | ||
51 | 51 | ||
52 | mqttTransportPayloadTypeTranslations = mqttTransportPayloadTypeTranslationMap; | 52 | mqttTransportPayloadTypeTranslations = mqttTransportPayloadTypeTranslationMap; |
53 | 53 | ||
54 | - | ||
55 | mqttDeviceProfileTransportConfigurationFormGroup: FormGroup; | 54 | mqttDeviceProfileTransportConfigurationFormGroup: FormGroup; |
56 | 55 | ||
57 | private requiredValue: boolean; | 56 | private requiredValue: boolean; |
@@ -87,7 +86,7 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control | @@ -87,7 +86,7 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control | ||
87 | deviceAttributesTopic: [null, [Validators.required, this.validationMQTTTopic()]], | 86 | deviceAttributesTopic: [null, [Validators.required, this.validationMQTTTopic()]], |
88 | deviceTelemetryTopic: [null, [Validators.required, this.validationMQTTTopic()]], | 87 | deviceTelemetryTopic: [null, [Validators.required, this.validationMQTTTopic()]], |
89 | transportPayloadType: [MqttTransportPayloadType.JSON, Validators.required] | 88 | transportPayloadType: [MqttTransportPayloadType.JSON, Validators.required] |
90 | - }) | 89 | + }, {validator: this.uniqueDeviceTopicValidator}) |
91 | }); | 90 | }); |
92 | this.mqttDeviceProfileTransportConfigurationFormGroup.valueChanges.subscribe(() => { | 91 | this.mqttDeviceProfileTransportConfigurationFormGroup.valueChanges.subscribe(() => { |
93 | this.updateModel(); | 92 | this.updateModel(); |
@@ -147,4 +146,14 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control | @@ -147,4 +146,14 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control | ||
147 | return null; | 146 | return null; |
148 | }; | 147 | }; |
149 | } | 148 | } |
149 | + | ||
150 | + private uniqueDeviceTopicValidator(control: FormGroup): { [key: string]: boolean } | null { | ||
151 | + if (control.value) { | ||
152 | + const formValue = control.value as MqttDeviceProfileTransportConfiguration; | ||
153 | + if (formValue.deviceAttributesTopic === formValue.deviceTelemetryTopic) { | ||
154 | + return {unique: true}; | ||
155 | + } | ||
156 | + } | ||
157 | + return null; | ||
158 | + } | ||
150 | } | 159 | } |
@@ -131,10 +131,13 @@ export default abstract class LeafletMap { | @@ -131,10 +131,13 @@ export default abstract class LeafletMap { | ||
131 | tooltipAnchor: [16, -28], | 131 | tooltipAnchor: [16, -28], |
132 | shadowSize: [41, 41] | 132 | shadowSize: [41, 41] |
133 | }); | 133 | }); |
134 | + const customLatLng = this.convertToCustomFormat(mousePositionOnMap); | ||
135 | + mousePositionOnMap.lat = customLatLng[this.options.latKeyName]; | ||
136 | + mousePositionOnMap.lng = customLatLng[this.options.lngKeyName]; | ||
137 | + | ||
134 | const newMarker = L.marker(mousePositionOnMap, { icon }).addTo(this.map); | 138 | const newMarker = L.marker(mousePositionOnMap, { icon }).addTo(this.map); |
135 | this.addMarkers.push(newMarker); | 139 | this.addMarkers.push(newMarker); |
136 | const datasourcesList = document.createElement('div'); | 140 | const datasourcesList = document.createElement('div'); |
137 | - const customLatLng = this.convertToCustomFormat(mousePositionOnMap); | ||
138 | const header = document.createElement('p'); | 141 | const header = document.createElement('p'); |
139 | header.appendChild(document.createTextNode('Select entity:')); | 142 | header.appendChild(document.createTextNode('Select entity:')); |
140 | header.setAttribute('style', 'font-size: 14px; margin: 8px 0'); | 143 | header.setAttribute('style', 'font-size: 14px; margin: 8px 0'); |
@@ -410,10 +413,15 @@ export default abstract class LeafletMap { | @@ -410,10 +413,15 @@ export default abstract class LeafletMap { | ||
410 | } | 413 | } |
411 | 414 | ||
412 | convertToCustomFormat(position: L.LatLng): object { | 415 | convertToCustomFormat(position: L.LatLng): object { |
413 | - return { | ||
414 | - [this.options.latKeyName]: position.lat % 90, | ||
415 | - [this.options.lngKeyName]: position.lng % 180 | ||
416 | - }; | 416 | + if (position.lng > 180) { |
417 | + position.lng = 180; | ||
418 | + } else if (position.lng < -180) { | ||
419 | + position.lng = -180; | ||
420 | + } | ||
421 | + return { | ||
422 | + [this.options.latKeyName]: position.lat, | ||
423 | + [this.options.lngKeyName]: position.lng | ||
424 | + }; | ||
417 | } | 425 | } |
418 | 426 | ||
419 | convertToPolygonFormat(points: Array<any>): Array<any> { | 427 | convertToPolygonFormat(points: Array<any>): Array<any> { |
@@ -94,7 +94,7 @@ export class Marker { | @@ -94,7 +94,7 @@ export class Marker { | ||
94 | } | 94 | } |
95 | 95 | ||
96 | updateMarkerPosition(position: L.LatLng) { | 96 | updateMarkerPosition(position: L.LatLng) { |
97 | - if (!this.location.equals(position)) { | 97 | + if (!this.leafletMarker.getLatLng().equals(position)) { |
98 | this.location = position; | 98 | this.location = position; |
99 | this.leafletMarker.setLatLng(position); | 99 | this.leafletMarker.setLatLng(position); |
100 | } | 100 | } |
@@ -259,9 +259,27 @@ export class ImageMap extends LeafletMap { | @@ -259,9 +259,27 @@ export class ImageMap extends LeafletMap { | ||
259 | 259 | ||
260 | convertToCustomFormat(position: L.LatLng, width = this.width, height = this.height): object { | 260 | convertToCustomFormat(position: L.LatLng, width = this.width, height = this.height): object { |
261 | const point = this.latLngToPoint(position); | 261 | const point = this.latLngToPoint(position); |
262 | + const customX = calculateNewPointCoordinate(point.x, width); | ||
263 | + const customY = calculateNewPointCoordinate(point.y, height); | ||
264 | + | ||
265 | + if (customX === 0) { | ||
266 | + point.x = 0; | ||
267 | + } else if (customX === 1) { | ||
268 | + point.x = width; | ||
269 | + } | ||
270 | + | ||
271 | + if (customY === 0) { | ||
272 | + point.y = 0; | ||
273 | + } else if (customY === 1) { | ||
274 | + point.y = height; | ||
275 | + } | ||
276 | + const customLatLng = this.pointToLatLng(point.x, point.y); | ||
277 | + | ||
262 | return { | 278 | return { |
263 | - [this.options.xPosKeyName]: calculateNewPointCoordinate(point.x, width), | ||
264 | - [this.options.yPosKeyName]: calculateNewPointCoordinate(point.y, height) | 279 | + [this.options.xPosKeyName]: customX, |
280 | + [this.options.yPosKeyName]: customY, | ||
281 | + [this.options.latKeyName]: customLatLng.lat, | ||
282 | + [this.options.lngKeyName]: customLatLng.lng | ||
265 | }; | 283 | }; |
266 | } | 284 | } |
267 | 285 |
@@ -47,7 +47,8 @@ export enum EntityType { | @@ -47,7 +47,8 @@ export enum EntityType { | ||
47 | RULE_NODE = 'RULE_NODE', | 47 | RULE_NODE = 'RULE_NODE', |
48 | ENTITY_VIEW = 'ENTITY_VIEW', | 48 | ENTITY_VIEW = 'ENTITY_VIEW', |
49 | WIDGETS_BUNDLE = 'WIDGETS_BUNDLE', | 49 | WIDGETS_BUNDLE = 'WIDGETS_BUNDLE', |
50 | - WIDGET_TYPE = 'WIDGET_TYPE' | 50 | + WIDGET_TYPE = 'WIDGET_TYPE', |
51 | + API_USAGE_STATE = 'API_USAGE_STATE' | ||
51 | } | 52 | } |
52 | 53 | ||
53 | export enum AliasEntityType { | 54 | export enum AliasEntityType { |
@@ -239,6 +240,12 @@ export const entityTypeTranslations = new Map<EntityType | AliasEntityType, Enti | @@ -239,6 +240,12 @@ export const entityTypeTranslations = new Map<EntityType | AliasEntityType, Enti | ||
239 | } | 240 | } |
240 | ], | 241 | ], |
241 | [ | 242 | [ |
243 | + EntityType.API_USAGE_STATE, | ||
244 | + { | ||
245 | + type: 'entity.type-api-usage-state' | ||
246 | + } | ||
247 | + ], | ||
248 | + [ | ||
242 | EntityType.WIDGETS_BUNDLE, | 249 | EntityType.WIDGETS_BUNDLE, |
243 | { | 250 | { |
244 | details: 'widgets-bundle.widgets-bundle-details', | 251 | details: 'widgets-bundle.widgets-bundle-details', |
@@ -885,6 +885,7 @@ | @@ -885,6 +885,7 @@ | ||
885 | "no-device-profiles-found": "No device profiles found.", | 885 | "no-device-profiles-found": "No device profiles found.", |
886 | "create-new-device-profile": "Create a new one!", | 886 | "create-new-device-profile": "Create a new one!", |
887 | "mqtt-device-topic-filters": "MQTT device topic filters", | 887 | "mqtt-device-topic-filters": "MQTT device topic filters", |
888 | + "mqtt-device-topic-filters-unique": "MQTT device topic filters need to be unique.", | ||
888 | "mqtt-device-payload-type": "MQTT device payload", | 889 | "mqtt-device-payload-type": "MQTT device payload", |
889 | "mqtt-device-payload-type-json": "JSON", | 890 | "mqtt-device-payload-type-json": "JSON", |
890 | "mqtt-device-payload-type-proto": "Protobuf", | 891 | "mqtt-device-payload-type-proto": "Protobuf", |
@@ -1099,7 +1100,8 @@ | @@ -1099,7 +1100,8 @@ | ||
1099 | "details": "Entity details", | 1100 | "details": "Entity details", |
1100 | "no-entities-prompt": "No entities found", | 1101 | "no-entities-prompt": "No entities found", |
1101 | "no-data": "No data to display", | 1102 | "no-data": "No data to display", |
1102 | - "columns-to-display": "Columns to Display" | 1103 | + "columns-to-display": "Columns to Display", |
1104 | + "type-api-usage-state": "Api Usage State" | ||
1103 | }, | 1105 | }, |
1104 | "entity-field": { | 1106 | "entity-field": { |
1105 | "created-time": "Created time", | 1107 | "created-time": "Created time", |