You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@solr.apache.org by ge...@apache.org on 2024/01/25 17:27:39 UTC

(solr) branch branch_9x updated: SOLR-16397: Tweak v2 'REQUESTSTATUS' API to be more REST-ful (#2144)

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

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


The following commit(s) were added to refs/heads/branch_9x by this push:
     new d5b4935bf42 SOLR-16397: Tweak v2 'REQUESTSTATUS' API to be more REST-ful  (#2144)
d5b4935bf42 is described below

commit d5b4935bf4246576ac0e42d680b7d2f5ed25bd08
Author: Sanjay Dutt <sa...@gmail.com>
AuthorDate: Thu Jan 25 21:26:48 2024 +0530

    SOLR-16397: Tweak v2 'REQUESTSTATUS' API to be more REST-ful  (#2144)
    
    This commit changes the v2 "requeststatus" (core level) API to be more in line
    with the REST-ful design we're targeting for Solr's v2 APIs.
    
    Following these changes, the v2 API now appears as:
        `GET /api/node/commands/someCommandId`
    
    ---------
    
    Co-authored-by: iamsanjay <sa...@yahoo.com>
---
 solr/CHANGES.txt                                   |  3 +
 .../api/endpoint/GetNodeCommandStatusApi.java      | 50 ++++++++++++
 .../api/model/GetNodeCommandStatusResponse.java    | 27 +++++++
 .../solr/handler/admin/CoreAdminHandler.java       |  6 +-
 .../solr/handler/admin/CoreAdminOperation.java     | 40 ++-------
 .../handler/admin/api/GetNodeCommandStatus.java    | 75 +++++++++++++++++
 .../admin/api/RequestCoreCommandStatusAPI.java     | 62 --------------
 .../admin/api/GetNodeCommandStatusTest.java        | 94 ++++++++++++++++++++++
 .../handler/admin/api/V2CoreAPIMappingTest.java    | 13 ---
 .../configuration-guide/pages/coreadmin-api.adoc   | 21 ++++-
 10 files changed, 280 insertions(+), 111 deletions(-)

diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 3d64b09e494..2a36cd1853a 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -94,6 +94,9 @@ Improvements
 
 * SOLR-17096: solr.xml now supports declaring clusterSingleton plugins (Paul McArthur, David Smiley)
 
+* SOLR-16397: The v2 endpoint to request the status of asynchronous CoreAdmin commands has been updated to be more REST-ful.
+  Now available at `GET /api/node/commands/someRequestId` (Sanjay Dutt via Jason Gerlowski)
+
 Optimizations
 ---------------------
 * SOLR-17084: LBSolrClient (used by CloudSolrClient) now returns the count of core tracked as not live AKA zombies
diff --git a/solr/api/src/java/org/apache/solr/client/api/endpoint/GetNodeCommandStatusApi.java b/solr/api/src/java/org/apache/solr/client/api/endpoint/GetNodeCommandStatusApi.java
new file mode 100644
index 00000000000..76b95556e6d
--- /dev/null
+++ b/solr/api/src/java/org/apache/solr/client/api/endpoint/GetNodeCommandStatusApi.java
@@ -0,0 +1,50 @@
+/*
+ * 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.client.api.endpoint;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import org.apache.solr.client.api.model.GetNodeCommandStatusResponse;
+
+/**
+ * V2 API for checking the status of a core-level asynchronous command.
+ *
+ * <p>This API is analogous to the v1 /admin/cores?action=REQUESTSTATUS command. It is not to be
+ * confused with the more robust asynchronous command support offered under the v2
+ * `/cluster/command-status` path (or the corresponding v1 path
+ * `/solr/admin/collections?action=REQUESTSTATUS`). Async support at the core level differs in that
+ * command IDs are local to individual Solr nodes and are not persisted across restarts.
+ *
+ * @see GetNodeCommandStatusResponse
+ */
+@Path("/node/commands/")
+public interface GetNodeCommandStatusApi {
+  @Path("/{requestId}")
+  @GET
+  @Operation(
+      summary = "Request the status of an already submitted asynchronous CoreAdmin API call.",
+      tags = {"node"})
+  GetNodeCommandStatusResponse getCommandStatus(
+      @Parameter(
+              description = "The user defined request-id for the asynchronous request.",
+              required = true)
+          @PathParam("requestId")
+          String id);
+}
diff --git a/solr/api/src/java/org/apache/solr/client/api/model/GetNodeCommandStatusResponse.java b/solr/api/src/java/org/apache/solr/client/api/model/GetNodeCommandStatusResponse.java
new file mode 100644
index 00000000000..754b69743f3
--- /dev/null
+++ b/solr/api/src/java/org/apache/solr/client/api/model/GetNodeCommandStatusResponse.java
@@ -0,0 +1,27 @@
+/*
+ * 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.client.api.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class GetNodeCommandStatusResponse extends SolrJerseyResponse {
+  @JsonProperty("STATUS")
+  public String status;
+
+  @JsonProperty public String msg;
+  @JsonProperty public Object response;
+}
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminHandler.java
index 415343422d1..ca67a355b44 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminHandler.java
@@ -58,6 +58,7 @@ import org.apache.solr.handler.admin.api.AllCoresStatusAPI;
 import org.apache.solr.handler.admin.api.CoreSnapshot;
 import org.apache.solr.handler.admin.api.CreateCoreAPI;
 import org.apache.solr.handler.admin.api.CreateCoreBackup;
+import org.apache.solr.handler.admin.api.GetNodeCommandStatus;
 import org.apache.solr.handler.admin.api.InstallCoreData;
 import org.apache.solr.handler.admin.api.MergeIndexes;
 import org.apache.solr.handler.admin.api.OverseerOperationAPI;
@@ -67,7 +68,6 @@ import org.apache.solr.handler.admin.api.ReloadCore;
 import org.apache.solr.handler.admin.api.RenameCore;
 import org.apache.solr.handler.admin.api.RequestApplyCoreUpdatesAPI;
 import org.apache.solr.handler.admin.api.RequestBufferUpdatesAPI;
-import org.apache.solr.handler.admin.api.RequestCoreCommandStatusAPI;
 import org.apache.solr.handler.admin.api.RequestCoreRecoveryAPI;
 import org.apache.solr.handler.admin.api.RequestSyncShardAPI;
 import org.apache.solr.handler.admin.api.RestoreCore;
@@ -384,7 +384,6 @@ public class CoreAdminHandler extends RequestHandlerBase implements PermissionNa
     apis.addAll(AnnotatedApi.getApis(new RejoinLeaderElectionAPI(this)));
     apis.addAll(AnnotatedApi.getApis(new OverseerOperationAPI(this)));
     apis.addAll(AnnotatedApi.getApis(new SplitCoreAPI(this)));
-    apis.addAll(AnnotatedApi.getApis(new RequestCoreCommandStatusAPI(this)));
     // Internal APIs
     apis.addAll(AnnotatedApi.getApis(new RequestCoreRecoveryAPI(this)));
     apis.addAll(AnnotatedApi.getApis(new PrepareCoreRecoveryAPI(this)));
@@ -406,7 +405,8 @@ public class CoreAdminHandler extends RequestHandlerBase implements PermissionNa
         UnloadCore.class,
         SwapCores.class,
         RenameCore.class,
-        MergeIndexes.class);
+        MergeIndexes.class,
+        GetNodeCommandStatus.class);
   }
 
   public interface CoreAdminOp {
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminOperation.java b/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminOperation.java
index 1f6deee32ca..0aafba7c70b 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminOperation.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminOperation.java
@@ -44,12 +44,6 @@ import static org.apache.solr.common.params.CoreAdminParams.REPLICA;
 import static org.apache.solr.common.params.CoreAdminParams.REPLICA_TYPE;
 import static org.apache.solr.common.params.CoreAdminParams.SHARD;
 import static org.apache.solr.handler.admin.CoreAdminHandler.CallInfo;
-import static org.apache.solr.handler.admin.CoreAdminHandler.CoreAdminAsyncTracker.COMPLETED;
-import static org.apache.solr.handler.admin.CoreAdminHandler.CoreAdminAsyncTracker.FAILED;
-import static org.apache.solr.handler.admin.CoreAdminHandler.CoreAdminAsyncTracker.RUNNING;
-import static org.apache.solr.handler.admin.CoreAdminHandler.OPERATION_RESPONSE;
-import static org.apache.solr.handler.admin.CoreAdminHandler.RESPONSE_MESSAGE;
-import static org.apache.solr.handler.admin.CoreAdminHandler.RESPONSE_STATUS;
 import static org.apache.solr.handler.admin.CoreAdminHandler.buildCoreParams;
 import static org.apache.solr.handler.admin.CoreAdminHandler.normalizePath;
 
@@ -79,6 +73,7 @@ import org.apache.solr.core.CoreDescriptor;
 import org.apache.solr.core.SolrCore;
 import org.apache.solr.handler.admin.CoreAdminHandler.CoreAdminOp;
 import org.apache.solr.handler.admin.api.CoreSnapshot;
+import org.apache.solr.handler.admin.api.GetNodeCommandStatus;
 import org.apache.solr.handler.admin.api.ReloadCore;
 import org.apache.solr.handler.admin.api.RenameCore;
 import org.apache.solr.handler.admin.api.SwapCores;
@@ -242,34 +237,15 @@ public enum CoreAdminOperation implements CoreAdminOp {
   REQUESTSTATUS_OP(
       REQUESTSTATUS,
       it -> {
-        SolrParams params = it.req.getParams();
-        String requestId = params.required().get(CoreAdminParams.REQUESTID);
+        final var params = it.req.getParams();
+        final String requestId = params.required().get(CoreAdminParams.REQUESTID);
         log().info("Checking request status for : " + requestId);
 
-        final CoreAdminHandler.CoreAdminAsyncTracker coreAdminAsyncTracker =
-            it.handler.getCoreAdminAsyncTracker();
-        if (coreAdminAsyncTracker.getRequestStatusMap(RUNNING).containsKey(requestId)) {
-          it.rsp.add(RESPONSE_STATUS, RUNNING);
-        } else if (coreAdminAsyncTracker.getRequestStatusMap(COMPLETED).containsKey(requestId)) {
-          it.rsp.add(RESPONSE_STATUS, COMPLETED);
-          it.rsp.add(
-              RESPONSE_MESSAGE,
-              coreAdminAsyncTracker.getRequestStatusMap(COMPLETED).get(requestId).getRspObject());
-          it.rsp.add(
-              OPERATION_RESPONSE,
-              coreAdminAsyncTracker
-                  .getRequestStatusMap(COMPLETED)
-                  .get(requestId)
-                  .getOperationRspObject());
-        } else if (coreAdminAsyncTracker.getRequestStatusMap(FAILED).containsKey(requestId)) {
-          it.rsp.add(RESPONSE_STATUS, FAILED);
-          it.rsp.add(
-              RESPONSE_MESSAGE,
-              coreAdminAsyncTracker.getRequestStatusMap(FAILED).get(requestId).getRspObject());
-        } else {
-          it.rsp.add(RESPONSE_STATUS, "notfound");
-          it.rsp.add(RESPONSE_MESSAGE, "No task found in running, completed or failed tasks");
-        }
+        final var requestCoreCommandStatusApi =
+            new GetNodeCommandStatus(
+                it.handler.coreContainer, it.handler.coreAdminAsyncTracker, it.req, it.rsp);
+        final SolrJerseyResponse response = requestCoreCommandStatusApi.getCommandStatus(requestId);
+        V2ApiUtils.squashIntoSolrResponseWithoutHeader(it.rsp, response);
       }),
 
   OVERSEEROP_OP(
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/GetNodeCommandStatus.java b/solr/core/src/java/org/apache/solr/handler/admin/api/GetNodeCommandStatus.java
new file mode 100644
index 00000000000..fa3435996ef
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/handler/admin/api/GetNodeCommandStatus.java
@@ -0,0 +1,75 @@
+/*
+ * 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.handler.admin.api;
+
+import static org.apache.solr.handler.admin.CoreAdminHandler.CoreAdminAsyncTracker.COMPLETED;
+import static org.apache.solr.handler.admin.CoreAdminHandler.CoreAdminAsyncTracker.FAILED;
+import static org.apache.solr.handler.admin.CoreAdminHandler.CoreAdminAsyncTracker.RUNNING;
+
+import javax.inject.Inject;
+import org.apache.solr.client.api.endpoint.GetNodeCommandStatusApi;
+import org.apache.solr.client.api.model.GetNodeCommandStatusResponse;
+import org.apache.solr.common.params.CoreAdminParams;
+import org.apache.solr.core.CoreContainer;
+import org.apache.solr.handler.admin.CoreAdminHandler;
+import org.apache.solr.jersey.PermissionName;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.security.PermissionNameProvider;
+
+/**
+ * Implementation of V2 API interface {@link GetNodeCommandStatusApi} for checking the status of a
+ * core-level asynchronous command.
+ */
+public class GetNodeCommandStatus extends CoreAdminAPIBase implements GetNodeCommandStatusApi {
+
+  @Inject
+  public GetNodeCommandStatus(
+      CoreContainer coreContainer,
+      CoreAdminHandler.CoreAdminAsyncTracker coreAdminAsyncTracker,
+      SolrQueryRequest req,
+      SolrQueryResponse rsp) {
+    super(coreContainer, coreAdminAsyncTracker, req, rsp);
+  }
+
+  @Override
+  @PermissionName(PermissionNameProvider.Name.CORE_READ_PERM)
+  public GetNodeCommandStatusResponse getCommandStatus(String requestId) {
+    ensureRequiredParameterProvided(CoreAdminParams.REQUESTID, requestId);
+    var requestStatusResponse = new GetNodeCommandStatusResponse();
+    if (coreAdminAsyncTracker.getRequestStatusMap(RUNNING).containsKey(requestId)) {
+      requestStatusResponse.status = RUNNING;
+    } else if (coreAdminAsyncTracker.getRequestStatusMap(COMPLETED).containsKey(requestId)) {
+      requestStatusResponse.status = COMPLETED;
+      requestStatusResponse.response =
+          coreAdminAsyncTracker.getRequestStatusMap(COMPLETED).get(requestId).getRspObject();
+      requestStatusResponse.response =
+          coreAdminAsyncTracker
+              .getRequestStatusMap(COMPLETED)
+              .get(requestId)
+              .getOperationRspObject();
+    } else if (coreAdminAsyncTracker.getRequestStatusMap(FAILED).containsKey(requestId)) {
+      requestStatusResponse.status = FAILED;
+      requestStatusResponse.response =
+          coreAdminAsyncTracker.getRequestStatusMap(FAILED).get(requestId).getRspObject();
+    } else {
+      requestStatusResponse.status = "notfound";
+      requestStatusResponse.msg = "No task found in running, completed or failed tasks";
+    }
+    return requestStatusResponse;
+  }
+}
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/RequestCoreCommandStatusAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/RequestCoreCommandStatusAPI.java
deleted file mode 100644
index 242ecbcd7a5..00000000000
--- a/solr/core/src/java/org/apache/solr/handler/admin/api/RequestCoreCommandStatusAPI.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * 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.handler.admin.api;
-
-import static org.apache.solr.client.solrj.SolrRequest.METHOD.GET;
-import static org.apache.solr.common.params.CollectionParams.ACTION;
-import static org.apache.solr.handler.ClusterAPI.wrapParams;
-import static org.apache.solr.security.PermissionNameProvider.Name.CORE_READ_PERM;
-
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-import org.apache.solr.api.EndPoint;
-import org.apache.solr.common.params.CoreAdminParams;
-import org.apache.solr.handler.admin.CoreAdminHandler;
-import org.apache.solr.request.SolrQueryRequest;
-import org.apache.solr.response.SolrQueryResponse;
-
-/**
- * V2 API for checking the status of a core-level asynchronous command.
- *
- * <p>This API (GET /v2/cores/command-status/someId is analogous to the v1
- * /admin/cores?action=REQUESTSTATUS command. It is not to be confused with the more robust
- * asynchronous command support offered under the v2 `/cluster/command-status` path (or the
- * corresponding v1 path `/solr/admin/collections?action=REQUESTSTATUS`). Async support at the core
- * level differs in that command IDs are local to individual Solr nodes and are not persisted across
- * restarts.
- *
- * @see org.apache.solr.client.solrj.request.beans.RequestSyncShardPayload
- */
-public class RequestCoreCommandStatusAPI {
-
-  private final CoreAdminHandler coreAdminHandler;
-
-  public RequestCoreCommandStatusAPI(CoreAdminHandler coreAdminHandler) {
-    this.coreAdminHandler = coreAdminHandler;
-  }
-
-  @EndPoint(method = GET, path = "/cores/{core}/command-status/{id}", permission = CORE_READ_PERM)
-  public void getCommandStatus(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
-    final Map<String, Object> v1Params = new HashMap<>();
-    v1Params.put(
-        ACTION, CoreAdminParams.CoreAdminAction.REQUESTSTATUS.name().toLowerCase(Locale.ROOT));
-    v1Params.put(CoreAdminParams.REQUESTID, req.getPathTemplateValues().get("id"));
-    coreAdminHandler.handleRequestBody(wrapParams(req, v1Params), rsp);
-  }
-}
diff --git a/solr/core/src/test/org/apache/solr/handler/admin/api/GetNodeCommandStatusTest.java b/solr/core/src/test/org/apache/solr/handler/admin/api/GetNodeCommandStatusTest.java
new file mode 100644
index 00000000000..4a52383bf20
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/handler/admin/api/GetNodeCommandStatusTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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.handler.admin.api;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Map;
+import org.apache.solr.SolrTestCase;
+import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.client.api.endpoint.GetNodeCommandStatusApi;
+import org.apache.solr.core.CoreContainer;
+import org.apache.solr.handler.admin.CoreAdminHandler;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/** Test for {@link GetNodeCommandStatus}. */
+public class GetNodeCommandStatusTest extends SolrTestCase {
+  private CoreContainer mockCoreContainer;
+  private CoreAdminHandler.CoreAdminAsyncTracker mockAsyncTracker;
+  private CoreAdminHandler.CoreAdminAsyncTracker.TaskObject taskObject;
+  private GetNodeCommandStatusApi requestNodeCommandApi;
+
+  @BeforeClass
+  public static void ensureWorkingMockito() {
+    SolrTestCaseJ4.assumeWorkingMockito();
+  }
+
+  @Before
+  public void setupMocks() {
+    mockCoreContainer = mock(CoreContainer.class);
+    mockAsyncTracker = mock(CoreAdminHandler.CoreAdminAsyncTracker.class);
+    taskObject = new CoreAdminHandler.CoreAdminAsyncTracker.TaskObject(null, null, false, null);
+    requestNodeCommandApi =
+        new GetNodeCommandStatus(mockCoreContainer, mockAsyncTracker, null, null);
+  }
+
+  public void resetMocks() {}
+
+  @Test
+  public void testNonexistentCommandId() {
+    final var response = requestNodeCommandApi.getCommandStatus("NOTFOUND-1");
+    assertEquals("notfound", response.status);
+  }
+
+  @Test
+  public void testReturnsStatusOfRunningCommandId() {
+    final var runningTaskId = "RUNNING-1";
+    whenTaskExistsWithStatus(runningTaskId, CoreAdminHandler.CoreAdminAsyncTracker.RUNNING);
+
+    final var response = requestNodeCommandApi.getCommandStatus(runningTaskId);
+
+    assertEquals("running", response.status);
+  }
+
+  @Test
+  public void testReturnsStatusOfCompletedCommandId() {
+    final var completedTaskId = "COMPLETED-1";
+    whenTaskExistsWithStatus(completedTaskId, CoreAdminHandler.CoreAdminAsyncTracker.COMPLETED);
+
+    final var response = requestNodeCommandApi.getCommandStatus(completedTaskId);
+
+    assertEquals("completed", response.status);
+  }
+
+  @Test
+  public void testReturnsStatusOfFailedCommandId() {
+    final var willFailTaskId = "failed-1";
+    whenTaskExistsWithStatus(willFailTaskId, CoreAdminHandler.CoreAdminAsyncTracker.FAILED);
+
+    final var response = requestNodeCommandApi.getCommandStatus(willFailTaskId);
+
+    assertEquals("failed", response.status);
+  }
+
+  private void whenTaskExistsWithStatus(String taskId, String status) {
+    when(mockAsyncTracker.getRequestStatusMap(status)).thenReturn(Map.of(taskId, taskObject));
+  }
+}
diff --git a/solr/core/src/test/org/apache/solr/handler/admin/api/V2CoreAPIMappingTest.java b/solr/core/src/test/org/apache/solr/handler/admin/api/V2CoreAPIMappingTest.java
index 2ab55905e04..bd3171cd1c3 100644
--- a/solr/core/src/test/org/apache/solr/handler/admin/api/V2CoreAPIMappingTest.java
+++ b/solr/core/src/test/org/apache/solr/handler/admin/api/V2CoreAPIMappingTest.java
@@ -26,7 +26,6 @@ import static org.apache.solr.common.params.CoreAdminParams.CORE_NODE_NAME;
 import static org.apache.solr.common.params.CoreAdminParams.NAME;
 import static org.apache.solr.common.params.CoreAdminParams.OTHER;
 import static org.apache.solr.common.params.CoreAdminParams.RANGES;
-import static org.apache.solr.common.params.CoreAdminParams.REQUESTID;
 import static org.apache.solr.common.params.CoreAdminParams.TARGET_CORE;
 
 import java.util.Arrays;
@@ -70,7 +69,6 @@ public class V2CoreAPIMappingTest extends V2ApiMappingTest<CoreAdminHandler> {
     apiBag.registerObject(new RequestApplyCoreUpdatesAPI(handler));
     apiBag.registerObject(new RequestSyncShardAPI(handler));
     apiBag.registerObject(new RequestBufferUpdatesAPI(handler));
-    apiBag.registerObject(new RequestCoreCommandStatusAPI(handler));
   }
 
   @Test
@@ -172,15 +170,4 @@ public class V2CoreAPIMappingTest extends V2ApiMappingTest<CoreAdminHandler> {
     assertEquals("requestbufferupdates", v1Params.get(ACTION));
     assertEquals("coreName", v1Params.get(NAME));
   }
-
-  // Strictly speaking, this API isn't at the /cores/coreName path, but as the only API at its path
-  // (/cores/coreName/command-status/requestId) it doesn't merit its own test class.
-  @Test
-  public void testRequestCommandStatusAllParams() throws Exception {
-    final SolrParams v1Params =
-        captureConvertedV1Params("/cores/coreName/command-status/someId", "GET", NO_BODY);
-
-    assertEquals("requeststatus", v1Params.get(ACTION));
-    assertEquals("someId", v1Params.get(REQUESTID));
-  }
 }
diff --git a/solr/solr-ref-guide/modules/configuration-guide/pages/coreadmin-api.adoc b/solr/solr-ref-guide/modules/configuration-guide/pages/coreadmin-api.adoc
index 9daedadf701..ae5d4cbd193 100644
--- a/solr/solr-ref-guide/modules/configuration-guide/pages/coreadmin-api.adoc
+++ b/solr/solr-ref-guide/modules/configuration-guide/pages/coreadmin-api.adoc
@@ -788,8 +788,27 @@ If the `split.key` parameter is specified then only documents having the same ro
 == REQUESTSTATUS
 
 Request the status of an already submitted asynchronous CoreAdmin API call.
+--
+[example.tab-pane#v1coreadmin-requeststatus]
+====
+[.tab-label]*V1 API*
+
+[source,bash]
+----
+http://localhost:8983/solr/admin/cores?action=REQUESTSTATUS&requestid=id
+
+----
+====
+[example.tab-pane#v2coreadmin-requeststatus]
+====
+[.tab-label]*V2 API*
 
-`admin/cores?action=REQUESTSTATUS&requestid=_id_`
+[source,bash]
+----
+curl -X GET http://localhost:8983/api/node/commands/id
+----
+====
+--
 
 === Core REQUESTSTATUS Parameters