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

Adding class level @ApiResponse removes default response, and results in NullPointerException when adding an ApiResponse with useReturnTypeSchema #4483

Closed
sgdesmet opened this issue Sep 27, 2023 · 1 comment

Comments

@sgdesmet
Copy link

When adding (an array of) @ApiResponse at the class level in order to define common error responses, the default responses for the methods from that class disappear from the generated schema (the ones that contain the response based on the method's return type).

For example:

@Tag(
        name = "Dummy",
        description = "Dummy resource for testing setup"
)
@Path("test")
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
@ApiResponses({
        @ApiResponse(responseCode = "401", description = "Authentication is required", content = @Content(array = @ArraySchema(schema = @Schema(implementation = LocalizedError.class)))),
})
public class TestResource {

    @GET
    @Operation(
            summary = "Dummy GET",
            description = "Dummy GET",
            security = @SecurityRequirement(name = "user_auth",
                    scopes = {
                            "user_access"
                    }))
    public Map<String, Boolean> dummy() {

        return Map.of("success", Boolean.TRUE);
    }
}

Without class level @ApiResponse:

"/rest/v1/test": {
      "get": {
        "tags": [
          "Dummy"
        ],
        "summary": "Dummy GET",
        "description": "Dummy GET",
        "operationId": "dummy_2",
        "responses": {
          "default": {
            "description": "default response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "user_auth": [
              "user_access"
            ]
          }
        ]
      }
    }

With class level @ApiResponse:

  "/rest/v1/test": {
    "get": {
      "tags": [
        "Dummy"
      ],
      "summary": "Dummy GET",
      "description": "Dummy GET",
      "operationId": "dummy_2",
      "responses": {
        "401": {
          "description": "Authentication is required",
          "content": {
            "application/json": {
              "schema": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "security": [
        {
          "user_auth": [
            "user_access"
          ]
        }
      ]
    }
  }

This may or may not be intended behaviour , but trying to add @ApiResponse(useReturnTypeSchema = true) or @ApiResponse(useReturnTypeSchema = true, responseCode = "200") to either the class, the method or the responses field of the @Operation annotation results in the following NPE:

java.lang.NullPointerException: Cannot invoke "io.swagger.v3.oas.models.responses.ApiResponse.getContent()" because "opResponse" is null
	at io.swagger.v3.jaxrs2.Reader.resolveResponseSchemaFromReturnType(Reader.java:1294)
	at io.swagger.v3.jaxrs2.Reader.parseMethod(Reader.java:1201)
	at io.swagger.v3.jaxrs2.Reader.parseMethod(Reader.java:952)
	at io.swagger.v3.jaxrs2.Reader.read(Reader.java:498)
	at io.swagger.v3.jaxrs2.Reader.read(Reader.java:196)
	at io.swagger.v3.jaxrs2.Reader.read(Reader.java:224)
	at io.swagger.v3.oas.integration.GenericOpenApiContext.read(GenericOpenApiContext.java:654)
	at io.swagger.v3.jaxrs2.integration.resources.BaseOpenApiResource.getOpenApi(BaseOpenApiResource.java:51)
	at io.swagger.v3.jaxrs2.integration.resources.OpenApiResource.getOpenApi(OpenApiResource.java:32)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory.lambda$static$0(ResourceMethodInvocationHandlerFactory.java:52)
	at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:134)
	at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:177)
	at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$ResponseOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:176)
	at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:81)
	at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:478)
	at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:400)
	at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:81)
	at org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:261)
	at org.glassfish.jersey.internal.Errors$1.call(Errors.java:248)
	at org.glassfish.jersey.internal.Errors$1.call(Errors.java:244)
	at org.glassfish.jersey.internal.Errors.process(Errors.java:292)
	at org.glassfish.jersey.internal.Errors.process(Errors.java:274)
	at org.glassfish.jersey.internal.Errors.process(Errors.java:244)
	at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:265)
	at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:240)
	at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:697)
	at org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:394)
	at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:346)
	at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:357)
	at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:311)
	at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:205)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:205)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
	at org.apache.catalina.filters.ExpiresFilter.doFilter(ExpiresFilter.java:1192)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
	at com.google.inject.servlet.FilterChainInvocation.doFilter(FilterChainInvocation.java:89)
	at com.google.inject.servlet.ManagedFilterPipeline.dispatch(ManagedFilterPipeline.java:121)
	at com.google.inject.servlet.GuiceFilter.doFilter(GuiceFilter.java:133)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:166)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)
	at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:676)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:341)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:391)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:894)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1740)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.base/java.lang.Thread.run(Thread.java:833)
 ]

This was observed on at least 2.2.15 and 2.2.16

@frantuma
Copy link
Member

Thanks for reporting this!

This may or may not be intended behaviour

It's indeed intended even if opinionated. a default response is only added when no response is defined with annotations (with populated content from return type, if available)

trying to add ... results in the following NPE:

The exception is indeed a bug (fixed in #4501), however the current logic ONLY generates a response given an @ApiResponse annotation if such annotations defines a description field, as such field is defined as REQUIRED in the spec.

In your case you would get the expected result (with no NPE also with latest release version) using

@ApiResponse(useReturnTypeSchema = true, responseCode = "200", description="my desc")

Closing ticket, please reopen if still experiencing issues

ndwlocatieservices added a commit to ndwnu/nls-accessibility-map that referenced this issue Jun 28, 2024
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [nu.ndw.nls:routing-map-matcher-spring-boot](https://spring.io/projects/spring-boot) ([source](https://github.com/spring-projects/spring-boot)) | compile | patch | `7.6.0` -> `7.6.1` |
| [io.swagger.core.v3:swagger-annotations](https://github.com/swagger-api/swagger-core) | compile | patch | `2.2.17` -> `2.2.19` |
| [org.openapitools:openapi-generator-maven-plugin](https://github.com/openapitools/openapi-generator) | build | minor | `7.0.1` -> `7.1.0` |
| [org.keycloak:keycloak-authz-client](http://keycloak.org) ([source](https://github.com/keycloak/keycloak)) | test | patch | `22.0.4` -> `22.0.5` |

---

### Release Notes

<details>
<summary>swagger-api/swagger-core (io.swagger.core.v3:swagger-annotations)</summary>

### [`v2.2.19`](https://github.com/swagger-api/swagger-core/releases/tag/v2.2.19): Swagger-core 2.2.19 released!

[Compare Source](swagger-api/swagger-core@v2.2.18...v2.2.19)

-   give precedence to requiredMode annotation ([#&#8203;4533](swagger-api/swagger-core#4533))
-   update dependencies ([#&#8203;4521](swagger-api/swagger-core#4521))
-   Bump org.apache.maven.plugins:maven-source-plugin from 3.2.1 to 3.3.0 ([#&#8203;4513](swagger-api/swagger-core#4513))
-   add deps update CI ([#&#8203;4509](swagger-api/swagger-core#4509))

### [`v2.2.18`](https://github.com/swagger-api/swagger-core/releases/tag/v2.2.18): Swagger-core 2.2.18 released!

[Compare Source](swagger-api/swagger-core@v2.2.17...v2.2.18)

-   refs [#&#8203;4483](swagger-api/swagger-core#4483) - fix NullPointer for [@&#8203;ApiResponse](https://github.com/ApiResponse) missing description ([#&#8203;4501](swagger-api/swagger-core#4501))
-   refs [#&#8203;4462](swagger-api/swagger-core#4462) - exclude javax.validation annotations from container values processing ([#&#8203;4500](swagger-api/swagger-core#4500))
-   Update README.md versions list ([#&#8203;4493](swagger-api/swagger-core#4493))
-   feat: add getUseFqn to TypeNameResolver ([#&#8203;4484](swagger-api/swagger-core#4484))

</details>

<details>
<summary>openapitools/openapi-generator (org.openapitools:openapi-generator-maven-plugin)</summary>

### [`v7.1.0`](https://github.com/OpenAPITools/openapi-generator/releases/tag/v7.1.0): released

[Compare Source](OpenAPITools/openapi-generator@v7.0.1...v7.1.0)

v7.1.0 is the first release [with breaking changes (with fallback)](https://github.com/OpenAPITools/openapi-generator/pulls?q=is%3Amerged+is%3Apr+milestone%3A7.1.0+label%3A%22Breaking+change+%28with+fallback%29%22+) after the major release v7.0.0 in Aug 2023.

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

No branches or pull requests

2 participants