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<>()) + ); } /**