From 0437e9009bbb27b421024f33fefe6c9bbc4f17e0 Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Mon, 2 Feb 2026 10:21:08 +0200 Subject: [PATCH 01/12] fixed tests --- .../EntityAggregationCalculatedFieldTest.java | 11 +++++++-- .../server/msa/cf/CalculatedFieldTest.java | 24 ++++++++++++++++++- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/cf/EntityAggregationCalculatedFieldTest.java b/application/src/test/java/org/thingsboard/server/cf/EntityAggregationCalculatedFieldTest.java index 0872e21dae..c23c59d137 100644 --- a/application/src/test/java/org/thingsboard/server/cf/EntityAggregationCalculatedFieldTest.java +++ b/application/src/test/java/org/thingsboard/server/cf/EntityAggregationCalculatedFieldTest.java @@ -205,6 +205,15 @@ public class EntityAggregationCalculatedFieldTest extends AbstractControllerTest CustomInterval customInterval = new CustomInterval(TZ, 0L, 2L); createConsumptionCF(device.getId(), customInterval, null); + long interval = customInterval.getCurrentIntervalDurationMillis(); + + // Wait for a fresh interval + long initialIntervalStart = customInterval.getCurrentIntervalStartTs(); + await().alias("wait for fresh interval") + .atMost(interval + 100, TimeUnit.MILLISECONDS) + .pollInterval(100, TimeUnit.MILLISECONDS) + .until(() -> customInterval.getCurrentIntervalStartTs() != initialIntervalStart); + long currentIntervalStartTs = customInterval.getCurrentIntervalStartTs(); long tsBeforeInterval = currentIntervalStartTs - 1000; @@ -216,8 +225,6 @@ public class EntityAggregationCalculatedFieldTest extends AbstractControllerTest postTelemetry(device.getId(), String.format("{\"ts\": \"%s\", \"values\": {\"energy\":180}}", tsInInterval_2)); postTelemetry(device.getId(), String.format("{\"ts\": \"%s\", \"values\": {\"energy\":120}}", tsInInterval_3)); - long interval = customInterval.getCurrentIntervalDurationMillis(); - await().alias("create CF -> perform aggregation after interval end") .atMost(2 * interval, TimeUnit.MILLISECONDS) .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/cf/CalculatedFieldTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/cf/CalculatedFieldTest.java index 4c5b228d3c..af4197d6ec 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/cf/CalculatedFieldTest.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/cf/CalculatedFieldTest.java @@ -810,18 +810,27 @@ public class CalculatedFieldTest extends AbstractContainerTest { // --- Build CF: PROPAGATION --- CalculatedField saved = createPropagationCF(device.getId()); + // Wait for INITIALIZED event + waitForDebugEvent(saved, CalculatedFieldEventType.INITIALIZED.name(), 1); + // Create relations FROM asset 2 TO device Asset asset2 = testRestClient.postAsset(createAsset("Propagated Asset 2", null)); EntityRelation rel2 = new EntityRelation(asset2.getId(), device.getId(), EntityRelation.CONTAINS_TYPE); testRestClient.postEntityRelation(rel2); + // Wait for RELATION_ADD_OR_UPDATE event + waitForDebugEvent(saved, CalculatedFieldEventType.RELATION_ADD_OR_UPDATE.name(), 2); + // Telemetry on device testRestClient.postTelemetry(deviceToken, JacksonUtil.toJsonNode("{\"temperature\":25.1}")); + // Wait for POST_TELEMETRY_REQUEST event + waitForDebugEvent(saved, TbMsgType.POST_TELEMETRY_REQUEST.name(), 3); + // Delete relation between asset 1 and device testRestClient.deleteEntityRelation(asset1.getId(), EntityRelation.CONTAINS_TYPE, device.getId()); - // --- Assert propagated calculation (arguments-only mode) --- + // --- Assert all debug events in correct sequence --- await().alias("check debug events") .atMost(TIMEOUT, TimeUnit.SECONDS) .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) @@ -845,6 +854,19 @@ public class CalculatedFieldTest extends AbstractContainerTest { testRestClient.deleteAsset(asset2.getId()); } + private void waitForDebugEvent(CalculatedField cf, String expectedEventType, int expectedTotalEvents) { + await().alias("wait for debug event: " + expectedEventType) + .atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + List eventTypes = testRestClient.getEvents(cf.getId(), EventType.DEBUG_CALCULATED_FIELD, tenantId, new TimePageLink(expectedTotalEvents, 0, null, SortOrder.BY_CREATED_TIME_DESC)).getData().stream() + .map(e -> e.getBody().get("msgType").asText()) + .toList(); + assertThat(eventTypes).hasSize(expectedTotalEvents); + assertThat(eventTypes.get(0)).isEqualTo(expectedEventType); + }); + } + private CalculatedField createPropagationCF(EntityId entityId) { CalculatedField cf = new CalculatedField(); cf.setEntityId(entityId); From aca3d6d5080e7c878411d0fe792c177a90d85c4e Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Mon, 2 Feb 2026 11:31:56 +0200 Subject: [PATCH 02/12] Skip latest Docker tag push for 4.2.1.2 --- msa/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msa/pom.xml b/msa/pom.xml index 495cf42c57..6bf4cd934d 100644 --- a/msa/pom.xml +++ b/msa/pom.xml @@ -134,7 +134,7 @@ 4.2.1-latest - false + true From d5fe1db7d4b64729391937491e629a146d7eade2 Mon Sep 17 00:00:00 2001 From: Nikita Mazurenko Date: Fri, 23 Jan 2026 13:00:53 +0200 Subject: [PATCH 03/12] Add test to verify that enum proto EdgeVersion is synchronized with maven project.version --- application/pom.xml | 1 + .../server/edge/EdgeLatestVersionTest.java | 31 +++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 application/src/test/java/org/thingsboard/server/edge/EdgeLatestVersionTest.java diff --git a/application/pom.xml b/application/pom.xml index 36dfcac42f..6903cae2e4 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -458,6 +458,7 @@ thingsboard + ${project.version} **/nosql/*Test.java diff --git a/application/src/test/java/org/thingsboard/server/edge/EdgeLatestVersionTest.java b/application/src/test/java/org/thingsboard/server/edge/EdgeLatestVersionTest.java new file mode 100644 index 0000000000..079cea1b35 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/edge/EdgeLatestVersionTest.java @@ -0,0 +1,31 @@ +package org.thingsboard.server.edge; + +import org.junit.Assert; +import org.junit.Test; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; + +import java.util.Arrays; +import java.util.Comparator; + +public class EdgeLatestVersionTest { + + @Test + public void edgeLatestVersionIsSynchronizedTest() { + EdgeVersion currentHighestEdgeVersion = Arrays.stream(EdgeVersion.values()) + .filter(v -> v != EdgeVersion.UNRECOGNIZED) + .filter(v -> v != EdgeVersion.V_LATEST) + .max(Comparator.comparingInt(EdgeVersion::getNumber)) + .orElseThrow(); + + String projectVersion = EdgeLatestVersionTest.class.getPackage().getImplementationVersion(); + if (projectVersion == null || projectVersion.isBlank()) { + projectVersion = System.getProperty("project.version", "UNKNOWN"); + } + + String projectVersionDigits = projectVersion.replaceAll("\\D", ""); + String currentHighestEdgeVersionDigits = currentHighestEdgeVersion.name().replaceAll("\\D", ""); + + Assert.assertEquals(projectVersionDigits, currentHighestEdgeVersionDigits); + } + +} From 3d7be726929895220cedb207fab9ed55a86f647f Mon Sep 17 00:00:00 2001 From: Nikita Mazurenko Date: Fri, 23 Jan 2026 13:14:27 +0200 Subject: [PATCH 04/12] Add error message to EdgeLatestVersionTest --- .../org/thingsboard/server/edge/EdgeLatestVersionTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/application/src/test/java/org/thingsboard/server/edge/EdgeLatestVersionTest.java b/application/src/test/java/org/thingsboard/server/edge/EdgeLatestVersionTest.java index 079cea1b35..384af66d01 100644 --- a/application/src/test/java/org/thingsboard/server/edge/EdgeLatestVersionTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/EdgeLatestVersionTest.java @@ -25,7 +25,8 @@ public class EdgeLatestVersionTest { String projectVersionDigits = projectVersion.replaceAll("\\D", ""); String currentHighestEdgeVersionDigits = currentHighestEdgeVersion.name().replaceAll("\\D", ""); - Assert.assertEquals(projectVersionDigits, currentHighestEdgeVersionDigits); + String msg = "EdgeVersion enum in edge.proto is out of sync. Please add respective " + projectVersionDigits + " to EdgeVersion"; + Assert.assertEquals(msg, projectVersionDigits, currentHighestEdgeVersionDigits); } } From 10aa63f78f5af1073f1690c83224ba97ed8d4014 Mon Sep 17 00:00:00 2001 From: Nikita Mazurenko Date: Fri, 23 Jan 2026 16:40:18 +0200 Subject: [PATCH 05/12] Add license format in EdgeLatestVersionTest --- .../server/edge/EdgeLatestVersionTest.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/application/src/test/java/org/thingsboard/server/edge/EdgeLatestVersionTest.java b/application/src/test/java/org/thingsboard/server/edge/EdgeLatestVersionTest.java index 384af66d01..84c90f6b63 100644 --- a/application/src/test/java/org/thingsboard/server/edge/EdgeLatestVersionTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/EdgeLatestVersionTest.java @@ -1,3 +1,18 @@ +/** + * Copyright © 2016-2026 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.edge; import org.junit.Assert; From 25e6464837b33913f22597949d8ede7ff32b17fe Mon Sep 17 00:00:00 2001 From: Nikita Mazurenko Date: Mon, 2 Feb 2026 14:01:41 +0200 Subject: [PATCH 06/12] Set EdgeVersion V_4_2_1_2 = 13 in edge.proto --- common/edge-api/src/main/proto/edge.proto | 1 + 1 file changed, 1 insertion(+) diff --git a/common/edge-api/src/main/proto/edge.proto b/common/edge-api/src/main/proto/edge.proto index 803bb98330..783c4ab394 100644 --- a/common/edge-api/src/main/proto/edge.proto +++ b/common/edge-api/src/main/proto/edge.proto @@ -44,6 +44,7 @@ enum EdgeVersion { V_4_0_0 = 10; V_4_1_0 = 11; V_4_2_0 = 12; + V_4_2_1_2 = 13; V_LATEST = 999; } From 5058379186c02624b4bae37aea11fa96f9d80f5c Mon Sep 17 00:00:00 2001 From: Nikita Mazurenko Date: Mon, 2 Feb 2026 14:29:01 +0200 Subject: [PATCH 07/12] Fix EdgeVersion for CE version --- common/edge-api/src/main/proto/edge.proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/edge-api/src/main/proto/edge.proto b/common/edge-api/src/main/proto/edge.proto index 783c4ab394..435faea028 100644 --- a/common/edge-api/src/main/proto/edge.proto +++ b/common/edge-api/src/main/proto/edge.proto @@ -44,7 +44,7 @@ enum EdgeVersion { V_4_0_0 = 10; V_4_1_0 = 11; V_4_2_0 = 12; - V_4_2_1_2 = 13; + V_4_2_1_2 = 14; V_LATEST = 999; } From 1c136988ea5ad7354c720f3041cf9001f54dff2d Mon Sep 17 00:00:00 2001 From: Nikita Mazurenko Date: Mon, 2 Feb 2026 14:44:32 +0200 Subject: [PATCH 08/12] Add 4_3_0_1 version to edge.proto --- common/edge-api/src/main/proto/edge.proto | 1 + 1 file changed, 1 insertion(+) diff --git a/common/edge-api/src/main/proto/edge.proto b/common/edge-api/src/main/proto/edge.proto index 836a961b26..bbe871f55b 100644 --- a/common/edge-api/src/main/proto/edge.proto +++ b/common/edge-api/src/main/proto/edge.proto @@ -46,6 +46,7 @@ enum EdgeVersion { V_4_2_0 = 12; V_4_3_0 = 13; V_4_2_1_2 = 14; + V_4_3_0_1 = 15; V_LATEST = 999; } From 22d6225ad6fb6b3942e2a91400c09687afeed4b0 Mon Sep 17 00:00:00 2001 From: Nikita Mazurenko Date: Mon, 2 Feb 2026 15:27:32 +0200 Subject: [PATCH 09/12] Add EdgeVersionComparator and related test --- common/edge-api/pom.xml | 5 ++ .../thingsboard/edge/rpc/EdgeGrpcClient.java | 11 +-- .../edge/rpc/EdgeVersionComparator.java | 69 ++++++++++++++++++ .../edge/rpc/EdgeVersionComparatorTest.java | 73 +++++++++++++++++++ 4 files changed, 148 insertions(+), 10 deletions(-) create mode 100644 common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeVersionComparator.java create mode 100644 common/edge-api/src/test/java/org/thingsboard/edge/rpc/EdgeVersionComparatorTest.java diff --git a/common/edge-api/pom.xml b/common/edge-api/pom.xml index c2d5d028b6..c903718788 100644 --- a/common/edge-api/pom.xml +++ b/common/edge-api/pom.xml @@ -106,6 +106,11 @@ com.google.protobuf protobuf-java + + org.springframework.boot + spring-boot-starter-test + test + diff --git a/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java b/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java index 1b17fbe849..7e58a2cb9f 100644 --- a/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java +++ b/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java @@ -143,16 +143,7 @@ public class EdgeGrpcClient implements EdgeRpcClient { } public static EdgeVersion getNewestEdgeVersion() { - EdgeVersion newest = null; - for (EdgeVersion v : EdgeVersion.values()) { - if (v == EdgeVersion.V_LATEST || v == EdgeVersion.UNRECOGNIZED) { - continue; - } - if (newest == null || v.getNumber() > newest.getNumber()) { - newest = v; - } - } - return newest; + return EdgeVersionComparator.getNewestEdgeVersion(); } private StreamObserver initOutputStream(String edgeKey, diff --git a/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeVersionComparator.java b/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeVersionComparator.java new file mode 100644 index 0000000000..6d3dae49ca --- /dev/null +++ b/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeVersionComparator.java @@ -0,0 +1,69 @@ +/** + * Copyright © 2016-2026 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.edge.rpc; + +import org.thingsboard.server.gen.edge.v1.EdgeVersion; + +import java.util.Comparator; + +public class EdgeVersionComparator implements Comparator { + + public static final EdgeVersionComparator INSTANCE = new EdgeVersionComparator(); + + @Override + public int compare(EdgeVersion v1, EdgeVersion v2) { + return compareVersionParts(parseVersionParts(v1), parseVersionParts(v2)); + } + + public static EdgeVersion getNewestEdgeVersion() { + EdgeVersion newest = null; + for (EdgeVersion v : EdgeVersion.values()) { + if (v == EdgeVersion.V_LATEST || v == EdgeVersion.UNRECOGNIZED) { + continue; + } + if (newest == null || INSTANCE.compare(v, newest) > 0) { + newest = v; + } + } + return newest; + } + + private static int[] parseVersionParts(EdgeVersion version) { + String name = version.name(); + if (name.startsWith("V_")) { + name = name.substring(2); + } + String[] parts = name.split("_"); + int[] result = new int[parts.length]; + for (int i = 0; i < parts.length; i++) { + result[i] = Integer.parseInt(parts[i]); + } + return result; + } + + private static int compareVersionParts(int[] a, int[] b) { + int maxLen = Math.max(a.length, b.length); + for (int i = 0; i < maxLen; i++) { + int partA = i < a.length ? a[i] : 0; + int partB = i < b.length ? b[i] : 0; + if (partA != partB) { + return Integer.compare(partA, partB); + } + } + return 0; + } + +} diff --git a/common/edge-api/src/test/java/org/thingsboard/edge/rpc/EdgeVersionComparatorTest.java b/common/edge-api/src/test/java/org/thingsboard/edge/rpc/EdgeVersionComparatorTest.java new file mode 100644 index 0000000000..f88665e9c6 --- /dev/null +++ b/common/edge-api/src/test/java/org/thingsboard/edge/rpc/EdgeVersionComparatorTest.java @@ -0,0 +1,73 @@ +/** + * Copyright © 2016-2026 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.edge.rpc; + +import org.junit.jupiter.api.Test; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; + +import static org.assertj.core.api.Assertions.assertThat; + +class EdgeVersionComparatorTest { + + @Test + void compare_sameVersion_returnsZero() { + assertThat(EdgeVersionComparator.INSTANCE.compare(EdgeVersion.V_3_3_0, EdgeVersion.V_3_3_0)).isEqualTo(0); + assertThat(EdgeVersionComparator.INSTANCE.compare(EdgeVersion.V_4_0_0, EdgeVersion.V_4_0_0)).isEqualTo(0); + assertThat(EdgeVersionComparator.INSTANCE.compare(EdgeVersion.V_4_2_1_2, EdgeVersion.V_4_2_1_2)).isEqualTo(0); + } + + @Test + void compare_majorVersionDifference_returnsCorrectOrder() { + assertThat(EdgeVersionComparator.INSTANCE.compare(EdgeVersion.V_3_3_0, EdgeVersion.V_4_0_0)).isLessThan(0); + assertThat(EdgeVersionComparator.INSTANCE.compare(EdgeVersion.V_4_0_0, EdgeVersion.V_3_3_0)).isGreaterThan(0); + } + + @Test + void compare_minorVersionDifference_returnsCorrectOrder() { + assertThat(EdgeVersionComparator.INSTANCE.compare(EdgeVersion.V_3_3_0, EdgeVersion.V_3_6_0)).isLessThan(0); + assertThat(EdgeVersionComparator.INSTANCE.compare(EdgeVersion.V_3_6_0, EdgeVersion.V_3_3_0)).isGreaterThan(0); + assertThat(EdgeVersionComparator.INSTANCE.compare(EdgeVersion.V_4_0_0, EdgeVersion.V_4_1_0)).isLessThan(0); + assertThat(EdgeVersionComparator.INSTANCE.compare(EdgeVersion.V_4_1_0, EdgeVersion.V_4_0_0)).isGreaterThan(0); + } + + @Test + void compare_patchVersionDifference_returnsCorrectOrder() { + assertThat(EdgeVersionComparator.INSTANCE.compare(EdgeVersion.V_3_6_0, EdgeVersion.V_3_6_1)).isLessThan(0); + assertThat(EdgeVersionComparator.INSTANCE.compare(EdgeVersion.V_3_6_1, EdgeVersion.V_3_6_0)).isGreaterThan(0); + assertThat(EdgeVersionComparator.INSTANCE.compare(EdgeVersion.V_3_6_1, EdgeVersion.V_3_6_2)).isLessThan(0); + assertThat(EdgeVersionComparator.INSTANCE.compare(EdgeVersion.V_3_6_2, EdgeVersion.V_3_6_4)).isLessThan(0); + } + + @Test + void compare_fourPartVersion_returnsCorrectOrder() { + assertThat(EdgeVersionComparator.INSTANCE.compare(EdgeVersion.V_4_2_0, EdgeVersion.V_4_2_1_2)).isLessThan(0); + assertThat(EdgeVersionComparator.INSTANCE.compare(EdgeVersion.V_4_2_1_2, EdgeVersion.V_4_2_0)).isGreaterThan(0); + } + + @Test + void compare_threePartVsFourPart_treatsImplicitZero() { + // V_4_2_0 should be less than V_4_2_1_2 (4.2.0.0 < 4.2.1.2) + assertThat(EdgeVersionComparator.INSTANCE.compare(EdgeVersion.V_4_2_0, EdgeVersion.V_4_2_1_2)).isLessThan(0); + } + + @Test + void getNewestEdgeVersion_excludesLatestAndUnrecognized() { + EdgeVersion newest = EdgeVersionComparator.getNewestEdgeVersion(); + assertThat(newest).isNotNull(); + assertThat(newest).isNotEqualTo(EdgeVersion.V_LATEST); + assertThat(newest).isNotEqualTo(EdgeVersion.UNRECOGNIZED); + } +} From ff5baf8337589027f75036ef55e3859a0d1030ab Mon Sep 17 00:00:00 2001 From: Nikita Mazurenko Date: Mon, 2 Feb 2026 15:31:24 +0200 Subject: [PATCH 10/12] Replace EdgeVersion comparison from .getNumber() to EdgeVersionComparator --- .../server/service/edge/EdgeMsgConstructorUtils.java | 3 ++- .../edge/rpc/processor/cf/CalculatedFieldEdgeProcessor.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/EdgeMsgConstructorUtils.java b/application/src/main/java/org/thingsboard/server/service/edge/EdgeMsgConstructorUtils.java index 9ecfd2be08..3003c046b9 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/EdgeMsgConstructorUtils.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/EdgeMsgConstructorUtils.java @@ -26,6 +26,7 @@ import com.google.gson.JsonPrimitive; import com.google.gson.reflect.TypeToken; import lombok.extern.slf4j.Slf4j; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.edge.rpc.EdgeVersionComparator; import org.thingsboard.rule.engine.action.TbSaveToCustomCassandraTableNode; import org.thingsboard.rule.engine.ai.TbAiNode; import org.thingsboard.rule.engine.aws.lambda.TbAwsLambdaNode; @@ -281,7 +282,7 @@ public class EdgeMsgConstructorUtils { public static String getEntityAndFixLwm2mBootstrapShortServerId(DeviceProfile deviceProfile, EdgeVersion edgeVersion) { DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); - if (!(transportConfiguration instanceof Lwm2mDeviceProfileTransportConfiguration) || edgeVersion.getNumber() >= EdgeVersion.V_4_3_0.getNumber()) { + if (!(transportConfiguration instanceof Lwm2mDeviceProfileTransportConfiguration) || EdgeVersionComparator.INSTANCE.compare(edgeVersion, EdgeVersion.V_4_3_0) >= 0) { return JacksonUtil.toString(deviceProfile); } JsonNode jsonNode = JacksonUtil.valueToTree(deviceProfile); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/cf/CalculatedFieldEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/cf/CalculatedFieldEdgeProcessor.java index 5f11abb478..bc085afc08 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/cf/CalculatedFieldEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/cf/CalculatedFieldEdgeProcessor.java @@ -22,6 +22,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.data.util.Pair; import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.edge.rpc.EdgeVersionComparator; import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.cf.CalculatedField; @@ -116,7 +117,7 @@ public class CalculatedFieldEdgeProcessor extends BaseCalculatedFieldProcessor i } private boolean isValidCfToSend(CalculatedFieldType type, EdgeVersion edgeVersion) { - return edgeVersion.getNumber() >= EdgeVersion.V_4_3_0.getNumber() || (type == SIMPLE || type == SCRIPT); + return EdgeVersionComparator.INSTANCE.compare(edgeVersion, EdgeVersion.V_4_3_0) >= 0 || (type == SIMPLE || type == SCRIPT); } @Override From f82f4017fd5057e7167a761ae388a9312ac307ba Mon Sep 17 00:00:00 2001 From: Nikita Mazurenko Date: Mon, 2 Feb 2026 15:52:35 +0200 Subject: [PATCH 11/12] Fix version comparison in EdgeVersionUtils --- .../service/edge/rpc/utils/EdgeVersionUtils.java | 3 ++- .../thingsboard/server/edge/EdgeLatestVersionTest.java | 10 ++-------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/utils/EdgeVersionUtils.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/utils/EdgeVersionUtils.java index b2b72db5ad..ca35b30e59 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/utils/EdgeVersionUtils.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/utils/EdgeVersionUtils.java @@ -16,13 +16,14 @@ package org.thingsboard.server.service.edge.rpc.utils; import lombok.extern.slf4j.Slf4j; +import org.thingsboard.edge.rpc.EdgeVersionComparator; import org.thingsboard.server.gen.edge.v1.EdgeVersion; @Slf4j public final class EdgeVersionUtils { public static boolean isEdgeVersionOlderThan(EdgeVersion currentVersion, EdgeVersion requiredVersion) { - return currentVersion.ordinal() < requiredVersion.ordinal(); + return EdgeVersionComparator.INSTANCE.compare(currentVersion, requiredVersion) < 0; } } diff --git a/application/src/test/java/org/thingsboard/server/edge/EdgeLatestVersionTest.java b/application/src/test/java/org/thingsboard/server/edge/EdgeLatestVersionTest.java index 84c90f6b63..3852fe2ff0 100644 --- a/application/src/test/java/org/thingsboard/server/edge/EdgeLatestVersionTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/EdgeLatestVersionTest.java @@ -17,20 +17,14 @@ package org.thingsboard.server.edge; import org.junit.Assert; import org.junit.Test; +import org.thingsboard.edge.rpc.EdgeVersionComparator; import org.thingsboard.server.gen.edge.v1.EdgeVersion; -import java.util.Arrays; -import java.util.Comparator; - public class EdgeLatestVersionTest { @Test public void edgeLatestVersionIsSynchronizedTest() { - EdgeVersion currentHighestEdgeVersion = Arrays.stream(EdgeVersion.values()) - .filter(v -> v != EdgeVersion.UNRECOGNIZED) - .filter(v -> v != EdgeVersion.V_LATEST) - .max(Comparator.comparingInt(EdgeVersion::getNumber)) - .orElseThrow(); + EdgeVersion currentHighestEdgeVersion = EdgeVersionComparator.getNewestEdgeVersion(); String projectVersion = EdgeLatestVersionTest.class.getPackage().getImplementationVersion(); if (projectVersion == null || projectVersion.isBlank()) { From c7c20ae9fdd2adb3b713ed8c5b310c1e2f42723a Mon Sep 17 00:00:00 2001 From: Nikita Mazurenko Date: Mon, 2 Feb 2026 17:21:56 +0200 Subject: [PATCH 12/12] Handle EdgeVersion comparison of non-numeric values --- .../edge/rpc/EdgeVersionComparator.java | 17 +++++++++++ .../edge/rpc/EdgeVersionComparatorTest.java | 29 +++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeVersionComparator.java b/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeVersionComparator.java index 6d3dae49ca..94ca577025 100644 --- a/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeVersionComparator.java +++ b/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeVersionComparator.java @@ -25,6 +25,23 @@ public class EdgeVersionComparator implements Comparator { @Override public int compare(EdgeVersion v1, EdgeVersion v2) { + if (v1 == v2) { + return 0; + } + // UNRECOGNIZED is less than any other version + if (v1 == EdgeVersion.UNRECOGNIZED) { + return -1; + } + if (v2 == EdgeVersion.UNRECOGNIZED) { + return 1; + } + // V_LATEST is treated as the newest version + if (v1 == EdgeVersion.V_LATEST) { + v1 = getNewestEdgeVersion(); + } + if (v2 == EdgeVersion.V_LATEST) { + v2 = getNewestEdgeVersion(); + } return compareVersionParts(parseVersionParts(v1), parseVersionParts(v2)); } diff --git a/common/edge-api/src/test/java/org/thingsboard/edge/rpc/EdgeVersionComparatorTest.java b/common/edge-api/src/test/java/org/thingsboard/edge/rpc/EdgeVersionComparatorTest.java index f88665e9c6..eab63fc092 100644 --- a/common/edge-api/src/test/java/org/thingsboard/edge/rpc/EdgeVersionComparatorTest.java +++ b/common/edge-api/src/test/java/org/thingsboard/edge/rpc/EdgeVersionComparatorTest.java @@ -70,4 +70,33 @@ class EdgeVersionComparatorTest { assertThat(newest).isNotEqualTo(EdgeVersion.V_LATEST); assertThat(newest).isNotEqualTo(EdgeVersion.UNRECOGNIZED); } + + @Test + void compare_vLatest_treatedAsNewestVersion() { + EdgeVersion newest = EdgeVersionComparator.getNewestEdgeVersion(); + // V_LATEST equals the newest version + assertThat(EdgeVersionComparator.INSTANCE.compare(EdgeVersion.V_LATEST, newest)).isEqualTo(0); + assertThat(EdgeVersionComparator.INSTANCE.compare(newest, EdgeVersion.V_LATEST)).isEqualTo(0); + // V_LATEST is greater than older versions + assertThat(EdgeVersionComparator.INSTANCE.compare(EdgeVersion.V_LATEST, EdgeVersion.V_3_3_0)).isGreaterThan(0); + assertThat(EdgeVersionComparator.INSTANCE.compare(EdgeVersion.V_3_3_0, EdgeVersion.V_LATEST)).isLessThan(0); + } + + @Test + void compare_vLatest_withItself_returnsZero() { + assertThat(EdgeVersionComparator.INSTANCE.compare(EdgeVersion.V_LATEST, EdgeVersion.V_LATEST)).isEqualTo(0); + } + + @Test + void compare_unrecognized_isLessThanAnyVersion() { + assertThat(EdgeVersionComparator.INSTANCE.compare(EdgeVersion.UNRECOGNIZED, EdgeVersion.V_3_3_0)).isLessThan(0); + assertThat(EdgeVersionComparator.INSTANCE.compare(EdgeVersion.UNRECOGNIZED, EdgeVersion.V_LATEST)).isLessThan(0); + assertThat(EdgeVersionComparator.INSTANCE.compare(EdgeVersion.V_3_3_0, EdgeVersion.UNRECOGNIZED)).isGreaterThan(0); + assertThat(EdgeVersionComparator.INSTANCE.compare(EdgeVersion.V_LATEST, EdgeVersion.UNRECOGNIZED)).isGreaterThan(0); + } + + @Test + void compare_unrecognized_withItself_returnsZero() { + assertThat(EdgeVersionComparator.INSTANCE.compare(EdgeVersion.UNRECOGNIZED, EdgeVersion.UNRECOGNIZED)).isEqualTo(0); + } }