Need review on Data Integrity Module API design

Hello everyone,

Currently I’m working on DINT-82 in which I need to create API of data integrity module so that the module services can be easily integrated with other services. Below is the API design I’ve created. Please review and provide feedback. Thanks

cc: @ssmusoke

###Our Resource will be:

Rules - /dataintegrityrule (base url)

GET

  • / - List of all rules (paginated)
  • /{rule-id} - Fetch a rule by id
  • /{rule-category} - Fetch all rules belong to a particular category (paginated)
  • /{uuid} - Fetch by uuid
  • /{rule-id}/results - List of all result for a particular rule (paginated)

PUT

  • /{rule-id} - Update attributes of a particular rule
  • /{uuid} - Update by uuid

DELETE

  • /{rule-id} - Delete a particular rule
  • /{uuid} - Delete by uuid

Results - /dataintegrityresult (base url)

GET

  • / - List of all results (paginated)
  • /{result-id} - Fetch result by id
  • /patient/{patient-id} - List of all result for a particular patient
  • /program/{program-id} - List of all result for a particular program
  • /{uuid} - Fetch by uuid

POST

  • / - Generate results for a particular rule ( rule_id passed in parameter )

PUT

  • /{result-id} - Update attributes of a particular result
  • /{uuid} - Update by uuid

DELETE

  • /{result-id} - Delete a particular result
  • /{uuid} - Delete by uuid

Not a big deal but i just wanted to say that we do not usually do resource names in plural.

Thanks @dkayiwa, I totally forgot. Corrected.

Shouldn’t /{rule-category} ideally be a query param of the base / ?

And Swagger doesn’t allow equivalent path params. For example

/{rule-id}
/{rule-category} (response will have a json array)

These two paths are identical as far as Swagger is concerned… And as the two operations return results in different formats, there will be no way we could document both in a swagger spec.

Also see: REST - supporting multiple possible identifiers

May be I have misunderstood something.

Should this not also work for /uuid/results?

Should we have /patient/{uuid} and /program/uuid [quote=“shivtej, post:1, topic:12762”] POST

/ - Generate results for a particular rule ( rule_id passed in parameter ) [/quote]

I think this should be in the dataintegrity rule not dataintegrityresults API call - since its a rule being run [quote=“shivtej, post:1, topic:12762”] DELETE

/{result-id} - Delete a particular result /{uuid} - Delete by uuid [/quote]

I do not think we need to have a way of deleting results since they will be overwritten at the next run - when fixed

Thanks @ssmusoke and @gayanw for feedback.

Maybe we can change this to /category/{rule-category} , then it’ll more clear.

Makes sense. Adding it.

Nice point. Adding it too.

I have one doubt - according to REST conventions, POST request should add new resource. If we host this api under /dataintegrityrule then it is not generating any rule and that’s why I added in /dataintegrityresults where this API call actually adding new resources. Please clarify.

Will update the design.

@shivtej just in case you find this useful: https://www.thoughtworks.com/insights/blog/rest-api-design-resource-modeling

1 Like

Thanks @dkayiwa for sharing the blog. It is very nicely written :

@ssmusoke, should we change this API to something like /run_rule/{rule-id} which is hosted under /dataintegrityrules ?

Skimming this topic very quickly (and thus probably duplicating what others have said):

  • I would prefer .../ws/rest/v1/dataintegrity/rule instead of .../ws/rest/v1/dataintegrityrule. (See for example the reportingrest module’s API.)
  • you shouldn’t duplicate the same functionality under different URLs, e.g. a result should be either a top-level resource, or a sub-resource of a rule, but not both.
  • if you do need to expose things in multiple ways, you’d use links for this, e.g. if you results are a top-level resource, then rule/uuid/results could return you a link to results/uuid-of-last-run-of-that-rule.
  • get rid of everything that “by id” and only support addressing individual items by UUID
  • in REST, resources should be nouns, not verbs, so you should not have run_rule. One possible way to handle the “trigger a rule” function would be POST .../rule/{uuid}/runrequest. In other word’s you’re creating a request to run the rule. (This would do whatever it needs on the back-end, and it could either return you a pointer to the run that has been queued, or else redirect you to the actual results if you’re running it right away. I don’t know the underlying Java API.)
1 Like

Thanks @darius for such a thorough feedback. I’ve made the changes according to the feedbacks and here is the new design. Please provide feedback if the design misses anything. @ssmusoke, can I go ahead with the design ?

Resource will be hosted under /dataintegrity and they are:

Rules - /dataintegrity/rule (base url)

GET

  • / - List of all rules (paginated)
  • /category/{rule-category} - Fetch all rules belong to a particular category (paginated)
  • /{uuid} - Fetch by uuid

POST

  • / - Add new rule
  • /{uuid}/runrequest - Run a single rule and generate results

PUT

  • /{uuid} - Update by uuid

DELETE

  • /{uuid} - Delete by uuid

Results - /dataintegrity/result (base url)

GET

  • / - Paginated list of results (belong to a rule if rule_id is passed otherwise all rows)
  • /patient/{patient-id} - List of all result for a particular patient given its patient-id
  • /patient/{uuid} - List of all result for a particular patient given its uuid
  • /program/{program-id} - List of all result for a particular program given its program-id
  • /program/{uuid} - List of all result for a particular program given its uuid
  • /{uuid} - Fetch by uuid

PUT

  • /{uuid} - Update by uuid

What would this do? Since the results are only updated from

There are column such action_url and notes which I think can be updated later so that’s why I kept this API. For instance, let’s say one may can updated notes columns of a particular result with some information (like some info. from patient’s medical report) which is not captured as of now.

@shivtej However this will be overwritten when the rules are run - since the results are deleted and new ones recreated. I suggest that we leave it out since the results are not permanent

Sure, will leave it.

If nobody have any further feedback on the design then I’m starting with the implementation. cc: @ssmusoke

Can anybody point me to the resources where I can find documentation or example of REST APIs implementation. I tried to search it by myself and did find some example but I’m unable to get complete picture. @dkayiwa, @darius, @ssmusoke

Thanks

@shivtej is this of help? https://wiki.openmrs.org/display/docs/Adding+a+Web+Service+Step+by+Step+Guide+for+Module+Developers

Thanks @dkayiwa, it’s very helpful. Just one more thing I want to ask is that do we have anything like hot-reloader which compiles and host the module as we make changes in OpenMRS ? It would be very difficult to restarting server again & again to view changes.

Is this of help? https://issues.openmrs.org/browse/SDK-80

Wrong answer @dkayiwa! :slight_smile:

@shivtej, what you’re really asking is what is the most efficient way to test that the small iterative changes you make to your code have the behaviors that you want. The right answer to this is not “hot reloading”, it’s Test Driven Development.

Basically, (1) you write a a test expressing the behavior that you want your REST API to have, then (2) you write the code to implement this behavior, (3) running the test each time you make a change to ensure you’ve got it right.

If you’re not familiar with TDD and/or you haven’t done it before, I can 100% guarantee you that learning to do TDD while you build this REST API will save you time, even on the first project, versus running this code on a live server (and reloading it a lot) and manually testing it.

1 Like