Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

Version 1 Next »

Introduction

In the RefApp, we currently have OpenAPI documentation (formerly called Swagger) of REST API endpoints that are both under-utilized and under-maintained. The documentation, which are both human and machine readable, can be valuable if they are completely and accurate. Besides giving developers an easy way to understand the API endpoints, it can be used to automatically generate REST API clients for the frontend, greatly reducing the boilerplate code that we need to make to do the same thing.

The page documents the existing architecture of the REST module, the existing documentation around it, and the challenges we face to improve it.

High Level Overview of REST Request Handling

When a GET request is sent to a REST endpoint (ex: /ws/rest/v1/patient/<uuid>?v=<rep>), the following happens:

  • MainResorceController handles the request, determines which resource the request is for (PatientResource), and calls that resource's retrieve() function.

  • DelegatingCrudResource (which is the superclass of most *Resource.java files) provides the retrieve function, which calls asRepresentation() to that reads the <rep> parameter

  • asRepresentation() attempts to convert the <rep> to a list of Patient properties (fields) to be retrieved using a combination of different strategies:

    • PatientResource.getRepresentationDescription() is first called. This function (usually) lists out the properties that should be retrieved for the "standard" representations commons to most resources (defaultfull and ref).

    • if the above function returns null, the findAnnotatedMethodForRepresentation function is called to find the method in the *Resource.java class that is annotated with @RepHandler to handle the specified <rep>.

    • If the above fails, it assumes that the <rep> is a customer representation (ex: custom:(uuid)), and calls getCustomRepresentationDescription to retrieve the list of properties.

  • The BaseDelegatingConverter.convertDelegateToRepresentation() function gets called to loop through each property name and converts it to actual values by calling DelegatingResourceDescription.Property.evaluate()

  • The evaluate() function tries a combination strategy of reflection and annotated methods to retrieve the value of the property. This process can be recursive as the property can itself be another Resource.

  • The evaluated object gets returned by MainResourceController.retrieve() to be serialized into the appropriate data format (xml or json)

Challenges and Ideas for Improvement

  • current documentations are in 2 places: we have OpenAPI docs are generated from the backend, and we also have rest.openmrs.org. Can we merge them?

    • rest.openmrs.org can have narrative explanations that are useful in addition to normal javadocs. However, its shell / java / javascript code examples are probably better represented / maintained in OpenAPI.

    • It would be really nice if javadocs get included into the OpenAPI docs. It should be possible, see this.

  • We only have OpenAPI documentation for REST Resources, but we have other web APIs (like controllers) that need to be documented.

    • Example: Appointments have a REST-like API, but is actually implemented as a Controller, so it doesn't show in the OpenAPI docs.

    • EMRAPI contains web APIs that are just not REST-like at all. Those need to be documented as well.

  • Most REST resources have "default", "full", and "ref" representation, but it is possible to have more via "NamedRepresentaton". Examples:

  • REST resources can also have "custom" representations. It would be nice to document what properties can be included, but that's difficult. It is possible to have properties that are not in "full" representation. Example:

  • We use getRepresentationDescription() to define the fields that get returned for a given resource's representation, but we use getGetModel() to document what fields are in a representation. This can lead to inconsistencies. Can we combine them?

  • With our current implementation, there is no way to type search parameters. For example, ObsResource1_8.java has a doSearch() function that takes in a RequestContext , and from that we extract the params patient, encounter and q.  Is it possible to re-architect the search function to take in a better typed object?

    • There are also resources that have different search implementations depending on which params are passed in (See ObsResource1_8.java, VisitResorce1_9.java). For those, it might make sense to have different search functions (with different input types) instead of trying to cramp the logic into one function.

  • OpenAPI 2.0 does not support oneOf() types, but upgrading to 3.0 should help. Where that can be useful:

    • when we do /patient?v=default, we should return PatientGetDefault, when we do /patient?v=full, we should return PatientGetFull

    • v= parameter can be oneOf(enum('ref', 'default', 'full'), string)

  • No labels