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