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

Ordering of GlobalOpenApiCustomizers different than for OpenApiCustomisers #2107

Closed
westse opened this issue Mar 7, 2023 · 0 comments
Closed

Comments

@westse
Copy link
Contributor

westse commented Mar 7, 2023

Describe the bug

The order of injection and execution of user-defined OpenApiCustomiser beans appears to be controllable via the standard Spring means (annotate with @Order or implement org.springframework.core.Ordered). However, this is not true of GlobalOpenApiCustomizer beans which appear to be injected/executed in the order they happened to be defined in the backend configuration. It is particularly confusing when there are multiple beans that implement both interfaces - the order of execution changes depending on whether the api-docs URL used is grouped vs non-grouped.

To Reproduce
Steps to reproduce the behavior:

  • Using spring-boot 2.7.6
  • Using springdoc-openapi 1.6.14.
  • Run the below Spring Boot app
  • Open http://localhost:8080/v3/api-docs in a browser
  • Observe the console output and notice that customizers are executed in order 1, 2, 3.
  • Open http://localhost:8080/v3/api-docs/mygroup in a browser
  • Observe the console output and notice that customizers are executed in order 3, 2, 1.
package order;

import io.swagger.v3.oas.models.OpenAPI;
import org.springdoc.core.customizers.GlobalOpenApiCustomizer;
import org.springdoc.core.customizers.OpenApiCustomiser;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Import;
import org.springframework.core.annotation.Order;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@Import({
        OrderDemo.Customizer3.class,
        OrderDemo.Customizer2.class,
        OrderDemo.Customizer1.class,
})
public class OrderDemo {

    @Order(3)
    public static class Customizer3 implements OpenApiCustomiser, GlobalOpenApiCustomizer {
        public void customise(OpenAPI openApi) {
            System.out.println("-> 3");
        }
    }

    @Order(2)
    public static class Customizer2 implements OpenApiCustomiser, GlobalOpenApiCustomizer {
        public void customise(OpenAPI openApi) {
            System.out.println("-> 2");
        }
    }

    @Order(1)
    public static class Customizer1 implements OpenApiCustomiser, GlobalOpenApiCustomizer {
        public void customise(OpenAPI openApi) {
            System.out.println("-> 1");
        }
    }

    @RestController
    public static class MyController {

        @GetMapping("/test")
        public String testingMethod() {
            return "foo";
        }
    }

    public static void main(String[] args) {
        // Ensure at least one group is configured
        System.setProperty("springdoc.group-configs[0].group", "mygroup");
        System.setProperty("springdoc.group-configs[0].paths-to-match", "/test");
        
        SpringApplication.run(OrderDemo.class, args);
    }
}

Expected behavior

  • The ordering behavior should be the same for GlobalOpenApiCustomizer and OpenApiCustomiser beans.
  • Ideally ordering would be controllable via standard Spring means (e.g. @Order).

Additional Context
I believe this is due to the fact that MultipleOpenApiResource.afterPropertiesSet() (only used for group-configs?) obtains its list of GlobalOpenApiCustomizers via ApplicationContext.getBeansOfType(Class), which is documented to return beans in the "order of the backend configuration", whereas the OpenApiWebResource (used for non-grouped) is autowired and therefore is controllable via standard means (e.g @Order).

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

1 participant