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

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


The following commit(s) were added to refs/heads/main by this push:
     new e19bfb4888 IGNITE-23793 Add exception handler for config option 
mismatching (#4795)
e19bfb4888 is described below

commit e19bfb4888ea91c52cf69f440acc8043e3359335
Author: Mikhail <[email protected]>
AuthorDate: Mon Dec 2 17:46:50 2024 +0300

    IGNITE-23793 Add exception handler for config option mismatching (#4795)
---
 .../cli/call/cluster/ClusterInitCallInput.java     | 15 +++++++
 .../commands/cluster/init/ClusterInitOptions.java  | 32 +++++++++++++-
 .../cluster/init/ClusterInitReplCommand.java       | 28 ++++++++++++-
 ...seException.java => ConfigAsPathException.java} | 18 ++++++--
 .../cluster/init/ConfigAsPathExceptionHandler.java | 49 ++++++++++++++++++++++
 .../cluster/init/ConfigFileParseException.java     |  5 +++
 .../handler/DefaultExceptionHandlers.java          |  2 +
 .../internal/cli/core/flow/builder/Flows.java      | 17 ++++++++
 8 files changed, 160 insertions(+), 6 deletions(-)

diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/ClusterInitCallInput.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/ClusterInitCallInput.java
index 24444e40c0..c4c882c292 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/ClusterInitCallInput.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/ClusterInitCallInput.java
@@ -125,6 +125,21 @@ public class ClusterInitCallInput implements CallInput {
             return this;
         }
 
+        public ClusterInitCallInputBuilder metaStorageNodes(List<String> 
metaStorageNodes) {
+            this.metaStorageNodes = metaStorageNodes;
+            return this;
+        }
+
+        public ClusterInitCallInputBuilder cmgNodes(List<String> cmgNodes) {
+            this.cmgNodes = cmgNodes;
+            return this;
+        }
+
+        public ClusterInitCallInputBuilder clusterName(String clusterName) {
+            this.clusterName = clusterName;
+            return this;
+        }
+
         /**
          * Extract cluster initialization options.
          *
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ClusterInitOptions.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ClusterInitOptions.java
index e69fe1e8a7..f0f041bf64 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ClusterInitOptions.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ClusterInitOptions.java
@@ -38,6 +38,9 @@ import com.typesafe.config.ConfigRenderOptions;
 import java.io.File;
 import java.io.IOException;
 import java.nio.file.Files;
+import java.nio.file.InvalidPathException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -138,7 +141,11 @@ public class ClusterInitOptions {
         if (clusterConfigOptions == null) {
             return null;
         } else if (clusterConfigOptions.config != null) {
-            return clusterConfigOptions.config;
+            String config = clusterConfigOptions.config;
+            if (checkConfigAsPath(config)) {
+                throw new ConfigAsPathException(config);
+            }
+            return config;
         } else if (clusterConfigOptions.files != null) {
             Config config = ConfigFactory.empty();
 
@@ -160,6 +167,29 @@ public class ClusterInitOptions {
         }
     }
 
+    String readConfigAsPath() {
+        if (clusterConfigOptions == null || clusterConfigOptions.config == 
null) {
+            throw new ConfigFileParseException("Couldn't parse cluster 
configuration file.");
+        }
+        Path file = Paths.get(clusterConfigOptions.config);
+        try {
+            String content = Files.readString(file);
+            return 
ConfigFactory.parseString(content).root().render(ConfigRenderOptions.concise().setFormatted(true).setJson(true));
+        } catch (IOException e) {
+            throw new IgniteCliException("Couldn't read cluster configuration 
file " + file, e);
+        } catch (ConfigException e) {
+            throw new ConfigFileParseException("Couldn't parse cluster 
configuration file " + file, e);
+        }
+    }
+
+    private static boolean checkConfigAsPath(String config) {
+        try {
+            Paths.get(config);
+            return true;
+        } catch (InvalidPathException e) {
+            return false;
+        }
+    }
 
     private static class DuplicatesChecker {
         private final String optionToCheck;
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ClusterInitReplCommand.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ClusterInitReplCommand.java
index b59616ddd6..0cf94f8eff 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ClusterInitReplCommand.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ClusterInitReplCommand.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.internal.cli.commands.cluster.init;
 
+import static 
org.apache.ignite.internal.cli.core.style.component.QuestionUiComponent.fromYesNoQuestion;
 import static picocli.CommandLine.Command;
 
 import jakarta.inject.Inject;
@@ -25,7 +26,9 @@ import 
org.apache.ignite.internal.cli.call.cluster.ClusterInitCallInput;
 import org.apache.ignite.internal.cli.commands.BaseCommand;
 import org.apache.ignite.internal.cli.commands.cluster.ClusterUrlMixin;
 import 
org.apache.ignite.internal.cli.commands.questions.ConnectToClusterQuestion;
+import org.apache.ignite.internal.cli.core.flow.builder.FlowBuilder;
 import org.apache.ignite.internal.cli.core.flow.builder.Flows;
+import org.apache.ignite.internal.cli.core.style.component.QuestionUiComponent;
 import picocli.CommandLine.Mixin;
 
 /**
@@ -50,12 +53,35 @@ public class ClusterInitReplCommand extends BaseCommand 
implements Runnable {
     @Override
     public void run() {
         runFlow(question.askQuestionIfNotConnected(clusterUrl.getClusterUrl())
-                .map(this::buildCallInput)
+                .then(askQuestionIfConfigIsPath().build())
                 .then(Flows.fromCall(call))
                 .print()
         );
     }
 
+
+    private FlowBuilder<String, ClusterInitCallInput> 
askQuestionIfConfigIsPath() {
+        try {
+            clusterInitOptions.clusterConfiguration();
+            return Flows.from(this::buildCallInput);
+        } catch (ConfigAsPathException e) {
+            QuestionUiComponent questionUiComponent = fromYesNoQuestion(
+                    "It seems that you passed the path to the config file to 
the config content option. "
+                            + "Do you want this file to be read as a config?"
+            );
+
+            return Flows.acceptQuestion(questionUiComponent,
+                    clusterUrl -> ClusterInitCallInput.builder()
+                            
.clusterConfiguration(clusterInitOptions.readConfigAsPath())
+                            .cmgNodes(clusterInitOptions.cmgNodes())
+                            
.metaStorageNodes(clusterInitOptions.metaStorageNodes())
+                            .clusterName(clusterInitOptions.clusterName())
+                            .clusterUrl(clusterUrl)
+                            .build()
+            );
+        }
+    }
+
     private ClusterInitCallInput buildCallInput(String clusterUrl) {
         return ClusterInitCallInput.builder()
                 .clusterUrl(clusterUrl)
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ConfigFileParseException.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ConfigAsPathException.java
similarity index 69%
copy from 
modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ConfigFileParseException.java
copy to 
modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ConfigAsPathException.java
index c551bbb7b6..1eda6e210f 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ConfigFileParseException.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ConfigAsPathException.java
@@ -17,9 +17,19 @@
 
 package org.apache.ignite.internal.cli.commands.cluster.init;
 
-/** Exception thrown when config file parse failed. */
-public class ConfigFileParseException extends RuntimeException {
-    public ConfigFileParseException(String message, Throwable cause) {
-        super(message, cause);
+/**
+ * Exception throws when config file path passed to config content option.
+ */
+public class ConfigAsPathException extends RuntimeException {
+    private static final long serialVersionUID = -7683264630128999379L;
+
+    private final String path;
+
+    public ConfigAsPathException(String path) {
+        this.path = path;
+    }
+
+    public String path() {
+        return path;
     }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ConfigAsPathExceptionHandler.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ConfigAsPathExceptionHandler.java
new file mode 100644
index 0000000000..adfb4bd0b5
--- /dev/null
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ConfigAsPathExceptionHandler.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.internal.cli.commands.cluster.init;
+
+import static 
org.apache.ignite.internal.cli.commands.Options.Constants.CLUSTER_CONFIG_FILE_OPTION;
+
+import org.apache.ignite.internal.cli.core.exception.ExceptionHandler;
+import org.apache.ignite.internal.cli.core.exception.ExceptionWriter;
+import org.apache.ignite.internal.cli.core.style.component.ErrorUiComponent;
+import org.apache.ignite.internal.cli.core.style.element.UiElements;
+
+/**
+ * Handler for {@link ConfigAsPathException}.
+ */
+public class ConfigAsPathExceptionHandler implements 
ExceptionHandler<ConfigAsPathException> {
+    @Override
+    public int handle(ExceptionWriter err, ConfigAsPathException e) {
+        err.write(
+                ErrorUiComponent.builder()
+                        .header(String.format("Failed to parse configuration 
file."
+                                        + " Looks like config file path 
passed."
+                                        + " Did you mean %s option?",
+                                
UiElements.command(CLUSTER_CONFIG_FILE_OPTION)))
+                        .build().render()
+        );
+
+        return 1;
+    }
+
+    @Override
+    public Class<ConfigAsPathException> applicableException() {
+        return ConfigAsPathException.class;
+    }
+}
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ConfigFileParseException.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ConfigFileParseException.java
index c551bbb7b6..631130ac14 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ConfigFileParseException.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ConfigFileParseException.java
@@ -19,6 +19,11 @@ package org.apache.ignite.internal.cli.commands.cluster.init;
 
 /** Exception thrown when config file parse failed. */
 public class ConfigFileParseException extends RuntimeException {
+    public ConfigFileParseException(String message) {
+        super(message);
+    }
+
+
     public ConfigFileParseException(String message, Throwable cause) {
         super(message, cause);
     }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/exception/handler/DefaultExceptionHandlers.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/exception/handler/DefaultExceptionHandlers.java
index 983949c9c2..f52af34b36 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/exception/handler/DefaultExceptionHandlers.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/exception/handler/DefaultExceptionHandlers.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.internal.cli.core.exception.handler;
 
+import 
org.apache.ignite.internal.cli.commands.cluster.init.ConfigAsPathExceptionHandler;
 import 
org.apache.ignite.internal.cli.commands.cluster.init.ConfigParseExceptionHandler;
 import org.apache.ignite.internal.cli.core.exception.ExceptionHandlers;
 
@@ -41,5 +42,6 @@ public final class DefaultExceptionHandlers extends 
ExceptionHandlers {
         addExceptionHandler(new UnitAlreadyExistsExceptionHandler());
         addExceptionHandler(new FileNotFoundExceptionHandler());
         addExceptionHandler(new ConfigParseExceptionHandler());
+        addExceptionHandler(new ConfigAsPathExceptionHandler());
     }
 }
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 08ccf949d7..e03b3bafc0 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
@@ -171,6 +171,23 @@ public final class Flows {
         return acceptQuestion(question.render(), onAccept);
     }
 
+    /**
+     * 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 mapping function.
+     * @param <I> input type.
+     * @param <O> output type.
+     * @return new {@link FlowBuilder}.
+     */
+    public static <I, O> FlowBuilder<I, O> acceptQuestion(QuestionUiComponent 
question, Function<I, O> onAccept) {
+        return question(question.render(),
+                List.of(new AcceptedQuestionAnswer<>((a, i) -> 
onAccept.apply(i)),
+                        new InterruptQuestionAnswer<>())
+        );
+    }
+
     /**
      * Create new {@link Flow} which asks questions and returns the result of 
answer.
      *

Reply via email to