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 7f717acd86 IGNITE-23424 Improve node version endpoint (#4567)
7f717acd86 is described below

commit 7f717acd8662536ffd46e9ed74a4ad8caba76623
Author: Vadim Pakhnushev <[email protected]>
AuthorDate: Sat Oct 19 14:31:33 2024 +0300

    IGNITE-23424 Improve node version endpoint (#4567)
---
 build.gradle                                       | 10 ++-
 buildscripts/java-core.gradle                      |  4 +-
 modules/cli/build.gradle                           | 33 ++++----
 .../internal/cli/commands/ItNodeNameTest.java      |  2 +-
 .../cli/commands/node/NodeVersionCommandTest.java  |  2 +-
 .../apache/ignite/internal/cli/CliVersionInfo.java | 23 +++---
 .../ignite/internal/cli/VersionProvider.java       |  3 +-
 .../cli/call/node/version/NodeVersionCall.java     | 18 ++--
 .../cli/decorators/DefaultDecoratorRegistry.java   |  2 +
 .../cli/decorators/NodeVersionDecorator.java}      | 31 +++----
 modules/cli/src/main/resources/version.properties  |  1 +
 .../internal/cli/commands/CliCommandTestBase.java  |  5 ++
 .../cli/commands/version/CliVersionTest.java}      | 13 +--
 modules/cli/src/test/resources/version.properties  | 18 ----
 .../internal/properties/IgniteProductVersion.java  |  3 +
 .../internal/properties/IgniteProperties.java      |  3 +
 modules/core/src/main/resources/ignite.properties  |  1 +
 .../internal/rest/api/node/NodeManagementApi.java  |  4 +-
 .../ignite/internal/rest/api/node/NodeVersion.java | 95 ++++++++++++++++++++++
 .../rest/ItInitializedClusterRestTest.java         |  7 +-
 .../rest/ItNotInitializedClusterRestTest.java      |  7 +-
 .../rest/node/NodeManagementController.java        |  8 +-
 22 files changed, 200 insertions(+), 93 deletions(-)

diff --git a/build.gradle b/build.gradle
index ee6bc5d6bf..b07abe80f0 100644
--- a/build.gradle
+++ b/build.gradle
@@ -68,13 +68,15 @@ ext {
     compilerArgs = [
             "--add-exports=java.base/sun.nio.ch=ALL-UNNAMED"
     ]
+
+    product = "Apache Ignite"
 }
 
 allprojects {
     group 'org.apache.ignite'
     version = "3.0.0-SNAPSHOT"
 
-    tasks.withType(Jar) {
+    tasks.withType(Jar).configureEach {
         duplicatesStrategy = DuplicatesStrategy.EXCLUDE
     }
 
@@ -90,18 +92,18 @@ allprojects {
         }
     }
 
-    tasks.withType(Sign) {
+    tasks.withType(Sign).configureEach {
         enabled = project.hasProperty('signing.keyId')
                 && project.hasProperty('signing.password')
                 && project.hasProperty('signing.secretKeyRingFile')
     }
 
-    tasks.withType(Javadoc) {
+    tasks.withType(Javadoc).configureEach {
         options.tags = ["apiNote"]
         options.addStringOption('bottom', javadocFooter())
     }
 
-    tasks.withType(JavaCompile) {
+    tasks.withType(JavaCompile).configureEach {
         options.encoding = 'UTF-8'
         options.compilerArgs += compilerArgs
     }
diff --git a/buildscripts/java-core.gradle b/buildscripts/java-core.gradle
index 12c28c8334..98185ea7c9 100644
--- a/buildscripts/java-core.gradle
+++ b/buildscripts/java-core.gradle
@@ -46,6 +46,7 @@ processResources {
     filesMatching('**/*.properties') {
         filter { String line ->
             line.replace("\${project.version}", project.version)
+                    .replace("\${project.product}", project.product)
         }
     }
 }
@@ -70,7 +71,7 @@ checkstyle {
     ]
 }
 
-tasks.withType(Checkstyle) {
+tasks.withType(Checkstyle).configureEach {
     excludes = ["**/generated-source/**",
                 "**/generated/**",
                 "com/facebook/presto/bytecode/**/*",
@@ -81,7 +82,6 @@ tasks.withType(Checkstyle) {
             required = true
             outputLocation = 
file("$rootDir/build/reports/checkstyle/${project.name}.html")
         }
-
     }
 }
 
diff --git a/modules/cli/build.gradle b/modules/cli/build.gradle
index 08f96420f6..f61c3c973b 100644
--- a/modules/cli/build.gradle
+++ b/modules/cli/build.gradle
@@ -42,6 +42,7 @@ dependencies {
     implementation project(':ignite-core')
     implementation project(':ignite-client')
     implementation project(':ignite-jdbc')
+    implementation project(':ignite-rest-api')
     openapiSpec(project(path: ':ignite-rest-api', configuration: 
'openapiSpec'))
 
     implementation libs.tree.sitter
@@ -135,7 +136,7 @@ configurations {
 
 ext.generatedClientDir = layout.buildDirectory.dir("swagger/client")
 
-task copyOpenapiDefinition(type: Copy) {
+def copyOpenapiDefinition = tasks.register('copyOpenapiDefinition', Copy) {
     from configurations.openapiSpec
     into(layout.buildDirectory.dir("customOpenapiDefinition"))
 }
@@ -144,26 +145,26 @@ pmdMain {
     excludes = ["**/org/apache/ignite/rest/client/**"]
 }
 
-task generateApiClient(type: GenerateTask) {
-    generatorName="java"
+tasks.register('generateApiClient', GenerateTask) {
+    dependsOn copyOpenapiDefinition
+
+    generatorName = "java"
     inputs.dir layout.buildDirectory.dir("customOpenapiDefinition")
-    
inputSpec=layout.buildDirectory.file("customOpenapiDefinition/openapi.yaml").get().asFile.path
-    apiPackage="org.apache.ignite.rest.client.api"
-    invokerPackage="org.apache.ignite.rest.client.invoker"
-    modelPackage="org.apache.ignite.rest.client.model"
+    inputSpec = 
layout.buildDirectory.file("customOpenapiDefinition/openapi.yaml").get().asFile.path
+    apiPackage = "org.apache.ignite.rest.client.api"
+    invokerPackage = "org.apache.ignite.rest.client.invoker"
+    modelPackage = "org.apache.ignite.rest.client.model"
     outputDir = generatedClientDir.get().asFile.toString()
-    generateModelTests=false
-    generateApiTests=false
-    languageSpecificPrimitives=["boolean", "int", "float", "double", "char", 
"byte", "short", "long"]
+    generateModelTests = false
+    generateApiTests = false
+    languageSpecificPrimitives = ["boolean", "int", "float", "double", "char", 
"byte", "short", "long"]
     configOptions = [
-        "openApiNullable": "false",
-        "supportStreaming": "false",
-        "library": "okhttp-gson"
+            "openApiNullable" : "false",
+            "supportStreaming": "false",
+            "library"         : "okhttp-gson"
     ]
 }
 
-generateApiClient.dependsOn copyOpenapiDefinition
-
 idea.module {
     sourceDirs += generatedClientDir.get().dir('src/main/java').asFile
     resourceDirs += generatedClientDir.get().dir('src/main/resources').asFile
@@ -176,7 +177,7 @@ compileJava {
     source generatedClientDir.get().dir('src/main/java')
 }
 
-task generateAutocompletionScript(type: JavaExec) {
+def generateAutocompletionScript = 
tasks.register('generateAutocompletionScript', JavaExec) {
     classpath = sourceSets.main.runtimeClasspath
     mainClass.set("picocli.AutoComplete")
     systemProperty("picocli.autocomplete.systemExitOnError", "")
diff --git 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/ItNodeNameTest.java
 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/ItNodeNameTest.java
index 08a283631f..3663fbf69b 100644
--- 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/ItNodeNameTest.java
+++ 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/ItNodeNameTest.java
@@ -67,7 +67,7 @@ public class ItNodeNameTest extends CliIntegrationTest {
         assertAll(
                 this::assertExitCodeIsZero,
                 this::assertErrOutputIsEmpty,
-                () -> 
assertOutputMatches("[1-9]\\d*\\.\\d+\\.\\d+(?:-[a-zA-Z0-9]+)?\\s+")
+                () -> assertOutputMatches("Apache Ignite version 
[1-9]\\d*\\.\\d+\\.\\d+(?:-[a-zA-Z0-9]+)?\\s+")
         );
     }
 
diff --git 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/node/NodeVersionCommandTest.java
 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/node/NodeVersionCommandTest.java
index 6f092dae92..861eb46e7f 100644
--- 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/node/NodeVersionCommandTest.java
+++ 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/node/NodeVersionCommandTest.java
@@ -35,7 +35,7 @@ public class NodeVersionCommandTest extends 
CliCommandTestNotInitializedIntegrat
         assertAll(
                 this::assertExitCodeIsZero,
                 this::assertErrOutputIsEmpty,
-                () -> 
assertOutputMatches("[1-9]\\d*\\.\\d+\\.\\d+(?:-[a-zA-Z0-9]+)?\\s+")
+                () -> assertOutputMatches("Apache Ignite version 
[1-9]\\d*\\.\\d+\\.\\d+(?:-[a-zA-Z0-9]+)?\\s+")
         );
     }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/CliVersionInfo.java 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/CliVersionInfo.java
index 2182e4e892..4759e7692c 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/CliVersionInfo.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/CliVersionInfo.java
@@ -29,7 +29,10 @@ import 
org.apache.ignite.internal.cli.core.exception.IgniteCliException;
 @Singleton
 public class CliVersionInfo {
     /** Ignite CLI version. */
-    public final String ver;
+    private final String version;
+
+    /** Ignite CLI product. */
+    private final String product;
 
     /**
      * Creates Ignite CLI version provider according to builtin version file.
@@ -39,18 +42,18 @@ public class CliVersionInfo {
             Properties prop = new Properties();
             prop.load(inputStream);
 
-            ver = prop.getProperty("version", "undefined");
+            version = prop.getProperty("version", "undefined");
+            product = prop.getProperty("product", "undefined");
         } catch (IOException e) {
-            throw new IgniteCliException("Can' read ignite version info");
+            throw new IgniteCliException("Can't read version info");
         }
     }
 
-    /**
-     * Creates Ignite CLI version provider from the manually set version.
-     *
-     * @param ver Ignite CLI version
-     */
-    public CliVersionInfo(String ver) {
-        this.ver = ver;
+    String version() {
+        return version;
+    }
+
+    String product() {
+        return product;
     }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/VersionProvider.java 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/VersionProvider.java
index b1f70cd01f..95a92759b6 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/VersionProvider.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/VersionProvider.java
@@ -49,9 +49,8 @@ public class VersionProvider implements 
CommandLine.IVersionProvider {
         this.cliVerInfo = cliVerInfo;
     }
 
-    /** {@inheritDoc} */
     @Override
     public String[] getVersion() {
-        return new String[]{"Apache Ignite CLI ver. " + cliVerInfo.ver};
+        return new String[]{cliVerInfo.product() + " CLI version " + 
cliVerInfo.version()};
     }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/version/NodeVersionCall.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/version/NodeVersionCall.java
index 60b36f01bb..be1d36b69c 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/version/NodeVersionCall.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/version/NodeVersionCall.java
@@ -17,19 +17,22 @@
 
 package org.apache.ignite.internal.cli.call.node.version;
 
+import static 
org.apache.ignite.internal.cli.core.call.DefaultCallOutput.failure;
+import static 
org.apache.ignite.internal.cli.core.call.DefaultCallOutput.success;
+
 import jakarta.inject.Singleton;
 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;
 import org.apache.ignite.internal.cli.core.call.UrlCallInput;
 import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
 import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
+import org.apache.ignite.internal.rest.api.node.NodeVersion;
 import org.apache.ignite.rest.client.api.NodeManagementApi;
 import org.apache.ignite.rest.client.invoker.ApiException;
 
 /** Call to get node version. */
 @Singleton
-public class NodeVersionCall implements Call<UrlCallInput, String> {
+public class NodeVersionCall implements Call<UrlCallInput, NodeVersion> {
     private final ApiClientFactory clientFactory;
 
     public NodeVersionCall(ApiClientFactory clientFactory) {
@@ -37,15 +40,16 @@ public class NodeVersionCall implements Call<UrlCallInput, 
String> {
     }
 
     @Override
-    public CallOutput<String> execute(UrlCallInput input) {
+    public CallOutput<NodeVersion> execute(UrlCallInput input) {
         try {
-            return DefaultCallOutput.success(getNodeVersion(input.getUrl()));
+            return success(getNodeVersion(input.getUrl()));
         } catch (ApiException | IllegalArgumentException e) {
-            return DefaultCallOutput.failure(new IgniteCliApiException(e, 
input.getUrl()));
+            return failure(new IgniteCliApiException(e, input.getUrl()));
         }
     }
 
-    private String getNodeVersion(String url) throws ApiException {
-        return new 
NodeManagementApi(clientFactory.getClient(url)).nodeVersion();
+    private NodeVersion getNodeVersion(String url) throws ApiException {
+        var nodeVersion = new 
NodeManagementApi(clientFactory.getClient(url)).nodeVersion();
+        return 
NodeVersion.builder().version(nodeVersion.getVersion()).product(nodeVersion.getProduct()).build();
     }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/decorators/DefaultDecoratorRegistry.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/decorators/DefaultDecoratorRegistry.java
index 8738aafa91..c4b6b8604a 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/decorators/DefaultDecoratorRegistry.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/decorators/DefaultDecoratorRegistry.java
@@ -27,6 +27,7 @@ import 
org.apache.ignite.internal.cli.core.decorator.Decorator;
 import org.apache.ignite.internal.cli.core.decorator.DecoratorRegistry;
 import org.apache.ignite.internal.cli.sql.SqlQueryResult;
 import org.apache.ignite.internal.cli.sql.table.Table;
+import org.apache.ignite.internal.rest.api.node.NodeVersion;
 
 /**
  * Default set of {@link Decorator}.
@@ -44,5 +45,6 @@ public class DefaultDecoratorRegistry extends 
DecoratorRegistry {
         add(SqlQueryResult.class, new SqlQueryResultDecorator(false));
         add(ClusterStatus.class, new ClusterStatusDecorator());
         add(NodeStatus.class, new NodeStatusDecorator());
+        add(NodeVersion.class, new NodeVersionDecorator());
     }
 }
diff --git 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/version/ItVersionCommandTest.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/decorators/NodeVersionDecorator.java
similarity index 55%
copy from 
modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/version/ItVersionCommandTest.java
copy to 
modules/cli/src/main/java/org/apache/ignite/internal/cli/decorators/NodeVersionDecorator.java
index eae2c2f7ff..82646756df 100644
--- 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/version/ItVersionCommandTest.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/decorators/NodeVersionDecorator.java
@@ -15,27 +15,20 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.cli.commands.version;
+package org.apache.ignite.internal.cli.decorators;
 
-import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.apache.ignite.internal.cli.core.style.AnsiStringSupport.ansi;
 
-import org.apache.ignite.internal.cli.CliIntegrationTest;
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Test;
+import org.apache.ignite.internal.cli.core.decorator.Decorator;
+import org.apache.ignite.internal.cli.core.decorator.TerminalOutput;
+import org.apache.ignite.internal.rest.api.node.NodeVersion;
 
-class ItVersionCommandTest extends CliIntegrationTest {
-
-    @Test
-    @DisplayName("Should print cli version that is got from pom.xml")
-    void printVersion() {
-        // When
-        execute("--version");
-
-        // Then
-        assertAll(
-                this::assertExitCodeIsZero,
-                this::assertErrOutputIsEmpty,
-                () -> assertOutputContains("Apache Ignite CLI ver")
-        );
+/**
+ * Decorator for {@link NodeVersion}.
+ */
+class NodeVersionDecorator implements Decorator<NodeVersion, TerminalOutput> {
+    @Override
+    public TerminalOutput decorate(NodeVersion data) {
+        return () -> ansi(String.format("%s version %s", data.product(), 
data.version()));
     }
 }
diff --git a/modules/cli/src/main/resources/version.properties 
b/modules/cli/src/main/resources/version.properties
index 0d488d370e..b87b65b251 100644
--- a/modules/cli/src/main/resources/version.properties
+++ b/modules/cli/src/main/resources/version.properties
@@ -16,3 +16,4 @@
 #
 
 version=${project.version}
+product=${project.product}
diff --git 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/CliCommandTestBase.java
 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/CliCommandTestBase.java
index fdbce95881..f830593d4e 100644
--- 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/CliCommandTestBase.java
+++ 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/CliCommandTestBase.java
@@ -24,6 +24,7 @@ import static org.hamcrest.Matchers.contains;
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.emptyString;
 import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.matchesRegex;
 import static org.hamcrest.Matchers.not;
 import static org.junit.jupiter.api.Assertions.assertAll;
 import static org.mockito.ArgumentMatchers.any;
@@ -120,6 +121,10 @@ public abstract class CliCommandTestBase extends 
BaseIgniteAbstractTest {
         assertThat("Unexpected command output", sout.toString(), 
allOf(matchers));
     }
 
+    protected void assertOutputMatches(String regex) {
+        assertThat("Unexpected command output", sout.toString(), 
matchesRegex(regex));
+    }
+
     protected void assertOutputIsEmpty() {
         assertThat("Unexpected command output", sout.toString(), 
is(emptyString()));
     }
diff --git 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/version/ItVersionCommandTest.java
 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/version/CliVersionTest.java
similarity index 74%
rename from 
modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/version/ItVersionCommandTest.java
rename to 
modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/version/CliVersionTest.java
index eae2c2f7ff..6d8609fd45 100644
--- 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/version/ItVersionCommandTest.java
+++ 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/version/CliVersionTest.java
@@ -19,14 +19,17 @@ package org.apache.ignite.internal.cli.commands.version;
 
 import static org.junit.jupiter.api.Assertions.assertAll;
 
-import org.apache.ignite.internal.cli.CliIntegrationTest;
-import org.junit.jupiter.api.DisplayName;
+import org.apache.ignite.internal.cli.commands.CliCommandTestBase;
+import org.apache.ignite.internal.cli.commands.TopLevelCliCommand;
 import org.junit.jupiter.api.Test;
 
-class ItVersionCommandTest extends CliIntegrationTest {
+class CliVersionTest extends CliCommandTestBase {
+    @Override
+    protected Class<?> getCommandClass() {
+        return TopLevelCliCommand.class;
+    }
 
     @Test
-    @DisplayName("Should print cli version that is got from pom.xml")
     void printVersion() {
         // When
         execute("--version");
@@ -35,7 +38,7 @@ class ItVersionCommandTest extends CliIntegrationTest {
         assertAll(
                 this::assertExitCodeIsZero,
                 this::assertErrOutputIsEmpty,
-                () -> assertOutputContains("Apache Ignite CLI ver")
+                () -> assertOutputMatches("Apache Ignite CLI version 
[1-9]\\d*\\.\\d+\\.\\d+(?:-[a-zA-Z0-9]+)?\\s+")
         );
     }
 }
diff --git a/modules/cli/src/test/resources/version.properties 
b/modules/cli/src/test/resources/version.properties
deleted file mode 100644
index 0d488d370e..0000000000
--- a/modules/cli/src/test/resources/version.properties
+++ /dev/null
@@ -1,18 +0,0 @@
-#
-# 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.
-#
-
-version=${project.version}
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/properties/IgniteProductVersion.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/properties/IgniteProductVersion.java
index 3753d6ad1f..439403cc30 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/properties/IgniteProductVersion.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/properties/IgniteProductVersion.java
@@ -59,6 +59,9 @@ public class IgniteProductVersion implements Serializable {
     /** Version of the current node. */
     public static final IgniteProductVersion CURRENT_VERSION = 
fromString(IgniteProperties.get(IgniteProperties.VERSION));
 
+    /** Product name of the current node. */
+    public static final String CURRENT_PRODUCT = 
IgniteProperties.get(IgniteProperties.PRODUCT);
+
     /** Major version number. */
     private final byte major;
 
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/properties/IgniteProperties.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/properties/IgniteProperties.java
index e74ce590ee..fadbd8a360 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/properties/IgniteProperties.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/properties/IgniteProperties.java
@@ -29,6 +29,9 @@ public class IgniteProperties {
     /** Ignite product version. */
     public static final String VERSION = "ignite.version";
 
+    /** Ignite product name. */
+    public static final String PRODUCT = "ignite.product";
+
     /** Properties file path. */
     private static final String FILE_PATH = "ignite.properties";
 
diff --git a/modules/core/src/main/resources/ignite.properties 
b/modules/core/src/main/resources/ignite.properties
index e34f8ba661..19f7945702 100644
--- a/modules/core/src/main/resources/ignite.properties
+++ b/modules/core/src/main/resources/ignite.properties
@@ -16,3 +16,4 @@
 #
 
 ignite.version=${project.version}
+ignite.product=${project.product}
diff --git 
a/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/api/node/NodeManagementApi.java
 
b/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/api/node/NodeManagementApi.java
index e0f692e2ba..dc058dd55d 100644
--- 
a/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/api/node/NodeManagementApi.java
+++ 
b/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/api/node/NodeManagementApi.java
@@ -63,9 +63,9 @@ public interface NodeManagementApi {
             description = "Gets the version of Apache Ignite the node uses."
     )
     @ApiResponse(responseCode = "200", description = "Node version.",
-            content = @Content(mediaType = MediaType.TEXT_PLAIN, schema = 
@Schema(type = "string")))
+            content = @Content(mediaType = MediaType.APPLICATION_JSON, schema 
= @Schema(implementation = NodeVersion.class)))
     @ApiResponse(responseCode = "500", description = "Internal error",
             content = @Content(mediaType = MediaType.PROBLEM_JSON, schema = 
@Schema(implementation = Problem.class)))
     @Produces(MediaType.PROBLEM_JSON)
-    String version();
+    NodeVersion version();
 }
diff --git 
a/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/api/node/NodeVersion.java
 
b/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/api/node/NodeVersion.java
new file mode 100644
index 0000000000..51adb2813a
--- /dev/null
+++ 
b/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/api/node/NodeVersion.java
@@ -0,0 +1,95 @@
+/*
+ * 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.node;
+
+import com.fasterxml.jackson.annotation.JsonGetter;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.media.Schema.RequiredMode;
+
+/**
+ * Node version information that is returned by REST.
+ */
+@Schema(description = "Node version.")
+public class NodeVersion {
+    @Schema(description = "Node version.", requiredMode = 
RequiredMode.REQUIRED)
+    private final String version;
+
+    @Schema(description = "Node product.", requiredMode = 
RequiredMode.REQUIRED)
+    private final String product;
+
+    /**
+     * Construct NodeVersion DTO.
+     */
+    private NodeVersion(String version, String product) {
+        this.version = version;
+        this.product = product;
+    }
+
+    @JsonGetter("version")
+    public String version() {
+        return version;
+    }
+
+    @JsonGetter("product")
+    public String product() {
+        return product;
+    }
+
+    /** Constructs new builder. */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /** Builder class. */
+    public static class Builder {
+        private String version;
+
+        private String product;
+
+        /**
+         * Sets version.
+         *
+         * @param version Version.
+         * @return This instance.
+         */
+        public Builder version(String version) {
+            this.version = version;
+            return this;
+        }
+
+        /**
+         * Sets product.
+         *
+         * @param product Product name.
+         * @return This instance.
+         */
+        public Builder product(String product) {
+            this.product = product;
+            return this;
+        }
+
+        /**
+         * Builds version object.
+         *
+         * @return Constructed version object.
+         */
+        public NodeVersion build() {
+            return new NodeVersion(version, product);
+        }
+    }
+}
diff --git 
a/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/ItInitializedClusterRestTest.java
 
b/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/ItInitializedClusterRestTest.java
index 4ee1e56744..fd9b395924 100644
--- 
a/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/ItInitializedClusterRestTest.java
+++ 
b/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/ItInitializedClusterRestTest.java
@@ -242,7 +242,10 @@ public class ItInitializedClusterRestTest extends 
AbstractRestTestBase {
 
         // Then
         assertThat(response.statusCode(), is(200));
-        // And version is a semver
-        assertThat(response.body(), 
matchesRegex(IgniteProductVersion.VERSION_PATTERN));
+        // And version contains product name and version which is a semver
+        assertAll(
+                () -> assertThat(response.body(), hasJsonPath("$.version", 
matchesRegex(IgniteProductVersion.VERSION_PATTERN))),
+                () -> assertThat(response.body(), hasJsonPath("$.product", 
is("Apache Ignite")))
+        );
     }
 }
diff --git 
a/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/ItNotInitializedClusterRestTest.java
 
b/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/ItNotInitializedClusterRestTest.java
index 6b6f8e3030..1b6cb3e606 100644
--- 
a/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/ItNotInitializedClusterRestTest.java
+++ 
b/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/ItNotInitializedClusterRestTest.java
@@ -111,8 +111,11 @@ public class ItNotInitializedClusterRestTest extends 
AbstractRestTestBase {
 
         // Then.
         assertThat(response.statusCode(), is(200));
-        // And version is a semver.
-        assertThat(response.body(), 
matchesRegex(IgniteProductVersion.VERSION_PATTERN));
+        // And version contains product name and version which is a semver
+        assertAll(
+                () -> assertThat(response.body(), hasJsonPath("$.version", 
matchesRegex(IgniteProductVersion.VERSION_PATTERN))),
+                () -> assertThat(response.body(), hasJsonPath("$.product", 
is("Apache Ignite")))
+        );
     }
 
     @Test
diff --git 
a/modules/rest/src/main/java/org/apache/ignite/internal/rest/node/NodeManagementController.java
 
b/modules/rest/src/main/java/org/apache/ignite/internal/rest/node/NodeManagementController.java
index 53b7e2c9ba..2a270f1c12 100644
--- 
a/modules/rest/src/main/java/org/apache/ignite/internal/rest/node/NodeManagementController.java
+++ 
b/modules/rest/src/main/java/org/apache/ignite/internal/rest/node/NodeManagementController.java
@@ -23,6 +23,7 @@ import org.apache.ignite.internal.rest.RestFactory;
 import org.apache.ignite.internal.rest.api.node.NodeInfo;
 import org.apache.ignite.internal.rest.api.node.NodeManagementApi;
 import org.apache.ignite.internal.rest.api.node.NodeState;
+import org.apache.ignite.internal.rest.api.node.NodeVersion;
 
 /**
  * REST endpoint allows to read node state.
@@ -55,8 +56,11 @@ public class NodeManagementController implements 
NodeManagementApi, RestFactory
     }
 
     @Override
-    public String version() {
-        return IgniteProductVersion.CURRENT_VERSION.toString();
+    public NodeVersion version() {
+        return NodeVersion.builder()
+                .version(IgniteProductVersion.CURRENT_VERSION.toString())
+                .product(IgniteProductVersion.CURRENT_PRODUCT)
+                .build();
     }
 
     @Override

Reply via email to