Copilot commented on code in PR #4177:
URL: https://github.com/apache/solr/pull/4177#discussion_r2874001999


##########
solr/core/src/java/org/apache/solr/jersey/container/ContainerRequestUtils.java:
##########
@@ -140,4 +144,20 @@ private static String getServerAddress(URI baseUri) {
     }
     return serverAddress;
   }
+
+  /**
+   * Normalizes the {@code /c/} collection alias to {@code /collections/} so 
that JAX-RS path
+   * matching works correctly. The V2 API supports {@code /c/} as a shorthand 
for {@code
+   * /collections/}, but JAX-RS path templates use the regex {@code 
cores|collections} to match the
+   * index-type segment.
+   */
+  static String normalizeCAlias(String uri) {
+    if (uri.startsWith("/c/")) {
+      return "/collections/" + uri.substring(3);
+    }
+    if (uri.equals("/c")) {
+      return "/collections";
+    }
+    return uri;

Review Comment:
   `normalizeCAlias` changes URI normalization behavior for all Jersey v2 
requests, but there’s no unit test asserting the expected transformations 
(e.g., `/c/<name>/...` → `/collections/<name>/...`, and `/c` → `/collections`). 
Adding a small focused test would help prevent regressions in v2 path matching 
(especially since this runs before all JAX-RS routing).



##########
solr/solr-ref-guide/modules/indexing-guide/pages/indexing-with-v2-apis.adoc:
##########
@@ -0,0 +1,183 @@
+= Indexing with the V2 Update API
+// 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.
+
+Solr's xref:configuration-guide:v2-api.adoc[v2 API] provides update endpoints 
under the `/api` path prefix.
+These endpoints accept the same document formats as the 
xref:indexing-with-update-handlers.adoc[v1 update handlers], with some 
important differences described below.
+
+NOTE: The v2 API is classified as "experimental" and may change in 
backwards-incompatible ways.
+
+== V2 Update Endpoint Paths
+
+For a SolrCloud collection the v2 update base path is:
+
+----
+/api/collections/{collection}/update
+----
+
+For a standalone core the v2 update base path is:
+
+----
+/api/cores/{core}/update
+----
+
+The following sub-paths are available:
+
+[width="100%",options="header",]
+|===
+|Path |Accepted Format |Notes
+|`/update` |JSON (array of documents) |Equivalent to v1 `/update/json/docs`; 
document arrays only
+|`/update/json` |JSON (array of documents) |Same behavior as `/update`; 
document arrays only
+|`/update/xml` |XML |Supports full XML update syntax (add, delete, commit, 
optimize)
+|`/update/csv` |CSV |Equivalent to v1 `/update/csv`
+|`/update/bin` |Javabin |Equivalent to v1 `/update` with `Content-Type: 
application/javabin`
+|===
+
+IMPORTANT: The v2 `/update` and `/update/json` endpoints are document-only: 
they process a JSON array of documents (like the v1 `/update/json/docs` path) 
and do *not* support the full 
xref:indexing-with-update-handlers.adoc#sending-json-update-commands[JSON 
update command syntax] (commit, delete, optimize within the request body).
+Use `/update/xml` for those operations, or issue commit/rollback via separate 
API calls.
+
+== JSON Document Indexing
+
+The v2 `/update` and `/update/json` endpoints both accept a JSON array of 
documents:

Review Comment:
   The endpoint table says v2 `/update` and `/update/json` accept a “JSON 
(array of documents)” and are “document arrays only”, but the same page later 
states a single document can be posted as a JSON object. Please reconcile these 
statements (e.g., document that both a single JSON object and an array are 
accepted) to avoid contradictory guidance.
   ```suggestion
   |`/update` |JSON document or array of documents |Equivalent to v1 
`/update/json/docs`; documents only (no JSON command syntax)
   |`/update/json` |JSON document or array of documents |Same behavior as 
`/update`; documents only (no JSON command syntax)
   |`/update/xml` |XML |Supports full XML update syntax (add, delete, commit, 
optimize)
   |`/update/csv` |CSV |Equivalent to v1 `/update/csv`
   |`/update/bin` |Javabin |Equivalent to v1 `/update` with `Content-Type: 
application/javabin`
   |===
   
   IMPORTANT: The v2 `/update` and `/update/json` endpoints are document-only: 
they process one or more JSON documents (either a single JSON object or an 
array of objects, like the v1 `/update/json/docs` path) and do *not* support 
the full 
xref:indexing-with-update-handlers.adoc#sending-json-update-commands[JSON 
update command syntax] (commit, delete, optimize within the request body).
   Use `/update/xml` for those operations, or issue commit/rollback via 
separate API calls.
   
   == JSON Document Indexing
   
   The v2 `/update` and `/update/json` endpoints both accept JSON document(s). 
The example below shows an array of documents:
   ```



##########
solr/core/src/java/org/apache/solr/handler/admin/api/UpdateAPI.java:
##########
@@ -17,52 +17,93 @@
 
 package org.apache.solr.handler.admin.api;
 
-import static org.apache.solr.client.solrj.SolrRequest.METHOD.POST;
 import static org.apache.solr.common.params.CommonParams.PATH;
 import static org.apache.solr.security.PermissionNameProvider.Name.UPDATE_PERM;
 
-import org.apache.solr.api.EndPoint;
+import jakarta.inject.Inject;
+import org.apache.solr.api.JerseyResource;
+import org.apache.solr.client.api.endpoint.UpdateApi;
+import org.apache.solr.client.api.model.SolrJerseyResponse;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.core.SolrCore;
 import org.apache.solr.handler.UpdateRequestHandler;
+import org.apache.solr.jersey.APIConfigProvider;
+import org.apache.solr.jersey.PermissionName;
 import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.response.SolrQueryResponse;
 
 /**
- * All v2 APIs that share a prefix of /update
+ * V2 API implementation for indexing documents.
  *
- * <p>Most of these v2 APIs are implemented as pure "pass-throughs" to the v1 
code paths, but there
- * are a few exceptions: /update and /update/json are both rewritten to 
/update/json/docs.
+ * <p>These APIs delegate to the v1 {@link UpdateRequestHandler}. The {@code 
/update} and {@code
+ * /update/json} paths are rewritten to {@code /update/json/docs} so that JSON 
arrays of documents
+ * are processed by the JSON loader rather than the update-command loader.
  */
-public class UpdateAPI {
+public class UpdateAPI extends JerseyResource implements UpdateApi {
+
   private final UpdateRequestHandler updateRequestHandler;
+  private final SolrQueryRequest solrQueryRequest;
+  private final SolrQueryResponse solrQueryResponse;
+
+  @Inject
+  public UpdateAPI(
+      UpdateRequestHandlerConfig handlerConfig,
+      SolrQueryRequest solrQueryRequest,
+      SolrQueryResponse solrQueryResponse) {
+    this.updateRequestHandler = handlerConfig.updateRequestHandler;
+    this.solrQueryRequest = solrQueryRequest;
+    this.solrQueryResponse = solrQueryResponse;
+  }

Review Comment:
   The PR title/description frame this as a docs-only change, but this file 
introduces a functional change to the v2 update implementation (JAX-RS resource 
+ rewrite/response handling). Please update the PR title/description to reflect 
the included production code/test changes so reviewers can assess scope and 
risk appropriately.



##########
solr/api/src/java/org/apache/solr/client/api/endpoint/UpdateApi.java:
##########
@@ -0,0 +1,69 @@
+/*
+ * 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 static org.apache.solr.client.api.util.Constants.INDEX_PATH_PREFIX;
+
+import io.swagger.v3.oas.annotations.Operation;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import org.apache.solr.client.api.model.SolrJerseyResponse;
+import org.apache.solr.client.api.util.StoreApiParameters;
+
+/** V2 API definitions for indexing documents via the update handler. */
+@Path(INDEX_PATH_PREFIX + "/update")
+public interface UpdateApi {
+
+  @POST
+  @StoreApiParameters
+  @Operation(
+      summary = "Add, delete, or update documents using any supported content 
type",
+      tags = {"update"})
+  SolrJerseyResponse update() throws Exception;
+
+  @POST
+  @Path("/json")
+  @StoreApiParameters
+  @Operation(
+      summary = "Add, delete, or update documents in JSON format",
+      tags = {"update"})
+  SolrJerseyResponse updateJson() throws Exception;
+
+  @POST
+  @Path("/xml")
+  @StoreApiParameters
+  @Operation(
+      summary = "Add, delete, or update documents in XML format",
+      tags = {"update"})
+  SolrJerseyResponse updateXml() throws Exception;
+
+  @POST
+  @Path("/csv")
+  @StoreApiParameters
+  @Operation(
+      summary = "Add, delete, or update documents in CSV format",
+      tags = {"update"})
+  SolrJerseyResponse updateCsv() throws Exception;
+
+  @POST
+  @Path("/bin")
+  @StoreApiParameters
+  @Operation(
+      summary = "Add, delete, or update documents in JavaBin format",
+      tags = {"update"})
+  SolrJerseyResponse updateBin() throws Exception;

Review Comment:
   Similarly, the summaries for `/update/csv` and `/update/bin` say “Add, 
delete, or update documents…”, but these loaders are document-ingest only (no 
delete/commit commands in-body). Please tighten the wording (e.g., “index 
documents”) to avoid implying full update-command support for these content 
types.



##########
solr/api/src/java/org/apache/solr/client/api/endpoint/UpdateApi.java:
##########
@@ -0,0 +1,69 @@
+/*
+ * 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 static org.apache.solr.client.api.util.Constants.INDEX_PATH_PREFIX;
+
+import io.swagger.v3.oas.annotations.Operation;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import org.apache.solr.client.api.model.SolrJerseyResponse;
+import org.apache.solr.client.api.util.StoreApiParameters;
+
+/** V2 API definitions for indexing documents via the update handler. */
+@Path(INDEX_PATH_PREFIX + "/update")
+public interface UpdateApi {
+
+  @POST
+  @StoreApiParameters
+  @Operation(
+      summary = "Add, delete, or update documents using any supported content 
type",
+      tags = {"update"})
+  SolrJerseyResponse update() throws Exception;
+
+  @POST
+  @Path("/json")
+  @StoreApiParameters
+  @Operation(
+      summary = "Add, delete, or update documents in JSON format",
+      tags = {"update"})
+  SolrJerseyResponse updateJson() throws Exception;

Review Comment:
   The OpenAPI summaries for `/update` and `/update/json` currently say they 
can “Add, delete, or update” and (for `/update`) accept “any supported content 
type”. In this PR’s implementation these endpoints are rewritten to 
`/update/json/docs` (docs-only JSON loader) and do not support JSON update 
commands in the request body, nor non-JSON bodies. Please update the summaries 
to match actual behavior so generated docs/clients don’t advertise unsupported 
operations.



##########
solr/core/src/java/org/apache/solr/handler/admin/api/UpdateAPI.java:
##########
@@ -17,52 +17,93 @@
 
 package org.apache.solr.handler.admin.api;
 
-import static org.apache.solr.client.solrj.SolrRequest.METHOD.POST;
 import static org.apache.solr.common.params.CommonParams.PATH;
 import static org.apache.solr.security.PermissionNameProvider.Name.UPDATE_PERM;
 
-import org.apache.solr.api.EndPoint;
+import jakarta.inject.Inject;
+import org.apache.solr.api.JerseyResource;
+import org.apache.solr.client.api.endpoint.UpdateApi;
+import org.apache.solr.client.api.model.SolrJerseyResponse;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.core.SolrCore;
 import org.apache.solr.handler.UpdateRequestHandler;
+import org.apache.solr.jersey.APIConfigProvider;
+import org.apache.solr.jersey.PermissionName;
 import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.response.SolrQueryResponse;
 
 /**
- * All v2 APIs that share a prefix of /update
+ * V2 API implementation for indexing documents.
  *
- * <p>Most of these v2 APIs are implemented as pure "pass-throughs" to the v1 
code paths, but there
- * are a few exceptions: /update and /update/json are both rewritten to 
/update/json/docs.
+ * <p>These APIs delegate to the v1 {@link UpdateRequestHandler}. The {@code 
/update} and {@code
+ * /update/json} paths are rewritten to {@code /update/json/docs} so that JSON 
arrays of documents
+ * are processed by the JSON loader rather than the update-command loader.
  */
-public class UpdateAPI {
+public class UpdateAPI extends JerseyResource implements UpdateApi {
+
   private final UpdateRequestHandler updateRequestHandler;
+  private final SolrQueryRequest solrQueryRequest;
+  private final SolrQueryResponse solrQueryResponse;
+
+  @Inject
+  public UpdateAPI(
+      UpdateRequestHandlerConfig handlerConfig,
+      SolrQueryRequest solrQueryRequest,
+      SolrQueryResponse solrQueryResponse) {
+    this.updateRequestHandler = handlerConfig.updateRequestHandler;
+    this.solrQueryRequest = solrQueryRequest;
+    this.solrQueryResponse = solrQueryResponse;
+  }
 
-  public UpdateAPI(UpdateRequestHandler updateRequestHandler) {
-    this.updateRequestHandler = updateRequestHandler;
+  @Override
+  @PermissionName(UPDATE_PERM)
+  public SolrJerseyResponse update() throws Exception {
+    return handleUpdate(UpdateRequestHandler.DOC_PATH);
   }
 
-  @EndPoint(method = POST, path = "/update", permission = UPDATE_PERM)
-  public void update(SolrQueryRequest req, SolrQueryResponse rsp) throws 
Exception {
-    req.getContext().put(PATH, "/update/json/docs");
-    updateRequestHandler.handleRequest(req, rsp);
+  @Override
+  @PermissionName(UPDATE_PERM)
+  public SolrJerseyResponse updateJson() {
+    return handleUpdate(UpdateRequestHandler.DOC_PATH);
   }
 
-  @EndPoint(method = POST, path = "/update/xml", permission = UPDATE_PERM)
-  public void updateXml(SolrQueryRequest req, SolrQueryResponse rsp) throws 
Exception {
-    updateRequestHandler.handleRequest(req, rsp);
+  @Override
+  @PermissionName(UPDATE_PERM)
+  public SolrJerseyResponse updateXml() {
+    return handleUpdate(null);
   }
 
-  @EndPoint(method = POST, path = "/update/csv", permission = UPDATE_PERM)
-  public void updateCsv(SolrQueryRequest req, SolrQueryResponse rsp) throws 
Exception {
-    updateRequestHandler.handleRequest(req, rsp);
+  @Override
+  @PermissionName(UPDATE_PERM)
+  public SolrJerseyResponse updateCsv() {
+    return handleUpdate(null);
   }
 
-  @EndPoint(method = POST, path = "/update/json", permission = UPDATE_PERM)
-  public void updateJson(SolrQueryRequest req, SolrQueryResponse rsp) throws 
Exception {
-    req.getContext().put(PATH, "/update/json/docs");
-    updateRequestHandler.handleRequest(req, rsp);
+  @Override
+  @PermissionName(UPDATE_PERM)
+  public SolrJerseyResponse updateBin() {
+    return handleUpdate(null);
   }
 
-  @EndPoint(method = POST, path = "/update/bin", permission = UPDATE_PERM)
-  public void updateJavabin(SolrQueryRequest req, SolrQueryResponse rsp) 
throws Exception {
-    updateRequestHandler.handleRequest(req, rsp);
+  private SolrJerseyResponse handleUpdate(String pathOverride) {
+    final SolrJerseyResponse response = 
instantiateJerseyResponse(SolrJerseyResponse.class);
+    if (pathOverride != null) {
+      solrQueryRequest.getContext().put(PATH, pathOverride);
+    }
+    SolrCore.preDecorateResponse(solrQueryRequest, solrQueryResponse);
+    updateRequestHandler.handleRequest(solrQueryRequest, solrQueryResponse);
+    SolrCore.postDecorateResponse(updateRequestHandler, solrQueryRequest, 
solrQueryResponse);

Review Comment:
   `handleUpdate` calls `SolrCore.postDecorateResponse(...)`, which adds a 
`responseHeader` to `solrQueryResponse`. The Jersey layer then serializes the 
returned `SolrJerseyResponse` by squashing it into the same `SolrQueryResponse` 
(also including its own `responseHeader`), which can result in duplicate 
`responseHeader` entries in the output. Consider removing the explicit pre/post 
decoration here (to align with other Jersey resources) or otherwise ensure only 
one responseHeader is serialized (e.g., clear the existing header before 
returning, or use a squash-without-header approach for this endpoint).
   ```suggestion
       // Do not call SolrCore.preDecorateResponse/postDecorateResponse here:
       // Jersey will handle response decoration (including responseHeader) to 
avoid duplicates.
       updateRequestHandler.handleRequest(solrQueryRequest, solrQueryResponse);
   ```



##########
solr/core/src/test/org/apache/solr/handler.admin.api/UpdateAPITest.java:
##########
@@ -0,0 +1,125 @@
+/*
+ * 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.core.CoreContainer.ALLOW_PATHS_SYSPROP;
+
+import org.apache.solr.SolrTestCaseJ4;

Review Comment:
   This new test is in `solr/core/src/test/org/apache/solr/handler.admin.api/` 
but declares package `org.apache.solr.handler.admin.api`. Nearly all other 
tests for this package live under 
`solr/core/src/test/org/apache/solr/handler/admin/api/`. Moving this file to 
the conventional directory would keep the test layout consistent and avoid 
surprises with IDE/test discovery tooling.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to