diff --git a/common/edge-api/pom.xml b/common/edge-api/pom.xml
index d52074ace6..7eb601e270 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 2b9aad0ff1..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,44 +143,7 @@ public class EdgeGrpcClient implements EdgeRpcClient {
}
public static EdgeVersion getNewestEdgeVersion() {
- EdgeVersion newest = null;
- int[] newestParts = null;
- for (EdgeVersion v : EdgeVersion.values()) {
- if (v == EdgeVersion.V_LATEST || v == EdgeVersion.UNRECOGNIZED) {
- continue;
- }
- int[] parts = parseVersionParts(v);
- if (newest == null || compareVersionParts(parts, newestParts) > 0) {
- newest = v;
- newestParts = parts;
- }
- }
- 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;
+ 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);
+ }
+}