Search code examples
swaggeropenapiquarkusmicroprofilesmallrye

Smallrye open api interceptor


I am developing a rest application.

Some endpoints require a custom header parameter, not related to authorisation. I created a custom annotation using jax-rs NameBinding. Here is an usage example:

@GET
@RequiresBankHeader
public int get(
        @HeaderParam("bank")
        @Parameter(ref = "#/components/parameters/banks")
                String bank) {        
    return someService.getSomeInformation();
}

There is a provider that intercepts this call and do some routine using the information in the header parameter.

The problem is that I have to repeat '@HeaderParam("bank") @Parameter(ref = "#/components/parameters/banks") String bank' everywhere, just so it appears in Swagger, even though the service classes do not need it. I was able to at least reuse the parameter definition with ref = "#/components/parameters/banks", and declaring it in the OpenAPI.yml file, that Quarkus merges with generated code very nicely.

But I also want to create and interceptor to dynamically add this do the OpenApi definition whenever RequiresBankHeader annotation is present.

Is there a way to do it?


Solution

  • As mentioned by Roberto Cortez, the MP OpenAPI spec provides a programmatic way to contribute metadata to the openapi.yml file.

    It is not possible to detect an annotation in the JAX-RS endpoint definition, but it was good enough to automate what I needed. Since all methods that had the RequiresBankHeader return the same Schema, I was able to hack it like this:

    public class OpenApiConfigurator implements OASFilter {
    
    @Override
    public Operation filterOperation(Operation operation) {
        operation.getResponses().getAPIResponses().values().stream().
                map(APIResponse::getContent).
                filter(Objects::nonNull).
                map(Content::getMediaTypes).
                flatMap(mediaTypes -> mediaTypes.values().stream()).
                map(MediaType::getSchema).
                filter(Objects::nonNull).
                map(Schema::getRef).
                filter(Objects::nonNull).
                filter(ref -> ref.contains("the common response schema")).
                findAny().
                ifPresent(schema -> {
                    ParameterImpl parameter = new ParameterImpl();
                    parameter.setRef("#/components/parameters/banks");
                    operation.addParameter(parameter);
                });
        return operation;
    }
    

    OpenApiConfigurator should be configure in the application properties, using mp.openapi.filter=com.yourcompany.OpenApiConfigurator