Microprofile ExampleObject externalvalue – Quarkus

The annotation ExampleObject is used to illustrate a particular Content for Request and Response. The documentation states that we can use a URL externalValue for referencing an external JSON file.

The json files were located under src\main\resources\META-INF\resources

    @GET
    @Path("/user")
    @Produces(MediaType.TEXT_PLAIN)
    @APIResponses(
            value = {
                    @APIResponse(responseCode = "200", content =  @Content(
                            mediaType = "*/*",
                            examples = {
                                    @ExampleObject(name = "User",
                                            summary = "User Example Summary",
                                            externalValue = "http://localhost:8080/user.json")
                            }

                    ))
            }
    )

However that still does not work, and Swagger UI will be empty:


To get around this, we had to implement a custom OASFilter which allows us to modify the openAPI tree, and add the external JSON files.

package org.acme;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.eclipse.microprofile.openapi.OASFactory;
import org.eclipse.microprofile.openapi.OASFilter;
import org.eclipse.microprofile.openapi.models.Components;
import org.eclipse.microprofile.openapi.models.OpenAPI;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.Map;

import org.eclipse.microprofile.openapi.models.examples.Example;

import io.quarkus.logging.Log;

public class CustomOASFilter implements OASFilter {

    ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public void filterOpenAPI(OpenAPI openAPI) {

        //openApi.getComponents() will result in NULL as we don't have any openapi.yaml file.
        Components defaultComponents = OASFactory.createComponents();
        if (openAPI.getComponents() == null) {
            openAPI.setComponents(defaultComponents);
        }

        generateExamples().forEach(openAPI.getComponents()::addExample);
    }

    Map<String, Example> generateExamples() {

        Map<String, Example> examples = new LinkedHashMap<>();

        try {

            ClassLoader loader = Thread.currentThread().getContextClassLoader();

            String userJSON = new String(loader.getResourceAsStream("user.json").readAllBytes(), StandardCharsets.UTF_8);
            String customerJson = new String(loader.getResourceAsStream("customer.json").readAllBytes(), StandardCharsets.UTF_8);

            Example userExample = OASFactory.createExample()
                                            .description("User JSON Example Description")
                                            .value(objectMapper.readValue(userJSON, ObjectNode.class));

            Example customerExample = OASFactory.createExample()
                                                .description("Customer JSON Example Description")
                                                .value(objectMapper.readValue(customerJson, ObjectNode.class));

            examples.put("userExample", userExample);
            examples.put("customerExample", customerExample);

        } catch (IOException ioException) {
            Log.error(ioException);
        }
        return examples;
    }

}

Update application.properties to use this custom filter:

mp.openapi.filter=org.acme.CustomOASFilter

Update the Controller @ExampleObject annotation to reference the previously created examples.

package org.acme;

import org.eclipse.microprofile.openapi.annotations.media.Content;
import org.eclipse.microprofile.openapi.annotations.media.ExampleObject;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/hello")
public class GreetingResource {

    @GET
    @Path("/user")
    @Produces(MediaType.TEXT_PLAIN)
    @APIResponses(
            value = {
                    @APIResponse(responseCode = "200", content =  @Content(
                            mediaType = "*/*",
                            examples = {
                                    @ExampleObject(name = "User",
                                            summary = "User Example Summary",
                                            ref = "userExample")
                            }

                    ))
            }
    )
    public String helloUser() {
        return "Hello User";
    }



    @GET
    @Path("/customer")
    @Produces(MediaType.TEXT_PLAIN)
    @APIResponses(
            value = {
                    @APIResponse(responseCode = "200", content =  @Content(
                            mediaType = "*/*",
                            examples = {
                                    @ExampleObject(name = "Customer",
                                            summary = "Customer Example Summary",
                                            ref = "customerExample")
                            }

                    ))
            }
    )
    public String helloCustomer() {
        return "Hello Customer";
    }
}

Then Swagger UI will showcase the proper JSON content for each response:

The code for this is located in github.

Leave a comment