This is an automated email from the ASF dual-hosted git repository.

apkhmv pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new a8c36a07c2 IGNITE-19450 Add * char to latest version of unit in list 
command (#2216)
a8c36a07c2 is described below

commit a8c36a07c26b423677aba72f55b983858650fd33
Author: Mikhail <[email protected]>
AuthorDate: Mon Jun 26 13:46:06 2023 +0300

    IGNITE-19450 Add * char to latest version of unit in list command (#2216)
    
    ---------
    
    Co-authored-by: Mikhail Pochatkin <[email protected]>
---
 .../cli/call/unit/ItDeployUndeployCallsTest.java   | 13 ++--
 .../cli/commands/unit/ItDeploymentUnitTest.java    | 53 ++++++++++++++-
 .../internal/rest/ItGeneratedRestClientTest.java   |  4 +-
 .../internal/cli/call/unit/ListUnitCall.java       | 18 +----
 .../internal/cli/call/unit/UnitStatusRecord.java   | 10 +--
 .../core/repl/registry/impl/UnitsRegistryImpl.java | 10 +--
 .../internal/cli/decorators/UnitListDecorator.java | 31 +++++----
 .../internal/deployunit/DeploymentManagerImpl.java | 10 +--
 .../ignite/internal/deployunit/UnitStatuses.java   | 36 ++++------
 .../internal/deployunit/UnitVersionStatus.java}    | 56 ++++++++++------
 modules/rest-api/openapi/openapi.yaml              | 21 +++++-
 .../internal/rest/api/deployment/UnitStatus.java   |  8 +--
 .../rest/api/deployment/UnitVersionStatus.java     | 77 ++++++++++++++++++++++
 .../DeploymentManagementControllerTest.java        | 11 ++--
 .../deployment/DeploymentManagementController.java | 21 +++---
 15 files changed, 258 insertions(+), 121 deletions(-)

diff --git 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/call/unit/ItDeployUndeployCallsTest.java
 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/call/unit/ItDeployUndeployCallsTest.java
index 89d9ac930f..49d7744d50 100644
--- 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/call/unit/ItDeployUndeployCallsTest.java
+++ 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/call/unit/ItDeployUndeployCallsTest.java
@@ -22,7 +22,6 @@ import static 
org.apache.ignite.internal.cli.call.unit.DeployUndeployTestSupport
 import static 
org.apache.ignite.internal.cli.call.unit.DeployUndeployTestSupport.tracker;
 import static org.apache.ignite.rest.client.model.DeploymentStatus.DEPLOYED;
 import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
-import static org.assertj.core.api.AssertionsForClassTypes.entry;
 import static org.awaitility.Awaitility.await;
 
 import jakarta.inject.Inject;
@@ -41,6 +40,8 @@ import 
org.apache.ignite.internal.cli.core.exception.UnitAlreadyExistsException;
 import org.apache.ignite.internal.cli.core.exception.UnitNotFoundException;
 import org.apache.ignite.internal.cli.core.style.component.MessageUiComponent;
 import org.apache.ignite.internal.cli.core.style.element.UiElements;
+import org.apache.ignite.rest.client.model.UnitStatus;
+import org.apache.ignite.rest.client.model.UnitVersionStatus;
 import org.assertj.core.api.Assertions;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Test;
@@ -104,9 +105,10 @@ public class ItDeployUndeployCallsTest extends 
CallInitializedIntegrationTestBas
 
         await().untilAsserted(() -> {
             // And list is not empty
-            List<UnitStatusRecord> unitsStatuses = 
listUnitCall.execute(listAllInput()).body();
+            List<UnitStatus> unitsStatuses = 
listUnitCall.execute(listAllInput()).body();
             assertThat(unitsStatuses.size()).isEqualTo(1);
-            
Assertions.assertThat(unitsStatuses.get(0).versionToStatus()).containsExactly(entry("1.0.0",
 DEPLOYED));
+            Assertions.assertThat(unitsStatuses.get(0).getVersionToStatus())
+                    .containsExactly((new 
UnitVersionStatus()).version("1.0.0").status(DEPLOYED));
         });
 
         // When undeploy unit
@@ -216,9 +218,10 @@ public class ItDeployUndeployCallsTest extends 
CallInitializedIntegrationTestBas
 
         await().untilAsserted(() -> {
             // And list is not empty
-            List<UnitStatusRecord> unisStatuses = 
listUnitCall.execute(listAllInput()).body();
+            List<UnitStatus> unisStatuses = 
listUnitCall.execute(listAllInput()).body();
             assertThat(unisStatuses.size()).isEqualTo(1);
-            
Assertions.assertThat(unisStatuses.get(0).versionToStatus()).containsExactly(entry("1.1.0",
 DEPLOYED));
+            Assertions.assertThat(unisStatuses.get(0).getVersionToStatus())
+                    .containsExactly((new 
UnitVersionStatus()).version("1.1.0").status(DEPLOYED));
         });
     }
 }
diff --git 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/unit/ItDeploymentUnitTest.java
 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/unit/ItDeploymentUnitTest.java
index c85578e772..5451e3a3ec 100644
--- 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/unit/ItDeploymentUnitTest.java
+++ 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/unit/ItDeploymentUnitTest.java
@@ -23,6 +23,7 @@ import static org.junit.jupiter.api.Assertions.assertAll;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.util.List;
 import 
org.apache.ignite.internal.cli.commands.CliCommandTestInitializedIntegrationBase;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.DisplayName;
@@ -268,16 +269,62 @@ public class ItDeploymentUnitTest extends 
CliCommandTestInitializedIntegrationBa
         assertDeployed("test-unit");
     }
 
+    @Test
+    @DisplayName("Should display * marker for latest version in list command")
+    void deployTwoVersionsAndCheckLatestMark() {
+        execute("cluster", "unit", "deploy", "test-unit", "--version", 
"1.0.0", "--path", testFile);
+
+        await().untilAsserted(() -> {
+            resetOutput();
+            execute("cluster", "unit", "list", "--plain", "test-unit");
+
+            assertDeployed("test-unit");
+        });
+
+        execute("cluster", "unit", "deploy", "test-unit", "--version", 
"2.0.0", "--path", testFile);
+
+        await().untilAsserted(() -> {
+            resetOutput();
+            execute("cluster", "unit", "list", "--plain", "test-unit");
+
+            assertDeployed(List.of(new UnitIdVersion("test-unit", "1.0.0"), 
new UnitIdVersion("test-unit", "*2.0.0")));
+        });
+    }
+
     private void assertDeployed(String id) {
-        assertDeployed(id, "1.0.0");
+        assertDeployed(id, "*1.0.0");
     }
 
     private void assertDeployed(String id, String version) {
+        assertDeployed(List.of(new UnitIdVersion(id, version)));
+    }
+
+    private void assertDeployed(List<UnitIdVersion> units) {
+        StringBuilder sb = new StringBuilder();
+        for (UnitIdVersion unit : units) {
+            sb.append(unit.id)
+                    .append("\t")
+                    .append(unit.version)
+                    .append("\tDEPLOYED")
+                    .append(System.lineSeparator());
+        }
+
         assertAll(
                 this::assertExitCodeIsZero,
                 this::assertErrOutputIsEmpty,
-                () -> assertOutputIs("id\tversion\tstatus" + 
System.lineSeparator()
-                        + id + "\t" + version + "\tDEPLOYED" + 
System.lineSeparator())
+                () -> assertOutputIs("id\tversion\tstatus" + 
System.lineSeparator() + sb)
         );
     }
+
+    private static class UnitIdVersion {
+        final String id;
+        final String version;
+
+        private UnitIdVersion(String id, String version) {
+            this.id = id;
+            this.version = version;
+
+        }
+
+    }
 }
diff --git 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/rest/ItGeneratedRestClientTest.java
 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/rest/ItGeneratedRestClientTest.java
index 5862014837..5d44d4f543 100644
--- 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/rest/ItGeneratedRestClientTest.java
+++ 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/rest/ItGeneratedRestClientTest.java
@@ -75,6 +75,7 @@ import org.apache.ignite.rest.client.model.MetricSource;
 import org.apache.ignite.rest.client.model.NodeState;
 import org.apache.ignite.rest.client.model.Problem;
 import org.apache.ignite.rest.client.model.UnitStatus;
+import org.apache.ignite.rest.client.model.UnitVersionStatus;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
@@ -399,7 +400,8 @@ public class ItGeneratedRestClientTest {
 
         new DeployUnitClient(apiClient).deployUnit(unitId, 
List.of(emptyFile()), unitVersion, DeployMode.MAJORITY, List.of());
 
-        UnitStatus expectedStatus = new 
UnitStatus().id(unitId).putVersionToStatusItem(unitVersion, DEPLOYED);
+        UnitStatus expectedStatus = new UnitStatus().id(unitId)
+                .addVersionToStatusItem((new 
UnitVersionStatus()).version(unitVersion).status(DEPLOYED));
 
         await().untilAsserted(() -> 
assertThat(deploymentApi.listClusterStatuses(null), contains(expectedStatus)));
 
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/ListUnitCall.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/ListUnitCall.java
index 6dc3d20dde..4f6440b030 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/ListUnitCall.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/ListUnitCall.java
@@ -18,7 +18,6 @@
 package org.apache.ignite.internal.cli.call.unit;
 
 import java.util.List;
-import java.util.stream.Collectors;
 import org.apache.ignite.internal.cli.core.call.Call;
 import org.apache.ignite.internal.cli.core.call.CallOutput;
 import org.apache.ignite.internal.cli.core.call.DefaultCallOutput;
@@ -27,31 +26,20 @@ import org.apache.ignite.rest.client.invoker.ApiException;
 import org.apache.ignite.rest.client.model.UnitStatus;
 
 /** List units call. */
-public abstract class ListUnitCall implements Call<ListUnitCallInput, 
List<UnitStatusRecord>> {
+public abstract class ListUnitCall implements Call<ListUnitCallInput, 
List<UnitStatus>> {
 
     @Override
-    public CallOutput<List<UnitStatusRecord>> execute(ListUnitCallInput input) 
{
+    public CallOutput<List<UnitStatus>> execute(ListUnitCallInput input) {
         try {
             List<UnitStatus> units = getStatuses(input);
             if (units.isEmpty()) {
                 return DefaultCallOutput.empty();
             }
-            return DefaultCallOutput.success(
-                    units.stream()
-                            .map(ListUnitCall::toRecord)
-                            .collect(Collectors.toList())
-            );
+            return DefaultCallOutput.success(units);
         } catch (ApiException e) {
             return DefaultCallOutput.failure(new IgniteCliApiException(e, 
input.url()));
         }
     }
 
     protected abstract List<UnitStatus> getStatuses(ListUnitCallInput input) 
throws ApiException;
-
-    private static UnitStatusRecord toRecord(UnitStatus unitStatus) {
-        return new UnitStatusRecord(
-                unitStatus.getId(),
-                unitStatus.getVersionToStatus()
-        );
-    }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/UnitStatusRecord.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/UnitStatusRecord.java
index 70f1b9909b..bac9e8f890 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/UnitStatusRecord.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/UnitStatusRecord.java
@@ -17,16 +17,16 @@
 
 package org.apache.ignite.internal.cli.call.unit;
 
-import java.util.Map;
+import java.util.List;
 import java.util.Objects;
-import org.apache.ignite.rest.client.model.DeploymentStatus;
+import org.apache.ignite.rest.client.model.UnitVersionStatus;
 
 /** Unit status record. */
 public class UnitStatusRecord {
     private final String id;
-    private final Map<String, DeploymentStatus> versionToStatus;
+    private final List<UnitVersionStatus> versionToStatus;
 
-    UnitStatusRecord(String id, Map<String, DeploymentStatus> versionToStatus) 
{
+    UnitStatusRecord(String id, List<UnitVersionStatus> versionToStatus) {
         this.id = id;
         this.versionToStatus = versionToStatus;
     }
@@ -35,7 +35,7 @@ public class UnitStatusRecord {
         return id;
     }
 
-    public Map<String, DeploymentStatus> versionToStatus() {
+    public List<UnitVersionStatus> versionToStatus() {
         return versionToStatus;
     }
 
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/registry/impl/UnitsRegistryImpl.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/registry/impl/UnitsRegistryImpl.java
index 15562b546e..e6c7169a4e 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/registry/impl/UnitsRegistryImpl.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/registry/impl/UnitsRegistryImpl.java
@@ -25,11 +25,12 @@ import java.util.concurrent.atomic.AtomicReference;
 import java.util.stream.Collectors;
 import org.apache.ignite.internal.cli.call.cluster.unit.ClusterListUnitCall;
 import org.apache.ignite.internal.cli.call.unit.ListUnitCallInput;
-import org.apache.ignite.internal.cli.call.unit.UnitStatusRecord;
 import org.apache.ignite.internal.cli.core.call.CallOutput;
 import org.apache.ignite.internal.cli.core.repl.AsyncSessionEventListener;
 import org.apache.ignite.internal.cli.core.repl.SessionInfo;
 import org.apache.ignite.internal.cli.core.repl.registry.UnitsRegistry;
+import org.apache.ignite.rest.client.model.UnitStatus;
+import org.apache.ignite.rest.client.model.UnitVersionStatus;
 
 /** Implementation of {@link UnitsRegistry}. */
 @Singleton
@@ -57,13 +58,14 @@ public class UnitsRegistryImpl implements UnitsRegistry, 
AsyncSessionEventListen
             ListUnitCallInput input = ListUnitCallInput.builder()
                     .url(url)
                     .build();
-            CallOutput<List<UnitStatusRecord>> output = call.execute(input);
+            CallOutput<List<UnitStatus>> output = call.execute(input);
             if (!output.hasError() && !output.isEmpty()) {
 
                 return output.body().stream()
                         .collect(Collectors.toMap(
-                                UnitStatusRecord::id,
-                                record -> record.versionToStatus().keySet())
+                                UnitStatus::getId,
+                                status -> status.getVersionToStatus()
+                                        
.stream().map(UnitVersionStatus::getVersion).collect(Collectors.toSet()))
                         );
             } else {
                 return null;
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/decorators/UnitListDecorator.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/decorators/UnitListDecorator.java
index 2392b431ba..8205dac069 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/decorators/UnitListDecorator.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/decorators/UnitListDecorator.java
@@ -18,17 +18,17 @@
 package org.apache.ignite.internal.cli.decorators;
 
 import com.jakewharton.fliptables.FlipTable;
+import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
 import java.util.stream.Stream;
-import org.apache.ignite.internal.cli.call.unit.UnitStatusRecord;
 import org.apache.ignite.internal.cli.core.decorator.Decorator;
 import org.apache.ignite.internal.cli.core.decorator.TerminalOutput;
 import org.apache.ignite.internal.cli.util.PlainTableRenderer;
-import org.apache.ignite.rest.client.model.DeploymentStatus;
+import org.apache.ignite.rest.client.model.UnitStatus;
+import org.apache.ignite.rest.client.model.UnitVersionStatus;
 
 /** Decorates list of units as a table. */
-public class UnitListDecorator implements Decorator<List<UnitStatusRecord>, 
TerminalOutput> {
+public class UnitListDecorator implements Decorator<List<UnitStatus>, 
TerminalOutput> {
 
     private static final String[] HEADERS = {"id", "version", "status"};
     private final boolean plain;
@@ -38,7 +38,7 @@ public class UnitListDecorator implements 
Decorator<List<UnitStatusRecord>, Term
     }
 
     @Override
-    public TerminalOutput decorate(List<UnitStatusRecord> data) {
+    public TerminalOutput decorate(List<UnitStatus> data) {
         if (plain) {
             return () -> PlainTableRenderer.render(HEADERS, toContent(data));
         } else {
@@ -46,16 +46,21 @@ public class UnitListDecorator implements 
Decorator<List<UnitStatusRecord>, Term
         }
     }
 
-    private static String[][] toContent(List<UnitStatusRecord> data) {
+    private static String[][] toContent(List<UnitStatus> data) {
         return 
data.stream().flatMap(UnitListDecorator::unfoldRecordWithVersions).toArray(String[][]::new);
     }
 
-    private static Stream<String[]> unfoldRecordWithVersions(UnitStatusRecord 
record) {
-        Map<String, DeploymentStatus> map = record.versionToStatus();
-        return map.entrySet().stream().map(entry -> new String[]{
-                record.id(),
-                entry.getKey(),
-                entry.getValue().getValue()
-        });
+    private static Stream<String[]> unfoldRecordWithVersions(UnitStatus 
status) {
+        List<String[]> result = new ArrayList<>();
+        List<UnitVersionStatus> versionStatuses = status.getVersionToStatus();
+        for (int i = 0, size = versionStatuses.size(); i < size; i++) {
+            UnitVersionStatus entry = versionStatuses.get(i);
+            result.add(new String[]{
+                    status.getId(),
+                    (i == size - 1 ? "*" : "") + entry.getVersion(),
+                    entry.getStatus().getValue()
+            });
+        }
+        return result.stream();
     }
 }
diff --git 
a/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/DeploymentManagerImpl.java
 
b/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/DeploymentManagerImpl.java
index e10abd3fd4..4f24d6a31f 100644
--- 
a/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/DeploymentManagerImpl.java
+++ 
b/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/DeploymentManagerImpl.java
@@ -339,11 +339,11 @@ public class DeploymentManagerImpl implements 
IgniteDeployment {
     @Override
     public CompletableFuture<Version> detectLatestDeployedVersion(String id) {
         return clusterStatusesAsync(id)
-                .thenApply(statuses -> statuses.versions()
-                        .stream()
-                        .filter(version -> statuses.status(version) == 
DEPLOYED)
-                        .max(Version::compareTo)
-                        .orElseThrow(() -> new 
DeploymentUnitNotFoundException(id)));
+                .thenApply(statuses -> statuses.versionStatuses().stream()
+                        .filter(e -> e.getStatus() == DEPLOYED)
+                        .reduce((first, second) -> second)
+                        .orElseThrow(() -> new 
DeploymentUnitNotFoundException(id))
+                        .getVersion());
     }
 
     @Override
diff --git 
a/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/UnitStatuses.java
 
b/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/UnitStatuses.java
index 8aaf5de04a..835323187d 100644
--- 
a/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/UnitStatuses.java
+++ 
b/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/UnitStatuses.java
@@ -18,10 +18,10 @@
 package org.apache.ignite.internal.deployunit;
 
 import java.util.Collections;
-import java.util.Map;
+import java.util.Comparator;
+import java.util.List;
 import java.util.Objects;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
 import org.apache.ignite.compute.version.Version;
 
 /**
@@ -36,7 +36,7 @@ public class UnitStatuses {
     /**
      * Map from the unit version to the unit status.
      */
-    private final Map<Version, DeploymentStatus> versionToStatus;
+    private final List<UnitVersionStatus> versionToStatus;
 
     /**
      * Constructor.
@@ -44,9 +44,10 @@ public class UnitStatuses {
      * @param id Unit identifier.
      * @param versionToStatus Map from the unit version to the unit status.
      */
-    private UnitStatuses(String id, Map<Version, DeploymentStatus> 
versionToStatus) {
+    private UnitStatuses(String id, List<UnitVersionStatus> versionToStatus) {
         this.id = id;
-        this.versionToStatus = Collections.unmodifiableMap(versionToStatus);
+        this.versionToStatus = versionToStatus;
+        
this.versionToStatus.sort(Comparator.comparing(UnitVersionStatus::getVersion));
     }
 
     /**
@@ -58,23 +59,8 @@ public class UnitStatuses {
         return id;
     }
 
-    /**
-     * Returns unit version.
-     *
-     * @return unit version.
-     */
-    public Set<Version> versions() {
-        return Collections.unmodifiableSet(versionToStatus.keySet());
-    }
-
-    /**
-     * Returns unit status.
-     *
-     * @param version Unit version.
-     * @return Unit status.
-     */
-    public DeploymentStatus status(Version version) {
-        return versionToStatus.get(version);
+    public List<UnitVersionStatus> versionStatuses() {
+        return Collections.unmodifiableList(versionToStatus);
     }
 
     /**
@@ -122,7 +108,7 @@ public class UnitStatuses {
     public static class UnitStatusesBuilder {
         private final String id;
 
-        private final Map<Version, DeploymentStatus> versionToStatus = new 
ConcurrentHashMap<>();
+        private final List<UnitVersionStatus> versionToStatus = new 
CopyOnWriteArrayList<>();
 
         /**
          * Constructor.
@@ -141,7 +127,7 @@ public class UnitStatuses {
          * @return {@code this} builder for use in a chained invocation.
          */
         public UnitStatusesBuilder append(Version version, DeploymentStatus 
deploymentStatus) {
-            versionToStatus.put(version, deploymentStatus);
+            versionToStatus.add(new UnitVersionStatus(version, 
deploymentStatus));
             return this;
         }
 
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/UnitStatusRecord.java
 
b/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/UnitVersionStatus.java
similarity index 51%
copy from 
modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/UnitStatusRecord.java
copy to 
modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/UnitVersionStatus.java
index 70f1b9909b..cc2c0cf35f 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/UnitStatusRecord.java
+++ 
b/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/UnitVersionStatus.java
@@ -15,28 +15,35 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.cli.call.unit;
+package org.apache.ignite.internal.deployunit;
 
-import java.util.Map;
-import java.util.Objects;
-import org.apache.ignite.rest.client.model.DeploymentStatus;
+import org.apache.ignite.compute.version.Version;
 
-/** Unit status record. */
-public class UnitStatusRecord {
-    private final String id;
-    private final Map<String, DeploymentStatus> versionToStatus;
+/**
+ * Unit version and status.
+ */
+public class UnitVersionStatus {
+    private final Version version;
+
+    private final DeploymentStatus status;
 
-    UnitStatusRecord(String id, Map<String, DeploymentStatus> versionToStatus) 
{
-        this.id = id;
-        this.versionToStatus = versionToStatus;
+    /**
+     * Constructor.
+     *
+     * @param version Unit version.
+     * @param status Unit status.
+     */
+    public UnitVersionStatus(Version version, DeploymentStatus status) {
+        this.version = version;
+        this.status = status;
     }
 
-    public String id() {
-        return id;
+    public Version getVersion() {
+        return version;
     }
 
-    public Map<String, DeploymentStatus> versionToStatus() {
-        return versionToStatus;
+    public DeploymentStatus getStatus() {
+        return status;
     }
 
     @Override
@@ -47,20 +54,27 @@ public class UnitStatusRecord {
         if (o == null || getClass() != o.getClass()) {
             return false;
         }
-        UnitStatusRecord that = (UnitStatusRecord) o;
-        return Objects.equals(id, that.id) && Objects.equals(versionToStatus, 
that.versionToStatus);
+
+        UnitVersionStatus that = (UnitVersionStatus) o;
+
+        if (version != null ? !version.equals(that.version) : that.version != 
null) {
+            return false;
+        }
+        return status == that.status;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(id, versionToStatus);
+        int result = version != null ? version.hashCode() : 0;
+        result = 31 * result + (status != null ? status.hashCode() : 0);
+        return result;
     }
 
     @Override
     public String toString() {
-        return "UnitStatusRecord{"
-                + "id='" + id + '\''
-                + ", versionToStatus=" + versionToStatus
+        return "UnitVersionStatus{"
+                + "version=" + version
+                + ", status=" + status
                 + '}';
     }
 }
diff --git a/modules/rest-api/openapi/openapi.yaml 
b/modules/rest-api/openapi/openapi.yaml
index f7c1ac06d5..24727400aa 100644
--- a/modules/rest-api/openapi/openapi.yaml
+++ b/modules/rest-api/openapi/openapi.yaml
@@ -943,11 +943,26 @@ components:
           type: string
           description: Unit identifier.
         versionToStatus:
-          type: object
-          additionalProperties:
-            $ref: '#/components/schemas/DeploymentStatus'
+          type: array
           description: Map from unit version to unit deployment status.
+          items:
+            $ref: '#/components/schemas/UnitVersionStatus'
       description: Unit status.
+    UnitVersionStatus:
+      required:
+      - status
+      - version
+      type: object
+      properties:
+        version:
+          type: string
+          description: Unit version.
+        status:
+          allOf:
+          - $ref: '#/components/schemas/DeploymentStatus'
+          - type: object
+            description: Unit status.
+      description: Unit version and status.
     Void:
       type: object
     deployMode:
diff --git 
a/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/api/deployment/UnitStatus.java
 
b/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/api/deployment/UnitStatus.java
index 19872ff223..2449e1fa66 100644
--- 
a/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/api/deployment/UnitStatus.java
+++ 
b/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/api/deployment/UnitStatus.java
@@ -22,7 +22,7 @@ import com.fasterxml.jackson.annotation.JsonGetter;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import io.swagger.v3.oas.annotations.media.Schema;
 import io.swagger.v3.oas.annotations.media.Schema.RequiredMode;
-import java.util.Map;
+import java.util.List;
 
 /**
  * DTO of unit status.
@@ -41,11 +41,11 @@ public class UnitStatus {
      */
     @Schema(description = "Map from unit version to unit deployment status.",
             requiredMode = RequiredMode.REQUIRED)
-    private final Map<String, DeploymentStatus> versionToStatus;
+    private final List<UnitVersionStatus> versionToStatus;
 
     @JsonCreator
     public UnitStatus(@JsonProperty("id") String id,
-            @JsonProperty("versionToStatus") Map<String, DeploymentStatus> 
versionToStatus) {
+            @JsonProperty("versionToStatus") List<UnitVersionStatus> 
versionToStatus) {
         this.id = id;
         this.versionToStatus = versionToStatus;
     }
@@ -66,7 +66,7 @@ public class UnitStatus {
      * @return Map from existing unit version to list of nodes consistent ids 
where unit deployed.
      */
     @JsonGetter("versionToStatus")
-    public Map<String, DeploymentStatus> versionToStatus() {
+    public List<UnitVersionStatus> versionToStatus() {
         return versionToStatus;
     }
 }
diff --git 
a/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/api/deployment/UnitVersionStatus.java
 
b/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/api/deployment/UnitVersionStatus.java
new file mode 100644
index 0000000000..ad53578b9d
--- /dev/null
+++ 
b/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/api/deployment/UnitVersionStatus.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.ignite.internal.rest.api.deployment;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonGetter;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.media.Schema.RequiredMode;
+
+/**
+ * DTO of unit version and status.
+ */
+@Schema(description = "Unit version and status.")
+public class UnitVersionStatus {
+    @Schema(description = "Unit version.", requiredMode = 
RequiredMode.REQUIRED)
+    private final String version;
+
+    @Schema(description = "Unit status.", requiredMode = RequiredMode.REQUIRED)
+    private final DeploymentStatus status;
+
+    @JsonCreator
+    public UnitVersionStatus(@JsonProperty("version") String version, 
@JsonProperty("status") DeploymentStatus status) {
+        this.version = version;
+        this.status = status;
+    }
+
+    @JsonGetter("version")
+    public String getVersion() {
+        return version;
+    }
+
+    @JsonGetter("status")
+    public DeploymentStatus getStatus() {
+        return status;
+    }
+
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        UnitVersionStatus that = (UnitVersionStatus) o;
+
+        if (version != null ? !version.equals(that.version) : that.version != 
null) {
+            return false;
+        }
+        return status == that.status;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = version != null ? version.hashCode() : 0;
+        result = 31 * result + (status != null ? status.hashCode() : 0);
+        return result;
+    }
+}
diff --git 
a/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/deployment/DeploymentManagementControllerTest.java
 
b/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/deployment/DeploymentManagementControllerTest.java
index 6c8cbe3852..a9d030fe72 100644
--- 
a/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/deployment/DeploymentManagementControllerTest.java
+++ 
b/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/deployment/DeploymentManagementControllerTest.java
@@ -28,7 +28,7 @@ import static 
org.apache.ignite.internal.rest.constants.HttpCode.OK;
 import static 
org.apache.ignite.internal.testframework.IgniteTestUtils.testNodeName;
 import static org.awaitility.Awaitility.await;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.hamcrest.Matchers.contains;
 import static org.hamcrest.Matchers.empty;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.is;
@@ -52,9 +52,9 @@ import java.nio.channels.SeekableByteChannel;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.List;
-import java.util.Set;
 import java.util.stream.Collectors;
 import org.apache.ignite.internal.rest.api.deployment.UnitStatus;
+import org.apache.ignite.internal.rest.api.deployment.UnitVersionStatus;
 import org.apache.ignite.internal.testframework.IntegrationTestBase;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
@@ -110,8 +110,7 @@ public class DeploymentManagementControllerTest extends 
IntegrationTestBase {
             UnitStatus status = client.toBlocking().retrieve(get, 
UnitStatus.class);
 
             assertThat(status.id(), is(id));
-            assertThat(status.versionToStatus().keySet(), 
equalTo(Set.of(version)));
-            assertThat(status.versionToStatus().get(version), 
equalTo(DEPLOYED));
+            assertThat(status.versionToStatus(), equalTo(List.of(new 
UnitVersionStatus(version, DEPLOYED))));
         });
     }
 
@@ -180,9 +179,9 @@ public class DeploymentManagementControllerTest extends 
IntegrationTestBase {
         List<UnitStatus> list = list(id);
 
         List<String> versions = list.stream()
-                .flatMap(unitStatus -> 
unitStatus.versionToStatus().keySet().stream())
+                .flatMap(unitStatus -> 
unitStatus.versionToStatus().stream().map(UnitVersionStatus::getVersion))
                 .collect(Collectors.toList());
-        assertThat(versions, containsInAnyOrder("1.0.0", "1.0.1", "1.1.1", 
"1.1.2", "1.2.1", "2.0.0"));
+        assertThat(versions, contains("1.0.0", "1.0.1", "1.1.1", "1.1.2", 
"1.2.1", "2.0.0"));
     }
 
     private HttpResponse<Object> deploy(String id, String version) {
diff --git 
a/modules/rest/src/main/java/org/apache/ignite/internal/rest/deployment/DeploymentManagementController.java
 
b/modules/rest/src/main/java/org/apache/ignite/internal/rest/deployment/DeploymentManagementController.java
index 6d8a946a7d..c3496d1f21 100644
--- 
a/modules/rest/src/main/java/org/apache/ignite/internal/rest/deployment/DeploymentManagementController.java
+++ 
b/modules/rest/src/main/java/org/apache/ignite/internal/rest/deployment/DeploymentManagementController.java
@@ -19,14 +19,12 @@ package org.apache.ignite.internal.rest.deployment;
 
 import io.micronaut.http.annotation.Controller;
 import io.micronaut.http.multipart.CompletedFileUpload;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.EnumSet;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
-import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
@@ -39,6 +37,7 @@ import 
org.apache.ignite.internal.rest.api.deployment.DeploymentCodeApi;
 import org.apache.ignite.internal.rest.api.deployment.DeploymentStatus;
 import org.apache.ignite.internal.rest.api.deployment.InitialDeployMode;
 import org.apache.ignite.internal.rest.api.deployment.UnitStatus;
+import org.apache.ignite.internal.rest.api.deployment.UnitVersionStatus;
 import org.jetbrains.annotations.Nullable;
 import org.reactivestreams.Publisher;
 
@@ -155,18 +154,18 @@ public class DeploymentManagementController implements 
DeploymentCodeApi {
      * @return Unit statuses DTO.
      */
     private static @Nullable UnitStatus fromUnitStatuses(UnitStatuses 
statuses, Predicate<DeploymentStatus> statusFilter) {
-        Map<String, DeploymentStatus> versionToDeploymentStatus = new 
HashMap<>();
-        Set<Version> versions = statuses.versions();
-        for (Version version : versions) {
-            DeploymentStatus status = 
fromDeploymentStatus(statuses.status(version));
-            if (statusFilter.test(status)) {
-                versionToDeploymentStatus.put(version.render(), status);
+        List<UnitVersionStatus> versionStatuses = new ArrayList<>();
+        for (org.apache.ignite.internal.deployunit.UnitVersionStatus 
versionStatus : statuses.versionStatuses()) {
+            DeploymentStatus deploymentStatus = 
fromDeploymentStatus(versionStatus.getStatus());
+            if (statusFilter.test(deploymentStatus)) {
+                versionStatuses.add(new 
UnitVersionStatus(versionStatus.getVersion().render(), deploymentStatus));
             }
         }
-        if (versionToDeploymentStatus.isEmpty()) {
+
+        if (versionStatuses.isEmpty()) {
             return null;
         }
-        return new UnitStatus(statuses.id(), versionToDeploymentStatus);
+        return new UnitStatus(statuses.id(), versionStatuses);
     }
 
     private static Predicate<DeploymentStatus> 
createStatusFilter(Optional<List<DeploymentStatus>> statuses) {


Reply via email to