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 a2f43aadfd2 SOLR-18504: Merge duplicate OpenAPI path templates (#5024)
a2f43aadfd2 is described below

commit a2f43aadfd212f6d9f05cdcdb5bd27b11fac81b1
Author: Jan Høydahl <[email protected]>
AuthorDate: Wed Oct 7 14:39:27 2026 +0200

    SOLR-18504: Merge duplicate OpenAPI path templates (#5024)
---
 .../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

Reply via email to