Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

openapi-swaggerui: schema reflection thinks javax.ws.rs.core.* types are REST Body types. #30621

Closed
NfNitLoop opened this issue Jan 26, 2023 · 6 comments · Fixed by #31152
Closed

Comments

@NfNitLoop
Copy link

NfNitLoop commented Jan 26, 2023

Describe the bug

https://quarkus.io/guides/resteasy-reactive#accessing-context-objects documents:

There are a number of contextual objects that the framework will give you, if your endpoint method takes parameters of the following type:

[a table with several types from javax.ws.rs.core.*, including HttpHeaders, and SecurityContext]

And if I add those types to my GET method, they work as expected w.r.t. resteasy-reactive:

    @GET
    @Path("/whatever/{example}")
    @Produces(MediaType.APPLICATION_JSON)
    fun getWhateverExample(
        @PathParam("example") example: String,
        headers: HttpHeaders,
    ): MyResponseType {
        // works, I get a `headers` param that I can grab HTTP headers. from. 👍 
    }

However, openapi-swaggerui interprets those objects as if they represent a JSON body to be passed to the REST endpoint. It inspects the type (here: HttpHeaders) and exposes it as a schema for the type of the body.

This is present in both the schema generated at /q/openapi and the UI at /q/swagger-ui (which I assume is just driven by the schema.)

This happens even if the endpoint is a @GET endpoint, which can't accept a body.

Expected behavior

I should be able to add the types documented at https://quarkus.io/guides/resteasy-reactive#accessing-context-objects to my method parameters list without having them show up in the generated openapi schema, or in the swagger-ui.

And they definitely shouldn't be interpreted as body types for @GET requests.

Actual behavior

The javax.ws.rs.core.* types get interpreted as a request body, even for HTTP methods that can't accept a body.

Here's what it looks like when I add HttpHeaders, but I see similar behavior with SecurityContext as well.

Screenshot 2023-01-25 at 6 22 26 PM

The full schema seems to be an attempt at reflecting the HttpHeaders objects as if it were a JSON model:

{
  "requestHeaders": {
    "additionalProp1": [
      "string"
    ],
    "additionalProp2": [
      "string"
    ],
    "additionalProp3": [
      "string"
    ]
  },
  "acceptableMediaTypes": [
    {
      "type": "string",
      "subtype": "string",
      "parameters": {
        "additionalProp1": "string",
        "additionalProp2": "string",
        "additionalProp3": "string"
      },
      "wildcardType": true,
      "wildcardSubtype": true
    }
  ],
  "acceptableLanguages": [
    {
      "language": "string",
      "script": "string",
      "country": "string",
      "variant": "string",
      "extensionKeys": [
        "string"
      ],
      "unicodeLocaleAttributes": [
        "string"
      ],
      "unicodeLocaleKeys": [
        "string"
      ],
      "iSO3Language": "string",
      "iSO3Country": "string",
      "displayLanguage": "string",
      "displayScript": "string",
      "displayCountry": "string",
      "displayVariant": "string",
      "displayName": "string"
    }
  ],
  "mediaType": {
    "type": "string",
    "subtype": "string",
    "parameters": {
      "additionalProp1": "string",
      "additionalProp2": "string",
      "additionalProp3": "string"
    },
    "wildcardType": true,
    "wildcardSubtype": true
  },
  "language": {
    "language": "string",
    "script": "string",
    "country": "string",
    "variant": "string",
    "extensionKeys": [
      "string"
    ],
    "unicodeLocaleAttributes": [
      "string"
    ],
    "unicodeLocaleKeys": [
      "string"
    ],
    "iSO3Language": "string",
    "iSO3Country": "string",
    "displayLanguage": "string",
    "displayScript": "string",
    "displayCountry": "string",
    "displayVariant": "string",
    "displayName": "string"
  },
  "cookies": {
    "additionalProp1": {
      "name": "string",
      "value": "string",
      "version": 0,
      "path": "string",
      "domain": "string"
    },
    "additionalProp2": {
      "name": "string",
      "value": "string",
      "version": 0,
      "path": "string",
      "domain": "string"
    },
    "additionalProp3": {
      "name": "string",
      "value": "string",
      "version": 0,
      "path": "string",
      "domain": "string"
    }
  },
  "date": "2022-03-10",
  "length": 0
}

How to Reproduce?

Just follow the example in https://quarkus.io/guides/resteasy-reactive#accessing-context-objects, then load up /q/swagger-ui and you'll see it.

Output of uname -a or ver

22.2.0 Darwin Kernel Version 22.2.0: Fri Nov 11 02:08:47 PST 2022; root:xnu-8792.61.2~4/RELEASE_X86_64 x86_64

Output of java -version

openjdk version "19.0.1" 2022-10-18

GraalVM version (if different from Java)

OpenJDK Runtime Environment GraalVM CE 22.3.0 (build 19.0.1+10-jvmci-22.3-b08)

Quarkus version or git rev

2.15.3.Final

Build tool (ie. output of mvnw --version or gradlew --version)

Gradle 7.6

Additional information

No response

@quarkus-bot
Copy link

quarkus-bot bot commented Jan 26, 2023

/cc @EricWittmann (openapi), @MikeEdgar (openapi,swagger-ui), @evanchooly (kotlin), @geoand (kotlin), @phillip-kruger (openapi,swagger-ui)

@NfNitLoop
Copy link
Author

Sorry for the kotlin pings. I used a kotlin code block in my sample code. But I suspect this is is just a mismatch between resteasy-reactive and the swagger schema generation code.

But while I've got developer eyes -- is there a way to work around this in the meantime? I'd been using @HeaderParam, but that makes the header parameter part of my public API and I don't want it to be. I was hoping to find a @Hidden or @NonApi annotation to remove/hide parameters from the schema that gets created.

@Postremus
Copy link
Member

@NfNitLoop
You can use something like this to hide the parameter from swagger-ui:

@javax.ws.rs.HeaderParam("name")
@org.eclipse.microprofile.openapi.annotations.parameters.Parameter(hidden=true)
String name

@MikeEdgar
Copy link
Contributor

@NfNitLoop this indeed appears to be specific to Kotlin. Can you provide a full Kotlin class that is affected by this? I haven't had any luck coming up with my own using the method you provided in the original message.

@NfNitLoop
Copy link
Author

NfNitLoop commented Jan 26, 2023

Huh, I didn't think I'd done anything special in my case. I'll try to create a minimal reproduction later today.

Brainstorming (in case someone is impatient and wants to try before I can), this is a small "legacy" codebase that used to be in Java (and not Quarkus), so there's one Java class for old methods, and a new Kotlin class for additions I'm making. Possibly an issue w/ mixed Java/Kotlin environments?

@NfNitLoop
Copy link
Author

Was pretty easy to reproduce.

quarkus create app rest-repro --gradle-kotlin-dsl --kotlin -xresteasy-reactive,smallrye-openapi

Update GreetingResource.kt to:

package org.acme

import javax.ws.rs.GET
import javax.ws.rs.Path
import javax.ws.rs.PathParam
import javax.ws.rs.Produces
import javax.ws.rs.core.HttpHeaders
import javax.ws.rs.core.MediaType

@Path("/hello")
class GreetingResource {

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    fun hello() = "Hello from RESTEasy Reactive"

    @GET
    @Path("/{name}")
    @Produces(MediaType.TEXT_PLAIN)
    fun greet(
        @PathParam("name") name: String,
        // The addition of this line causes the bug:
        headers: HttpHeaders,
    ) =
        "Hello, $name!"
}

Here's a zip that reproduces the issue:

rest-repro.zip

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants