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

janhoy pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr.git


The following commit(s) were added to refs/heads/main by this push:
     new 14924e6f78b SOLR-18509: Port ApiTool to picocli (#5037)
14924e6f78b is described below

commit 14924e6f78b912026f01425d22d8c696bb401a33
Author: Serhiy Bzhezytskyy <[email protected]>
AuthorDate: Thu Oct 8 18:23:04 2026 +0300

    SOLR-18509: Port ApiTool to picocli (#5037)
    
    Co-authored-by: Jan Høydahl <[email protected]>
---
 .../SOLR-17697-picocli-experimental-cli.yml        |  2 +
 .../core/src/java/org/apache/solr/cli/ApiTool.java | 34 ++++++++++-
 .../core/src/java/org/apache/solr/cli/SolrCLI.java |  3 +-
 .../org/apache/solr/cli/ApiToolPicocliTest.java    | 39 ++++++++++++
 .../src/test/org/apache/solr/cli/ApiToolTest.java  | 19 ++++++
 solr/packaging/build.gradle                        |  2 +
 .../modules/deployment-guide/deployment-nav.adoc   |  1 +
 .../modules/deployment-guide/pages/cli/index.adoc  |  1 +
 .../deployment-guide/pages/cli/solr-api.adoc       | 71 ++++++++++++++++++++++
 9 files changed, 170 insertions(+), 2 deletions(-)

diff --git a/changelog/unreleased/SOLR-17697-picocli-experimental-cli.yml 
b/changelog/unreleased/SOLR-17697-picocli-experimental-cli.yml
index bf2d57cb1f3..410b7c408a5 100644
--- a/changelog/unreleased/SOLR-17697-picocli-experimental-cli.yml
+++ b/changelog/unreleased/SOLR-17697-picocli-experimental-cli.yml
@@ -13,3 +13,5 @@ links:
     url: https://issues.apache.org/jira/browse/SOLR-17697
   - name: SOLR-18510
     url: https://issues.apache.org/jira/browse/SOLR-18510
+  - name: SOLR-18509
+    url: https://issues.apache.org/jira/browse/SOLR-18509
diff --git a/solr/core/src/java/org/apache/solr/cli/ApiTool.java 
b/solr/core/src/java/org/apache/solr/cli/ApiTool.java
index 3f174ed58b8..0f904185bdc 100644
--- a/solr/core/src/java/org/apache/solr/cli/ApiTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/ApiTool.java
@@ -31,8 +31,22 @@ import org.apache.solr.common.params.ModifiableSolrParams;
  *
  * <p>Used to send an arbitrary HTTP request to a Solr API endpoint.
  */
+@SuppressWarnings("UnnecessarilyFullyQualified")
[email protected](
+    name = "api",
+    description = "Used to send an arbitrary HTTP GET request to a Solr API 
endpoint.",
+    footerHeading = "%nExamples:%n",
+    footer = {
+      "  # Send a GET request to a Solr API endpoint",
+      "  bin/solr api -s http://localhost:8983/api/collections";
+    })
 public class ApiTool extends ToolBase {
 
+  /**
+   * @deprecated Only used by the commons-cli parser; the picocli path 
declares this as an annotated
+   *     field.
+   */
+  @Deprecated
   private static final Option SOLR_URL_OPTION =
       Option.builder("s")
           .longOpt("solr-url")
@@ -45,6 +59,22 @@ public class ApiTool extends ToolBase {
   /** Parameters for the api command, independent of the command line parser. 
*/
   record ApiParams(String getUrl, String credentials) {}
 
+  // --- picocli fields ---
+
+  @picocli.CommandLine.Option(
+      names = {"-s", "--solr-url"},
+      required = true,
+      paramLabel = "URL",
+      description =
+          "Full URL of the Solr API endpoint to GET, e.g. 
http://localhost:8983/api/collections";)
+  private String solrUrlOpt;
+
+  @picocli.CommandLine.Mixin private CredentialsOptions credentialsOptions;
+
+  public ApiTool() {
+    this(new DefaultToolRuntime());
+  }
+
   public ApiTool(ToolRuntime runtime) {
     super(runtime);
   }
@@ -124,6 +154,8 @@ public class ApiTool extends ToolBase {
 
   @Override
   public int callTool() throws Exception {
-    throw new UnsupportedOperationException("This tool does not yet support 
PicoCli");
+    ApiParams params = new ApiParams(solrUrlOpt, 
credentialsOptions.credentials);
+    callApi(params);
+    return 0;
   }
 }
diff --git a/solr/core/src/java/org/apache/solr/cli/SolrCLI.java 
b/solr/core/src/java/org/apache/solr/cli/SolrCLI.java
index 62d3475c978..998414dd4dd 100755
--- a/solr/core/src/java/org/apache/solr/cli/SolrCLI.java
+++ b/solr/core/src/java/org/apache/solr/cli/SolrCLI.java
@@ -81,7 +81,8 @@ import org.slf4j.LoggerFactory;
       AuthTool.class,
       CreateTool.class,
       DeleteTool.class,
-      AssertTool.class
+      AssertTool.class,
+      ApiTool.class
     })
 public class SolrCLI implements CLIO, java.util.concurrent.Callable<Integer> {
 
diff --git a/solr/core/src/test/org/apache/solr/cli/ApiToolPicocliTest.java 
b/solr/core/src/test/org/apache/solr/cli/ApiToolPicocliTest.java
new file mode 100644
index 00000000000..ef8c7ab3ba6
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/cli/ApiToolPicocliTest.java
@@ -0,0 +1,39 @@
+/*
+ * 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.solr.cli;
+
+import java.util.Arrays;
+import picocli.CommandLine;
+
+/**
+ * Runs all {@link ApiToolTest} tests through the picocli invocation path.
+ *
+ * <p>All {@code @Test} methods are inherited; only the invocation strategy is 
overridden.
+ */
+public class ApiToolPicocliTest extends ApiToolTest {
+
+  @Override
+  protected int runTool(String[] args, Class<? extends ToolBase> clazz) throws 
Exception {
+    // args[0] is the tool name used by commons-cli dispatch; strip it for 
picocli.
+    String[] toolArgs = Arrays.copyOfRange(args, 1, args.length);
+    ToolRuntime runtime = new CLITestHelper.TestingRuntime(false);
+    ToolBase tool = 
clazz.getDeclaredConstructor(ToolRuntime.class).newInstance(runtime);
+    return new CommandLine(tool)
+        .setDefaultValueProvider(new CliDefaultValueProvider())
+        .execute(toolArgs);
+  }
+}
diff --git a/solr/core/src/test/org/apache/solr/cli/ApiToolTest.java 
b/solr/core/src/test/org/apache/solr/cli/ApiToolTest.java
index 6893e51dc22..c44c5258c27 100644
--- a/solr/core/src/test/org/apache/solr/cli/ApiToolTest.java
+++ b/solr/core/src/test/org/apache/solr/cli/ApiToolTest.java
@@ -32,6 +32,11 @@ import org.junit.Test;
 public class ApiToolTest extends SolrCloudTestCase {
   static String COLLECTION_NAME = "globalLoaderColl";
 
+  /** Runs the tool. Overridden by the picocli variant of this test. */
+  protected int runTool(String[] args, Class<? extends ToolBase> clazz) throws 
Exception {
+    return CLITestHelper.runTool(args, clazz);
+  }
+
   @BeforeClass
   public static void setupCluster() throws Exception {
     configureCluster(1)
@@ -40,6 +45,20 @@ public class ApiToolTest extends SolrCloudTestCase {
         .configure();
   }
 
+  @Test
+  public void testApiCommandSucceedsOnAnOkResponse() throws Exception {
+    String url = cluster.getJettySolrRunner(0).getBaseUrl() + 
"/admin/info/system";
+
+    assertEquals(0, runTool(new String[] {"api", "-s", url}, ApiTool.class));
+  }
+
+  @Test
+  public void testApiCommandFailsOnANon2xxResponse() throws Exception {
+    String url = cluster.getJettySolrRunner(0).getBaseUrl() + 
"/admin/no-such-handler";
+
+    assertEquals(1, runTool(new String[] {"api", "-s", url}, ApiTool.class));
+  }
+
   @Test
   public void testParsingGetUrl() throws URISyntaxException {
 
diff --git a/solr/packaging/build.gradle b/solr/packaging/build.gradle
index ccdffbeeaf4..19c9de38673 100644
--- a/solr/packaging/build.gradle
+++ b/solr/packaging/build.gradle
@@ -364,6 +364,8 @@ task integrationTestsPicocli(type: BatsTask) {
       'test/test_delete_collection.bats',
       'test/test_zk.bats',
       'test/test_auth.bats',
+      'test/test_modules.bats',
+      'test/test_security_manager.bats',
   ]
 }
 
diff --git a/solr/solr-ref-guide/modules/deployment-guide/deployment-nav.adoc 
b/solr/solr-ref-guide/modules/deployment-guide/deployment-nav.adoc
index 53a667155ef..a5c2d787e0b 100644
--- a/solr/solr-ref-guide/modules/deployment-guide/deployment-nav.adoc
+++ b/solr/solr-ref-guide/modules/deployment-guide/deployment-nav.adoc
@@ -20,6 +20,7 @@
 * xref:solr-control-script-reference.adoc[]
 ** xref:cli/index.adoc[Experimental picocli CLI]
 // CLI-DOCS-START
+*** xref:cli/solr-api.adoc[api]
 *** xref:cli/solr-assert.adoc[assert]
 *** xref:cli/solr-auth.adoc[auth]
 *** xref:cli/solr-create.adoc[create]
diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/cli/index.adoc 
b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/index.adoc
index 1690291943b..ed3b6ccda34 100644
--- a/solr/solr-ref-guide/modules/deployment-guide/pages/cli/index.adoc
+++ b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/index.adoc
@@ -59,6 +59,7 @@ Any command not listed here is unavailable when the picocli 
implementation is en
 |===
 |Command
 
+|xref:cli/solr-api.adoc[bin/solr api]
 |xref:cli/solr-assert.adoc[bin/solr assert]
 |xref:cli/solr-auth.adoc[bin/solr auth]
 |xref:cli/solr-create.adoc[bin/solr create]
diff --git 
a/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-api.adoc 
b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-api.adoc
new file mode 100644
index 00000000000..cccb38dc9e9
--- /dev/null
+++ b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-api.adoc
@@ -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.
+//
+// DO NOT EDIT -- this page is auto-generated from picocli annotations.
+// To update: modify the @Command/@Option annotations in the Java source, then 
run:
+//   ./gradlew :solr:solr-ref-guide:generateCliDocs
+
+= bin/solr api
+:page-toclevels: 2
+
+[WARNING]
+====
+This page documents the *experimental* picocli implementation of `bin/solr`, 
which is not enabled by default.
+For what `bin/solr` does by default, see 
xref:solr-control-script-reference.adoc[].
+See xref:cli/index.adoc[] to enable this implementation and for its known 
limitations.
+====
+
+// tag::picocli-generated-man-section-synopsis[]
+== Synopsis
+
+....
+bin/solr api [-hv] -s=URL [-u=<credentials>]
+....
+
+// end::picocli-generated-man-section-synopsis[]
+
+// tag::picocli-generated-man-section-description[]
+== Description
+
+Used to send an arbitrary HTTP GET request to a Solr API endpoint.
+
+// end::picocli-generated-man-section-description[]
+
+// tag::picocli-generated-man-section-options[]
+== Options
+
+*-h*, *--help*::
+  Print this help message and exit.
+
+*-s*, *--solr-url*=_URL_::
+  *(required)* Full URL of the Solr API endpoint to GET, e.g. 
http://localhost:8983/api/collections
+
+*-u*, *--credentials*=_<credentials>_::
+  Credentials in the format username:password. Example: --credentials 
solr:SolrRocks
+
+*-v*, *--verbose*::
+  Enable verbose mode.
+
+// end::picocli-generated-man-section-options[]
+
+// tag::picocli-generated-man-section-footer[]
+== Examples
+[%hardbreaks]
+  # Send a GET request to a Solr API endpoint
+  bin/solr api -s http://localhost:8983/api/collections
+
+// end::picocli-generated-man-section-footer[]

Reply via email to