BATMAN_2008
BATMAN_2008

Reputation: 3540

OpenApi how to add example from resources file for @RequestBody -> @Content -> @Schema -> example

I am developing a service-based application for which I am adding openapi based annotations such as @RequestBody, @Parameter, @Schema within the @Schema I have an example field for which I can provide the example template in String format.

I have provided the example JSON string but the JSON content is huge so I would like to add it from the file present in my resources folder. But I am currently unable to load it. Can someone please let me know how can I add the example content from the file rather than String?

I tried looking and found that there is a field externalValue but I am unable to understand how to make it work. Following is the link to the documentation.

Following is the code I have which is working perfectly fine:

@Path("/generate")
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@RequestBody(description = "InputTemplate body",
        content = @Content(schema = @Schema(implementation = InputTemplate.class, example = "{\n" +
                "  \"names\":[\n" +
                "    \"Batman\",\n" +
                "    \"Superman\",\n" +
                "    \"Ironman\"\n" +
                "  ],\n" +
                "  \"jobs\":[\n" +
                "    \"Fighting\",\n" +
                "    \"Fyling\",\n" +
                "    \"Teching\"\n" +
                "  ]\n" +
                "}")))
public Multi<String> generate(final Map<String, Object> input) throws CustomException {
        
}

I would like to replace the JSON contents present example with the contents from the external file which is present in my resources folder.

After trying many things I got to know that I need to use @ExampleObject but if I add the respective annotations and try to open my Swagger UI then I am not getting the contents of the file that I have added. Rather it provides me with the data from InputTemplate.class.

Following is the modified code:

@RequestBody(description = "InputTemplate body",
        content = @Content(schema = @Schema(implementation = InputTemplate.class), examples = {
                @ExampleObject(name = "Example-1",
                        description = "Example-1 for InputTemplate.",
                        ref = "#/resources/Example1.json"), externalValue = "#/resources/Example2.json"
                @ExampleObject(name = "Example-2",
                        description = "Example-2 for InputTemplate.",
                        ref = "#/resources/Example1.json") //externalValue = "#/resources/Example1.json"
        }))

I tried to look into a similar question but the provided response does not work for me:

  1. How to refrence files in SpringDoc OpenAPI3?
  2. https://github.com/springdoc/springdoc-openapi/issues/1432
  3. https://github.com/springdoc/springdoc-openapi/issues/17

Upvotes: 9

Views: 5500

Answers (1)

lees2bytes
lees2bytes

Reputation: 406

As best as I can tell, the ref value seems to be expecting a url where a schema could be found? I saw someone recommend making an endpoint to return examples? This seems a bit much to me...

I decided the easiest thing to do was just add something to pull examples from the files and insert them into the OpenApi object.

I implemented an OpenApiCustomiser in my spring config, this allows me to point to files in the apps resources folder for Response examples.

I annotate the Controller method like this:

@ApiResponses(value = {
    @ApiResponse(responseCode = "200",
        content = { @Content(mediaType = "application/json", 
        schema = @Schema(implementation = SomeResponse.class, 
                  name = "YourResponse"),  
                  examples = {@ExampleObject(value = "@your_data_200_response.json")}) }) 
    })

To get the above to work you then add the following OpenApiCustomiser config bean:

@Bean
public OpenApiCustomiser applyStandardOpenAPIModifications() {
    return openApi -> {
        Paths paths = new Paths();
        openApi.getPaths().entrySet().stream()
                .sorted(Map.Entry.comparingByKey())
                .forEach(stringPathItemEntry -> {
                    paths.addPathItem(stringPathItemEntry.getKey(), addExamples(stringPathItemEntry.getValue()));
                });
        openApi.setPaths(paths);
    };
}

private PathItem addExamples(PathItem pathItem) {
    if(pathItem.getPost() !=null)  {
        //Note you can also Do this to APIResponses to insert info from a file into examples in say, a 200 response.
            pathItem.getPost().getRequestBody().getContent().values().stream()
                    .forEach(c ->{
                        String fileName = c.getExample().toString().replaceFirst("@","");
                        ObjectNode node = null;
                        try {
                            //load file from where you want. also don't insert is as a string, it wont format properly
                            node = (ObjectNode) new ObjectMapper().readTree(methodToReadInFileToString(fileName)); 
                        } catch (JsonProcessingException e) {
                            throw new RuntimeException(e);
                        }
                        c.setExample(node);
                    }
            );
    }
    return pathItem;
}

I just load the files from a folder places in /resources which contains the .json files.

Upvotes: 3

Related Questions