GSoC 2025: Enhancing OpenAPI Documentation Generation - Weekly Updates & Talk

Hi everyone! :waving_hand:

I’m Marvin Sukumar, an undergraduate student studying computer science in Mumbai, India. I’m thrilled to be selected as a Google Summer of Code 2025 contributor with OpenMRS! I thank my mentors @chibongho1 and @mherman22 for consistently guiding me throughout.

:memo:Project Overview

My project is focused on replacing the current OpenMRS Standalone REST documentation generation process with a modern, automated system that generates OpenAPI 3.0 specifications at build-time.

The idea is to reduce the manual, error-prone approach currently used, and instead build a custom Maven plugin that extracts and constructs API specs using:

  • Annotation processing
  • Static code analysis
  • Javadoc integration
  • And OpenMRS-specific REST handler support

Ultimately, the goal is to provide an accurate, complete, and maintainable OpenAPI 3.0 specification

:spiral_calendar: Updates and Progress

I’ll use this thread to:

  • Post weekly updates
  • Share blockers and discussions
  • Get feedback from mentors and the community
  • Keep the documentation trail open and transparent

Feel free to ask questions, give suggestions, or help me debug tricky areas! I’d love to collaborate closely with everyone here. :raising_hands:

I plan on making weekly blog posts, preferably on Notion but any inputs or suggestions would be appreciated.

Resources & Links

Notion Doc: Your connected workspace for wiki, docs & projects | Notion

Proposal: Marvin_GSOC_Proposal.pdf - Google Drive

Project Description: Enhancing OpenAPI Documentation Generation - Projects - OpenMRS Wiki

cc: @ibacher @dkayiwa @grace @jayasanka

4 Likes

Hello everyone!

I have written my first blog pertaining to all the progress I have had in my first week post GSoC. I hope it is clear and informative, any suggestions or feedback would be highly appreciated.

Thank You!

Link: (Your connected workspace for wiki, docs & projects | Notion)

1 Like

:sparkles:Major Project Progress Update - 01

Upon initiating my GSoC work, I began by tackling the task of extracting Javadoc comments present at both class and method levels within each REST resource. After thorough exploration, I narrowed down the approach to two viable options: the Doclet API, which operates by invoking the javadoc tool to process source code, and JavaParser, which parses .java files into an Abstract Syntax Tree (AST), enabling traversal and structured analysis of code elements.

After evaluating the trade-offs (which I documented extensively), I chose to proceed with JavaParser due to its lightweight footprint, developer ergonomics, and seamless programmatic integration into a Maven plugin environment.

The next and most substantial challenge I encountered relates to accurately capturing the REST API representations (reps) defined in the webservices.rest module—specifically, for GET operations on resource classes. OpenMRS supports multiple representations like default, full, ref, and custom, each returning different sets of properties. To generate high-fidelity OpenAPI documentation, it’s essential to statically determine what each representation returns for every resource. This level of documentation is invaluable for new contributors, integrators, and external developers consuming the APIs.

Historically, the OpenMRS REST module relied on the getGETModel() method to define representation content, but this was inconsistent and unreliable across resources. To overcome this, I’m now working on invoking the getRepresentationDescription(Representation rep) method reflectively at build time. This method returns a DelegatingResourceDescription, which explicitly defines the fields and sub-resources included in each representation type.

However, this approach introduces a significant technical caveat: during Maven’s build lifecycle, we lack access to the Spring application context and many of the supporting OpenMRS services that would normally be injected at runtime. As a result, invoking methods that internally rely on these services (even indirectly) can result in exceptions, NullPointerExceptions, or incomplete behavior.

For instance, while testing with PatientResource1_8, I was able to instantiate the class using its default no-args constructor. However, when calling getRepresentationDescription() on it, the method fails because it references static members like RestConstants, which in turn may rely on Spring-managed beans or modules not yet initialized in the build phase. To verify this, I commented out the problematic lines, and the method successfully returned the expected properties for both default and full representations.

[INFO] ------------< org.openmrs.module:webservices.rest-omod-1.8 >------------
[INFO] Building Rest Web Services 1.8 OMOD 2.50.0-SNAPSHOT
[INFO]   from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- openapi-generator:1.0-SNAPSHOT:openapi (default-cli) @ webservices.rest-omod-1.8 ---        
[INFO] === OPENAPI GENERATION STARTED ===
[INFO] ClassLoader setup complete: 22 URLs
[INFO] Loaded class: org.openmrs.module.webservices.rest.web.v1_0.resource.openmrs1_8.PatientResource1_8
[INFO] === Testing PatientResource Instance Creation ===
[INFO] Attempting to create PatientResource1_8 instance using normal constructor...
[INFO] ? SUCCESS! PatientResource1_8 instance created successfully!
[INFO] Instance class: org.openmrs.module.webservices.rest.web.v1_0.resource.openmrs1_8.PatientResource1_8
[INFO] Instance toString: org.openmrs.module.webservices.rest.web.v1_0.resource.openmrs1_8.PatientResource1_8@77f905e3
[INFO] --- Testing Basic Method Calls ---
[INFO] ? toString() works: org.openmrs.module.webservices.rest.web.v1_0.resource.openmrs1_8.PatientResource1_8@77f905e3
[INFO] ? getClass() works: org.openmrs.module.webservices.rest.web.v1_0.resource.openmrs1_8.PatientResource1_8
[INFO] ? Found 30 declared methods
[INFO] ? Found getRepresentationDescription method: public org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription org.openmrs.module.webservices.rest.web.v1_0.resource.openmrs1_8.PatientResource1_8.getRepresentationDescription(org.openmrs.module.webservices.rest.web.representation.Representation)
[INFO] === Testing getRepresentationDescription Method ===
[INFO] ? DEFAULT result: org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription@2b2954e1
[INFO] ? FULL result: org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription@58d6e55a
[INFO] === Extracting Schema from DEFAULT ===
[INFO] Found 5 properties:
[INFO]   - uuid -> org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription$Property@235d659c
[INFO]   - display -> org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription$Property@4232b34a
[INFO]   - identifiers -> org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription$Property@2da16263
[INFO]   - person -> org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription$Property@f5ce0bb
[INFO]   - voided -> org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription$Property@47e51549
[INFO] === Extracting Schema from FULL ===
[INFO] Found 6 properties:
[INFO]   - uuid -> org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription$Property@101a461c
[INFO]   - display -> org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription$Property@360e9c06
[INFO]   - identifiers -> org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription$Property@5ebffb44
[INFO]   - person -> org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription$Property@311ff287
[INFO]   - voided -> org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription$Property@7377781e
[INFO]   - auditInfo -> org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription$Property@31db34da
[INFO] ==============
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS

I also experimented with unsafe object allocation (via sun.misc.Unsafe or ObjectInputStream) to instantiate the class without triggering constructors or initialization blocks, but this yielded similar issues due to static dependencies being evaluated during method execution.

I intend to share the exact stack traces and points of failure in follow-up discussions, but I wanted to flag this design tension early—especially if there’s precedent or tooling within the OpenMRS ecosystem for solving this kind of build-time reflection over runtime-dependent logic.

Despite the complexity, I’m optimistic. Progress has been steady and productive, and I’m confident that with the right design abstractions, this plugin will bring clarity, consistency, and significant value to the way OpenMRS exposes its REST API documentation.

This is the architecture of the whole documentation generator that we have planned (Thank you @mherman22 for such a detailed graph)

Thank you again for the support and guidance—I’m excited to keep pushing forward and deliver something the community will be proud of.

2 Likes