From 8ba34a953ab20408f77674614018e279d1f43be4 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Tue, 3 Dec 2024 14:06:06 +0200 Subject: [PATCH 01/75] Dashboard/widget resources export - process whole config --- .../controller/DashboardControllerTest.java | 20 +++- .../dao/resource/BaseResourceService.java | 108 ++++++++++++------ 2 files changed, 89 insertions(+), 39 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/DashboardControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/DashboardControllerTest.java index 88dd8f241c..ffbf95d990 100644 --- a/application/src/test/java/org/thingsboard/server/controller/DashboardControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/DashboardControllerTest.java @@ -17,6 +17,7 @@ import com.datastax.oss.driver.api.core.uuid.Uuids; import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -602,13 +603,14 @@ public void testExportImportDashboardWithResources() throws Exception { dashboard.setTitle("My dashboard"); dashboard.setConfiguration(JacksonUtil.newObjectNode() .put("someImage", "tb-image;/api/images/tenant/" + imageInfo.getResourceKey()) - .set("widgets", JacksonUtil.toJsonNode(""" + .set("widgets", JacksonUtil.toJsonNode(""" {"xxx": {"config":{"actions":{"elementClick":[ {"customResources":[{"url":{"entityType":"TB_RESOURCE","id": "tb-resource;/api/resource/js_module/tenant/gateway-management-extension.js"},"isModule":true}, {"url":"tb-resource;/api/resource/js_module/tenant/gateway-management-extension.js","isModule":true}]}]}}}} - """))); + """)) + .put("someResource", "tb-resource;/api/resource/js_module/tenant/gateway-management-extension.js")); dashboard = doPost("/api/dashboard", dashboard, Dashboard.class); Dashboard exportedDashboard = doGet("/api/dashboard/" + dashboard.getUuidId() + "?includeResources=true", Dashboard.class); @@ -637,12 +639,18 @@ public void testExportImportDashboardWithResources() throws Exception { doPost("/api/resource", resource, TbResourceInfo.class); Dashboard importedDashboard = doPost("/api/dashboard", exportedDashboard, Dashboard.class); + String newResourceKey = "gateway-management-extension_(1).js"; + imageRef = importedDashboard.getConfiguration().get("someImage").asText(); assertThat(imageRef).isEqualTo("tb-image;/api/images/tenant/" + imageInfo.getResourceKey()); - resourceRef = importedDashboard.getConfiguration().get("widgets").get("xxx").get("config") - .get("actions").get("elementClick").get(0).get("customResources").get(0).get("url").asText(); - String newResourceKey = "gateway-management-extension_(1).js"; - assertThat(resourceRef).isEqualTo("tb-resource;/api/resource/js_module/tenant/" + newResourceKey); + + List resourcesRefs = new ArrayList<>(); + resourcesRefs.add(importedDashboard.getConfiguration().get("widgets").get("xxx").get("config") + .get("actions").get("elementClick").get(0).get("customResources").get(0).get("url").asText()); + resourcesRefs.add(importedDashboard.getConfiguration().get("someResource").asText()); + assertThat(resourcesRefs).allSatisfy(ref -> { + assertThat(ref).isEqualTo("tb-resource;/api/resource/js_module/tenant/" + newResourceKey); + }); TbResourceInfo importedImageInfo = doGet("/api/images/tenant/" + imageInfo.getResourceKey() + "/info", TbResourceInfo.class); assertThat(importedImageInfo.getEtag()).isEqualTo(imageInfo.getEtag()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java index 22aa2ae748..7101922f6b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java @@ -57,6 +57,7 @@ import org.thingsboard.server.dao.service.validator.ResourceDataValidator; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Base64; import java.util.Collection; import java.util.Collections; @@ -403,16 +404,26 @@ public long sumDataSizeByTenantId(TenantId tenantId) { @Override public boolean updateResourcesUsage(Dashboard dashboard) { Map links = getResourcesLinks(dashboard.getResources()); - return updateResourcesUsage(dashboard.getTenantId(), dashboard.getConfiguration(), DASHBOARD_RESOURCES_MAPPING, links); + return updateResourcesUsage(dashboard.getTenantId(), List.of(dashboard.getConfiguration()), List.of(DASHBOARD_RESOURCES_MAPPING), links); } @Override public boolean updateResourcesUsage(WidgetTypeDetails widgetTypeDetails) { Map links = getResourcesLinks(widgetTypeDetails.getResources()); - boolean updated = updateResourcesUsage(widgetTypeDetails.getTenantId(), widgetTypeDetails.getDescriptor(), WIDGET_RESOURCES_MAPPING, links); + List jsonNodes = new ArrayList<>(2); + List> mappings = new ArrayList<>(2); + + jsonNodes.add(widgetTypeDetails.getDescriptor()); + mappings.add(WIDGET_RESOURCES_MAPPING); + JsonNode defaultConfig = widgetTypeDetails.getDefaultConfig(); if (defaultConfig != null) { - updated |= updateResourcesUsage(widgetTypeDetails.getTenantId(), defaultConfig, WIDGET_DEFAULT_CONFIG_RESOURCES_MAPPING, links); + jsonNodes.add(defaultConfig); + mappings.add(WIDGET_DEFAULT_CONFIG_RESOURCES_MAPPING); + } + + boolean updated = updateResourcesUsage(widgetTypeDetails.getTenantId(), jsonNodes, mappings, links); + if (defaultConfig != null) { widgetTypeDetails.setDefaultConfig(defaultConfig); } return updated; @@ -433,8 +444,9 @@ protected Map getResourcesLinks(List resourc return links; } - private boolean updateResourcesUsage(TenantId tenantId, JsonNode jsonNode, Map mapping, Map links) { - return processResources(jsonNode, mapping, value -> { + private boolean updateResourcesUsage(TenantId tenantId, List jsonNodes, List> mappings, Map links) { + log.trace("[{}] updateResourcesUsage (new links: {}) for {}", tenantId, links, jsonNodes); + return processResources(jsonNodes, mappings, value -> { String link = getResourceLink(value); if (link != null) { String newLink = links.get(link); @@ -463,22 +475,30 @@ private boolean updateResourcesUsage(TenantId tenantId, JsonNode jsonNode, Map getUsedResources(Dashboard dashboard) { - return getUsedResources(dashboard.getTenantId(), dashboard.getConfiguration(), DASHBOARD_RESOURCES_MAPPING).values(); + return getUsedResources(dashboard.getTenantId(), List.of(dashboard.getConfiguration()), List.of(DASHBOARD_RESOURCES_MAPPING)).values(); } @Override public Collection getUsedResources(WidgetTypeDetails widgetTypeDetails) { - Map resources = getUsedResources(widgetTypeDetails.getTenantId(), widgetTypeDetails.getDescriptor(), WIDGET_RESOURCES_MAPPING); + List jsonNodes = new ArrayList<>(2); + List> mappings = new ArrayList<>(2); + + jsonNodes.add(widgetTypeDetails.getDescriptor()); + mappings.add(WIDGET_RESOURCES_MAPPING); + JsonNode defaultConfig = widgetTypeDetails.getDefaultConfig(); if (defaultConfig != null) { - resources.putAll(getUsedResources(widgetTypeDetails.getTenantId(), defaultConfig, WIDGET_DEFAULT_CONFIG_RESOURCES_MAPPING)); + jsonNodes.add(defaultConfig); + mappings.add(WIDGET_DEFAULT_CONFIG_RESOURCES_MAPPING); } - return resources.values(); + + return getUsedResources(widgetTypeDetails.getTenantId(), jsonNodes, mappings).values(); } - private Map getUsedResources(TenantId tenantId, JsonNode jsonNode, Map mapping) { + private Map getUsedResources(TenantId tenantId, List jsonNodes, List> mappings) { Map resources = new HashMap<>(); - processResources(jsonNode, mapping, value -> { + log.trace("[{}] getUsedResources for {}", tenantId, jsonNodes); + processResources(jsonNodes, mappings, value -> { String link = getResourceLink(value); if (link == null) { return value; @@ -517,32 +537,54 @@ private String getResourceLink(String value) { } } - private boolean processResources(JsonNode jsonNode, Map mapping, UnaryOperator processor) { + private boolean processResources(List jsonNodes, List> mappings, UnaryOperator processor) { AtomicBoolean updated = new AtomicBoolean(false); - JacksonUtil.replaceByMapping(jsonNode, mapping, Collections.emptyMap(), (name, urlNode) -> { - String value = null; - if (urlNode.isTextual()) { // link is in the right place - value = urlNode.asText(); - } else { - JsonNode id = urlNode.get("id"); // old structure is used - if (id != null && id.isTextual()) { - value = id.asText(); + + for (int i = 0; i < jsonNodes.size(); i++) { + JsonNode jsonNode = jsonNodes.get(i); + // processing by mappings first + JacksonUtil.replaceByMapping(jsonNode, mappings.get(i), Collections.emptyMap(), (name, urlNode) -> { + String value = null; + if (urlNode.isTextual()) { // link is in the right place + value = urlNode.asText(); + } else { + JsonNode id = urlNode.get("id"); // old structure is used + if (id != null && id.isTextual()) { + value = id.asText(); + } } - } - if (StringUtils.isNotBlank(value)) { - value = processor.apply(value); - } else { - value = ""; - } + if (StringUtils.isNotBlank(value)) { + value = processor.apply(value); + } else { + value = ""; + } + + JsonNode newValue = new TextNode(value); + if (!newValue.toString().equals(urlNode.toString())) { + updated.set(true); + log.trace("Replaced by mapping '{}' ({}) with '{}'", value, name, newValue); + } + return newValue; + }); + + // processing all + JacksonUtil.replaceAll(jsonNode, "", (name, value) -> { + if (!StringUtils.startsWith(value, DataConstants.TB_RESOURCE_PREFIX + "/api/resource/")) { + return value; + } + + String newValue = processor.apply(value); + if (StringUtils.equals(value, newValue)) { + return value; + } else { + updated.set(true); + log.trace("Replaced '{}' ({}) with '{}'", value, name, newValue); + return newValue; + } + }); + } - JsonNode newValue = new TextNode(value); - if (!newValue.toString().equals(urlNode.toString())) { - updated.set(true); - log.trace("Replaced '{}' with '{}'", urlNode, newValue); - } - return newValue; - }); return updated.get(); } From f50789ebf3c2be362e788071f384302d2a1a84a4 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Tue, 3 Dec 2024 17:17:49 +0200 Subject: [PATCH 02/75] Minor refactoring for ResourceService --- .../install/update/ResourcesUpdater.java | 4 +- .../resource/DefaultTbResourceService.java | 15 +++-- .../server/dao/resource/ResourceService.java | 8 +-- .../dao/dashboard/DashboardServiceImpl.java | 11 ++-- .../dao/resource/BaseResourceService.java | 63 ++++++++++--------- .../dao/widget/WidgetTypeServiceImpl.java | 9 +-- 6 files changed, 57 insertions(+), 53 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/install/update/ResourcesUpdater.java b/application/src/main/java/org/thingsboard/server/service/install/update/ResourcesUpdater.java index 00797da449..da2b7276dd 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/update/ResourcesUpdater.java +++ b/application/src/main/java/org/thingsboard/server/service/install/update/ResourcesUpdater.java @@ -101,7 +101,7 @@ public void updateDashboardsResources() { for (DashboardId dashboardId : dashboards) { executor.submit(() -> { Dashboard dashboard = dashboardService.findDashboardById(TenantId.SYS_TENANT_ID, dashboardId); - boolean updated = resourceService.updateResourcesUsage(dashboard); // will convert resources ids to new structure + boolean updated = resourceService.updateResourcesUsage(dashboard.getTenantId(), dashboard); // will convert resources ids to new structure if (updated) { dashboardService.saveDashboard(dashboard); updatedCount.incrementAndGet(); @@ -130,7 +130,7 @@ public void updateWidgetsResources() { for (WidgetTypeId widgetTypeId : widgets) { executor.submit(() -> { WidgetTypeDetails widgetTypeDetails = widgetTypeService.findWidgetTypeDetailsById(TenantId.SYS_TENANT_ID, widgetTypeId); - boolean updated = resourceService.updateResourcesUsage(widgetTypeDetails); + boolean updated = resourceService.updateResourcesUsage(widgetTypeDetails.getTenantId(), widgetTypeDetails); if (updated) { widgetTypeService.saveWidgetType(widgetTypeDetails); updatedCount.incrementAndGet(); diff --git a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java index 90ad3e38dd..e3a79a8ba5 100644 --- a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java +++ b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java @@ -46,7 +46,7 @@ import java.util.Collection; import java.util.Comparator; import java.util.List; -import java.util.function.Function; +import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -132,12 +132,12 @@ public List findLwM2mObjectPage(TenantId tenantId, String sortPrope @Override public List exportResources(Dashboard dashboard, SecurityUser user) throws ThingsboardException { - return exportResources(dashboard, imageService::getUsedImages, resourceService::getUsedResources, user); + return exportResources(() -> imageService.getUsedImages(dashboard), () -> resourceService.getUsedResources(user.getTenantId(), dashboard), user); } @Override public List exportResources(WidgetTypeDetails widgetTypeDetails, SecurityUser user) throws ThingsboardException { - return exportResources(widgetTypeDetails, imageService::getUsedImages, resourceService::getUsedResources, user); + return exportResources(() -> imageService.getUsedImages(widgetTypeDetails), () -> resourceService.getUsedResources(user.getTenantId(), widgetTypeDetails), user); } @Override @@ -153,13 +153,12 @@ public void importResources(List resources, SecurityUser use } } - private List exportResources(T entity, - Function> imagesProcessor, - Function> resourcesProcessor, + private List exportResources(Supplier> imagesProcessor, + Supplier> resourcesProcessor, SecurityUser user) throws ThingsboardException { List resources = new ArrayList<>(); - resources.addAll(imagesProcessor.apply(entity)); - resources.addAll(resourcesProcessor.apply(entity)); + resources.addAll(imagesProcessor.get()); + resources.addAll(resourcesProcessor.get()); for (TbResourceInfo resourceInfo : resources) { accessControlService.checkPermission(user, Resource.TB_RESOURCE, Operation.READ, resourceInfo.getId(), resourceInfo); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java index 0e2acb2e4e..f7f4b1ce4b 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java @@ -80,13 +80,13 @@ public interface ResourceService extends EntityDaoService { TbResourceInfo findSystemOrTenantResourceByEtag(TenantId tenantId, ResourceType resourceType, String etag); - boolean updateResourcesUsage(Dashboard dashboard); + boolean updateResourcesUsage(TenantId tenantId, Dashboard dashboard); - boolean updateResourcesUsage(WidgetTypeDetails widgetTypeDetails); + boolean updateResourcesUsage(TenantId tenantId, WidgetTypeDetails widgetTypeDetails); - Collection getUsedResources(Dashboard dashboard); + Collection getUsedResources(TenantId tenantId, Dashboard dashboard); - Collection getUsedResources(WidgetTypeDetails widgetTypeDetails); + Collection getUsedResources(TenantId tenantId, WidgetTypeDetails widgetTypeDetails); TbResource createOrUpdateSystemResource(ResourceType resourceType, String resourceKey, byte[] data); diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java index b547de1eca..2e9e37577c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java @@ -162,18 +162,19 @@ public Dashboard saveDashboard(Dashboard dashboard, boolean doValidate) { dashboardValidator.validate(dashboard, DashboardInfo::getTenantId); } try { + TenantId tenantId = dashboard.getTenantId(); if (CollectionUtils.isNotEmpty(dashboard.getResources())) { - resourceService.importResources(dashboard.getTenantId(), dashboard.getResources()); + resourceService.importResources(tenantId, dashboard.getResources()); } imageService.updateImagesUsage(dashboard); - resourceService.updateResourcesUsage(dashboard); + resourceService.updateResourcesUsage(tenantId, dashboard); - var saved = dashboardDao.save(dashboard.getTenantId(), dashboard); + var saved = dashboardDao.save(tenantId, dashboard); publishEvictEvent(new DashboardTitleEvictEvent(saved.getId())); - eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(saved.getTenantId()) + eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(tenantId) .entityId(saved.getId()).created(dashboard.getId() == null).build()); if (dashboard.getId() == null) { - countService.publishCountEntityEvictEvent(saved.getTenantId(), EntityType.DASHBOARD); + countService.publishCountEntityEvictEvent(tenantId, EntityType.DASHBOARD); } return saved; } catch (Exception e) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java index 7101922f6b..361d2b3894 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java @@ -402,13 +402,13 @@ public long sumDataSizeByTenantId(TenantId tenantId) { } @Override - public boolean updateResourcesUsage(Dashboard dashboard) { + public boolean updateResourcesUsage(TenantId tenantId, Dashboard dashboard) { Map links = getResourcesLinks(dashboard.getResources()); - return updateResourcesUsage(dashboard.getTenantId(), List.of(dashboard.getConfiguration()), List.of(DASHBOARD_RESOURCES_MAPPING), links); + return updateResourcesUsage(tenantId, List.of(dashboard.getConfiguration()), List.of(DASHBOARD_RESOURCES_MAPPING), links); } @Override - public boolean updateResourcesUsage(WidgetTypeDetails widgetTypeDetails) { + public boolean updateResourcesUsage(TenantId tenantId, WidgetTypeDetails widgetTypeDetails) { Map links = getResourcesLinks(widgetTypeDetails.getResources()); List jsonNodes = new ArrayList<>(2); List> mappings = new ArrayList<>(2); @@ -422,7 +422,7 @@ public boolean updateResourcesUsage(WidgetTypeDetails widgetTypeDetails) { mappings.add(WIDGET_DEFAULT_CONFIG_RESOURCES_MAPPING); } - boolean updated = updateResourcesUsage(widgetTypeDetails.getTenantId(), jsonNodes, mappings, links); + boolean updated = updateResourcesUsage(tenantId, jsonNodes, mappings, links); if (defaultConfig != null) { widgetTypeDetails.setDefaultConfig(defaultConfig); } @@ -474,12 +474,12 @@ private boolean updateResourcesUsage(TenantId tenantId, List jsonNodes } @Override - public Collection getUsedResources(Dashboard dashboard) { - return getUsedResources(dashboard.getTenantId(), List.of(dashboard.getConfiguration()), List.of(DASHBOARD_RESOURCES_MAPPING)).values(); + public Collection getUsedResources(TenantId tenantId, Dashboard dashboard) { + return getUsedResources(tenantId, List.of(dashboard.getConfiguration()), List.of(DASHBOARD_RESOURCES_MAPPING)).values(); } @Override - public Collection getUsedResources(WidgetTypeDetails widgetTypeDetails) { + public Collection getUsedResources(TenantId tenantId, WidgetTypeDetails widgetTypeDetails) { List jsonNodes = new ArrayList<>(2); List> mappings = new ArrayList<>(2); @@ -492,7 +492,7 @@ public Collection getUsedResources(WidgetTypeDetails widgetTypeD mappings.add(WIDGET_DEFAULT_CONFIG_RESOURCES_MAPPING); } - return getUsedResources(widgetTypeDetails.getTenantId(), jsonNodes, mappings).values(); + return getUsedResources(tenantId, jsonNodes, mappings).values(); } private Map getUsedResources(TenantId tenantId, List jsonNodes, List> mappings) { @@ -543,30 +543,33 @@ private boolean processResources(List jsonNodes, List { - String value = null; - if (urlNode.isTextual()) { // link is in the right place - value = urlNode.asText(); - } else { - JsonNode id = urlNode.get("id"); // old structure is used - if (id != null && id.isTextual()) { - value = id.asText(); + if (i <= mappings.size() - 1) { + JacksonUtil.replaceByMapping(jsonNode, mappings.get(i), Collections.emptyMap(), (name, urlNode) -> { + String value = null; + if (urlNode.isTextual()) { // link is in the right place + value = urlNode.asText(); + } else { + JsonNode id = urlNode.get("id"); // old structure is used + if (id != null && id.isTextual()) { + value = id.asText(); + } } - } - if (StringUtils.isNotBlank(value)) { - value = processor.apply(value); - } else { - value = ""; - } + if (StringUtils.isNotBlank(value)) { + value = processor.apply(value); + } else { + value = ""; + } + + JsonNode newValue = new TextNode(value); + if (!newValue.toString().equals(urlNode.toString())) { + updated.set(true); + log.trace("Replaced by mapping '{}' ({}) with '{}'", value, name, newValue); + } + return newValue; + }); + } - JsonNode newValue = new TextNode(value); - if (!newValue.toString().equals(urlNode.toString())) { - updated.set(true); - log.trace("Replaced by mapping '{}' ({}) with '{}'", value, name, newValue); - } - return newValue; - }); // processing all JacksonUtil.replaceAll(jsonNode, "", (name, value) -> { @@ -597,7 +600,7 @@ public TbResource createOrUpdateSystemResource(ResourceType resourceType, String importResources(dashboard.getTenantId(), dashboard.getResources()); } imageService.updateImagesUsage(dashboard); - updateResourcesUsage(dashboard); + updateResourcesUsage(dashboard.getTenantId(), dashboard); data = JacksonUtil.writeValueAsBytes(dashboard); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeServiceImpl.java index fd9b1260ba..a1e241073f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeServiceImpl.java @@ -99,14 +99,15 @@ public WidgetTypeDetails saveWidgetType(WidgetTypeDetails widgetTypeDetails) { log.trace("Executing saveWidgetType [{}]", widgetTypeDetails); widgetTypeValidator.validate(widgetTypeDetails, WidgetType::getTenantId); try { + TenantId tenantId = widgetTypeDetails.getTenantId(); if (CollectionUtils.isNotEmpty(widgetTypeDetails.getResources())) { - resourceService.importResources(widgetTypeDetails.getTenantId(), widgetTypeDetails.getResources()); + resourceService.importResources(tenantId, widgetTypeDetails.getResources()); } imageService.updateImagesUsage(widgetTypeDetails); - resourceService.updateResourcesUsage(widgetTypeDetails); + resourceService.updateResourcesUsage(tenantId, widgetTypeDetails); - WidgetTypeDetails result = widgetTypeDao.save(widgetTypeDetails.getTenantId(), widgetTypeDetails); - eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(result.getTenantId()) + WidgetTypeDetails result = widgetTypeDao.save(tenantId, widgetTypeDetails); + eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(tenantId) .entityId(result.getId()).created(widgetTypeDetails.getId() == null).build()); return result; } catch (Exception t) { From 18eab054a437462bc55d51daca2a679c0e934048 Mon Sep 17 00:00:00 2001 From: nick Date: Wed, 4 Dec 2024 11:35:35 +0200 Subject: [PATCH 03/75] tbel: ver 1.2.5 add bitwise operation for boolean to MathProcessor --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a1d5f26ecf..05f16ca5b1 100755 --- a/pom.xml +++ b/pom.xml @@ -83,7 +83,7 @@ 3.9.2 3.25.3 1.63.0 - 1.2.4 + 1.2.5 1.18.32 1.2.5 1.2.5 From 9fc597a5a4d933da7728a21a8d49cf893c93bc37 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Wed, 4 Dec 2024 18:22:00 +0200 Subject: [PATCH 04/75] fixed oauth2 client fetch by for mobile apps --- .../dao/oauth2/OAuth2ClientServiceImpl.java | 2 +- .../dao/sql/oauth2/JpaOAuth2ClientDao.java | 14 ++++++++++++-- .../dao/sql/oauth2/OAuth2ClientRepository.java | 18 ++++++++++++++---- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientServiceImpl.java index 683c323ab5..452bd7ccf7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientServiceImpl.java @@ -64,7 +64,7 @@ public List findOAuth2ClientLoginInfosByDomainName(String @Override public List findOAuth2ClientLoginInfosByMobilePkgNameAndPlatformType(String pkgName, PlatformType platformType) { - log.trace("Executing findOAuth2ClientLoginInfosByMobilePkgNameAndPlatformType pkgName=[{}] platformType=[{}]",pkgName, platformType); + log.trace("Executing findOAuth2ClientLoginInfosByMobilePkgNameAndPlatformType pkgName=[{}] platformType=[{}]", pkgName, platformType); return oauth2ClientDao.findEnabledByPkgNameAndPlatformType(pkgName, platformType) .stream() .map(OAuth2Utils::toClientLoginInfo) diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientDao.java index 4310e0b705..31464cbe9a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientDao.java @@ -31,6 +31,7 @@ import org.thingsboard.server.dao.sql.JpaAbstractDao; import org.thingsboard.server.dao.util.SqlDao; +import java.util.Collections; import java.util.List; import java.util.UUID; @@ -65,8 +66,17 @@ public List findEnabledByDomainName(String domainName) { @Override public List findEnabledByPkgNameAndPlatformType(String pkgName, PlatformType platformType) { - return DaoUtil.convertDataList(repository.findEnabledByPkgNameAndPlatformType(pkgName, - platformType != null ? platformType.name() : null)); + List clientEntities; + if (platformType != null) { + clientEntities = switch (platformType) { + case ANDROID -> repository.findEnabledByAndroidPkgNameAndPlatformType(pkgName, platformType.name()); + case IOS -> repository.findEnabledByIosPkgNameAndPlatformType(pkgName, platformType.name()); + default -> Collections.emptyList(); + }; + } else { + clientEntities = Collections.emptyList(); + } + return DaoUtil.convertDataList(clientEntities); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRepository.java index f7fe5bce3a..2a5526c146 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRepository.java @@ -49,11 +49,21 @@ List findEnabledByDomainNameAndPlatformType(@Param("domainNa "FROM OAuth2ClientEntity c " + "LEFT JOIN MobileAppBundleOauth2ClientEntity ac ON c.id = ac.oauth2ClientId " + "LEFT JOIN MobileAppBundleEntity b ON ac.mobileAppBundleId = b.id " + - "LEFT JOIN MobileAppEntity andApp ON b.androidAppId = andApp.id LEFT JOIN MobileAppEntity iosApp ON b.iosAppID = iosApp.id " + - "WHERE andApp.pkgName = :pkgName OR iosApp.pkgName = :pkgName AND b.oauth2Enabled = true " + + "LEFT JOIN MobileAppEntity andApp ON b.androidAppId = andApp.id " + + "WHERE andApp.pkgName = :pkgName AND b.oauth2Enabled = true " + "AND (:platformFilter IS NULL OR c.platforms IS NULL OR c.platforms = '' OR ilike(c.platforms, CONCAT('%', :platformFilter, '%')) = true)") - List findEnabledByPkgNameAndPlatformType(@Param("pkgName") String pkgName, - @Param("platformFilter") String platformFilter); + List findEnabledByAndroidPkgNameAndPlatformType(@Param("pkgName") String pkgName, + @Param("platformFilter") String platformFilter); + + @Query("SELECT c " + + "FROM OAuth2ClientEntity c " + + "LEFT JOIN MobileAppBundleOauth2ClientEntity ac ON c.id = ac.oauth2ClientId " + + "LEFT JOIN MobileAppBundleEntity b ON ac.mobileAppBundleId = b.id " + + "LEFT JOIN MobileAppEntity iosApp ON b.iosAppID = iosApp.id " + + "WHERE iosApp.pkgName = :pkgName AND b.oauth2Enabled = true " + + "AND (:platformFilter IS NULL OR c.platforms IS NULL OR c.platforms = '' OR ilike(c.platforms, CONCAT('%', :platformFilter, '%')) = true)") + List findEnabledByIosPkgNameAndPlatformType(@Param("pkgName") String pkgName, + @Param("platformFilter") String platformFilter); @Query("SELECT c " + "FROM OAuth2ClientEntity c " + From b2c934a4a44f450573a4f83b1a4d5e64a60a239a Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Thu, 5 Dec 2024 10:57:29 +0200 Subject: [PATCH 05/75] Fixed ordering for available notification delivery methods --- .../thingsboard/server/controller/NotificationController.java | 2 +- .../service/notification/DefaultNotificationCenter.java | 4 ++-- .../org/thingsboard/rule/engine/api/NotificationCenter.java | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java index e055c6d6ae..72c339c635 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java @@ -477,7 +477,7 @@ public NotificationSettings getNotificationSettings(@AuthenticationPrincipal Sec SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @GetMapping("/notification/deliveryMethods") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - public Set getAvailableDeliveryMethods(@AuthenticationPrincipal SecurityUser user) throws ThingsboardException { + public List getAvailableDeliveryMethods(@AuthenticationPrincipal SecurityUser user) throws ThingsboardException { return notificationCenter.getAvailableDeliveryMethods(user.getTenantId()); } diff --git a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java index 311fe6cb2c..af63256f0f 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java @@ -417,7 +417,7 @@ public void deleteNotification(TenantId tenantId, UserId recipientId, Notificati } @Override - public Set getAvailableDeliveryMethods(TenantId tenantId) { + public List getAvailableDeliveryMethods(TenantId tenantId) { return channels.values().stream() .filter(channel -> { try { @@ -428,7 +428,7 @@ public Set getAvailableDeliveryMethods(TenantId tena } }) .map(NotificationChannel::getDeliveryMethod) - .collect(Collectors.toSet()); + .sorted().toList(); } @Override diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/NotificationCenter.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/NotificationCenter.java index 02ee53f3ad..b8fb9b61ff 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/NotificationCenter.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/NotificationCenter.java @@ -30,7 +30,7 @@ import org.thingsboard.server.common.data.notification.targets.platform.UsersFilter; import org.thingsboard.server.common.data.notification.template.NotificationTemplate; -import java.util.Set; +import java.util.List; public interface NotificationCenter { @@ -48,6 +48,6 @@ public interface NotificationCenter { void deleteNotification(TenantId tenantId, UserId recipientId, NotificationId notificationId); - Set getAvailableDeliveryMethods(TenantId tenantId); + List getAvailableDeliveryMethods(TenantId tenantId); } From 143d5a95b33e090d932747db72edf024285e2ebc Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 5 Dec 2024 11:23:51 +0200 Subject: [PATCH 06/75] UI: Fixed double open dialog when edit mobile bundle --- .../mobile-bundle-table-config.resolve.ts | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-table-config.resolve.ts b/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-table-config.resolve.ts index 24a503147e..177f41ba1b 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-table-config.resolve.ts +++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-table-config.resolve.ts @@ -24,13 +24,13 @@ import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; import { MobileAppBundleInfo } from '@shared/models/mobile-app.models'; -import { ActivatedRouteSnapshot } from '@angular/router'; +import { ActivatedRouteSnapshot, Router } from '@angular/router'; import { EntityType, entityTypeResources, entityTypeTranslations } from '@shared/models/entity-type.models'; import { Direction } from '@shared/models/page/sort-order'; import { MobileBundleTableHeaderComponent } from '@home/pages/mobile/bundes/mobile-bundle-table-header.component'; import { DatePipe } from '@angular/common'; import { MobileAppService } from '@core/http/mobile-app.service'; -import { map, take } from 'rxjs/operators'; +import { finalize, map, skip, take, takeUntil } from 'rxjs/operators'; import { TranslateService } from '@ngx-translate/core'; import { EntityAction } from '@home/models/entity/entity-component.models'; import { MatDialog } from '@angular/material/dialog'; @@ -52,11 +52,14 @@ export class MobileBundleTableConfigResolver { private readonly config: EntityTableConfig = new EntityTableConfig(); + private openingEditDialog = false; + constructor( private datePipe: DatePipe, private mobileAppService: MobileAppService, private translate : TranslateService, private dialog: MatDialog, + private router: Router, private store: Store ) { this.config.selectionEnabled = false; @@ -108,9 +111,15 @@ export class MobileBundleTableConfigResolver { this.config.handleRowClick = ($event, bundle) => { $event?.stopPropagation(); - this.mobileAppService.getMobileAppBundleInfoById(bundle.id.id).subscribe(appBundleInfo => { - this.editBundle($event, appBundleInfo); - }) + if (!this.openingEditDialog) { + this.openingEditDialog = true; + this.mobileAppService.getMobileAppBundleInfoById(bundle.id.id).pipe( + takeUntil(this.router.events.pipe(skip(1))), + finalize(() => {this.openingEditDialog = false;}) + ).subscribe( + appBundleInfo => this.editBundle($event, appBundleInfo) + ); + } return true; }; From 9a5d37c3c229bdeb843c6dc3b39f3a61734fd3d2 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Thu, 5 Dec 2024 12:02:40 +0200 Subject: [PATCH 07/75] UI: Fixed for value less than min and more than max --- .../system/scada_symbols/dynamic-horizontal-scale-hp.svg | 8 ++++---- .../system/scada_symbols/dynamic-vertical-scale-hp.svg | 8 ++++---- .../system/scada_symbols/simple-horizontal-scale-hp.svg | 8 ++++---- .../system/scada_symbols/simple-vertical-scale-hp.svg | 8 ++++---- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/application/src/main/data/json/system/scada_symbols/dynamic-horizontal-scale-hp.svg b/application/src/main/data/json/system/scada_symbols/dynamic-horizontal-scale-hp.svg index 5576c939c4..bfd711cd38 100644 --- a/application/src/main/data/json/system/scada_symbols/dynamic-horizontal-scale-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/dynamic-horizontal-scale-hp.svg @@ -18,12 +18,12 @@ }, { "tag": "highCriticalScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalState = ctx.values.highCriticalState;\nif (showHighCriticalScale && highCriticalState !== null) {\n element.show();\n var offset = calculateOffset(highCriticalState, minValue, maxValue);\n element.width(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighCriticalScale && value !== null && highCriticalState !== null) {\n if (value >= highCriticalState && value <= ctx.properties.maxValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalState = ctx.values.highCriticalState;\nif (showHighCriticalScale && highCriticalState !== null) {\n element.show();\n var offset = calculateOffset(highCriticalState, minValue, maxValue);\n element.width(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighCriticalScale && value !== null && highCriticalState !== null) {\n if (value >= highCriticalState) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", "actions": null }, { "tag": "highWarningScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar highWarningState = ctx.values.highWarningState;\nvar highCriticalState = ctx.values.highCriticalState;\nif (showHighWarningScale && highWarningState !== null) {\n element.show();\n var offset = calculateOffset(highWarningState, minValue, maxValue);\n element.width(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighWarningScale && value !== null) {\n if (!showHighCriticalScale) {\n highCriticalState = ctx.properties.maxValue;\n }\n \n if (highWarningState !== null && highCriticalState !== null) {\n if (value < highCriticalState && value >= highWarningState) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar highWarningState = ctx.values.highWarningState;\nvar highCriticalState = ctx.values.highCriticalState;\nif (showHighWarningScale && highWarningState !== null) {\n element.show();\n var offset = calculateOffset(highWarningState, minValue, maxValue);\n element.width(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighWarningScale && value !== null) {\n if (!showHighCriticalScale) {\n highCriticalState = Number.MAX_SAFE_INTEGER;\n }\n \n if (highWarningState !== null && highCriticalState !== null) {\n if (value < highCriticalState && value >= highWarningState) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}", "actions": null }, { @@ -33,12 +33,12 @@ }, { "tag": "lowCriticalScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalValue = ctx.values.lowCriticalState;\n\nif (showLowCriticalScale && lowCriticalValue !== null) {\n element.show();\n var offset = calculateOffset(lowCriticalValue, minValue, maxValue);\n element.width(offset);\n} else {\n element.hide();\n}\n\nif (showLowCriticalScale && value !== null) {\n var lowCriticalScale = ctx.values.lowCriticalState;\n if (value <= lowCriticalScale && value >= ctx.properties.minValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalValue = ctx.values.lowCriticalState;\n\nif (showLowCriticalScale && lowCriticalValue !== null) {\n element.show();\n var offset = calculateOffset(lowCriticalValue, minValue, maxValue);\n element.width(offset);\n} else {\n element.hide();\n}\n\nif (showLowCriticalScale && value !== null) {\n var lowCriticalScale = ctx.values.lowCriticalState;\n if (value <= lowCriticalScale) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", "actions": null }, { "tag": "lowWarningScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowWarningState = ctx.values.lowWarningState;\nvar lowCriticalState = ctx.values.lowCriticalState;\nif (showLowWarningScale && lowWarningState !== null) {\n element.show();\n var offset = calculateOffset(lowWarningState, minValue, maxValue);\n element.width(offset);\n} else {\n element.hide();\n}\n\nif (showLowWarningScale && value !== null) {\n if (!showLowCriticalScale) {\n lowCriticalState = ctx.properties.minValue;\n }\n if (lowCriticalState !== null && lowWarningState !== null) {\n if (value > lowCriticalState && value <= lowWarningState) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowWarningState = ctx.values.lowWarningState;\nvar lowCriticalState = ctx.values.lowCriticalState;\nif (showLowWarningScale && lowWarningState !== null) {\n element.show();\n var offset = calculateOffset(lowWarningState, minValue, maxValue);\n element.width(offset);\n} else {\n element.hide();\n}\n\nif (showLowWarningScale && value !== null) {\n if (!showLowCriticalScale) {\n lowCriticalState = Number.MIN_SAFE_INTEGER;\n }\n if (lowCriticalState !== null && lowWarningState !== null) {\n if (value > lowCriticalState && value <= lowWarningState) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}", "actions": null }, { diff --git a/application/src/main/data/json/system/scada_symbols/dynamic-vertical-scale-hp.svg b/application/src/main/data/json/system/scada_symbols/dynamic-vertical-scale-hp.svg index b7d5e0184b..5dd6dd43a3 100644 --- a/application/src/main/data/json/system/scada_symbols/dynamic-vertical-scale-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/dynamic-vertical-scale-hp.svg @@ -18,12 +18,12 @@ }, { "tag": "highCriticalScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalState = ctx.values.highCriticalState;\nif (showHighCriticalScale && highCriticalState !== null) {\n element.show();\n var offset = calculateOffset(highCriticalState, minValue, maxValue);\n element.height(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighCriticalScale && value !== null && highCriticalState !== null) {\n if (value >= highCriticalState && value <= ctx.properties.maxValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalState = ctx.values.highCriticalState;\nif (showHighCriticalScale && highCriticalState !== null) {\n element.show();\n var offset = calculateOffset(highCriticalState, minValue, maxValue);\n element.height(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighCriticalScale && value !== null && highCriticalState !== null) {\n if (value >= highCriticalState) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", "actions": null }, { "tag": "highWarningScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar highWarningState = ctx.values.highWarningState;\nvar highCriticalState = ctx.values.highCriticalState;\nif (showHighWarningScale && highWarningState !== null) {\n element.show();\n var offset = calculateOffset(highWarningState, minValue, maxValue);\n element.height(653-offset);\n} else {\n element.hide();\n}\nif (showHighWarningScale && value !== null) {\n if (!showHighCriticalScale) {\n highCriticalState = ctx.properties.maxValue;\n }\n if (highWarningState !== null && highCriticalState !== null) {\n if (value < highCriticalState && value >= highWarningState) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar highWarningState = ctx.values.highWarningState;\nvar highCriticalState = ctx.values.highCriticalState;\nif (showHighWarningScale && highWarningState !== null) {\n element.show();\n var offset = calculateOffset(highWarningState, minValue, maxValue);\n element.height(653-offset);\n} else {\n element.hide();\n}\nif (showHighWarningScale && value !== null) {\n if (!showHighCriticalScale) {\n highCriticalState = Number.MAX_SAFE_INTEGER;\n }\n if (highWarningState !== null && highCriticalState !== null) {\n if (value < highCriticalState && value >= highWarningState) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}", "actions": null }, { @@ -33,12 +33,12 @@ }, { "tag": "lowCriticalScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalValue = ctx.values.lowCriticalState;\n\nif (showLowCriticalScale && lowCriticalValue !== null) {\n element.show();\n var offset = calculateOffset(lowCriticalValue, minValue, maxValue);\n element.height(offset);\n} else {\n element.hide();\n}\nif (showLowCriticalScale && value !== null) {\n var lowCriticalScale = ctx.values.lowCriticalState;\n if (value <= lowCriticalScale && value >= ctx.properties.minValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalValue = ctx.values.lowCriticalState;\n\nif (showLowCriticalScale && lowCriticalValue !== null) {\n element.show();\n var offset = calculateOffset(lowCriticalValue, minValue, maxValue);\n element.height(offset);\n} else {\n element.hide();\n}\nif (showLowCriticalScale && value !== null) {\n var lowCriticalScale = ctx.values.lowCriticalState;\n if (value <= lowCriticalScale) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", "actions": null }, { "tag": "lowWarningScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowWarningState = ctx.values.lowWarningState;\nvar lowCriticalState = ctx.values.lowCriticalState;\nif (showLowWarningScale && lowWarningState !== null) {\n element.show();\n var offset = calculateOffset(lowWarningState, minValue, maxValue);\n element.height(offset);\n} else {\n element.hide();\n}\nif (showLowWarningScale && value !== null) {\n if (!showLowCriticalScale) {\n lowCriticalState = ctx.properties.minValue;\n }\n if (lowCriticalState !== null && lowWarningState !== null) {\n if (value > lowCriticalState && value <= lowWarningState) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowWarningState = ctx.values.lowWarningState;\nvar lowCriticalState = ctx.values.lowCriticalState;\nif (showLowWarningScale && lowWarningState !== null) {\n element.show();\n var offset = calculateOffset(lowWarningState, minValue, maxValue);\n element.height(offset);\n} else {\n element.hide();\n}\nif (showLowWarningScale && value !== null) {\n if (!showLowCriticalScale) {\n lowCriticalState = Number.MIN_SAFE_INTEGER;\n }\n if (lowCriticalState !== null && lowWarningState !== null) {\n if (value > lowCriticalState && value <= lowWarningState) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}", "actions": null }, { diff --git a/application/src/main/data/json/system/scada_symbols/simple-horizontal-scale-hp.svg b/application/src/main/data/json/system/scada_symbols/simple-horizontal-scale-hp.svg index e69daae548..7b43164cce 100644 --- a/application/src/main/data/json/system/scada_symbols/simple-horizontal-scale-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/simple-horizontal-scale-hp.svg @@ -18,12 +18,12 @@ }, { "tag": "highCriticalScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalValue = ctx.properties.highCriticalScale;\nvar value = ctx.values.value;\n\nif (showHighCriticalScale) {\n element.show();\n var offset = calculateOffset(highCriticalValue, minValue, maxValue);\n element.width(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighCriticalScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (value >= highCriticalScale && value <= ctx.properties.maxValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalValue = ctx.properties.highCriticalScale;\nvar value = ctx.values.value;\n\nif (showHighCriticalScale) {\n element.show();\n var offset = calculateOffset(highCriticalValue, minValue, maxValue);\n element.width(653-offset);\n} else {\n element.hide();\n}\nif (showHighCriticalScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (value >= highCriticalScale) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", "actions": null }, { "tag": "highWarningScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highWarningValue = ctx.properties.highWarningScale;\nvar value = ctx.values.value;\n\nif (showHighWarningScale) {\n element.show();\n var offset = calculateOffset(highWarningValue, minValue, maxValue);\n element.width(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighWarningScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (!showHighCriticalScale) {\n highCriticalScale = ctx.properties.maxValue;\n }\n var highWarningScale = ctx.properties.highWarningScale;\n if (value < highCriticalScale && value >= highWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highWarningValue = ctx.properties.highWarningScale;\nvar value = ctx.values.value;\n\nif (showHighWarningScale) {\n element.show();\n var offset = calculateOffset(highWarningValue, minValue, maxValue);\n element.width(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighWarningScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (!showHighCriticalScale) {\n highCriticalScale = Number.MAX_SAFE_INTEGER;\n }\n var highWarningScale = ctx.properties.highWarningScale;\n if (value < highCriticalScale && value >= highWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}", "actions": null }, { @@ -33,12 +33,12 @@ }, { "tag": "lowCriticalScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalScale = ctx.properties.lowCriticalScale;\n\nif (showLowCriticalScale) {\n element.show();\n var offset = calculateOffset(lowCriticalScale, minValue, maxValue);\n var childrenElement = element.children();\n element.width(offset);\n} else {\n element.hide();\n}\n\nif (showLowCriticalScale && value !== null) {\n if (value <= lowCriticalScale && value >= ctx.properties.minValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalScale = ctx.properties.lowCriticalScale;\n\nif (showLowCriticalScale) {\n element.show();\n var offset = calculateOffset(lowCriticalScale, minValue, maxValue);\n var childrenElement = element.children();\n element.width(offset);\n} else {\n element.hide();\n}\n\nif (showLowCriticalScale && value !== null) {\n if (value <= lowCriticalScale) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", "actions": null }, { "tag": "lowWarningScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowWarningScale = ctx.properties.lowWarningScale;\nif (showLowWarningScale) {\n element.show();\n var offset = calculateOffset(lowWarningScale, minValue, maxValue);\n element.width(offset);\n} else {\n element.hide();\n}\n\nif (showLowWarningScale && value !== null) {\n var lowCriticalScale = ctx.properties.lowCriticalScale;\n if (!showLowCriticalScale) {\n lowCriticalScale = ctx.properties.minValue;\n }\n if (value > lowCriticalScale && value <= lowWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowWarningScale = ctx.properties.lowWarningScale;\nif (showLowWarningScale) {\n element.show();\n var offset = calculateOffset(lowWarningScale, minValue, maxValue);\n element.width(offset);\n} else {\n element.hide();\n}\n\nif (showLowWarningScale && value !== null) {\n var lowCriticalScale = ctx.properties.lowCriticalScale;\n if (!showLowCriticalScale) {\n lowCriticalScale = Number.MIN_SAFE_INTEGER;\n }\n if (value > lowCriticalScale && value <= lowWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}", "actions": null }, { diff --git a/application/src/main/data/json/system/scada_symbols/simple-vertical-scale-hp.svg b/application/src/main/data/json/system/scada_symbols/simple-vertical-scale-hp.svg index 7f9244eaea..8cceb84a74 100644 --- a/application/src/main/data/json/system/scada_symbols/simple-vertical-scale-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/simple-vertical-scale-hp.svg @@ -18,12 +18,12 @@ }, { "tag": "highCriticalScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalScale = ctx.properties.highCriticalScale;\n\nif (showHighCriticalScale) {\n element.show();\n var offset = calculateOffset(highCriticalScale, minValue, maxValue);\n element.height(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighCriticalScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (value >= highCriticalScale && value <= ctx.properties.maxValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalScale = ctx.properties.highCriticalScale;\n\nif (showHighCriticalScale) {\n element.show();\n var offset = calculateOffset(highCriticalScale, minValue, maxValue);\n element.height(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighCriticalScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (value >= highCriticalScale) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", "actions": null }, { "tag": "highWarningScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highWarningValue = ctx.properties.highWarningScale;\nif (showHighWarningScale) {\n element.show();\n var offset = calculateOffset(highWarningValue, minValue, maxValue);\n element.height(653-offset);\n} else {\n element.hide();\n}\nif (showHighWarningScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (!showHighCriticalScale) {\n highCriticalScale = ctx.properties.maxValue;\n }\n var highWarningScale = ctx.properties.highWarningScale;\n if (value < highCriticalScale && value >= highWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highWarningValue = ctx.properties.highWarningScale;\nif (showHighWarningScale) {\n element.show();\n var offset = calculateOffset(highWarningValue, minValue, maxValue);\n element.height(653-offset);\n} else {\n element.hide();\n}\nif (showHighWarningScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (!showHighCriticalScale) {\n highCriticalScale = Number.MAX_SAFE_INTEGER;\n }\n var highWarningScale = ctx.properties.highWarningScale;\n if (value < highCriticalScale && value >= highWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}", "actions": null }, { @@ -33,12 +33,12 @@ }, { "tag": "lowCriticalScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalScale = ctx.properties.lowCriticalScale;\nif (showLowCriticalScale) {\n element.show();\n var offset = calculateOffset(lowCriticalScale, minValue, maxValue);\n element.height(offset);\n} else {\n element.hide();\n}\n\nif (showLowCriticalScale && value !== null) {\n if (value <= lowCriticalScale && value >= ctx.properties.minValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalScale = ctx.properties.lowCriticalScale;\nif (showLowCriticalScale) {\n element.show();\n var offset = calculateOffset(lowCriticalScale, minValue, maxValue);\n element.height(offset);\n} else {\n element.hide();\n}\n\nif (showLowCriticalScale && value !== null) {\n if (value <= lowCriticalScale) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", "actions": null }, { "tag": "lowWarningScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowWarningScale = ctx.properties.lowWarningScale;\nif (showLowWarningScale) {\n element.show();\n var offset = calculateOffset(lowWarningScale, minValue, maxValue);\n element.height(offset);\n} else {\n element.hide();\n}\n\nif (showLowWarningScale && value !== null) {\n var lowCriticalScale = ctx.properties.lowCriticalScale;\n if (!showLowCriticalScale) {\n lowCriticalScale = ctx.properties.minValue;\n }\n if (value > lowCriticalScale && value <= lowWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowWarningScale = ctx.properties.lowWarningScale;\nif (showLowWarningScale) {\n element.show();\n var offset = calculateOffset(lowWarningScale, minValue, maxValue);\n element.height(offset);\n} else {\n element.hide();\n}\nif (showLowWarningScale && value !== null) {\n var lowCriticalScale = ctx.properties.lowCriticalScale;\n if (!showLowCriticalScale) {\n lowCriticalScale = Number.MIN_SAFE_INTEGER;\n }\n if (value > lowCriticalScale && value <= lowWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}", "actions": null }, { From a2ae5b06c01ad9638c01690c3d3e0fc1c38db48d Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Thu, 5 Dec 2024 12:44:50 +0200 Subject: [PATCH 08/75] Non-null check for updateResourcesUsage --- .../server/common/data/widget/WidgetType.java | 3 ++- .../server/dao/resource/BaseResourceService.java | 9 +++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetType.java b/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetType.java index d025aab3d5..fed271cbbe 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetType.java @@ -50,7 +50,8 @@ public WidgetType(WidgetType widgetType) { @JsonIgnore public JsonNode getDefaultConfig() { - return Optional.ofNullable(descriptor.get("defaultConfig")) + return Optional.ofNullable(descriptor) + .map(descriptor -> descriptor.get("defaultConfig")) .filter(JsonNode::isTextual).map(JsonNode::asText) .map(json -> { try { diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java index 361d2b3894..9395aae6c7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java @@ -403,6 +403,9 @@ public long sumDataSizeByTenantId(TenantId tenantId) { @Override public boolean updateResourcesUsage(TenantId tenantId, Dashboard dashboard) { + if (dashboard.getConfiguration() == null) { + return false; + } Map links = getResourcesLinks(dashboard.getResources()); return updateResourcesUsage(tenantId, List.of(dashboard.getConfiguration()), List.of(DASHBOARD_RESOURCES_MAPPING), links); } @@ -413,8 +416,10 @@ public boolean updateResourcesUsage(TenantId tenantId, WidgetTypeDetails widgetT List jsonNodes = new ArrayList<>(2); List> mappings = new ArrayList<>(2); - jsonNodes.add(widgetTypeDetails.getDescriptor()); - mappings.add(WIDGET_RESOURCES_MAPPING); + if (widgetTypeDetails.getDescriptor() != null) { + jsonNodes.add(widgetTypeDetails.getDescriptor()); + mappings.add(WIDGET_RESOURCES_MAPPING); + } JsonNode defaultConfig = widgetTypeDetails.getDefaultConfig(); if (defaultConfig != null) { From 0af23059320768ada2d26605a947087f4a0bf822 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Thu, 5 Dec 2024 21:40:36 +0100 Subject: [PATCH 09/75] removed FROM_VERSION check from upgrade scripts --- docker/docker-upgrade-tb.sh | 8 +------- msa/tb-node/docker/start-tb-node.sh | 8 ++------ msa/tb/docker/upgrade-tb.sh | 7 +------ packaging/java/scripts/install/upgrade.sh | 8 +------- packaging/java/scripts/install/upgrade_dev_db.sh | 8 +------- packaging/java/scripts/windows/upgrade.bat | 6 ------ 6 files changed, 6 insertions(+), 39 deletions(-) diff --git a/docker/docker-upgrade-tb.sh b/docker/docker-upgrade-tb.sh index 41f50c7019..07aa7ef05f 100755 --- a/docker/docker-upgrade-tb.sh +++ b/docker/docker-upgrade-tb.sh @@ -28,13 +28,7 @@ case $i in esac done -if [[ -z "${FROM_VERSION// }" ]]; then - echo "--fromVersion parameter is invalid or unspecified!" - echo "Usage: docker-upgrade-tb.sh --fromVersion={VERSION}" - exit 1 -else - fromVersion="${FROM_VERSION// }" -fi +fromVersion="${FROM_VERSION// }" set -e diff --git a/msa/tb-node/docker/start-tb-node.sh b/msa/tb-node/docker/start-tb-node.sh index c164954a82..b90553e772 100755 --- a/msa/tb-node/docker/start-tb-node.sh +++ b/msa/tb-node/docker/start-tb-node.sh @@ -47,12 +47,8 @@ elif [ "$UPGRADE_TB" == "true" ]; then echo "Starting ThingsBoard upgrade ..." - if [[ -z "${FROM_VERSION// }" ]]; then - echo "FROM_VERSION variable is invalid or unspecified!" - exit 1 - else - fromVersion="${FROM_VERSION// }" - fi + + fromVersion="${FROM_VERSION// }" exec java -cp ${jarfile} $JAVA_OPTS -Dloader.main=org.thingsboard.server.ThingsboardInstallApplication \ -Dspring.jpa.hibernate.ddl-auto=none \ diff --git a/msa/tb/docker/upgrade-tb.sh b/msa/tb/docker/upgrade-tb.sh index 545bf4b8d6..3e8f7d804a 100644 --- a/msa/tb/docker/upgrade-tb.sh +++ b/msa/tb/docker/upgrade-tb.sh @@ -28,12 +28,7 @@ FROM_VERSION=`cat ${upgradeversion}` echo "Starting ThingsBoard upgrade ..." -if [[ -z "${FROM_VERSION// }" ]]; then - echo "FROM_VERSION variable is invalid or unspecified!" - exit 1 -else - fromVersion="${FROM_VERSION// }" -fi +fromVersion="${FROM_VERSION// }" java -cp ${jarfile} $JAVA_OPTS -Dloader.main=org.thingsboard.server.ThingsboardInstallApplication \ -Dspring.jpa.hibernate.ddl-auto=none \ diff --git a/packaging/java/scripts/install/upgrade.sh b/packaging/java/scripts/install/upgrade.sh index 2b87b26aa6..07164ef2c8 100755 --- a/packaging/java/scripts/install/upgrade.sh +++ b/packaging/java/scripts/install/upgrade.sh @@ -28,13 +28,7 @@ case $i in esac done -if [[ -z "${FROM_VERSION// }" ]]; then - echo "--fromVersion parameter is invalid or unspecified!" - echo "Usage: upgrade.sh --fromVersion={VERSION}" - exit 1 -else - fromVersion="${FROM_VERSION// }" -fi +fromVersion="${FROM_VERSION// }" CONF_FOLDER=${pkg.installFolder}/conf configfile=${pkg.name}.conf diff --git a/packaging/java/scripts/install/upgrade_dev_db.sh b/packaging/java/scripts/install/upgrade_dev_db.sh index d0c42eaaa6..010f6d3655 100755 --- a/packaging/java/scripts/install/upgrade_dev_db.sh +++ b/packaging/java/scripts/install/upgrade_dev_db.sh @@ -28,13 +28,7 @@ case $i in esac done -if [[ -z "${FROM_VERSION// }" ]]; then - echo "--fromVersion parameter is invalid or unspecified!" - echo "Usage: upgrade_dev_db.sh --fromVersion={VERSION}" - exit 1 -else - fromVersion="${FROM_VERSION// }" -fi +fromVersion="${FROM_VERSION// }" BASE=${project.basedir}/target CONF_FOLDER=${BASE}/conf diff --git a/packaging/java/scripts/windows/upgrade.bat b/packaging/java/scripts/windows/upgrade.bat index b86121b8f1..115eea5244 100644 --- a/packaging/java/scripts/windows/upgrade.bat +++ b/packaging/java/scripts/windows/upgrade.bat @@ -15,12 +15,6 @@ IF NOT "%1"=="" ( GOTO :loop ) -if not defined fromVersion ( - echo "--fromVersion parameter is invalid or unspecified!" - echo "Usage: upgrade.bat --fromVersion {VERSION}" - exit /b 1 -) - SET LOADER_PATH=%BASE%\conf,%BASE%\extensions SET SQL_DATA_FOLDER=%BASE%\data\sql SET jarfile=%BASE%\lib\${pkg.name}.jar From 48c8160e1f01cab7de5c0e6a7315bf85805f6ec5 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Fri, 6 Dec 2024 09:54:03 +0200 Subject: [PATCH 10/75] UI: Add no text for resources autocomplete --- .../resource/resource-autocomplete.component.html | 13 +++++++++++-- .../resource/resource-autocomplete.component.ts | 8 +++++++- ui-ngx/src/assets/locale/locale.constant-en_US.json | 4 +++- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.html b/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.html index 7d0c9ffb9f..bcc566aeea 100644 --- a/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.html +++ b/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.html @@ -41,8 +41,17 @@ - - {{ searchText }} + +
+
+ {{ 'js-func.no-js-module-text' | translate }} +
+ + + {{ translate.get('js-func.no-js-module-matching', {module: searchText}) | async }} + + +
diff --git a/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.ts b/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.ts index 0cf13f2052..b87cfe8c4f 100644 --- a/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.ts +++ b/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.ts @@ -33,6 +33,7 @@ import { TbResourceId } from '@shared/models/id/tb-resource-id'; import { ResourceService } from '@core/http/resource.service'; import { PageLink } from '@shared/models/page/page-link'; import { MatFormFieldAppearance, SubscriptSizing } from '@angular/material/form-field'; +import { TranslateService } from '@ngx-translate/core'; @Component({ selector: 'tb-resource-autocomplete', @@ -95,7 +96,8 @@ export class ResourceAutocompleteComponent implements ControlValueAccessor, OnIn private propagateChange: (value: any) => void = () => {}; constructor(private fb: FormBuilder, - private resourceService: ResourceService) { + private resourceService: ResourceService, + public translate: TranslateService) { } ngOnInit(): void { @@ -210,6 +212,10 @@ export class ResourceAutocompleteComponent implements ControlValueAccessor, OnIn } } + textIsNotEmpty(text: string): boolean { + return (text && text.length > 0); + } + private fetchResources(searchText?: string): Observable> { this.searchText = searchText; return this.resourceService.getResources(new PageLink(50, 0, searchText), ResourceType.JS_MODULE, this.subType, {ignoreLoading: true}).pipe( diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index c2f45da84f..6b50deb6ab 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -3332,7 +3332,9 @@ "module-no-members": "Module has no exported members", "module-load-error": "Module load error", "source-code": "Source code", - "source-code-load-error": "Source code load error" + "source-code-load-error": "Source code load error", + "no-js-module-text": "No JS module found", + "no-js-module-matching": "No JS module matching '{{module}}' were found." }, "key-val": { "key": "Key", From 97f3288c893e6395a21b0a89033ccf6c99d2553d Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Fri, 6 Dec 2024 11:25:06 +0200 Subject: [PATCH 11/75] UI: Fixed hide menu export dashboard after select option --- .../home/components/dashboard-page/dashboard-page.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts index 2e22975f7e..4a213ce263 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts @@ -874,7 +874,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC public exportDashboard($event: Event) { if ($event) { - $event.stopPropagation(); + $event.preventDefault(); } this.importExport.exportDashboard(this.currentDashboardId); } From 76c39b6d0b2824aa66bf2bc10df8a066a5333e25 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Fri, 6 Dec 2024 12:14:06 +0200 Subject: [PATCH 12/75] UI: Fixed error toast when editing device detais --- .../ota-package/ota-package-autocomplete.component.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts b/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts index 04511fad38..d3570150e8 100644 --- a/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts +++ b/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts @@ -31,10 +31,11 @@ import { OtaPackageService } from '@core/http/ota-package.service'; import { PageLink } from '@shared/models/page/page-link'; import { Direction } from '@shared/models/page/sort-order'; import { emptyPageData } from '@shared/models/page/page-data'; -import { getEntityDetailsPageURL } from '@core/utils'; +import { getEntityDetailsPageURL, isDefinedAndNotNull } from '@core/utils'; import { AuthUser } from '@shared/models/user.model'; import { getCurrentAuthUser } from '@core/auth/auth.selectors'; import { Authority } from '@shared/models/authority.enum'; +import { NULL_UUID } from '@shared/models/id/has-uuid'; @Component({ selector: 'tb-ota-package-autocomplete', @@ -76,7 +77,7 @@ export class OtaPackageAutocompleteComponent implements ControlValueAccessor, On if (this.deviceProfile) { this.reset(); } - this.deviceProfile = value; + this.deviceProfile = value ? value :NULL_UUID; } } From 3a97faeb17447d2ac149ba607e8dd41b7babb119 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Fri, 6 Dec 2024 12:21:29 +0200 Subject: [PATCH 13/75] UI: Refactoring --- .../ota-package/ota-package-autocomplete.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts b/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts index d3570150e8..4a117d74b9 100644 --- a/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts +++ b/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts @@ -31,7 +31,7 @@ import { OtaPackageService } from '@core/http/ota-package.service'; import { PageLink } from '@shared/models/page/page-link'; import { Direction } from '@shared/models/page/sort-order'; import { emptyPageData } from '@shared/models/page/page-data'; -import { getEntityDetailsPageURL, isDefinedAndNotNull } from '@core/utils'; +import { getEntityDetailsPageURL } from '@core/utils'; import { AuthUser } from '@shared/models/user.model'; import { getCurrentAuthUser } from '@core/auth/auth.selectors'; import { Authority } from '@shared/models/authority.enum'; @@ -77,7 +77,7 @@ export class OtaPackageAutocompleteComponent implements ControlValueAccessor, On if (this.deviceProfile) { this.reset(); } - this.deviceProfile = value ? value :NULL_UUID; + this.deviceProfile = value ? value : NULL_UUID; } } From d56fc4c440edc518979919a74f9b1caeb1bb0e5c Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 6 Dec 2024 12:35:14 +0200 Subject: [PATCH 14/75] UI: Fixed mobile center translate, validation and editor --- .../mobile/applications/mobile-app.component.ts | 8 ++++---- .../mobile/bundes/layout/mobile-layout.component.ts | 2 +- .../bundes/layout/mobile-page-item-row.component.ts | 4 ++-- .../pages/mobile/common/editor-panel.component.ts | 12 ++++++++++-- ui-ngx/src/assets/locale/locale.constant-en_US.json | 4 ++-- 5 files changed, 19 insertions(+), 11 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app.component.ts b/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app.component.ts index 803748c7d5..0d7a9c081c 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app.component.ts +++ b/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app.component.ts @@ -76,9 +76,9 @@ export class MobileAppComponent extends EntityComponent { }), storeInfo: this.fb.group({ storeLink: [entity?.storeInfo?.storeLink ? entity.storeInfo.storeLink : '', - Validators.pattern(/^https?:\/\/play\.google\.com\/store\/apps\/details\?id=[a-zA-Z0-9._]+$/)], + Validators.pattern(/^https?:\/\/play\.google\.com\/store\/apps\/details\?id=[a-zA-Z0-9._]+(?:&[a-zA-Z0-9._-]+=[a-zA-Z0-9._%-]*)*$/)], sha256CertFingerprints: [entity?.storeInfo?.sha256CertFingerprints ? entity.storeInfo.sha256CertFingerprints : '', - Validators.pattern(/^[A-Fa-f0-9]{2}(:[A-Fa-f0-9]{2}){1,31}$/)], + Validators.pattern(/^[A-Fa-f0-9]{2}(:[A-Fa-f0-9]{2}){31}$/)], appId: [entity?.storeInfo?.appId ? entity.storeInfo.appId : '', Validators.pattern(/^[A-Z0-9]{10}\.[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)*$/)], }), }); @@ -89,11 +89,11 @@ export class MobileAppComponent extends EntityComponent { if (value === PlatformType.ANDROID) { form.get('storeInfo.sha256CertFingerprints').enable({emitEvent: false}); form.get('storeInfo.appId').disable({emitEvent: false}); - form.get('storeInfo.storeLink').setValidators(Validators.pattern(/^https?:\/\/play\.google\.com\/store\/apps\/details\?id=[a-zA-Z0-9._]+$/)); + form.get('storeInfo.storeLink').setValidators(Validators.pattern(/^https?:\/\/play\.google\.com\/store\/apps\/details\?id=[a-zA-Z0-9._]+(?:&[a-zA-Z0-9._-]+=[a-zA-Z0-9._%-]*)*$/)); } else if (value === PlatformType.IOS) { form.get('storeInfo.sha256CertFingerprints').disable({emitEvent: false}); form.get('storeInfo.appId').enable({emitEvent: false}); - form.get('storeInfo.storeLink').setValidators(Validators.pattern(/^https?:\/\/apps\.apple\.com\/[a-z]{2}\/app\/[\w-]+\/id\d{7,10}$/)); + form.get('storeInfo.storeLink').setValidators(Validators.pattern(/^https?:\/\/apps\.apple\.com\/[a-z]{2}\/app\/[\w-]+\/id\d{7,10}(?:\?[^\s]*)?$/)); } form.get('storeInfo.storeLink').setValue('', {emitEvent: false}); }); diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-layout.component.ts b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-layout.component.ts index 2b561a4fce..c7dd980378 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-layout.component.ts +++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-layout.component.ts @@ -207,7 +207,7 @@ export class MobileLayoutComponent implements ControlValueAccessor, Validator { private updateModel() { if (isDefaultMobilePagesConfig(this.pagesForm.value.pages as MobilePage[])) { - this.propagateChange({pages: []}); + this.propagateChange(null); } else { this.propagateChange(this.pagesForm.value); } diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.ts b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.ts index eb9f7a2d63..9a60c87959 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.ts +++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.ts @@ -187,7 +187,7 @@ export class MobilePageItemRowComponent implements ControlValueAccessor, OnInit, } } else { this.isCustomMenuItem = true; - this.mobilePageRowForm.get('label').setValidators([Validators.required]); + this.mobilePageRowForm.get('label').addValidators([Validators.required]); this.mobilePageRowForm.get('label').updateValueAndValidity({emitEvent: false}); } this.updateCleanupState(); @@ -270,7 +270,7 @@ export class MobilePageItemRowComponent implements ControlValueAccessor, OnInit, private updateModel() { this.modelValue.visible = this.mobilePageRowForm.get('visible').value; - const label = this.mobilePageRowForm.get('label').value; + const label = this.mobilePageRowForm.get('label').value.trim(); if (label) { this.modelValue.label = label; } else { diff --git a/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.ts b/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.ts index 9b9b282e9d..15dff508c8 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.ts +++ b/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.ts @@ -17,6 +17,7 @@ import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; import { FormBuilder, FormControl } from '@angular/forms'; import { TbPopoverComponent } from '@shared/components/popover.component'; +import { EditorOptions } from 'tinymce'; @Component({ selector: 'tb-release-notes-panel', @@ -43,7 +44,7 @@ export class EditorPanelComponent implements OnInit { editorControl: FormControl; - tinyMceOptions: Record = { + tinyMceOptions: Partial = { base_url: '/assets/tinymce', suffix: '.min', plugins: ['lists'], @@ -55,7 +56,14 @@ export class EditorPanelComponent implements OnInit { autofocus: false, branding: false, promotion: false, - resize: false + resize: false, + setup: (editor) => { + editor.on('PostRender', function() { + const container = editor.getContainer(); + const uiContainer = document.querySelector('.tox.tox-tinymce-aux'); + container.parentNode.appendChild(uiContainer); + }); + } }; constructor(private fb: FormBuilder) { diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index c2f45da84f..491e7136a6 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -3550,7 +3550,7 @@ "tablet-959": "Tablet (max 959px)", "max-element-number": "Max elements number", "page-name": "Page name", - "page-nam-required": "Page name is required.", + "page-name-required": "Page name is required.", "page-type": "Page type", "pages-types": { "dashboard": "Dashboard", @@ -3560,7 +3560,7 @@ "url": "URL", "url-pattern": "Invalid URL", "path": "Path", - "path-pattern": "Path pattern", + "path-pattern": "Invalid path", "custom-page": "Custom page", "edit-page": "Edit page", "edit-custom-page": "Edit custom page", From 617381ffc76b12304eb7a4e818f3abedf08c16d0 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 6 Dec 2024 13:05:53 +0200 Subject: [PATCH 15/75] UI: Fix Json Form conditions. --- .../components/json-form/react/json-form-schema-form.tsx | 6 +++--- .../shared/components/json-form/react/json-form.models.ts | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form-schema-form.tsx b/ui-ngx/src/app/shared/components/json-form/react/json-form-schema-form.tsx index 5ce35ae96f..bbdd90cbe7 100644 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form-schema-form.tsx +++ b/ui-ngx/src/app/shared/components/json-form/react/json-form-schema-form.tsx @@ -131,10 +131,10 @@ class ThingsboardSchemaForm extends React.Component { } if (form.condition) { this.hasConditions = true; - if (!this.conditionFunction) { - this.conditionFunction = new Function('form', 'model', 'index', `return ${form.condition};`); + if (!form.conditionFunction) { + form.conditionFunction = new Function('form', 'model', 'index', `return ${form.condition};`); } - if (this.conditionFunction(form, model, index) === false) { + if (form.conditionFunction(form, model, index) === false) { return null; } } diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form.models.ts b/ui-ngx/src/app/shared/components/json-form/react/json-form.models.ts index edba015521..ffcf201136 100644 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form.models.ts +++ b/ui-ngx/src/app/shared/components/json-form/react/json-form.models.ts @@ -87,6 +87,7 @@ export interface JsonFormData { required: boolean; default?: any; condition?: string; + conditionFunction?: Function; style?: any; rows?: number; rowsMax?: number; From e563649e98e2c4891ef404f7c1f1e3464b253f62 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 6 Dec 2024 13:09:59 +0200 Subject: [PATCH 16/75] UI: Fixed load background in image map --- .../home/components/widget/lib/maps/providers/image-map.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts index 0abcc9f7c8..d91a84d3b2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts @@ -23,14 +23,14 @@ import { PosFunction, WidgetUnitedMapSettings } from '../map-models'; -import { forkJoin, Observable, of, ReplaySubject, switchMap } from 'rxjs'; +import { combineLatest, Observable, of, ReplaySubject, switchMap } from 'rxjs'; import { catchError } from 'rxjs/operators'; import { calculateNewPointCoordinate, loadImageWithAspect } from '@home/components/widget/lib/maps/common-maps-utils'; import { WidgetContext } from '@home/models/widget-component.models'; import { DataSet, DatasourceType, FormattedData, widgetType } from '@shared/models/widget.models'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { WidgetSubscriptionOptions } from '@core/api/widget-api.models'; -import { isDefinedAndNotNull, isEmptyStr, isNotEmptyStr, parseFunction, parseTbFunction } from '@core/utils'; +import { isDefinedAndNotNull, isEmptyStr, isNotEmptyStr, parseTbFunction } from '@core/utils'; import { EntityDataPageLink } from '@shared/models/query/query.models'; import { ImagePipe } from '@shared/pipe/image.pipe'; import { CompiledTbFunction } from '@shared/models/js-function.models'; @@ -55,7 +55,7 @@ export class ImageMap extends LeafletMap { mapImage: this.mapImage(options) }; - forkJoin(initData).subscribe(inited => { + combineLatest(initData).subscribe(inited => { this.posFunction = inited.posFunction; const mapImage = inited.mapImage; this.imageUrl = mapImage.imageUrl; From 2a1910792939a6224735f1e45e6c359bc2e57579 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Fri, 6 Dec 2024 14:43:29 +0200 Subject: [PATCH 17/75] fixed primary key name --- dao/src/main/resources/sql/schema-entities.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index f43ea4a5f6..916a5487f8 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -896,7 +896,7 @@ CREATE TABLE IF NOT EXISTS queue_stats ( ); CREATE TABLE IF NOT EXISTS qr_code_settings ( - id uuid NOT NULL CONSTRAINT mobile_app_settings_pkey PRIMARY KEY, + id uuid NOT NULL CONSTRAINT qr_code_settings_pkey PRIMARY KEY, created_time bigint NOT NULL, tenant_id uuid NOT NULL, use_default_app boolean, From 534e2349fbc264e941476bfc41ac87c4e8be2117 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Fri, 6 Dec 2024 14:49:15 +0200 Subject: [PATCH 18/75] updated upgrade script to rename old primary key name --- application/src/main/data/upgrade/basic/schema_update.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/application/src/main/data/upgrade/basic/schema_update.sql b/application/src/main/data/upgrade/basic/schema_update.sql index 9390238139..ced110e65a 100644 --- a/application/src/main/data/upgrade/basic/schema_update.sql +++ b/application/src/main/data/upgrade/basic/schema_update.sql @@ -189,6 +189,7 @@ $$ END IF; END LOOP; ALTER TABLE qr_code_settings RENAME CONSTRAINT mobile_app_settings_tenant_id_unq_key TO qr_code_settings_tenant_id_unq_key; + ALTER TABLE qr_code_settings RENAME CONSTRAINT mobile_app_settings_pkey TO qr_code_settings_pkey; END IF; ALTER TABLE qr_code_settings DROP COLUMN IF EXISTS android_config, DROP COLUMN IF EXISTS ios_config; END; From 8e20d740338f27825a2c2a92a03b93f363b1f5a5 Mon Sep 17 00:00:00 2001 From: kalytka Date: Fri, 6 Dec 2024 16:54:18 +0200 Subject: [PATCH 19/75] Share echarts-widget.models --- ui-ngx/src/app/modules/home/components/public-api.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/ui-ngx/src/app/modules/home/components/public-api.ts b/ui-ngx/src/app/modules/home/components/public-api.ts index 461c106b51..6fa57fb4c4 100644 --- a/ui-ngx/src/app/modules/home/components/public-api.ts +++ b/ui-ngx/src/app/modules/home/components/public-api.ts @@ -19,6 +19,7 @@ export * from './widget/config/basic/basic-widget-config.module'; export * from './widget/lib/settings/common/widget-settings-common.module'; export * from './widget/widget-components.module'; export * from './widget/config/widget-config-components.module'; +export * from './widget/lib/chart/echarts-widget.models'; export * from './widget/config/widget-config.component.models'; export * from './widget/lib/table-widget.models'; From 09bc214bd3faa487e11e9952fde7b21a6c488a4d Mon Sep 17 00:00:00 2001 From: mpetrov Date: Fri, 6 Dec 2024 17:07:15 +0200 Subject: [PATCH 20/75] UI: Debug Settings fixes --- .../app/core/services/item-buffer.service.ts | 2 +- .../debug-settings-button.component.html | 11 +++--- .../debug-settings-button.component.ts | 39 ++++++++++++------- .../debug-settings-panel.component.html | 5 ++- .../debug-settings-panel.component.ts | 7 ++-- .../rulechain/rulechain-page.component.html | 6 +-- .../rulechain/rulechain-page.component.ts | 18 ++++++--- .../pipe/milliseconds-to-time-string.pipe.ts | 15 ++++--- .../assets/locale/locale.constant-ar_AE.json | 1 - .../assets/locale/locale.constant-ca_ES.json | 3 +- .../assets/locale/locale.constant-cs_CZ.json | 3 +- .../assets/locale/locale.constant-da_DK.json | 3 +- .../assets/locale/locale.constant-el_GR.json | 3 +- .../assets/locale/locale.constant-en_US.json | 7 ++-- .../assets/locale/locale.constant-es_ES.json | 1 - .../assets/locale/locale.constant-ka_GE.json | 3 +- .../assets/locale/locale.constant-ko_KR.json | 3 +- .../assets/locale/locale.constant-lt_LT.json | 2 +- .../assets/locale/locale.constant-lv_LV.json | 3 +- .../assets/locale/locale.constant-nl_BE.json | 3 +- .../assets/locale/locale.constant-pl_PL.json | 1 - .../assets/locale/locale.constant-pt_BR.json | 3 +- .../assets/locale/locale.constant-ro_RO.json | 1 - .../assets/locale/locale.constant-sl_SI.json | 3 +- .../assets/locale/locale.constant-tr_TR.json | 3 +- .../assets/locale/locale.constant-uk_UA.json | 3 +- .../assets/locale/locale.constant-zh_CN.json | 1 - .../assets/locale/locale.constant-zh_TW.json | 3 +- 28 files changed, 79 insertions(+), 77 deletions(-) diff --git a/ui-ngx/src/app/core/services/item-buffer.service.ts b/ui-ngx/src/app/core/services/item-buffer.service.ts index c77784c4f5..ff4211961a 100644 --- a/ui-ngx/src/app/core/services/item-buffer.service.ts +++ b/ui-ngx/src/app/core/services/item-buffer.service.ts @@ -305,7 +305,7 @@ export class ItemBufferService { connectors: [], additionalInfo: origNode.additionalInfo, configuration: origNode.configuration, - debugMode: origNode.debugMode, + debugSettings: origNode.debugSettings, x: origNode.x, y: origNode.y, name: origNode.name, diff --git a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.html b/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.html index b779ae1e4c..d20fa2e90d 100644 --- a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.html +++ b/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.html @@ -23,10 +23,9 @@ [disabled]="disabled" (click)="openDebugStrategyPanel($event, matButton)"> bug_report - common.disabled - debug-config.all - - {{ !allEnabled ? (allEnabledUntil | durationLeft) : ('debug-config.min' | translate: { number: maxDebugModeDurationMinutes }) }} - - debug-config.failures + @if (isDebugAllActive$ | async) { + {{ !allEnabled() ? (allEnabledUntil | durationLeft) : (maxDebugModeDuration | milliSecondsToTimeString: true : true).trim() }} + } @else { + {{ failuresEnabled ? ('debug-config.failures' | translate) : ('common.disabled' | translate) }} + } diff --git a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.ts b/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.ts index dafda75c0b..4db01892a2 100644 --- a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.ts +++ b/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.ts @@ -14,18 +14,26 @@ /// limitations under the License. /// -import { ChangeDetectionStrategy, Component, forwardRef, Input, Renderer2, ViewContainerRef } from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + forwardRef, + Input, + Renderer2, + signal, + ViewContainerRef +} from '@angular/core'; import { CommonModule } from '@angular/common'; import { SharedModule } from '@shared/shared.module'; import { DurationLeftPipe } from '@shared/pipe/duration-left.pipe'; import { TbPopoverService } from '@shared/components/popover.service'; import { MatButton } from '@angular/material/button'; import { DebugSettingsPanelComponent } from './debug-settings-panel.component'; -import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop'; import { of, shareReplay, timer } from 'rxjs'; -import { SECOND } from '@shared/models/time/time.models'; +import { MINUTE, SECOND } from '@shared/models/time/time.models'; import { DebugSettings } from '@shared/models/entity.models'; -import { map, startWith, switchMap, takeWhile } from 'rxjs/operators'; +import { map, switchMap, takeWhile } from 'rxjs/operators'; import { getCurrentAuthState } from '@core/auth/auth.selectors'; import { AppState } from '@core/core.state'; import { Store } from '@ngrx/store'; @@ -60,11 +68,11 @@ export class DebugSettingsButtonComponent implements ControlValueAccessor { }); disabled = false; + allEnabled = signal(false); - isDebugAllActive$ = this.debugSettingsFormGroup.get('allEnabled').valueChanges.pipe( - startWith(null), - switchMap(() => { - if (this.allEnabled) { + isDebugAllActive$ = toObservable(this.allEnabled).pipe( + switchMap((value) => { + if (value) { return of(true); } else { return timer(0, SECOND).pipe( @@ -77,7 +85,7 @@ export class DebugSettingsButtonComponent implements ControlValueAccessor { shareReplay(1) ); - readonly maxDebugModeDurationMinutes = getCurrentAuthState(this.store).maxDebugModeDurationMinutes; + readonly maxDebugModeDuration = getCurrentAuthState(this.store).maxDebugModeDurationMinutes * MINUTE; private propagateChange: (settings: DebugSettings) => void = () => {}; @@ -91,17 +99,17 @@ export class DebugSettingsButtonComponent implements ControlValueAccessor { takeUntilDestroyed() ).subscribe(value => { this.propagateChange(value); - }) + }); + + this.debugSettingsFormGroup.get('allEnabled').valueChanges.pipe( + takeUntilDestroyed() + ).subscribe(value => this.allEnabled.set(value)); } get failuresEnabled(): boolean { return this.debugSettingsFormGroup.get('failuresEnabled').value; } - get allEnabled(): boolean { - return this.debugSettingsFormGroup.get('allEnabled').value; - } - get allEnabledUntil(): number { return this.debugSettingsFormGroup.get('allEnabledUntil').value; } @@ -120,7 +128,7 @@ export class DebugSettingsButtonComponent implements ControlValueAccessor { this.viewContainerRef, DebugSettingsPanelComponent, 'bottom', true, null, { ...debugSettings, - maxDebugModeDurationMinutes: this.maxDebugModeDurationMinutes, + maxDebugModeDuration: this.maxDebugModeDuration, debugLimitsConfiguration: this.debugLimitsConfiguration }, {}, @@ -141,6 +149,7 @@ export class DebugSettingsButtonComponent implements ControlValueAccessor { writeValue(settings: DebugSettings): void { this.debugSettingsFormGroup.patchValue(settings, {emitEvent: false}); + this.allEnabled.set(settings?.allEnabled); this.debugSettingsFormGroup.get('allEnabled').updateValueAndValidity({onlySelf: true}); } diff --git a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.html b/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.html index 5fe0aa2546..f088fc38ac 100644 --- a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.html @@ -20,7 +20,7 @@
@if (debugLimitsConfiguration) { - {{ 'debug-config.hint.main-limited' | translate: { msg: maxMessagesCount, sec: maxTimeFrameSec } }} + {{ 'debug-config.hint.main-limited' | translate: { msg: maxMessagesCount, time: (maxTimeFrameDuration | milliSecondsToTimeString: true : true).trim() } }} } @else { {{ 'debug-config.hint.main' | translate }} } @@ -35,7 +35,7 @@
- {{ 'debug-config.all-messages' | translate: { time: (isDebugAllActive$ | async) && !allEnabled ? (allEnabledUntil | durationLeft) : ('debug-config.min' | translate: { number: maxDebugModeDurationMinutes }) } }} + {{ 'debug-config.all-messages' | translate: { time: (isDebugAllActive$ | async) && !allEnabled ? (allEnabledUntil | durationLeft) : (maxDebugModeDuration | milliSecondsToTimeString: true : true).trim() } }}
diff --git a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.ts b/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.ts index a9e237245c..2c98dee6c4 100644 --- a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.ts @@ -52,14 +52,14 @@ export class DebugSettingsPanelComponent extends PageComponent implements OnInit @Input({ transform: booleanAttribute }) failuresEnabled = false; @Input({ transform: booleanAttribute }) allEnabled = false; @Input() allEnabledUntil = 0; - @Input() maxDebugModeDurationMinutes: number; + @Input() maxDebugModeDuration: number; @Input() debugLimitsConfiguration: string; onFailuresControl = this.fb.control(false); debugAllControl = this.fb.control(false); maxMessagesCount: string; - maxTimeFrameSec: string; + maxTimeFrameDuration: number; initialAllEnabled: boolean; isDebugAllActive$ = this.debugAllControl.valueChanges.pipe( @@ -99,7 +99,7 @@ export class DebugSettingsPanelComponent extends PageComponent implements OnInit ngOnInit(): void { this.maxMessagesCount = this.debugLimitsConfiguration?.split(':')[0]; - this.maxTimeFrameSec = this.debugLimitsConfiguration?.split(':')[1]; + this.maxTimeFrameDuration = parseInt(this.debugLimitsConfiguration?.split(':')[1]) * SECOND; this.onFailuresControl.patchValue(this.failuresEnabled); this.debugAllControl.patchValue(this.allEnabled); this.initialAllEnabled = this.allEnabled || this.allEnabledUntil > new Date().getTime(); @@ -128,6 +128,7 @@ export class DebugSettingsPanelComponent extends PageComponent implements OnInit onReset(): void { this.debugAllControl.patchValue(true); + this.debugAllControl.markAsDirty(); this.allEnabledUntil = 0; this.cd.markForCheck(); } diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.html b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.html index 5e955dc4b4..7bf85aa215 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.html +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.html @@ -239,10 +239,10 @@ matTooltipPosition="above"> delete - diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.ts b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.ts index 35b10a27b3..2d440ed28c 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.ts +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.ts @@ -95,6 +95,7 @@ import { ComponentClusteringMode } from '@shared/models/component-descriptor.mod import { MatDrawer } from '@angular/material/sidenav'; import { HttpStatusCode } from '@angular/common/http'; import { TbContextMenuEvent } from '@shared/models/jquery-event.models'; +import { DebugSettings } from '@shared/models/entity.models'; import Timeout = NodeJS.Timeout; @Component({ @@ -1415,17 +1416,20 @@ export class RuleChainPageComponent extends PageComponent this.ruleChainCanvas.modelService.deleteSelected(); } - isDebugModeEnabled(): boolean { - const res = this.ruleChainModel.nodes.find((node) => node.debugMode); + isDebugSettingsEnabled(): boolean { + const res = this.ruleChainModel.nodes.find(({ debugSettings }) => debugSettings && this.isDebugSettingsActive(debugSettings)); return typeof res !== 'undefined'; } - resetDebugModeInAllNodes() { + resetDebugSettingsInAllNodes(): void { let changed = false; this.ruleChainModel.nodes.forEach((node) => { if (node.component.type !== RuleNodeType.INPUT) { - changed = changed || node.debugMode; - node.debugMode = false; + const nodeHasActiveDebugSettings = node.debugSettings && this.isDebugSettingsActive(node.debugSettings); + changed = changed || nodeHasActiveDebugSettings; + if (nodeHasActiveDebugSettings) { + node.debugSettings = { allEnabled: false, failuresEnabled: false, allEnabledUntil: 0 }; + } } }); if (changed) { @@ -1433,6 +1437,10 @@ export class RuleChainPageComponent extends PageComponent } } + private isDebugSettingsActive({ allEnabled = false, failuresEnabled = false, allEnabledUntil = 0 }: DebugSettings): boolean { + return allEnabled || failuresEnabled || allEnabledUntil > new Date().getTime(); + } + validate() { setTimeout(() => { this.isInvalid = false; diff --git a/ui-ngx/src/app/shared/pipe/milliseconds-to-time-string.pipe.ts b/ui-ngx/src/app/shared/pipe/milliseconds-to-time-string.pipe.ts index 853890ee09..d8884992bf 100644 --- a/ui-ngx/src/app/shared/pipe/milliseconds-to-time-string.pipe.ts +++ b/ui-ngx/src/app/shared/pipe/milliseconds-to-time-string.pipe.ts @@ -16,7 +16,7 @@ import { Pipe, PipeTransform } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; -import { DAY, HOUR, MINUTE, SECOND } from '@shared/models/time/time.models'; +import { DAY, HOUR, MINUTE, SECOND, YEAR } from '@shared/models/time/time.models'; @Pipe({ name: 'milliSecondsToTimeString' @@ -27,19 +27,21 @@ export class MillisecondsToTimeStringPipe implements PipeTransform { } transform(milliSeconds: number, shortFormat = false, onlyFirstDigit = false): string { - const { days, hours, minutes, seconds } = this.extractTimeUnits(milliSeconds); - return this.formatTimeString(days, hours, minutes, seconds, shortFormat, onlyFirstDigit); + const { years, days, hours, minutes, seconds } = this.extractTimeUnits(milliSeconds); + return this.formatTimeString(years, days, hours, minutes, seconds, shortFormat, onlyFirstDigit); } - private extractTimeUnits(milliseconds: number): { days: number; hours: number; minutes: number; seconds: number } { - const days = Math.floor(milliseconds / DAY); + private extractTimeUnits(milliseconds: number): { years: number; days: number; hours: number; minutes: number; seconds: number } { + const years = Math.floor(milliseconds / YEAR); + const days = Math.floor((milliseconds % YEAR) / DAY); const hours = Math.floor((milliseconds % DAY) / HOUR); const minutes = Math.floor((milliseconds % HOUR) / MINUTE); const seconds = Math.floor((milliseconds % MINUTE) / SECOND); - return { days, hours, minutes, seconds }; + return { years, days, hours, minutes, seconds }; } private formatTimeString( + years: number, days: number, hours: number, minutes: number, @@ -48,6 +50,7 @@ export class MillisecondsToTimeStringPipe implements PipeTransform { onlyFirstDigit: boolean ): string { const timeUnits = [ + { value: years, key: 'years', shortKey: 'short.years' }, { value: days, key: 'days', shortKey: 'short.days' }, { value: hours, key: 'hours', shortKey: 'short.hours' }, { value: minutes, key: 'minutes', shortKey: 'short.minutes' }, diff --git a/ui-ngx/src/assets/locale/locale.constant-ar_AE.json b/ui-ngx/src/assets/locale/locale.constant-ar_AE.json index e4c7ddc917..695e1ad441 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ar_AE.json +++ b/ui-ngx/src/assets/locale/locale.constant-ar_AE.json @@ -4602,7 +4602,6 @@ "output": "الإخراج", "test": "اختبار", "help": "مساعدة", - "reset-debug-mode": "إعادة تعيين وضع التصحيح في جميع العقد", "test-with-this-message": "{{test}} مع هذه الرسالة" }, "role": { diff --git a/ui-ngx/src/assets/locale/locale.constant-ca_ES.json b/ui-ngx/src/assets/locale/locale.constant-ca_ES.json index c04c0e12cf..43a644a537 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ca_ES.json +++ b/ui-ngx/src/assets/locale/locale.constant-ca_ES.json @@ -3651,8 +3651,7 @@ "metadata-required": "Les entrades de metadades no poden estar buides.", "output": "Sortida", "test": "Test", - "help": "Ajuda", - "reset-debug-mode": "Restablir el mode de depuració a tots els nodes" + "help": "Ajuda" }, "role": { "role": "Rol", diff --git a/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json b/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json index 67b8b22005..4ae54c4d65 100644 --- a/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json +++ b/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json @@ -2426,8 +2426,7 @@ "metadata-required": "Záznam metadat nemůže být prázdný.", "output": "Výstup", "test": "Test", - "help": "Nápověda", - "reset-debug-mode": "Resetovat režim ladění na všech uzlech" + "help": "Nápověda" }, "timezone": { "timezone": "Časová zóna", diff --git a/ui-ngx/src/assets/locale/locale.constant-da_DK.json b/ui-ngx/src/assets/locale/locale.constant-da_DK.json index 9d08a72085..fbfeddd886 100644 --- a/ui-ngx/src/assets/locale/locale.constant-da_DK.json +++ b/ui-ngx/src/assets/locale/locale.constant-da_DK.json @@ -2642,8 +2642,7 @@ "metadata-required": "Metadataposter må ikke være tomme.", "output": "Output", "test": "Test", - "help": "Hjælp", - "reset-debug-mode": "Nulstil debug-tilstand i alle knuder" + "help": "Hjælp" }, "role": { "role": "Rolle", diff --git a/ui-ngx/src/assets/locale/locale.constant-el_GR.json b/ui-ngx/src/assets/locale/locale.constant-el_GR.json index eea0ea4377..164b5486b9 100644 --- a/ui-ngx/src/assets/locale/locale.constant-el_GR.json +++ b/ui-ngx/src/assets/locale/locale.constant-el_GR.json @@ -1886,8 +1886,7 @@ "metadata-required": "Οι καταχωρίσεις μεταδεδομένων δεν μπορούν να είναι κενές.", "output": "Απόδοση", "test": "Τεστ", - "help": "Βοήθεια", - "reset-debug-mode": "Επαναφορά λειτουργίας εντοπισμού σφαλμάτων σε όλους τους κόμβους" + "help": "Βοήθεια" }, "role": { "role": "Ρόλος", diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index c2f45da84f..1968f96346 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -990,15 +990,13 @@ "type-sms-sent": "SMS sent" }, "debug-config": { - "min": "{{number}} min", "label": "Debug configuration", "on-failure": "Failures only (24/7)", "all-messages": "All messages ({{time}})", "failures": "Failures", - "all": "All", "hint": { "main": "All node debug messages rate limited with:", - "main-limited": "All node debug messages will be rate-limited, with a maximum of {{msg}} messages allowed per {{sec}} seconds.", + "main-limited": "All node debug messages will be rate-limited, with a maximum of {{msg}} messages allowed per {{time}}.", "on-failure": "Save all failure debug events without time limit.", "all-messages": "Save all debug events during time limit." } @@ -4293,7 +4291,7 @@ "output": "Output", "test": "Test", "help": "Help", - "reset-debug-mode": "Reset debug mode in all nodes", + "reset-debug-settings": "Reset debug settings in all nodes", "test-with-this-message": "{{test}} with this message", "queue-hint": "Select a queue for message forwarding to another queue. 'Main' queue is used by default.", "queue-singleton-hint": "Select a queue for message forwarding in multi-instance environments. 'Main' queue is used by default." @@ -4743,6 +4741,7 @@ "sec": "{{ sec }} sec", "sec-short": "{{ sec }}s", "short": { + "years": "{ years, plural, =1 {1 year } other {# years } }", "days": "{ days, plural, =1 {1 day } other {# days } }", "hours": "{ hours, plural, =1 {1 hour } other {# hours } }", "minutes": "{{minutes}} min ", diff --git a/ui-ngx/src/assets/locale/locale.constant-es_ES.json b/ui-ngx/src/assets/locale/locale.constant-es_ES.json index 0cb9528bdd..1122c1d6fa 100644 --- a/ui-ngx/src/assets/locale/locale.constant-es_ES.json +++ b/ui-ngx/src/assets/locale/locale.constant-es_ES.json @@ -3529,7 +3529,6 @@ "output": "Salida", "test": "Test", "help": "Ayuda", - "reset-debug-mode": "Restablecer el modo de depuración en todos los nodos", "test-with-this-message": "{{test}} con este mensaje" }, "timezone": { diff --git a/ui-ngx/src/assets/locale/locale.constant-ka_GE.json b/ui-ngx/src/assets/locale/locale.constant-ka_GE.json index f645cb0f31..ae8ce2b943 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ka_GE.json +++ b/ui-ngx/src/assets/locale/locale.constant-ka_GE.json @@ -1413,8 +1413,7 @@ "metadata-required": "მეტამონაცემები ვერ იქნება ცარიელი", "output": "რეზულტატი", "test": "ტესტი", - "help": "დახმარება", - "reset-debug-mode": "Debug რეჟიმის გათიშვა ყველა ნოდისთვის" + "help": "დახმარება" }, "tenant": { "tenant": "ტენანტი", diff --git a/ui-ngx/src/assets/locale/locale.constant-ko_KR.json b/ui-ngx/src/assets/locale/locale.constant-ko_KR.json index 9235fc9e95..80a2cb0830 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ko_KR.json +++ b/ui-ngx/src/assets/locale/locale.constant-ko_KR.json @@ -1878,8 +1878,7 @@ "metadata-required": "메타데이터 엔트리를 입력하세요.", "output": "출력", "test": "테스트", - "help": "도움말", - "reset-debug-mode": "모든 노드에 대해 디버그 모드 초기화" + "help": "도움말" }, "timezone": { "timezone": "Timezone", diff --git a/ui-ngx/src/assets/locale/locale.constant-lt_LT.json b/ui-ngx/src/assets/locale/locale.constant-lt_LT.json index c856652198..1d2a672326 100644 --- a/ui-ngx/src/assets/locale/locale.constant-lt_LT.json +++ b/ui-ngx/src/assets/locale/locale.constant-lt_LT.json @@ -4504,7 +4504,7 @@ "output": "Output", "test": "Test", "help": "Help", - "reset-debug-mode": "Reset debug mode in all nodes", + "reset-debug-settings": "Reset debug settings in all nodes", "test-with-this-message": "{{test}} with this message" }, "role": { diff --git a/ui-ngx/src/assets/locale/locale.constant-lv_LV.json b/ui-ngx/src/assets/locale/locale.constant-lv_LV.json index 0f5c946063..bba71609ec 100644 --- a/ui-ngx/src/assets/locale/locale.constant-lv_LV.json +++ b/ui-ngx/src/assets/locale/locale.constant-lv_LV.json @@ -1338,8 +1338,7 @@ "metadata-required": "Metadatu ievadi nevar būt tukši.", "output": "Izeja", "test": "Tests", - "help": "Palīdzība", - "reset-debug-mode": "Atiestatīt atkļūdošanu visās nodēs" + "help": "Palīdzība" }, "tenant": { "tenant": "Īrnieks", diff --git a/ui-ngx/src/assets/locale/locale.constant-nl_BE.json b/ui-ngx/src/assets/locale/locale.constant-nl_BE.json index f9fe60f44d..7b99ad9953 100644 --- a/ui-ngx/src/assets/locale/locale.constant-nl_BE.json +++ b/ui-ngx/src/assets/locale/locale.constant-nl_BE.json @@ -4560,8 +4560,7 @@ "metadata-required": "Metagegevensvermeldingen mogen niet leeg zijn.", "output": "Uitvoer", "test": "Test", - "help": "Help", - "reset-debug-mode": "Foutopsporingsmodus resetten in alle rule nodes" + "help": "Help" }, "role": { "role": "Rol", diff --git a/ui-ngx/src/assets/locale/locale.constant-pl_PL.json b/ui-ngx/src/assets/locale/locale.constant-pl_PL.json index 9ab89169c0..b369f061b7 100644 --- a/ui-ngx/src/assets/locale/locale.constant-pl_PL.json +++ b/ui-ngx/src/assets/locale/locale.constant-pl_PL.json @@ -4520,7 +4520,6 @@ "output": "Wyjście", "test": "Test", "help": "Pomoc", - "reset-debug-mode": "Zresetuj tryb debugowania we wszystkich węzłach", "test-with-this-message": "{{test}} with this message", "description": "Opis" }, diff --git a/ui-ngx/src/assets/locale/locale.constant-pt_BR.json b/ui-ngx/src/assets/locale/locale.constant-pt_BR.json index 8b77be4573..bfc616e40b 100644 --- a/ui-ngx/src/assets/locale/locale.constant-pt_BR.json +++ b/ui-ngx/src/assets/locale/locale.constant-pt_BR.json @@ -1554,8 +1554,7 @@ "metadata-required": "As entradas de metadados não podem estar em branco.", "output": "Saída", "test": "Teste", - "help": "Ajuda", - "reset-debug-mode": "Redefinir modo de depuração em todos os nós" + "help": "Ajuda" }, "timezone": { "timezone": "Fuso horário", diff --git a/ui-ngx/src/assets/locale/locale.constant-ro_RO.json b/ui-ngx/src/assets/locale/locale.constant-ro_RO.json index 2eb5bc491a..1054b812a1 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ro_RO.json +++ b/ui-ngx/src/assets/locale/locale.constant-ro_RO.json @@ -1401,7 +1401,6 @@ "output": "Ieşire", "test": "Test", "help": "Ajutor", - "reset-debug-mode": "Dezactivează modul depanare în toate nodurile" }, "tenant": { "tenant": "Locatar", diff --git a/ui-ngx/src/assets/locale/locale.constant-sl_SI.json b/ui-ngx/src/assets/locale/locale.constant-sl_SI.json index ff23223107..668bf8c2c9 100644 --- a/ui-ngx/src/assets/locale/locale.constant-sl_SI.json +++ b/ui-ngx/src/assets/locale/locale.constant-sl_SI.json @@ -1879,8 +1879,7 @@ "metadata-required": "Vnosi metapodatkov ne smejo biti prazni.", "output": "Izdelek", "test": "Test", - "help": "Pomoč", - "reset-debug-mode": "Ponastavi način za odpravljanje napak v vseh vozliščih" + "help": "Pomoč" }, "timezone": { "timezone": "Časovni pas", diff --git a/ui-ngx/src/assets/locale/locale.constant-tr_TR.json b/ui-ngx/src/assets/locale/locale.constant-tr_TR.json index e954e8f2e1..98998ab312 100644 --- a/ui-ngx/src/assets/locale/locale.constant-tr_TR.json +++ b/ui-ngx/src/assets/locale/locale.constant-tr_TR.json @@ -2447,8 +2447,7 @@ "metadata-required": "Meta veri girişleri boş bırakılamaz.", "output": "Çıktı", "test": "Ölçek", - "help": "Yardım et", - "reset-debug-mode": "Tüm düğümlerde hata ayıklama modunu sıfırla" + "help": "Yardım et" }, "timezone": { "timezone": "Saat dilimi", diff --git a/ui-ngx/src/assets/locale/locale.constant-uk_UA.json b/ui-ngx/src/assets/locale/locale.constant-uk_UA.json index e1bf787994..09cf86bc21 100644 --- a/ui-ngx/src/assets/locale/locale.constant-uk_UA.json +++ b/ui-ngx/src/assets/locale/locale.constant-uk_UA.json @@ -1978,8 +1978,7 @@ "metadata-required": "Записи метаданих не можуть бути порожніми.", "output": "Вихід", "test": "Тест", - "help": "Допомога", - "reset-debug-mode": "Вимкнути режим налагодження у всіх правилах" + "help": "Допомога" }, "scheduler": { "scheduler": "Планувальник", diff --git a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json index f0dacca36d..47096fe5f2 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json +++ b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json @@ -4012,7 +4012,6 @@ "output": "输出", "test": "测试", "help": "帮助", - "reset-debug-mode": "重置所有节点中的调试模式", "test-with-this-message": "使用此消息进行{{test}}测试", "queue-hint": "选择一个队列将消息转发到另一个队列,默认情况下使用'Main'队列。", "queue-singleton-hint": "选择一个队列以在多实体中转发消息,默认情况下使用'Main'队列。" diff --git a/ui-ngx/src/assets/locale/locale.constant-zh_TW.json b/ui-ngx/src/assets/locale/locale.constant-zh_TW.json index a100d95fa4..64b4c71f16 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_TW.json +++ b/ui-ngx/src/assets/locale/locale.constant-zh_TW.json @@ -2768,8 +2768,7 @@ "metadata-required": "元資料項不能為空。", "output": "輸出", "test": "測試", - "help": "幫助", - "reset-debug-mode": "重置所有節點中的調試模式" + "help": "幫助" }, "timezone": { "timezone": "時區", From 327455305d661baf6257926941f598ecee82a554 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Fri, 6 Dec 2024 17:48:37 +0200 Subject: [PATCH 21/75] UI: Debug Settings fixes refactoring --- .../debug-settings/debug-settings-button.component.html | 4 ++-- .../debug-settings/debug-settings-panel.component.html | 4 ++-- .../home/pages/rulechain/rulechain-page.component.ts | 8 ++++---- .../app/shared/pipe/milliseconds-to-time-string.pipe.ts | 2 +- ui-ngx/src/assets/locale/locale.constant-lt_LT.json | 1 - ui-ngx/src/assets/locale/locale.constant-uk_UA.json | 3 ++- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.html b/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.html index d20fa2e90d..de765556e4 100644 --- a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.html +++ b/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.html @@ -24,8 +24,8 @@ (click)="openDebugStrategyPanel($event, matButton)"> bug_report @if (isDebugAllActive$ | async) { - {{ !allEnabled() ? (allEnabledUntil | durationLeft) : (maxDebugModeDuration | milliSecondsToTimeString: true : true).trim() }} + {{ !allEnabled() ? (allEnabledUntil | durationLeft) : (maxDebugModeDuration | milliSecondsToTimeString: true : true) }} } @else { - {{ failuresEnabled ? ('debug-config.failures' | translate) : ('common.disabled' | translate) }} + {{ failuresEnabled ? 'debug-config.failures' : 'common.disabled' | translate }} } diff --git a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.html b/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.html index f088fc38ac..d47ee69091 100644 --- a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.html @@ -20,7 +20,7 @@
@if (debugLimitsConfiguration) { - {{ 'debug-config.hint.main-limited' | translate: { msg: maxMessagesCount, time: (maxTimeFrameDuration | milliSecondsToTimeString: true : true).trim() } }} + {{ 'debug-config.hint.main-limited' | translate: { msg: maxMessagesCount, time: (maxTimeFrameDuration | milliSecondsToTimeString: true : true) } }} } @else { {{ 'debug-config.hint.main' | translate }} } @@ -35,7 +35,7 @@
- {{ 'debug-config.all-messages' | translate: { time: (isDebugAllActive$ | async) && !allEnabled ? (allEnabledUntil | durationLeft) : (maxDebugModeDuration | milliSecondsToTimeString: true : true).trim() } }} + {{ 'debug-config.all-messages' | translate: { time: (isDebugAllActive$ | async) && !allEnabled ? (allEnabledUntil | durationLeft) : (maxDebugModeDuration | milliSecondsToTimeString: true : true) } }}
diff --git a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.ts b/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.ts index 4db01892a2..8617d25de7 100644 --- a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.ts +++ b/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.ts @@ -16,6 +16,7 @@ import { ChangeDetectionStrategy, + ChangeDetectorRef, Component, forwardRef, Input, @@ -94,6 +95,7 @@ export class DebugSettingsButtonComponent implements ControlValueAccessor { private store: Store, private viewContainerRef: ViewContainerRef, private fb: FormBuilder, + private cd : ChangeDetectorRef, ) { this.debugSettingsFormGroup.valueChanges.pipe( takeUntilDestroyed() @@ -136,6 +138,7 @@ export class DebugSettingsButtonComponent implements ControlValueAccessor { debugStrategyPopover.tbComponentRef.instance.popover = debugStrategyPopover; debugStrategyPopover.tbComponentRef.instance.onSettingsApplied.subscribe((settings: DebugSettings) => { this.debugSettingsFormGroup.patchValue(settings); + this.cd.markForCheck(); debugStrategyPopover.hide(); }); } From b5dd07153725f35d728c3bd538e3c31927a5ad86 Mon Sep 17 00:00:00 2001 From: kalytka Date: Fri, 6 Dec 2024 17:56:05 +0200 Subject: [PATCH 23/75] Refactoring --- ui-ngx/src/app/modules/home/components/public-api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/public-api.ts b/ui-ngx/src/app/modules/home/components/public-api.ts index 6fa57fb4c4..9a320664da 100644 --- a/ui-ngx/src/app/modules/home/components/public-api.ts +++ b/ui-ngx/src/app/modules/home/components/public-api.ts @@ -19,8 +19,8 @@ export * from './widget/config/basic/basic-widget-config.module'; export * from './widget/lib/settings/common/widget-settings-common.module'; export * from './widget/widget-components.module'; export * from './widget/config/widget-config-components.module'; -export * from './widget/lib/chart/echarts-widget.models'; +export * from './widget/lib/chart/echarts-widget.models'; export * from './widget/config/widget-config.component.models'; export * from './widget/lib/table-widget.models'; export * from './widget/lib/flot-widget.models'; From 8e34cfefc89c1fe67cfe54a4e0cf9acbbbb394b2 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Fri, 6 Dec 2024 18:44:21 +0200 Subject: [PATCH 24/75] UI: Added allEnabled = true on rule node copying if debug active --- ui-ngx/src/app/core/services/item-buffer.service.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/app/core/services/item-buffer.service.ts b/ui-ngx/src/app/core/services/item-buffer.service.ts index ff4211961a..c43d1ef35b 100644 --- a/ui-ngx/src/app/core/services/item-buffer.service.ts +++ b/ui-ngx/src/app/core/services/item-buffer.service.ts @@ -305,7 +305,11 @@ export class ItemBufferService { connectors: [], additionalInfo: origNode.additionalInfo, configuration: origNode.configuration, - debugSettings: origNode.debugSettings, + debugSettings: { + failuresEnabled: origNode.debugSettings?.failuresEnabled, + allEnabled: origNode.debugSettings?.allEnabled || origNode.debugSettings?.allEnabledUntil > new Date().getTime(), + allEnabledUntil: 0 + }, x: origNode.x, y: origNode.y, name: origNode.name, From 57df0395416e9ff2e5dd225a371f6cbd79aed8fb Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Fri, 6 Dec 2024 19:03:30 +0200 Subject: [PATCH 25/75] fixed upgrade script for disabled android/ios configuration --- .../main/data/upgrade/basic/schema_update.sql | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/application/src/main/data/upgrade/basic/schema_update.sql b/application/src/main/data/upgrade/basic/schema_update.sql index ced110e65a..9aec87af00 100644 --- a/application/src/main/data/upgrade/basic/schema_update.sql +++ b/application/src/main/data/upgrade/basic/schema_update.sql @@ -143,7 +143,7 @@ $$ LOOP generatedBundleId := NULL; -- migrate android config - IF (qrCodeRecord.android_config IS NOT NULL AND qrCodeRecord.android_config::jsonb -> 'appPackage' IS NOT NULL) THEN + IF (qrCodeRecord.android_config::jsonb ->> 'appPackage' IS NOT NULL) THEN androidPkgName := qrCodeRecord.android_config::jsonb ->> 'appPackage'; SELECT id into androidAppId FROM mobile_app WHERE pkg_name = androidPkgName AND platform_type = 'ANDROID'; IF androidAppId IS NULL THEN @@ -154,17 +154,16 @@ $$ generatedBundleId := uuid_generate_v4(); INSERT INTO mobile_app_bundle(id, created_time, tenant_id, title, android_app_id) VALUES (generatedBundleId, (extract(epoch from now()) * 1000), qrCodeRecord.tenant_id, androidPkgName || ' (autogenerated)', androidAppId); - UPDATE qr_code_settings SET mobile_app_bundle_id = generatedBundleId, - android_enabled = (qrCodeRecord.android_config::jsonb ->> 'enabled')::boolean WHERE id = qrCodeRecord.id; + UPDATE qr_code_settings SET mobile_app_bundle_id = generatedBundleId; ELSE UPDATE mobile_app SET store_info = qrCodeRecord.android_config::jsonb - 'appPackage' - 'enabled' WHERE id = androidAppId; - UPDATE qr_code_settings SET mobile_app_bundle_id = (SELECT id FROM mobile_app_bundle WHERE mobile_app_bundle.android_app_id = androidAppId), - android_enabled = (qrCodeRecord.android_config::jsonb ->> 'enabled')::boolean WHERE id = qrCodeRecord.id; + UPDATE qr_code_settings SET mobile_app_bundle_id = (SELECT id FROM mobile_app_bundle WHERE mobile_app_bundle.android_app_id = androidAppId); END IF; END IF; + UPDATE qr_code_settings SET android_enabled = (qrCodeRecord.android_config::jsonb ->> 'enabled')::boolean WHERE id = qrCodeRecord.id; -- migrate ios config - IF (qrCodeRecord.ios_config IS NOT NULL AND qrCodeRecord.ios_config::jsonb -> 'appId' IS NOT NULL) THEN + IF (qrCodeRecord.ios_config::jsonb ->> 'appId' IS NOT NULL) THEN iosPkgName := substring(qrCodeRecord.ios_config::jsonb ->> 'appId', strpos(qrCodeRecord.ios_config::jsonb ->> 'appId', '.') + 1); SELECT id INTO iosAppId FROM mobile_app WHERE pkg_name = iosPkgName AND platform_type = 'IOS'; IF iosAppId IS NULL THEN @@ -176,17 +175,16 @@ $$ generatedBundleId := uuid_generate_v4(); INSERT INTO mobile_app_bundle(id, created_time, tenant_id, title, ios_app_id) VALUES (generatedBundleId, (extract(epoch from now()) * 1000), qrCodeRecord.tenant_id, iosPkgName || ' (autogenerated)', iosAppId); - UPDATE qr_code_settings SET mobile_app_bundle_id = generatedBundleId, - ios_enabled = (qrCodeRecord.ios_config::jsonb ->> 'enabled')::boolean WHERE id = qrCodeRecord.id; + UPDATE qr_code_settings SET mobile_app_bundle_id = generatedBundleId; ELSE UPDATE mobile_app_bundle SET ios_app_id = iosAppId WHERE id = generatedBundleId; END IF; ELSE - UPDATE qr_code_settings SET mobile_app_bundle_id = (SELECT id FROM mobile_app_bundle WHERE mobile_app_bundle.ios_app_id = iosAppId), - ios_enabled = (qrCodeRecord.ios_config::jsonb -> 'enabled')::boolean WHERE id = qrCodeRecord.id; + UPDATE qr_code_settings SET mobile_app_bundle_id = (SELECT id FROM mobile_app_bundle WHERE mobile_app_bundle.ios_app_id = iosAppId); UPDATE mobile_app SET store_info = qrCodeRecord.ios_config::jsonb - 'enabled' WHERE id = iosAppId; END IF; END IF; + UPDATE qr_code_settings SET ios_enabled = (qrCodeRecord.ios_config::jsonb -> 'enabled')::boolean WHERE id = qrCodeRecord.id; END LOOP; ALTER TABLE qr_code_settings RENAME CONSTRAINT mobile_app_settings_tenant_id_unq_key TO qr_code_settings_tenant_id_unq_key; ALTER TABLE qr_code_settings RENAME CONSTRAINT mobile_app_settings_pkey TO qr_code_settings_pkey; From b906f424424f90a3ca53debeb3b84e93c70b0035 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Mon, 9 Dec 2024 10:17:25 +0200 Subject: [PATCH 26/75] UI: Refactoring --- .../ota-package/ota-package-autocomplete.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts b/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts index 4a117d74b9..89b728916f 100644 --- a/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts +++ b/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts @@ -77,7 +77,7 @@ export class OtaPackageAutocompleteComponent implements ControlValueAccessor, On if (this.deviceProfile) { this.reset(); } - this.deviceProfile = value ? value : NULL_UUID; + this.deviceProfile = value ?? NULL_UUID; } } From 845f2099965b3d787fe19bfe3656fce9413151e9 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Mon, 9 Dec 2024 10:36:51 +0200 Subject: [PATCH 27/75] UI: Refactoring no text option for extension --- .../resource-autocomplete.component.html | 25 ++++++++++++------- .../resource-autocomplete.component.ts | 2 ++ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.html b/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.html index bcc566aeea..04e7a4dbc6 100644 --- a/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.html +++ b/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.html @@ -41,17 +41,24 @@ - -
-
- {{ 'js-func.no-js-module-text' | translate }} -
- + + +
+
+ {{ 'js-func.no-js-module-text' | translate }} +
+ {{ translate.get('js-func.no-js-module-matching', {module: searchText}) | async }} - -
-
+
+
+
+ + + + {{ searchText }} + + diff --git a/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.ts b/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.ts index b87cfe8c4f..1b5a541368 100644 --- a/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.ts +++ b/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.ts @@ -79,6 +79,8 @@ export class ResourceAutocompleteComponent implements ControlValueAccessor, OnIn @Input() subType = ResourceSubType.EXTENSION; + ResourceSubType = ResourceSubType; + resourceFormGroup = this.fb.group({ resource: this.fb.control(null) }); From 7d34e038de53e93e36af1b0bf7270aefb86eb501 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Mon, 9 Dec 2024 10:53:20 +0200 Subject: [PATCH 28/75] UI: translate pipe and truncate --- .../components/resource/resource-autocomplete.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.html b/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.html index 04e7a4dbc6..513bee2808 100644 --- a/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.html +++ b/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.html @@ -49,7 +49,7 @@
- {{ translate.get('js-func.no-js-module-matching', {module: searchText}) | async }} + {{ 'js-func.no-js-module-matching' | translate: {module: searchText | truncate: true: 6: '...'} }}
From 928a34611ae00164a654d67f736671bf71ba0ddf Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Mon, 9 Dec 2024 10:57:24 +0200 Subject: [PATCH 29/75] UI: Refactoring --- .../components/resource/resource-autocomplete.component.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.ts b/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.ts index 1b5a541368..3aab98a394 100644 --- a/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.ts +++ b/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.ts @@ -33,7 +33,6 @@ import { TbResourceId } from '@shared/models/id/tb-resource-id'; import { ResourceService } from '@core/http/resource.service'; import { PageLink } from '@shared/models/page/page-link'; import { MatFormFieldAppearance, SubscriptSizing } from '@angular/material/form-field'; -import { TranslateService } from '@ngx-translate/core'; @Component({ selector: 'tb-resource-autocomplete', @@ -98,8 +97,7 @@ export class ResourceAutocompleteComponent implements ControlValueAccessor, OnIn private propagateChange: (value: any) => void = () => {}; constructor(private fb: FormBuilder, - private resourceService: ResourceService, - public translate: TranslateService) { + private resourceService: ResourceService) { } ngOnInit(): void { From 0083d92862d8d6b7ebe01a6993ddb9c8deb690b9 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Mon, 9 Dec 2024 11:23:21 +0200 Subject: [PATCH 30/75] UI: Fixed relative urls in send email notification --- .../notification-template-configuration.component.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-template-configuration.component.ts b/ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-template-configuration.component.ts index 76cc84d905..31f66f5ea2 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-template-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-template-configuration.component.ts @@ -37,6 +37,7 @@ import { Subject } from 'rxjs'; import { deepClone, isDefinedAndNotNull } from '@core/utils'; import { coerceBoolean } from '@shared/decorators/coercion'; import { TranslateService } from '@ngx-translate/core'; +import { EditorOptions } from 'tinymce'; @Component({ selector: 'tb-template-configuration', @@ -81,7 +82,7 @@ export class NotificationTemplateConfigurationComponent implements OnDestroy, Co readonly NotificationDeliveryMethod = NotificationDeliveryMethod; readonly NotificationTemplateTypeTranslateMap = NotificationTemplateTypeTranslateMap; - tinyMceOptions: Record = { + tinyMceOptions: Partial = { base_url: '/assets/tinymce', suffix: '.min', plugins: ['link', 'table', 'image', 'lists', 'code', 'fullscreen'], @@ -93,7 +94,8 @@ export class NotificationTemplateConfigurationComponent implements OnDestroy, Co height: 400, autofocus: false, branding: false, - promotion: false + promotion: false, + relative_urls: false }; private propagateChange = null; From fcb32c29f912e2a62f3a01528c4de6a500b3feb2 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Mon, 9 Dec 2024 15:08:48 +0200 Subject: [PATCH 31/75] Fix migration from postgres to kafka for edge-events --- .../service/edge/rpc/EdgeGrpcService.java | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java index 71387aa542..2e9465fc9d 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java @@ -413,23 +413,27 @@ private void scheduleEdgeEventsCheck(EdgeGrpcSession session) { if (Boolean.TRUE.equals(sessionNewEvents.get(edgeId))) { log.trace("[{}][{}] Set session new events flag to false", tenantId, edgeId.getId()); sessionNewEvents.put(edgeId, false); - processEdgeEventMigrationIfNeeded(session, edgeId); session.processHighPriorityEvents(); - Futures.addCallback(session.processEdgeEvents(), new FutureCallback<>() { - @Override - public void onSuccess(Boolean newEventsAdded) { - if (Boolean.TRUE.equals(newEventsAdded)) { - sessionNewEvents.put(edgeId, true); + processEdgeEventMigrationIfNeeded(session, edgeId); + if (Boolean.TRUE.equals(edgeEventsMigrationProcessed.get(edgeId))) { + Futures.addCallback(session.processEdgeEvents(), new FutureCallback<>() { + @Override + public void onSuccess(Boolean newEventsAdded) { + if (Boolean.TRUE.equals(newEventsAdded)) { + sessionNewEvents.put(edgeId, true); + } + scheduleEdgeEventsCheck(session); + } + + @Override + public void onFailure(Throwable t) { + log.warn("[{}] Failed to process edge events for edge [{}]!", tenantId, session.getEdge().getId().getId(), t); + scheduleEdgeEventsCheck(session); } - scheduleEdgeEventsCheck(session); - } - - @Override - public void onFailure(Throwable t) { - log.warn("[{}] Failed to process edge events for edge [{}]!", tenantId, session.getEdge().getId().getId(), t); - scheduleEdgeEventsCheck(session); - } - }, ctx.getGrpcCallbackExecutorService()); + }, ctx.getGrpcCallbackExecutorService()); + } else { + scheduleEdgeEventsCheck(session); + } } else { scheduleEdgeEventsCheck(session); } @@ -457,8 +461,6 @@ private void processEdgeEventMigrationIfNeeded(EdgeGrpcSession session, EdgeId e scheduleEdgeEventsCheck(session); } else if (Boolean.FALSE.equals(eventsExist)) { edgeEventsMigrationProcessed.put(edgeId, true); - } else { - scheduleEdgeEventsCheck(session); } } } From d2fdcb289c61455f7a29365980ac020c9da98a1b Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Mon, 9 Dec 2024 15:23:21 +0200 Subject: [PATCH 32/75] UI: Fixed dynamic sources for filters list --- .../components/filter/filter-predicate-list.component.html | 4 ++-- .../components/filter/filter-predicate-value.component.html | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html index 2e9f12da2b..b5d3489c95 100644 --- a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html +++ b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html @@ -40,13 +40,13 @@
-
{{ complexOperationTranslations.get(operation) | translate }}
-
+
{{ hintText | translate }}
-
+
From 6e019951cd5ed674d63c20fd6b3ee8cce6b7d58f Mon Sep 17 00:00:00 2001 From: mpetrov Date: Mon, 9 Dec 2024 16:00:43 +0200 Subject: [PATCH 33/75] UI: adjusted audit logs dialog codeblock height --- .../components/audit-log/audit-log-details-dialog.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.ts b/ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.ts index 881961ed34..de405b0f0d 100644 --- a/ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.ts @@ -105,7 +105,7 @@ export class AuditLogDetailsDialogComponent extends DialogComponent 0) { const lines = content.split('\n'); - newHeight = 17 * lines.length + 16; + newHeight = 18 * lines.length + 16; let maxLineLength = 0; lines.forEach((row) => { const line = row.replace(/\t/g, ' ').replace(/\n/g, ''); From d68342d883fe812e327a98960372ae44a4430143 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Mon, 9 Dec 2024 16:44:00 +0200 Subject: [PATCH 34/75] UI: Refactoring inline style --- .../components/filter/filter-predicate-list.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html index b5d3489c95..439480d5ed 100644 --- a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html +++ b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html @@ -40,13 +40,13 @@
-
{{ complexOperationTranslations.get(operation) | translate }}
-
+
Date: Mon, 9 Dec 2024 17:03:55 +0200 Subject: [PATCH 35/75] Timewindow: fix applying invalid grouping interval when list of allowed intervals set --- .../src/app/shared/components/time/timeinterval.component.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ui-ngx/src/app/shared/components/time/timeinterval.component.ts b/ui-ngx/src/app/shared/components/time/timeinterval.component.ts index a7229660d5..689e42c747 100644 --- a/ui-ngx/src/app/shared/components/time/timeinterval.component.ts +++ b/ui-ngx/src/app/shared/components/time/timeinterval.component.ts @@ -191,8 +191,7 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor, OnCh if (typeof this.modelValue !== 'undefined') { const min = this.timeService.boundMinInterval(this.minValue); const max = this.timeService.boundMaxInterval(this.maxValue); - if (this.allowedIntervals?.length || - IntervalMath.numberValue(this.modelValue) >= min && IntervalMath.numberValue(this.modelValue) <= max) { + if (IntervalMath.numberValue(this.modelValue) >= min && IntervalMath.numberValue(this.modelValue) <= max) { const advanced = this.allowedIntervals?.length ? !this.allowedIntervals.includes(this.modelValue) : !this.timeService.matchesExistingInterval(this.minValue, this.maxValue, this.modelValue, From 910e0cabb88c6eacf726d71fdc2c9d914e691e75 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Mon, 9 Dec 2024 17:20:42 +0200 Subject: [PATCH 36/75] UI: Refactoring --- .../components/filter/filter-predicate-list.component.html | 4 ++-- ui-ngx/tailwind.config.js | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html index 439480d5ed..93fd973753 100644 --- a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html +++ b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html @@ -40,13 +40,13 @@
-
{{ complexOperationTranslations.get(operation) | translate }}
-
+
Date: Mon, 9 Dec 2024 17:25:29 +0200 Subject: [PATCH 37/75] UI: Refactoring --- .../home/components/filter/filter-predicate-list.component.html | 2 +- ui-ngx/tailwind.config.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html index 93fd973753..7285c41a9d 100644 --- a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html +++ b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html @@ -46,7 +46,7 @@
{{ complexOperationTranslations.get(operation) | translate }}
-
+
Date: Mon, 9 Dec 2024 17:26:25 +0200 Subject: [PATCH 38/75] UI: Refactoring --- .../home/components/filter/filter-predicate-list.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html index 7285c41a9d..fd1cf2ea27 100644 --- a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html +++ b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html @@ -46,7 +46,7 @@
{{ complexOperationTranslations.get(operation) | translate }}
-
+
Date: Mon, 9 Dec 2024 18:00:34 +0200 Subject: [PATCH 39/75] Revert "Added fix for timeseries chart showing hidden yAxis" This reverts commit 1bea76a774eea6c42f606d70b1c6ee741310739d. --- .../widget/lib/chart/time-series-chart-widget.component.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-widget.component.ts index efb25a0258..ed023c1a0d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-widget.component.ts @@ -112,9 +112,6 @@ export class TimeSeriesChartWidgetComponent implements OnInit, OnDestroy, AfterV legendKey.dataKey.settings = mergeDeep({} as TimeSeriesChartKeySettings, timeSeriesChartKeyDefaultSettings, legendKey.dataKey.settings); legendKey.dataKey.hidden = legendKey.dataKey.settings.dataHiddenByDefault; - if (this.settings.yAxes[legendKey.dataKey.settings.yAxisId]) { - this.settings.yAxes[legendKey.dataKey.settings.yAxisId].show = !legendKey.dataKey.settings.dataHiddenByDefault; - } }); this.legendKeys = this.legendKeys.filter(legendKey => legendKey.dataKey.settings.showInLegend); if (!this.legendKeys.length) { From 5b2eb75c92f56106bced631a1d7328663e122d74 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Mon, 9 Dec 2024 18:25:14 +0200 Subject: [PATCH 40/75] Update locale.constant-en_US.json --- ui-ngx/src/assets/locale/locale.constant-en_US.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 6b50deb6ab..362df078ab 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -3333,8 +3333,8 @@ "module-load-error": "Module load error", "source-code": "Source code", "source-code-load-error": "Source code load error", - "no-js-module-text": "No JS module found", - "no-js-module-matching": "No JS module matching '{{module}}' were found." + "no-js-module-text": "No JS modules found", + "no-js-module-matching": "No JS modules matching '{{module}}' were found." }, "key-val": { "key": "Key", From bc44779a96f8a9d8ef3284cb8e73663e56ed0782 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Mon, 9 Dec 2024 18:34:01 +0200 Subject: [PATCH 41/75] UI: Fixed scroll on progress bar for usage info widget --- .../widget/lib/home-page/usage-info-widget.component.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.scss index b7c02acef6..529a3d4495 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.scss @@ -97,6 +97,7 @@ :host ::ng-deep { .tb-usage-items-progress { .mat-mdc-progress-bar { + overflow: hidden; .mdc-linear-progress__bar-inner { border-top-width: 8px; } From 3e2ebcf6858894c2b26dd568f105ea9e15728dcd Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Tue, 10 Dec 2024 09:52:49 +0200 Subject: [PATCH 42/75] UI: Fixed tenant profile default configuration --- ...lt-tenant-profile-configuration.component.ts | 2 +- ui-ngx/src/app/shared/models/tenant.model.ts | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts index 490bdc902b..36ac40ecf2 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts @@ -86,7 +86,6 @@ export class DefaultTenantProfileConfigurationComponent implements ControlValueA tenantNotificationRequestsRateLimit: [null, []], tenantNotificationRequestsPerRuleRateLimit: [null, []], maxTransportMessages: [null, [Validators.required, Validators.min(0)]], - maxDebugModeDurationMinutes: [null, [Validators.min(0)]], maxTransportDataPoints: [null, [Validators.required, Validators.min(0)]], maxREExecutions: [null, [Validators.required, Validators.min(0)]], maxJSExecutions: [null, [Validators.required, Validators.min(0)]], @@ -97,6 +96,7 @@ export class DefaultTenantProfileConfigurationComponent implements ControlValueA maxSms: [null, []], smsEnabled: [null, []], maxCreatedAlarms: [null, [Validators.required, Validators.min(0)]], + maxDebugModeDurationMinutes: [null, [Validators.min(0)]], defaultStorageTtlDays: [null, [Validators.required, Validators.min(0)]], alarmsTtlDays: [null, [Validators.required, Validators.min(0)]], rpcTtlDays: [null, [Validators.required, Validators.min(0)]], diff --git a/ui-ngx/src/app/shared/models/tenant.model.ts b/ui-ngx/src/app/shared/models/tenant.model.ts index 0520dd2b4e..aefc85a7f6 100644 --- a/ui-ngx/src/app/shared/models/tenant.model.ts +++ b/ui-ngx/src/app/shared/models/tenant.model.ts @@ -31,6 +31,7 @@ export interface DefaultTenantProfileConfiguration { maxUsers: number; maxDashboards: number; maxRuleChains: number; + maxEdges: number; maxResourcesInBytes: number; maxOtaPackagesInBytes: number; maxResourceSize: number; @@ -42,6 +43,13 @@ export interface DefaultTenantProfileConfiguration { transportDeviceTelemetryMsgRateLimit?: string; transportDeviceTelemetryDataPointsRateLimit?: string; + transportGatewayMsgRateLimit?: string; + transportGatewayTelemetryMsgRateLimit?: string; + transportGatewayTelemetryDataPointsRateLimit?: string; + transportGatewayDeviceMsgRateLimit?: string; + transportGatewayDeviceTelemetryMsgRateLimit?: string; + transportGatewayDeviceTelemetryDataPointsRateLimit?: string; + tenantEntityExportRateLimit?: string; tenantEntityImportRateLimit?: string; tenantNotificationRequestsRateLimit?: string; @@ -59,6 +67,8 @@ export interface DefaultTenantProfileConfiguration { smsEnabled: boolean; maxCreatedAlarms: number; + maxDebugModeDurationMinutes: number; + tenantServerRestLimitsConfiguration: string; customerServerRestLimitsConfiguration: string; @@ -75,6 +85,11 @@ export interface DefaultTenantProfileConfiguration { cassandraQueryTenantRateLimitsConfiguration: string; + edgeEventRateLimits?: string; + edgeEventRateLimitsPerEdge?: string; + edgeUplinkMessagesRateLimits?: string; + edgeUplinkMessagesRateLimitsPerEdge?: string; + defaultStorageTtlDays: number; alarmsTtlDays: number; rpcTtlDays: number; @@ -100,6 +115,7 @@ export function createTenantProfileConfiguration(type: TenantProfileType): Tenan maxUsers: 0, maxDashboards: 0, maxRuleChains: 0, + maxEdges: 0, maxResourcesInBytes: 0, maxOtaPackagesInBytes: 0, maxResourceSize: 0, @@ -114,6 +130,7 @@ export function createTenantProfileConfiguration(type: TenantProfileType): Tenan maxSms: 0, smsEnabled: true, maxCreatedAlarms: 0, + maxDebugModeDurationMinutes: 0, tenantServerRestLimitsConfiguration: '', customerServerRestLimitsConfiguration: '', maxWsSessionsPerTenant: 0, From 9c659a7029925e7b46e11874279f0957c14e29ed Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Tue, 10 Dec 2024 11:51:16 +0200 Subject: [PATCH 43/75] UI: Refactoring --- .../ota-package-autocomplete.component.ts | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts b/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts index 89b728916f..79fbd36d73 100644 --- a/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts +++ b/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts @@ -31,11 +31,10 @@ import { OtaPackageService } from '@core/http/ota-package.service'; import { PageLink } from '@shared/models/page/page-link'; import { Direction } from '@shared/models/page/sort-order'; import { emptyPageData } from '@shared/models/page/page-data'; -import { getEntityDetailsPageURL } from '@core/utils'; +import { getEntityDetailsPageURL, isDefinedAndNotNull } from '@core/utils'; import { AuthUser } from '@shared/models/user.model'; import { getCurrentAuthUser } from '@core/auth/auth.selectors'; import { Authority } from '@shared/models/authority.enum'; -import { NULL_UUID } from '@shared/models/id/has-uuid'; @Component({ selector: 'tb-ota-package-autocomplete', @@ -65,19 +64,19 @@ export class OtaPackageAutocompleteComponent implements ControlValueAccessor, On this.reset(); } - private deviceProfile: string; + private deviceProfileIdValue: string; get deviceProfileId(): string { - return this.deviceProfile; + return this.deviceProfileIdValue; } @Input() set deviceProfileId(value: string) { - if (this.deviceProfile !== value) { - if (this.deviceProfile) { + if (this.deviceProfileIdValue !== value) { + if (this.deviceProfileIdValue) { this.reset(); } - this.deviceProfile = value ?? NULL_UUID; + this.deviceProfileIdValue = value; } } @@ -258,16 +257,20 @@ export class OtaPackageAutocompleteComponent implements ControlValueAccessor, On } fetchPackages(searchText?: string): Observable> { - this.searchText = searchText; - const pageLink = new PageLink(50, 0, searchText, { - property: 'title', - direction: Direction.ASC - }); - return this.otaPackageService.getOtaPackagesInfoByDeviceProfileId(pageLink, this.deviceProfileId, this.type, - {ignoreLoading: true}).pipe( - catchError(() => of(emptyPageData())), - map((data) => data && data.data.length ? data.data : null) - ); + if (isDefinedAndNotNull(this.deviceProfileId)) { + this.searchText = searchText; + const pageLink = new PageLink(50, 0, searchText, { + property: 'title', + direction: Direction.ASC + }); + return this.otaPackageService.getOtaPackagesInfoByDeviceProfileId(pageLink, this.deviceProfileId, this.type, + {ignoreLoading: true}).pipe( + catchError(() => of(emptyPageData())), + map((data) => data && data.data.length ? data.data : null) + ); + } else { + return of([]); + } } clear() { From 935739ac85eda9c31f3f9243be494b7dfaeb53f7 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Tue, 10 Dec 2024 15:29:17 +0200 Subject: [PATCH 44/75] UI: Debug Settings minor refactoring --- .../debug}/debug-settings-button.component.html | 0 .../debug}/debug-settings-button.component.ts | 10 +++++----- .../debug}/debug-settings-panel.component.html | 0 .../debug}/debug-settings-panel.component.ts | 4 ++-- .../modules/home/pages/rulechain/rulechain.module.ts | 2 +- ui-ngx/src/app/shared/models/entity.models.ts | 6 +++--- ui-ngx/src/app/shared/models/rule-node.models.ts | 6 +++--- 7 files changed, 14 insertions(+), 14 deletions(-) rename ui-ngx/src/app/modules/home/components/{debug-settings => entity/debug}/debug-settings-button.component.html (100%) rename ui-ngx/src/app/modules/home/components/{debug-settings => entity/debug}/debug-settings-button.component.ts (93%) rename ui-ngx/src/app/modules/home/components/{debug-settings => entity/debug}/debug-settings-panel.component.html (100%) rename ui-ngx/src/app/modules/home/components/{debug-settings => entity/debug}/debug-settings-panel.component.ts (97%) diff --git a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.html b/ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-button.component.html similarity index 100% rename from ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.html rename to ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-button.component.html diff --git a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.ts b/ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-button.component.ts similarity index 93% rename from ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.ts rename to ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-button.component.ts index dafda75c0b..f89e7ec198 100644 --- a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.ts +++ b/ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-button.component.ts @@ -24,7 +24,7 @@ import { DebugSettingsPanelComponent } from './debug-settings-panel.component'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { of, shareReplay, timer } from 'rxjs'; import { SECOND } from '@shared/models/time/time.models'; -import { DebugSettings } from '@shared/models/entity.models'; +import { EntityDebugSettings } from '@shared/models/entity.models'; import { map, startWith, switchMap, takeWhile } from 'rxjs/operators'; import { getCurrentAuthState } from '@core/auth/auth.selectors'; import { AppState } from '@core/core.state'; @@ -79,7 +79,7 @@ export class DebugSettingsButtonComponent implements ControlValueAccessor { readonly maxDebugModeDurationMinutes = getCurrentAuthState(this.store).maxDebugModeDurationMinutes; - private propagateChange: (settings: DebugSettings) => void = () => {}; + private propagateChange: (settings: EntityDebugSettings) => void = () => {}; constructor(private popoverService: TbPopoverService, private renderer: Renderer2, @@ -126,20 +126,20 @@ export class DebugSettingsButtonComponent implements ControlValueAccessor { {}, {}, {}, true); debugStrategyPopover.tbComponentRef.instance.popover = debugStrategyPopover; - debugStrategyPopover.tbComponentRef.instance.onSettingsApplied.subscribe((settings: DebugSettings) => { + debugStrategyPopover.tbComponentRef.instance.onSettingsApplied.subscribe((settings: EntityDebugSettings) => { this.debugSettingsFormGroup.patchValue(settings); debugStrategyPopover.hide(); }); } } - registerOnChange(fn: (settings: DebugSettings) => void): void { + registerOnChange(fn: (settings: EntityDebugSettings) => void): void { this.propagateChange = fn; } registerOnTouched(_: () => void): void {} - writeValue(settings: DebugSettings): void { + writeValue(settings: EntityDebugSettings): void { this.debugSettingsFormGroup.patchValue(settings, {emitEvent: false}); this.debugSettingsFormGroup.get('allEnabled').updateValueAndValidity({onlySelf: true}); } diff --git a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.html b/ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-panel.component.html similarity index 100% rename from ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.html rename to ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-panel.component.html diff --git a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.ts b/ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-panel.component.ts similarity index 97% rename from ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.ts rename to ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-panel.component.ts index a9e237245c..702d416982 100644 --- a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-panel.component.ts @@ -32,7 +32,7 @@ import { SECOND } from '@shared/models/time/time.models'; import { DurationLeftPipe } from '@shared/pipe/duration-left.pipe'; import { of, shareReplay, timer } from 'rxjs'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { DebugSettings } from '@shared/models/entity.models'; +import { EntityDebugSettings } from '@shared/models/entity.models'; import { distinctUntilChanged, map, startWith, switchMap, takeWhile } from 'rxjs/operators'; @Component({ @@ -78,7 +78,7 @@ export class DebugSettingsPanelComponent extends PageComponent implements OnInit shareReplay(1), ); - onSettingsApplied = new EventEmitter(); + onSettingsApplied = new EventEmitter(); constructor(private fb: FormBuilder, private cd: ChangeDetectorRef) { diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.module.ts b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.module.ts index 1fdf11c780..a89b6868f7 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.module.ts +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.module.ts @@ -33,7 +33,7 @@ import { RuleNodeLinkComponent } from './rule-node-link.component'; import { LinkLabelsComponent } from '@home/pages/rulechain/link-labels.component'; import { RuleNodeConfigComponent } from './rule-node-config.component'; import { DurationLeftPipe } from '@shared/pipe/duration-left.pipe'; -import { DebugSettingsButtonComponent } from '@home/components/debug-settings/debug-settings-button.component'; +import { DebugSettingsButtonComponent } from '@home/components/entity/debug-settings/debug-settings-button.component'; @NgModule({ declarations: [ diff --git a/ui-ngx/src/app/shared/models/entity.models.ts b/ui-ngx/src/app/shared/models/entity.models.ts index 19b0718336..9934da65aa 100644 --- a/ui-ngx/src/app/shared/models/entity.models.ts +++ b/ui-ngx/src/app/shared/models/entity.models.ts @@ -193,11 +193,11 @@ export interface HasVersion { version?: number; } -export interface HasDebugSettings { - debugSettings?: DebugSettings; +export interface HasEntityDebugSettings { + debugSettings?: EntityDebugSettings; } -export interface DebugSettings { +export interface EntityDebugSettings { failuresEnabled?: boolean; allEnabled?: boolean; allEnabledUntil?: number; diff --git a/ui-ngx/src/app/shared/models/rule-node.models.ts b/ui-ngx/src/app/shared/models/rule-node.models.ts index ddfad98f11..f7d5e20d1d 100644 --- a/ui-ngx/src/app/shared/models/rule-node.models.ts +++ b/ui-ngx/src/app/shared/models/rule-node.models.ts @@ -27,13 +27,13 @@ import { AppState } from '@core/core.state'; import { AbstractControl, UntypedFormGroup } from '@angular/forms'; import { RuleChainType } from '@shared/models/rule-chain.models'; import { DebugRuleNodeEventBody } from '@shared/models/event.models'; -import { HasDebugSettings } from '@shared/models/entity.models'; +import { HasEntityDebugSettings } from '@shared/models/entity.models'; export interface RuleNodeConfiguration { [key: string]: any; } -export interface RuleNode extends BaseData, HasDebugSettings { +export interface RuleNode extends BaseData, HasEntityDebugSettings { ruleChainId?: RuleChainId; type: string; name: string; @@ -331,7 +331,7 @@ export interface RuleNodeComponentDescriptor extends ComponentDescriptor { configurationDescriptor?: RuleNodeConfigurationDescriptor; } -export interface FcRuleNodeType extends FcNode, HasDebugSettings { +export interface FcRuleNodeType extends FcNode, HasEntityDebugSettings { component?: RuleNodeComponentDescriptor; singletonMode?: boolean; queueName?: string; From 0b57d0ccac6986c523dbb30c85598a10072ea0f1 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Tue, 10 Dec 2024 15:37:20 +0200 Subject: [PATCH 45/75] folder rename --- ui-ngx/src/app/modules/home/pages/rulechain/rulechain.module.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.module.ts b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.module.ts index a89b6868f7..830d539182 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.module.ts +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.module.ts @@ -33,7 +33,7 @@ import { RuleNodeLinkComponent } from './rule-node-link.component'; import { LinkLabelsComponent } from '@home/pages/rulechain/link-labels.component'; import { RuleNodeConfigComponent } from './rule-node-config.component'; import { DurationLeftPipe } from '@shared/pipe/duration-left.pipe'; -import { DebugSettingsButtonComponent } from '@home/components/entity/debug-settings/debug-settings-button.component'; +import { DebugSettingsButtonComponent } from '@home/components/entity/debug/debug-settings-button.component'; @NgModule({ declarations: [ From 74543f80d276fd183d5a11e5766dd29ca37b37b8 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Tue, 10 Dec 2024 15:56:53 +0200 Subject: [PATCH 46/75] UI: Debug Settings minor refactoring, files rename --- ...l => entity-debug-settings-button.component.html} | 0 ....ts => entity-debug-settings-button.component.ts} | 12 ++++++------ ...ml => entity-debug-settings-panel.component.html} | 0 ...t.ts => entity-debug-settings-panel.component.ts} | 8 ++++---- .../pages/rulechain/rule-node-details.component.html | 2 +- .../modules/home/pages/rulechain/rulechain.module.ts | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) rename ui-ngx/src/app/modules/home/components/entity/debug/{debug-settings-button.component.html => entity-debug-settings-button.component.html} (100%) rename ui-ngx/src/app/modules/home/components/entity/debug/{debug-settings-button.component.ts => entity-debug-settings-button.component.ts} (92%) rename ui-ngx/src/app/modules/home/components/entity/debug/{debug-settings-panel.component.html => entity-debug-settings-panel.component.html} (100%) rename ui-ngx/src/app/modules/home/components/entity/debug/{debug-settings-panel.component.ts => entity-debug-settings-panel.component.ts} (93%) diff --git a/ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-button.component.html b/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-button.component.html similarity index 100% rename from ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-button.component.html rename to ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-button.component.html diff --git a/ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-button.component.ts b/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-button.component.ts similarity index 92% rename from ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-button.component.ts rename to ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-button.component.ts index 3af2ca270c..932658f9bf 100644 --- a/ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-button.component.ts +++ b/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-button.component.ts @@ -29,7 +29,7 @@ import { SharedModule } from '@shared/shared.module'; import { DurationLeftPipe } from '@shared/pipe/duration-left.pipe'; import { TbPopoverService } from '@shared/components/popover.service'; import { MatButton } from '@angular/material/button'; -import { DebugSettingsPanelComponent } from './debug-settings-panel.component'; +import { EntityDebugSettingsPanelComponent } from './entity-debug-settings-panel.component'; import { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop'; import { of, shareReplay, timer } from 'rxjs'; import { SECOND, MINUTE } from '@shared/models/time/time.models'; @@ -41,8 +41,8 @@ import { Store } from '@ngrx/store'; import { ControlValueAccessor, FormBuilder, NG_VALUE_ACCESSOR } from '@angular/forms'; @Component({ - selector: 'tb-debug-settings-button', - templateUrl: './debug-settings-button.component.html', + selector: 'tb-entity-debug-settings-button', + templateUrl: './entity-debug-settings-button.component.html', standalone: true, imports: [ CommonModule, @@ -52,13 +52,13 @@ import { ControlValueAccessor, FormBuilder, NG_VALUE_ACCESSOR } from '@angular/f providers: [ { provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => DebugSettingsButtonComponent), + useExisting: forwardRef(() => EntityDebugSettingsButtonComponent), multi: true }, ], changeDetection: ChangeDetectionStrategy.OnPush }) -export class DebugSettingsButtonComponent implements ControlValueAccessor { +export class EntityDebugSettingsButtonComponent implements ControlValueAccessor { @Input() debugLimitsConfiguration: string; @@ -127,7 +127,7 @@ export class DebugSettingsButtonComponent implements ControlValueAccessor { this.popoverService.hidePopover(trigger); } else { const debugStrategyPopover = this.popoverService.displayPopover(trigger, this.renderer, - this.viewContainerRef, DebugSettingsPanelComponent, 'bottom', true, null, + this.viewContainerRef, EntityDebugSettingsPanelComponent, 'bottom', true, null, { ...debugSettings, maxDebugModeDuration: this.maxDebugModeDuration, diff --git a/ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-panel.component.html b/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-panel.component.html similarity index 100% rename from ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-panel.component.html rename to ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-panel.component.html diff --git a/ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-panel.component.ts b/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-panel.component.ts similarity index 93% rename from ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-panel.component.ts rename to ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-panel.component.ts index 2c26e1d3cf..cfa8775a35 100644 --- a/ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-panel.component.ts @@ -36,8 +36,8 @@ import { EntityDebugSettings } from '@shared/models/entity.models'; import { distinctUntilChanged, map, startWith, switchMap, takeWhile } from 'rxjs/operators'; @Component({ - selector: 'tb-debug-settings-panel', - templateUrl: './debug-settings-panel.component.html', + selector: 'tb-entity-debug-settings-panel', + templateUrl: './entity-debug-settings-panel.component.html', standalone: true, imports: [ SharedModule, @@ -46,9 +46,9 @@ import { distinctUntilChanged, map, startWith, switchMap, takeWhile } from 'rxjs ], changeDetection: ChangeDetectionStrategy.OnPush }) -export class DebugSettingsPanelComponent extends PageComponent implements OnInit { +export class EntityDebugSettingsPanelComponent extends PageComponent implements OnInit { - @Input() popover: TbPopoverComponent; + @Input() popover: TbPopoverComponent; @Input({ transform: booleanAttribute }) failuresEnabled = false; @Input({ transform: booleanAttribute }) allEnabled = false; @Input() allEnabledUntil = 0; diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.html b/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.html index 13cc24a7b7..b2d17857b6 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.html +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.html @@ -35,7 +35,7 @@
- Date: Tue, 10 Dec 2024 16:29:54 +0200 Subject: [PATCH 47/75] UI: Refactoring --- .../widget/lib/home-page/usage-info-widget.component.scss | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.scss index 529a3d4495..86f7036276 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.scss @@ -16,6 +16,8 @@ @import "../../../../../../../scss/constants"; :host { + --mdc-linear-progress-track-height: 8px; + --mdc-linear-progress-active-indicator-height: 8px; .tb-card-header { display: flex; flex-direction: row; @@ -49,7 +51,6 @@ display: none; } .mdc-linear-progress { - height: 8px; margin-top: 6px; margin-bottom: 6px; border-radius: 2px; @@ -97,10 +98,6 @@ :host ::ng-deep { .tb-usage-items-progress { .mat-mdc-progress-bar { - overflow: hidden; - .mdc-linear-progress__bar-inner { - border-top-width: 8px; - } &.critical { .mdc-linear-progress__buffer-bar { background: rgba(209, 39, 48, 0.06); From 42646d4e5c3b7759acae114ddfd10d72482040ab Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Tue, 10 Dec 2024 17:14:01 +0200 Subject: [PATCH 48/75] UI: Refactoring css --- .../usage-info-widget.component.scss | 25 ++++++------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.scss index 86f7036276..1de8288241 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.scss @@ -16,8 +16,6 @@ @import "../../../../../../../scss/constants"; :host { - --mdc-linear-progress-track-height: 8px; - --mdc-linear-progress-active-indicator-height: 8px; .tb-card-header { display: flex; flex-direction: row; @@ -46,10 +44,18 @@ } .tb-usage-items-progress { + --mdc-linear-progress-track-height: 8px; + --mdc-linear-progress-active-indicator-height: 8px; width: 34px; @media #{$mat-md} { display: none; } + .mat-mdc-progress-bar { + &.critical { + --mdc-linear-progress-track-color: rgba(209, 39, 48, 0.06); + --mdc-linear-progress-active-indicator-color: #D12730; + } + } .mdc-linear-progress { margin-top: 6px; margin-bottom: 6px; @@ -94,18 +100,3 @@ color: rgba(0, 0, 0, 0.76); } } - -:host ::ng-deep { - .tb-usage-items-progress { - .mat-mdc-progress-bar { - &.critical { - .mdc-linear-progress__buffer-bar { - background: rgba(209, 39, 48, 0.06); - } - .mdc-linear-progress__bar-inner { - border-top-color: #D12730; - } - } - } - } -} From 8d6920671dd2ecf3ff86eeb1552701da71b0c028 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Tue, 10 Dec 2024 19:23:23 +0200 Subject: [PATCH 49/75] UI: Gateway MQTT md files update --- .../lib/gateway/mqtt-bytes-expression_fn.md | 18 +++++ ...xpressions_fn.md => mqtt-expression_fn.md} | 0 .../lib/gateway/mqtt-json-expression_fn.md | 78 +++++++++++++++++++ ...s_fn.md => mqtt-json-key-expression_fn.md} | 6 +- 4 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 ui-ngx/src/assets/help/en_US/widget/lib/gateway/mqtt-bytes-expression_fn.md rename ui-ngx/src/assets/help/en_US/widget/lib/gateway/{expressions_fn.md => mqtt-expression_fn.md} (100%) create mode 100644 ui-ngx/src/assets/help/en_US/widget/lib/gateway/mqtt-json-expression_fn.md rename ui-ngx/src/assets/help/en_US/widget/lib/gateway/{attributes_timeseries_expressions_fn.md => mqtt-json-key-expression_fn.md} (85%) diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/gateway/mqtt-bytes-expression_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/gateway/mqtt-bytes-expression_fn.md new file mode 100644 index 0000000000..aa7225898a --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/widget/lib/gateway/mqtt-bytes-expression_fn.md @@ -0,0 +1,18 @@ +### Expressions +#### Bytes converter: + +For bytes converter, expression fields can use slices format only. A slice specifies how to slice a sequence, determining the start point, and the endpoint. Here's a basic overview of slice components: + +- `start`: The starting index of the slice. It is included in the slice. If omitted, slicing starts at the beginning of the sequence. Indexing starts at 0, so the first element of the sequence is at index 0. + +- `stop`: The ending index of the slice. It is excluded from the slice, meaning the slice will end just before this index. If omitted, slicing goes through the end of the sequence. + +##### Bytes parsing examples: + + +| Message body | Slice | Output data | Description | +|:-----------------------|-----------------|--------------------------|------------------------------| +| AM123,mytype,12.2,45 | [:5] | AM123 | Extracting device name | +| AM123,mytype,12.2,45 | [:] | AM123,mytype,12.2,45 | Extracting all data | +| AM123,mytype,12.2,45 | [18:] | 45 | Extracting humidity value | +| AM123,mytype,12.2,45 | [13:17] | 12.2 | Extracting temperature value | diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/gateway/expressions_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/gateway/mqtt-expression_fn.md similarity index 100% rename from ui-ngx/src/assets/help/en_US/widget/lib/gateway/expressions_fn.md rename to ui-ngx/src/assets/help/en_US/widget/lib/gateway/mqtt-expression_fn.md diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/gateway/mqtt-json-expression_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/gateway/mqtt-json-expression_fn.md new file mode 100644 index 0000000000..28387b595b --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/widget/lib/gateway/mqtt-json-expression_fn.md @@ -0,0 +1,78 @@ +### Expressions +#### JSON Path: + +The expression field is used to extract data from the MQTT message. There are various available options for different parts of the messages: + +- The JSONPath format can be used to extract data from the message body. + +- The regular expression format can be used to extract data from the topic where the message will arrive. + +- Slices can only be used in the expression fields of bytes converters. + +JSONPath expressions specify the items within a JSON structure (which could be an object, array, or nested combination of both) that you want to access. These expressions can select elements from JSON data on specific criteria. Here's a basic overview of how JSONPath expressions are structured: + +- `$`: The root element of the JSON document; + +- `.`: Child operator used to select child elements. For example, $.store.book ; + +- `[]`: Child operator used to select child elements. $['store']['book'] accesses the book array within a store object; + +##### Examples: + +For example, if we want to extract the device name from the following message, we can use the expression below: + +MQTT message: + +``` +{ + "sensorModelInfo": { + "sensorName": "AM-123", + "sensorType": "myDeviceType" + }, + "data": { + "temp": 12.2, + "hum": 56, + "status": "ok" + } +} +{:copy-code} +``` + +Expression: + +`${sensorModelInfo.sensorName}` + +Converted data: + +`AM-123` + +If we want to extract all data from the message above, we can use the following expression: + +`${data}` + +Converted data: + +`{"temp": 12.2, "hum": 56, "status": "ok"}` + +Or if we want to extract specific data (for example “temperature”), you can use the following expression: + +`${data.temp}` + +And as a converted data we will get: + +`12.2` + +
+ +#### Regular expressions for topic: + +Device name or device profile can be parsed from the MQTT topic using regular expression. A regular expression, often abbreviated as regex or regexp, is a sequence of characters that forms a search pattern, primarily used for string matching and manipulation. + +##### Regular expression for topic examples: + +| Topic | Regular expression | Output data | Description | +|:---------------------------|----------------------------------|--------------------------|--------------------------------------| +| /devices/AM123/mytype/data | /devices/([^/]+)/mytype/data | AM123 | Getting device name from topic | +| /devices/AM123/mytype/data | /devices/[A-Z0-9]+/([^/]+)/data | mytype | Getting device profile from topic | + +
diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/gateway/attributes_timeseries_expressions_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/gateway/mqtt-json-key-expression_fn.md similarity index 85% rename from ui-ngx/src/assets/help/en_US/widget/lib/gateway/attributes_timeseries_expressions_fn.md rename to ui-ngx/src/assets/help/en_US/widget/lib/gateway/mqtt-json-key-expression_fn.md index 91cf23ce26..98337dcd77 100644 --- a/ui-ngx/src/assets/help/en_US/widget/lib/gateway/attributes_timeseries_expressions_fn.md +++ b/ui-ngx/src/assets/help/en_US/widget/lib/gateway/mqtt-json-key-expression_fn.md @@ -3,11 +3,11 @@ The expression field is used to extract data from the MQTT message. There are various available options for different parts of the messages: - - The JSONPath format can be used to extract data from the message body. +- The JSONPath format can be used to extract data from the message body. - - The regular expression format can be used to extract data from the topic where the message will arrive. +- The regular expression format can be used to extract data from the topic where the message will arrive. - - Slices can only be used in the expression fields of bytes converters. +- Slices can only be used in the expression fields of bytes converters. JSONPath expressions specify the items within a JSON structure (which could be an object, array, or nested combination of both) that you want to access. These expressions can select elements from JSON data on specific criteria. Here's a basic overview of how JSONPath expressions are structured: From 9cbe2ca4d5ba55cc98a05645cb075f38880e9769 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 11 Dec 2024 11:14:34 +0200 Subject: [PATCH 50/75] Ignore not chosen recipients for system notification types --- .../server/service/notification/DefaultNotificationCenter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java index af63256f0f..d30df7cd65 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java @@ -149,7 +149,7 @@ public NotificationRequest processNotificationRequest(TenantId tenantId, Notific return; // if originated by rule - just ignore delivery method } } - if (ruleId == null) { + if (ruleId == null && !notificationTemplate.getNotificationType().isSystem()) { if (targets.stream().noneMatch(target -> target.getConfiguration().getType().getSupportedDeliveryMethods().contains(deliveryMethod))) { throw new IllegalArgumentException("Recipients for " + deliveryMethod.getName() + " delivery method not chosen"); } From 072a08564cda53796195c1ba95fe14c4b71e9e20 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 11 Dec 2024 11:28:20 +0200 Subject: [PATCH 51/75] Use tenant notification providers for system notification type --- .../service/notification/DefaultNotificationCenter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java index d30df7cd65..9a293ada7e 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java @@ -225,13 +225,13 @@ public void sendSystemNotification(TenantId tenantId, NotificationTargetId targe NotificationTemplate notificationTemplate = notificationTemplateService.findTenantOrSystemNotificationTemplate(tenantId, type) .orElseThrow(() -> new IllegalArgumentException("No notification template found for type " + type)); NotificationRequest notificationRequest = NotificationRequest.builder() - .tenantId(TenantId.SYS_TENANT_ID) + .tenantId(tenantId) .targets(List.of(targetId.getId())) .templateId(notificationTemplate.getId()) .info(info) .originatorEntityId(TenantId.SYS_TENANT_ID) .build(); - processNotificationRequest(TenantId.SYS_TENANT_ID, notificationRequest, null); + processNotificationRequest(tenantId, notificationRequest, null); } private void processNotificationRequestAsync(NotificationProcessingContext ctx, List targets, FutureCallback callback) { From c218c51db4812e2ff4745458e00fea92bf37c2c4 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 11 Dec 2024 14:59:23 +0200 Subject: [PATCH 52/75] UI: Fixed adaptive grid when moving widget --- .../modules/home/components/dashboard/dashboard.component.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.ts b/ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.ts index 7169e4a20b..e3e7cb2f1d 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.ts +++ b/ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.ts @@ -253,7 +253,10 @@ export class DashboardComponent extends PageComponent implements IDashboardCompo draggable: { enabled: this.isEdit && !this.isEditingWidget, delayStart: 100, - stop: (_, itemComponent) => {(itemComponent.item as DashboardWidget).updatePosition(itemComponent.$item.x, itemComponent.$item.y);} + stop: (_, itemComponent) => { + (itemComponent.item as DashboardWidget).updatePosition(itemComponent.$item.x, itemComponent.$item.y); + this.notifyGridsterOptionsChanged(); + } }, itemChangeCallback: () => this.dashboardWidgets.sortWidgets(), itemInitCallback: (_, itemComponent) => { From b2a1793f4d2fd3de41b6ac80febec0c24e370f2d Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 11 Dec 2024 15:31:16 +0200 Subject: [PATCH 53/75] UI: Fixed updated mobile layout and show mobile edit panel --- .../mobile/bundes/layout/mobile-page-item-row.component.ts | 4 ++-- .../home/pages/mobile/common/editor-panel.component.scss | 1 + .../home/pages/mobile/common/editor-panel.component.ts | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.ts b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.ts index 9a60c87959..c862a48042 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.ts +++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.ts @@ -270,8 +270,8 @@ export class MobilePageItemRowComponent implements ControlValueAccessor, OnInit, private updateModel() { this.modelValue.visible = this.mobilePageRowForm.get('visible').value; - const label = this.mobilePageRowForm.get('label').value.trim(); - if (label) { + const label = this.mobilePageRowForm.get('label').value; + if (label?.trim()) { this.modelValue.label = label; } else { delete this.modelValue.label; diff --git a/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.scss b/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.scss index 00a2ebca2d..87dd67e8e0 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.scss +++ b/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.scss @@ -32,6 +32,7 @@ } .tb-editor { height: 400px; + overflow-y: auto; } .tb-editor-buttons { height: 40px; diff --git a/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.ts b/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.ts index 15dff508c8..92ab23f3c2 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.ts +++ b/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.ts @@ -59,7 +59,7 @@ export class EditorPanelComponent implements OnInit { resize: false, setup: (editor) => { editor.on('PostRender', function() { - const container = editor.getContainer(); + const container = editor.getContainer().closest('.tb-popover-content'); const uiContainer = document.querySelector('.tox.tox-tinymce-aux'); container.parentNode.appendChild(uiContainer); }); From 272a1aa1b165e095d3bbcec7daef5f3c68e0f0d5 Mon Sep 17 00:00:00 2001 From: nick Date: Wed, 11 Dec 2024 18:23:15 +0200 Subject: [PATCH 54/75] coaps: x509 - dtls add: DTLS_MAX_FRAGMENT_LENGTH, DTLS_MAX_TRANSMISSION_UNIT --- .../src/main/resources/thingsboard.yml | 4 +++ .../server/coapserver/TbCoapDtlsSettings.java | 28 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index f642d7fea5..5b53339346 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -1302,6 +1302,10 @@ coap: # - A value between 0 and <= 4: SingleNodeConnectionIdGenerator is used # - A value that are > 4: MultiNodeConnectionIdGenerator is used connection_id_length: "${COAP_DTLS_CONNECTION_ID_LENGTH:}" + # Specify the MTU (Maximum Transmission Unit). + max_transmission_unit: "${COAP_DTLS_MAX_TRANSMISSION_UNIT:1024}" + # DTLS maximum fragment length (RFC 6066) + max_fragment_length: "${COAP_DTLS_MAX_FRAGMENT_LENGTH:1024}" # Server DTLS credentials credentials: # Server credentials type (PEM - pem certificate file; KEYSTORE - java keystore) diff --git a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java index f83a20b139..9d3f191f48 100644 --- a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java +++ b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java @@ -21,6 +21,7 @@ import org.eclipse.californium.elements.util.SslContextUtil; import org.eclipse.californium.scandium.config.DtlsConnectorConfig; import org.eclipse.californium.scandium.dtls.CertificateType; +import org.eclipse.californium.scandium.dtls.MaxFragmentLengthExtension.Length; import org.eclipse.californium.scandium.dtls.x509.SingleCertificateProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -44,6 +45,8 @@ import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_CLIENT_AUTHENTICATION_MODE; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_CONNECTION_ID_LENGTH; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_CONNECTION_ID_NODE_ID; +import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_MAX_FRAGMENT_LENGTH; +import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_MAX_TRANSMISSION_UNIT; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_RETRANSMISSION_TIMEOUT; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_ROLE; import static org.eclipse.californium.scandium.config.DtlsConfig.DtlsRole.SERVER_ONLY; @@ -66,6 +69,12 @@ public class TbCoapDtlsSettings { @Value("${coap.dtls.connection_id_length:}") private Integer cIdLength; + @Value("${coap.dtls.max_transmission_unit:}") + private Integer maxTransmissionUnit; + + @Value("${coap.dtls.max_fragment_length:}") + private Integer maxFragmentLength; + @Bean @ConfigurationProperties(prefix = "coap.dtls.credentials") public SslCredentialsConfig coapDtlsCredentials() { @@ -108,6 +117,15 @@ public DtlsConnectorConfig dtlsConnectorConfig(Configuration configuration) thro configBuilder.set(DTLS_CONNECTION_ID_NODE_ID, null); } } + if (maxTransmissionUnit != null) { + configBuilder.set(DTLS_MAX_TRANSMISSION_UNIT, maxTransmissionUnit); + } + if (maxFragmentLength != null) { + Length length = fromLength(maxFragmentLength); + if (length != null) { + configBuilder.set(DTLS_MAX_FRAGMENT_LENGTH, fromLength(maxFragmentLength)); + } + } configBuilder.setAdvancedCertificateVerifier( new TbCoapDtlsCertificateVerifier( transportService, @@ -127,4 +145,14 @@ private InetSocketAddress getInetSocketAddress() throws UnknownHostException { return new InetSocketAddress(addr, port); } + + private static Length fromLength(int length) { + for (Length l : Length.values()) { + if (l.length() == length) { + return l; + } + } + return null; + } } + From 66c9bca5a0901d37cb247b53c952a5c466462936 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 11 Dec 2024 13:21:07 +0200 Subject: [PATCH 55/75] UI: Fixed incorrect copy widget in dashboard --- .../core/services/dashboard-utils.service.ts | 59 +++++++++++++++++++ .../dashboard/dashboard.component.ts | 8 ++- .../home/models/dashboard-component.models.ts | 3 + 3 files changed, 67 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/core/services/dashboard-utils.service.ts b/ui-ngx/src/app/core/services/dashboard-utils.service.ts index 235924f98f..7e5009f199 100644 --- a/ui-ngx/src/app/core/services/dashboard-utils.service.ts +++ b/ui-ngx/src/app/core/services/dashboard-utils.service.ts @@ -60,6 +60,7 @@ import { BackgroundType, colorBackground, isBackgroundSettings } from '@shared/m import { MediaBreakpoints } from '@shared/models/constants'; import { TranslateService } from '@ngx-translate/core'; import { DashboardPageLayout } from '@home/components/dashboard-page/dashboard-page.models'; +import { maxGridsterCol, maxGridsterRow } from '@home/models/dashboard-component.models'; @Injectable({ providedIn: 'root' @@ -682,6 +683,10 @@ export class DashboardUtilsService { if (row > -1 && column > - 1) { widgetLayout.row = row; widgetLayout.col = column; + if (this.hasWidgetCollision(widgetLayout.row, widgetLayout.col, + widgetLayout.sizeX, widgetLayout.sizeY, Object.values(layout.widgets))) { + this.widgetPossiblePosition(widgetLayout, layout); + } } else { row = 0; for (const w of Object.keys(layout.widgets)) { @@ -703,6 +708,60 @@ export class DashboardUtilsService { layout.widgets[widget.id] = widgetLayout; } + private widgetPossiblePosition(widgetLayout: WidgetLayout, layout: DashboardLayout) { + let bestRow = 0; + let bestCol = 0; + + let maxCol = layout.gridSettings.minColumns || layout.gridSettings.columns || 0; + let maxRow = 0; + + const widgetLayouts = Object.values(layout.widgets); + + widgetLayouts.forEach(widget => { + maxCol = Math.max(maxCol, widget.col + widget.sizeX); + maxRow = Math.max(maxRow, widget.row + widget.sizeY); + }) + + for (; bestRow < maxRow; bestRow++) { + for (bestCol = 0; bestCol < maxCol; bestCol++) { + if (!this.hasWidgetCollision(bestRow, bestCol, widgetLayout.sizeX, widgetLayout.sizeY, widgetLayouts)) { + widgetLayout.row = bestRow; + widgetLayout.col = bestCol; + return; + } + } + } + const canAddToRows = maxGridsterRow >= maxRow + bestRow; + const canAddToColumns = maxGridsterCol >= maxCol + bestCol; + const addToRows = bestRow <= bestCol && canAddToRows; + if (!addToRows && canAddToColumns) { + widgetLayout.col = maxCol; + widgetLayout.row = 0; + } else if (canAddToRows) { + widgetLayout.row = maxRow; + widgetLayout.col = 0; + } + } + + private hasWidgetCollision(row: number, col: number, sizeX: number, sizeY: number, widgetLayouts: WidgetLayout[]) { + const left = col; + const right = col + sizeX; + const top = row; + const bottom = row + sizeY; + + for (const widget of widgetLayouts) { + const left2 = widget.col; + const right2 = widget.col + widget.sizeX; + const top2 = widget.row; + const bottom2 = widget.row + widget.sizeY; + + if (left < right2 && right > left2 && top < bottom2 && bottom > top2) { + return true; + } + } + return false; + } + public removeWidgetFromLayout(dashboard: Dashboard, targetState: string, targetLayout: DashboardLayoutId, diff --git a/ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.ts b/ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.ts index 7169e4a20b..d473a6bb67 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.ts +++ b/ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.ts @@ -41,7 +41,9 @@ import { DashboardCallbacks, DashboardWidget, DashboardWidgets, - IDashboardComponent + IDashboardComponent, + maxGridsterCol, + maxGridsterRow } from '../../models/dashboard-component.models'; import { ReplaySubject, Subject, Subscription } from 'rxjs'; import { WidgetLayout, WidgetLayouts } from '@shared/models/dashboard.models'; @@ -231,10 +233,10 @@ export class DashboardComponent extends PageComponent implements IDashboardCompo disableAutoPositionOnConflict: false, pushItems: false, swap: false, - maxRows: 3000, + maxRows: maxGridsterRow, minCols: this.columns ? this.columns : 24, setGridSize: this.setGridSize, - maxCols: 3000, + maxCols: maxGridsterCol, maxItemCols: 1000, maxItemRows: 1000, maxItemArea: 1000000, diff --git a/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts b/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts index 12c00597d5..0de6161e27 100644 --- a/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts +++ b/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts @@ -111,6 +111,9 @@ interface DashboardWidgetUpdateRecord { operation: DashboardWidgetUpdateOperation; } +export const maxGridsterCol = 3000; +export const maxGridsterRow = 3000; + export class DashboardWidgets implements Iterable { highlightedMode = false; From b1179dc4d498b5a0d30eab583cd63c08d6e50b09 Mon Sep 17 00:00:00 2001 From: Ekaterina Chantsova Date: Wed, 11 Dec 2024 19:16:25 +0200 Subject: [PATCH 56/75] Timewindow: fix applying default grouping interval --- .../time/timewindow-config-dialog.component.html | 10 +++++----- .../time/timewindow-config-dialog.component.ts | 16 ++++++++-------- .../time/timewindow-panel.component.ts | 16 ++++++++-------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.html b/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.html index 3574aff0b1..ad71e30f28 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.html +++ b/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.html @@ -68,7 +68,7 @@

{{ 'timewindow.timewindow-settings' | translate }}

timewindowForm.get('realtime.realtimeType').value === realtimeTypes.LAST_INTERVAL"> @@ -95,7 +95,7 @@

{{ 'timewindow.timewindow-settings' | translate }}

timewindowForm.get('realtime.realtimeType').value === realtimeTypes.INTERVAL"> @@ -137,7 +137,7 @@

{{ 'timewindow.timewindow-settings' | translate }}

timewindowForm.get('history.historyType').value === historyTypes.LAST_INTERVAL"> @@ -177,7 +177,7 @@

{{ 'timewindow.timewindow-settings' | translate }}

timewindowForm.get('history.historyType').value === historyTypes.INTERVAL"> @@ -199,7 +199,7 @@

{{ 'timewindow.timewindow-settings' | translate }}

formControlName="type" [allowedAggregationTypes]="timewindowForm.get('allowedAggTypes').value"> diff --git a/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.ts b/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.ts index a5a3533b79..a3ab4614e6 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.ts +++ b/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.ts @@ -245,9 +245,9 @@ export class TimewindowConfigDialogComponent extends PageComponent implements On this.timewindowForm.get('realtime.advancedParams.lastAggIntervalsConfig').value; if (lastAggIntervalsConfig?.hasOwnProperty(timewindowMs) && lastAggIntervalsConfig[timewindowMs].defaultAggInterval) { - this.timewindowForm.get('realtime.interval').patchValue( + setTimeout(() => this.timewindowForm.get('realtime.interval').patchValue( lastAggIntervalsConfig[timewindowMs].defaultAggInterval, {emitEvent: false} - ); + )); } }); this.timewindowForm.get('realtime.quickInterval').valueChanges.pipe( @@ -257,9 +257,9 @@ export class TimewindowConfigDialogComponent extends PageComponent implements On this.timewindowForm.get('realtime.advancedParams.quickAggIntervalsConfig').value; if (quickAggIntervalsConfig?.hasOwnProperty(quickInterval) && quickAggIntervalsConfig[quickInterval].defaultAggInterval) { - this.timewindowForm.get('realtime.interval').patchValue( + setTimeout(() => this.timewindowForm.get('realtime.interval').patchValue( quickAggIntervalsConfig[quickInterval].defaultAggInterval, {emitEvent: false} - ); + )); } }); this.timewindowForm.get('history.timewindowMs').valueChanges.pipe( @@ -269,9 +269,9 @@ export class TimewindowConfigDialogComponent extends PageComponent implements On this.timewindowForm.get('history.advancedParams.lastAggIntervalsConfig').value; if (lastAggIntervalsConfig?.hasOwnProperty(timewindowMs) && lastAggIntervalsConfig[timewindowMs].defaultAggInterval) { - this.timewindowForm.get('history.interval').patchValue( + setTimeout(() => this.timewindowForm.get('history.interval').patchValue( lastAggIntervalsConfig[timewindowMs].defaultAggInterval, {emitEvent: false} - ); + )); } }); this.timewindowForm.get('history.quickInterval').valueChanges.pipe( @@ -281,9 +281,9 @@ export class TimewindowConfigDialogComponent extends PageComponent implements On this.timewindowForm.get('history.advancedParams.quickAggIntervalsConfig').value; if (quickAggIntervalsConfig?.hasOwnProperty(quickInterval) && quickAggIntervalsConfig[quickInterval].defaultAggInterval) { - this.timewindowForm.get('history.interval').patchValue( + setTimeout(() => this.timewindowForm.get('history.interval').patchValue( quickAggIntervalsConfig[quickInterval].defaultAggInterval, {emitEvent: false} - ); + )); } }); diff --git a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts index 14247997ac..d3be3e8449 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts +++ b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts @@ -308,9 +308,9 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O ).subscribe((timewindowMs: number) => { if (this.realtimeAdvancedParams?.lastAggIntervalsConfig?.hasOwnProperty(timewindowMs) && this.realtimeAdvancedParams.lastAggIntervalsConfig[timewindowMs].defaultAggInterval) { - this.timewindowForm.get('realtime.interval').patchValue( + setTimeout(() => this.timewindowForm.get('realtime.interval').patchValue( this.realtimeAdvancedParams.lastAggIntervalsConfig[timewindowMs].defaultAggInterval, {emitEvent: false} - ); + )); } }); this.timewindowForm.get('realtime.quickInterval').valueChanges.pipe( @@ -318,9 +318,9 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O ).subscribe((quickInterval: number) => { if (this.realtimeAdvancedParams?.quickAggIntervalsConfig?.hasOwnProperty(quickInterval) && this.realtimeAdvancedParams.quickAggIntervalsConfig[quickInterval].defaultAggInterval) { - this.timewindowForm.get('realtime.interval').patchValue( + setTimeout(() => this.timewindowForm.get('realtime.interval').patchValue( this.realtimeAdvancedParams.quickAggIntervalsConfig[quickInterval].defaultAggInterval, {emitEvent: false} - ); + )); } }); this.timewindowForm.get('history.timewindowMs').valueChanges.pipe( @@ -328,9 +328,9 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O ).subscribe((timewindowMs: number) => { if (this.historyAdvancedParams?.lastAggIntervalsConfig?.hasOwnProperty(timewindowMs) && this.historyAdvancedParams.lastAggIntervalsConfig[timewindowMs].defaultAggInterval) { - this.timewindowForm.get('history.interval').patchValue( + setTimeout(() => this.timewindowForm.get('history.interval').patchValue( this.historyAdvancedParams.lastAggIntervalsConfig[timewindowMs].defaultAggInterval, {emitEvent: false} - ); + )); } }); this.timewindowForm.get('history.quickInterval').valueChanges.pipe( @@ -338,9 +338,9 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O ).subscribe((quickInterval: number) => { if (this.historyAdvancedParams?.quickAggIntervalsConfig?.hasOwnProperty(quickInterval) && this.historyAdvancedParams.quickAggIntervalsConfig[quickInterval].defaultAggInterval) { - this.timewindowForm.get('history.interval').patchValue( + setTimeout(() => this.timewindowForm.get('history.interval').patchValue( this.historyAdvancedParams.quickAggIntervalsConfig[quickInterval].defaultAggInterval, {emitEvent: false} - ); + )); } }); From 19e0c11b6912dfac2f0b552179ae393eaf51fbf7 Mon Sep 17 00:00:00 2001 From: nick Date: Thu, 12 Dec 2024 11:52:48 +0200 Subject: [PATCH 57/75] coaps: x509 - dtls add: DTLS_MAX_FRAGMENT_LENGTH, DTLS_MAX_TRANSMISSION_UNIT add to microservice --- transport/coap/src/main/resources/tb-coap-transport.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/transport/coap/src/main/resources/tb-coap-transport.yml b/transport/coap/src/main/resources/tb-coap-transport.yml index 2c1c0550e1..865b85c985 100644 --- a/transport/coap/src/main/resources/tb-coap-transport.yml +++ b/transport/coap/src/main/resources/tb-coap-transport.yml @@ -198,6 +198,10 @@ coap: # - A value between 0 and <= 4: SingleNodeConnectionIdGenerator is used # - A value that are > 4: MultiNodeConnectionIdGenerator is used connection_id_length: "${COAP_DTLS_CONNECTION_ID_LENGTH:}" + # Specify the MTU (Maximum Transmission Unit). + max_transmission_unit: "${COAP_DTLS_MAX_TRANSMISSION_UNIT:1024}" + # DTLS maximum fragment length (RFC 6066) + max_fragment_length: "${COAP_DTLS_MAX_FRAGMENT_LENGTH:1024}" # Server DTLS credentials credentials: # Server credentials type (PEM - pem certificate file; KEYSTORE - java keystore) From dc316ec10dcc035e6fadbcf6977a6415ad79ba3f Mon Sep 17 00:00:00 2001 From: nick Date: Thu, 12 Dec 2024 17:38:08 +0200 Subject: [PATCH 58/75] coaps: x509 - dtls reuse length variable --- .../org/thingsboard/server/coapserver/TbCoapDtlsSettings.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java index 9d3f191f48..6ebc54323d 100644 --- a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java +++ b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java @@ -123,7 +123,7 @@ public DtlsConnectorConfig dtlsConnectorConfig(Configuration configuration) thro if (maxFragmentLength != null) { Length length = fromLength(maxFragmentLength); if (length != null) { - configBuilder.set(DTLS_MAX_FRAGMENT_LENGTH, fromLength(maxFragmentLength)); + configBuilder.set(DTLS_MAX_FRAGMENT_LENGTH, length); } } configBuilder.setAdvancedCertificateVerifier( From 4cfe7441b1ab451833d147dcf1ee283911eeb85e Mon Sep 17 00:00:00 2001 From: nick Date: Thu, 12 Dec 2024 19:30:50 +0200 Subject: [PATCH 59/75] coaps: x509 - dtls add default values here (1024) --- .../org/thingsboard/server/coapserver/TbCoapDtlsSettings.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java index 6ebc54323d..063494d630 100644 --- a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java +++ b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java @@ -69,10 +69,10 @@ public class TbCoapDtlsSettings { @Value("${coap.dtls.connection_id_length:}") private Integer cIdLength; - @Value("${coap.dtls.max_transmission_unit:}") + @Value("${coap.dtls.max_transmission_unit:1024}") private Integer maxTransmissionUnit; - @Value("${coap.dtls.max_fragment_length:}") + @Value("${coap.dtls.max_fragment_length:1024}") private Integer maxFragmentLength; @Bean From 9f6bbba7c3457c2cd688710e7466312a70afc80d Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 13 Dec 2024 10:43:54 +0200 Subject: [PATCH 60/75] UI: Change mobile-center help link --- ui-ngx/src/app/shared/models/constants.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/shared/models/constants.ts b/ui-ngx/src/app/shared/models/constants.ts index 6aafef4b83..9bdc7c3c23 100644 --- a/ui-ngx/src/app/shared/models/constants.ts +++ b/ui-ngx/src/app/shared/models/constants.ts @@ -193,8 +193,8 @@ export const HelpLinks = { scadaSymbolDev: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/scada/scada-symbols-dev-guide/`, scadaSymbolDevAnimation: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/scada/scada-symbols-dev-guide/#scadasymbolanimation`, mobileApplication: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/ui/mobile-qr-code/`, - mobileBundle: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/ui/mobile-qr-code/`, - mobileQrCode: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/ui/mobile-qr-code/`, + mobileBundle: `${helpBaseUrl}/docs${docPlatformPrefix}/mobile-center/mobile-center/`, + mobileQrCode: `${helpBaseUrl}/docs${docPlatformPrefix}/mobile-center/applications/`, } }; /* eslint-enable max-len */ From 50e69681706ddaf25e63f09b77eea789c8f9e836 Mon Sep 17 00:00:00 2001 From: nick Date: Fri, 13 Dec 2024 12:13:11 +0200 Subject: [PATCH 61/75] coaps: x509 - dtls add default values here (1024) and in yml add note --- .../src/main/resources/thingsboard.yml | 22 ++++++++++++++++++- .../server/coapserver/TbCoapDtlsSettings.java | 4 ++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 5b53339346..4d69afedb2 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -1303,8 +1303,28 @@ coap: # - A value that are > 4: MultiNodeConnectionIdGenerator is used connection_id_length: "${COAP_DTLS_CONNECTION_ID_LENGTH:}" # Specify the MTU (Maximum Transmission Unit). + # Should be used if LAN MTU is not used, e.g. if IP tunnels are used or if the client uses a smaller value than the LAN MTU. + # Default = 1024 + # Minimum value = 64 + # If set to 0 - LAN MTU is used. max_transmission_unit: "${COAP_DTLS_MAX_TRANSMISSION_UNIT:1024}" - # DTLS maximum fragment length (RFC 6066) + # DTLS maximum fragment length (RFC 6066, Section 4). + # Default = 1024 + # Possible values: 512, 1024, 2048, 4096. + # If set to 0, the default maximum fragment size of 2^14 bytes (16,384 bytes) is used. + # Without this extension, TLS specifies a fixed maximum plaintext fragment length of 2^14 bytes. + # It may be desirable for constrained clients to negotiate a smaller maximum fragment length due to memory limitations or bandwidth limitations. + # In order to negotiate smaller maximum fragment lengths, + # clients MAY include an extension of type "max_fragment_length" in the (extended) client hello. + # The "extension_data" field of this extension SHALL contain: + # enum { + # 2^9(1) == 512, + # 2^10(2) == 1024, + # 2^11(3) == 2048, + # 2^12(4) == 4096, + # (255) + # } MaxFragmentLength; + # TLS already requires clients and servers to support fragmentation of handshake messages. max_fragment_length: "${COAP_DTLS_MAX_FRAGMENT_LENGTH:1024}" # Server DTLS credentials credentials: diff --git a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java index 063494d630..2f0127643a 100644 --- a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java +++ b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java @@ -117,10 +117,10 @@ public DtlsConnectorConfig dtlsConnectorConfig(Configuration configuration) thro configBuilder.set(DTLS_CONNECTION_ID_NODE_ID, null); } } - if (maxTransmissionUnit != null) { + if (maxTransmissionUnit > 0) { configBuilder.set(DTLS_MAX_TRANSMISSION_UNIT, maxTransmissionUnit); } - if (maxFragmentLength != null) { + if (maxFragmentLength > 0) { Length length = fromLength(maxFragmentLength); if (length != null) { configBuilder.set(DTLS_MAX_FRAGMENT_LENGTH, length); From 75e64f7f95c27d3dd9e3dcca40e012cd91f20a54 Mon Sep 17 00:00:00 2001 From: nick Date: Fri, 13 Dec 2024 12:18:09 +0200 Subject: [PATCH 62/75] coaps: x509 - dtls add default values here (1024) and in yml add note --- .../src/main/resources/tb-coap-transport.yml | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/transport/coap/src/main/resources/tb-coap-transport.yml b/transport/coap/src/main/resources/tb-coap-transport.yml index 865b85c985..0c7ff4e43e 100644 --- a/transport/coap/src/main/resources/tb-coap-transport.yml +++ b/transport/coap/src/main/resources/tb-coap-transport.yml @@ -199,8 +199,28 @@ coap: # - A value that are > 4: MultiNodeConnectionIdGenerator is used connection_id_length: "${COAP_DTLS_CONNECTION_ID_LENGTH:}" # Specify the MTU (Maximum Transmission Unit). + # Should be used if LAN MTU is not used, e.g. if IP tunnels are used or if the client uses a smaller value than the LAN MTU. + # Default = 1024 + # Minimum value = 64 + # If set to 0 - LAN MTU is used. max_transmission_unit: "${COAP_DTLS_MAX_TRANSMISSION_UNIT:1024}" - # DTLS maximum fragment length (RFC 6066) + # DTLS maximum fragment length (RFC 6066, Section 4). + # Default = 1024 + # Possible values: 512, 1024, 2048, 4096. + # If set to 0, the default maximum fragment size of 2^14 bytes (16,384 bytes) is used. + # Without this extension, TLS specifies a fixed maximum plaintext fragment length of 2^14 bytes. + # It may be desirable for constrained clients to negotiate a smaller maximum fragment length due to memory limitations or bandwidth limitations. + # In order to negotiate smaller maximum fragment lengths, + # clients MAY include an extension of type "max_fragment_length" in the (extended) client hello. + # The "extension_data" field of this extension SHALL contain: + # enum { + # 2^9(1) == 512, + # 2^10(2) == 1024, + # 2^11(3) == 2048, + # 2^12(4) == 4096, + # (255) + # } MaxFragmentLength; + # TLS already requires clients and servers to support fragmentation of handshake messages. max_fragment_length: "${COAP_DTLS_MAX_FRAGMENT_LENGTH:1024}" # Server DTLS credentials credentials: From b401a487f103d9d8a4336c3e538e470ae62e4ad8 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 13 Dec 2024 12:37:42 +0200 Subject: [PATCH 63/75] UI[Mobile center]: Add layout label only space validation and improved translation key --- .../layout/custom-mobile-page.component.html | 20 +++++++++++++++++-- .../layout/custom-mobile-page.component.ts | 2 +- .../mobile-page-item-row.component.html | 8 ++++++++ .../layout/mobile-page-item-row.component.ts | 2 +- .../assets/locale/locale.constant-en_US.json | 5 +++-- 5 files changed, 31 insertions(+), 6 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/custom-mobile-page.component.html b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/custom-mobile-page.component.html index f543126660..1c89c7e06b 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/custom-mobile-page.component.html +++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/custom-mobile-page.component.html @@ -30,6 +30,22 @@ mobile.page-name + + warning + + + warning +
@@ -59,7 +75,7 @@ - {{ 'mobile.url-pattern' | translate }} + {{ 'mobile.invalid-url-format' | translate }} @@ -69,7 +85,7 @@ - {{ 'mobile.path-pattern' | translate }} + {{ 'mobile.invalid-path-format' | translate }} diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/custom-mobile-page.component.ts b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/custom-mobile-page.component.ts index 5ba2ee3c6f..3075ae772b 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/custom-mobile-page.component.ts +++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/custom-mobile-page.component.ts @@ -60,7 +60,7 @@ export class CustomMobilePageComponent implements ControlValueAccessor, Validato customMobilePageForm = this.fb.group({ visible: [true], icon: ['star'], - label: ['', Validators.required], + label: ['', [Validators.required, Validators.pattern(/\S/)]], type: [MobilePageType.DASHBOARD], dashboardId: this.fb.control(null, Validators.required), url: [{value:'', disabled: true}, [Validators.required, Validators.pattern(/^(https?:\/\/)?(localhost|([\w\-]+\.)+[\w\-]+)(:\d+)?(\/[\w\-._~:\/?#[\]@!$&'()*+,;=%]*)?$/)]], diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.html b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.html index eeec353103..6ece48086b 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.html +++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.html @@ -42,6 +42,14 @@ class="tb-error"> warning + + warning + diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.ts b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.ts index c862a48042..7083d98860 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.ts +++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.ts @@ -99,7 +99,7 @@ export class MobilePageItemRowComponent implements ControlValueAccessor, OnInit, mobilePageRowForm = this.fb.group({ visible: [true, []], icon: ['', []], - label: ['', []], + label: ['', [Validators.pattern(/\S/)]], type: [MobilePageType.DEFAULT] }); diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index e7157222de..dd73c4776c 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -3551,6 +3551,7 @@ "max-element-number": "Max elements number", "page-name": "Page name", "page-name-required": "Page name is required.", + "page-name-cannot-contain-only-spaces": "Page name cannot contain only spaces.", "page-type": "Page type", "pages-types": { "dashboard": "Dashboard", @@ -3558,9 +3559,9 @@ "custom": "Custom" }, "url": "URL", - "url-pattern": "Invalid URL", + "invalid-url-format": "Invalid URL format", "path": "Path", - "path-pattern": "Invalid path", + "invalid-path-format": "Invalid path format", "custom-page": "Custom page", "edit-page": "Edit page", "edit-custom-page": "Edit custom page", From efb0cd2681f7812118b9f1ab7253c77de31857bc Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Fri, 13 Dec 2024 17:42:01 +0200 Subject: [PATCH 64/75] fixed mobile redirect if ios is disabled in properties --- .../thingsboard/server/controller/QrCodeSettingsController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/QrCodeSettingsController.java b/application/src/main/java/org/thingsboard/server/controller/QrCodeSettingsController.java index 3e3ffe9f20..76369e7ccd 100644 --- a/application/src/main/java/org/thingsboard/server/controller/QrCodeSettingsController.java +++ b/application/src/main/java/org/thingsboard/server/controller/QrCodeSettingsController.java @@ -176,7 +176,7 @@ public ResponseEntity getApplicationRedirect(@RequestHeader(value = "User-Age return ResponseEntity.status(HttpStatus.FOUND) .header("Location", googlePlayLink) .build(); - } else if (userAgent.contains("iPhone") || userAgent.contains("iPad") && qrCodeSettings.isIosEnabled()) { + } else if ((userAgent.contains("iPhone") || userAgent.contains("iPad")) && qrCodeSettings.isIosEnabled()) { String appStoreLink = qrCodeSettings.getAppStoreLink(); return ResponseEntity.status(HttpStatus.FOUND) .header("Location", appStoreLink) From 5cd0f0ceb95bf2efdb6931d9f0e5de1a790a268f Mon Sep 17 00:00:00 2001 From: Ekaterina Chantsova Date: Fri, 13 Dec 2024 17:44:36 +0200 Subject: [PATCH 65/75] Fixed truncateWithTooltip directive behaviour on different mouse events --- .../truncate-with-tooltip.directive.ts | 75 ++++++------------- 1 file changed, 21 insertions(+), 54 deletions(-) diff --git a/ui-ngx/src/app/shared/directives/truncate-with-tooltip.directive.ts b/ui-ngx/src/app/shared/directives/truncate-with-tooltip.directive.ts index d37841be6f..9d8f3599fa 100644 --- a/ui-ngx/src/app/shared/directives/truncate-with-tooltip.directive.ts +++ b/ui-ngx/src/app/shared/directives/truncate-with-tooltip.directive.ts @@ -14,25 +14,18 @@ /// limitations under the License. /// -import { - AfterViewInit, - Directive, - ElementRef, - Input, - OnDestroy, - OnInit, - Renderer2, -} from '@angular/core'; -import { fromEvent, Subject } from 'rxjs'; -import { filter, takeUntil, tap } from 'rxjs/operators'; +import { AfterViewInit, Directive, ElementRef, Input, OnInit, Renderer2 } from '@angular/core'; import { MatTooltip, TooltipPosition } from '@angular/material/tooltip'; import { coerceBoolean } from '@shared/decorators/coercion'; @Directive({ selector: '[tbTruncateWithTooltip]', - providers: [MatTooltip], + hostDirectives: [{ + directive: MatTooltip, + inputs: ['matTooltipClass', 'matTooltipTouchGestures'], + }] }) -export class TruncateWithTooltipDirective implements OnInit, AfterViewInit, OnDestroy { +export class TruncateWithTooltipDirective implements OnInit, AfterViewInit { @Input('tbTruncateWithTooltip') text: string; @@ -44,48 +37,31 @@ export class TruncateWithTooltipDirective implements OnInit, AfterViewInit, OnDe @Input() position: TooltipPosition = 'above'; - private destroy$ = new Subject(); - constructor( - private elementRef: ElementRef, + private elementRef: ElementRef, private renderer: Renderer2, private tooltip: MatTooltip ) {} ngOnInit(): void { - this.observeMouseEvents(); this.applyTruncationStyles(); - } - - ngAfterViewInit(): void { this.tooltip.position = this.position; + this.showTooltipOnOverflow(this); } - ngOnDestroy(): void { - if (this.tooltip._isTooltipVisible()) { - this.hideTooltip(); - } - this.destroy$.next(); - this.destroy$.complete(); + ngAfterViewInit(): void { + this.tooltip.message = this.text || this.elementRef.nativeElement.innerText; } - private observeMouseEvents(): void { - fromEvent(this.elementRef.nativeElement, 'mouseenter') - .pipe( - filter(() => this.tooltipEnabled), - filter(() => this.isOverflown(this.elementRef.nativeElement)), - tap(() => this.showTooltip()), - takeUntil(this.destroy$), - ) - .subscribe(); - fromEvent(this.elementRef.nativeElement, 'mouseleave') - .pipe( - filter(() => this.tooltipEnabled), - filter(() => this.tooltip._isTooltipVisible()), - tap(() => this.hideTooltip()), - takeUntil(this.destroy$), - ) - .subscribe(); + private showTooltipOnOverflow(ctx: TruncateWithTooltipDirective) { + ctx.tooltip.show = (function(old) { + function extendsFunction() { + if (ctx.tooltipEnabled && ctx.isOverflown()) { + old.apply(ctx.tooltip, arguments); + } + } + return extendsFunction; + })(ctx.tooltip.show); } private applyTruncationStyles(): void { @@ -94,16 +70,7 @@ export class TruncateWithTooltipDirective implements OnInit, AfterViewInit, OnDe this.renderer.setStyle(this.elementRef.nativeElement, 'text-overflow', 'ellipsis'); } - private isOverflown(element: HTMLElement): boolean { - return element.clientWidth < element.scrollWidth; - } - - private showTooltip(): void { - this.tooltip.message = this.text || this.elementRef.nativeElement.innerText; - this.tooltip.show(); - } - - private hideTooltip(): void { - this.tooltip.hide(); + private isOverflown(): boolean { + return this.elementRef.nativeElement.clientWidth < this.elementRef.nativeElement.scrollWidth; } } From fc905e4e098afd000b7b5050bd0e577913a063b7 Mon Sep 17 00:00:00 2001 From: Daria Shevchenko <116559345+dashevchenko@users.noreply.github.com> Date: Mon, 16 Dec 2024 11:45:57 +0200 Subject: [PATCH 66/75] Added domain validation (#12248) * added domain validation * deleted empty line * fixed controller test * deleted redundant check --- .../server/dao/domain/DomainServiceImpl.java | 4 +++ .../server/dao/service/DataValidator.java | 15 ++++++++- .../validator/DomainDataValidator.java | 32 +++++++++++++++++++ .../server/dao/service/DomainServiceTest.java | 2 +- 4 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 dao/src/main/java/org/thingsboard/server/dao/service/validator/DomainDataValidator.java diff --git a/dao/src/main/java/org/thingsboard/server/dao/domain/DomainServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/domain/DomainServiceImpl.java index 8a735303a7..d349d2d59b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/domain/DomainServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/domain/DomainServiceImpl.java @@ -35,6 +35,7 @@ import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent; import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent; import org.thingsboard.server.dao.oauth2.OAuth2ClientDao; +import org.thingsboard.server.dao.service.validator.DomainDataValidator; import java.util.Comparator; import java.util.List; @@ -53,11 +54,14 @@ public class DomainServiceImpl extends AbstractEntityService implements DomainSe private OAuth2ClientDao oauth2ClientDao; @Autowired private DomainDao domainDao; + @Autowired + private DomainDataValidator domainDataValidator; @Override public Domain saveDomain(TenantId tenantId, Domain domain) { log.trace("Executing saveDomain [{}]", domain); try { + domainDataValidator.validate(domain, Domain::getTenantId); Domain savedDomain = domainDao.save(tenantId, domain); eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(tenantId).entityId(savedDomain.getId()).entity(savedDomain).build()); return savedDomain; diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java index aff7af8165..1321fa5081 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java @@ -43,7 +43,10 @@ public abstract class DataValidator> { Pattern.compile("^[A-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[A-Z0-9.-]+\\.[A-Z]{2,}$", Pattern.CASE_INSENSITIVE); private static final Pattern QUEUE_PATTERN = Pattern.compile("^[a-zA-Z0-9_.\\-]+$"); - + private static final String DOMAIN_REGEX = "^(((?!-))(xn--|_)?[a-z0-9-]{0,61}[a-z0-9]{1,1}\\.)*(xn--)?([a-z0-9][a-z0-9\\-]{0,60}|[a-z0-9-]{1,30}\\.[a-z]{2,})$"; + private static final Pattern DOMAIN_PATTERN = Pattern.compile(DOMAIN_REGEX); + private static final String LOCALHOST_REGEX = "^localhost(:\\d{1,5})?$"; + private static final Pattern LOCALHOST_PATTERN = Pattern.compile(LOCALHOST_REGEX); private static final String NAME = "name"; private static final String TOPIC = "topic"; @@ -171,4 +174,14 @@ static void validateQueueNameOrTopic(String value, String fieldName) { } } + public static boolean isValidDomain(String domainName) { + if (domainName == null) { + return false; + } + if (LOCALHOST_PATTERN.matcher(domainName).matches()) { + return true; + } + return DOMAIN_PATTERN.matcher(domainName).matches(); + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/DomainDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/DomainDataValidator.java new file mode 100644 index 0000000000..9a9cf99d0e --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/DomainDataValidator.java @@ -0,0 +1,32 @@ +/** + * Copyright © 2016-2024 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.domain.Domain; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.exception.IncorrectParameterException; + +@Component +public class DomainDataValidator extends AbstractHasOtaPackageValidator { + + @Override + protected void validateDataImpl(TenantId tenantId, Domain domain) { + if (!isValidDomain(domain.getName())) { + throw new IncorrectParameterException("Domain name " + domain.getName() + " is invalid"); + } + } +} diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/DomainServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/DomainServiceTest.java index 734e30137a..c50991ea87 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/DomainServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/DomainServiceTest.java @@ -90,7 +90,7 @@ public void testSaveDomain() { public void testGetTenantDomains() { List domains = new ArrayList<>(); for (int i = 0; i < 5; i++) { - Domain oAuth2Client = constructDomain(TenantId.SYS_TENANT_ID, StringUtils.randomAlphabetic(5), true, false); + Domain oAuth2Client = constructDomain(TenantId.SYS_TENANT_ID, StringUtils.randomAlphabetic(5).toLowerCase(), true, false); Domain savedOauth2Client = domainService.saveDomain(SYSTEM_TENANT_ID, oAuth2Client); domains.add(savedOauth2Client); } From c3652bac5fe5ac6d2dd37dc42c103788f0933527 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Mon, 16 Dec 2024 12:24:39 +0200 Subject: [PATCH 67/75] UI[Mobile center]: Updated link getting started in flutter doc --- .../bundes/mobile-app-configuration-dialog.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-app-configuration-dialog.component.html b/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-app-configuration-dialog.component.html index 25315fca53..4497ece277 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-app-configuration-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-app-configuration-dialog.component.html @@ -72,7 +72,7 @@

mobile.configuration-dialog

mobile.configuration-step.more-information
rocket_launch{{ 'mobile.configuration-step.getting-started' | translate }} From 124d3bfc2758617dfc424bc518e8c1a554b78f9d Mon Sep 17 00:00:00 2001 From: mpetrov Date: Mon, 16 Dec 2024 15:10:43 +0200 Subject: [PATCH 68/75] Debug settings minor fixes and adjustments --- ...entity-debug-settings-button.component.html | 4 ++-- .../entity-debug-settings-button.component.ts | 18 ++++++++++-------- .../entity-debug-settings-panel.component.html | 14 +++++++------- .../entity-debug-settings-panel.component.ts | 1 + .../rulechain/rule-node-details.component.html | 1 + .../assets/locale/locale.constant-en_US.json | 5 +++-- 6 files changed, 24 insertions(+), 19 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-button.component.html b/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-button.component.html index 81563f4bf8..8bc399974c 100644 --- a/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-button.component.html +++ b/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-button.component.html @@ -24,8 +24,8 @@ (click)="openDebugStrategyPanel($event, matButton)"> bug_report @if (isDebugAllActive$ | async) { - {{ !allEnabled() ? (allEnabledUntil | durationLeft) : (maxDebugModeDuration | milliSecondsToTimeString: true : true) }} + {{ (allEnabled$ | async) === false ? (allEnabledUntil | durationLeft) : (maxDebugModeDuration | milliSecondsToTimeString: true : true) }} } @else { - {{ (failuresEnabled ? 'debug-config.failures' : 'common.disabled') | translate }} + {{ (failuresEnabled ? 'debug-settings.failures' : 'common.disabled') | translate }} } diff --git a/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-button.component.ts b/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-button.component.ts index 932658f9bf..e46112dd9c 100644 --- a/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-button.component.ts +++ b/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-button.component.ts @@ -21,7 +21,6 @@ import { forwardRef, Input, Renderer2, - signal, ViewContainerRef } from '@angular/core'; import { CommonModule } from '@angular/common'; @@ -30,8 +29,8 @@ import { DurationLeftPipe } from '@shared/pipe/duration-left.pipe'; import { TbPopoverService } from '@shared/components/popover.service'; import { MatButton } from '@angular/material/button'; import { EntityDebugSettingsPanelComponent } from './entity-debug-settings-panel.component'; -import { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop'; -import { of, shareReplay, timer } from 'rxjs'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { BehaviorSubject, of, shareReplay, timer } from 'rxjs'; import { SECOND, MINUTE } from '@shared/models/time/time.models'; import { EntityDebugSettings } from '@shared/models/entity.models'; import { map, switchMap, takeWhile } from 'rxjs/operators'; @@ -61,6 +60,7 @@ import { ControlValueAccessor, FormBuilder, NG_VALUE_ACCESSOR } from '@angular/f export class EntityDebugSettingsButtonComponent implements ControlValueAccessor { @Input() debugLimitsConfiguration: string; + @Input() entityLabel = 'entity'; debugSettingsFormGroup = this.fb.group({ failuresEnabled: [false], @@ -69,9 +69,10 @@ export class EntityDebugSettingsButtonComponent implements ControlValueAccessor }); disabled = false; - allEnabled = signal(false); + private allEnabledSubject = new BehaviorSubject(false); + allEnabled$ = this.allEnabledSubject.asObservable(); - isDebugAllActive$ = toObservable(this.allEnabled).pipe( + isDebugAllActive$ = this.allEnabled$.pipe( switchMap((value) => { if (value) { return of(true); @@ -105,7 +106,7 @@ export class EntityDebugSettingsButtonComponent implements ControlValueAccessor this.debugSettingsFormGroup.get('allEnabled').valueChanges.pipe( takeUntilDestroyed() - ).subscribe(value => this.allEnabled.set(value)); + ).subscribe(value => this.allEnabledSubject.next(value)); } get failuresEnabled(): boolean { @@ -131,7 +132,8 @@ export class EntityDebugSettingsButtonComponent implements ControlValueAccessor { ...debugSettings, maxDebugModeDuration: this.maxDebugModeDuration, - debugLimitsConfiguration: this.debugLimitsConfiguration + debugLimitsConfiguration: this.debugLimitsConfiguration, + entityLabel: this.entityLabel }, {}, {}, {}, true); @@ -152,7 +154,7 @@ export class EntityDebugSettingsButtonComponent implements ControlValueAccessor writeValue(settings: EntityDebugSettings): void { this.debugSettingsFormGroup.patchValue(settings, {emitEvent: false}); - this.allEnabled.set(settings?.allEnabled); + this.allEnabledSubject.next(settings?.allEnabled); this.debugSettingsFormGroup.get('allEnabled').updateValueAndValidity({onlySelf: true}); } diff --git a/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-panel.component.html b/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-panel.component.html index d47ee69091..27717c0f54 100644 --- a/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-panel.component.html @@ -16,26 +16,26 @@ -->
-
debug-config.label
+
debug-settings.label
@if (debugLimitsConfiguration) { - {{ 'debug-config.hint.main-limited' | translate: { msg: maxMessagesCount, time: (maxTimeFrameDuration | milliSecondsToTimeString: true : true) } }} + {{ 'debug-settings.hint.main-limited' | translate: { entity: entityLabel, msg: maxMessagesCount, time: (maxTimeFrameDuration | milliSecondsToTimeString: true : true) } }} } @else { - {{ 'debug-config.hint.main' | translate }} + {{ 'debug-settings.hint.main' | translate }} }
-
- {{ 'debug-config.on-failure' | translate }} +
+ {{ 'debug-settings.on-failure' | translate }}
-
- {{ 'debug-config.all-messages' | translate: { time: (isDebugAllActive$ | async) && !allEnabled ? (allEnabledUntil | durationLeft) : (maxDebugModeDuration | milliSecondsToTimeString: true : true) } }} +
+ {{ 'debug-settings.all-messages' | translate: { time: (isDebugAllActive$ | async) && !allEnabled ? (allEnabledUntil | durationLeft) : (maxDebugModeDuration | milliSecondsToTimeString: true : true) } }}