From a18e76839bdff4843b9130e96bc397dba1f88638 Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Tue, 15 Apr 2025 15:40:50 +0300 Subject: [PATCH 1/7] added calculated fields to rest client --- .../thingsboard/rest/client/RestClient.java | 65 ++++++++++++++++++- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java index 4f7538d205..606107b92f 100644 --- a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java +++ b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java @@ -55,9 +55,9 @@ import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.EntityViewInfo; import org.thingsboard.server.common.data.EventInfo; -import org.thingsboard.server.common.data.ResourceExportData; import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.OtaPackageInfo; +import org.thingsboard.server.common.data.ResourceExportData; import org.thingsboard.server.common.data.ResourceSubType; import org.thingsboard.server.common.data.SaveDeviceWithCredentialsRequest; import org.thingsboard.server.common.data.StringUtils; @@ -86,6 +86,7 @@ import org.thingsboard.server.common.data.asset.AssetProfileInfo; import org.thingsboard.server.common.data.asset.AssetSearchQuery; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.audit.AuditLog; +import org.thingsboard.server.common.data.cf.CalculatedField; import org.thingsboard.server.common.data.device.DeviceSearchQuery; import org.thingsboard.server.common.data.domain.Domain; import org.thingsboard.server.common.data.domain.DomainInfo; @@ -99,6 +100,7 @@ import org.thingsboard.server.common.data.id.AlarmCommentId; import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.AssetProfileId; +import org.thingsboard.server.common.data.id.CalculatedFieldId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.DeviceId; @@ -3765,7 +3767,7 @@ public class RestClient implements Closeable { } public PageData getImages(PageLink pageLink, boolean includeSystemImages) { - return this.getImages(pageLink, null, includeSystemImages); + return this.getImages(pageLink, null, includeSystemImages); } public PageData getImages(PageLink pageLink, ResourceSubType imageSubType, boolean includeSystemImages) { @@ -4056,6 +4058,65 @@ public class RestClient implements Closeable { timeout).getBody(); } + public CalculatedField saveCalculatedField(CalculatedField calculatedField) { + return restTemplate.postForEntity(baseURL + "/api/calculatedField", calculatedField, CalculatedField.class).getBody(); + } + + public Optional getCalculatedFieldById(CalculatedFieldId calculatedFieldId) { + try { + ResponseEntity calculatedField = restTemplate.getForEntity(baseURL + "/api/calculatedField/{calculatedFieldId}", CalculatedField.class, calculatedFieldId.getId()); + return Optional.ofNullable(calculatedField.getBody()); + } catch (HttpClientErrorException exception) { + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } else { + throw exception; + } + } + } + + public PageData getCalculatedFieldsByEntityId(EntityId entityId, PageLink pageLink) { + Map params = new HashMap<>(); + params.put("entityType", entityId.getEntityType().name()); + params.put("entityId", entityId.getId().toString()); + addPageLinkToParam(params, pageLink); + return restTemplate.exchange( + baseURL + "/api/{entityType}/{entityId}/calculatedFields?" + getUrlParams(pageLink), + HttpMethod.GET, HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, params).getBody(); + } + + public void deleteCalculatedField(CalculatedFieldId calculatedFieldId) { + restTemplate.delete(baseURL + "/api/calculatedField/{calculatedFieldId}", calculatedFieldId.getId()); + } + + public Optional getLatestCalculatedFieldDebugEvent(CalculatedFieldId calculatedFieldId) { + try { + ResponseEntity jsonNode = restTemplate.getForEntity(baseURL + "/api/calculatedField/{calculatedFieldId}/debug", JsonNode.class, calculatedFieldId.getId()); + return Optional.ofNullable(jsonNode.getBody()); + } catch (HttpClientErrorException exception) { + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } else { + throw exception; + } + } + } + + public Optional testCalculatedFieldScript(JsonNode inputParams) { + try { + ResponseEntity jsonNode = restTemplate.postForEntity(baseURL + "/api/calculatedField/testScript", inputParams, JsonNode.class); + return Optional.ofNullable(jsonNode.getBody()); + } catch (HttpClientErrorException exception) { + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } else { + throw exception; + } + } + } + private String getTimeUrlParams(TimePageLink pageLink) { String urlParams = getUrlParams(pageLink); if (pageLink.getStartTime() != null) { From df1e2a0c51f25956f1299f91141df654c32889d8 Mon Sep 17 00:00:00 2001 From: yevhenii Date: Tue, 17 Jun 2025 17:49:08 +0300 Subject: [PATCH 2/7] [CP-17334] Edge rule chain error - Skip saving EdgeEvent if edgeId equals originatorEdgeId --- .../server/service/edge/rpc/processor/BaseEdgeProcessor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java index 6fcb02e4bc..4eadcfc5e8 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java @@ -219,7 +219,7 @@ public abstract class BaseEdgeProcessor implements EdgeProcessor { case CREDENTIALS_UPDATED: case ASSIGNED_TO_CUSTOMER: case UNASSIGNED_FROM_CUSTOMER: - if (edgeId != null) { + if (edgeId != null && !edgeId.equals(originatorEdgeId)) { return saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, body); } else { return processNotificationToRelatedEdges(tenantId, entityId, type, actionType, originatorEdgeId); From 78d3a97dd0a5754b58059a7a6c0b8ffd254fdf03 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 18 Jun 2025 07:18:54 +0300 Subject: [PATCH 3/7] Cleanup actor mailbox messages on destroy --- .../org/thingsboard/server/actors/TbActorMailbox.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/common/actor/src/main/java/org/thingsboard/server/actors/TbActorMailbox.java b/common/actor/src/main/java/org/thingsboard/server/actors/TbActorMailbox.java index 4b822e0030..6cd28fa98d 100644 --- a/common/actor/src/main/java/org/thingsboard/server/actors/TbActorMailbox.java +++ b/common/actor/src/main/java/org/thingsboard/server/actors/TbActorMailbox.java @@ -237,8 +237,14 @@ public final class TbActorMailbox implements TbActorCtx { try { ready.set(NOT_READY); actor.destroy(stopReason, cause); - highPriorityMsgs.forEach(msg -> msg.onTbActorStopped(stopReason)); - normalPriorityMsgs.forEach(msg -> msg.onTbActorStopped(stopReason)); + highPriorityMsgs.removeIf(msg -> { + msg.onTbActorStopped(stopReason); + return true; + }); + normalPriorityMsgs.removeIf(msg -> { + msg.onTbActorStopped(stopReason); + return true; + }); } catch (Throwable t) { log.warn("[{}] Failed to destroy actor: ", selfId, t); } From 1ec2450c7801f1bffc3dd1790e441fe7789a0978 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 18 Jun 2025 07:22:11 +0300 Subject: [PATCH 4/7] Get rid of rule node configurationBytes --- .../server/common/data/rule/RuleNode.java | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java index d9eff103a7..24d5c20d45 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java @@ -62,9 +62,7 @@ public class RuleNode extends BaseDataWithAdditionalInfo implements @Schema(description = "Version of rule node configuration. ", example = "0") private int configurationVersion; @Schema(description = "JSON with the rule node configuration. Structure depends on the rule node implementation.", implementation = JsonNode.class) - private transient JsonNode configuration; - @JsonIgnore - private byte[] configurationBytes; + private JsonNode configuration; private RuleNodeId externalId; @@ -92,14 +90,6 @@ public class RuleNode extends BaseDataWithAdditionalInfo implements return name; } - public JsonNode getConfiguration() { - return BaseDataWithAdditionalInfo.getJson(() -> configuration, () -> configurationBytes); - } - - public void setConfiguration(JsonNode data) { - setJson(data, json -> this.configuration = json, bytes -> this.configurationBytes = bytes); - } - @Schema(description = "JSON object with the Rule Node Id. " + "Specify this field to update the Rule Node. " + "Referencing non-existing Rule Node Id will cause error. " + From 2c0001e5e01d23adb27f137a175833b6bad3c8bb Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 18 Jun 2025 07:23:56 +0300 Subject: [PATCH 5/7] Remove redundant "transient" usages --- .../server/common/data/AdminSettings.java | 6 +++--- .../server/common/data/BaseData.java | 8 ++++---- .../server/common/data/Dashboard.java | 6 +++--- .../common/data/settings/UserSettings.java | 20 ++----------------- .../server/common/data/widget/WidgetType.java | 4 +++- .../config/ssl/KeystoreSslCredentials.java | 2 +- .../config/ssl/PemSslCredentials.java | 4 ++-- 7 files changed, 18 insertions(+), 32 deletions(-) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/AdminSettings.java b/common/data/src/main/java/org/thingsboard/server/common/data/AdminSettings.java index ea3297e621..6092e65648 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/AdminSettings.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/AdminSettings.java @@ -32,8 +32,8 @@ public class AdminSettings extends BaseData implements HasTenan @NoXss @Length(fieldName = "key") private String key; - private transient JsonNode jsonValue; - + private JsonNode jsonValue; + public AdminSettings() { super(); } @@ -41,7 +41,7 @@ public class AdminSettings extends BaseData implements HasTenan public AdminSettings(AdminSettingsId id) { super(id); } - + public AdminSettings(AdminSettings adminSettings) { super(adminSettings); this.tenantId = adminSettings.getTenantId(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/BaseData.java b/common/data/src/main/java/org/thingsboard/server/common/data/BaseData.java index 10ea83397b..2ac78b04b6 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/BaseData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/BaseData.java @@ -25,9 +25,9 @@ public abstract class BaseData extends IdBased implement private static final long serialVersionUID = 5422817607129962637L; public static final ObjectMapper mapper = new ObjectMapper(); - + protected long createdTime; - + public BaseData() { super(); } @@ -35,7 +35,7 @@ public abstract class BaseData extends IdBased implement public BaseData(I id) { super(id); } - + public BaseData(BaseData data) { super(data.getId()); this.createdTime = data.getCreatedTime(); @@ -53,7 +53,7 @@ public abstract class BaseData extends IdBased implement public int hashCode() { final int prime = 31; int result = super.hashCode(); - result = prime * result + (int) (createdTime ^ (createdTime >>> 32)); + result = prime * result + Long.hashCode(createdTime); return result; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Dashboard.java b/common/data/src/main/java/org/thingsboard/server/common/data/Dashboard.java index 1deb322d39..820e983d60 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Dashboard.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Dashboard.java @@ -38,7 +38,7 @@ public class Dashboard extends DashboardInfo implements ExportableEntity settings, () -> settingsBytes); - } + private JsonNode settings; - public void setSettings(JsonNode settings) { - setJson(settings, json -> this.settings = json, bytes -> this.settingsBytes = bytes); - } } 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 bdac430ce1..5385a54f74 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 @@ -21,15 +21,17 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.id.WidgetTypeId; import java.util.Optional; +@EqualsAndHashCode(callSuper = true) @Data public class WidgetType extends BaseWidgetType { @Schema(description = "Complex JSON object that describes the widget type", accessMode = Schema.AccessMode.READ_ONLY) - private transient JsonNode descriptor; + private JsonNode descriptor; public WidgetType() { super(); diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/config/ssl/KeystoreSslCredentials.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/config/ssl/KeystoreSslCredentials.java index edb1e4694b..97f438a8e1 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/config/ssl/KeystoreSslCredentials.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/config/ssl/KeystoreSslCredentials.java @@ -26,7 +26,7 @@ import java.security.GeneralSecurityException; import java.security.KeyStore; @Data -@EqualsAndHashCode(callSuper = false) +@EqualsAndHashCode(callSuper = true) public class KeystoreSslCredentials extends AbstractSslCredentials { private String type; diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/config/ssl/PemSslCredentials.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/config/ssl/PemSslCredentials.java index 6c7e0cf842..4d269f6964 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/config/ssl/PemSslCredentials.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/config/ssl/PemSslCredentials.java @@ -46,7 +46,7 @@ import java.util.List; import java.util.stream.Collectors; @Data -@EqualsAndHashCode(callSuper = false) +@EqualsAndHashCode(callSuper = true) public class PemSslCredentials extends AbstractSslCredentials { private static final String DEFAULT_KEY_ALIAS = "server"; @@ -72,7 +72,7 @@ public class PemSslCredentials extends AbstractSslCredentials { try (InputStream inStream = ResourceUtils.getInputStream(this, this.certFile)) { try (PEMParser pemParser = new PEMParser(new InputStreamReader(inStream))) { Object object; - while((object = pemParser.readObject()) != null) { + while ((object = pemParser.readObject()) != null) { if (object instanceof X509CertificateHolder) { X509Certificate x509Cert = certConverter.getCertificate((X509CertificateHolder) object); certificates.add(x509Cert); From f9523209bff0c29913dea990621708013e351241 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 18 Jun 2025 16:26:25 +0300 Subject: [PATCH 6/7] Fix tests --- .../main/java/org/thingsboard/server/common/data/Dashboard.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Dashboard.java b/common/data/src/main/java/org/thingsboard/server/common/data/Dashboard.java index 820e983d60..b5ec74c2d3 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Dashboard.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Dashboard.java @@ -38,7 +38,7 @@ public class Dashboard extends DashboardInfo implements ExportableEntity Date: Thu, 19 Jun 2025 11:13:22 +0300 Subject: [PATCH 7/7] Fix some flaky tests --- .../java/org/thingsboard/server/service/job/JobManagerTest.java | 2 +- .../queue/ruleengine/TbRuleEngineQueueConsumerManagerTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java index af499ade31..0069310b5d 100644 --- a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java +++ b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java @@ -165,7 +165,7 @@ public class JobManagerTest extends AbstractControllerTest { await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { Job job = findJobById(jobId); assertThat(job.getStatus()).isEqualTo(JobStatus.CANCELLED); - assertThat(job.getResult().getDiscardedCount()).isBetween(1, tasksCount - 1); + assertThat(job.getResult().getDiscardedCount()).isBetween(1, tasksCount); assertThat(job.getResult().getTotalCount()).isEqualTo(tasksCount); assertThat(job.getResult().getCompletedCount()).isEqualTo(tasksCount); }); diff --git a/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineQueueConsumerManagerTest.java b/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineQueueConsumerManagerTest.java index dd9c4bebcf..09bd02e5a4 100644 --- a/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineQueueConsumerManagerTest.java +++ b/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineQueueConsumerManagerTest.java @@ -467,7 +467,7 @@ public class TbRuleEngineQueueConsumerManagerTest { consumerManager.delete(true); - await().atMost(2, TimeUnit.SECONDS) + await().atMost(5, TimeUnit.SECONDS) .untilAsserted(() -> { verify(ruleEngineMsgProducer).send(any(), any(), any()); });