diff --git a/implementation/src/main/java/io/smallrye/openapi/runtime/scanner/ParameterProcessor.java b/implementation/src/main/java/io/smallrye/openapi/runtime/scanner/ParameterProcessor.java index 745f319d1..bae2c95f8 100644 --- a/implementation/src/main/java/io/smallrye/openapi/runtime/scanner/ParameterProcessor.java +++ b/implementation/src/main/java/io/smallrye/openapi/runtime/scanner/ParameterProcessor.java @@ -1398,15 +1398,17 @@ ParameterContext getParameterContext(ParameterContextKey key, AnnotationTarget t } boolean haveSameAnnotatedTarget(ParameterContext context, AnnotationTarget target, String name) { - boolean nameMatches = Objects.equals(context.name, name); + /* + * Consider names to match if one is unspecified or they are equal. + */ + boolean nameMatches = (context.name == null || name == null || Objects.equals(context.name, name)); if (target.equals(context.target)) { /* - * This logic formerly also required that the parameter names matched - * (nameMatches == true) or that the kind of the target was not a - * method. + * The name must match for annotations on a method because it is + * ambiguous which parameters is being referenced. */ - return true; + return nameMatches || target.kind() != Kind.METHOD; } if (nameMatches && target.kind() == Kind.METHOD && context.target.kind() == Kind.METHOD_PARAMETER) { diff --git a/implementation/src/test/java/io/smallrye/openapi/runtime/scanner/ResourceParameterTests.java b/implementation/src/test/java/io/smallrye/openapi/runtime/scanner/ResourceParameterTests.java index 612fbe60e..7d380ff68 100644 --- a/implementation/src/test/java/io/smallrye/openapi/runtime/scanner/ResourceParameterTests.java +++ b/implementation/src/test/java/io/smallrye/openapi/runtime/scanner/ResourceParameterTests.java @@ -21,9 +21,13 @@ import java.time.OffsetTime; import java.time.ZoneId; import java.time.ZoneOffset; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Set; +import javax.enterprise.context.RequestScoped; import javax.ws.rs.BeanParam; import javax.ws.rs.Consumes; import javax.ws.rs.CookieParam; @@ -40,9 +44,13 @@ import javax.ws.rs.core.Response; import org.eclipse.microprofile.openapi.annotations.Operation; +import org.eclipse.microprofile.openapi.annotations.enums.ParameterIn; import org.eclipse.microprofile.openapi.annotations.enums.SchemaType; +import org.eclipse.microprofile.openapi.annotations.headers.Header; import org.eclipse.microprofile.openapi.annotations.media.Content; import org.eclipse.microprofile.openapi.annotations.media.Schema; +import org.eclipse.microprofile.openapi.annotations.parameters.Parameter; +import org.eclipse.microprofile.openapi.annotations.parameters.Parameters; import org.eclipse.microprofile.openapi.annotations.parameters.RequestBody; import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; import org.eclipse.microprofile.openapi.annotations.responses.APIResponses; @@ -492,4 +500,78 @@ public Response getWithBeanParams(@BeanParam BeanParamImpl params) { return null; } } + + /*************************************************************************/ + /* + * Test case derived from original example in SmallRye OpenAPI issue #330. + * + * https://github.com/smallrye/smallrye-open-api/issues/330 + * + */ + + @Test + public void testMethodTargetParametersWithoutJAXRS() throws IOException, JSONException { + Index i = indexOf(MethodTargetParametersResource.class, + MethodTargetParametersResource.PagedResponse.class); + OpenApiAnnotationScanner scanner = new OpenApiAnnotationScanner(emptyConfig(), i); + OpenAPI result = scanner.scan(); + printToConsole(result); + assertJsonEquals("params.method-target-nojaxrs.json", result); + } + + @Path("/policies") + @Produces("application/json") + @Consumes("application/json") + @RequestScoped + static class MethodTargetParametersResource { + static class PagedResponse { + public Map meta = new HashMap<>(1); + public Map links = new HashMap<>(3); + public List data = new ArrayList<>(); + } + + @Operation(summary = "Return all policies for a given account") + @GET + @Path("/") + @Parameters({ + @Parameter(name = "offset", in = ParameterIn.QUERY, description = "Page number, starts 0, if not specified uses 0.", schema = @Schema(type = SchemaType.INTEGER)), + @Parameter(name = "limit", in = ParameterIn.QUERY, description = "Number of items per page, if not specified uses 10. " + + "NO_LIMIT can be used to specify an unlimited page, when specified it ignores the offset", schema = @Schema(type = SchemaType.INTEGER)), + @Parameter(name = "sortColumn", in = ParameterIn.QUERY, description = "Column to sort the results by", schema = @Schema(type = SchemaType.STRING, enumeration = { + "name", + "description", + "is_enabled", + "mtime" + })), + @Parameter(name = "sortDirection", in = ParameterIn.QUERY, description = "Sort direction used", schema = @Schema(type = SchemaType.STRING, enumeration = { + "asc", + "desc" + })), + @Parameter(name = "filter[name]", in = ParameterIn.QUERY, description = "Filtering policies by the name depending on the Filter operator used.", schema = @Schema(type = SchemaType.STRING)), + @Parameter(name = "filter:op[name]", in = ParameterIn.QUERY, description = "Operations used with the filter", schema = @Schema(type = SchemaType.STRING, enumeration = { + "equal", + "like", + "ilike", + "not_equal" + }, defaultValue = "equal")), + @Parameter(name = "filter[description]", in = ParameterIn.QUERY, description = "Filtering policies by the description depending on the Filter operator used.", schema = @Schema(type = SchemaType.STRING)), + @Parameter(name = "filter:op[description]", in = ParameterIn.QUERY, description = "Operations used with the filter", schema = @Schema(type = SchemaType.STRING, enumeration = { + "equal", + "like", + "ilike", + "not_equal" + }, defaultValue = "equal")), + @Parameter(name = "filter[is_enabled]", in = ParameterIn.QUERY, description = "Filtering policies by the is_enabled field." + + + "Defaults to true if no operand is given.", schema = @Schema(type = SchemaType.STRING, defaultValue = "true", enumeration = { + "true", "false" })), + }) + @APIResponse(responseCode = "400", description = "Bad parameter for sorting was passed") + @APIResponse(responseCode = "404", description = "No policies found for customer") + @APIResponse(responseCode = "403", description = "Individual permissions missing to complete action") + @APIResponse(responseCode = "200", description = "Policies found", content = @Content(schema = @Schema(implementation = PagedResponse.class)), headers = @Header(name = "TotalCount", description = "Total number of items found", schema = @Schema(type = SchemaType.INTEGER))) + public Response getPoliciesForCustomer() { + return null; + } + } } diff --git a/implementation/src/test/resources/io/smallrye/openapi/runtime/scanner/params.method-target-nojaxrs.json b/implementation/src/test/resources/io/smallrye/openapi/runtime/scanner/params.method-target-nojaxrs.json new file mode 100644 index 000000000..394d129ef --- /dev/null +++ b/implementation/src/test/resources/io/smallrye/openapi/runtime/scanner/params.method-target-nojaxrs.json @@ -0,0 +1,171 @@ +{ + "openapi": "3.0.1", + "paths": { + "/policies": { + "get": { + "summary": "Return all policies for a given account", + "parameters": [ + { + "name": "filter:op[description]", + "in": "query", + "description": "Operations used with the filter", + "schema": { + "default": "equal", + "enum": [ + "equal", + "like", + "ilike", + "not_equal" + ], + "type": "string" + } + }, + { + "name": "filter:op[name]", + "in": "query", + "description": "Operations used with the filter", + "schema": { + "default": "equal", + "enum": [ + "equal", + "like", + "ilike", + "not_equal" + ], + "type": "string" + } + }, + { + "name": "filter[description]", + "in": "query", + "description": "Filtering policies by the description depending on the Filter operator used.", + "schema": { + "type": "string" + } + }, + { + "name": "filter[is_enabled]", + "in": "query", + "description": "Filtering policies by the is_enabled field.Defaults to true if no operand is given.", + "schema": { + "default": "true", + "enum": [ + "true", + "false" + ], + "type": "string" + } + }, + { + "name": "filter[name]", + "in": "query", + "description": "Filtering policies by the name depending on the Filter operator used.", + "schema": { + "type": "string" + } + }, + { + "name": "limit", + "in": "query", + "description": "Number of items per page, if not specified uses 10. NO_LIMIT can be used to specify an unlimited page, when specified it ignores the offset", + "schema": { + "type": "integer" + } + }, + { + "name": "offset", + "in": "query", + "description": "Page number, starts 0, if not specified uses 0.", + "schema": { + "type": "integer" + } + }, + { + "name": "sortColumn", + "in": "query", + "description": "Column to sort the results by", + "schema": { + "enum": [ + "name", + "description", + "is_enabled", + "mtime" + ], + "type": "string" + } + }, + { + "name": "sortDirection", + "in": "query", + "description": "Sort direction used", + "schema": { + "enum": [ + "asc", + "desc" + ], + "type": "string" + } + } + ], + "responses": { + "400": { + "description": "Bad parameter for sorting was passed" + }, + "404": { + "description": "No policies found for customer" + }, + "403": { + "description": "Individual permissions missing to complete action" + }, + "200": { + "description": "Policies found", + "headers": { + "TotalCount": { + "description": "Total number of items found", + "style": "simple", + "schema": { + "type": "integer" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedResponse" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "PagedResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "object" + } + }, + "links": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "meta": { + "type": "object", + "additionalProperties": { + "format": "int64", + "type": "integer" + } + } + } + } + } + } +}