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