Mismatch between the REST API Swagger and Manual Documentation - which is which

Tags: #<Tag:0x00007f0a29b7ed70> #<Tag:0x00007f0a29b7ec80> #<Tag:0x00007f0a29b7ebb8>

I am having a hard time correlating which documentation is correct and which one should be followed.

For example when creating an encounter the bundled documentation in OpenMRS shows this

{
  "patient": {
    "person": "uuid",
    "identifiers": [
      {
        "identifier": "string",
        "identifierType": "uuid",
        "location": "uuid",
        "preferred": true
      }
    ]
  },
  "encounterType": {
    "name": "string",
    "description": "string"
  },
  "encounterDatetime": "string",
  "location": {
    "name": "string",
    "description": "string",
    "address1": "string",
    "address2": "string",
    "cityVillage": "string",
    "stateProvince": "string",
    "country": "string",
    "postalCode": "string",
    "latitude": "string",
    "longitude": "string",
    "countyDistrict": "string",
    "address3": "string",
    "address4": "string",
    "address5": "string",
    "address6": "string",
    "tags": [
      "string"
    ],
    "parentLocation": "string",
    "childLocations": [
      "string"
    ]
  },
  "form": {
    "name": "string",
    "description": "string",
    "version": "string",
    "encounterType": "string",
    "build": 0,
    "published": true,
    "formFields": [
      "string"
    ],
    "xslt": "string",
    "template": "string"
  },
  "provider": "string",
  "orders": [
    {
      "encounter": "uuid",
      "orderType": "uuid",
      "action": "NEW",
      "accessionNumber": "string",
      "dateActivated": "string",
      "scheduledDate": "string",
      "patient": "uuid",
      "concept": "uuid",
      "careSetting": "uuid",
      "dateStopped": "string",
      "autoExpireDate": "string",
      "orderer": "uuid",
      "previousOrder": "uuid",
      "urgency": "ROUTINE",
      "orderReason": "uuid",
      "orderReasonNonCoded": "string",
      "instructions": "string",
      "commentToFulfiller": "string"
    }
  ],
  "obs": [
    null
  ]
} 

However the https://rest.openmrs.org/?shell#list-encounters shows the following

POST /Encounter
{
  "encounterDatetime": "2019-10-16 12:08:43",
  "patient": "070f0120-0283-4858-885d-a20d967729cf",
  "encounterType": "e22e39fd-7db2-45e7-80f1-60fa0d5a4378",
  "location": "aff27d58-a15c-49a6-9beb-d30dcfc0c66e",
  "encounterProviders": [
    {
      "provider": "bb1a7781-7896-40be-aaca-7d1b41d843a6",
      "encounterRole": "240b26f9-dd88-4172-823d-4a8bfeb7841f"
    }
  ]
}

The difference in case /encounter in REST and /Rest in documentation is glaring.

IMO maintaining manual documentation is a very big pain point especially with tools that can generate documentation from OpenAPI (formerly Swagger)

UPDATE: So why does this matter the JSON request from the documentation (below)

{
  "encounterDatetime": "2020-07-16 12:08:43",
  "patient": {
      "person": "8f158fad-1469-42e2-9798-b9f5de225240"
  },
  "encounterType": {
      "uuid": "8d5b2be0-c2cc-11de-8d13-0010c6dffd0f"
  },
  "location": "aff27d58-a15c-49a6-9beb-d30dcfc0c66e",
  "encounterProviders": [
    {
      "provider": "f9badd80-ab76-11e2-9e96-0800200c9a66",
      "encounterRole": "240b26f9-dd88-4172-823d-4a8bfeb7841f"
    }
  ]
}

and this also does not work

{
  "encounterDatetime": "2020-07-16 12:08:43",
  "patient": "378c7325-d7b8-4506-ada9-a8da199b1d3e",
  "encounterType": "8d5b2be0-c2cc-11de-8d13-0010c6dffd0f",
  "location": "aff27d58-a15c-49a6-9beb-d30dcfc0c66e",
  "encounterProviders": [
    {
      "provider": "f9badd80-ab76-11e2-9e96-0800200c9a66",
      "encounterRole": "240b26f9-dd88-4172-823d-4a8bfeb7841f"
    }
  ]
}

throws the error https://gist.github.com/ssmusoke/1b2745438a564b25be47de6a0915e37c

1 Like

Uganda can be accurately described with GPS coordinates and satellite views, but I wouldn’t consider it sufficient to describe Uganda. Likewise, autogenerated documentation can accurately generate the schema, but fails to describe how & why to use the API. Our autogenerated Swagger documentation may be able to faithfully produce the resources, but it doesn’t provide any guidance on how resources related, which properties are optional, and many of the properties are oversimplified as “string”.

The ideal solution would be to leverage the strengths of both: manual documentation to orient new users and provide richer explanations of how to use the API and its resources using auto-generated representations & interactive tests against our demo environment. And this would probably best be accomplished by embedding the manual documentation alongside the code, so the content at rest.openmrs.org could be auto-generated from code + documentation in code. Alternatively, we could make the manual documentation more accessible by keeping it in a repo and supplementing it (via scripting) with auto-generated versions of resources.

This was the vision. Since we already had Swagger “documentation” – albeit with several deficits† – my hope with the Improved REST API Documentation projects was to get some human-friendly documentation of our API focusing on those parts missing from auto-generated resource description with the hope that the Swagger output could later be merged or the markdown created could be migrated into the code (whichever made the most sense).

The goal is that developers, whether new or experienced, should be going to rest.openmrs.org to reference or learn our REST API just like anyone coding against GitHub’s API goes to docs.github.com.


† Lack of anything beyond an auto-generated catalog of resources (e.g., text orienting or describing how or why/when resources should be used), delay in generation every time you go to look at it, underspecified data types in many cases without examples or context, etc.

I would go for this option, automatically generated documentation hosted online and packaged with any module along with human readable examples

The beauty is that this documentation is within one or more modules not core so can easily be updated over time

Any time the two are in separate “locations” then keeping them in sync becomes a bigger challenge

1 Like

From our experience, using the bundled documentation is better as its more descriptive compared to the https://rest.openmrs.org/?shell#list-encounters documentation. However, the bundled documentation is missing some important properties such as Visit information that extremely relevant to an encounter.

Below is a draft JOSN Body used in UgandaEMR to create an encounter using REST.

{
  "encounterDatetime": "currentdatetime",
  "patient": "patient_uuid",
  "encounterType": "encounterType_uuid",
  "location": "location_uuid",
  "visit": {
    "patient": "patient_uuid",
    "visitType": "visit_uuid",
    "startDatetime": "currentdatetime"
  },
  "encounterProviders": [
    {
      "provider": "provider_uuid",
      "encounterRole": "encounter_role_uuid"
    }
  ],
  "obs": [
    {
      "person": "patient_uuid",
      "concept": "obs_concept_uuid",
      "obsDatetime": "currentdatetime",
      "value": "obs_value"
    },
    {
      "person": "patient_uuid",
      "concept": "obs_concept_uuid",
      "obsDatetime": "currentdatetime",
      "value": "obs_value"
    },
    {
      "person": "patient_uuid",
      "concept": "obs_coded_concept_uuid",
      "obsDatetime": "currentdatetime",
      "valueCodedName": "Current Regimen",
      "value": "coded_concept_answer_uuid"
    }
  ]
}
1 Like

This is very helpful. Let’s manually create documentation for a few (even one) resource(s) that we all agree is ideal. Then we can devise a strategy to apply that pattern more broadly in a manner that maximizes sustainability & usefulness.

At a minimum, I would expect every resource documentation to include information like:

  • Brief overview/purpose of the resources with tips on usage and links to learn more.
  • At least datatype, optional/required, default, and sample value for each property with links where appropriate (e.g., a datatype like “Patient UUID” would link to documentation on Patient.
  • Possible responses
  • Simple example (ideally, interactive against demo)
  • A brief FAQ, if applicable, covering common mistakes, known limitations, etc.

@burke so are we using these points in the rest documentation or it is another documentation

1 Like

Yes. I would suggest we consider 1-2 resource(s) in the current REST documentation and get to where everyone considers the documentation to be in good shape. For example, let’s say we consider the documentation for Encounter and where it falls short for @ssmusoke and @solemabrothers. For example, specifically for encounter creation:

  • The details provided in Stephen’s example from the bundled documentation (patient, encounterType, location, form) seem inappropriate. I would expect UUIDs to be better practice, so the manual documentation is better than the Swagger documentation in these cases.
  • The manual documentation just shows the case of an existing visit (using visit UUID), but doesn’t explain the case where a visit is created for the encounter (as in @solemabrothers’ example).
  • The manual documentation of encounterProviders just says “Array of …” without any example of the structure (e.g., we assume the reader will figure out the proper casing of encounterRole).
  • The manual documentation example doesn’t show how to create an encounter with observations, which is the 99% use case for creating an encounter.

Personally, I’m not a big fan of our use of the term “subresource” (sometimes “sub resource”). While a technically correct term and useful in the architecture/design of the underlying code, I think it overcomplicates the API documentation and the only place I’ve encountered its usage is within OpenMRS. I was able to find a couple references in Twilio’s API, but I didn’t see it anywhere in GitHub’s API. Do users really need to know or care about this detail?

In short, I’m proposing we take a deep dive into a resource like Encounter and get the documentation where everyone is proud of it. Then we can develop a strategy on how or which parts we can automatically generate in the future.

3 Likes

Sure @burke

@burke I think the inbuilt documentation should be the first place to improve as it has a try it out feature which simplifies development especially without an internet connection (which is common in Africa that has the largest implementations and developers)

We need to reword the documentation, and labels to do that properly then move to the manually generated documentation

@ssmusoke Hi Stephe

So for the google season of docs we have a project going on to update the manual documentation (res api docs). So i hope it will be great if we can get the inputs from the discussion here for it.

1 Like

The feedback I have is make it consistent with the Swagger generated documentation, use the same case for the paths and make sure to cater for the different elements as they are in swagger

Our ultimate goal should be to generate as much documentation (ideally with interactive examples) from the code as we can, but I’d like to agree on what we want our documentation to look like and then we know what any auto-generated documentation should produce. Our current Swagger documentation has many problems. For example, considering the encounter example in your original post in this thread:

  • We should promote use of patientUuid (not personUuid) for encounter creation.
  • Providing a UUID for the encounter type directly
    "encounterType": "{uuid}"
    
    seems simpler/better than putting it into an object
    "encounterType": { "uuid": "{uuid}" }
    
    Based on the error mentioned in your original post (“ConversionException: encounterType”), it’s possible the API doesn’t yet accept simply a UUID (or perhaps that encounter type doesn’t exist on the server you were using). The Swagger documentation’s suggestion of
    "encounterType": { "name": "string", "description": "string" }
    
    looks incorrect to me (doesn’t match your example or the one from @solemabrothers).
  • When creating an encounter, the location property should simply provide a UUID for the location of the encounter and not provide all the detail of the location as the current Swagger documentation suggests.
  • If a form is used to collect the encounter data, then the form’s UUID may be included in the encounter’s form property, but I would guess this is rarely/never used. You certainly don’t need to provide the form details suggested in the current Swagger documentation. If we mention form in the documentation for creating an encounter, we need to explain what this is and make it clear that it is rarely if ever used.
  • Swagger’s suggestion of “provider” as a property of encounter looks wrong. It should be encounterProvider and isn’t a string. Our current Swagger documentation does a lousy job of documenting data types. Most everything is simply "string" (including dates/times) with no explanation or example.
  • Hopefully, the person is implied for obs and doesn’t need to be included in each obs as your Swagger example suggests. Also, "person": "patient_uuid", while technically valid in our current data model, promotes a flawed constraint in our data model (patient uuid == person_uuid) which we should be trying to move away from rather than embrace.

Creating interactive or even working examples for complex resource creation like encounters can be a little tricky, since all the UUID references must match what is on the target server (which should be demo.openmrs.org for general documentation).

1 Like

@burke I agree with you on most of the comments made except the fourth, A form uuid on an encounter is important as it allows for editing of data, in our experience, creating an encounter without a form uuid throws an exception on editing as there is no form set.

@burke the formuuid on an encounter is used by HTML Form entry module and is staple in being able to edit data entered from a single form