0

I am developing a simple application where I would like to read the files from a remote URL and add them to the @ExampleObject. I am able to achieve this using CustomClass SchemaFileReader implements OASFilter but the only problem is that I need to manually specify the name of the file in the ref such as @ExampleObject(name = "Example1", ref = "Example1.json").

Since I am reading the URL there can be many files and I do not know the name of all the files so I need an approach where I can add the @ExampleObject dynamically directly without specifying ref. Rather it should read all the data from the examples. Can someone please specify some logic on how to achieve this?

I have posted my complete code on GitHub: https://github.com/Aravinda93/code-with-quarkus.

As of now, I have added manually 2 files to my @ExampleObject by specifying the ref but I need a dynamic approach to add all the 3 files present in the resourses/jsonfiles without providing the ref for all the files individually.

To run the application, please follow the following steps:

  1. Open the terminal for the project and run mvn compile quarkus:dev
  2. Press d in the command line this should open the Swagger-UI.
  3. Select swagger-ui from SmallRye OpenAPI
  4. Expand api/generate and there we will see only 2 files under examples. I need an approach to get all 3 field without specifying the ref for all of them.
BATMAN_2008
  • 2,788
  • 3
  • 31
  • 98
  • you cant modify the annotation data. The annotation which has element `examples` will not allow you to plugin an array of examples. It must be constants. That means you need to do `@ExampleObject, @ExampleObject, @ExampleObject`. You cant do `,examples = ExampleObject[]` – JCompetence Apr 22 '22 at 10:23
  • If you are looking for a completely dynamic approach, then you will need to define the paths dynamically, similar to how I showed you early. – JCompetence Apr 22 '22 at 10:24
  • @SMA Thanks a lot for your response. I totally agree `examples = ExampleObject[]` is not possible but is there a way we can create the customer annotation/class within which we will loop over the element in `examples` and create the `@ExampleObject` for each element in the `examples` then finally add that newly created annotation to my `RestResourceController`? or maybe inject the class. – BATMAN_2008 Apr 22 '22 at 10:46
  • https://stackoverflow.com/questions/16299717/how-to-create-an-instance-of-an-annotation – JCompetence Apr 22 '22 at 10:48
  • @SMA Thanks a lot for the response. I am looking into this answer. I will reach out to you if I need some guidance. Hope that's fine :) – BATMAN_2008 Apr 22 '22 at 11:26
  • @SMA I was able to make it work and seems like it's working fine for me. I have posted my answer for your reference :). Do let me know if there is any better way to achieve this. Also, I have one query as of now I am able to add both XML and JSON data but the problem is that while displaying in Swagger-UI the JSON part is displayed correctly but XML files are not displayed properly. It maybe because of `.getContent().getMediaType(MediaType.APPLICATION_JSON).addExample` but I am unable to pass multiple mediaType to it – BATMAN_2008 Apr 25 '22 at 11:01
  • Make your logic of generating examples smarter so you have 2 lists (jsons) and (xmls), then do `.getMediaType(json).addExample(jsonExamples) and .getMediaType(xml).addExample(xmlExamples)` – JCompetence Apr 25 '22 at 11:57
  • @SMA Thanks a lot for the response. Actuallly I tried that but I am getting some sort of error: `` when I select `application/xml` in the SwaggerUI. When I searched about it found the answer that we need to add `wrapped:true` to XML media type. But not understanding exactly where to add in my code. Issue link: https://github.com/swagger-api/swagger-ui/issues/4650#issuecomment-640815541 – BATMAN_2008 Apr 25 '22 at 13:02

1 Answers1

0

After trying some things, finally, this worked for me. Posting here as it can be useful to someone in the future:

Following is my RestControllerResponse:

import org.eclipse.microprofile.openapi.annotations.media.Content;
import org.eclipse.microprofile.openapi.annotations.parameters.RequestBody;

import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.util.Map;

@Path("/api")
public class RestControllerResponse {

    @Path("/generate")
    @POST
    @Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    @RequestBody(description = "Testing Example without ref",
            content = @Content())
    public String generator(final Map<String, Object> input) throws Exception {
        return "Hello From Generator Method";
    }
}

Following is my SchemaFileReader which has the capability to read all files and respective subfolder with files and get the file contents and add to examples, pass the required URL to the getFolderData method:

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
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 org.eclipse.microprofile.openapi.models.examples.Example;
import org.json.JSONArray;
import org.json.JSONObject;

import javax.ws.rs.core.MediaType;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

public class SchemaFileReader implements OASFilter {

    private final ObjectMapper objectMapper = new ObjectMapper();
    private final CloseableHttpClient httpClient = HttpClients.createDefault();

    @Override
    public void filterOpenAPI(OpenAPI openAPI) {

        Components defaultComponents = OASFactory.createComponents();
        if (openAPI.getComponents() == null) {
            openAPI.setComponents(defaultComponents);
        }

        try {
            //generateExamples().forEach(openAPI.getComponents()::addExample);
            generateExamples().entrySet().forEach(ex -> openAPI.getPaths().getPathItem("/api/generate").getPOST().getRequestBody().getContent().getMediaType(MediaType.APPLICATION_JSON).addExample(ex.getKey(), ex.getValue()));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    Map<String, Example> generateExamples() throws Exception {
        final Map<String, Example> examples = new LinkedHashMap<>();
        getFolderData(examples, "PLACE YOUR URL HERE");
        //getExamples(examples);
        return examples;
    }

    //If user has provided the folder then recursively loop over it to get the files and their contents
    private void getFolderData(final Map<String, Example> examples, final String inputURL) throws IOException {
        //Make the request to provided folder path and get the folder/files from it.
        final CloseableHttpResponse folderResponse = httpClient.execute(new HttpGet(inputURL));
        final String responseBody = EntityUtils.toString(folderResponse.getEntity(), StandardCharsets.UTF_8);

        //If the folder API request provides valid response and contains the list of files or folders then loop over it else its plain/text with direct contents
        if (folderResponse.getStatusLine().getStatusCode() == 200 && ContentType.get(folderResponse.getEntity()).toString().equalsIgnoreCase("application/json; charset=utf-8")) {

            final JSONArray jsonArray = new JSONArray(responseBody);

            jsonArray.forEach(item -> {
                final JSONObject obj = (JSONObject) item;

                if (obj.getString("type").equalsIgnoreCase("file")) {
                    //Make request to each file in the GitHub folder and obtain its contents
                    try {
                        final CloseableHttpResponse fileResponse = httpClient.execute(new HttpGet(obj.getString("download_url")));
                        //If the response code is 200 then add the contents to Example
                        if (fileResponse.getStatusLine().getStatusCode() == 200) {
                            final String fileResponseBody = EntityUtils.toString(fileResponse.getEntity(), StandardCharsets.UTF_8);
                            if (obj.getString("download_url").contains(".json")) {
                                examples.put(obj.getString("name"), OASFactory.createExample().value(objectMapper.readValue(fileResponseBody, ObjectNode.class)));
                            } else if (obj.getString("download_url").contains(".xml")) {
                                examples.put(obj.getString("name"), OASFactory.createExample().value(fileResponseBody));
                            }
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                } else {
                    try {
                        getFolderData(examples, obj.getString("url"));
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });

        } else if (folderResponse.getStatusLine().getStatusCode() == 200 && ContentType.get(folderResponse.getEntity()).toString().equalsIgnoreCase("text/plain; charset=utf-8")) {
            //if direct file provided then add its content
            examples.put(inputURL.substring(inputURL.lastIndexOf("/")), OASFactory.createExample().value(objectMapper.readValue(responseBody, ObjectNode.class)));
        }
    }
}


BATMAN_2008
  • 2,788
  • 3
  • 31
  • 98