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 bb4d83f584 IGNITE-18097 CLI should check if it's already connected 
before trying to connect. Fixes #1404
bb4d83f584 is described below

commit bb4d83f5849808c42e5c2102b77e3b42a935cd5c
Author: Vadim Pakhnushev <8614891+valep...@users.noreply.github.com>
AuthorDate: Fri Dec 23 15:05:18 2022 +0200

    IGNITE-18097 CLI should check if it's already connected before trying to 
connect. Fixes #1404
    
    Signed-off-by: Slava Koptilin <slava.kopti...@gmail.com>
---
 ...liCommandTestNotInitializedIntegrationBase.java |  6 +++-
 .../cli/commands/connect/ItConnectCommandTest.java | 28 +++++++++++++++
 .../commands/questions/ItConnectToClusterTest.java | 39 ++++++++++++++++++++
 .../internal/cli/call/connect/ConnectCall.java     |  9 +++--
 .../cli/commands/connect/ConnectReplCommand.java   | 17 +++++----
 .../questions/ConnectToClusterQuestion.java        | 42 ++++++++++++++--------
 .../internal/cli/core/flow/builder/Flows.java      |  9 +++--
 7 files changed, 121 insertions(+), 29 deletions(-)

diff --git 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/CliCommandTestNotInitializedIntegrationBase.java
 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/CliCommandTestNotInitializedIntegrationBase.java
index ba3ce092a4..c185b3866e 100644
--- 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/CliCommandTestNotInitializedIntegrationBase.java
+++ 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/CliCommandTestNotInitializedIntegrationBase.java
@@ -81,11 +81,15 @@ public class CliCommandTestNotInitializedIntegrationBase 
extends IntegrationTest
         cmd = new CommandLine(getCommandClass(), new MicronautFactory(context))
                 .registerConverter(NodeNameOrUrl.class, new 
NodeNameOrUrlConverter(nodeNameRegistry));
         cmd.setDefaultValueProvider(configDefaultValueProvider);
+        resetOutput();
+        CommandLineContextProvider.setCmd(cmd);
+    }
+
+    protected void resetOutput() {
         sout = new StringWriter();
         serr = new StringWriter();
         cmd.setOut(new PrintWriter(sout));
         cmd.setErr(new PrintWriter(serr));
-        CommandLineContextProvider.setCmd(cmd);
     }
 
     @BeforeAll
diff --git 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/connect/ItConnectCommandTest.java
 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/connect/ItConnectCommandTest.java
index 75267cf256..123d45319b 100644
--- 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/connect/ItConnectCommandTest.java
+++ 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/connect/ItConnectCommandTest.java
@@ -107,6 +107,34 @@ class ItConnectCommandTest extends 
CliCommandTestInitializedIntegrationBase {
         assertThat(promptAfter).isEqualTo("[disconnected]> ");
     }
 
+    @Test
+    @DisplayName("Should state that already connected")
+    void connectTwice() {
+        // Given connected to cluster
+        execute("connect");
+        // And output is
+        assertAll(
+                this::assertErrOutputIsEmpty,
+                () -> assertOutputIs("Connected to http://localhost:10300"; + 
System.lineSeparator())
+        );
+        // And prompt is
+        String promptBefore = Ansi.OFF.string(promptProvider.getPrompt());
+        assertThat(promptBefore).isEqualTo("[" + nodeName() + "]> ");
+
+        // When connect again
+        resetOutput();
+        execute("connect");
+
+        // Then
+        assertAll(
+                this::assertErrOutputIsEmpty,
+                () -> assertOutputIs("You are already connected to 
http://localhost:10300"; + System.lineSeparator())
+        );
+        // And prompt is still connected
+        String promptAfter = Ansi.OFF.string(promptProvider.getPrompt());
+        assertThat(promptAfter).isEqualTo("[" + nodeName() + "]> ");
+    }
+
     private String nodeName() {
         return CLUSTER_NODES.get(0).name();
     }
diff --git 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/questions/ItConnectToClusterTest.java
 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/questions/ItConnectToClusterTest.java
index 3df96fe8d6..1adfaae91c 100644
--- 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/questions/ItConnectToClusterTest.java
+++ 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/questions/ItConnectToClusterTest.java
@@ -27,6 +27,7 @@ import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import 
org.apache.ignite.internal.cli.commands.CliCommandTestInitializedIntegrationBase;
+import org.apache.ignite.internal.cli.commands.TopLevelCliReplCommand;
 import 
org.apache.ignite.internal.cli.commands.cliconfig.TestConfigManagerHelper;
 import org.apache.ignite.internal.cli.config.ConfigConstants;
 import org.apache.ignite.internal.cli.config.TestStateConfigHelper;
@@ -58,6 +59,11 @@ class ItConnectToClusterTest extends 
CliCommandTestInitializedIntegrationBase {
     private Terminal terminal;
     private Path input;
 
+    @Override
+    protected Class<?> getCommandClass() {
+        return TopLevelCliReplCommand.class;
+    }
+
     @Override
     @BeforeEach
     public void setUp(TestInfo testInfo) throws Exception {
@@ -132,6 +138,39 @@ class ItConnectToClusterTest extends 
CliCommandTestInitializedIntegrationBase {
                 .isEqualTo("http://localhost:10300";);
     }
 
+    @Test
+    @DisplayName("Should ask to connect to different URL")
+    void connectToAnotherUrl() throws IOException {
+        // Given prompt before connect
+        String promptBefore = Ansi.OFF.string(promptProvider.getPrompt());
+        assertThat(promptBefore).isEqualTo("[disconnected]> ");
+
+        // And connected
+        execute("connect");
+
+        // And output is
+        assertAll(
+                this::assertErrOutputIsEmpty,
+                () -> assertOutputIs("Connected to http://localhost:10300"; + 
System.lineSeparator())
+        );
+
+        // And answer is "y"
+        bindAnswers("y");
+
+        // When connect to different URL
+        resetOutput();
+        execute("connect", "http://localhost:10301";);
+
+        // Then
+        assertAll(
+                this::assertErrOutputIsEmpty,
+                () -> assertOutputIs("Connected to http://localhost:10301"; + 
System.lineSeparator())
+        );
+        // And prompt is changed to another node
+        String promptAfter = Ansi.OFF.string(promptProvider.getPrompt());
+        assertThat(promptAfter).isEqualTo("[" + CLUSTER_NODES.get(1).name() + 
"]> ");
+    }
+
     private String nodeName() {
         return CLUSTER_NODES.get(0).name();
     }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectCall.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectCall.java
index a29401da1e..506182f81d 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectCall.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectCall.java
@@ -21,6 +21,7 @@ import com.google.gson.Gson;
 import jakarta.inject.Singleton;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.util.Objects;
 import org.apache.ignite.internal.cli.NodeNameRegistry;
 import org.apache.ignite.internal.cli.config.ConfigConstants;
 import org.apache.ignite.internal.cli.config.StateConfigProvider;
@@ -60,8 +61,12 @@ public class ConnectCall implements Call<ConnectCallInput, 
String> {
 
     @Override
     public CallOutput<String> execute(ConnectCallInput input) {
+        String nodeUrl = input.getNodeUrl();
+        if (session.isConnectedToNode() && Objects.equals(session.nodeUrl(), 
nodeUrl)) {
+            MessageUiComponent message = MessageUiComponent.fromMessage("You 
are already connected to %s", UiElements.url(nodeUrl));
+            return DefaultCallOutput.success(message.render());
+        }
         try {
-            String nodeUrl = input.getNodeUrl();
             String configuration = fetchNodeConfiguration(nodeUrl);
             session.setNodeName(fetchNodeName(nodeUrl));
             session.setNodeUrl(nodeUrl);
@@ -73,7 +78,7 @@ public class ConnectCall implements Call<ConnectCallInput, 
String> {
 
         } catch (ApiException | IllegalArgumentException e) {
             session.setConnectedToNode(false);
-            return DefaultCallOutput.failure(new IgniteCliApiException(e, 
input.getNodeUrl()));
+            return DefaultCallOutput.failure(new IgniteCliApiException(e, 
nodeUrl));
         }
     }
 
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/connect/ConnectReplCommand.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/connect/ConnectReplCommand.java
index db12b83720..55cd5c962e 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/connect/ConnectReplCommand.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/connect/ConnectReplCommand.java
@@ -25,7 +25,8 @@ import 
org.apache.ignite.internal.cli.call.connect.ConnectCall;
 import org.apache.ignite.internal.cli.call.connect.ConnectCallInput;
 import org.apache.ignite.internal.cli.commands.BaseCommand;
 import org.apache.ignite.internal.cli.commands.node.NodeNameOrUrl;
-import org.apache.ignite.internal.cli.core.call.CallExecutionPipeline;
+import 
org.apache.ignite.internal.cli.commands.questions.ConnectToClusterQuestion;
+import org.apache.ignite.internal.cli.core.flow.builder.Flows;
 import picocli.CommandLine.Command;
 import picocli.CommandLine.Parameters;
 
@@ -42,15 +43,17 @@ public class ConnectReplCommand extends BaseCommand 
implements Runnable {
     @Inject
     private ConnectCall connectCall;
 
+    @Inject
+    private ConnectToClusterQuestion question;
+
     /** {@inheritDoc} */
     @Override
     public void run() {
-        CallExecutionPipeline.builder(connectCall)
-                .inputProvider(() -> new 
ConnectCallInput(nodeNameOrUrl.stringUrl()))
-                .output(spec.commandLine().getOut())
-                .errOutput(spec.commandLine().getErr())
+        question.askQuestionIfConnected(nodeNameOrUrl.stringUrl())
+                .map(ConnectCallInput::new)
+                .then(Flows.fromCall(connectCall))
                 .verbose(verbose)
-                .build()
-                .runPipeline();
+                .print()
+                .start();
     }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/questions/ConnectToClusterQuestion.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/questions/ConnectToClusterQuestion.java
index b5743fa014..40304184e1 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/questions/ConnectToClusterQuestion.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/questions/ConnectToClusterQuestion.java
@@ -51,12 +51,17 @@ public class ConnectToClusterQuestion {
 
 
     /**
-     * Execute call with question about connect to cluster in case when 
disconnected state.
+     * Asks whether the user wants to connect to the default node when user 
hasn't passed a URL explicitly and we're not connected.
      *
      * @param clusterUrl cluster url.
-     * @return {@link FlowBuilder} instance with question in case when cluster 
url.
+     * @return {@link FlowBuilder} instance which returns a URL.
      */
     public FlowBuilder<Void, String> askQuestionIfNotConnected(String 
clusterUrl) {
+        String url = clusterUrlOrSessionNode(clusterUrl);
+        if (url != null) {
+            return Flows.from(url);
+        }
+
         String defaultUrl = 
configManagerProvider.get().getCurrentProperty(ConfigConstants.CLUSTER_URL);
 
         QuestionUiComponent questionUiComponent = 
QuestionUiComponent.fromQuestion(
@@ -64,24 +69,33 @@ public class ConnectToClusterQuestion {
                 UiElements.url(defaultUrl), UiElements.yesNo()
         );
 
-        return Flows.from(clusterUrlOrSessionNode(clusterUrl))
-                .flatMap(v -> {
-                    if (Objects.isNull(v)) {
-                        return Flows.<String, 
ConnectCallInput>acceptQuestion(questionUiComponent,
-                                        () -> new ConnectCallInput(defaultUrl))
-                                .then(Flows.fromCall(connectCall))
-                                .print()
-                                .map(ignored -> 
clusterUrlOrSessionNode(clusterUrl));
-                    } else {
-                        return Flows.identity();
-                    }
-                });
+        return Flows.<Void, 
ConnectCallInput>acceptQuestion(questionUiComponent, () -> new 
ConnectCallInput(defaultUrl))
+                .then(Flows.fromCall(connectCall))
+                .print()
+                .map(ignored -> session.nodeUrl());
     }
 
     private String clusterUrlOrSessionNode(String clusterUrl) {
         return clusterUrl != null ? clusterUrl : session.nodeUrl();
     }
 
+    /**
+     * Ask if the user really wants to connect if we are already connected and 
the URL is different.
+     *
+     * @param clusterUrl cluster url.
+     * @return {@link FlowBuilder} instance with question in case when cluster 
url.
+     */
+    public FlowBuilder<Void, String> askQuestionIfConnected(String clusterUrl) 
{
+        if (session.isConnectedToNode() && !Objects.equals(session.nodeUrl(), 
clusterUrl)) {
+            QuestionUiComponent question = QuestionUiComponent.fromQuestion(
+                    "You are already connected to the %s, do you want to 
connect to the %s? %s ",
+                    UiElements.url(session.nodeUrl()), 
UiElements.url(clusterUrl), UiElements.yesNo()
+            );
+            return Flows.acceptQuestion(question, () -> clusterUrl);
+        }
+        return Flows.from(clusterUrl);
+    }
+
     /**
      * Ask for connect to the cluster and suggest to save the last connected 
URL as default.
      */
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/flow/builder/Flows.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/flow/builder/Flows.java
index 2f98bfe9c2..cf94f792c0 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/flow/builder/Flows.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/flow/builder/Flows.java
@@ -133,11 +133,10 @@ public final class Flows {
      * @return new {@link FlowBuilder}.
      */
     public static <I, O> FlowBuilder<I, O> acceptQuestion(String question, 
Supplier<O> onAccept) {
-        return Flows.<I, O>question(question,
-                        List.of(new AcceptedQuestionAnswer<>((a, i) -> null),
-                                new InterruptQuestionAnswer<>())
-                )
-                .then(mono(unused -> onAccept.get()));
+        return question(question,
+                List.of(new AcceptedQuestionAnswer<>((a, i) -> onAccept.get()),
+                        new InterruptQuestionAnswer<>())
+        );
     }
 
     /**

Reply via email to