You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@solr.apache.org by ds...@apache.org on 2026/10/08 03:41:56 UTC

(solr) 09/12: SOLR-18504: Merge duplicate OpenAPI path templates (#5024)

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

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

commit b5f148ad7bb4547b4257053cb8035c621413bf50
Author: Jan Høydahl <ja...@apache.org>
AuthorDate: Wed Oct 7 14:39:27 2026 +0200

    SOLR-18504: Merge duplicate OpenAPI path templates (#5024)
    
    (cherry picked from commit a2f43aadfd212f6d9f05cdcdb5bd27b11fac81b1)
---
 .../SOLR-18504-duplicate-path-templates.yml        | 10 +++++++++
 solr/api/build.gradle                              | 24 ++++++++++++++++++++++
 .../client/api/endpoint/ClusterFileStoreApis.java  |  4 ++--
 .../solr/client/api/endpoint/GetSchemaApi.java     |  5 +++--
 .../solr/handler/admin/api/GetSchemaField.java     | 10 ++++-----
 5 files changed, 44 insertions(+), 9 deletions(-)

diff --git a/changelog/unreleased/SOLR-18504-duplicate-path-templates.yml b/changelog/unreleased/SOLR-18504-duplicate-path-templates.yml
new file mode 100644
index 00000000000..1e93ea2bcd9
--- /dev/null
+++ b/changelog/unreleased/SOLR-18504-duplicate-path-templates.yml
@@ -0,0 +1,10 @@
+title: >
+  The OpenAPI spec no longer describes the same URL twice: the dynamic-field and filestore-file
+  endpoints each named their path parameter differently per HTTP method, so consumers of the spec
+  saw two colliding entries, each missing the other's operations.
+type: fixed
+authors:
+  - name: Jan Høydahl
+links:
+  - name: SOLR-18504
+    url: https://issues.apache.org/jira/browse/SOLR-18504
diff --git a/solr/api/build.gradle b/solr/api/build.gradle
index fb42df27a30..58386b103f2 100644
--- a/solr/api/build.gradle
+++ b/solr/api/build.gradle
@@ -79,6 +79,30 @@ dependencies {
   swaggerBuild libs.swagger3.jaxrs2.jakarta
 }
 
+// OpenAPI treats templated paths that differ only in parameter name as the same path, so
+// such a collision silently hides operations from consumers of the spec.
+tasks.register('validateOpenApiSpec') {
+  dependsOn resolve
+  def specFile = file(project.openApiSpecFile)
+  def marker = layout.buildDirectory.file("openapi-spec-validated.txt").get().asFile
+  inputs.file(specFile)
+  outputs.file(marker)
+  doLast {
+    def collisions = new groovy.json.JsonSlurper().parse(specFile).paths.keySet()
+        .groupBy { it.replaceAll(/\{[^}]*\}/, '{}') }
+        .findAll { it.value.size() > 1 }
+    if (!collisions.isEmpty()) {
+      throw new GradleException(
+          "OpenAPI spec declares paths that differ only in path-parameter name:\n"
+              + collisions.values().flatten().collect { "  " + it }.join("\n") + "\n"
+              + "Make the @PathParam names agree so the operations merge into a single path.")
+    }
+    marker.text = "ok"
+  }
+}
+
+check.dependsOn validateOpenApiSpec
+
 // Non-Java client generation tasks below:
 task buildJSClient(type: org.openapitools.generator.gradle.plugin.tasks.GenerateTask) {
   generatorName.set("javascript")
diff --git a/solr/api/src/java/org/apache/solr/client/api/endpoint/ClusterFileStoreApis.java b/solr/api/src/java/org/apache/solr/client/api/endpoint/ClusterFileStoreApis.java
index e8dafdd4498..9effa63f791 100644
--- a/solr/api/src/java/org/apache/solr/client/api/endpoint/ClusterFileStoreApis.java
+++ b/solr/api/src/java/org/apache/solr/client/api/endpoint/ClusterFileStoreApis.java
@@ -87,10 +87,10 @@ public interface ClusterFileStoreApis {
   @Operation(
       summary = "Delete a file or directory from the filestore.",
       tags = {"file-store"})
-  @Path("/files{path:.+}")
+  @Path("/files{filePath:.+}")
   SolrJerseyResponse deleteFile(
       @Parameter(description = "Path to a file or directory within the filestore")
-          @PathParam("path")
+          @PathParam("filePath")
           String path,
       @Parameter(
               description =
diff --git a/solr/api/src/java/org/apache/solr/client/api/endpoint/GetSchemaApi.java b/solr/api/src/java/org/apache/solr/client/api/endpoint/GetSchemaApi.java
index 119bf9d19ce..a42f832b154 100644
--- a/solr/api/src/java/org/apache/solr/client/api/endpoint/GetSchemaApi.java
+++ b/solr/api/src/java/org/apache/solr/client/api/endpoint/GetSchemaApi.java
@@ -78,12 +78,13 @@ public interface GetSchemaApi {
     SchemaListDynamicFieldsResponse listDynamicFields();
 
     @GET
-    @Path("/dynamicfields/{fieldName}")
+    @Path("/dynamicfields/{dynamicFieldName}")
     @StoreApiParameters
     @Operation(
         summary = "Get detailed info about a single dynamic field",
         tags = {"schema"})
-    SchemaGetDynamicFieldInfoResponse getDynamicFieldInfo(@PathParam("fieldName") String fieldName);
+    SchemaGetDynamicFieldInfoResponse getDynamicFieldInfo(
+        @PathParam("dynamicFieldName") String dynamicFieldName);
 
     @GET
     @Path("/fieldtypes")
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/GetSchemaField.java b/solr/core/src/java/org/apache/solr/handler/admin/api/GetSchemaField.java
index 70fc868bee5..e19b08fc325 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/api/GetSchemaField.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/api/GetSchemaField.java
@@ -49,7 +49,7 @@ import org.apache.solr.security.PermissionNameProvider;
  *   <li>/fields/{fieldName}
  *   <li>/copyfields
  *   <li>/dynamicfields
- *   <li>/dynamicfields/{fieldName}
+ *   <li>/dynamicfields/{dynamicFieldName}
  *   <li>/fieldtypes
  *   <li>/fieldtypes/{fieldTypeName}
  * </ul>
@@ -122,8 +122,8 @@ public class GetSchemaField extends JerseyResource implements GetSchemaApi.Field
 
   @Override
   @PermissionName(PermissionNameProvider.Name.SCHEMA_READ_PERM)
-  public SchemaGetDynamicFieldInfoResponse getDynamicFieldInfo(String fieldName) {
-    if (fieldName == null) {
+  public SchemaGetDynamicFieldInfoResponse getDynamicFieldInfo(String dynamicFieldName) {
+    if (dynamicFieldName == null) {
       throw new SolrException(
           SolrException.ErrorCode.BAD_REQUEST, "Dynamic field name must not be null");
     }
@@ -132,13 +132,13 @@ public class GetSchemaField extends JerseyResource implements GetSchemaApi.Field
     final String realName = "dynamicfields";
 
     SimpleOrderedMap<Object> dynamicFieldInfo =
-        retrieveFieldInfoOfType(realName, fieldName, params);
+        retrieveFieldInfoOfType(realName, dynamicFieldName, params);
     if (dynamicFieldInfo != null) {
       response.dynamicFieldInfo = dynamicFieldInfo;
       return response;
     }
     throw new SolrException(
-        SolrException.ErrorCode.NOT_FOUND, "No such dynamic field [" + fieldName + "]");
+        SolrException.ErrorCode.NOT_FOUND, "No such dynamic field [" + dynamicFieldName + "]");
   }
 
   @Override