You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by sk...@apache.org on 2022/08/24 07:58:03 UTC
[ignite-3] branch main updated: IGNITE-17349 Added common UI components. Fixes #986
This is an automated email from the ASF dual-hosted git repository.
sk0x50 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 ad625c51a4 IGNITE-17349 Added common UI components. Fixes #986
ad625c51a4 is described below
commit ad625c51a4084f0ced38b645d4aafdb0b8157424
Author: Vadim Pakhnushev <86...@users.noreply.github.com>
AuthorDate: Wed Aug 24 10:57:23 2022 +0300
IGNITE-17349 Added common UI components. Fixes #986
Signed-off-by: Slava Koptilin <sl...@gmail.com>
---
.../ItUpdateConfigurationCallTest.java | 2 +-
.../ItClusterConfigCommandNotInitializedTest.java | 42 +++++++
.../cli/commands/connect/ItConnectCommandTest.java | 3 +-
.../ignite/cli/commands/sql/ItSqlCommandTest.java | 3 +-
.../cli/deprecated/ItClusterCommandTest.java | 2 +-
.../ignite/cli/deprecated/ItConfigCommandTest.java | 10 +-
.../ignite/rest/ItGeneratedRestClientTest.java | 12 ++
.../ignite/cli/call/cluster/ClusterInitCall.java | 3 +-
.../configuration/ClusterConfigUpdateCall.java | 2 +-
.../call/configuration/NodeConfigUpdateCall.java | 2 +-
.../ignite/cli/call/connect/ConnectCall.java | 5 +-
.../ignite/cli/call/connect/DisconnectCall.java | 7 +-
.../ignite/cli/commands/OptionsConstants.java | 24 ++--
.../commands/cliconfig/CliConfigGetSubCommand.java | 2 +-
.../commands/cliconfig/CliConfigSetSubCommand.java | 2 +-
.../commands/cliconfig/CliConfigSubCommand.java | 3 +-
.../profile/CliConfigCreateProfileCommand.java | 8 +-
.../cliconfig/profile/CliConfigProfileCommand.java | 4 +-
.../profile/CliConfigShowProfileCommand.java | 3 +-
.../cli/commands/cluster/ClusterCommand.java | 2 +-
.../cli/commands/cluster/ClusterReplCommand.java | 2 +-
.../config/ClusterConfigReplSubCommand.java | 2 +-
.../config/ClusterConfigShowReplSubCommand.java | 33 +-----
.../config/ClusterConfigShowSubCommand.java | 11 +-
.../cluster/config/ClusterConfigSubCommand.java | 3 +-
.../config/ClusterConfigUpdateReplSubCommand.java | 10 +-
.../config/ClusterConfigUpdateSubCommand.java | 10 +-
.../cluster/init/ClusterInitReplSubCommand.java | 11 +-
.../cluster/init/ClusterInitSubCommand.java | 7 +-
.../status/ClusterStatusReplSubCommand.java | 13 +-
.../cluster/status/ClusterStatusSubCommand.java | 9 +-
.../cli/commands/connect/ConnectCommand.java | 10 +-
.../cli/commands/connect/DisconnectCommand.java | 2 +-
.../ignite/cli/commands/node/NodeCommand.java | 3 +-
.../ignite/cli/commands/node/NodeReplCommand.java | 2 +-
.../node/config/NodeConfigReplSubCommand.java | 3 +-
.../node/config/NodeConfigShowSubCommand.java | 13 +-
.../commands/node/config/NodeConfigSubCommand.java | 3 +-
.../node/config/NodeConfigUpdateSubCommand.java | 10 +-
.../node/status/NodeStatusReplSubCommand.java | 13 +-
.../commands/node/status/NodeStatusSubCommand.java | 9 +-
.../questions/ConnectToClusterQuestion.java | 27 +++--
.../apache/ignite/cli/commands/sql/SqlCommand.java | 12 +-
.../ignite/cli/commands/sql/SqlReplCommand.java | 10 +-
.../commands/sql/SqlReplTopLevelCliCommand.java | 2 +-
.../topology/LogicalTopologyReplSubCommand.java | 12 +-
.../topology/LogicalTopologySubCommand.java | 6 +-
.../topology/PhysicalTopologyReplSubCommand.java | 12 +-
.../topology/PhysicalTopologySubCommand.java | 6 +-
.../cli/commands/topology/TopologyCommand.java | 11 +-
.../cli/commands/topology/TopologyReplCommand.java | 11 +-
.../cli/commands/version/VersionCommand.java | 3 +-
.../config/ini/SectionAlreadyExistsException.java | 2 +-
.../cli/core/call/CallExecutionPipeline.java | 29 ++---
.../cli/core/decorator/DecoratorRegistry.java | 2 +-
.../cli/core/exception/ExceptionHandler.java | 10 +-
.../handler/ConfigStoringExceptionHandler.java | 10 +-
.../handler/IgniteCliApiExceptionHandler.java | 53 +++++++--
.../handler/IgniteCliExceptionHandler.java | 6 +-
.../handler/PicocliExecutionExceptionHandler.java | 2 +-
.../handler/ProfileNotFoundExceptionHandler.java | 6 +-
.../SectionAlreadyExistsExceptionHandler.java | 6 +-
.../handler/ShowConfigExceptionHandler.java | 49 ++++++++
.../exception/handler/SqlExceptionHandler.java | 23 ++--
.../exception/handler/TimeoutExceptionHandler.java | 10 +-
.../handler/UnknownCommandExceptionHandler.java | 5 +-
.../cli/core/flow/builder/FlowBuilderImpl.java | 2 +-
.../apache/ignite/cli/core/flow/builder/Flows.java | 17 ++-
.../ignite/cli/core/style/AnsiStringSupport.java | 41 ++++++-
.../style/component/CommonMessages.java} | 26 ++--
.../cli/core/style/component/ErrorUiComponent.java | 132 +++++++++++++++++++++
.../core/style/component/MessageUiComponent.java | 95 +++++++++++++++
.../core/style/component/QuestionUiComponent.java | 71 +++++++++++
.../style/component/UiComponent.java} | 10 +-
.../style/element/MarkedUiElement.java} | 29 +++--
.../style/element/UiElement.java} | 11 +-
.../style/element/UiElements.java} | 36 +++---
.../style/element/UiString.java} | 23 ++--
.../decorators/ClusterStatusDecorator.java | 3 +-
.../decorators/DefaultDecorator.java | 3 +-
.../decorators/DefaultDecoratorRegistry.java | 3 +-
.../{commands => }/decorators/JsonDecorator.java | 3 +-
.../decorators/NodeStatusDecorator.java | 3 +-
.../decorators/ProfileDecorator.java | 2 +-
.../decorators/SqlQueryResultDecorator.java | 3 +-
.../{commands => }/decorators/TableDecorator.java | 3 +-
.../decorators/TopologyDecorator.java | 3 +-
.../builtins/init/InitIgniteCommand.java | 46 ++++---
.../cli/deprecated/spec/NodeCommandSpec.java | 59 +++++----
.../ignite/cli/deprecated/ui/ProgressBar.java | 4 +-
.../org/apache/ignite/cli/sql/SqlQueryResult.java | 3 +-
.../cli/commands/UrlOptionsNegativeTest.java | 11 +-
.../apache/ignite/cli/commands/flow/FlowTest.java | 71 +++++++++++
.../cli/commands/flow/TestExceptionHandler.java} | 18 ++-
.../ignite/cli/commands/flow/ThrowingStrCall.java} | 16 +--
.../core/style/component/ErrorUiComponentTest.java | 81 +++++++++++++
.../style/component/MessageUiComponentTest.java | 69 +++++++++++
.../cli/deprecated/IgniteCliInterfaceTest.java | 48 ++++----
.../ignite/cli/deprecated/ui/ProgressBarTest.java | 4 +-
.../ConfigurationValidationException.java | 8 +-
.../ConfigurationControllerBaseTest.java | 18 +--
.../java/org/apache/ignite/lang/ErrorGroup.java | 21 +++-
.../org/apache/ignite/lang/ErrorGroupTest.java | 49 ++++++++
.../apache/ignite/internal/rest/api/Problem.java | 23 +++-
.../internal/rest/api/ValidationProblem.java | 111 -----------------
.../exception/handler/IgniteExceptionHandler.java | 22 +++-
.../handler/IgniteExceptionHandlerTest.java | 12 +-
.../rest/ItInitializedClusterRestTest.java | 15 +++
108 files changed, 1257 insertions(+), 616 deletions(-)
diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/call/configuration/ItUpdateConfigurationCallTest.java b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/call/configuration/ItUpdateConfigurationCallTest.java
index 36286753b7..b1d0ec09fe 100644
--- a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/call/configuration/ItUpdateConfigurationCallTest.java
+++ b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/call/configuration/ItUpdateConfigurationCallTest.java
@@ -54,7 +54,7 @@ public class ItUpdateConfigurationCallTest extends CallInitializedIntegrationTes
// Then
assertThat(output.hasError()).isFalse();
// And
- assertThat(output.body()).contains("Cluster configuration was updated successfully.");
+ assertThat(output.body()).contains("Cluster configuration was updated successfully");
// And buffer size is updated
String updatedConfigurationProperty = readConfigurationProperty("rocksDb.defaultRegion.writeBufferSize");
assertThat(updatedConfigurationProperty).isEqualTo("1024");
diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/cluster/config/ItClusterConfigCommandNotInitializedTest.java b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/cluster/config/ItClusterConfigCommandNotInitializedTest.java
new file mode 100644
index 0000000000..072bab833c
--- /dev/null
+++ b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/cluster/config/ItClusterConfigCommandNotInitializedTest.java
@@ -0,0 +1,42 @@
+/*
+ * 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.cli.commands.cluster.config;
+
+import static org.junit.jupiter.api.Assertions.assertAll;
+
+import org.apache.ignite.cli.commands.CliCommandTestNotInitializedIntegrationBase;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for {@link ClusterConfigSubCommand} for the cluster that is not initialized.
+ */
+class ItClusterConfigCommandNotInitializedTest extends CliCommandTestNotInitializedIntegrationBase {
+ @Test
+ @DisplayName("Should print error message when run cluster config show on not initialized cluster")
+ void printStatus() {
+ execute("cluster", "config", "show", "--cluster-endpoint-url", NODE_URL);
+
+ assertAll(
+ this::assertOutputIsEmpty,
+ () -> assertExitCodeIs(1),
+ () -> assertErrOutputContains("Cannot show cluster config" + System.lineSeparator()
+ + "Probably, you have not initialized the cluster, try to run cluster init command")
+ );
+ }
+}
diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/connect/ItConnectCommandTest.java b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/connect/ItConnectCommandTest.java
index d49e0699fe..c1fc2ba388 100644
--- a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/connect/ItConnectCommandTest.java
+++ b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/connect/ItConnectCommandTest.java
@@ -78,7 +78,8 @@ class ItConnectCommandTest extends CliCommandTestInitializedIntegrationBase {
// Then
assertAll(
- () -> assertErrOutputIs("Could not connect to URL [url=http://localhost:11111]" + System.lineSeparator())
+ () -> assertErrOutputIs("Node unavailable" + System.lineSeparator()
+ + "Could not connect to node with URL http://localhost:11111" + System.lineSeparator())
);
// And prompt is
String prompt = Ansi.OFF.string(promptProvider.getPrompt());
diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/sql/ItSqlCommandTest.java b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/sql/ItSqlCommandTest.java
index 861e9f8b34..b0d2ce5253 100644
--- a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/sql/ItSqlCommandTest.java
+++ b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/sql/ItSqlCommandTest.java
@@ -77,7 +77,8 @@ class ItSqlCommandTest extends CliCommandTestInitializedIntegrationBase {
() -> assertExitCodeIs(1),
this::assertOutputIsEmpty,
// TODO: https://issues.apache.org/jira/browse/IGNITE-17090
- () -> assertErrOutputIs("SQL query parsing error: Sql query execution failed." + System.lineSeparator())
+ () -> assertErrOutputIs("SQL query parsing error" + System.lineSeparator()
+ + "Sql query execution failed." + System.lineSeparator())
);
}
}
diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/deprecated/ItClusterCommandTest.java b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/deprecated/ItClusterCommandTest.java
index a8aaaf2f8b..9ca94bad23 100644
--- a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/deprecated/ItClusterCommandTest.java
+++ b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/deprecated/ItClusterCommandTest.java
@@ -181,7 +181,7 @@ class ItClusterCommandTest extends AbstractCliIntegrationTest {
String.format("Wrong exit code; std is '%s', stderr is '%s'", out.toString(UTF_8), err.toString(UTF_8)),
exitCode, is(0)
);
- assertThat(out.toString(UTF_8), is("Cluster was initialized successfully." + NL));
+ assertThat(out.toString(UTF_8), is("Cluster was initialized successfully" + NL));
// TODO: when IGNITE-16526 is implemented, also check that the logical topology contains all 4 nodes
}
diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/deprecated/ItConfigCommandTest.java b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/deprecated/ItConfigCommandTest.java
index c0aad09f5f..ea2c4ca0cc 100644
--- a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/deprecated/ItConfigCommandTest.java
+++ b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/deprecated/ItConfigCommandTest.java
@@ -21,9 +21,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import static org.apache.ignite.internal.testframework.IgniteTestUtils.testNodeName;
import static org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willCompleteSuccessfully;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.both;
import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.startsWith;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -90,7 +88,7 @@ public class ItConfigCommandTest extends AbstractCliIntegrationTest {
assertEquals(0, exitCode);
assertThat(
out.toString(UTF_8),
- containsString("Node configuration was updated successfully.")
+ containsString("Node configuration was updated successfully")
);
@@ -126,8 +124,7 @@ public class ItConfigCommandTest extends AbstractCliIntegrationTest {
assertEquals(1, exitCode);
assertThat(
err.toString(UTF_8),
- both(startsWith("An error occurred"))
- .and(containsString("'network' configuration doesn't have the 'foo' sub-configuration"))
+ containsString("'network' configuration doesn't have the 'foo' sub-configuration")
);
resetStreams();
@@ -144,8 +141,7 @@ public class ItConfigCommandTest extends AbstractCliIntegrationTest {
assertEquals(1, exitCode);
assertThat(
err.toString(UTF_8),
- both(containsString("An error occurred"))
- .and(containsString("'long' is expected as a type for the 'network.shutdownQuietPeriod' configuration value"))
+ containsString("'long' is expected as a type for the 'network.shutdownQuietPeriod' configuration value")
);
}
diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/rest/ItGeneratedRestClientTest.java b/modules/cli/src/integrationTest/java/org/apache/ignite/rest/ItGeneratedRestClientTest.java
index ed0b89ee0f..5f2619cff6 100644
--- a/modules/cli/src/integrationTest/java/org/apache/ignite/rest/ItGeneratedRestClientTest.java
+++ b/modules/cli/src/integrationTest/java/org/apache/ignite/rest/ItGeneratedRestClientTest.java
@@ -242,6 +242,18 @@ public class ItGeneratedRestClientTest {
});
}
+ @Test
+ void updateNodeConfigurationWithInvalidParam() throws JsonProcessingException {
+ ApiException thrown = assertThrows(
+ ApiException.class,
+ () -> clusterConfigurationApi.updateClusterConfiguration("rocksDb.defaultRegion.cache=invalid")
+ );
+
+ Problem problem = objectMapper.readValue(thrown.getResponseBody(), Problem.class);
+ assertThat(problem.getStatus(), equalTo(400));
+ assertThat(problem.getInvalidParams(), hasSize(1));
+ }
+
@Test
void initCluster() {
assertDoesNotThrow(() -> {
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/call/cluster/ClusterInitCall.java b/modules/cli/src/main/java/org/apache/ignite/cli/call/cluster/ClusterInitCall.java
index ec0f86296b..dbdc0474fc 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/call/cluster/ClusterInitCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/call/cluster/ClusterInitCall.java
@@ -32,7 +32,6 @@ import org.apache.ignite.rest.client.model.InitCommand;
*/
@Singleton
public class ClusterInitCall implements Call<ClusterInitCallInput, String> {
-
/** {@inheritDoc} */
@Override
public DefaultCallOutput<String> execute(ClusterInitCallInput input) {
@@ -44,7 +43,7 @@ public class ClusterInitCall implements Call<ClusterInitCallInput, String> {
.cmgNodes(input.getCmgNodes())
.clusterName(input.getClusterName())
);
- return DefaultCallOutput.success("Cluster was initialized successfully.");
+ return DefaultCallOutput.success("Cluster was initialized successfully");
} catch (ApiException | IllegalArgumentException e) {
return DefaultCallOutput.failure(new IgniteCliApiException(e, input.getClusterUrl()));
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/call/configuration/ClusterConfigUpdateCall.java b/modules/cli/src/main/java/org/apache/ignite/cli/call/configuration/ClusterConfigUpdateCall.java
index 52770eb25a..0481c872e0 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/call/configuration/ClusterConfigUpdateCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/call/configuration/ClusterConfigUpdateCall.java
@@ -46,7 +46,7 @@ public class ClusterConfigUpdateCall implements Call<ClusterConfigUpdateCallInpu
private DefaultCallOutput<String> updateClusterConfig(ClusterConfigurationApi api, ClusterConfigUpdateCallInput input)
throws ApiException {
api.updateClusterConfiguration(input.getConfig());
- return DefaultCallOutput.success("Cluster configuration was updated successfully.");
+ return DefaultCallOutput.success("Cluster configuration was updated successfully");
}
private ClusterConfigurationApi createApiClient(ClusterConfigUpdateCallInput input) {
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/call/configuration/NodeConfigUpdateCall.java b/modules/cli/src/main/java/org/apache/ignite/cli/call/configuration/NodeConfigUpdateCall.java
index 3824411555..a568bcf9ed 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/call/configuration/NodeConfigUpdateCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/call/configuration/NodeConfigUpdateCall.java
@@ -46,7 +46,7 @@ public class NodeConfigUpdateCall implements Call<NodeConfigUpdateCallInput, Str
private DefaultCallOutput<String> updateNodeConfig(NodeConfigurationApi api, NodeConfigUpdateCallInput input)
throws ApiException {
api.updateNodeConfiguration(input.getConfig());
- return DefaultCallOutput.success("Node configuration was updated successfully.");
+ return DefaultCallOutput.success("Node configuration was updated successfully");
}
private NodeConfigurationApi createApiClient(NodeConfigUpdateCallInput input) {
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/call/connect/ConnectCall.java b/modules/cli/src/main/java/org/apache/ignite/cli/call/connect/ConnectCall.java
index bf72de7750..6aaf320516 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/call/connect/ConnectCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/call/connect/ConnectCall.java
@@ -29,6 +29,8 @@ import org.apache.ignite.cli.core.call.DefaultCallOutput;
import org.apache.ignite.cli.core.exception.IgniteCliApiException;
import org.apache.ignite.cli.core.repl.Session;
import org.apache.ignite.cli.core.repl.config.RootConfig;
+import org.apache.ignite.cli.core.style.component.MessageUiComponent;
+import org.apache.ignite.cli.core.style.element.UiElements;
import org.apache.ignite.rest.client.api.NodeConfigurationApi;
import org.apache.ignite.rest.client.api.NodeManagementApi;
import org.apache.ignite.rest.client.invoker.ApiException;
@@ -40,7 +42,6 @@ import org.apache.ignite.rest.client.invoker.Configuration;
*/
@Singleton
public class ConnectCall implements Call<ConnectCallInput, String> {
-
private final Session session;
private final StateConfigProvider stateConfigProvider;
@@ -61,7 +62,7 @@ public class ConnectCall implements Call<ConnectCallInput, String> {
session.setJdbcUrl(constructJdbcUrl(configuration, nodeUrl));
session.setConnectedToNode(true);
- return DefaultCallOutput.success("Connected to " + nodeUrl);
+ return DefaultCallOutput.success(MessageUiComponent.fromMessage("Connected to %s", UiElements.url(nodeUrl)).render());
} catch (ApiException | IllegalArgumentException e) {
session.setConnectedToNode(false);
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/call/connect/DisconnectCall.java b/modules/cli/src/main/java/org/apache/ignite/cli/call/connect/DisconnectCall.java
index e6c5404a85..bb60ee059e 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/call/connect/DisconnectCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/call/connect/DisconnectCall.java
@@ -24,13 +24,14 @@ import org.apache.ignite.cli.core.call.CallOutput;
import org.apache.ignite.cli.core.call.DefaultCallOutput;
import org.apache.ignite.cli.core.call.EmptyCallInput;
import org.apache.ignite.cli.core.repl.Session;
+import org.apache.ignite.cli.core.style.component.MessageUiComponent;
+import org.apache.ignite.cli.core.style.element.UiElements;
/**
* Call for disconnect.
*/
@Singleton
public class DisconnectCall implements Call<EmptyCallInput, String> {
-
@Inject
private final Session session;
@@ -46,7 +47,9 @@ public class DisconnectCall implements Call<EmptyCallInput, String> {
session.setNodeName(null);
session.setConnectedToNode(false);
- return DefaultCallOutput.success("Disconnected from " + nodeUrl);
+ return DefaultCallOutput.success(
+ MessageUiComponent.fromMessage("Disconnected from %s", UiElements.url(nodeUrl)).render()
+ );
}
return DefaultCallOutput.empty();
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/OptionsConstants.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/OptionsConstants.java
index 934e72d2ad..5cd52a3b7c 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/OptionsConstants.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/OptionsConstants.java
@@ -23,28 +23,18 @@ import org.apache.ignite.cli.config.ConfigConstants;
* Constants to use in {@code Option} annotations for commands.
*/
public class OptionsConstants {
- /**
- * Cluster endpoint URL option name.
- */
+ /** Cluster endpoint URL option name. */
public static final String CLUSTER_URL_OPTION = "--cluster-endpoint-url";
- /**
- * Cluster endpoint URL option description.
- */
- public static final String CLUSTER_URL_DESC = "URL of cluster endpoint.";
+ /** Cluster endpoint URL option description. */
+ public static final String CLUSTER_URL_DESC = "URL of cluster endpoint";
- /**
- * Cluster endpoint URL option description key.
- */
+ /** Cluster endpoint URL option description key. */
public static final String CLUSTER_URL_KEY = ConfigConstants.CLUSTER_URL;
- /**
- * Node URL option name.
- */
+ /** Node URL option name. */
public static final String NODE_URL_OPTION = "--node-url";
- /**
- * Node URL option description.
- */
- public static final String NODE_URL_DESC = "URL of ignite node.";
+ /** Node URL option description. */
+ public static final String NODE_URL_DESC = "URL of ignite node";
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigGetSubCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigGetSubCommand.java
index 46d9388ceb..2b43922113 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigGetSubCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigGetSubCommand.java
@@ -35,7 +35,7 @@ public class CliConfigGetSubCommand extends BaseCommand implements Callable<Inte
@Parameters
private String key;
- @Option(names = {"--profile", "-p"}, description = "Get property from specified profile.")
+ @Option(names = {"--profile", "-p"}, description = "Get property from specified profile")
private String profileName;
@Inject
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigSetSubCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigSetSubCommand.java
index 1bfc8bba64..7fafe3c9fa 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigSetSubCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigSetSubCommand.java
@@ -36,7 +36,7 @@ public class CliConfigSetSubCommand extends BaseCommand implements Callable<Inte
@Parameters(arity = "1..*")
private Map<String, String> parameters;
- @Option(names = {"--profile", "-p"}, description = "Set property in specified profile.")
+ @Option(names = {"--profile", "-p"}, description = "Set property in specified profile")
private String profileName;
@Inject
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigSubCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigSubCommand.java
index bd7dcd7500..8c723dc8fe 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigSubCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/CliConfigSubCommand.java
@@ -23,9 +23,9 @@ import java.util.concurrent.Callable;
import org.apache.ignite.cli.call.cliconfig.CliConfigCall;
import org.apache.ignite.cli.commands.BaseCommand;
import org.apache.ignite.cli.commands.cliconfig.profile.CliConfigProfileCommand;
-import org.apache.ignite.cli.commands.decorators.ProfileDecorator;
import org.apache.ignite.cli.core.call.CallExecutionPipeline;
import org.apache.ignite.cli.core.call.StringCallInput;
+import org.apache.ignite.cli.decorators.ProfileDecorator;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
@@ -39,7 +39,6 @@ import picocli.CommandLine.Option;
})
@Singleton
public class CliConfigSubCommand extends BaseCommand implements Callable<Integer> {
-
@Option(names = {"--profile", "-p"})
private String profileName;
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/profile/CliConfigCreateProfileCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/profile/CliConfigCreateProfileCommand.java
index 848e9cfc13..e41ffa90c9 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/profile/CliConfigCreateProfileCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/profile/CliConfigCreateProfileCommand.java
@@ -29,15 +29,15 @@ import picocli.CommandLine.Option;
/**
* Command for create CLI profile.
*/
-@Command(name = "create", description = "Create profile command.")
+@Command(name = "create", description = "Create profile command")
public class CliConfigCreateProfileCommand extends BaseCommand implements Callable<Integer> {
- @Option(names = {"--name", "-n"}, required = true, description = "Name of new profile.")
+ @Option(names = {"--name", "-n"}, required = true, description = "Name of new profile")
private String profileName;
- @Option(names = {"--copy-from", "-c"}, description = "Profile whose content will be copied to new one.")
+ @Option(names = {"--copy-from", "-c"}, description = "Profile whose content will be copied to new one")
private String copyFrom;
- @Option(names = {"--activate", "-a"}, description = "Activate new profile as current or not.")
+ @Option(names = {"--activate", "-a"}, description = "Activate new profile as current or not")
private boolean activate;
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/profile/CliConfigProfileCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/profile/CliConfigProfileCommand.java
index dd83cd92ec..9ea13f82af 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/profile/CliConfigProfileCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/profile/CliConfigProfileCommand.java
@@ -30,13 +30,13 @@ import picocli.CommandLine.Option;
* Root profile command.
*/
@Command(name = "profile",
- description = "Create profile command.",
+ description = "Create profile command",
subcommands = {
CliConfigCreateProfileCommand.class,
CliConfigShowProfileCommand.class
})
public class CliConfigProfileCommand extends BaseCommand implements Callable<Integer> {
- @Option(names = {"--set-current", "-s"}, description = "Name of profile which should be activated as default.")
+ @Option(names = {"--set-current", "-s"}, description = "Name of profile which should be activated as default")
private String profileName;
@Inject
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/profile/CliConfigShowProfileCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/profile/CliConfigShowProfileCommand.java
index ca300b7e33..ae5b3b49a5 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/profile/CliConfigShowProfileCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cliconfig/profile/CliConfigShowProfileCommand.java
@@ -28,9 +28,8 @@ import picocli.CommandLine;
/**
* Show current profile command.
*/
-@CommandLine.Command(name = "show", description = "Show current default profile.")
+@CommandLine.Command(name = "show", description = "Show current default profile")
public class CliConfigShowProfileCommand extends BaseCommand implements Callable<Integer> {
-
@Inject
private CliConfigShowProfileCall call;
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/ClusterCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/ClusterCommand.java
index fb73a8be44..afee472c91 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/ClusterCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/ClusterCommand.java
@@ -32,6 +32,6 @@ import picocli.CommandLine.Command;
ClusterInitSubCommand.class,
ClusterStatusSubCommand.class,
TopologyCommand.class},
- description = "Manages an Ignite cluster.")
+ description = "Manages an Ignite cluster")
public class ClusterCommand {
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/ClusterReplCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/ClusterReplCommand.java
index 44e517bb7b..78482ac30e 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/ClusterReplCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/ClusterReplCommand.java
@@ -33,6 +33,6 @@ import picocli.CommandLine.Command;
ClusterStatusReplSubCommand.class,
TopologyReplCommand.class
},
- description = "Manages an Ignite cluster.")
+ description = "Manages an Ignite cluster")
public class ClusterReplCommand {
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/config/ClusterConfigReplSubCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/config/ClusterConfigReplSubCommand.java
index ecb6c6a94c..d9d3be021b 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/config/ClusterConfigReplSubCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/config/ClusterConfigReplSubCommand.java
@@ -24,7 +24,7 @@ import picocli.CommandLine.Command;
*/
@Command(name = "config",
subcommands = {ClusterConfigShowReplSubCommand.class, ClusterConfigUpdateReplSubCommand.class},
- description = "Cluster config operations.")
+ description = "Cluster config operations")
public class ClusterConfigReplSubCommand {
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/config/ClusterConfigShowReplSubCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/config/ClusterConfigShowReplSubCommand.java
index 8ec239b9ba..e01e670d13 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/config/ClusterConfigShowReplSubCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/config/ClusterConfigShowReplSubCommand.java
@@ -26,12 +26,9 @@ import org.apache.ignite.cli.call.configuration.ClusterConfigShowCall;
import org.apache.ignite.cli.call.configuration.ClusterConfigShowCallInput;
import org.apache.ignite.cli.commands.BaseCommand;
import org.apache.ignite.cli.commands.questions.ConnectToClusterQuestion;
-import org.apache.ignite.cli.core.exception.ExceptionWriter;
-import org.apache.ignite.cli.core.exception.IgniteCliApiException;
-import org.apache.ignite.cli.core.exception.handler.IgniteCliApiExceptionHandler;
+import org.apache.ignite.cli.core.exception.handler.ShowConfigExceptionHandler;
import org.apache.ignite.cli.core.flow.Flowable;
import org.apache.ignite.cli.core.flow.builder.Flows;
-import org.apache.ignite.rest.client.invoker.ApiException;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;
@@ -40,18 +37,13 @@ import picocli.CommandLine.Parameters;
* Command that shows configuration from the cluster in REPL mode.
*/
@Command(name = "show",
- description = "Shows cluster configuration.")
+ description = "Shows cluster configuration")
public class ClusterConfigShowReplSubCommand extends BaseCommand implements Runnable {
-
- /**
- * Configuration selector option.
- */
+ /** Configuration selector option. */
@Parameters(arity = "0..1", description = "Configuration path selector")
private String selector;
- /**
- * Cluster endpoint URL option.
- */
+ /** Cluster endpoint URL option. */
@Option(names = {CLUSTER_URL_OPTION}, description = CLUSTER_URL_DESC, descriptionKey = CLUSTER_URL_KEY)
private String clusterUrl;
@@ -64,10 +56,10 @@ public class ClusterConfigShowReplSubCommand extends BaseCommand implements Runn
@Override
public void run() {
question.askQuestionIfNotConnected(clusterUrl)
+ .exceptionHandler(new ShowConfigExceptionHandler())
.map(this::configShowCallInput)
.then(Flows.fromCall(call))
.toOutput(spec.commandLine().getOut(), spec.commandLine().getErr())
- .exceptionHandler(new ShowConfigReplExceptionHandler())
.build()
.start(Flowable.empty());
}
@@ -75,19 +67,4 @@ public class ClusterConfigShowReplSubCommand extends BaseCommand implements Runn
private ClusterConfigShowCallInput configShowCallInput(String clusterUrl) {
return ClusterConfigShowCallInput.builder().selector(selector).clusterUrl(clusterUrl).build();
}
-
- private static class ShowConfigReplExceptionHandler extends IgniteCliApiExceptionHandler {
- @Override
- public int handle(ExceptionWriter err, IgniteCliApiException e) {
- if (e.getCause() instanceof ApiException) {
- ApiException apiException = (ApiException) e.getCause();
- if (apiException.getCode() == 500) { //TODO: https://issues.apache.org/jira/browse/IGNITE-17091
- err.write("Cannot show cluster config, probably you have not initialized the cluster. "
- + "Try to run 'cluster init' command.");
- return 1;
- }
- }
- return super.handle(err, e);
- }
- }
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/config/ClusterConfigShowSubCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/config/ClusterConfigShowSubCommand.java
index 2dda323076..12593cfbf9 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/config/ClusterConfigShowSubCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/config/ClusterConfigShowSubCommand.java
@@ -26,8 +26,9 @@ import java.util.concurrent.Callable;
import org.apache.ignite.cli.call.configuration.ClusterConfigShowCall;
import org.apache.ignite.cli.call.configuration.ClusterConfigShowCallInput;
import org.apache.ignite.cli.commands.BaseCommand;
-import org.apache.ignite.cli.commands.decorators.JsonDecorator;
import org.apache.ignite.cli.core.call.CallExecutionPipeline;
+import org.apache.ignite.cli.core.exception.handler.ShowConfigExceptionHandler;
+import org.apache.ignite.cli.decorators.JsonDecorator;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;
@@ -36,12 +37,9 @@ import picocli.CommandLine.Parameters;
* Command that shows configuration from the cluster.
*/
@Command(name = "show",
- description = "Shows cluster configuration.")
+ description = "Shows cluster configuration")
public class ClusterConfigShowSubCommand extends BaseCommand implements Callable<Integer> {
-
- /**
- * Configuration selector option.
- */
+ /** Configuration selector option. */
@Parameters(arity = "0..1", description = "Configuration path selector")
private String selector;
@@ -62,6 +60,7 @@ public class ClusterConfigShowSubCommand extends BaseCommand implements Callable
.output(spec.commandLine().getOut())
.errOutput(spec.commandLine().getErr())
.decorator(new JsonDecorator())
+ .exceptionHandler(new ShowConfigExceptionHandler())
.build()
.runPipeline();
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/config/ClusterConfigSubCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/config/ClusterConfigSubCommand.java
index 8d8eb57be5..7449889cc9 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/config/ClusterConfigSubCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/config/ClusterConfigSubCommand.java
@@ -24,7 +24,6 @@ import picocli.CommandLine.Command;
*/
@Command(name = "config",
subcommands = {ClusterConfigShowSubCommand.class, ClusterConfigUpdateSubCommand.class},
- description = "Cluster config operations.")
+ description = "Cluster config operations")
public class ClusterConfigSubCommand {
-
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/config/ClusterConfigUpdateReplSubCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/config/ClusterConfigUpdateReplSubCommand.java
index 5e8c759200..90eadb9daa 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/config/ClusterConfigUpdateReplSubCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/config/ClusterConfigUpdateReplSubCommand.java
@@ -37,18 +37,14 @@ import picocli.CommandLine.Parameters;
* Command that updates cluster configuration in REPL mode.
*/
@Command(name = "update",
- description = "Updates cluster configuration.")
+ description = "Updates cluster configuration")
@Singleton
public class ClusterConfigUpdateReplSubCommand extends BaseCommand implements Runnable {
- /**
- * Cluster endpoint URL option.
- */
+ /** Cluster endpoint URL option. */
@Option(names = {CLUSTER_URL_OPTION}, description = CLUSTER_URL_DESC, descriptionKey = CLUSTER_URL_KEY)
private String clusterUrl;
- /**
- * Configuration that will be updated.
- */
+ /** Configuration that will be updated. */
@Parameters(index = "0")
private String config;
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/config/ClusterConfigUpdateSubCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/config/ClusterConfigUpdateSubCommand.java
index b5d337b3a1..1852ccd1af 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/config/ClusterConfigUpdateSubCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/config/ClusterConfigUpdateSubCommand.java
@@ -36,21 +36,17 @@ import picocli.CommandLine.Parameters;
* Command that updates cluster configuration.
*/
@Command(name = "update",
- description = "Updates cluster configuration.")
+ description = "Updates cluster configuration")
@Singleton
public class ClusterConfigUpdateSubCommand extends BaseCommand implements Callable<Integer> {
@Inject
ClusterConfigUpdateCall call;
- /**
- * Cluster endpoint URL option.
- */
+ /** Cluster endpoint URL option. */
@Option(names = {CLUSTER_URL_OPTION}, description = CLUSTER_URL_DESC, descriptionKey = CLUSTER_URL_KEY)
private String clusterUrl;
- /**
- * Configuration that will be updated.
- */
+ /** Configuration that will be updated. */
@Parameters(index = "0")
private String config;
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/init/ClusterInitReplSubCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/init/ClusterInitReplSubCommand.java
index 61f8ea774c..70f923b2e3 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/init/ClusterInitReplSubCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/init/ClusterInitReplSubCommand.java
@@ -20,6 +20,7 @@ package org.apache.ignite.cli.commands.cluster.init;
import static org.apache.ignite.cli.commands.OptionsConstants.CLUSTER_URL_DESC;
import static org.apache.ignite.cli.commands.OptionsConstants.CLUSTER_URL_KEY;
import static org.apache.ignite.cli.commands.OptionsConstants.CLUSTER_URL_OPTION;
+import static org.apache.ignite.cli.core.style.component.CommonMessages.CONNECT_OR_USE_CLUSTER_URL_MESSAGE;
import static picocli.CommandLine.Command;
import jakarta.inject.Inject;
@@ -36,12 +37,9 @@ import picocli.CommandLine.Option;
/**
* Initializes an Ignite cluster.
*/
-@Command(name = "init", description = "Initializes an Ignite cluster.")
+@Command(name = "init", description = "Initializes an Ignite cluster")
public class ClusterInitReplSubCommand extends BaseCommand implements Runnable {
-
- /**
- * Cluster endpoint URL option.
- */
+ /** Cluster endpoint URL option. */
@Option(
names = {CLUSTER_URL_OPTION}, description = CLUSTER_URL_DESC, descriptionKey = CLUSTER_URL_KEY,
defaultValue = "http://localhost:10300"
@@ -89,8 +87,7 @@ public class ClusterInitReplSubCommand extends BaseCommand implements Runnable {
} else if (clusterUrl != null) {
input.clusterUrl(clusterUrl);
} else {
- spec.commandLine().getErr().println("You are not connected to node. Run 'connect' command or use '"
- + CLUSTER_URL_OPTION + "' option.");
+ spec.commandLine().getErr().println(CONNECT_OR_USE_CLUSTER_URL_MESSAGE.render());
return;
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/init/ClusterInitSubCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/init/ClusterInitSubCommand.java
index dbe2d66301..4f6998c954 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/init/ClusterInitSubCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/init/ClusterInitSubCommand.java
@@ -35,12 +35,9 @@ import picocli.CommandLine.Option;
/**
* Initializes an Ignite cluster.
*/
-@Command(name = "init", description = "Initializes an Ignite cluster.")
+@Command(name = "init", description = "Initializes an Ignite cluster")
public class ClusterInitSubCommand extends BaseCommand implements Callable<Integer> {
-
- /**
- * Cluster endpoint URL option.
- */
+ /** Cluster endpoint URL option. */
@Option(
names = {CLUSTER_URL_OPTION}, description = CLUSTER_URL_DESC, descriptionKey = CLUSTER_URL_KEY,
defaultValue = "http://localhost:10300"
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/status/ClusterStatusReplSubCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/status/ClusterStatusReplSubCommand.java
index 9f0166cc39..216c0d8dad 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/status/ClusterStatusReplSubCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/status/ClusterStatusReplSubCommand.java
@@ -20,28 +20,26 @@ package org.apache.ignite.cli.commands.cluster.status;
import static org.apache.ignite.cli.commands.OptionsConstants.CLUSTER_URL_DESC;
import static org.apache.ignite.cli.commands.OptionsConstants.CLUSTER_URL_KEY;
import static org.apache.ignite.cli.commands.OptionsConstants.CLUSTER_URL_OPTION;
+import static org.apache.ignite.cli.core.style.component.CommonMessages.CONNECT_OR_USE_CLUSTER_URL_MESSAGE;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import org.apache.ignite.cli.call.cluster.status.ClusterStatusCall;
import org.apache.ignite.cli.commands.BaseCommand;
-import org.apache.ignite.cli.commands.decorators.ClusterStatusDecorator;
import org.apache.ignite.cli.core.call.CallExecutionPipeline;
import org.apache.ignite.cli.core.call.StatusCallInput;
import org.apache.ignite.cli.core.repl.Session;
+import org.apache.ignite.cli.decorators.ClusterStatusDecorator;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
/**
* Command that prints status of ignite cluster.
*/
-@Command(name = "status", description = "Prints status of the cluster.")
+@Command(name = "status", description = "Prints status of the cluster")
@Singleton
public class ClusterStatusReplSubCommand extends BaseCommand implements Runnable {
-
- /**
- * Cluster endpoint URL option.
- */
+ /** Cluster endpoint URL option. */
@SuppressWarnings("PMD.UnusedPrivateField")
@Option(names = {CLUSTER_URL_OPTION}, description = CLUSTER_URL_DESC, descriptionKey = CLUSTER_URL_KEY)
private String clusterUrl;
@@ -62,8 +60,7 @@ public class ClusterStatusReplSubCommand extends BaseCommand implements Runnable
} else if (session.isConnectedToNode()) {
inputUrl = session.nodeUrl();
} else {
- spec.commandLine().getErr().println("You are not connected to node. Run 'connect' command or use '"
- + CLUSTER_URL_OPTION + "' option.");
+ spec.commandLine().getErr().println(CONNECT_OR_USE_CLUSTER_URL_MESSAGE.render());
return;
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/status/ClusterStatusSubCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/status/ClusterStatusSubCommand.java
index 7b5a7ac9e2..251e9335aa 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/status/ClusterStatusSubCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/cluster/status/ClusterStatusSubCommand.java
@@ -26,9 +26,9 @@ import jakarta.inject.Singleton;
import java.util.concurrent.Callable;
import org.apache.ignite.cli.call.cluster.status.ClusterStatusCall;
import org.apache.ignite.cli.commands.BaseCommand;
-import org.apache.ignite.cli.commands.decorators.ClusterStatusDecorator;
import org.apache.ignite.cli.core.call.CallExecutionPipeline;
import org.apache.ignite.cli.core.call.StatusCallInput;
+import org.apache.ignite.cli.decorators.ClusterStatusDecorator;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
@@ -37,13 +37,10 @@ import picocli.CommandLine.Option;
*/
@Command(name = "status",
aliases = "cluster show", //TODO: https://issues.apache.org/jira/browse/IGNITE-17102
- description = "Prints status of the cluster.")
+ description = "Prints status of the cluster")
@Singleton
public class ClusterStatusSubCommand extends BaseCommand implements Callable<Integer> {
-
- /**
- * Cluster endpoint URL option.
- */
+ /** Cluster endpoint URL option. */
@SuppressWarnings("PMD.UnusedPrivateField")
@Option(names = {CLUSTER_URL_OPTION}, description = CLUSTER_URL_DESC, descriptionKey = CLUSTER_URL_KEY)
private String clusterUrl;
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/connect/ConnectCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/connect/ConnectCommand.java
index 77c258ef56..673fb5c52d 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/connect/ConnectCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/connect/ConnectCommand.java
@@ -30,15 +30,12 @@ import picocli.CommandLine.Parameters;
/**
* Connects to the Ignite 3 node.
*/
-@Command(name = "connect", description = "Connect to Ignite 3 node.")
+@Command(name = "connect", description = "Connect to Ignite 3 node")
@Singleton
public class ConnectCommand extends BaseCommand implements Runnable {
-
- /**
- * Cluster url option.
- */
+ /** Cluster url option. */
@Parameters(
- description = "Ignite node url.",
+ description = "Ignite node url",
descriptionKey = ConfigConstants.CLUSTER_URL
)
private String nodeUrl;
@@ -56,5 +53,4 @@ public class ConnectCommand extends BaseCommand implements Runnable {
.build()
.runPipeline();
}
-
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/connect/DisconnectCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/connect/DisconnectCommand.java
index a6da5d1a44..d3ebd5fed7 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/connect/DisconnectCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/connect/DisconnectCommand.java
@@ -28,7 +28,7 @@ import picocli.CommandLine.Command;
/**
* Connects to the Ignite 3 node.
*/
-@Command(name = "disconnect", description = "Disconnect from Ignite 3 node.")
+@Command(name = "disconnect", description = "Disconnect from Ignite 3 node")
@Singleton
public class DisconnectCommand extends BaseCommand implements Runnable {
@Inject
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/node/NodeCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/node/NodeCommand.java
index a00703c831..f96daa30f3 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/node/NodeCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/node/NodeCommand.java
@@ -28,9 +28,8 @@ import picocli.CommandLine.Mixin;
*/
@Command(name = "node",
subcommands = {NodeConfigSubCommand.class, NodeStatusSubCommand.class},
- description = "Node operations.")
+ description = "Node operations")
public class NodeCommand {
-
@Mixin
NodeCommandSpec nodeCommandSpec;
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/node/NodeReplCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/node/NodeReplCommand.java
index 04f7edce8a..9cf11b0bd4 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/node/NodeReplCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/node/NodeReplCommand.java
@@ -28,7 +28,7 @@ import picocli.CommandLine.Mixin;
*/
@Command(name = "node",
subcommands = {NodeConfigReplSubCommand.class, NodeStatusReplSubCommand.class},
- description = "Node operations.")
+ description = "Node operations")
public class NodeReplCommand {
@Mixin
NodeCommandSpec nodeCommandSpec;
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/node/config/NodeConfigReplSubCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/node/config/NodeConfigReplSubCommand.java
index 712f8367dc..1674e45619 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/node/config/NodeConfigReplSubCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/node/config/NodeConfigReplSubCommand.java
@@ -24,7 +24,6 @@ import picocli.CommandLine.Command;
*/
@Command(name = "config",
subcommands = {NodeConfigShowReplSubCommand.class, NodeConfigUpdateReplSubCommand.class},
- description = "Node config operations.")
+ description = "Node config operations")
public class NodeConfigReplSubCommand {
-
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/node/config/NodeConfigShowSubCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/node/config/NodeConfigShowSubCommand.java
index f92a853987..3da2b22346 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/node/config/NodeConfigShowSubCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/node/config/NodeConfigShowSubCommand.java
@@ -26,8 +26,8 @@ import java.util.concurrent.Callable;
import org.apache.ignite.cli.call.configuration.NodeConfigShowCall;
import org.apache.ignite.cli.call.configuration.NodeConfigShowCallInput;
import org.apache.ignite.cli.commands.BaseCommand;
-import org.apache.ignite.cli.commands.decorators.JsonDecorator;
import org.apache.ignite.cli.core.call.CallExecutionPipeline;
+import org.apache.ignite.cli.decorators.JsonDecorator;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;
@@ -36,18 +36,13 @@ import picocli.CommandLine.Parameters;
* Command that shows configuration from the cluster.
*/
@Command(name = "show",
- description = "Shows node configuration.")
+ description = "Shows node configuration")
public class NodeConfigShowSubCommand extends BaseCommand implements Callable<Integer> {
-
- /**
- * Configuration selector option.
- */
+ /** Configuration selector option. */
@Parameters(arity = "0..1", description = "Configuration path selector")
private String selector;
- /**
- * Node URL option.
- */
+ /** Node URL option. */
@Option(names = {NODE_URL_OPTION}, description = NODE_URL_DESC, descriptionKey = CLUSTER_URL_KEY)
private String nodeUrl;
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/node/config/NodeConfigSubCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/node/config/NodeConfigSubCommand.java
index 4bbce2cf95..43ef7c6c39 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/node/config/NodeConfigSubCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/node/config/NodeConfigSubCommand.java
@@ -24,7 +24,6 @@ import picocli.CommandLine.Command;
*/
@Command(name = "config",
subcommands = {NodeConfigShowSubCommand.class, NodeConfigUpdateSubCommand.class},
- description = "Node config operations.")
+ description = "Node config operations")
public class NodeConfigSubCommand {
-
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/node/config/NodeConfigUpdateSubCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/node/config/NodeConfigUpdateSubCommand.java
index f2f8006b09..b682f0eddc 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/node/config/NodeConfigUpdateSubCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/node/config/NodeConfigUpdateSubCommand.java
@@ -36,18 +36,14 @@ import picocli.CommandLine.Parameters;
* Command that updates node configuration.
*/
@Command(name = "update",
- description = "Updates node configuration.")
+ description = "Updates node configuration")
@Singleton
public class NodeConfigUpdateSubCommand extends BaseCommand implements Callable<Integer> {
- /**
- * Node URL option.
- */
+ /** Node URL option. */
@Option(names = {NODE_URL_OPTION}, description = NODE_URL_DESC, descriptionKey = CLUSTER_URL_KEY)
private String nodeUrl;
- /**
- * Configuration that will be updated.
- */
+ /** Configuration that will be updated. */
@Parameters(index = "0")
private String config;
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/node/status/NodeStatusReplSubCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/node/status/NodeStatusReplSubCommand.java
index 43672b05fc..dc63667f0a 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/node/status/NodeStatusReplSubCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/node/status/NodeStatusReplSubCommand.java
@@ -20,16 +20,17 @@ package org.apache.ignite.cli.commands.node.status;
import static org.apache.ignite.cli.commands.OptionsConstants.CLUSTER_URL_KEY;
import static org.apache.ignite.cli.commands.OptionsConstants.NODE_URL_DESC;
import static org.apache.ignite.cli.commands.OptionsConstants.NODE_URL_OPTION;
+import static org.apache.ignite.cli.core.style.component.CommonMessages.CONNECT_OR_USE_NODE_URL_MESSAGE;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import java.util.concurrent.Callable;
import org.apache.ignite.cli.call.node.status.NodeStatusCall;
import org.apache.ignite.cli.commands.BaseCommand;
-import org.apache.ignite.cli.commands.decorators.NodeStatusDecorator;
import org.apache.ignite.cli.core.call.CallExecutionPipeline;
import org.apache.ignite.cli.core.call.StatusCallInput;
import org.apache.ignite.cli.core.repl.Session;
+import org.apache.ignite.cli.decorators.NodeStatusDecorator;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
@@ -37,13 +38,10 @@ import picocli.CommandLine.Option;
* Display the node status in REPL.
*/
@Command(name = "status",
- description = "Prints status of the node.")
+ description = "Prints status of the node")
@Singleton
public class NodeStatusReplSubCommand extends BaseCommand implements Callable<Integer> {
-
- /**
- * Node URL option.
- */
+ /** Node URL option. */
@SuppressWarnings("PMD.UnusedPrivateField")
@Option(names = {NODE_URL_OPTION}, description = NODE_URL_DESC, descriptionKey = CLUSTER_URL_KEY)
private String nodeUrl;
@@ -64,8 +62,7 @@ public class NodeStatusReplSubCommand extends BaseCommand implements Callable<In
} else if (session.isConnectedToNode()) {
inputUrl = session.nodeUrl();
} else {
- spec.commandLine().getErr().println("You are not connected to node. Run 'connect' command or use '"
- + NODE_URL_OPTION + "' option.");
+ spec.commandLine().getErr().println(CONNECT_OR_USE_NODE_URL_MESSAGE.render());
return 2;
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/node/status/NodeStatusSubCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/node/status/NodeStatusSubCommand.java
index 6a42402795..cb68811895 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/node/status/NodeStatusSubCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/node/status/NodeStatusSubCommand.java
@@ -26,9 +26,9 @@ import jakarta.inject.Singleton;
import java.util.concurrent.Callable;
import org.apache.ignite.cli.call.node.status.NodeStatusCall;
import org.apache.ignite.cli.commands.BaseCommand;
-import org.apache.ignite.cli.commands.decorators.NodeStatusDecorator;
import org.apache.ignite.cli.core.call.CallExecutionPipeline;
import org.apache.ignite.cli.core.call.StatusCallInput;
+import org.apache.ignite.cli.decorators.NodeStatusDecorator;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
@@ -36,13 +36,10 @@ import picocli.CommandLine.Option;
* Display the node status.
*/
@Command(name = "status",
- description = "Prints status of the node.")
+ description = "Prints status of the node")
@Singleton
public class NodeStatusSubCommand extends BaseCommand implements Callable<Integer> {
-
- /**
- * Node URL option.
- */
+ /** Node URL option. */
@SuppressWarnings("PMD.UnusedPrivateField")
@Option(names = {NODE_URL_OPTION}, description = NODE_URL_DESC, descriptionKey = CLUSTER_URL_KEY)
private String nodeUrl;
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/questions/ConnectToClusterQuestion.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/questions/ConnectToClusterQuestion.java
index 5c3a6496e2..9dc1f5df1f 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/questions/ConnectToClusterQuestion.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/questions/ConnectToClusterQuestion.java
@@ -30,6 +30,8 @@ import org.apache.ignite.cli.core.flow.builder.FlowBuilder;
import org.apache.ignite.cli.core.flow.builder.Flows;
import org.apache.ignite.cli.core.repl.Session;
import org.apache.ignite.cli.core.repl.context.CommandLineContextProvider;
+import org.apache.ignite.cli.core.style.component.QuestionUiComponent;
+import org.apache.ignite.cli.core.style.element.UiElements;
/**
@@ -37,7 +39,6 @@ import org.apache.ignite.cli.core.repl.context.CommandLineContextProvider;
*/
@Singleton
public class ConnectToClusterQuestion {
-
@Inject
private ConnectCall connectCall;
@@ -59,11 +60,14 @@ public class ConnectToClusterQuestion {
*/
public FlowBuilder<Void, String> askQuestionIfNotConnected(String clusterUrl) {
String defaultUrl = configManagerProvider.get().getCurrentProperty(ConfigConstants.CLUSTER_URL);
- String question = "You are not connected to node. Do you want to connect to the default node "
- + defaultUrl + " ? [Y/n] ";
+
+ QuestionUiComponent questionUiComponent = QuestionUiComponent.fromQuestion(
+ "You are not connected to node. Do you want to connect to the default node %s? %s ",
+ UiElements.url(defaultUrl), UiElements.yesNo()
+ );
return Flows.from(clusterUrlOrSessionNode(clusterUrl))
- .ifThen(Objects::isNull, Flows.<String, ConnectCallInput>acceptQuestion(question,
+ .ifThen(Objects::isNull, Flows.<String, ConnectCallInput>acceptQuestion(questionUiComponent,
() -> new ConnectCallInput(defaultUrl))
.then(Flows.fromCall(connectCall))
.toOutput(CommandLineContextProvider.getContext())
@@ -81,13 +85,17 @@ public class ConnectToClusterQuestion {
public void askQuestionOnReplStart() {
String defaultUrl = configManagerProvider.get().getCurrentProperty(ConfigConstants.CLUSTER_URL);
String lastConnectedUrl = stateConfigProvider.get().getProperty(ConfigConstants.LAST_CONNECTED_URL);
- String question;
+ QuestionUiComponent question;
String clusterUrl;
if (lastConnectedUrl != null) {
- question = "Do you want to connect to the last connected node " + lastConnectedUrl + " ? [Y/n]";
+ question = QuestionUiComponent.fromQuestion(
+ "Do you want to connect to the last connected node %s? %s ", UiElements.url(lastConnectedUrl), UiElements.yesNo()
+ );
clusterUrl = lastConnectedUrl;
} else {
- question = "Do you want to connect to the default node " + defaultUrl + " ? [Y/n]";
+ question = QuestionUiComponent.fromQuestion(
+ "Do you want to connect to the default node %s? %s ", UiElements.url(defaultUrl), UiElements.yesNo()
+ );
clusterUrl = defaultUrl;
}
@@ -100,8 +108,9 @@ public class ConnectToClusterQuestion {
}
private FlowBuilder<String, String> defaultUrlQuestion(String lastConnectedUrl) {
- return Flows.acceptQuestion("Would you like to use " + lastConnectedUrl + " as the default URL? [Y/n]",
- () -> {
+ return Flows.acceptQuestion(QuestionUiComponent.fromQuestion(
+ "Would you like to use %s as the default URL? %s ", UiElements.url(lastConnectedUrl), UiElements.yesNo()
+ ), () -> {
configManagerProvider.get().setProperty(ConfigConstants.CLUSTER_URL, lastConnectedUrl);
return "Config saved";
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/sql/SqlCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/sql/SqlCommand.java
index bd968e93a8..c16142c235 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/sql/SqlCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/sql/SqlCommand.java
@@ -25,11 +25,11 @@ import java.sql.SQLException;
import java.util.concurrent.Callable;
import org.apache.ignite.cli.call.sql.SqlQueryCall;
import org.apache.ignite.cli.commands.BaseCommand;
-import org.apache.ignite.cli.commands.decorators.SqlQueryResultDecorator;
import org.apache.ignite.cli.core.call.CallExecutionPipeline;
import org.apache.ignite.cli.core.call.StringCallInput;
import org.apache.ignite.cli.core.exception.ExceptionWriter;
import org.apache.ignite.cli.core.exception.handler.SqlExceptionHandler;
+import org.apache.ignite.cli.decorators.SqlQueryResultDecorator;
import org.apache.ignite.cli.deprecated.IgniteCliException;
import org.apache.ignite.cli.sql.SqlManager;
import picocli.CommandLine.ArgGroup;
@@ -40,9 +40,8 @@ import picocli.CommandLine.Parameters;
/**
* Command for sql execution.
*/
-@Command(name = "sql", description = "Executes SQL query.")
+@Command(name = "sql", description = "Executes SQL query")
public class SqlCommand extends BaseCommand implements Callable<Integer> {
-
@Option(names = {"-u", "--jdbc-url"}, required = true,
descriptionKey = "ignite.jdbc-url", description = "JDBC url to ignite cluster")
private String jdbc;
@@ -51,10 +50,10 @@ public class SqlCommand extends BaseCommand implements Callable<Integer> {
private ExecOptions execOptions;
private static class ExecOptions {
- @Parameters(index = "0", description = "SQL query to execute.")
+ @Parameters(index = "0", description = "SQL query to execute")
private String command;
- @Option(names = {"-f", "--script-file"}, description = "Path to file with SQL commands to execute.")
+ @Option(names = {"-f", "--script-file"}, description = "Path to file with SQL commands to execute")
private File file;
}
@@ -62,7 +61,7 @@ public class SqlCommand extends BaseCommand implements Callable<Integer> {
try {
return String.join("\n", Files.readAllLines(file.toPath(), StandardCharsets.UTF_8));
} catch (IOException e) {
- throw new IgniteCliException("File with command not found.");
+ throw new IgniteCliException("File with command not found");
}
}
@@ -83,5 +82,4 @@ public class SqlCommand extends BaseCommand implements Callable<Integer> {
return new SqlExceptionHandler().handle(ExceptionWriter.fromPrintWriter(spec.commandLine().getErr()), e);
}
}
-
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/sql/SqlReplCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/sql/SqlReplCommand.java
index 1647916c82..fe4c820b5e 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/sql/SqlReplCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/sql/SqlReplCommand.java
@@ -25,7 +25,6 @@ import java.nio.file.Files;
import java.sql.SQLException;
import org.apache.ignite.cli.call.sql.SqlQueryCall;
import org.apache.ignite.cli.commands.BaseCommand;
-import org.apache.ignite.cli.commands.decorators.SqlQueryResultDecorator;
import org.apache.ignite.cli.core.CallExecutionPipelineProvider;
import org.apache.ignite.cli.core.call.CallExecutionPipeline;
import org.apache.ignite.cli.core.call.StringCallInput;
@@ -35,6 +34,7 @@ import org.apache.ignite.cli.core.exception.handler.SqlExceptionHandler;
import org.apache.ignite.cli.core.repl.Repl;
import org.apache.ignite.cli.core.repl.executor.RegistryCommandExecutor;
import org.apache.ignite.cli.core.repl.executor.ReplExecutorProvider;
+import org.apache.ignite.cli.decorators.SqlQueryResultDecorator;
import org.apache.ignite.cli.deprecated.IgniteCliException;
import org.apache.ignite.cli.sql.SqlManager;
import org.apache.ignite.cli.sql.SqlSchemaProvider;
@@ -46,7 +46,7 @@ import picocli.CommandLine.Parameters;
/**
* Command for sql execution in REPL mode.
*/
-@Command(name = "sql", description = "Executes SQL query.")
+@Command(name = "sql", description = "Executes SQL query")
public class SqlReplCommand extends BaseCommand implements Runnable {
@Option(names = {"-u", "--jdbc-url"}, required = true,
descriptionKey = "ignite.jdbc-url", description = "JDBC url to ignite cluster")
@@ -56,10 +56,10 @@ public class SqlReplCommand extends BaseCommand implements Runnable {
private ExecOptions execOptions;
private static class ExecOptions {
- @Parameters(index = "0", description = "SQL query to execute.")
+ @Parameters(index = "0", description = "SQL query to execute")
private String command;
- @Option(names = {"-f", "--script-file"}, description = "Path to file with SQL commands to execute.")
+ @Option(names = {"-f", "--script-file"}, description = "Path to file with SQL commands to execute")
private File file;
}
@@ -70,7 +70,7 @@ public class SqlReplCommand extends BaseCommand implements Runnable {
try {
return String.join("\n", Files.readAllLines(file.toPath(), StandardCharsets.UTF_8));
} catch (IOException e) {
- throw new IgniteCliException("File with command not found.");
+ throw new IgniteCliException("File with command not found");
}
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/sql/SqlReplTopLevelCliCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/sql/SqlReplTopLevelCliCommand.java
index 6613644107..0e977bb795 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/sql/SqlReplTopLevelCliCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/sql/SqlReplTopLevelCliCommand.java
@@ -26,7 +26,7 @@ import picocli.shell.jline3.PicocliCommands;
*/
@CommandLine.Command(name = "",
description = {""},
- footer = {"", "Press Ctrl-D to exit."},
+ footer = {"", "Press Ctrl-D to exit"},
subcommands = {
CommandLine.HelpCommand.class,
PicocliCommands.ClearScreen.class
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/topology/LogicalTopologyReplSubCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/topology/LogicalTopologyReplSubCommand.java
index e1902c6377..be32c99106 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/topology/LogicalTopologyReplSubCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/topology/LogicalTopologyReplSubCommand.java
@@ -20,6 +20,7 @@ package org.apache.ignite.cli.commands.topology;
import static org.apache.ignite.cli.commands.OptionsConstants.CLUSTER_URL_DESC;
import static org.apache.ignite.cli.commands.OptionsConstants.CLUSTER_URL_KEY;
import static org.apache.ignite.cli.commands.OptionsConstants.CLUSTER_URL_OPTION;
+import static org.apache.ignite.cli.core.style.component.CommonMessages.CONNECT_OR_USE_NODE_URL_MESSAGE;
import jakarta.inject.Inject;
import java.util.concurrent.Callable;
@@ -27,9 +28,9 @@ import org.apache.ignite.cli.call.cluster.topology.LogicalTopologyCall;
import org.apache.ignite.cli.call.cluster.topology.TopologyCallInput;
import org.apache.ignite.cli.call.cluster.topology.TopologyCallInput.TopologyCallInputBuilder;
import org.apache.ignite.cli.commands.BaseCommand;
-import org.apache.ignite.cli.commands.decorators.TopologyDecorator;
import org.apache.ignite.cli.core.call.CallExecutionPipeline;
import org.apache.ignite.cli.core.repl.Session;
+import org.apache.ignite.cli.decorators.TopologyDecorator;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
@@ -38,9 +39,7 @@ import picocli.CommandLine.Option;
*/
@Command(name = "logical")
public class LogicalTopologyReplSubCommand extends BaseCommand implements Callable<Integer> {
- /**
- * Cluster endpoint URL option.
- */
+ /** Cluster endpoint URL option. */
@Option(names = {CLUSTER_URL_OPTION}, description = CLUSTER_URL_DESC, descriptionKey = CLUSTER_URL_KEY)
private String clusterUrl;
@@ -57,11 +56,10 @@ public class LogicalTopologyReplSubCommand extends BaseCommand implements Callab
if (clusterUrl != null) {
inputBuilder.clusterUrl(clusterUrl);
- } else if (session.isConnectedToNode()) {
+ } else if (session.isConnectedToNode()) {
inputBuilder.clusterUrl(session.nodeUrl());
} else {
- spec.commandLine().getErr().println("You are not connected to node. Run 'connect' command or use '"
- + CLUSTER_URL_OPTION + "' option.");
+ spec.commandLine().getErr().println(CONNECT_OR_USE_NODE_URL_MESSAGE.render());
return 2;
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/topology/LogicalTopologySubCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/topology/LogicalTopologySubCommand.java
index 27bca3d243..d7043e84ef 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/topology/LogicalTopologySubCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/topology/LogicalTopologySubCommand.java
@@ -26,8 +26,8 @@ import java.util.concurrent.Callable;
import org.apache.ignite.cli.call.cluster.topology.LogicalTopologyCall;
import org.apache.ignite.cli.call.cluster.topology.TopologyCallInput;
import org.apache.ignite.cli.commands.BaseCommand;
-import org.apache.ignite.cli.commands.decorators.TopologyDecorator;
import org.apache.ignite.cli.core.call.CallExecutionPipeline;
+import org.apache.ignite.cli.decorators.TopologyDecorator;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
@@ -36,9 +36,7 @@ import picocli.CommandLine.Option;
*/
@Command(name = "logical")
public class LogicalTopologySubCommand extends BaseCommand implements Callable<Integer> {
- /**
- * Cluster endpoint URL option.
- */
+ /** Cluster endpoint URL option. */
@Option(names = {CLUSTER_URL_OPTION}, description = CLUSTER_URL_DESC, descriptionKey = CLUSTER_URL_KEY)
private String clusterUrl;
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/topology/PhysicalTopologyReplSubCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/topology/PhysicalTopologyReplSubCommand.java
index 50a9517ee2..dfee7731f6 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/topology/PhysicalTopologyReplSubCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/topology/PhysicalTopologyReplSubCommand.java
@@ -20,6 +20,7 @@ package org.apache.ignite.cli.commands.topology;
import static org.apache.ignite.cli.commands.OptionsConstants.CLUSTER_URL_DESC;
import static org.apache.ignite.cli.commands.OptionsConstants.CLUSTER_URL_KEY;
import static org.apache.ignite.cli.commands.OptionsConstants.CLUSTER_URL_OPTION;
+import static org.apache.ignite.cli.core.style.component.CommonMessages.CONNECT_OR_USE_NODE_URL_MESSAGE;
import jakarta.inject.Inject;
import java.util.concurrent.Callable;
@@ -27,9 +28,9 @@ import org.apache.ignite.cli.call.cluster.topology.PhysicalTopologyCall;
import org.apache.ignite.cli.call.cluster.topology.TopologyCallInput;
import org.apache.ignite.cli.call.cluster.topology.TopologyCallInput.TopologyCallInputBuilder;
import org.apache.ignite.cli.commands.BaseCommand;
-import org.apache.ignite.cli.commands.decorators.TopologyDecorator;
import org.apache.ignite.cli.core.call.CallExecutionPipeline;
import org.apache.ignite.cli.core.repl.Session;
+import org.apache.ignite.cli.decorators.TopologyDecorator;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
@@ -38,9 +39,7 @@ import picocli.CommandLine.Option;
*/
@Command(name = "physical")
public class PhysicalTopologyReplSubCommand extends BaseCommand implements Callable<Integer> {
- /**
- * Cluster endpoint URL option.
- */
+ /** Cluster endpoint URL option. */
@Option(names = {CLUSTER_URL_OPTION}, description = CLUSTER_URL_DESC, descriptionKey = CLUSTER_URL_KEY)
private String clusterUrl;
@@ -57,11 +56,10 @@ public class PhysicalTopologyReplSubCommand extends BaseCommand implements Calla
if (clusterUrl != null) {
inputBuilder.clusterUrl(clusterUrl);
- } else if (session.isConnectedToNode()) {
+ } else if (session.isConnectedToNode()) {
inputBuilder.clusterUrl(session.nodeUrl());
} else {
- spec.commandLine().getErr().println("You are not connected to node. Run 'connect' command or use '"
- + CLUSTER_URL_OPTION + "' option.");
+ spec.commandLine().getErr().println(CONNECT_OR_USE_NODE_URL_MESSAGE.render());
return 2;
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/topology/PhysicalTopologySubCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/topology/PhysicalTopologySubCommand.java
index 59ad74b967..10642df21b 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/topology/PhysicalTopologySubCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/topology/PhysicalTopologySubCommand.java
@@ -26,8 +26,8 @@ import java.util.concurrent.Callable;
import org.apache.ignite.cli.call.cluster.topology.PhysicalTopologyCall;
import org.apache.ignite.cli.call.cluster.topology.TopologyCallInput;
import org.apache.ignite.cli.commands.BaseCommand;
-import org.apache.ignite.cli.commands.decorators.TopologyDecorator;
import org.apache.ignite.cli.core.call.CallExecutionPipeline;
+import org.apache.ignite.cli.decorators.TopologyDecorator;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
@@ -36,9 +36,7 @@ import picocli.CommandLine.Option;
*/
@Command(name = "physical")
public class PhysicalTopologySubCommand extends BaseCommand implements Callable<Integer> {
- /**
- * Cluster endpoint URL option.
- */
+ /** Cluster endpoint URL option. */
@Option(names = {CLUSTER_URL_OPTION}, description = CLUSTER_URL_DESC, descriptionKey = CLUSTER_URL_KEY)
private String clusterUrl;
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/topology/TopologyCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/topology/TopologyCommand.java
index 5da14720fa..c68da8af8f 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/topology/TopologyCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/topology/TopologyCommand.java
@@ -18,21 +18,14 @@
package org.apache.ignite.cli.commands.topology;
import jakarta.inject.Singleton;
-import java.util.concurrent.Callable;
import org.apache.ignite.cli.commands.BaseCommand;
import picocli.CommandLine.Command;
/**
* Command that prints ignite cluster topology.
*/
-@Command(name = "topology", description = "Prints topology information.",
+@Command(name = "topology", description = "Prints topology information",
subcommands = {PhysicalTopologySubCommand.class, LogicalTopologySubCommand.class })
@Singleton
-public class TopologyCommand extends BaseCommand implements Callable<Integer> {
-
- /** {@inheritDoc} */
- @Override
- public Integer call() {
- return 0;
- }
+public class TopologyCommand extends BaseCommand {
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/topology/TopologyReplCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/topology/TopologyReplCommand.java
index fa2c9ed054..7b7a1eaa98 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/topology/TopologyReplCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/topology/TopologyReplCommand.java
@@ -18,21 +18,14 @@
package org.apache.ignite.cli.commands.topology;
import jakarta.inject.Singleton;
-import java.util.concurrent.Callable;
import org.apache.ignite.cli.commands.BaseCommand;
import picocli.CommandLine.Command;
/**
* Command that prints ignite cluster topology in REPL mode.
*/
-@Command(name = "topology", description = "Prints topology information.",
+@Command(name = "topology", description = "Prints topology information",
subcommands = {PhysicalTopologyReplSubCommand.class, LogicalTopologyReplSubCommand.class })
@Singleton
-public class TopologyReplCommand extends BaseCommand implements Callable<Integer> {
-
- /** {@inheritDoc} */
- @Override
- public Integer call() {
- return 0;
- }
+public class TopologyReplCommand extends BaseCommand {
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/version/VersionCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/commands/version/VersionCommand.java
index 046d319e82..465209ea92 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/version/VersionCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/commands/version/VersionCommand.java
@@ -26,10 +26,9 @@ import picocli.CommandLine.Command;
/**
* Command that prints CLI version.
*/
-@Command(name = "version", description = "Prints CLI version.")
+@Command(name = "version", description = "Prints CLI version")
@Singleton
public class VersionCommand extends BaseCommand implements Runnable {
-
@Inject
private VersionProvider versionProvider;
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/config/ini/SectionAlreadyExistsException.java b/modules/cli/src/main/java/org/apache/ignite/cli/config/ini/SectionAlreadyExistsException.java
index 72b69ffe7e..84def57b7f 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/config/ini/SectionAlreadyExistsException.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/config/ini/SectionAlreadyExistsException.java
@@ -22,6 +22,6 @@ package org.apache.ignite.cli.config.ini;
*/
public class SectionAlreadyExistsException extends RuntimeException {
public SectionAlreadyExistsException(String name) {
- super("Section " + name + " already exists.");
+ super("Section " + name + " already exists");
}
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/core/call/CallExecutionPipeline.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/call/CallExecutionPipeline.java
index 38c012ca8a..c9f43dd28f 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/core/call/CallExecutionPipeline.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/call/CallExecutionPipeline.java
@@ -21,13 +21,13 @@ import java.io.OutputStream;
import java.io.PrintWriter;
import java.nio.charset.Charset;
import java.util.function.Supplier;
-import org.apache.ignite.cli.commands.decorators.DefaultDecorator;
import org.apache.ignite.cli.core.decorator.Decorator;
import org.apache.ignite.cli.core.decorator.TerminalOutput;
import org.apache.ignite.cli.core.exception.ExceptionHandler;
import org.apache.ignite.cli.core.exception.ExceptionHandlers;
import org.apache.ignite.cli.core.exception.ExceptionWriter;
import org.apache.ignite.cli.core.exception.handler.DefaultExceptionHandlers;
+import org.apache.ignite.cli.decorators.DefaultDecorator;
/**
* Call execution pipeline.
@@ -36,34 +36,22 @@ import org.apache.ignite.cli.core.exception.handler.DefaultExceptionHandlers;
* @param <T> Call output's body type.
*/
public class CallExecutionPipeline<I extends CallInput, T> {
- /**
- * Call to execute.
- */
+ /** Call to execute. */
private final Call<I, T> call;
- /**
- * Writer for execution output.
- */
+ /** Writer for execution output. */
private final PrintWriter output;
- /**
- * Writer for error execution output.
- */
+ /** Writer for error execution output. */
private final PrintWriter errOutput;
- /**
- * Decorator that decorates call's output.
- */
+ /** Decorator that decorates call's output. */
private final Decorator<T, TerminalOutput> decorator;
- /**
- * Handlers for any exceptions.
- */
+ /** Handlers for any exceptions. */
private final ExceptionHandlers exceptionHandlers;
- /**
- * Provider for call's input.
- */
+ /** Provider for call's input. */
private final Supplier<I> inputProvider;
private CallExecutionPipeline(Call<I, T> call,
@@ -85,8 +73,7 @@ public class CallExecutionPipeline<I extends CallInput, T> {
*
* @return builder for {@link CallExecutionPipeline}.
*/
- public static <I extends CallInput, T> CallExecutionPipelineBuilder<I, T> builder(
- Call<I, T> call) {
+ public static <I extends CallInput, T> CallExecutionPipelineBuilder<I, T> builder(Call<I, T> call) {
return new CallExecutionPipelineBuilder<>(call);
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/core/decorator/DecoratorRegistry.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/decorator/DecoratorRegistry.java
index 1b1162d0fb..ca22a3bcd9 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/core/decorator/DecoratorRegistry.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/decorator/DecoratorRegistry.java
@@ -19,7 +19,7 @@ package org.apache.ignite.cli.core.decorator;
import java.util.HashMap;
import java.util.Map;
-import org.apache.ignite.cli.commands.decorators.DefaultDecorator;
+import org.apache.ignite.cli.decorators.DefaultDecorator;
/**
* Registry for {@link Decorator}.
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/ExceptionHandler.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/ExceptionHandler.java
index 84583ed5da..c65de68999 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/ExceptionHandler.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/ExceptionHandler.java
@@ -17,6 +17,7 @@
package org.apache.ignite.cli.core.exception;
+import org.apache.ignite.cli.core.style.component.ErrorUiComponent;
import org.apache.ignite.internal.logger.IgniteLogger;
import org.apache.ignite.internal.logger.Loggers;
@@ -32,7 +33,14 @@ public interface ExceptionHandler<T extends Throwable> {
@Override
public int handle(ExceptionWriter err, Throwable e) {
LOG.error("Unhandled exception", e);
- err.write("Internal error!");
+ err.write(
+ ErrorUiComponent.builder()
+ .header("Unknown error")
+ .details(e.getMessage())
+ .build()
+ .render()
+ );
+
return 1;
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/ConfigStoringExceptionHandler.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/ConfigStoringExceptionHandler.java
index 0f317483ca..534ab1eb64 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/ConfigStoringExceptionHandler.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/ConfigStoringExceptionHandler.java
@@ -20,6 +20,7 @@ package org.apache.ignite.cli.core.exception.handler;
import org.apache.ignite.cli.config.ConfigStoringException;
import org.apache.ignite.cli.core.exception.ExceptionHandler;
import org.apache.ignite.cli.core.exception.ExceptionWriter;
+import org.apache.ignite.cli.core.style.component.ErrorUiComponent;
import org.apache.ignite.internal.logger.IgniteLogger;
import org.apache.ignite.internal.logger.Loggers;
@@ -31,8 +32,13 @@ public class ConfigStoringExceptionHandler implements ExceptionHandler<ConfigSto
@Override
public int handle(ExceptionWriter err, ConfigStoringException e) {
- log.error("CLI config storing error: ", e);
- err.write("Error happened while saving CLI config " + e.getMessage());
+ ErrorUiComponent errorComponent = ErrorUiComponent.builder()
+ .header("Could not save CLI config")
+ .details(e.getMessage())
+ .build();
+
+ log.error(errorComponent.header(), e);
+ err.write(errorComponent.render());
return 1;
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/IgniteCliApiExceptionHandler.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/IgniteCliApiExceptionHandler.java
index 1ace50ea2d..53c92517c7 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/IgniteCliApiExceptionHandler.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/IgniteCliApiExceptionHandler.java
@@ -17,14 +17,22 @@
package org.apache.ignite.cli.core.exception.handler;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.ConnectException;
import java.net.UnknownHostException;
+import java.util.stream.Collectors;
import org.apache.ignite.cli.core.exception.ExceptionHandler;
import org.apache.ignite.cli.core.exception.ExceptionWriter;
import org.apache.ignite.cli.core.exception.IgniteCliApiException;
+import org.apache.ignite.cli.core.style.component.ErrorUiComponent;
+import org.apache.ignite.cli.core.style.component.ErrorUiComponent.ErrorComponentBuilder;
+import org.apache.ignite.cli.core.style.element.UiElements;
import org.apache.ignite.internal.logger.IgniteLogger;
import org.apache.ignite.internal.logger.Loggers;
import org.apache.ignite.rest.client.invoker.ApiException;
+import org.apache.ignite.rest.client.model.Problem;
+import org.jetbrains.annotations.NotNull;
/**
* Exception handler for {@link IgniteCliApiException}.
@@ -32,33 +40,62 @@ import org.apache.ignite.rest.client.invoker.ApiException;
public class IgniteCliApiExceptionHandler implements ExceptionHandler<IgniteCliApiException> {
private static final IgniteLogger LOG = Loggers.forClass(IgniteCliApiExceptionHandler.class);
+ private static final ObjectMapper objectMapper = new ObjectMapper();
+
@Override
public int handle(ExceptionWriter err, IgniteCliApiException e) {
- String message;
+ ErrorUiComponent.ErrorComponentBuilder errorComponentBuilder = ErrorUiComponent.builder();
if (e.getCause() instanceof ApiException) {
ApiException cause = (ApiException) e.getCause();
Throwable apiCause = cause.getCause();
if (apiCause instanceof UnknownHostException) {
- message = "Could not determine IP address when connecting to URL [url=" + e.getUrl() + ']';
+ errorComponentBuilder
+ .header("Unknown host: %s", UiElements.url(e.getUrl()));
} else if (apiCause instanceof ConnectException) {
- message = "Could not connect to URL [url=" + e.getUrl() + ']';
+ errorComponentBuilder
+ .header("Node unavailable")
+ .details("Could not connect to node with URL %s", UiElements.url(e.getUrl()));
} else if (apiCause != null) {
- message = apiCause.getMessage();
+ errorComponentBuilder.header(apiCause.getMessage());
} else {
- message = "An error occurred [errorCode=" + cause.getCode() + ", response=" + cause.getResponseBody() + ']';
+ tryToExtractProblem(errorComponentBuilder, cause);
}
} else {
- message = e.getCause() != e ? e.getCause().getMessage() : e.getMessage();
+ errorComponentBuilder.header(e.getCause() != e ? e.getCause().getMessage() : e.getMessage());
}
- LOG.error(message, e);
+ ErrorUiComponent errorComponent = errorComponentBuilder.build();
+
+ LOG.error(errorComponent.header(), e);
- err.write(message);
+ err.write(errorComponent.render());
return 1;
}
+ private static void tryToExtractProblem(ErrorComponentBuilder errorComponentBuilder, ApiException cause) {
+ try {
+ Problem problem = objectMapper.readValue(cause.getResponseBody(), Problem.class);
+ if (!problem.getInvalidParams().isEmpty()) {
+ errorComponentBuilder.details(extractInvalidParams(problem));
+ }
+ errorComponentBuilder
+ .header(problem.getDetail())
+ .errorCode(problem.getCode())
+ .traceId(problem.getTraceId());
+ } catch (JsonProcessingException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ @NotNull
+ private static String extractInvalidParams(Problem problem) {
+ return problem.getInvalidParams().stream()
+ .map(invalidParam -> "" + invalidParam.getName() + ": " + invalidParam.getReason())
+ .collect(Collectors.joining(System.lineSeparator()));
+ }
+
@Override
public Class<IgniteCliApiException> applicableException() {
return IgniteCliApiException.class;
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/IgniteCliExceptionHandler.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/IgniteCliExceptionHandler.java
index 0d98620803..4e539209d3 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/IgniteCliExceptionHandler.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/IgniteCliExceptionHandler.java
@@ -19,6 +19,7 @@ package org.apache.ignite.cli.core.exception.handler;
import org.apache.ignite.cli.core.exception.ExceptionHandler;
import org.apache.ignite.cli.core.exception.ExceptionWriter;
+import org.apache.ignite.cli.core.style.component.ErrorUiComponent;
import org.apache.ignite.cli.deprecated.IgniteCliException;
/**
@@ -27,7 +28,10 @@ import org.apache.ignite.cli.deprecated.IgniteCliException;
public class IgniteCliExceptionHandler implements ExceptionHandler<IgniteCliException> {
@Override
public int handle(ExceptionWriter err, IgniteCliException e) {
- err.write(e.getMessage());
+ err.write(
+ ErrorUiComponent.fromHeader(e.getMessage()).render()
+ );
+
return 1;
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/PicocliExecutionExceptionHandler.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/PicocliExecutionExceptionHandler.java
index 9c96a8b0d6..120d192a33 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/PicocliExecutionExceptionHandler.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/PicocliExecutionExceptionHandler.java
@@ -30,6 +30,6 @@ public class PicocliExecutionExceptionHandler implements IExecutionExceptionHand
@Override
public int handleExecutionException(Exception ex, CommandLine commandLine, ParseResult parseResult) {
- return exceptionHandlers.handleException(System.err::println, ex);
+ return exceptionHandlers.handleException(commandLine.getErr()::println, ex);
}
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/ProfileNotFoundExceptionHandler.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/ProfileNotFoundExceptionHandler.java
index a410dd0bc4..dc2cf7a09d 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/ProfileNotFoundExceptionHandler.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/ProfileNotFoundExceptionHandler.java
@@ -20,6 +20,7 @@ package org.apache.ignite.cli.core.exception.handler;
import org.apache.ignite.cli.config.ProfileNotFoundException;
import org.apache.ignite.cli.core.exception.ExceptionHandler;
import org.apache.ignite.cli.core.exception.ExceptionWriter;
+import org.apache.ignite.cli.core.style.component.ErrorUiComponent;
/**
* Handler for {@link ProfileNotFoundException}.
@@ -27,7 +28,10 @@ import org.apache.ignite.cli.core.exception.ExceptionWriter;
public class ProfileNotFoundExceptionHandler implements ExceptionHandler<ProfileNotFoundException> {
@Override
public int handle(ExceptionWriter err, ProfileNotFoundException e) {
- err.write(e.getMessage());
+ err.write(
+ ErrorUiComponent.fromHeader(e.getMessage()).render()
+ );
+
return 1;
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/SectionAlreadyExistsExceptionHandler.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/SectionAlreadyExistsExceptionHandler.java
index aa868ca8a1..e3af26c13b 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/SectionAlreadyExistsExceptionHandler.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/SectionAlreadyExistsExceptionHandler.java
@@ -20,6 +20,7 @@ package org.apache.ignite.cli.core.exception.handler;
import org.apache.ignite.cli.config.ini.SectionAlreadyExistsException;
import org.apache.ignite.cli.core.exception.ExceptionHandler;
import org.apache.ignite.cli.core.exception.ExceptionWriter;
+import org.apache.ignite.cli.core.style.component.ErrorUiComponent;
/**
* Handler for {@link SectionAlreadyExistsException}.
@@ -27,7 +28,10 @@ import org.apache.ignite.cli.core.exception.ExceptionWriter;
public class SectionAlreadyExistsExceptionHandler implements ExceptionHandler<SectionAlreadyExistsException> {
@Override
public int handle(ExceptionWriter err, SectionAlreadyExistsException e) {
- err.write(e.getMessage());
+ err.write(
+ ErrorUiComponent.fromHeader(e.getMessage()).render()
+ );
+
return 1;
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/ShowConfigExceptionHandler.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/ShowConfigExceptionHandler.java
new file mode 100644
index 0000000000..ec7528067e
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/ShowConfigExceptionHandler.java
@@ -0,0 +1,49 @@
+/*
+ * 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.cli.core.exception.handler;
+
+import org.apache.ignite.cli.core.exception.ExceptionWriter;
+import org.apache.ignite.cli.core.exception.IgniteCliApiException;
+import org.apache.ignite.cli.core.style.component.ErrorUiComponent;
+import org.apache.ignite.cli.core.style.element.UiElements;
+import org.apache.ignite.rest.client.invoker.ApiException;
+
+/**
+ * This exception handler is used only for `cluster config show` command and handles a tricky case with the 500 error on not initialized
+ * cluster.
+ */
+public class ShowConfigExceptionHandler extends IgniteCliApiExceptionHandler {
+ @Override
+ public int handle(ExceptionWriter err, IgniteCliApiException e) {
+ if (e.getCause() instanceof ApiException) {
+ ApiException apiException = (ApiException) e.getCause();
+ if (apiException.getCode() == 500) { //TODO: https://issues.apache.org/jira/browse/IGNITE-17510
+ err.write(
+ ErrorUiComponent.builder()
+ .header("Cannot show cluster config")
+ .details("Probably, you have not initialized the cluster, try to run %s command",
+ UiElements.command("cluster init"))
+ .build()
+ .render()
+ );
+ return 1;
+ }
+ }
+ return super.handle(err, e);
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/SqlExceptionHandler.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/SqlExceptionHandler.java
index 813366a307..d3d62e87b7 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/SqlExceptionHandler.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/SqlExceptionHandler.java
@@ -20,6 +20,7 @@ package org.apache.ignite.cli.core.exception.handler;
import java.sql.SQLException;
import org.apache.ignite.cli.core.exception.ExceptionHandler;
import org.apache.ignite.cli.core.exception.ExceptionWriter;
+import org.apache.ignite.cli.core.style.component.ErrorUiComponent;
import org.apache.ignite.internal.jdbc.proto.SqlStateCode;
import org.apache.ignite.internal.logger.IgniteLogger;
import org.apache.ignite.internal.logger.Loggers;
@@ -30,35 +31,39 @@ import org.apache.ignite.internal.logger.Loggers;
public class SqlExceptionHandler implements ExceptionHandler<SQLException> {
private static final IgniteLogger LOG = Loggers.forClass(SqlExceptionHandler.class);
- public static final String PARSING_ERROR_MESSAGE = "SQL query parsing error: %s";
+ public static final String PARSING_ERROR_MESSAGE = "SQL query parsing error";
- public static final String INVALID_PARAMETER_MESSAGE = "Invalid parameter value.";
+ public static final String INVALID_PARAMETER_MESSAGE = "Invalid parameter value";
- public static final String CLIENT_CONNECTION_FAILED_MESSAGE = "Connection failed.";
+ public static final String CLIENT_CONNECTION_FAILED_MESSAGE = "Connection failed";
- public static final String CONNECTION_BROKE_MESSAGE = "Connection error.";
+ public static final String CONNECTION_BROKE_MESSAGE = "Connection error";
@Override
public int handle(ExceptionWriter err, SQLException e) {
+ var errorComponentBuilder = ErrorUiComponent.builder();
+
switch (e.getSQLState()) {
case SqlStateCode.CONNECTION_FAILURE:
case SqlStateCode.CONNECTION_CLOSED:
case SqlStateCode.CONNECTION_REJECTED:
- err.write(CONNECTION_BROKE_MESSAGE);
+ errorComponentBuilder.header(CONNECTION_BROKE_MESSAGE);
break;
case SqlStateCode.PARSING_EXCEPTION:
- err.write(String.format(PARSING_ERROR_MESSAGE, e.getMessage()));
+ errorComponentBuilder.header(PARSING_ERROR_MESSAGE).details(e.getMessage());
break;
case SqlStateCode.INVALID_PARAMETER_VALUE:
- err.write(INVALID_PARAMETER_MESSAGE);
+ errorComponentBuilder.header(INVALID_PARAMETER_MESSAGE);
break;
case SqlStateCode.CLIENT_CONNECTION_FAILED:
- err.write(CLIENT_CONNECTION_FAILED_MESSAGE);
+ errorComponentBuilder.header(CLIENT_CONNECTION_FAILED_MESSAGE);
break;
default:
LOG.error("Unrecognized error", e);
- err.write("Unrecognized error while process SQL query.");
+ errorComponentBuilder.header("Unrecognized error while process SQL query");
}
+
+ err.write(errorComponentBuilder.build().render());
return 1;
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/TimeoutExceptionHandler.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/TimeoutExceptionHandler.java
index 70c81ec800..9b67cc8fa0 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/TimeoutExceptionHandler.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/TimeoutExceptionHandler.java
@@ -20,6 +20,7 @@ package org.apache.ignite.cli.core.exception.handler;
import java.util.concurrent.TimeoutException;
import org.apache.ignite.cli.core.exception.ExceptionHandler;
import org.apache.ignite.cli.core.exception.ExceptionWriter;
+import org.apache.ignite.cli.core.style.component.ErrorUiComponent;
import org.apache.ignite.internal.logger.IgniteLogger;
import org.apache.ignite.internal.logger.Loggers;
@@ -32,7 +33,14 @@ public class TimeoutExceptionHandler implements ExceptionHandler<TimeoutExceptio
@Override
public int handle(ExceptionWriter err, TimeoutException e) {
LOG.error("Timeout exception", e);
- err.write("Command failed with timeout.");
+ err.write(
+ ErrorUiComponent.builder()
+ .header("The command is running for too long")
+ .details(e.getMessage())
+ .build()
+ .render()
+ );
+
return 1;
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/UnknownCommandExceptionHandler.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/UnknownCommandExceptionHandler.java
index 5750ecdcdd..728bf4e732 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/UnknownCommandExceptionHandler.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/UnknownCommandExceptionHandler.java
@@ -19,6 +19,7 @@ package org.apache.ignite.cli.core.exception.handler;
import org.apache.ignite.cli.core.exception.ExceptionHandler;
import org.apache.ignite.cli.core.exception.ExceptionWriter;
+import org.apache.ignite.cli.core.style.component.ErrorUiComponent;
import org.jline.console.impl.SystemRegistryImpl.UnknownCommandException;
/**
@@ -29,7 +30,9 @@ public class UnknownCommandExceptionHandler implements ExceptionHandler<UnknownC
@Override
public int handle(ExceptionWriter err, UnknownCommandException e) {
- err.write(e.getMessage());
+ err.write(
+ ErrorUiComponent.fromHeader(e.getMessage()).render()
+ );
// This exception is only thrown in the REPL mode so the return value is irrelevant, but we use 2 to keep it consistent
return 2;
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/core/flow/builder/FlowBuilderImpl.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/flow/builder/FlowBuilderImpl.java
index 2de366e741..ffd8559f42 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/core/flow/builder/FlowBuilderImpl.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/flow/builder/FlowBuilderImpl.java
@@ -21,7 +21,6 @@ import java.io.PrintWriter;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
-import org.apache.ignite.cli.commands.decorators.DefaultDecoratorRegistry;
import org.apache.ignite.cli.core.decorator.DecoratorRegistry;
import org.apache.ignite.cli.core.exception.ExceptionHandler;
import org.apache.ignite.cli.core.exception.ExceptionHandlers;
@@ -33,6 +32,7 @@ import org.apache.ignite.cli.core.flow.Flowable;
import org.apache.ignite.cli.core.flow.question.QuestionAnswer;
import org.apache.ignite.cli.core.flow.question.QuestionAskerFactory;
import org.apache.ignite.cli.core.repl.context.CommandLineContextProvider;
+import org.apache.ignite.cli.decorators.DefaultDecoratorRegistry;
/**
* Implementation of {@link FlowBuilder}.
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/core/flow/builder/Flows.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/flow/builder/Flows.java
index af62836012..9070efd5af 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/core/flow/builder/Flows.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/flow/builder/Flows.java
@@ -30,14 +30,13 @@ import org.apache.ignite.cli.core.flow.question.AcceptedQuestionAnswer;
import org.apache.ignite.cli.core.flow.question.InterruptQuestionAnswer;
import org.apache.ignite.cli.core.flow.question.QuestionAnswer;
import org.apache.ignite.cli.core.flow.question.QuestionAskerFactory;
+import org.apache.ignite.cli.core.style.component.QuestionUiComponent;
/**
* Helper class for operating and creating {@link Flow} and {@link FlowBuilder}.
*/
public final class Flows {
-
private Flows() {
-
}
/**
@@ -140,4 +139,18 @@ public final class Flows {
)
.then(Flows.mono(unused -> onAccept.get()));
}
+
+ /**
+ * Create new {@link FlowBuilder} which starts from yes/no question and pass the result of the @{code onAccept}
+ * call on positive answer or interrupts the flow on negative answer.
+ *
+ * @param question question UI component.
+ * @param onAccept callback to call on positive answer
+ * @param <I> input type.
+ * @param <O> output type.
+ * @return new {@link FlowBuilder}.
+ */
+ public static <I, O> FlowBuilder<I, O> acceptQuestion(QuestionUiComponent question, Supplier<O> onAccept) {
+ return acceptQuestion(question.render(), onAccept);
+ }
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/core/style/AnsiStringSupport.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/style/AnsiStringSupport.java
index 0d3ce6de44..e9e20a6e62 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/core/style/AnsiStringSupport.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/style/AnsiStringSupport.java
@@ -33,26 +33,63 @@ public final class AnsiStringSupport {
return new Fg(color);
}
+ /**
+ * Can mark the string as a ANSI string.
+ */
+ public interface Marker {
+ String mark(String content);
+ }
+
/**
* Dsl for ansi fg: takes color and marks string like: mytext -> @|fg(10) mytext|@ where 10 is the ansi color.
*/
- public static class Fg {
+ public static class Fg implements Marker {
private final Color color;
+ private Style style;
private Fg(Color color) {
this.color = color;
}
+ public Fg with(Style style) {
+ this.style = style;
+ return this;
+ }
+
+ /**
+ * Marks given text with the configured before style.
+ */
public String mark(String textToMark) {
+ if (style == Style.BOLD) {
+ return String.format("@|fg(%d),bold %s|@", color.code, textToMark);
+ }
return String.format("@|fg(%d) %s|@", color.code, textToMark);
}
}
+ /**
+ * Represents the text style.
+ */
+ public enum Style implements Marker {
+ BOLD("bold"),
+ UNDERLINE("underline");
+
+ private final String value;
+
+ Style(String value) {
+ this.value = value;
+ }
+
+ public String mark(String textToMark) {
+ return String.format("@|%s %s|@", value, textToMark);
+ }
+ }
+
/**
* Represents ansi colors that are used in CLI.
*/
public enum Color {
- RED(1), GREEN(2), YELLOW(3);
+ RED(1), GREEN(2), YELLOW(3), GRAY(246);
Color(int code) {
this.code = code;
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/DefaultDecorator.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/style/component/CommonMessages.java
similarity index 50%
copy from modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/DefaultDecorator.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/core/style/component/CommonMessages.java
index c2f1518b54..7bbf3f3518 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/DefaultDecorator.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/style/component/CommonMessages.java
@@ -15,21 +15,23 @@
* limitations under the License.
*/
-package org.apache.ignite.cli.commands.decorators;
+package org.apache.ignite.cli.core.style.component;
-import org.apache.ignite.cli.core.decorator.Decorator;
-import org.apache.ignite.cli.core.decorator.TerminalOutput;
+import org.apache.ignite.cli.core.style.element.UiElements;
/**
- * Default decorator that calls toString method.
- *
- * @param <I> Input type.
+ * Common UI messages.
*/
-public class DefaultDecorator<I> implements Decorator<I, TerminalOutput> {
+public class CommonMessages {
+ public static MessageUiComponent CONNECT_OR_USE_CLUSTER_URL_MESSAGE = MessageUiComponent.builder()
+ .message("You are not connected to node")
+ .hint("Run %s command or use %s option",
+ UiElements.command("connect"), UiElements.option("--cluster-url"))
+ .build();
- /** {@inheritDoc} */
- @Override
- public TerminalOutput decorate(I data) {
- return data::toString;
- }
+ public static MessageUiComponent CONNECT_OR_USE_NODE_URL_MESSAGE = MessageUiComponent.builder()
+ .message("You are not connected to node")
+ .hint("Run %s command or use %s option",
+ UiElements.command("connect"), UiElements.option("--node-url"))
+ .build();
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/core/style/component/ErrorUiComponent.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/style/component/ErrorUiComponent.java
new file mode 100644
index 0000000000..96d4267429
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/style/component/ErrorUiComponent.java
@@ -0,0 +1,132 @@
+/*
+ * 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.cli.core.style.component;
+
+import static org.apache.ignite.cli.core.style.AnsiStringSupport.ansi;
+import static org.apache.ignite.cli.core.style.AnsiStringSupport.fg;
+
+import java.util.UUID;
+import org.apache.ignite.cli.core.style.AnsiStringSupport.Color;
+import org.apache.ignite.cli.core.style.AnsiStringSupport.Style;
+import org.apache.ignite.cli.core.style.element.UiElement;
+import org.apache.ignite.cli.core.style.element.UiString;
+
+/**
+ * UI component that represent any error message.
+ */
+public class ErrorUiComponent implements UiComponent {
+ private final String header;
+
+ private final UiElement[] headerUiElements;
+
+ private final String details;
+
+ private final UiElement[] detailsUiElements;
+
+ private final UUID traceId;
+
+ private final String errorCode;
+
+ private ErrorUiComponent(
+ String header, UiElement[] headerUiElements,
+ String details, UiElement[] detailsUiElements,
+ UUID traceId,
+ String errorCode) {
+ this.header = header;
+ this.headerUiElements = headerUiElements;
+ this.details = details;
+ this.detailsUiElements = detailsUiElements;
+ this.traceId = traceId;
+ this.errorCode = errorCode;
+ }
+
+ /** Creates ErrorComponent from given header. */
+ public static ErrorUiComponent fromHeader(String header) {
+ return builder().header(header).build();
+ }
+
+ /** Builder. */
+ public static ErrorComponentBuilder builder() {
+ return new ErrorComponentBuilder();
+ }
+
+ public String header() {
+ return header;
+ }
+
+ public String details() {
+ return details;
+ }
+
+ @Override
+ public String render() {
+ return ansi(
+ (errorCode == null ? "" : fg(Color.GRAY).mark(errorCode))
+ + traceDetails()
+ + fg(Color.RED).with(Style.BOLD).mark(ansi(UiString.format(header, headerUiElements)))
+ + (details == null ? "" : System.lineSeparator() + UiString.format(details, detailsUiElements))
+ );
+ }
+
+ private String traceDetails() {
+ return traceId == null ? "" : fg(Color.GRAY).mark(" Trace ID: " + traceId + System.lineSeparator());
+ }
+
+ /** Builder. */
+ public static class ErrorComponentBuilder {
+ private String header;
+
+ private UiElement[] headerUiElements;
+
+ private String details;
+
+ private UiElement[] detailsUiElement;
+
+ private UUID traceId;
+
+ private String errorCode;
+
+ /** Sets header. */
+ public ErrorComponentBuilder header(String header, UiElement... uiElements) {
+ this.header = header;
+ this.headerUiElements = uiElements;
+ return this;
+ }
+
+ /** Sets details. */
+ public ErrorComponentBuilder details(String details, UiElement... uiElements) {
+ this.details = details;
+ this.detailsUiElement = uiElements;
+ return this;
+ }
+
+ public ErrorComponentBuilder traceId(UUID traceId) {
+ this.traceId = traceId;
+ return this;
+ }
+
+ public ErrorComponentBuilder errorCode(String errorCode) {
+ this.errorCode = errorCode;
+ return this;
+ }
+
+ public ErrorUiComponent build() {
+ return new ErrorUiComponent(header, headerUiElements, details, detailsUiElement, traceId, errorCode);
+ }
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/core/style/component/MessageUiComponent.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/style/component/MessageUiComponent.java
new file mode 100644
index 0000000000..184d013391
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/style/component/MessageUiComponent.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.cli.core.style.component;
+
+import static org.apache.ignite.cli.core.style.AnsiStringSupport.ansi;
+
+import org.apache.ignite.cli.core.style.element.UiElement;
+import org.apache.ignite.cli.core.style.element.UiString;
+
+/**
+ * UI component that represents a message.
+ */
+public class MessageUiComponent implements UiComponent {
+ private final String message;
+
+ private final UiElement[] messageUiElements;
+
+ private final String hint;
+
+ private final UiElement[] hintUiElements;
+
+ private MessageUiComponent(
+ String message,
+ UiElement[] messageUiElements,
+ String hint,
+ UiElement[] hintUiElements) {
+ this.message = message;
+ this.messageUiElements = messageUiElements;
+ this.hint = hint;
+ this.hintUiElements = hintUiElements;
+ }
+
+ @Override
+ public String render() {
+ String messageString = UiString.format(message, messageUiElements);
+ String hintString = UiString.format(hint, hintUiElements);
+ return ansi(
+ messageString
+ + (hintString == null ? "" : System.lineSeparator() + hintString)
+ );
+ }
+
+ public static MessageUiComponent fromMessage(String message, UiElement... messageUiElements) {
+ return builder().message(message, messageUiElements).build();
+ }
+
+ /** Builder. */
+ public static MessageComponentBuilder builder() {
+ return new MessageComponentBuilder();
+ }
+
+ /** Builder. */
+ public static class MessageComponentBuilder {
+ private String message;
+
+ private String hint;
+
+ private UiElement[] messageUiElements;
+
+ private UiElement[] hintUiElements;
+
+ /** Sets message. */
+ public MessageComponentBuilder message(String message, UiElement... uiElements) {
+ this.message = message;
+ this.messageUiElements = uiElements;
+ return this;
+ }
+
+ /** Sets hint. */
+ public MessageComponentBuilder hint(String hint, UiElement... uiElements) {
+ this.hint = hint;
+ this.hintUiElements = uiElements;
+ return this;
+ }
+
+ public MessageUiComponent build() {
+ return new MessageUiComponent(message, messageUiElements, hint, hintUiElements);
+ }
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/core/style/component/QuestionUiComponent.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/style/component/QuestionUiComponent.java
new file mode 100644
index 0000000000..7d9a63fa9e
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/style/component/QuestionUiComponent.java
@@ -0,0 +1,71 @@
+/*
+ * 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.cli.core.style.component;
+
+import static org.apache.ignite.cli.core.style.AnsiStringSupport.ansi;
+
+import org.apache.ignite.cli.core.style.element.UiElement;
+import org.apache.ignite.cli.core.style.element.UiString;
+
+/**
+ * UI component that represents a question.
+ */
+public class QuestionUiComponent implements UiComponent {
+ private final String question;
+
+ private final UiElement[] questionUiElements;
+
+ private QuestionUiComponent(
+ String question,
+ UiElement[] questionUiElements) {
+ this.question = question;
+ this.questionUiElements = questionUiElements;
+ }
+
+ @Override
+ public String render() {
+ return ansi(UiString.format(question, questionUiElements));
+ }
+
+ public static QuestionUiComponent fromQuestion(String question, UiElement... questionUiElements) {
+ return builder().question(question, questionUiElements).build();
+ }
+
+ /** Builder. */
+ public static MessageComponentBuilder builder() {
+ return new MessageComponentBuilder();
+ }
+
+ /** Builder. */
+ public static class MessageComponentBuilder {
+ private String question;
+
+ private UiElement[] questionUiElements;
+
+ /** Sets question. */
+ public MessageComponentBuilder question(String question, UiElement... uiElements) {
+ this.question = question;
+ this.questionUiElements = uiElements;
+ return this;
+ }
+
+ public QuestionUiComponent build() {
+ return new QuestionUiComponent(question, questionUiElements);
+ }
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/config/ini/SectionAlreadyExistsException.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/style/component/UiComponent.java
similarity index 73%
copy from modules/cli/src/main/java/org/apache/ignite/cli/config/ini/SectionAlreadyExistsException.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/core/style/component/UiComponent.java
index 72b69ffe7e..b63f8fa083 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/config/ini/SectionAlreadyExistsException.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/style/component/UiComponent.java
@@ -15,13 +15,11 @@
* limitations under the License.
*/
-package org.apache.ignite.cli.config.ini;
+package org.apache.ignite.cli.core.style.component;
/**
- * Exception when already created INI section trying to create.
+ * UI component that can render to ANSI String.
*/
-public class SectionAlreadyExistsException extends RuntimeException {
- public SectionAlreadyExistsException(String name) {
- super("Section " + name + " already exists.");
- }
+public interface UiComponent {
+ String render();
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/DefaultDecorator.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/style/element/MarkedUiElement.java
similarity index 59%
copy from modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/DefaultDecorator.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/core/style/element/MarkedUiElement.java
index c2f1518b54..56522c1f3b 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/DefaultDecorator.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/style/element/MarkedUiElement.java
@@ -15,21 +15,30 @@
* limitations under the License.
*/
-package org.apache.ignite.cli.commands.decorators;
+package org.apache.ignite.cli.core.style.element;
-import org.apache.ignite.cli.core.decorator.Decorator;
-import org.apache.ignite.cli.core.decorator.TerminalOutput;
+import org.apache.ignite.cli.core.style.AnsiStringSupport.Marker;
/**
- * Default decorator that calls toString method.
- *
- * @param <I> Input type.
+ * IU element that is marked with provided ANSI marker.
*/
-public class DefaultDecorator<I> implements Decorator<I, TerminalOutput> {
+public class MarkedUiElement implements UiElement {
+ private final String content;
+
+ private final Marker marker;
+
+ MarkedUiElement(String content, Marker marker) {
+ this.content = content;
+ this.marker = marker;
+ }
+
+ @Override
+ public String represent() {
+ return marker.mark(content);
+ }
- /** {@inheritDoc} */
@Override
- public TerminalOutput decorate(I data) {
- return data::toString;
+ public String toString() {
+ return represent();
}
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/config/ini/SectionAlreadyExistsException.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/style/element/UiElement.java
similarity index 73%
copy from modules/cli/src/main/java/org/apache/ignite/cli/config/ini/SectionAlreadyExistsException.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/core/style/element/UiElement.java
index 72b69ffe7e..a808770e69 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/config/ini/SectionAlreadyExistsException.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/style/element/UiElement.java
@@ -15,13 +15,12 @@
* limitations under the License.
*/
-package org.apache.ignite.cli.config.ini;
+package org.apache.ignite.cli.core.style.element;
/**
- * Exception when already created INI section trying to create.
+ * Can represent UI Element as an ANSI string.
*/
-public class SectionAlreadyExistsException extends RuntimeException {
- public SectionAlreadyExistsException(String name) {
- super("Section " + name + " already exists.");
- }
+public interface UiElement {
+ /** Represents the UI Element as ansi String. For example '@|fg(1) text |@'. */
+ String represent();
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/NodeStatusDecorator.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/style/element/UiElements.java
similarity index 54%
copy from modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/NodeStatusDecorator.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/core/style/element/UiElements.java
index 2cb9c08cfb..224130bb7a 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/NodeStatusDecorator.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/style/element/UiElements.java
@@ -15,30 +15,34 @@
* limitations under the License.
*/
-package org.apache.ignite.cli.commands.decorators;
+package org.apache.ignite.cli.core.style.element;
-import static org.apache.ignite.cli.core.style.AnsiStringSupport.ansi;
import static org.apache.ignite.cli.core.style.AnsiStringSupport.fg;
-import org.apache.ignite.cli.call.node.status.NodeStatus;
-import org.apache.ignite.cli.call.node.status.State;
-import org.apache.ignite.cli.core.decorator.Decorator;
-import org.apache.ignite.cli.core.decorator.TerminalOutput;
import org.apache.ignite.cli.core.style.AnsiStringSupport.Color;
+import org.apache.ignite.cli.core.style.AnsiStringSupport.Style;
/**
- * Decorator for {@link NodeStatus}.
+ * Defines all UI Elements that are used in the CLI.
*/
-public class NodeStatusDecorator implements Decorator<NodeStatus, TerminalOutput> {
+public class UiElements {
+ public static UiElement url(String content) {
+ return new MarkedUiElement(content, Style.UNDERLINE);
+ }
+
+ public static UiElement command(String content) {
+ return new MarkedUiElement(content, Style.BOLD);
+ }
- @Override
- public TerminalOutput decorate(NodeStatus data) {
- Color c = data.state().equals(State.STARTED) ? Color.GREEN : Color.YELLOW;
+ public static UiElement option(String content) {
+ return new MarkedUiElement(content, fg(Color.YELLOW));
+ }
+
+ public static UiElement done() {
+ return new MarkedUiElement("Done", fg(Color.GREEN).with(Style.BOLD));
+ }
- return () -> ansi(
- "[name: %s, state: %s]",
- data.name(),
- fg(c).mark(data.state().name().toLowerCase())
- );
+ public static UiElement yesNo() {
+ return new MarkedUiElement("[Y/n]", fg(Color.GRAY));
}
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/DefaultDecorator.java b/modules/cli/src/main/java/org/apache/ignite/cli/core/style/element/UiString.java
similarity index 61%
copy from modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/DefaultDecorator.java
copy to modules/cli/src/main/java/org/apache/ignite/cli/core/style/element/UiString.java
index c2f1518b54..ee92072444 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/DefaultDecorator.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/core/style/element/UiString.java
@@ -15,21 +15,22 @@
* limitations under the License.
*/
-package org.apache.ignite.cli.commands.decorators;
+package org.apache.ignite.cli.core.style.element;
-import org.apache.ignite.cli.core.decorator.Decorator;
-import org.apache.ignite.cli.core.decorator.TerminalOutput;
+import java.util.Arrays;
/**
- * Default decorator that calls toString method.
- *
- * @param <I> Input type.
+ * Can format the string with UI Elements.
*/
-public class DefaultDecorator<I> implements Decorator<I, TerminalOutput> {
+public class UiString {
+ /** Accepts the String template with UI Elements. */
+ public static String format(String template, UiElement... elements) {
+ if (elements == null || elements.length == 0) {
+ return template;
+ }
+
+ Object[] elementsAsObjects = Arrays.stream(elements).map(Object.class::cast).toArray();
- /** {@inheritDoc} */
- @Override
- public TerminalOutput decorate(I data) {
- return data::toString;
+ return String.format(template, elementsAsObjects);
}
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/ClusterStatusDecorator.java b/modules/cli/src/main/java/org/apache/ignite/cli/decorators/ClusterStatusDecorator.java
similarity index 97%
rename from modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/ClusterStatusDecorator.java
rename to modules/cli/src/main/java/org/apache/ignite/cli/decorators/ClusterStatusDecorator.java
index 01371aaa9e..2935b7d058 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/ClusterStatusDecorator.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/decorators/ClusterStatusDecorator.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-package org.apache.ignite.cli.commands.decorators;
+package org.apache.ignite.cli.decorators;
import static org.apache.ignite.cli.core.style.AnsiStringSupport.ansi;
import static org.apache.ignite.cli.core.style.AnsiStringSupport.fg;
@@ -29,7 +29,6 @@ import org.apache.ignite.cli.core.style.AnsiStringSupport.Color;
* Decorator for {@link ClusterStatus}.
*/
public class ClusterStatusDecorator implements Decorator<ClusterStatus, TerminalOutput> {
-
@Override
public TerminalOutput decorate(ClusterStatus data) {
return data.isInitialized()
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/DefaultDecorator.java b/modules/cli/src/main/java/org/apache/ignite/cli/decorators/DefaultDecorator.java
similarity index 95%
rename from modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/DefaultDecorator.java
rename to modules/cli/src/main/java/org/apache/ignite/cli/decorators/DefaultDecorator.java
index c2f1518b54..811db85898 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/DefaultDecorator.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/decorators/DefaultDecorator.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-package org.apache.ignite.cli.commands.decorators;
+package org.apache.ignite.cli.decorators;
import org.apache.ignite.cli.core.decorator.Decorator;
import org.apache.ignite.cli.core.decorator.TerminalOutput;
@@ -26,7 +26,6 @@ import org.apache.ignite.cli.core.decorator.TerminalOutput;
* @param <I> Input type.
*/
public class DefaultDecorator<I> implements Decorator<I, TerminalOutput> {
-
/** {@inheritDoc} */
@Override
public TerminalOutput decorate(I data) {
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/DefaultDecoratorRegistry.java b/modules/cli/src/main/java/org/apache/ignite/cli/decorators/DefaultDecoratorRegistry.java
similarity index 97%
rename from modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/DefaultDecoratorRegistry.java
rename to modules/cli/src/main/java/org/apache/ignite/cli/decorators/DefaultDecoratorRegistry.java
index 26b95d839a..20f483219a 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/DefaultDecoratorRegistry.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/decorators/DefaultDecoratorRegistry.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-package org.apache.ignite.cli.commands.decorators;
+package org.apache.ignite.cli.decorators;
import org.apache.ignite.cli.call.cluster.status.ClusterStatus;
import org.apache.ignite.cli.call.configuration.JsonString;
@@ -29,7 +29,6 @@ import org.apache.ignite.cli.sql.table.Table;
* Default set of {@link org.apache.ignite.cli.core.decorator.Decorator}.
*/
public class DefaultDecoratorRegistry extends DecoratorRegistry {
-
/**
* Constructor.
*/
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/JsonDecorator.java b/modules/cli/src/main/java/org/apache/ignite/cli/decorators/JsonDecorator.java
similarity index 97%
rename from modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/JsonDecorator.java
rename to modules/cli/src/main/java/org/apache/ignite/cli/decorators/JsonDecorator.java
index da54585629..8c83da7030 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/JsonDecorator.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/decorators/JsonDecorator.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-package org.apache.ignite.cli.commands.decorators;
+package org.apache.ignite.cli.decorators;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
@@ -28,7 +28,6 @@ import org.apache.ignite.cli.core.decorator.TerminalOutput;
* Pretty json decorator.
*/
public class JsonDecorator implements Decorator<JsonString, TerminalOutput> {
-
/** {@inheritDoc} */
@Override
public TerminalOutput decorate(JsonString json) {
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/NodeStatusDecorator.java b/modules/cli/src/main/java/org/apache/ignite/cli/decorators/NodeStatusDecorator.java
similarity index 97%
rename from modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/NodeStatusDecorator.java
rename to modules/cli/src/main/java/org/apache/ignite/cli/decorators/NodeStatusDecorator.java
index 2cb9c08cfb..de2d73b899 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/NodeStatusDecorator.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/decorators/NodeStatusDecorator.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-package org.apache.ignite.cli.commands.decorators;
+package org.apache.ignite.cli.decorators;
import static org.apache.ignite.cli.core.style.AnsiStringSupport.ansi;
import static org.apache.ignite.cli.core.style.AnsiStringSupport.fg;
@@ -30,7 +30,6 @@ import org.apache.ignite.cli.core.style.AnsiStringSupport.Color;
* Decorator for {@link NodeStatus}.
*/
public class NodeStatusDecorator implements Decorator<NodeStatus, TerminalOutput> {
-
@Override
public TerminalOutput decorate(NodeStatus data) {
Color c = data.state().equals(State.STARTED) ? Color.GREEN : Color.YELLOW;
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/ProfileDecorator.java b/modules/cli/src/main/java/org/apache/ignite/cli/decorators/ProfileDecorator.java
similarity index 96%
rename from modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/ProfileDecorator.java
rename to modules/cli/src/main/java/org/apache/ignite/cli/decorators/ProfileDecorator.java
index c9d4271c93..468b8eac2f 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/ProfileDecorator.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/decorators/ProfileDecorator.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-package org.apache.ignite.cli.commands.decorators;
+package org.apache.ignite.cli.decorators;
import java.util.stream.Collectors;
import org.apache.ignite.cli.config.Profile;
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/SqlQueryResultDecorator.java b/modules/cli/src/main/java/org/apache/ignite/cli/decorators/SqlQueryResultDecorator.java
similarity index 96%
rename from modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/SqlQueryResultDecorator.java
rename to modules/cli/src/main/java/org/apache/ignite/cli/decorators/SqlQueryResultDecorator.java
index 619fcffbc0..5bf72ca27f 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/SqlQueryResultDecorator.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/decorators/SqlQueryResultDecorator.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-package org.apache.ignite.cli.commands.decorators;
+package org.apache.ignite.cli.decorators;
import org.apache.ignite.cli.core.decorator.Decorator;
import org.apache.ignite.cli.core.decorator.TerminalOutput;
@@ -25,7 +25,6 @@ import org.apache.ignite.cli.sql.SqlQueryResult;
* Composite decorator for {@link SqlQueryResult}.
*/
public class SqlQueryResultDecorator implements Decorator<SqlQueryResult, TerminalOutput> {
-
private final TableDecorator tableDecorator = new TableDecorator();
private final DefaultDecorator<String> messageDecorator = new DefaultDecorator<>();
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/TableDecorator.java b/modules/cli/src/main/java/org/apache/ignite/cli/decorators/TableDecorator.java
similarity index 96%
rename from modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/TableDecorator.java
rename to modules/cli/src/main/java/org/apache/ignite/cli/decorators/TableDecorator.java
index d1b8b72d73..c6ce5806e6 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/TableDecorator.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/decorators/TableDecorator.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-package org.apache.ignite.cli.commands.decorators;
+package org.apache.ignite.cli.decorators;
import com.jakewharton.fliptables.FlipTableConverters;
import org.apache.ignite.cli.core.decorator.Decorator;
@@ -26,7 +26,6 @@ import org.apache.ignite.cli.sql.table.Table;
* Implementation of {@link Decorator} for {@link Table}.
*/
public class TableDecorator implements Decorator<Table, TerminalOutput> {
-
/**
* Transform {@link Table} to {@link TerminalOutput}.
*
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/TopologyDecorator.java b/modules/cli/src/main/java/org/apache/ignite/cli/decorators/TopologyDecorator.java
similarity index 97%
rename from modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/TopologyDecorator.java
rename to modules/cli/src/main/java/org/apache/ignite/cli/decorators/TopologyDecorator.java
index af5426eb89..42d9ecea3e 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/commands/decorators/TopologyDecorator.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/decorators/TopologyDecorator.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-package org.apache.ignite.cli.commands.decorators;
+package org.apache.ignite.cli.decorators;
import com.jakewharton.fliptables.FlipTable;
import java.util.List;
@@ -27,7 +27,6 @@ import org.apache.ignite.rest.client.model.ClusterNode;
* Implementation of {@link Decorator} for the list of {@link ClusterNode}.
*/
public class TopologyDecorator implements Decorator<List<ClusterNode>, TerminalOutput> {
-
/**
* Transform list of {@link ClusterNode} to {@link TerminalOutput}.
*
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/deprecated/builtins/init/InitIgniteCommand.java b/modules/cli/src/main/java/org/apache/ignite/cli/deprecated/builtins/init/InitIgniteCommand.java
index 5240c84d9c..705a415970 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/deprecated/builtins/init/InitIgniteCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/deprecated/builtins/init/InitIgniteCommand.java
@@ -18,6 +18,7 @@
package org.apache.ignite.cli.deprecated.builtins.init;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.apache.ignite.cli.core.style.AnsiStringSupport.ansi;
import jakarta.inject.Inject;
import java.io.File;
@@ -31,14 +32,14 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.Optional;
import java.util.Properties;
+import org.apache.ignite.cli.core.style.component.MessageUiComponent;
+import org.apache.ignite.cli.core.style.element.UiElements;
import org.apache.ignite.cli.deprecated.CliPathsConfigLoader;
import org.apache.ignite.cli.deprecated.IgniteCliException;
import org.apache.ignite.cli.deprecated.IgnitePaths;
-import org.apache.ignite.cli.deprecated.Table;
import org.apache.ignite.cli.deprecated.builtins.SystemPathResolver;
import org.apache.ignite.cli.deprecated.builtins.module.ModuleManager;
import org.jetbrains.annotations.NotNull;
-import picocli.CommandLine.Help.Ansi;
import picocli.CommandLine.Help.ColorScheme;
/**
@@ -62,8 +63,8 @@ public class InitIgniteCommand {
/**
* Creates init command instance.
*
- * @param pathRslvr Resolver of paths like home directory and etc.
- * @param moduleMgr Manager of Ignite server and CLI modules.
+ * @param pathRslvr Resolver of paths like home directory and etc.
+ * @param moduleMgr Manager of Ignite server and CLI modules.
* @param cliPathsCfgLdr Loader of current Ignite distro dirs configuration.
*/
@Inject
@@ -81,8 +82,8 @@ public class InitIgniteCommand {
* to recover after corruption of node directories structure.
*
* @param urls Urls with custom maven repositories for Ignite download.
- * @param out PrintWriter for output user message.
- * @param cs ColorScheme for enriching user outputs with colors.
+ * @param out PrintWriter for output user message.
+ * @param cs ColorScheme for enriching user outputs with colors.
*/
public void init(URL[] urls, PrintWriter out, ColorScheme cs) {
moduleMgr.setOut(out);
@@ -95,21 +96,13 @@ public class InitIgniteCommand {
IgnitePaths cfg = cliPathsCfgLdr.loadIgnitePathsConfig().get();
- out.print("Creating directories... ");
-
cfg.initOrRecover();
- out.println(Ansi.AUTO.string("@|green,bold Done!|@"));
-
- Table tbl = new Table(0, cs);
-
- tbl.addRow("@|bold Binaries Directory|@", cfg.binDir);
- tbl.addRow("@|bold Work Directory|@", cfg.workDir);
- tbl.addRow("@|bold Config Directory|@", cfg.cfgDir);
- tbl.addRow("@|bold Log Directory|@", cfg.logDir);
-
- out.println(tbl);
- out.println();
+ out.println(cfg.binDir);
+ out.println(cfg.workDir);
+ out.println(cfg.cfgDir);
+ out.println(cfg.logDir);
+ out.println(ansi(UiElements.done().represent()));
installIgnite(cfg, urls);
@@ -118,8 +111,13 @@ public class InitIgniteCommand {
initJavaUtilLoggingPros(cfg.serverJavaUtilLoggingPros());
out.println();
- out.println("Apache Ignite is successfully initialized. Use the "
- + cs.commandText("ignite node start") + " command to start a new local node.");
+ out.println(
+ MessageUiComponent.builder()
+ .message("Apache Ignite is successfully initialized")
+ .hint("Run the " + cs.commandText("ignite node start") + " command to start a new local node")
+ .build()
+ .render()
+ );
}
/**
@@ -160,7 +158,7 @@ public class InitIgniteCommand {
* Downloads ignite node distro with all needed dependencies.
*
* @param ignitePaths Ignite distributive paths (bin, config, etc.).
- * @param urls Urls for custom maven repositories.
+ * @param urls Urls for custom maven repositories.
*/
private void installIgnite(IgnitePaths ignitePaths, URL[] urls) {
moduleMgr.addModule("_server", ignitePaths,
@@ -195,8 +193,8 @@ public class InitIgniteCommand {
/**
* Fills config file with bin and work directories paths.
*
- * @param f Config file.
- * @param binDir Path for bin dir.
+ * @param f Config file.
+ * @param binDir Path for bin dir.
* @param workDir Path for work dir.
*/
private void fillNewConfigFile(File f,
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/deprecated/spec/NodeCommandSpec.java b/modules/cli/src/main/java/org/apache/ignite/cli/deprecated/spec/NodeCommandSpec.java
index 49416d3d79..7bfaf09c68 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/deprecated/spec/NodeCommandSpec.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/deprecated/spec/NodeCommandSpec.java
@@ -17,6 +17,9 @@
package org.apache.ignite.cli.deprecated.spec;
+import static org.apache.ignite.cli.core.style.AnsiStringSupport.ansi;
+
+import com.jakewharton.fliptables.FlipTable;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import com.typesafe.config.ConfigRenderOptions;
@@ -31,10 +34,10 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import org.apache.ignite.cli.commands.BaseCommand;
+import org.apache.ignite.cli.core.style.element.UiElements;
import org.apache.ignite.cli.deprecated.CliPathsConfigLoader;
import org.apache.ignite.cli.deprecated.IgniteCliException;
import org.apache.ignite.cli.deprecated.IgnitePaths;
-import org.apache.ignite.cli.deprecated.Table;
import org.apache.ignite.cli.deprecated.builtins.node.NodeManager;
import picocli.CommandLine.ArgGroup;
import picocli.CommandLine.Command;
@@ -48,7 +51,7 @@ import picocli.CommandLine.Parameters;
*/
@Command(
name = "node",
- description = "Manages locally running Ignite nodes.",
+ description = "Manages locally running Ignite nodes",
subcommands = {
NodeCommandSpec.StartNodeCommandSpec.class,
NodeCommandSpec.StopNodeCommandSpec.class,
@@ -60,9 +63,12 @@ public class NodeCommandSpec {
/**
* Starts Ignite node command.
*/
- @Command(name = "start", description = "Starts an Ignite node locally.")
+ @Command(name = "start", description = "Starts an Ignite node locally")
@Singleton
public static class StartNodeCommandSpec extends BaseCommand implements Callable<Integer> {
+ /** Consistent id, which will be used by new node. */
+ @Parameters(paramLabel = "name", description = "Name of the new node")
+ public String nodeName;
/** Loader for Ignite distributive paths. */
@Inject
@@ -72,10 +78,6 @@ public class NodeCommandSpec {
@Inject
private NodeManager nodeMgr;
- /** Consistent id, which will be used by new node. */
- @Parameters(paramLabel = "name", description = "Name of the new node")
- public String nodeName;
-
@ArgGroup(exclusive = false)
private ConfigOptions configOptions;
@@ -117,18 +119,14 @@ public class NodeCommandSpec {
ignitePaths.serverJavaUtilLoggingPros(),
out);
+ out.println(ansi(UiElements.done().represent()));
+
+ out.println(String.format("[name: %s, pid: %d]", node.name, node.pid));
+
out.println();
out.println("Node is successfully started. To stop, type "
+ cs.commandText("ignite node stop ") + cs.parameterText(node.name));
- out.println();
-
- Table tbl = new Table(0, cs);
- tbl.addRow("@|bold Node name|@", node.name);
- tbl.addRow("@|bold PID|@", node.pid);
- tbl.addRow("@|bold Log File|@", node.logFile);
-
- out.println(tbl);
return 0;
}
@@ -189,10 +187,10 @@ public class NodeCommandSpec {
ColorScheme cs = spec.commandLine().getColorScheme();
consistentIds.forEach(p -> {
- out.print("Stopping locally running node with consistent ID " + cs.parameterText(p) + "... ");
+ out.println("Stopping locally running node with consistent ID " + cs.parameterText(p) + "...");
if (nodeMgr.stopWait(p, ignitePaths.cliPidsDir())) {
- out.println(cs.text("@|bold,green Done!|@"));
+ out.println(cs.text("@|bold,green Done|@"));
} else {
out.println(cs.text("@|bold,red Failed|@"));
}
@@ -225,23 +223,22 @@ public class NodeCommandSpec {
ColorScheme cs = spec.commandLine().getColorScheme();
if (nodes.isEmpty()) {
- out.println("Currently, there are no locally running nodes.");
- out.println();
- out.println("Use the " + cs.commandText("ignite node start")
- + " command to start a new node.");
+ out.println("There are no locally running nodes");
+ out.println("use the " + cs.commandText("ignite node start")
+ + " command to start a new node");
} else {
- out.println("Number of running nodes: " + cs.text("@|bold " + nodes.size() + "|@"));
- out.println();
+ String[] headers = {"consistent id", "pid", "log file"};
+ String[][] content = nodes.stream().map(
+ node -> new String[]{
+ node.name,
+ String.valueOf(node.pid),
+ String.valueOf(node.logFile)
+ }
+ ).toArray(String[][]::new);
- Table tbl = new Table(0, cs);
+ out.println(FlipTable.of(headers, content));
- tbl.addRow("@|bold Consistent ID|@", "@|bold PID|@", "@|bold Log File|@");
-
- for (NodeManager.RunningNode node : nodes) {
- tbl.addRow(node.name, node.pid, node.logFile);
- }
-
- out.println(tbl);
+ out.println("Number of running nodes: " + cs.text("@|bold " + nodes.size() + "|@"));
}
return 0;
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/deprecated/ui/ProgressBar.java b/modules/cli/src/main/java/org/apache/ignite/cli/deprecated/ui/ProgressBar.java
index 960d20b637..238771ca84 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/deprecated/ui/ProgressBar.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/deprecated/ui/ProgressBar.java
@@ -107,7 +107,7 @@ public class ProgressBar implements AutoCloseable {
var completedPart = ((double) curr / (double) max);
- // Space reserved for '||Done!'
+ // Space reserved for '||Done'
var reservedSpace = 7;
if (targetBarWidth < reservedSpace) {
@@ -133,7 +133,7 @@ public class ProgressBar implements AutoCloseable {
sb.append("|").append(" ".repeat(4 - percentageLen)).append(percentage);
} else {
- sb.append("=|@|green,bold Done!|@");
+ sb.append("=|@|green,bold Done|@");
}
return Ansi.AUTO.string(sb.toString());
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/sql/SqlQueryResult.java b/modules/cli/src/main/java/org/apache/ignite/cli/sql/SqlQueryResult.java
index 45ad9a97f0..9337ecf386 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/sql/SqlQueryResult.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/sql/SqlQueryResult.java
@@ -17,16 +17,15 @@
package org.apache.ignite.cli.sql;
-import org.apache.ignite.cli.commands.decorators.TableDecorator;
import org.apache.ignite.cli.core.decorator.Decorator;
import org.apache.ignite.cli.core.decorator.TerminalOutput;
+import org.apache.ignite.cli.decorators.TableDecorator;
import org.apache.ignite.cli.sql.table.Table;
/**
* Composite object of sql query result.
*/
public class SqlQueryResult {
-
private final Table<String> table;
private final String message;
diff --git a/modules/cli/src/test/java/org/apache/ignite/cli/commands/UrlOptionsNegativeTest.java b/modules/cli/src/test/java/org/apache/ignite/cli/commands/UrlOptionsNegativeTest.java
index a6307d4a1c..5926f51e59 100644
--- a/modules/cli/src/test/java/org/apache/ignite/cli/commands/UrlOptionsNegativeTest.java
+++ b/modules/cli/src/test/java/org/apache/ignite/cli/commands/UrlOptionsNegativeTest.java
@@ -164,7 +164,7 @@ public class UrlOptionsNegativeTest {
this::assertExitCodeIsFailure,
this::assertOutputIsEmpty,
() -> assertErrOutputIs(
- "Could not determine IP address when connecting to URL [url=http://no-such-host.com]" + System.lineSeparator())
+ "Unknown host: http://no-such-host.com" + System.lineSeparator())
);
}
@@ -177,7 +177,8 @@ public class UrlOptionsNegativeTest {
assertAll(
this::assertExitCodeIsFailure,
this::assertOutputIsEmpty,
- () -> assertErrOutputIs("Could not connect to URL [url=" + NODE_URL + "]" + System.lineSeparator())
+ () -> assertErrOutputIs("Node unavailable" + System.lineSeparator()
+ + "Could not connect to node with URL " + NODE_URL + System.lineSeparator())
);
}
@@ -213,8 +214,7 @@ public class UrlOptionsNegativeTest {
assertAll(
this::assertOutputIsEmpty,
- () -> assertErrOutputIs(
- "Could not determine IP address when connecting to URL [url=http://no-such-host.com]" + System.lineSeparator())
+ () -> assertErrOutputIs("Unknown host: http://no-such-host.com" + System.lineSeparator())
);
}
@@ -226,7 +226,8 @@ public class UrlOptionsNegativeTest {
assertAll(
this::assertOutputIsEmpty,
- () -> assertErrOutputIs("Could not connect to URL [url=" + NODE_URL + "]" + System.lineSeparator())
+ () -> assertErrOutputIs("Node unavailable" + System.lineSeparator()
+ + "Could not connect to node with URL " + NODE_URL + System.lineSeparator())
);
}
diff --git a/modules/cli/src/test/java/org/apache/ignite/cli/commands/flow/FlowTest.java b/modules/cli/src/test/java/org/apache/ignite/cli/commands/flow/FlowTest.java
index efe5194838..20896800b2 100644
--- a/modules/cli/src/test/java/org/apache/ignite/cli/commands/flow/FlowTest.java
+++ b/modules/cli/src/test/java/org/apache/ignite/cli/commands/flow/FlowTest.java
@@ -17,10 +17,16 @@
package org.apache.ignite.cli.commands.flow;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.emptyString;
+import static org.hamcrest.Matchers.equalTo;
+
import io.micronaut.test.extensions.junit5.annotation.MicronautTest;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
@@ -36,6 +42,7 @@ import org.jline.terminal.impl.DumbTerminal;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@@ -92,6 +99,70 @@ class FlowTest {
Assertions.assertEquals(1, call.value());
}
+ @Test
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-17519")
+ void printsToOutput() throws IOException {
+ // Given
+ bindAnswers("no"); // we don't care about answer in this test
+
+ StringWriter out = new StringWriter();
+ PrintWriter output = new PrintWriter(out);
+ StringWriter errOut = new StringWriter();
+ PrintWriter errOutput = new PrintWriter(errOut);
+
+ // When build flow
+ Flow<Object, String> build = Flows.question("Do you like this?",
+ List.of(new QuestionAnswer<>("yes"::equals, (a, i) -> 1),
+ new QuestionAnswer<>("no"::equals, (a, i) -> 2))
+ )
+ .map(String::valueOf)
+ .exceptionHandler(new TestExceptionHandler())
+ .then(Flows.fromCall(new ThrowingStrCall(), StrCallInput::new))
+ .toOutput(output, errOutput)
+ .build();
+
+ // Then the output is empty
+ assertThat(errOut.toString(), emptyString());
+
+ // When start flow
+ build.start(Flowable.empty());
+
+ // Then output equals to the message from the exception because we use TestExceptionHandler
+ assertThat(errOut.toString(), equalTo("Ooops!")); // BUT there is the message taken from default exception handler
+ }
+
+ @Test
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-17519")
+ void printsToOutputThatWorks() throws IOException {
+ // Given
+ bindAnswers("no"); // we don't care about answer in this test
+
+ StringWriter out = new StringWriter();
+ PrintWriter output = new PrintWriter(out);
+ StringWriter errOut = new StringWriter();
+ PrintWriter errOutput = new PrintWriter(errOut);
+
+ // When build flow
+ Flow<Object, String> build = Flows.question("Do you like this?",
+ List.of(new QuestionAnswer<>("yes"::equals, (a, i) -> 1),
+ new QuestionAnswer<>("no"::equals, (a, i) -> 2))
+ )
+ .map(String::valueOf)
+ .toOutput(output, errOutput)
+ .then(Flows.fromCall(new ThrowingStrCall(), StrCallInput::new))
+ .exceptionHandler(new TestExceptionHandler())
+ .build();
+
+ // Then the output is empty
+ assertThat(errOut.toString(), emptyString());
+
+ // When start flow
+ build.start(Flowable.empty());
+
+ // Then output equals to the message from the exception because we use TestExceptionHandler
+ assertThat(errOut.toString(), equalTo("Ooops!")); // BUT it is empty
+ }
+
private static Flow<Object, Integer> createFlow() {
return Flows.question("Do you like this?",
List.of(new QuestionAnswer<>("yes"::equals, (a, i) -> 1),
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/IgniteCliExceptionHandler.java b/modules/cli/src/test/java/org/apache/ignite/cli/commands/flow/TestExceptionHandler.java
similarity index 69%
copy from modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/IgniteCliExceptionHandler.java
copy to modules/cli/src/test/java/org/apache/ignite/cli/commands/flow/TestExceptionHandler.java
index 0d98620803..4466a6d23d 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/core/exception/handler/IgniteCliExceptionHandler.java
+++ b/modules/cli/src/test/java/org/apache/ignite/cli/commands/flow/TestExceptionHandler.java
@@ -15,24 +15,22 @@
* limitations under the License.
*/
-package org.apache.ignite.cli.core.exception.handler;
+package org.apache.ignite.cli.commands.flow;
+import jdk.jshell.spi.ExecutionControl.RunException;
import org.apache.ignite.cli.core.exception.ExceptionHandler;
import org.apache.ignite.cli.core.exception.ExceptionWriter;
-import org.apache.ignite.cli.deprecated.IgniteCliException;
-/**
- * Exception handler for {@link IgniteCliException}.
- */
-public class IgniteCliExceptionHandler implements ExceptionHandler<IgniteCliException> {
+class TestExceptionHandler implements ExceptionHandler<RunException> {
@Override
- public int handle(ExceptionWriter err, IgniteCliException e) {
+ public int handle(ExceptionWriter err, RunException e) {
err.write(e.getMessage());
- return 1;
+
+ return 0;
}
@Override
- public Class<IgniteCliException> applicableException() {
- return IgniteCliException.class;
+ public Class<RunException> applicableException() {
+ return RunException.class;
}
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/config/ini/SectionAlreadyExistsException.java b/modules/cli/src/test/java/org/apache/ignite/cli/commands/flow/ThrowingStrCall.java
similarity index 66%
copy from modules/cli/src/main/java/org/apache/ignite/cli/config/ini/SectionAlreadyExistsException.java
copy to modules/cli/src/test/java/org/apache/ignite/cli/commands/flow/ThrowingStrCall.java
index 72b69ffe7e..3f872a0de5 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/config/ini/SectionAlreadyExistsException.java
+++ b/modules/cli/src/test/java/org/apache/ignite/cli/commands/flow/ThrowingStrCall.java
@@ -15,13 +15,15 @@
* limitations under the License.
*/
-package org.apache.ignite.cli.config.ini;
+package org.apache.ignite.cli.commands.flow;
-/**
- * Exception when already created INI section trying to create.
- */
-public class SectionAlreadyExistsException extends RuntimeException {
- public SectionAlreadyExistsException(String name) {
- super("Section " + name + " already exists.");
+import org.apache.ignite.cli.core.call.Call;
+import org.apache.ignite.cli.core.call.CallOutput;
+import org.apache.ignite.cli.core.call.DefaultCallOutput;
+
+class ThrowingStrCall implements Call<StrCallInput, String> {
+ @Override
+ public CallOutput<String> execute(StrCallInput input) {
+ return DefaultCallOutput.failure(new RuntimeException("Ooops!"));
}
}
diff --git a/modules/cli/src/test/java/org/apache/ignite/cli/core/style/component/ErrorUiComponentTest.java b/modules/cli/src/test/java/org/apache/ignite/cli/core/style/component/ErrorUiComponentTest.java
new file mode 100644
index 0000000000..7cf0af6d59
--- /dev/null
+++ b/modules/cli/src/test/java/org/apache/ignite/cli/core/style/component/ErrorUiComponentTest.java
@@ -0,0 +1,81 @@
+/*
+ * 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.cli.core.style.component;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+
+import java.util.UUID;
+import org.apache.ignite.cli.core.style.element.UiElements;
+import org.junit.jupiter.api.Test;
+
+class ErrorUiComponentTest {
+ @Test
+ void rendersHeader() {
+ // Given
+ ErrorUiComponent errorUiComponent = ErrorUiComponent.fromHeader("Just single header");
+
+ // When
+ String rendered = errorUiComponent.render();
+
+ // Then
+ assertThat(rendered, equalTo("Just single header"));
+ }
+
+ @Test
+ void rendersAllParts() {
+ // Given
+ ErrorUiComponent errorUiComponent = ErrorUiComponent.builder()
+ .header("I am header")
+ .errorCode("IGN-TBL-01")
+ .traceId(UUID.fromString("a0c5aca8-73ab-4e7e-bf7a-64fdcf08ece7"))
+ .details("Some useful information about the error happened")
+ .build();
+
+ // When
+ String rendered = errorUiComponent.render();
+
+ // Then
+ assertThat(rendered, equalTo(
+ "IGN-TBL-01 Trace ID: a0c5aca8-73ab-4e7e-bf7a-64fdcf08ece7" + System.lineSeparator()
+ + "I am header" + System.lineSeparator()
+ + "Some useful information about the error happened"
+ ));
+ }
+
+ @Test
+ void renderAllPartsWithElements() {
+ // Given
+ ErrorUiComponent errorUiComponent = ErrorUiComponent.builder()
+ .header("I am header with url %s", UiElements.url("http://host.com"))
+ .errorCode("IGN-TBL-01")
+ .traceId(UUID.fromString("a0c5aca8-73ab-4e7e-bf7a-64fdcf08ece7"))
+ .details("Some useful information about the error happened with url %s", UiElements.url("http://host.com"))
+ .build();
+
+ // When
+ String rendered = errorUiComponent.render();
+
+ // Then
+ assertThat(rendered, equalTo(
+ "IGN-TBL-01 Trace ID: a0c5aca8-73ab-4e7e-bf7a-64fdcf08ece7" + System.lineSeparator()
+ + "I am header with url http://host.com" + System.lineSeparator()
+ + "Some useful information about the error happened with url http://host.com"
+ ));
+ }
+}
diff --git a/modules/cli/src/test/java/org/apache/ignite/cli/core/style/component/MessageUiComponentTest.java b/modules/cli/src/test/java/org/apache/ignite/cli/core/style/component/MessageUiComponentTest.java
new file mode 100644
index 0000000000..c8a5d8fb07
--- /dev/null
+++ b/modules/cli/src/test/java/org/apache/ignite/cli/core/style/component/MessageUiComponentTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.cli.core.style.component;
+
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+
+import org.apache.ignite.cli.core.style.element.UiElements;
+import org.junit.jupiter.api.Test;
+
+class MessageUiComponentTest {
+ @Test
+ void rendersMessageAndHint() {
+ // Given
+ var messageUiComponent = MessageUiComponent.builder()
+ .message("Hello, I am a message")
+ .hint("And I am a hint")
+ .build();
+
+ // When
+ String renderedUiComponent = messageUiComponent.render();
+
+ // Then
+ assertThat(renderedUiComponent, equalTo("Hello, I am a message" + System.lineSeparator() + "And I am a hint"));
+ }
+
+ @Test
+ void rendersMessage() {
+ // Given message without a hint
+ var messageUiComponent = MessageUiComponent.fromMessage("Hello, I am just a message");
+
+ // When
+ String renderedUiComponent = messageUiComponent.render();
+
+ // Then there is no \n at the end
+ assertThat(renderedUiComponent, equalTo("Hello, I am just a message"));
+ }
+
+ @Test
+ void rendersMessageWithUrl() {
+ // Given
+ MessageUiComponent messageUiComponent = MessageUiComponent.fromMessage(
+ "This is a message with url %s",
+ UiElements.url("https://host.com")
+ );
+
+ // When
+ String renderedUiComponent = messageUiComponent.render();
+
+ // Then
+ assertThat(renderedUiComponent, equalTo("This is a message with url https://host.com"));
+ }
+}
diff --git a/modules/cli/src/test/java/org/apache/ignite/cli/deprecated/IgniteCliInterfaceTest.java b/modules/cli/src/test/java/org/apache/ignite/cli/deprecated/IgniteCliInterfaceTest.java
index a4f5337e8e..e3d67984d4 100644
--- a/modules/cli/src/test/java/org/apache/ignite/cli/deprecated/IgniteCliInterfaceTest.java
+++ b/modules/cli/src/test/java/org/apache/ignite/cli/deprecated/IgniteCliInterfaceTest.java
@@ -203,14 +203,9 @@ public class IgniteCliInterfaceTest extends AbstractCliTest {
ignitePaths.serverJavaUtilLoggingPros(),
cli.getOut());
- assertOutputEqual("\nNode is successfully started. To stop, type ignite node stop " + nodeName + "\n\n"
- + "+-----------+---------+\n"
- + "| Node name | node1 |\n"
- + "+-----------+---------+\n"
- + "| PID | 1 |\n"
- + "+-----------+---------+\n"
- + "| Log File | logfile |\n"
- + "+-----------+---------+\n"
+ assertOutputEqual("Done\n"
+ + "[name: " + nodeName + ", pid: 1]\n\n"
+ + "Node is successfully started. To stop, type ignite node stop node1"
);
assertThatStderrIsEmpty();
}
@@ -343,7 +338,7 @@ public class IgniteCliInterfaceTest extends AbstractCliTest {
assertOutputEqual(
"Stopping locally running node with consistent ID "
+ cmd.getColorScheme().parameterText(nodeName)
- + cmd.getColorScheme().text("... @|bold,green Done!|@\n")
+ + cmd.getColorScheme().text("...\n@|bold,green Done|@\n")
);
assertThatStderrIsEmpty();
}
@@ -368,7 +363,7 @@ public class IgniteCliInterfaceTest extends AbstractCliTest {
assertOutputEqual(
"Stopping locally running node with consistent ID "
+ cmd.getColorScheme().parameterText(nodeName)
- + cmd.getColorScheme().text("... @|bold,red Failed|@\n")
+ + cmd.getColorScheme().text("...\n@|bold,red Failed|@\n")
);
assertThatStderrIsEmpty();
}
@@ -391,14 +386,15 @@ public class IgniteCliInterfaceTest extends AbstractCliTest {
assertThatExitCodeMeansSuccess(exitCode);
verify(nodeMgr).getRunningNodes(ignitePaths.logDir, ignitePaths.cliPidsDir());
- assertOutputEqual(cmd.getColorScheme().text("Number of running nodes: @|bold 2|@\n\n")
- + "+---------------+-----+----------+\n"
- + cmd.getColorScheme().text("| @|bold Consistent ID|@ | @|bold PID|@ | @|bold Log File|@ |\n")
- + "+---------------+-----+----------+\n"
- + "| new1 | 1 | logFile1 |\n"
- + "+---------------+-----+----------+\n"
- + "| new2 | 2 | logFile2 |\n"
- + "+---------------+-----+----------+\n"
+ assertOutputEqual(
+ "╔═══════════════╤═════╤══════════╗\n"
+ + "║ consistent id │ pid │ log file ║\n"
+ + "╠═══════════════╪═════╪══════════╣\n"
+ + "║ new1 │ 1 │ logFile1 ║\n"
+ + "╟───────────────┼─────┼──────────╢\n"
+ + "║ new2 │ 2 │ logFile2 ║\n"
+ + "╚═══════════════╧═════╧══════════╝\n\n"
+ + cmd.getColorScheme().text("Number of running nodes: @|bold 2|@\n")
);
assertThatStderrIsEmpty();
}
@@ -418,8 +414,8 @@ public class IgniteCliInterfaceTest extends AbstractCliTest {
assertThatExitCodeMeansSuccess(exitCode);
verify(nodeMgr).getRunningNodes(ignitePaths.logDir, ignitePaths.cliPidsDir());
- assertOutputEqual("Currently, there are no locally running nodes.\n\n"
- + "Use the " + cmd.getColorScheme().commandText("ignite node start") + " command to start a new node.\n"
+ assertOutputEqual("There are no locally running nodes\n"
+ + "use the " + cmd.getColorScheme().commandText("ignite node start") + " command to start a new node"
);
assertThatStderrIsEmpty();
}
@@ -508,7 +504,7 @@ public class IgniteCliInterfaceTest extends AbstractCliTest {
assertThatExitCodeMeansSuccess(exitCode);
- assertOutputEqual("Node configuration was updated successfully.");
+ assertOutputEqual("Node configuration was updated successfully");
assertThatStderrIsEmpty();
}
}
@@ -550,7 +546,7 @@ public class IgniteCliInterfaceTest extends AbstractCliTest {
assertThatExitCodeMeansSuccess(exitCode);
- assertOutputEqual("Cluster was initialized successfully.");
+ assertOutputEqual("Cluster was initialized successfully");
assertThatStderrIsEmpty();
}
@@ -563,7 +559,7 @@ public class IgniteCliInterfaceTest extends AbstractCliTest {
)
.respond(response()
.withStatusCode(INTERNAL_SERVER_ERROR_500.code())
- .withBody("Oops")
+ .withBody("{\"status\":500, \"detail\":\"Oops\"}")
);
int exitCode = cmd(ctx).execute(
@@ -579,7 +575,7 @@ public class IgniteCliInterfaceTest extends AbstractCliTest {
assertThatExitCodeIs(1, exitCode);
assertThatStdoutIsEmpty();
- assertErrOutputEqual("An error occurred [errorCode=500, response=Oops]");
+ assertErrOutputEqual("Oops");
}
@Test
@@ -619,7 +615,7 @@ public class IgniteCliInterfaceTest extends AbstractCliTest {
assertThatExitCodeMeansSuccess(exitCode);
- assertOutputEqual("Cluster was initialized successfully.");
+ assertOutputEqual("Cluster was initialized successfully");
assertThatStderrIsEmpty();
}
@@ -702,7 +698,7 @@ public class IgniteCliInterfaceTest extends AbstractCliTest {
assertThatExitCodeMeansSuccess(exitCode);
- assertOutputEqual("Cluster configuration was updated successfully.");
+ assertOutputEqual("Cluster configuration was updated successfully");
assertThatStderrIsEmpty();
}
}
diff --git a/modules/cli/src/test/java/org/apache/ignite/cli/deprecated/ui/ProgressBarTest.java b/modules/cli/src/test/java/org/apache/ignite/cli/deprecated/ui/ProgressBarTest.java
index d678d1c824..93355b8932 100644
--- a/modules/cli/src/test/java/org/apache/ignite/cli/deprecated/ui/ProgressBarTest.java
+++ b/modules/cli/src/test/java/org/apache/ignite/cli/deprecated/ui/ProgressBarTest.java
@@ -72,8 +72,8 @@ public class ProgressBarTest extends AbstractCliTest {
assertEquals(AUTO.string(
"\r|========================> | 33%"
+ "\r|================================================> | 66%"
- + "\r|==========================================================================|@|green,bold Done!|@"
- + "\r|==========================================================================|@|green,bold Done!|@"),
+ + "\r|==========================================================================|@|green,bold Done|@"
+ + "\r|==========================================================================|@|green,bold Done|@"),
outputStream.toString(UTF_8)
);
}
diff --git a/modules/configuration-api/src/main/java/org/apache/ignite/configuration/validation/ConfigurationValidationException.java b/modules/configuration-api/src/main/java/org/apache/ignite/configuration/validation/ConfigurationValidationException.java
index 097944e6d6..5547d8390c 100644
--- a/modules/configuration-api/src/main/java/org/apache/ignite/configuration/validation/ConfigurationValidationException.java
+++ b/modules/configuration-api/src/main/java/org/apache/ignite/configuration/validation/ConfigurationValidationException.java
@@ -18,6 +18,7 @@
package org.apache.ignite.configuration.validation;
import java.util.List;
+import java.util.stream.Collectors;
/**
* Configuration validation exception.
@@ -41,7 +42,7 @@ public class ConfigurationValidationException extends RuntimeException {
* @param issues List of issues occurred during validation.
*/
public ConfigurationValidationException(List<ValidationIssue> issues) {
- super(issues.toString());
+ super(createMessageFromIssues(issues));
this.issues = issues;
}
@@ -54,4 +55,9 @@ public class ConfigurationValidationException extends RuntimeException {
public List<ValidationIssue> getIssues() {
return issues;
}
+
+ private static String createMessageFromIssues(List<ValidationIssue> issues) {
+ return "Validation did not pass for keys: "
+ + issues.stream().map(issue -> "[" + issue.key() + ", " + issue.message() + "]").collect(Collectors.joining(", "));
+ }
}
diff --git a/modules/configuration/src/test/java/org/apache/ignite/internal/rest/configuration/ConfigurationControllerBaseTest.java b/modules/configuration/src/test/java/org/apache/ignite/internal/rest/configuration/ConfigurationControllerBaseTest.java
index 80d04b5818..be5359a10b 100644
--- a/modules/configuration/src/test/java/org/apache/ignite/internal/rest/configuration/ConfigurationControllerBaseTest.java
+++ b/modules/configuration/src/test/java/org/apache/ignite/internal/rest/configuration/ConfigurationControllerBaseTest.java
@@ -20,6 +20,7 @@ package org.apache.ignite.internal.rest.configuration;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.hasSize;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -35,8 +36,8 @@ import jakarta.inject.Inject;
import org.apache.ignite.internal.configuration.ConfigurationRegistry;
import org.apache.ignite.internal.configuration.rest.presentation.ConfigurationPresentation;
import org.apache.ignite.internal.configuration.rest.presentation.TestRootConfiguration;
+import org.apache.ignite.internal.rest.api.InvalidParam;
import org.apache.ignite.internal.rest.api.Problem;
-import org.apache.ignite.internal.rest.api.ValidationProblem;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -136,19 +137,18 @@ public abstract class ConfigurationControllerBaseTest {
assertEquals(HttpStatus.BAD_REQUEST, thrown.getResponse().status());
- var problem = getValidationProblem(thrown);
+ var problem = getProblem(thrown);
assertEquals(400, problem.status());
- assertThat(problem.detail(), containsString("ValidationIssue [key=root.foo, message=Error word]"));
- assertEquals("Error word", problem.invalidParams().stream().findFirst().get().reason()); // todo: check name and reason
+ assertThat(problem.detail(), containsString("Validation did not pass for keys: [root.foo, Error word]"));
+ assertThat(problem.invalidParams(), hasSize(1));
+
+ InvalidParam invalidParam = problem.invalidParams().stream().findFirst().get();
+ assertEquals("root.foo", invalidParam.name());
+ assertEquals("Error word", invalidParam.reason());
}
@NotNull
private Problem getProblem(HttpClientResponseException exception) {
return exception.getResponse().getBody(Problem.class).orElseThrow();
}
-
- @NotNull
- private ValidationProblem getValidationProblem(HttpClientResponseException exception) {
- return exception.getResponse().getBody(ValidationProblem.class).orElseThrow();
- }
}
diff --git a/modules/core/src/main/java/org/apache/ignite/lang/ErrorGroup.java b/modules/core/src/main/java/org/apache/ignite/lang/ErrorGroup.java
index c9a97da9fb..3f18678b13 100755
--- a/modules/core/src/main/java/org/apache/ignite/lang/ErrorGroup.java
+++ b/modules/core/src/main/java/org/apache/ignite/lang/ErrorGroup.java
@@ -229,14 +229,29 @@ public class ErrorGroup {
String c = (cause != null && cause.getMessage() != null) ? cause.getMessage() : null;
if (c != null) {
- Matcher m = EXCEPTION_MESSAGE_PATTERN.matcher(c);
-
- c = (m.matches()) ? m.group(8) : c;
+ c = extractCauseMessage(c);
}
return errorMessage(traceId, groupName, code, c);
}
+ /**
+ * Returns a message extracted from the given {@code errorMessage} if this {@code errorMessage} matches
+ * {@link #EXCEPTION_MESSAGE_PATTERN}. If {@code errorMessage} does not match the pattern or {@code null} then returns the original
+ * {@code errorMessage}.
+ *
+ * @param errorMessage Message that is returned by {@link Throwable#getMessage()}
+ * @return Extracted message.
+ */
+ public static String extractCauseMessage(String errorMessage) {
+ if (errorMessage == null) {
+ return null;
+ }
+
+ Matcher m = EXCEPTION_MESSAGE_PATTERN.matcher(errorMessage);
+ return (m.matches()) ? m.group(8) : errorMessage;
+ }
+
/** {@inheritDoc} */
@Override
public String toString() {
diff --git a/modules/core/src/test/java/org/apache/ignite/lang/ErrorGroupTest.java b/modules/core/src/test/java/org/apache/ignite/lang/ErrorGroupTest.java
new file mode 100644
index 0000000000..65e95429f5
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/lang/ErrorGroupTest.java
@@ -0,0 +1,49 @@
+/*
+ * 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.lang;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+
+import org.junit.jupiter.api.Test;
+
+class ErrorGroupTest {
+ @Test
+ void extractsCauseMessageFromIgniteExceptionMessage() {
+ // Given
+ String igniteExceptionMessage = "IGN-CMN-65535 TraceId:24103638-d079-4a19-a8f6-ca9c23662908 I'm the reason";
+
+ // When
+ String extractedMessage = ErrorGroup.extractCauseMessage(igniteExceptionMessage);
+
+ // Then
+ assertThat(extractedMessage, equalTo("I'm the reason"));
+ }
+
+ @Test
+ void extractsEmptyCauseMessageFromIgniteExceptionMessage() {
+ // Given message without the reason of the error
+ String igniteExceptionMessage = "IGN-CMN-65535 TraceId:24103638-d079-4a19-a8f6-ca9c23662908";
+
+ // When
+ String extractedMessage = ErrorGroup.extractCauseMessage(igniteExceptionMessage);
+
+ // Then
+ assertThat(extractedMessage, equalTo(""));
+ }
+}
diff --git a/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/api/Problem.java b/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/api/Problem.java
index d0e814e7b7..e7c9bec2c2 100644
--- a/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/api/Problem.java
+++ b/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/api/Problem.java
@@ -20,6 +20,7 @@ package org.apache.ignite.internal.rest.api;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.Collection;
import java.util.Objects;
import java.util.UUID;
import org.apache.ignite.internal.rest.constants.HttpCode;
@@ -50,6 +51,9 @@ public class Problem {
/** Unique identifier that will help to trace the error in the log (optional). */
private final UUID traceId;
+ /** List of parameter that did not pass the validation (optional). */
+ private final Collection<InvalidParam> invalidParams;
+
/** Constructor. */
@JsonCreator
protected Problem(
@@ -59,7 +63,8 @@ public class Problem {
@JsonProperty("type") String type,
@JsonProperty("detail") String detail,
@JsonProperty("node") String node,
- @JsonProperty("traceId") UUID traceId) {
+ @JsonProperty("traceId") UUID traceId,
+ @JsonProperty("invalidParams") Collection<InvalidParam> invalidParams) {
this.title = title;
this.status = status;
this.code = code;
@@ -67,6 +72,7 @@ public class Problem {
this.detail = detail;
this.node = node;
this.traceId = traceId;
+ this.invalidParams = invalidParams;
}
/** Returns {@link ProblemBuilder}. */
@@ -118,6 +124,11 @@ public class Problem {
return traceId;
}
+ @JsonGetter("invalidParams")
+ public Collection<InvalidParam> invalidParams() {
+ return invalidParams;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) {
@@ -146,6 +157,7 @@ public class Problem {
+ ", type='" + type + '\''
+ ", detail='" + detail + '\''
+ ", node='" + node + '\''
+ + ", invalidParams='" + invalidParams + '\''
+ ", traceId=" + traceId
+ '}';
}
@@ -166,6 +178,8 @@ public class Problem {
protected UUID traceId;
+ protected Collection<InvalidParam> invalidParams;
+
public B title(String title) {
this.title = title;
return (B) this;
@@ -201,9 +215,14 @@ public class Problem {
return (B) this;
}
+ public B invalidParams(Collection<InvalidParam> invalidParams) {
+ this.invalidParams = invalidParams;
+ return (B) this;
+ }
+
@Override
public T build() {
- return (T) new Problem(title, status, code, type, detail, node, traceId);
+ return (T) new Problem(title, status, code, type, detail, node, traceId, invalidParams);
}
}
}
diff --git a/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/api/ValidationProblem.java b/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/api/ValidationProblem.java
deleted file mode 100644
index c6964671c1..0000000000
--- a/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/api/ValidationProblem.java
+++ /dev/null
@@ -1,111 +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.
- */
-
-package org.apache.ignite.internal.rest.api;
-
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonGetter;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import java.util.Collection;
-import java.util.Objects;
-import java.util.UUID;
-import org.apache.ignite.internal.rest.constants.HttpCode;
-
-/**
- * Validation problem that adds one more property (invalidParams) to the standard problem.
- */
-public class ValidationProblem extends Problem {
- /** List of parameter that did not pass the validation (optional). */
- private final Collection<InvalidParam> invalidParams;
-
- /** Constructor. */
- @JsonCreator
- public ValidationProblem(
- @JsonProperty("title") String title,
- @JsonProperty("status") int status,
- @JsonProperty("code") String code,
- @JsonProperty("type") String type,
- @JsonProperty("detail") String detail,
- @JsonProperty("node") String node,
- @JsonProperty("traceId") UUID traceId,
- @JsonProperty("invalidParams") Collection<InvalidParam> invalidParams) {
-
- super(title, status, code, type, detail, node, traceId);
- this.invalidParams = invalidParams;
- }
-
- @JsonGetter("invalidParams")
- public Collection<InvalidParam> invalidParams() {
- return invalidParams;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- if (!super.equals(o)) {
- return false;
- }
- ValidationProblem that = (ValidationProblem) o;
- return Objects.equals(invalidParams, that.invalidParams);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(super.hashCode(), invalidParams);
- }
-
- @Override
- public String toString() {
- return "ValidationProblem{"
- + "invalidParams=" + invalidParams
- + "} " + super.toString();
- }
-
- /** Returns {@link ValidationProblemBuilder}. */
- public static ValidationProblemBuilder builder() {
- return new ValidationProblemBuilder();
- }
-
- /** Returns {@link ValidationProblemBuilder} with http status and title. */
- public static ValidationProblemBuilder fromHttpCode(HttpCode httpCode) {
- ValidationProblemBuilder builder = new ValidationProblemBuilder();
- builder.status(httpCode.code());
- builder.title(httpCode.message());
-
- return builder;
- }
-
- /** Builder for {@link ValidationProblem}. */
- public static class ValidationProblemBuilder extends ProblemBuilder<ValidationProblem, ValidationProblemBuilder> {
- private Collection<InvalidParam> invalidParams;
-
- public ValidationProblemBuilder invalidParams(Collection<InvalidParam> invalidParams) {
- this.invalidParams = invalidParams;
- return this;
- }
-
- @Override
- public ValidationProblem build() {
- return new ValidationProblem(title, status, code, type, detail, node, traceId, invalidParams);
- }
- }
-}
diff --git a/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/exception/handler/IgniteExceptionHandler.java b/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/exception/handler/IgniteExceptionHandler.java
index 342e79a6eb..9f14a3297b 100644
--- a/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/exception/handler/IgniteExceptionHandler.java
+++ b/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/exception/handler/IgniteExceptionHandler.java
@@ -27,10 +27,11 @@ import java.util.stream.Collectors;
import org.apache.ignite.configuration.validation.ConfigurationValidationException;
import org.apache.ignite.internal.rest.api.InvalidParam;
import org.apache.ignite.internal.rest.api.Problem;
-import org.apache.ignite.internal.rest.api.ValidationProblem;
import org.apache.ignite.internal.rest.constants.HttpCode;
import org.apache.ignite.internal.rest.problem.HttpProblemResponse;
+import org.apache.ignite.lang.ErrorGroup;
import org.apache.ignite.lang.IgniteException;
+import org.jetbrains.annotations.Nullable;
/**
* Handles {@link IgniteException} and represents it as an application/problem+json response.
@@ -40,10 +41,12 @@ import org.apache.ignite.lang.IgniteException;
public class IgniteExceptionHandler implements ExceptionHandler<IgniteException, HttpResponse<? extends Problem>> {
@Override
public HttpResponse<? extends Problem> handle(HttpRequest request, IgniteException exception) {
+ String detail = extractDetailMessageOrNull(exception);
+
if (exception.getCause() instanceof IllegalArgumentException) {
return HttpProblemResponse.from(
Problem.fromHttpCode(HttpCode.BAD_REQUEST)
- .detail(exception.getMessage())
+ .detail(detail)
.traceId(exception.traceId())
.code(exception.codeAsString())
);
@@ -51,8 +54,8 @@ public class IgniteExceptionHandler implements ExceptionHandler<IgniteException,
if (exception.getCause() instanceof ConfigurationValidationException) {
return HttpProblemResponse.from(
- ValidationProblem.fromHttpCode(HttpCode.BAD_REQUEST)
- .detail(exception.getMessage())
+ Problem.fromHttpCode(HttpCode.BAD_REQUEST)
+ .detail(detail)
.invalidParams(mapValidationIssuesToRestFormat((ConfigurationValidationException) exception.getCause()))
.traceId(exception.traceId())
.code(exception.codeAsString())
@@ -61,12 +64,21 @@ public class IgniteExceptionHandler implements ExceptionHandler<IgniteException,
return HttpProblemResponse.from(
Problem.fromHttpCode(HttpCode.INTERNAL_ERROR)
- .detail(exception.getMessage())
+ .detail(detail)
.traceId(exception.traceId())
.code(exception.codeAsString())
);
}
+ @Nullable
+ private static String extractDetailMessageOrNull(IgniteException exception) {
+ String detail = ErrorGroup.extractCauseMessage(exception.getMessage());
+ if (detail != null && detail.isBlank()) {
+ detail = null;
+ }
+ return detail;
+ }
+
private List<InvalidParam> mapValidationIssuesToRestFormat(ConfigurationValidationException exception) {
return exception.getIssues()
.stream()
diff --git a/modules/rest-api/src/test/java/org/apache/ignite/internal/rest/exception/handler/IgniteExceptionHandlerTest.java b/modules/rest-api/src/test/java/org/apache/ignite/internal/rest/exception/handler/IgniteExceptionHandlerTest.java
index c666799f56..5344997de9 100644
--- a/modules/rest-api/src/test/java/org/apache/ignite/internal/rest/exception/handler/IgniteExceptionHandlerTest.java
+++ b/modules/rest-api/src/test/java/org/apache/ignite/internal/rest/exception/handler/IgniteExceptionHandlerTest.java
@@ -17,7 +17,6 @@
package org.apache.ignite.internal.rest.exception.handler;
-import static org.apache.ignite.lang.ErrorGroup.errorMessage;
import static org.apache.ignite.lang.ErrorGroup.extractErrorCode;
import static org.apache.ignite.lang.ErrorGroups.Common.COMMON_ERR_GROUP;
import static org.apache.ignite.lang.ErrorGroups.Common.UNKNOWN_ERR;
@@ -34,7 +33,6 @@ import org.apache.ignite.configuration.validation.ValidationIssue;
import org.apache.ignite.internal.rest.api.InvalidParam;
import org.apache.ignite.internal.rest.api.Problem;
import org.apache.ignite.internal.rest.api.Problem.ProblemBuilder;
-import org.apache.ignite.internal.rest.api.ValidationProblem;
import org.apache.ignite.lang.ErrorGroup;
import org.apache.ignite.lang.IgniteException;
import org.junit.jupiter.api.BeforeEach;
@@ -49,7 +47,6 @@ class IgniteExceptionHandlerTest {
static Stream<Arguments> igniteExceptions() {
UUID traceId = UUID.randomUUID();
- String errorMessage = errorMessage(traceId, UNKNOWN_ERR, null);
String humanReadableCode = ErrorGroup.ERR_PREFIX + COMMON_ERR_GROUP.name() + '-' + extractErrorCode(UNKNOWN_ERR);
var invalidParams = List.of(
@@ -69,7 +66,7 @@ class IgniteExceptionHandlerTest {
.status(500)
.title("Internal Server Error")
.code(humanReadableCode)
- .detail(errorMessage + " Ooops")
+ .detail("Ooops")
.traceId(traceId)),
Arguments.of(
// given
@@ -79,7 +76,6 @@ class IgniteExceptionHandlerTest {
.status(500)
.title("Internal Server Error")
.code(humanReadableCode)
- .detail(errorMessage)
.traceId(traceId)),
Arguments.of(
// given
@@ -90,7 +86,7 @@ class IgniteExceptionHandlerTest {
.title("Bad Request")
.code(humanReadableCode)
.traceId(traceId)
- .detail(errorMessage + " Illegal value")),
+ .detail("Illegal value")),
Arguments.of(
// given
new IgniteException(
@@ -98,10 +94,10 @@ class IgniteExceptionHandlerTest {
UNKNOWN_ERR,
new ConfigurationValidationException(validationIssues)),
// expected
- ValidationProblem.builder()
+ Problem.builder()
.status(400)
.title("Bad Request")
- .detail(errorMessage + ' ' + validationIssues)
+ .detail("Validation did not pass for keys: [key1, Some issue1], [key2, Some issue2]")
.code(humanReadableCode)
.traceId(traceId)
.invalidParams(invalidParams))
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/rest/ItInitializedClusterRestTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/rest/ItInitializedClusterRestTest.java
index be2635546e..9f50330ebe 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/rest/ItInitializedClusterRestTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/rest/ItInitializedClusterRestTest.java
@@ -139,6 +139,21 @@ public class ItInitializedClusterRestTest extends AbstractRestTestBase {
assertThat(config.getInt("rocksDb.defaultRegion.writeBufferSize"), is(1024));
}
+ @Test
+ @DisplayName("Cluster configuration can not be updated if provided config did not pass the validation")
+ void clusterConfigurationUpdateValidation() throws IOException, InterruptedException {
+ // When PATCH /management/v1/configuration/cluster invalid with invalid value
+ HttpResponse<String> patchRequest = client.send(
+ patch("/management/v1/configuration/cluster", "rocksDb.defaultRegion.cache=invalid"),
+ BodyHandlers.ofString()
+ );
+
+ // Then
+ assertThat(patchRequest.statusCode(), is(400));
+ // And invalidParams key is in response body
+ assertThat(patchRequest.body(), hasJsonPath("$.invalidParams"));
+ }
+
@Test
@DisplayName("Cluster configuration by path is available when the cluster is initialized")
void clusterConfigurationByPath() throws IOException, InterruptedException {