diff --git a/changelog/unreleased/SOLR-17697-picocli-experimental-cli.yml b/changelog/unreleased/SOLR-17697-picocli-experimental-cli.yml index bf2d57cb1f34..410b7c408a53 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 3f174ed58b81..0f904185bdcb 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 @@ * *

Used to send an arbitrary HTTP request to a Solr API endpoint. */ +@SuppressWarnings("UnnecessarilyFullyQualified") +@picocli.CommandLine.Command( + 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 static ModifiableSolrParams getSolrParamsFromUri(URI uri) { @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 62d3475c9788..998414dd4dda 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 @@ AuthTool.class, CreateTool.class, DeleteTool.class, - AssertTool.class + AssertTool.class, + ApiTool.class }) public class SolrCLI implements CLIO, java.util.concurrent.Callable { 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 000000000000..ef8c7ab3ba6b --- /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. + * + *

All {@code @Test} methods are inherited; only the invocation strategy is overridden. + */ +public class ApiToolPicocliTest extends ApiToolTest { + + @Override + protected int runTool(String[] args, Class 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 6893e51dc227..c44c5258c27d 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 @@ 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 clazz) throws Exception { + return CLITestHelper.runTool(args, clazz); + } + @BeforeClass public static void setupCluster() throws Exception { configureCluster(1) @@ -40,6 +45,20 @@ public static void setupCluster() throws Exception { .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 ccdffbeeaf4d..19c9de386731 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 53a667155efa..a5c2d787e0b5 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 1690291943b8..ed3b6ccda34e 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 000000000000..cccb38dc9e95 --- /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=] +.... + +// 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 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[]