Improving Our Swagger Documentation Process

Hi everyone,

During last week’s platform call, we had an insightful discussion about the current state of our Swagger documentation. As it stands, our approach to maintaining this documentation is both developer-intensive and prone to inconsistencies. The process relies heavily on developers manually specifying which resources, properties, and schemas should be documented. This manual dependency has, over time, led to gaps in the documentation and a general lack of up-to-date information, making it less robust and reliable keeping in mind that we have modules like appointments and others that don’t get documented by our current strategy.

The consensus from our discussion was that we need a more automated and maintainable approach. Ideally, we envision a solution where the documentation is auto-generated by scanning through our codebase. This would include analyzing resource handlers, controllers, models, and other components—extracting relevant details from Javadoc comments, annotations, or other metadata—to produce a comprehensive specification file for Swagger.

While this sounds ideal, it’s clear that the current Swagger ecosystem doesn’t fully support this functionality out of the box. Libraries like springdoc and springfox offer tools for integrating Swagger with Spring-based applications, but they are highly dependent on adherence to conventional REST practices and extensive use of Swagger-specific annotations such as @Api, @ApiResponse, and @Schema.

Implementing these libraries in our context would require significant changes to our existing codebase, particularly in the core data models and resource handlers, where we would need to annotate everything consistently. This level of change might not be feasible given the size and complexity of our codebase, not to mention the potential impact on legacy systems and backward compatibility.

I’d like to open this up to everyone for ideas and suggestions for libraries, tools, or strategies that could help us bridge this gap. The goal is to reduce the overhead of maintaining the documentation while ensuring accuracy and consistency across the board. If anyone has experience with alternative approaches or libraries that support auto-generation of Swagger specs from code , please share your insights.

cc: @burke @dkayiwa @mseaton @ibacher @dev5 @dev4

Read through @chibongho1 's reply on slack below that better explains our conundrum.

tl;dr

Hi Herman,Yeah, I saw that you did some research from your PR here. I looked into this a while back as well . Unfortunately, it’s unlikely that we’ll find a drop-in library to give us the typings, with the accuracy we want, without a lot of manual work or a lot of refactoring. The biggest reason is that the REST module has a non-standard (non Spring / Spring Boot) way of handling REST calls. I documented some of this in here.

  • MainResourceController.java handles all requests methods for all REST resources. It is a standard Spring @Controller, but its logic is pretty simple; most of the heavy-lifting is in other files. You can already tell that a lot of functions there use Object as the output type does not have better typed inputs beyond HttpServletRequest, and HttpServletResponse, which makes automatic type inference difficult already.
    • Even within the individual Resource.java files, which define CRUD functions to be called by MainResourceController, they heavily use SimpleObject (which is basically a HashMap) to store the property name → property value mapping of the returned objects. Again, that makes automatic type inference difficult.
  • For each resource, what representation does it have? Almost all REST resources have at least the “default”, “full” and “ref” representations on GET, but that’s not enforced. The properties returned for each of these representations are defined in the getRepresentationDescription() function in the Resource.java file. It’s also possible to have other representations. They can either be defined within getRepresentationDescription(), or in another function annotated with @RepHandler. It’s also possible for resources to be retrieved with “custom” representation as well, where the caller list of the properties to return.
    • Similarly, on PUT, getCreatableProperties() lists out required fields to; and on POST, getUpdatableProperties() lists out updadable properties.
  • For each resource, what properties are actually available? That is a difficult question to answer. The REST module uses a combination strategy of reflection and annotations to convert a property name to an actual Java value.
    • This also implies that the “full” representation does not actually have a complete list of properties for each resource.
  • For each property of a resource, which is its data type? Again, we’ll have to follow the combination strategy to get the function that returns the property to get its return type.
    • This gets a tricky when the property is actually another Resource or and List of Resources, as the process will need to be recursive.
    • Our current Swagger docs rely on getGETModel(), getCREATEModel() and getUPDATEModel() to document the properties’ types. But (as far as I know) there is no verification for correctness or completeness.

I did some review of what I documented internally at PIH. @mseaton suggested that we implement a reflection-based strategy to get the documentation of each type. I think it’s somewhat risky, but if it works, then it will save us a lot of manual documentation maintenance. I think it’s worth prototyping it for a Resource or two and see whether it looks promising. If so, we should be able to get rid of the getGETModel(), getCREATEModel() and getUPDATEModel() functions.Another strategy would be to make the getRepresentationDescription() function not only list of the properties of each representation but also the property types. For example, we can do something like this for PersonResource: description.addProperty(“uuid”).expectedType(new StringProperty()); description.addProperty(“display”).expectedType(new StringProperty()); We can also get rid of getGETModel(), getCREATEModel() and getUPDATEModel() this way.

In either case, I think it’ll be helpful to write automated tests to make CRUD requests and verify that the documented types matches the actual types. (Yes… it’s a lot of work to do this for each resource)Let me know what you think. Feel free to forward what I wrote to whoever, or loop me in any conversations on this.

2 Likes

UPDATE: From the Platform call.

Action Points:

In the meantime, we are going to do the following as we work towards having a tooling or coming up with one that either autogenerates our swagger document by looking at the javadocs, return types, etc. OR make our restapi more mvc or springboot-like than it is now. The ideal is to have an openapi spec generated at compile time rather than runtime like it is at the moment to curb any errors at compile time rather than runtime.

  • Remove the use of getGETSchema, getCREATESchema, getUPDATE methods as the manual mechanism of making swagger aware of what resources to document and which properties to document.
  • Adopt a new approach that scans through the parameterized superclass or interface (e.g DelegatingResourceHandler, DelegatingSubResourceHandler, etc) and extracts the generic type parameter. This might involve creating a method in the ReflectionUtils class that works by checking the current class for parameterized interfaces or superclasses. If the current class does not have a generic type, the method traverses up the class hierarchy until it finds one or reaches the top of the hierarchy.

The implementation can be tracked by this PR at → RESTWS-958: Upgrade swagger to 2.2.23 by mherman22 · Pull Request #631 · openmrs/openmrs-module-webservices.rest · GitHub

1 Like

Shouldn’t that be a different ticket and pull request? For the one you linked above is simply about ugrading the version of swagger without changing the approach.

1 Like

True dat!

Please find the ticket at Refactor the mechanism for making Swagger aware of resource handlers and adopt a more automated approach and currate it. This will be used to track the work as highlighted above or in the ticket description.

It is always a good practice to add a link to the talk thread that led to the ticket.

1 Like

Update:

So far with the changes in RESTWS-963: Refactor the mechanism for making Swagger aware of resource handlers for documentation by mherman22 · Pull Request #637 · openmrs/openmrs-module-webservices.rest · GitHub, i am able to generate the documentation. What is entailed in that PR is the ability to just scan through the resource handlers and extract the generic type and then go in there and fetch the variables that would have been defined in the resource handlers’ getcreatableproperties, getupdatableproperties and getgetrepresentational methods.

With this, the developer doesnt have to manually maintain methods just for documentation. Just adhere to OpenMRS custom RestFul standard and the resources will be documented.

It would be easier to review if you did two separate pull requests. One for the actual code that pulled that off, and the other for removing the documentation methods from the various resources.

Makes sense. Let me fix that in a beat

I have updated the PR at RESTWS-963: Remove the manual methods used for generating the swagger documentation by mherman22 · Pull Request #637 · openmrs/openmrs-module-webservices.rest · GitHub

I still see only one pull request.

The Pull request that adds the autogeneration stuff is at RESTWS-963: Add a swagger autogeneration util to scan through resource handlers and generate the swagger spec