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

Inherited Methods Not Included in Swagger Documentation with @RouterOperation in Spring Boot WebFlux Application #2525

Closed
oms12 opened this issue Mar 12, 2024 · 3 comments
Labels
incomplete incomplete description: Make sure you Provide a Minimal, Reproducible Example - with HelloController

Comments

@oms12
Copy link

oms12 commented Mar 12, 2024

Describe the bug

When using @RouterOperation to document API endpoints in Spring Boot Webflux applications with springdoc-openapi, the Swagger does not include methods inherited from parent classes. This occurs because internally, AopUtils.getTargetClass(handlerBean).getDeclaredMethods() is used, which only retrieves declared methods, not methods from parent classes.

Because suppose there is a method already implemented in parent class and I don't want to implement that method again in child class and the method implemented in the parentClass is my beanMethod.
Since that method is already inherited by the child class it should be child class function also.

To Reproduce

  1. Specify @RouterOperation for an endpoint in a Spring Boot application.
  2. Define a method in a subclass that overrides a method from a parent class and parent class method is the beanMethod.
  3. Check the generated Swagger documentation.

Spring Boot version: 3
springdoc-openapi module version:

        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-starter-webflux-ui</artifactId>
            <version>2.3.0</version>
        </dependency>

Expected behavior

Swagger documentation should include methods inherited from parent classes when using @RouterOperation.

Screenshots
image

Additional context
Code where beanClass and beanMethod is used.
@RouterOperation(path = "v1/payment", method = RequestMethod.POST, beanClass = PaymentHandler.class, beanMethod = "handle", operation = @Operation(description = "Say hello GET", operationId = "lowercaseGET", tags = "persons"))

@oms12 oms12 changed the title Inherited Methods Not Included in Swagger Documentation with @RouterOperation in Spring Boot Application Inherited Methods Not Included in Swagger Documentation with @RouterOperation in Spring Boot WebFlux Application Mar 12, 2024
@bnasslahsen
Copy link
Contributor

bnasslahsen commented Mar 12, 2024

@oms12,

Try adding a complete reproducible sample, so we can confirm the fix.
Feel free to provide a Minimal, Reproducible Example - with HelloController that reproduces the problem.

This ticket will be closed, but can be reopened if your provide the reproducible sample.

@bnasslahsen bnasslahsen added the incomplete incomplete description: Make sure you Provide a Minimal, Reproducible Example - with HelloController label Mar 12, 2024
@oms12
Copy link
Author

oms12 commented Mar 13, 2024

Sure, I will add the one simple code to reproduce the issue.

@oms12
Copy link
Author

oms12 commented Mar 14, 2024

Hi @bnasslahsen
To Reproduce the issue
BaseHandler Class

package com.example.swagger.Handler;

import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

@Component
public abstract class BaseHandler {
    protected abstract Mono<ServerResponse> apply (ServerRequest serverRequest);

    public Mono<ServerResponse> handle(ServerRequest serverRequest) {
        return this.apply(serverRequest);
    }
}

GetNameHandlerClass

package com.example.swagger.Handler;

import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

@Component
public class GetNameHandler extends BaseHandler{
    @Override
    protected Mono<ServerResponse> apply(ServerRequest serverRequest) {
        return ServerResponse.ok()
                .contentType(MediaType.APPLICATION_JSON)
                .bodyValue("Name API is called");
    }

    // Since handle function is present in the BaseHandler class so it is not able to detect but it should

    // Uncommenting below code will work as expected since now explicitly handle is declared here

//    @Override
//    public Mono<ServerResponse> handle(ServerRequest serverRequest) {
//        return super.handle(serverRequest);
//    }
}

Router Class I am using Functional Endpoint

package com.example.swagger.Router;

import com.example.swagger.Handler.GetNameHandler;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import org.springdoc.core.annotations.RouterOperation;
import org.springdoc.core.annotations.RouterOperations;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;


@Configuration
public class GetNameRouter {

    @Bean
    @RouterOperations(
            value = {
                    @RouterOperation(path = "/v1/name", produces = {MediaType.APPLICATION_JSON_VALUE}, method = RequestMethod.GET,
                            beanClass = GetNameHandler.class, beanMethod = "handle", operation = @Operation(operationId = "getName",
                            description = "get name", responses = {@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = String.class)))})
                    )
            })

    public RouterFunction<ServerResponse> routerFunction(GetNameHandler getNameHandler) {
        return RouterFunctions.route(RequestPredicates.GET("/v1/name"), getNameHandler::handle);
    }

}

In the GetNameHandler class I am commenting and uncommenting the code, I am getting the results as below.
Hitting this url - http://localhost:8080/v1/swagger/swagger-ui.html

This is the result of keeping the code commented.
image

This is the result of uncommented code.
image

Below is repo link of the simple Spring-Boot working code of above example.
https://github.com/oms12/Swagger

Please clone and check the results I mentioned above.

Please let me know if anything wrong I am doing or any clarification regarding my code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
incomplete incomplete description: Make sure you Provide a Minimal, Reproducible Example - with HelloController
Projects
None yet
Development

No branches or pull requests

2 participants