Re: Sateesh's comment on encounter creation

classic Classic list List threaded Threaded
10 messages Options
Friedman, Roger (CDC/CGH/DGHA) (CTR) Friedman, Roger (CDC/CGH/DGHA) (CTR)
Reply | Threaded
Open this post in threaded view
|

Re: Sateesh's comment on encounter creation

I believe that Darius, Rafal and I are pretty much in agreement that sub-resources should be creatable from resources, but that full resources should be created separately and then either used as a uuid reference or added explicitly to a multi-object collection.  Since there's no a priori way to know whether a link has been put in the record itself, in a mapping object that's also a (sub-)resource, or a mapping object that's only a mapping object, the REST machinery has difficulty in figuring out what to do; we may need to have add/remove member service or pojo methods to handle collections.  Rafal found that bulk replacement of collection members produces Hibernate errors.

 

My idea in creating the page <a href="https://wiki.openmrs.org/display/docs/Standard_Modeling&#43;of&#43;Resource&#43;Relationship&#43;Types"> https://wiki.openmrs.org/display/docs/Standard_Modeling+of+Resource+Relationship+Types was to document the types of relationship we have in OpenMRS and how those relationships should be modeled in REST.  It still needs input.

 

In the case of encounter, the answer to Sateesh's comment depends on whether there can be encounterless obs.  If there are no encounterless obs, then obs should be a sub-resource of encounter and thus creatable.  If there are encounterless obs, then obs could be a full resource (as it is now) and the encounter and the obs should be created separately and then the obs added to the encounter.   However, obs could be a sub-resource of patient (there are no patientless obs), in which case you could create obs while creating a patient.

 

Further, every encounter has an encounter type.  Encounter type is a resource, so it would have to be created separately and then used within the encounter being created.  Likewise, you couldn't create a location or a provider/user while creating the encounter.

 

If there were a way to inform the REST machinery what kind of a linkage exists between two resources, then it might be possible to weaken these constraints.  But I don't think we'd ever want a complete free-for-all; the idea of creating a new encounter type or location on the fly seems pretty dubious to me.

 

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Burke Mamlin
Sent: Friday, April 27, 2012 9:49 AM
To: [hidden email]
Subject: Re: [OPENMRS-DEV] An Alternative Approach to Subclass Implementation

 

Good point, Sateesh.  When creating an encounter, you could be able to create all of the associated data as well within a single call; in fact, this should be the preferred method for creating an encounter.  This would include observations (obs) now, include orders as they are added to REST, and eventually include notes & form data once those are fully realized.

 

Cheers,

 

-Burke

On Fri, Apr 27, 2012 at 4:23 AM, Sateesh Babu <[hidden email]> wrote:

One question (concern) here. For example, if a client has to create a patient encounter with drug and lab orders, the client has to potentially make 2-3 (or even more) REST calls.
What if the first REST call succeeds and the next two fail or a combination like pass-fail-pass, pass-pass-fail etc. The failure can be because of any reason ranging from network failure, data corruption, bugs etc. REST inherently being a stateless protocol would leave the data on the server in an inconsistent state.
Instead, why not send the complete data in the transaction in one single go. So for the same above example, one single data object could be created with the encounter + drug order + lab order. This way if the transaction fails, then atleast the data is left in a consistent state. If data size is a concern, we can always use compression. There could be a meta-data object in the beginning of the object to describe what the contents of the json object are. Something like:
element {
  encounter: {
                   ...
                  }
  drug-order: {
                   ...
                   }
  lab-order: {
                  ...
                }
}

--
Sateesh

 

On Fri, Apr 27, 2012 at 11:22 AM, Darius Jazayeri <[hidden email]> wrote:

Yeah, I coded it such that classes have to actively choose to make themselves subclassable, but it should only take a few lines of code to change that.

 

-Darius

 

On Thu, Apr 26, 2012 at 8:28 PM, Ben Wolfe <[hidden email]> wrote:

So I was in Rogers camp until this argument.  There were three types of subclassing that we defined.  The patient/person subclassing is a first class resource, so both are primary documents. The ProblemList/Allergy subclassing completely hide their parents, so they are primary documents. However, these Order subclasses are second class citizens of sorts. They depend on the parent Order resource.  I don't like introducing yet another *Handler class, but calling them a resource is mixing ideas.

 

I'd favor making every class subclassable by default.  I think you had reversed Darius.

 

Ben

 

On Thu, Apr 26, 2012 at 9:42 PM, Darius Jazayeri <[hidden email]> wrote:

Hi Roger,

 

So, as you can probably imagine, I disagree. :-)

 

I think the whole point is that it's not appropriate to expose subclasses as if they were resources. I'm explicitly saying they're not resources, and that consumers of the REST API should not think of them as resources, but rather should think "the order resource tells me about orders, and drug orders are one type of order".

 

So I don't think that exposing a drugorder URI that redirects to order?t=drugorder is right. Also, using HTTP 307 (Temporary Redirect) means that the client has to make 2 requests to do anything (unless they cheat by going straight to the order resource).

 

What do others think?

 

I refactored my code a bit so now DelegatingSubclassHandler declares save() and purge() methods, which have a default implementation in BaseDelegatingSubclassHandler. Is that a good enough solution?

 

-Darius

 

On Thu, Apr 26, 2012 at 12:50 PM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

So I have been trying to figure out if there is any way to break out of this back and forth.  I have decided that what I really want to see is subclass resources and controllers that act as if they were full-fledged resources and preserve the subclass hierarchy but don't screw up caching.  So I am going to suggest the following:

 

(1) Subclass resources should be addressed like full resources, ws/<subclass>/... but the subclass controller should issue a 307 redirect to ws/<superclass>/...?t=<subclass>.  Subclasses can have child classes represented as sub-resources, and sub-resources can be created when a resource is created.  It is OK for subclass controllers to hand custom queries directly to their resource without passing through the superclass.

 

(2) The superclass should delegate to the subclass for representations, searches and saves.  If the named representation or search does not exist, my choice would be to ascend the class hierarchy.  I understand this might require some registration, but only to the parent.

 

(3) The superclass resource would have to implement the superclass representations, searches and saves.  It would also have to implement the delegation mechanism for subclasses.

 

(4) Subclass name will be returned as part of the standard representations.  Subclass name will not be needed for POST.

 

So given our Order -->  DrugOrder --> HIVDrugOrder class hierarchy

 

POST ws/order {body} creates an order which is filled from the body and validated and saved

POST ws/drugorder {body} is redirected to ws/order?t=drugorder {body} which delegates to the drugorder resource to create a drugorder which is filled from the body and delegated to the drugorder resource for validation and saving

POST ws/hivdrugorder {body} is redirected to ws/order?t=hivdrugorder {body} which delegates to the hivdrugorder resource to create an hivdrugorder which is filled from the body and delegated to the hivdrugorder resource for validation and saving.

 

POST ws/order/uuid {body} retrieves the order which is filled from the body and validated and saved

POST ws/drugorder/uuid {body} is redirected to ws/order?t=drugorder {body} which retrieves the order which is filled from the body and delegated to the drugorder resource for validation and saving

POST ws/hivdrugorder/uuid {body} is redirected to ws/order?t=hivdrugorder {body} which retrieves the order which is filled from the body and delegated to the hivdrugorder resource for validation and saving

 

GET ws/order/uuid?v=<rep> retrieves the order whose type is determined and delegated to the subclass for generation of the rep.  If the object with the uuid is not assignable from order or the rep does not exist, an error is thrown. 

GET ws/drugorder/uuid?v=<rep> is redirected to ws/order/uuid&v=<rep>?t=drugorder.  The search for the representation is done in the resoruce drugorder.

GET ws/hivdrugorder/uuid?v=<rep> is redirected to ws/order/uuid&v=<rep>?t=drugorder.  The search for the representation is done in the resource hivdrugorder.

 

GET ws/order&v=<rep> retrieves all orders; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named rep does not exist, an error is thrown.

GET ws/drugorder&v=<rep> is redirected to ws/order?v=rep&t=drugorder.  The superclass delegates to the class, which retrieves all drugorders; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named rep does not exist, an error is thrown.

GET ws/hivdrugorder&v=<rep> is redirected to ws/order?v=rep&t=hivdrugorder.  The superclass delegates to the class, which retrieves all hivdrugorders; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named rep does not exist, an error is thrown.

 

GET ws/order&<query>=<param> retrieves all orders satisfying the condition; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named query does not exist, an error is thrown.

GET ws/drugorder&<query>=<param> is redirected to ws/order?<query>=<param>&t=drugorder.  The superclass delegates to the subclass to retrieve all orders satisfying the condition; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named query does not exist, an error is thrown.

GET ws/hivdrugorder&<query>=<param> is redirected to ws/order?<query>=<param>&t=hivdrugorder.  The superclass delegates to the subclass to retrieve all orders satisfying the condition; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named query does not exist, an error is thrown.

 

DELETE is handled by the superclass.

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Darius Jazayeri
Sent: Thursday, April 26, 2012 11:34 AM


To: [hidden email]
Subject: Re: [OPENMRS-DEV] An Alternative Approach to Subclass Implementation

 

To be clear, the POSTs that Burke showed a couple of emails ago will work exactly as written, except of course that he simplified the actual properties into just "order". And the response to the GET that he showed will also be as he wrote it (except that uri is within links and the whole thing is in a PageableResults object). And the URIs he gave for the POSTs and GET are exactly right, there is no need for a ?t=type parameter.

 

Responses inline below.

 

-Darius

On Thu, Apr 26, 2012 at 7:14 AM, Burke Mamlin <[hidden email]> wrote:

On Thu, Apr 26, 2012 at 8:32 AM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

Burke --

     Regardless of whether it's Darius or me, each of the posts 1 and 3 would need a t=<type> parameter. 

     In my design, t is always virtual, it is the classname of the object; this avoids issues like having to use a global variable or predefined value or concept map to know the value of order type for drug order.  It also leaves order type available for distinctions that might be important to the facility without affecting the data model (say a narcotic drug order).  I'm not sure where Darius is at on this; at one point he required a discriminator field with a known name (registered?), but I think he has become more virtual.  The point is, you can't change the value of a discriminator field, so why include it in the POST body?  I do return it in the body on GET, as does Darius.

 

You can't change the concept of an order either, so would that move the URL as well?  I don't like the idea of putting some of the properties in the URL and others in the data.  You're right that you can't change the type of order; however, you must specify it when creating an order.

 

I'm also not thrilled about clients needing to know the Java class name for every order type.  There are not an infinite number of orderables.  Most implementations will be able to get by with two up front (drugs & tests).  Even for an enterprise-level inpatient system, you can get by with only a dozen or so (e.g., activities, precautions, diet, call orders, drugs, lab tests, radiology tests, procedures, nursing orders, referrals, education/handouts).  Actually, each of these basic types could be represented by a concept class and that class name could be used.

 

As I've coded it, the java class names are not exposed. To create a drug order you say ' type: "drugorder" '.

(@Roger, as coded you only use a &t=drugorder when you're doing a get-all or search, to say you only want objects of that type.)

 

 

     Also, Darius and I differ about privileges and validation at posts 1 and 3.  In Darius' design, both commands would require the privileges to create an order and would validate per order's requirements.  In my design, post 1 would require the privileges to create a drug order and would validate per drug order's requirements, while post 3 would require the privileges to create a referral order and would validate per referral order's requirements. 

 

There should be a top level privilege for ordering in general.  My assumption in posting the orders was that the REST document received would be passed to the order type-specific handler, so permissions & validation could be type-specific as you describe – i.e., we would have separate validators for each basic type of order.

 

The underlying Java API is built such that if you have Manage Orders privilege, you can do OrderService.saveOrder(anyOrderSubclass). My implementation does the actual save using this.

As coded, the type-specific-handler is only allowed to specify a "delegating resource description" which indicates what fields may be created/edited, it doesn't actually get a "save(T)" call. This is an oversight, due to the fact that none of our core resources actually do any custom save/validation logic that isn't routed through the resource description.

I can add a save(T) method on the subtype handlers, and let the base implementation of it just call the resource's save method.

 

 

    At get 4, both designs would use the default rep associated with the subclass of the object.  However, suppose you wanted to display all the types of order with just the order fields and some combination of subclass-specific fields in a details column.  In my design, you could define a named representation (say, COMPRESSED) that would be defined differently for different subclasses; you could even define it at a parent level (say drug order) and it could be used by children (like HIV drug order).  I don't believe you can do this in Darius' design.

 

Again, I think I agree with you here.  I was assuming that the generation of representation for each order would be delegated to it's type-specific handler, so if you asked for a FOOBAR representation of all the patient's orders, the API would iterate through the orders, asking for the representation for each from its type-specific handler.

 

@Darius – is that not what you're doing?  i.e., were you creating something that would not defer calls to type-specific handlers?

 

I do delegate to type-specific handlers. You can also define named representations on subclasses. What you cannot do is define a representation in the top-level resource, and get a subtype in that representation. E.g. if order defines a "foobar" rep, that is not inherited by drugorder. (Though drugorder could of course define that named representation also, as a one-liner that calls the parent resource.)

 

 

    With Darius, at step 0 (before the drug order is built in), you have to create a resource and controller for the order superclass; you have to create a handler, and a controller if you want custom searches, for drug order.  The superclass resource will contain all the methods of the current design plus the non-final declaration.

 

Yes.  The idea is that drug orders are provided for you.  So "you" will not have to do these steps.

 

  Darius depends on each resource and handler having the variable lists created by RESTWS-226, which I do not.  The drug order handler will contain its registration plus most of the methods of a resource (no save, delete or purge).  At step 2, you would need to build another handler and possibly a controller.

    With me, at step 0 you have to create a resource and controller for the hierarchy, a resource and controller for the superclass (order), and a resource and controller for the subclass (drug order).  The controllers are one-liners unless you want to add custom searches; they are just like the current design.  The hierarchy resource could also be a one-liner, with the actual delegations taking place in an abstract class using generics.  The resource for the superclass and the subclass would look just like the current design.  At step 2, you would create another controller and resource for referral order and that would also look just like the current design.

 

If you're creating a separate resource for each type, then doesn't that imply a different URL for each order type?

 

     In our discussion, Darius and I identified some situations (as I recall relating to validation) where it would be necessary under his design to override the supplied core resource with a user-defined resource that extended the core resource.  This could not be done independently for more than one subclass, and would require a custom build.  This would not be necessary with my design.

 

Can you provide an concrete example?  I'm presuming each order type has an associated handler/validator.

 

-Burke

 

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Burke Mamlin
Sent: Wednesday, April 25, 2012 5:08 PM


To: [hidden email]
Subject: Re: [OPENMRS-DEV] An Alternative Approach to Subclass Implementation

 

Ok, so if I want to create a drug order and a referral order for patient with uuid 123-456, then request all orders for the patient, it would look something like this (simplifying the order details for a simpler example)?

 

1. Create a drug order for my patient; they're built-in:

POST order

{ "patient":"123-456", "type":"drugorder", "order":"penicillin 500 mg orally twice daily for 2 weeks" }

 

2. Install a module to add support for referral orders:

Install a referrals module that adds a referralorder type of order.  Presumably, by introducing a new concept class for referral order, along with a handler for the new order type and, of course, a ReferralOrder class that extends Order.

 

3. Create a referral order for my patient:

POST order

{ "patient":"123-456", "type":"referralorder", "order":"cardiology referral please evaluate for rheumatic heart disease" }

 

4. Find all the active orders for my patient:

GET order?patient=123-456

[

{ "patient":"123-456", "type":"drugorder", "order":"penicillin 500 mg orally twice daily for 2 weeks", "uri":"…" },

{ "patient":"123-456", "type":"referralorder", "order":"cardiology referral please evaluate for rheumatic heart disease", "uri":"…" }

]

 

Is that close?

 

-Burke

 

On Tue, Apr 24, 2012 at 9:21 PM, Darius Jazayeri <[hidden email]> wrote:

On RESTWS-243 I've attached a patch that contains my implementation that I'm now ready to commit. (It's what Roger was responding to in his email.)

 

I'm going to hold off on committing until after tomorrow's design call in case we have time to discuss this a bit.

 

Basically what I've done is allow resources to indicate that they support subclasses, and if they do, they'll have all appropriate DelegatingSubclassHandlers registered with them via spring.

 

The idea is that we're hiding subclasses from the consumer of the REST API. As far as they're concerned there is a single "order" resource, which has documents of different types (e.g. "order", "drugorder", etc.) Behind the scenes each type is a subclass, but in REST that's not visible, rather they're exposed as different document types. 

 

DelegatingSubclassHandler encapsulates the methods subclasses need to specify for their owning resource.

 

Here is the functionality that my approach supports:

 

GET order

  • gets all orders regardless of type
  • each document in the list may have different types
  • the top-level resource does the db query and delegates to the appropriate handler to convert each result

GET order?t=drugorder

  • gets all orders whose type is drugorder
  • the subclass handler does the db query
  • In my implementation GET order?t=order (i.e. get only things that are order stubs) is not allowed

GET order?patient=<uuid>

  • gets all orders for the given patient
  • each document in the list may have different types

GET order?patient=<uuid>&t=drugorder

  • gets all orders for the given patient whose type is drugorder
  • if the subclass handler defines a suitable method, we use it, otherwise the top-level resource queries by patient, and manually filters out by type

GET order/<uuid>

  • returns a specific order
  • representations are defined by the type, e.g. { "type": "drugorder", "dose": "100", ... }

POST order

  • creates a new order
  • you must specify the type in the content, e.g. { "type": "drugorder", "dose": "100", ... }
  • allowed properties are defined by the type

POST order/<uuid>

  • modifies an existing order
  • you are not allowed to change the type
  • allowed properties are defined by the type

DELETE order/<uuid>

  • voids an order
  • handled by the top-level resource, not the subclass handler

DELETE order/<uuid>?purge=true

  • purges an order
  • handled by the top-level resource, not the subclass handler

@Roger, can you please phrase your counterproposal in terms of the additional functionality that you want to expose beyond these methods?

 

-Darius

 

On Mon, Apr 23, 2012 at 9:54 AM, Burke Mamlin <[hidden email]> wrote:

Roger,

 

Could you provide some concrete examples?

 

-Burke

 

On Mon, Apr 23, 2012 at 8:07 AM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

AN ALTERNATIVE APPROACH TO SUBCLASS IMPLEMENTATION

In this approach, subclass resources remain resources, just are mapped via the superclass. Substantially all operations are delegated to the appropriate subclass resource. This is motivated by simplicity, validation and representation considerations. It is also motivated by a privilege issue I just realized – the people authorized to create/update a lab order are likely different from those allowed to create/update a drug order. I have assumed that the privilege of updating a parent class field in subclass record requires only parent class privileges

I don't believe this alternative requires much in the way of changes to existing code other than mappings; adding the controller and resource for each superclass; some changes in asRepresentation where it finds the right representation to use; and some changes where updates are going on to allow the possibility that the fields to be looked for in the request body may come from an object which is a parent class of the object being updated. It eliminates the need for the two variable lists used by Darius (although they could still be used if it was thought they added value). I still think it is a good idea to treat ActiveLists as an abstract class so that the AllergyList and the ProblemList appear to be two different superclasses.

All members of a class hierarchy request a mapping to /<superclass>?t=<subclass>. The superclass also requests a mapping to /<superclass>?!t. I have tried to make all the calls of /<superclass>?!t work the same as calls to /<superclass>?t=<superclass>, if I have missed please let me know.  It might be worthwhile to extend Resource to SuperclassResource to make the coding of the type-free calls easier.  All representations include a t virtual field which contains the actual subclass of the object being represented.

1. GET <superclass>?t=<subclass>&v=<rep>, GET <superclass>?!t

Purpose is to get all records of a particular subclass in a formatted representation. Spring routes this to the subclass controller. The subclass resource uses getAll to get a (polymorphic) list of objects. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

2. GET <superclass>?t=<subclass>&v=<rep>&q=<search param>, GET <superclass>?!t&v=<rep>&q=<search param>

Purpose is to use the standard query for a subclass to get a formatted representation. Spring routes this to the subclass controller. The subclass resource uses doSearch to get a (polymorphic) list of objects. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

3. GET <superclass>?t=<subclass>&v=<rep>&<custom search>=<search param>

Purpose is to do a custom search that is defined at some level of the class hierarchy.  Spring routes this to the subclass controller. The subclass controller creates a resource for its type and delegates the search to it. The resource uses service methods to do the search to produce a (polymorphic) list of objects. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

4. GET <superclass>/<uuid>?!t&v=<rep>

Purpose is to get all records of a particular subclass in a formatted representation. Spring routes this to the superclass controller. The superclass resource uses getByUuid to get an object. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

5. GET <superclass>/<uuid>?t=<subclass>&v=<rep>

Purpose is to use a representation from a parent class. Like 5, except after getting by Uuid, the subclass resource coerces the result to be its type. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the supertype has been searched, at which point a rep does not exist error is thrown.

6. POST <superclass>?t=<subclass> {body}

Purpose is to create a new object of type <subclass>. The subclass resource creates a new object whose fields are extracted from the body (no need to know. The subclass resource save method uses services method to save the object; validation takes place in the save method.

7. POST <superclass>/<uuid>?!t {body}

Purpose is to update an object of any subclass. The type is determined and the operation is delegated to the resource of the actual type.

8. POST <superclass/<uuid>?t=<subclass> {body}

Purpose is to update an object using only parent-level fields. This is to avoid additional privileges at the actual subclass level. The type is deermined and the operation is delegated to the resource of the declared type. Throws an error if the actual type is not a subclass of the type declared.

9. DELETE <superclass>/<uuid>?!t&!purge, DELETE <superclass>/<uuid>?t=<subclass>&!purge

Purpose is to delete an object of any subclass. The type is determined and the operation is delegated to the resource of the actual type. The subclass, if present, must match the actual type of the resource.

10. DELETE <superclass>/<uuid>?purge&!t, DELETE <superclass>/<uuid>?purge

Purpose is to purge an object of any subclass. The type is determine and the operation is delegated to the resource of the actual type. The subclass, if present, must match the actual type of the resource.


[hidden email] from OpenMRS Developers' mailing list

Burke Mamlin Burke Mamlin
Reply | Threaded
Open this post in threaded view
|

Re: Sateesh's comment on encounter creation

Well, we have encounterless obs and we definitely want to be able to access observations without going through encounters.  Are you saying that because of this, we can't post a new encounter containing observations (and eventually orders, notes, and form data)?  That, for an encounter with 100 obs, we would have to do 101 posts instead of one?

-Burke

On Fri, Apr 27, 2012 at 12:02 PM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

I believe that Darius, Rafal and I are pretty much in agreement that sub-resources should be creatable from resources, but that full resources should be created separately and then either used as a uuid reference or added explicitly to a multi-object collection.  Since there's no a priori way to know whether a link has been put in the record itself, in a mapping object that's also a (sub-)resource, or a mapping object that's only a mapping object, the REST machinery has difficulty in figuring out what to do; we may need to have add/remove member service or pojo methods to handle collections.  Rafal found that bulk replacement of collection members produces Hibernate errors.

 

My idea in creating the page https://wiki.openmrs.org/display/docs/Standard_Modeling+of+Resource+Relationship+Types was to document the types of relationship we have in OpenMRS and how those relationships should be modeled in REST.  It still needs input.

 

In the case of encounter, the answer to Sateesh's comment depends on whether there can be encounterless obs.  If there are no encounterless obs, then obs should be a sub-resource of encounter and thus creatable.  If there are encounterless obs, then obs could be a full resource (as it is now) and the encounter and the obs should be created separately and then the obs added to the encounter.   However, obs could be a sub-resource of patient (there are no patientless obs), in which case you could create obs while creating a patient.

 

Further, every encounter has an encounter type.  Encounter type is a resource, so it would have to be created separately and then used within the encounter being created.  Likewise, you couldn't create a location or a provider/user while creating the encounter.

 

If there were a way to inform the REST machinery what kind of a linkage exists between two resources, then it might be possible to weaken these constraints.  But I don't think we'd ever want a complete free-for-all; the idea of creating a new encounter type or location on the fly seems pretty dubious to me.

 

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Burke Mamlin
Sent: Friday, April 27, 2012 9:49 AM
To: [hidden email]
Subject: Re: [OPENMRS-DEV] An Alternative Approach to Subclass Implementation

 

Good point, Sateesh.  When creating an encounter, you could be able to create all of the associated data as well within a single call; in fact, this should be the preferred method for creating an encounter.  This would include observations (obs) now, include orders as they are added to REST, and eventually include notes & form data once those are fully realized.

 

Cheers,

 

-Burke

On Fri, Apr 27, 2012 at 4:23 AM, Sateesh Babu <[hidden email]> wrote:

One question (concern) here. For example, if a client has to create a patient encounter with drug and lab orders, the client has to potentially make 2-3 (or even more) REST calls.
What if the first REST call succeeds and the next two fail or a combination like pass-fail-pass, pass-pass-fail etc. The failure can be because of any reason ranging from network failure, data corruption, bugs etc. REST inherently being a stateless protocol would leave the data on the server in an inconsistent state.
Instead, why not send the complete data in the transaction in one single go. So for the same above example, one single data object could be created with the encounter + drug order + lab order. This way if the transaction fails, then atleast the data is left in a consistent state. If data size is a concern, we can always use compression. There could be a meta-data object in the beginning of the object to describe what the contents of the json object are. Something like:
element {
  encounter: {
                   ...
                  }
  drug-order: {
                   ...
                   }
  lab-order: {
                  ...
                }
}

--
Sateesh

 

On Fri, Apr 27, 2012 at 11:22 AM, Darius Jazayeri <[hidden email]> wrote:

Yeah, I coded it such that classes have to actively choose to make themselves subclassable, but it should only take a few lines of code to change that.

 

-Darius

 

On Thu, Apr 26, 2012 at 8:28 PM, Ben Wolfe <[hidden email]> wrote:

So I was in Rogers camp until this argument.  There were three types of subclassing that we defined.  The patient/person subclassing is a first class resource, so both are primary documents. The ProblemList/Allergy subclassing completely hide their parents, so they are primary documents. However, these Order subclasses are second class citizens of sorts. They depend on the parent Order resource.  I don't like introducing yet another *Handler class, but calling them a resource is mixing ideas.

 

I'd favor making every class subclassable by default.  I think you had reversed Darius.

 

Ben

 

On Thu, Apr 26, 2012 at 9:42 PM, Darius Jazayeri <[hidden email]> wrote:

Hi Roger,

 

So, as you can probably imagine, I disagree. :-)

 

I think the whole point is that it's not appropriate to expose subclasses as if they were resources. I'm explicitly saying they're not resources, and that consumers of the REST API should not think of them as resources, but rather should think "the order resource tells me about orders, and drug orders are one type of order".

 

So I don't think that exposing a drugorder URI that redirects to order?t=drugorder is right. Also, using HTTP 307 (Temporary Redirect) means that the client has to make 2 requests to do anything (unless they cheat by going straight to the order resource).

 

What do others think?

 

I refactored my code a bit so now DelegatingSubclassHandler declares save() and purge() methods, which have a default implementation in BaseDelegatingSubclassHandler. Is that a good enough solution?

 

-Darius

 

On Thu, Apr 26, 2012 at 12:50 PM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

So I have been trying to figure out if there is any way to break out of this back and forth.  I have decided that what I really want to see is subclass resources and controllers that act as if they were full-fledged resources and preserve the subclass hierarchy but don't screw up caching.  So I am going to suggest the following:

 

(1) Subclass resources should be addressed like full resources, ws/<subclass>/... but the subclass controller should issue a 307 redirect to ws/<superclass>/...?t=<subclass>.  Subclasses can have child classes represented as sub-resources, and sub-resources can be created when a resource is created.  It is OK for subclass controllers to hand custom queries directly to their resource without passing through the superclass.

 

(2) The superclass should delegate to the subclass for representations, searches and saves.  If the named representation or search does not exist, my choice would be to ascend the class hierarchy.  I understand this might require some registration, but only to the parent.

 

(3) The superclass resource would have to implement the superclass representations, searches and saves.  It would also have to implement the delegation mechanism for subclasses.

 

(4) Subclass name will be returned as part of the standard representations.  Subclass name will not be needed for POST.

 

So given our Order -->  DrugOrder --> HIVDrugOrder class hierarchy

 

POST ws/order {body} creates an order which is filled from the body and validated and saved

POST ws/drugorder {body} is redirected to ws/order?t=drugorder {body} which delegates to the drugorder resource to create a drugorder which is filled from the body and delegated to the drugorder resource for validation and saving

POST ws/hivdrugorder {body} is redirected to ws/order?t=hivdrugorder {body} which delegates to the hivdrugorder resource to create an hivdrugorder which is filled from the body and delegated to the hivdrugorder resource for validation and saving.

 

POST ws/order/uuid {body} retrieves the order which is filled from the body and validated and saved

POST ws/drugorder/uuid {body} is redirected to ws/order?t=drugorder {body} which retrieves the order which is filled from the body and delegated to the drugorder resource for validation and saving

POST ws/hivdrugorder/uuid {body} is redirected to ws/order?t=hivdrugorder {body} which retrieves the order which is filled from the body and delegated to the hivdrugorder resource for validation and saving

 

GET ws/order/uuid?v=<rep> retrieves the order whose type is determined and delegated to the subclass for generation of the rep.  If the object with the uuid is not assignable from order or the rep does not exist, an error is thrown. 

GET ws/drugorder/uuid?v=<rep> is redirected to ws/order/uuid&v=<rep>?t=drugorder.  The search for the representation is done in the resoruce drugorder.

GET ws/hivdrugorder/uuid?v=<rep> is redirected to ws/order/uuid&v=<rep>?t=drugorder.  The search for the representation is done in the resource hivdrugorder.

 

GET ws/order&v=<rep> retrieves all orders; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named rep does not exist, an error is thrown.

GET ws/drugorder&v=<rep> is redirected to ws/order?v=rep&t=drugorder.  The superclass delegates to the class, which retrieves all drugorders; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named rep does not exist, an error is thrown.

GET ws/hivdrugorder&v=<rep> is redirected to ws/order?v=rep&t=hivdrugorder.  The superclass delegates to the class, which retrieves all hivdrugorders; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named rep does not exist, an error is thrown.

 

GET ws/order&<query>=<param> retrieves all orders satisfying the condition; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named query does not exist, an error is thrown.

GET ws/drugorder&<query>=<param> is redirected to ws/order?<query>=<param>&t=drugorder.  The superclass delegates to the subclass to retrieve all orders satisfying the condition; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named query does not exist, an error is thrown.

GET ws/hivdrugorder&<query>=<param> is redirected to ws/order?<query>=<param>&t=hivdrugorder.  The superclass delegates to the subclass to retrieve all orders satisfying the condition; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named query does not exist, an error is thrown.

 

DELETE is handled by the superclass.

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Darius Jazayeri
Sent: Thursday, April 26, 2012 11:34 AM


To: [hidden email]
Subject: Re: [OPENMRS-DEV] An Alternative Approach to Subclass Implementation

 

To be clear, the POSTs that Burke showed a couple of emails ago will work exactly as written, except of course that he simplified the actual properties into just "order". And the response to the GET that he showed will also be as he wrote it (except that uri is within links and the whole thing is in a PageableResults object). And the URIs he gave for the POSTs and GET are exactly right, there is no need for a ?t=type parameter.

 

Responses inline below.

 

-Darius

On Thu, Apr 26, 2012 at 7:14 AM, Burke Mamlin <[hidden email]> wrote:

On Thu, Apr 26, 2012 at 8:32 AM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

Burke --

     Regardless of whether it's Darius or me, each of the posts 1 and 3 would need a t=<type> parameter. 

     In my design, t is always virtual, it is the classname of the object; this avoids issues like having to use a global variable or predefined value or concept map to know the value of order type for drug order.  It also leaves order type available for distinctions that might be important to the facility without affecting the data model (say a narcotic drug order).  I'm not sure where Darius is at on this; at one point he required a discriminator field with a known name (registered?), but I think he has become more virtual.  The point is, you can't change the value of a discriminator field, so why include it in the POST body?  I do return it in the body on GET, as does Darius.

 

You can't change the concept of an order either, so would that move the URL as well?  I don't like the idea of putting some of the properties in the URL and others in the data.  You're right that you can't change the type of order; however, you must specify it when creating an order.

 

I'm also not thrilled about clients needing to know the Java class name for every order type.  There are not an infinite number of orderables.  Most implementations will be able to get by with two up front (drugs & tests).  Even for an enterprise-level inpatient system, you can get by with only a dozen or so (e.g., activities, precautions, diet, call orders, drugs, lab tests, radiology tests, procedures, nursing orders, referrals, education/handouts).  Actually, each of these basic types could be represented by a concept class and that class name could be used.

 

As I've coded it, the java class names are not exposed. To create a drug order you say ' type: "drugorder" '.

(@Roger, as coded you only use a &t=drugorder when you're doing a get-all or search, to say you only want objects of that type.)

 

 

     Also, Darius and I differ about privileges and validation at posts 1 and 3.  In Darius' design, both commands would require the privileges to create an order and would validate per order's requirements.  In my design, post 1 would require the privileges to create a drug order and would validate per drug order's requirements, while post 3 would require the privileges to create a referral order and would validate per referral order's requirements. 

 

There should be a top level privilege for ordering in general.  My assumption in posting the orders was that the REST document received would be passed to the order type-specific handler, so permissions & validation could be type-specific as you describe – i.e., we would have separate validators for each basic type of order.

 

The underlying Java API is built such that if you have Manage Orders privilege, you can do OrderService.saveOrder(anyOrderSubclass). My implementation does the actual save using this.

As coded, the type-specific-handler is only allowed to specify a "delegating resource description" which indicates what fields may be created/edited, it doesn't actually get a "save(T)" call. This is an oversight, due to the fact that none of our core resources actually do any custom save/validation logic that isn't routed through the resource description.

I can add a save(T) method on the subtype handlers, and let the base implementation of it just call the resource's save method.

 

 

    At get 4, both designs would use the default rep associated with the subclass of the object.  However, suppose you wanted to display all the types of order with just the order fields and some combination of subclass-specific fields in a details column.  In my design, you could define a named representation (say, COMPRESSED) that would be defined differently for different subclasses; you could even define it at a parent level (say drug order) and it could be used by children (like HIV drug order).  I don't believe you can do this in Darius' design.

 

Again, I think I agree with you here.  I was assuming that the generation of representation for each order would be delegated to it's type-specific handler, so if you asked for a FOOBAR representation of all the patient's orders, the API would iterate through the orders, asking for the representation for each from its type-specific handler.

 

@Darius – is that not what you're doing?  i.e., were you creating something that would not defer calls to type-specific handlers?

 

I do delegate to type-specific handlers. You can also define named representations on subclasses. What you cannot do is define a representation in the top-level resource, and get a subtype in that representation. E.g. if order defines a "foobar" rep, that is not inherited by drugorder. (Though drugorder could of course define that named representation also, as a one-liner that calls the parent resource.)

 

 

    With Darius, at step 0 (before the drug order is built in), you have to create a resource and controller for the order superclass; you have to create a handler, and a controller if you want custom searches, for drug order.  The superclass resource will contain all the methods of the current design plus the non-final declaration.

 

Yes.  The idea is that drug orders are provided for you.  So "you" will not have to do these steps.

 

  Darius depends on each resource and handler having the variable lists created by RESTWS-226, which I do not.  The drug order handler will contain its registration plus most of the methods of a resource (no save, delete or purge).  At step 2, you would need to build another handler and possibly a controller.

    With me, at step 0 you have to create a resource and controller for the hierarchy, a resource and controller for the superclass (order), and a resource and controller for the subclass (drug order).  The controllers are one-liners unless you want to add custom searches; they are just like the current design.  The hierarchy resource could also be a one-liner, with the actual delegations taking place in an abstract class using generics.  The resource for the superclass and the subclass would look just like the current design.  At step 2, you would create another controller and resource for referral order and that would also look just like the current design.

 

If you're creating a separate resource for each type, then doesn't that imply a different URL for each order type?

 

     In our discussion, Darius and I identified some situations (as I recall relating to validation) where it would be necessary under his design to override the supplied core resource with a user-defined resource that extended the core resource.  This could not be done independently for more than one subclass, and would require a custom build.  This would not be necessary with my design.

 

Can you provide an concrete example?  I'm presuming each order type has an associated handler/validator.

 

-Burke

 

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Burke Mamlin
Sent: Wednesday, April 25, 2012 5:08 PM


To: [hidden email]
Subject: Re: [OPENMRS-DEV] An Alternative Approach to Subclass Implementation

 

Ok, so if I want to create a drug order and a referral order for patient with uuid 123-456, then request all orders for the patient, it would look something like this (simplifying the order details for a simpler example)?

 

1. Create a drug order for my patient; they're built-in:

POST order

{ "patient":"123-456", "type":"drugorder", "order":"penicillin 500 mg orally twice daily for 2 weeks" }

 

2. Install a module to add support for referral orders:

Install a referrals module that adds a referralorder type of order.  Presumably, by introducing a new concept class for referral order, along with a handler for the new order type and, of course, a ReferralOrder class that extends Order.

 

3. Create a referral order for my patient:

POST order

{ "patient":"123-456", "type":"referralorder", "order":"cardiology referral please evaluate for rheumatic heart disease" }

 

4. Find all the active orders for my patient:

GET order?patient=123-456

[

{ "patient":"123-456", "type":"drugorder", "order":"penicillin 500 mg orally twice daily for 2 weeks", "uri":"…" },

{ "patient":"123-456", "type":"referralorder", "order":"cardiology referral please evaluate for rheumatic heart disease", "uri":"…" }

]

 

Is that close?

 

-Burke

 

On Tue, Apr 24, 2012 at 9:21 PM, Darius Jazayeri <[hidden email]> wrote:

On RESTWS-243 I've attached a patch that contains my implementation that I'm now ready to commit. (It's what Roger was responding to in his email.)

 

I'm going to hold off on committing until after tomorrow's design call in case we have time to discuss this a bit.

 

Basically what I've done is allow resources to indicate that they support subclasses, and if they do, they'll have all appropriate DelegatingSubclassHandlers registered with them via spring.

 

The idea is that we're hiding subclasses from the consumer of the REST API. As far as they're concerned there is a single "order" resource, which has documents of different types (e.g. "order", "drugorder", etc.) Behind the scenes each type is a subclass, but in REST that's not visible, rather they're exposed as different document types. 

 

DelegatingSubclassHandler encapsulates the methods subclasses need to specify for their owning resource.

 

Here is the functionality that my approach supports:

 

GET order

  • gets all orders regardless of type
  • each document in the list may have different types
  • the top-level resource does the db query and delegates to the appropriate handler to convert each result

GET order?t=drugorder

  • gets all orders whose type is drugorder
  • the subclass handler does the db query
  • In my implementation GET order?t=order (i.e. get only things that are order stubs) is not allowed

GET order?patient=<uuid>

  • gets all orders for the given patient
  • each document in the list may have different types

GET order?patient=<uuid>&t=drugorder

  • gets all orders for the given patient whose type is drugorder
  • if the subclass handler defines a suitable method, we use it, otherwise the top-level resource queries by patient, and manually filters out by type

GET order/<uuid>

  • returns a specific order
  • representations are defined by the type, e.g. { "type": "drugorder", "dose": "100", ... }

POST order

  • creates a new order
  • you must specify the type in the content, e.g. { "type": "drugorder", "dose": "100", ... }
  • allowed properties are defined by the type

POST order/<uuid>

  • modifies an existing order
  • you are not allowed to change the type
  • allowed properties are defined by the type

DELETE order/<uuid>

  • voids an order
  • handled by the top-level resource, not the subclass handler

DELETE order/<uuid>?purge=true

  • purges an order
  • handled by the top-level resource, not the subclass handler

@Roger, can you please phrase your counterproposal in terms of the additional functionality that you want to expose beyond these methods?

 

-Darius

 

On Mon, Apr 23, 2012 at 9:54 AM, Burke Mamlin <[hidden email]> wrote:

Roger,

 

Could you provide some concrete examples?

 

-Burke

 

On Mon, Apr 23, 2012 at 8:07 AM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

AN ALTERNATIVE APPROACH TO SUBCLASS IMPLEMENTATION

In this approach, subclass resources remain resources, just are mapped via the superclass. Substantially all operations are delegated to the appropriate subclass resource. This is motivated by simplicity, validation and representation considerations. It is also motivated by a privilege issue I just realized – the people authorized to create/update a lab order are likely different from those allowed to create/update a drug order. I have assumed that the privilege of updating a parent class field in subclass record requires only parent class privileges

I don't believe this alternative requires much in the way of changes to existing code other than mappings; adding the controller and resource for each superclass; some changes in asRepresentation where it finds the right representation to use; and some changes where updates are going on to allow the possibility that the fields to be looked for in the request body may come from an object which is a parent class of the object being updated. It eliminates the need for the two variable lists used by Darius (although they could still be used if it was thought they added value). I still think it is a good idea to treat ActiveLists as an abstract class so that the AllergyList and the ProblemList appear to be two different superclasses.

All members of a class hierarchy request a mapping to /<superclass>?t=<subclass>. The superclass also requests a mapping to /<superclass>?!t. I have tried to make all the calls of /<superclass>?!t work the same as calls to /<superclass>?t=<superclass>, if I have missed please let me know.  It might be worthwhile to extend Resource to SuperclassResource to make the coding of the type-free calls easier.  All representations include a t virtual field which contains the actual subclass of the object being represented.

1. GET <superclass>?t=<subclass>&v=<rep>, GET <superclass>?!t

Purpose is to get all records of a particular subclass in a formatted representation. Spring routes this to the subclass controller. The subclass resource uses getAll to get a (polymorphic) list of objects. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

2. GET <superclass>?t=<subclass>&v=<rep>&q=<search param>, GET <superclass>?!t&v=<rep>&q=<search param>

Purpose is to use the standard query for a subclass to get a formatted representation. Spring routes this to the subclass controller. The subclass resource uses doSearch to get a (polymorphic) list of objects. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

3. GET <superclass>?t=<subclass>&v=<rep>&<custom search>=<search param>

Purpose is to do a custom search that is defined at some level of the class hierarchy.  Spring routes this to the subclass controller. The subclass controller creates a resource for its type and delegates the search to it. The resource uses service methods to do the search to produce a (polymorphic) list of objects. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

4. GET <superclass>/<uuid>?!t&v=<rep>

Purpose is to get all records of a particular subclass in a formatted representation. Spring routes this to the superclass controller. The superclass resource uses getByUuid to get an object. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

5. GET <superclass>/<uuid>?t=<subclass>&v=<rep>

Purpose is to use a representation from a parent class. Like 5, except after getting by Uuid, the subclass resource coerces the result to be its type. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the supertype has been searched, at which point a rep does not exist error is thrown.

6. POST <superclass>?t=<subclass> {body}

Purpose is to create a new object of type <subclass>. The subclass resource creates a new object whose fields are extracted from the body (no need to know. The subclass resource save method uses services method to save the object; validation takes place in the save method.

7. POST <superclass>/<uuid>?!t {body}

Purpose is to update an object of any subclass. The type is determined and the operation is delegated to the resource of the actual type.

8. POST <superclass/<uuid>?t=<subclass> {body}

Purpose is to update an object using only parent-level fields. This is to avoid additional privileges at the actual subclass level. The type is deermined and the operation is delegated to the resource of the declared type. Throws an error if the actual type is not a subclass of the type declared.

9. DELETE <superclass>/<uuid>?!t&!purge, DELETE <superclass>/<uuid>?t=<subclass>&!purge

Purpose is to delete an object of any subclass. The type is determined and the operation is delegated to the resource of the actual type. The subclass, if present, must match the actual type of the resource.

10. DELETE <superclass>/<uuid>?purge&!t, DELETE <superclass>/<uuid>?purge

Purpose is to purge an object of any subclass. The type is determine and the operation is delegated to the resource of the actual type. The subclass, if present, must match the actual type of the resource.


[hidden email] from OpenMRS Developers' mailing list



[hidden email] from OpenMRS Developers' mailing list
Darius Jazayeri-3 Darius Jazayeri-3
Reply | Threaded
Open this post in threaded view
|

Re: Sateesh's comment on encounter creation

For the client's sake, we have to provide a way to create an encounter with all its obs and orders in a single POST.

(I do worry, because this won't invalidate the caches for obs and orders, but I don't think asking the client to do 101 POSTs to create an encounter is an option...)

-Darius

On Fri, Apr 27, 2012 at 10:25 AM, Burke Mamlin <[hidden email]> wrote:
Well, we have encounterless obs and we definitely want to be able to access observations without going through encounters.  Are you saying that because of this, we can't post a new encounter containing observations (and eventually orders, notes, and form data)?  That, for an encounter with 100 obs, we would have to do 101 posts instead of one?

-Burke


On Fri, Apr 27, 2012 at 12:02 PM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

I believe that Darius, Rafal and I are pretty much in agreement that sub-resources should be creatable from resources, but that full resources should be created separately and then either used as a uuid reference or added explicitly to a multi-object collection.  Since there's no a priori way to know whether a link has been put in the record itself, in a mapping object that's also a (sub-)resource, or a mapping object that's only a mapping object, the REST machinery has difficulty in figuring out what to do; we may need to have add/remove member service or pojo methods to handle collections.  Rafal found that bulk replacement of collection members produces Hibernate errors.

 

My idea in creating the page https://wiki.openmrs.org/display/docs/Standard_Modeling+of+Resource+Relationship+Types was to document the types of relationship we have in OpenMRS and how those relationships should be modeled in REST.  It still needs input.

 

In the case of encounter, the answer to Sateesh's comment depends on whether there can be encounterless obs.  If there are no encounterless obs, then obs should be a sub-resource of encounter and thus creatable.  If there are encounterless obs, then obs could be a full resource (as it is now) and the encounter and the obs should be created separately and then the obs added to the encounter.   However, obs could be a sub-resource of patient (there are no patientless obs), in which case you could create obs while creating a patient.

 

Further, every encounter has an encounter type.  Encounter type is a resource, so it would have to be created separately and then used within the encounter being created.  Likewise, you couldn't create a location or a provider/user while creating the encounter.

 

If there were a way to inform the REST machinery what kind of a linkage exists between two resources, then it might be possible to weaken these constraints.  But I don't think we'd ever want a complete free-for-all; the idea of creating a new encounter type or location on the fly seems pretty dubious to me.

 

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Burke Mamlin
Sent: Friday, April 27, 2012 9:49 AM
To: [hidden email]
Subject: Re: [OPENMRS-DEV] An Alternative Approach to Subclass Implementation

 

Good point, Sateesh.  When creating an encounter, you could be able to create all of the associated data as well within a single call; in fact, this should be the preferred method for creating an encounter.  This would include observations (obs) now, include orders as they are added to REST, and eventually include notes & form data once those are fully realized.

 

Cheers,

 

-Burke

On Fri, Apr 27, 2012 at 4:23 AM, Sateesh Babu <[hidden email]> wrote:

One question (concern) here. For example, if a client has to create a patient encounter with drug and lab orders, the client has to potentially make 2-3 (or even more) REST calls.
What if the first REST call succeeds and the next two fail or a combination like pass-fail-pass, pass-pass-fail etc. The failure can be because of any reason ranging from network failure, data corruption, bugs etc. REST inherently being a stateless protocol would leave the data on the server in an inconsistent state.
Instead, why not send the complete data in the transaction in one single go. So for the same above example, one single data object could be created with the encounter + drug order + lab order. This way if the transaction fails, then atleast the data is left in a consistent state. If data size is a concern, we can always use compression. There could be a meta-data object in the beginning of the object to describe what the contents of the json object are. Something like:
element {
  encounter: {
                   ...
                  }
  drug-order: {
                   ...
                   }
  lab-order: {
                  ...
                }
}

--
Sateesh

 

On Fri, Apr 27, 2012 at 11:22 AM, Darius Jazayeri <[hidden email]> wrote:

Yeah, I coded it such that classes have to actively choose to make themselves subclassable, but it should only take a few lines of code to change that.

 

-Darius

 

On Thu, Apr 26, 2012 at 8:28 PM, Ben Wolfe <[hidden email]> wrote:

So I was in Rogers camp until this argument.  There were three types of subclassing that we defined.  The patient/person subclassing is a first class resource, so both are primary documents. The ProblemList/Allergy subclassing completely hide their parents, so they are primary documents. However, these Order subclasses are second class citizens of sorts. They depend on the parent Order resource.  I don't like introducing yet another *Handler class, but calling them a resource is mixing ideas.

 

I'd favor making every class subclassable by default.  I think you had reversed Darius.

 

Ben

 

On Thu, Apr 26, 2012 at 9:42 PM, Darius Jazayeri <[hidden email]> wrote:

Hi Roger,

 

So, as you can probably imagine, I disagree. :-)

 

I think the whole point is that it's not appropriate to expose subclasses as if they were resources. I'm explicitly saying they're not resources, and that consumers of the REST API should not think of them as resources, but rather should think "the order resource tells me about orders, and drug orders are one type of order".

 

So I don't think that exposing a drugorder URI that redirects to order?t=drugorder is right. Also, using HTTP 307 (Temporary Redirect) means that the client has to make 2 requests to do anything (unless they cheat by going straight to the order resource).

 

What do others think?

 

I refactored my code a bit so now DelegatingSubclassHandler declares save() and purge() methods, which have a default implementation in BaseDelegatingSubclassHandler. Is that a good enough solution?

 

-Darius

 

On Thu, Apr 26, 2012 at 12:50 PM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

So I have been trying to figure out if there is any way to break out of this back and forth.  I have decided that what I really want to see is subclass resources and controllers that act as if they were full-fledged resources and preserve the subclass hierarchy but don't screw up caching.  So I am going to suggest the following:

 

(1) Subclass resources should be addressed like full resources, ws/<subclass>/... but the subclass controller should issue a 307 redirect to ws/<superclass>/...?t=<subclass>.  Subclasses can have child classes represented as sub-resources, and sub-resources can be created when a resource is created.  It is OK for subclass controllers to hand custom queries directly to their resource without passing through the superclass.

 

(2) The superclass should delegate to the subclass for representations, searches and saves.  If the named representation or search does not exist, my choice would be to ascend the class hierarchy.  I understand this might require some registration, but only to the parent.

 

(3) The superclass resource would have to implement the superclass representations, searches and saves.  It would also have to implement the delegation mechanism for subclasses.

 

(4) Subclass name will be returned as part of the standard representations.  Subclass name will not be needed for POST.

 

So given our Order -->  DrugOrder --> HIVDrugOrder class hierarchy

 

POST ws/order {body} creates an order which is filled from the body and validated and saved

POST ws/drugorder {body} is redirected to ws/order?t=drugorder {body} which delegates to the drugorder resource to create a drugorder which is filled from the body and delegated to the drugorder resource for validation and saving

POST ws/hivdrugorder {body} is redirected to ws/order?t=hivdrugorder {body} which delegates to the hivdrugorder resource to create an hivdrugorder which is filled from the body and delegated to the hivdrugorder resource for validation and saving.

 

POST ws/order/uuid {body} retrieves the order which is filled from the body and validated and saved

POST ws/drugorder/uuid {body} is redirected to ws/order?t=drugorder {body} which retrieves the order which is filled from the body and delegated to the drugorder resource for validation and saving

POST ws/hivdrugorder/uuid {body} is redirected to ws/order?t=hivdrugorder {body} which retrieves the order which is filled from the body and delegated to the hivdrugorder resource for validation and saving

 

GET ws/order/uuid?v=<rep> retrieves the order whose type is determined and delegated to the subclass for generation of the rep.  If the object with the uuid is not assignable from order or the rep does not exist, an error is thrown. 

GET ws/drugorder/uuid?v=<rep> is redirected to ws/order/uuid&v=<rep>?t=drugorder.  The search for the representation is done in the resoruce drugorder.

GET ws/hivdrugorder/uuid?v=<rep> is redirected to ws/order/uuid&v=<rep>?t=drugorder.  The search for the representation is done in the resource hivdrugorder.

 

GET ws/order&v=<rep> retrieves all orders; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named rep does not exist, an error is thrown.

GET ws/drugorder&v=<rep> is redirected to ws/order?v=rep&t=drugorder.  The superclass delegates to the class, which retrieves all drugorders; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named rep does not exist, an error is thrown.

GET ws/hivdrugorder&v=<rep> is redirected to ws/order?v=rep&t=hivdrugorder.  The superclass delegates to the class, which retrieves all hivdrugorders; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named rep does not exist, an error is thrown.

 

GET ws/order&<query>=<param> retrieves all orders satisfying the condition; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named query does not exist, an error is thrown.

GET ws/drugorder&<query>=<param> is redirected to ws/order?<query>=<param>&t=drugorder.  The superclass delegates to the subclass to retrieve all orders satisfying the condition; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named query does not exist, an error is thrown.

GET ws/hivdrugorder&<query>=<param> is redirected to ws/order?<query>=<param>&t=hivdrugorder.  The superclass delegates to the subclass to retrieve all orders satisfying the condition; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named query does not exist, an error is thrown.

 

DELETE is handled by the superclass.

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Darius Jazayeri
Sent: Thursday, April 26, 2012 11:34 AM


To: [hidden email]
Subject: Re: [OPENMRS-DEV] An Alternative Approach to Subclass Implementation

 

To be clear, the POSTs that Burke showed a couple of emails ago will work exactly as written, except of course that he simplified the actual properties into just "order". And the response to the GET that he showed will also be as he wrote it (except that uri is within links and the whole thing is in a PageableResults object). And the URIs he gave for the POSTs and GET are exactly right, there is no need for a ?t=type parameter.

 

Responses inline below.

 

-Darius

On Thu, Apr 26, 2012 at 7:14 AM, Burke Mamlin <[hidden email]> wrote:

On Thu, Apr 26, 2012 at 8:32 AM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

Burke --

     Regardless of whether it's Darius or me, each of the posts 1 and 3 would need a t=<type> parameter. 

     In my design, t is always virtual, it is the classname of the object; this avoids issues like having to use a global variable or predefined value or concept map to know the value of order type for drug order.  It also leaves order type available for distinctions that might be important to the facility without affecting the data model (say a narcotic drug order).  I'm not sure where Darius is at on this; at one point he required a discriminator field with a known name (registered?), but I think he has become more virtual.  The point is, you can't change the value of a discriminator field, so why include it in the POST body?  I do return it in the body on GET, as does Darius.

 

You can't change the concept of an order either, so would that move the URL as well?  I don't like the idea of putting some of the properties in the URL and others in the data.  You're right that you can't change the type of order; however, you must specify it when creating an order.

 

I'm also not thrilled about clients needing to know the Java class name for every order type.  There are not an infinite number of orderables.  Most implementations will be able to get by with two up front (drugs & tests).  Even for an enterprise-level inpatient system, you can get by with only a dozen or so (e.g., activities, precautions, diet, call orders, drugs, lab tests, radiology tests, procedures, nursing orders, referrals, education/handouts).  Actually, each of these basic types could be represented by a concept class and that class name could be used.

 

As I've coded it, the java class names are not exposed. To create a drug order you say ' type: "drugorder" '.

(@Roger, as coded you only use a &t=drugorder when you're doing a get-all or search, to say you only want objects of that type.)

 

 

     Also, Darius and I differ about privileges and validation at posts 1 and 3.  In Darius' design, both commands would require the privileges to create an order and would validate per order's requirements.  In my design, post 1 would require the privileges to create a drug order and would validate per drug order's requirements, while post 3 would require the privileges to create a referral order and would validate per referral order's requirements. 

 

There should be a top level privilege for ordering in general.  My assumption in posting the orders was that the REST document received would be passed to the order type-specific handler, so permissions & validation could be type-specific as you describe – i.e., we would have separate validators for each basic type of order.

 

The underlying Java API is built such that if you have Manage Orders privilege, you can do OrderService.saveOrder(anyOrderSubclass). My implementation does the actual save using this.

As coded, the type-specific-handler is only allowed to specify a "delegating resource description" which indicates what fields may be created/edited, it doesn't actually get a "save(T)" call. This is an oversight, due to the fact that none of our core resources actually do any custom save/validation logic that isn't routed through the resource description.

I can add a save(T) method on the subtype handlers, and let the base implementation of it just call the resource's save method.

 

 

    At get 4, both designs would use the default rep associated with the subclass of the object.  However, suppose you wanted to display all the types of order with just the order fields and some combination of subclass-specific fields in a details column.  In my design, you could define a named representation (say, COMPRESSED) that would be defined differently for different subclasses; you could even define it at a parent level (say drug order) and it could be used by children (like HIV drug order).  I don't believe you can do this in Darius' design.

 

Again, I think I agree with you here.  I was assuming that the generation of representation for each order would be delegated to it's type-specific handler, so if you asked for a FOOBAR representation of all the patient's orders, the API would iterate through the orders, asking for the representation for each from its type-specific handler.

 

@Darius – is that not what you're doing?  i.e., were you creating something that would not defer calls to type-specific handlers?

 

I do delegate to type-specific handlers. You can also define named representations on subclasses. What you cannot do is define a representation in the top-level resource, and get a subtype in that representation. E.g. if order defines a "foobar" rep, that is not inherited by drugorder. (Though drugorder could of course define that named representation also, as a one-liner that calls the parent resource.)

 

 

    With Darius, at step 0 (before the drug order is built in), you have to create a resource and controller for the order superclass; you have to create a handler, and a controller if you want custom searches, for drug order.  The superclass resource will contain all the methods of the current design plus the non-final declaration.

 

Yes.  The idea is that drug orders are provided for you.  So "you" will not have to do these steps.

 

  Darius depends on each resource and handler having the variable lists created by RESTWS-226, which I do not.  The drug order handler will contain its registration plus most of the methods of a resource (no save, delete or purge).  At step 2, you would need to build another handler and possibly a controller.

    With me, at step 0 you have to create a resource and controller for the hierarchy, a resource and controller for the superclass (order), and a resource and controller for the subclass (drug order).  The controllers are one-liners unless you want to add custom searches; they are just like the current design.  The hierarchy resource could also be a one-liner, with the actual delegations taking place in an abstract class using generics.  The resource for the superclass and the subclass would look just like the current design.  At step 2, you would create another controller and resource for referral order and that would also look just like the current design.

 

If you're creating a separate resource for each type, then doesn't that imply a different URL for each order type?

 

     In our discussion, Darius and I identified some situations (as I recall relating to validation) where it would be necessary under his design to override the supplied core resource with a user-defined resource that extended the core resource.  This could not be done independently for more than one subclass, and would require a custom build.  This would not be necessary with my design.

 

Can you provide an concrete example?  I'm presuming each order type has an associated handler/validator.

 

-Burke

 

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Burke Mamlin
Sent: Wednesday, April 25, 2012 5:08 PM


To: [hidden email]
Subject: Re: [OPENMRS-DEV] An Alternative Approach to Subclass Implementation

 

Ok, so if I want to create a drug order and a referral order for patient with uuid 123-456, then request all orders for the patient, it would look something like this (simplifying the order details for a simpler example)?

 

1. Create a drug order for my patient; they're built-in:

POST order

{ "patient":"123-456", "type":"drugorder", "order":"penicillin 500 mg orally twice daily for 2 weeks" }

 

2. Install a module to add support for referral orders:

Install a referrals module that adds a referralorder type of order.  Presumably, by introducing a new concept class for referral order, along with a handler for the new order type and, of course, a ReferralOrder class that extends Order.

 

3. Create a referral order for my patient:

POST order

{ "patient":"123-456", "type":"referralorder", "order":"cardiology referral please evaluate for rheumatic heart disease" }

 

4. Find all the active orders for my patient:

GET order?patient=123-456

[

{ "patient":"123-456", "type":"drugorder", "order":"penicillin 500 mg orally twice daily for 2 weeks", "uri":"…" },

{ "patient":"123-456", "type":"referralorder", "order":"cardiology referral please evaluate for rheumatic heart disease", "uri":"…" }

]

 

Is that close?

 

-Burke

 

On Tue, Apr 24, 2012 at 9:21 PM, Darius Jazayeri <[hidden email]> wrote:

On RESTWS-243 I've attached a patch that contains my implementation that I'm now ready to commit. (It's what Roger was responding to in his email.)

 

I'm going to hold off on committing until after tomorrow's design call in case we have time to discuss this a bit.

 

Basically what I've done is allow resources to indicate that they support subclasses, and if they do, they'll have all appropriate DelegatingSubclassHandlers registered with them via spring.

 

The idea is that we're hiding subclasses from the consumer of the REST API. As far as they're concerned there is a single "order" resource, which has documents of different types (e.g. "order", "drugorder", etc.) Behind the scenes each type is a subclass, but in REST that's not visible, rather they're exposed as different document types. 

 

DelegatingSubclassHandler encapsulates the methods subclasses need to specify for their owning resource.

 

Here is the functionality that my approach supports:

 

GET order

  • gets all orders regardless of type
  • each document in the list may have different types
  • the top-level resource does the db query and delegates to the appropriate handler to convert each result

GET order?t=drugorder

  • gets all orders whose type is drugorder
  • the subclass handler does the db query
  • In my implementation GET order?t=order (i.e. get only things that are order stubs) is not allowed

GET order?patient=<uuid>

  • gets all orders for the given patient
  • each document in the list may have different types

GET order?patient=<uuid>&t=drugorder

  • gets all orders for the given patient whose type is drugorder
  • if the subclass handler defines a suitable method, we use it, otherwise the top-level resource queries by patient, and manually filters out by type

GET order/<uuid>

  • returns a specific order
  • representations are defined by the type, e.g. { "type": "drugorder", "dose": "100", ... }

POST order

  • creates a new order
  • you must specify the type in the content, e.g. { "type": "drugorder", "dose": "100", ... }
  • allowed properties are defined by the type

POST order/<uuid>

  • modifies an existing order
  • you are not allowed to change the type
  • allowed properties are defined by the type

DELETE order/<uuid>

  • voids an order
  • handled by the top-level resource, not the subclass handler

DELETE order/<uuid>?purge=true

  • purges an order
  • handled by the top-level resource, not the subclass handler

@Roger, can you please phrase your counterproposal in terms of the additional functionality that you want to expose beyond these methods?

 

-Darius

 

On Mon, Apr 23, 2012 at 9:54 AM, Burke Mamlin <[hidden email]> wrote:

Roger,

 

Could you provide some concrete examples?

 

-Burke

 

On Mon, Apr 23, 2012 at 8:07 AM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

AN ALTERNATIVE APPROACH TO SUBCLASS IMPLEMENTATION

In this approach, subclass resources remain resources, just are mapped via the superclass. Substantially all operations are delegated to the appropriate subclass resource. This is motivated by simplicity, validation and representation considerations. It is also motivated by a privilege issue I just realized – the people authorized to create/update a lab order are likely different from those allowed to create/update a drug order. I have assumed that the privilege of updating a parent class field in subclass record requires only parent class privileges

I don't believe this alternative requires much in the way of changes to existing code other than mappings; adding the controller and resource for each superclass; some changes in asRepresentation where it finds the right representation to use; and some changes where updates are going on to allow the possibility that the fields to be looked for in the request body may come from an object which is a parent class of the object being updated. It eliminates the need for the two variable lists used by Darius (although they could still be used if it was thought they added value). I still think it is a good idea to treat ActiveLists as an abstract class so that the AllergyList and the ProblemList appear to be two different superclasses.

All members of a class hierarchy request a mapping to /<superclass>?t=<subclass>. The superclass also requests a mapping to /<superclass>?!t. I have tried to make all the calls of /<superclass>?!t work the same as calls to /<superclass>?t=<superclass>, if I have missed please let me know.  It might be worthwhile to extend Resource to SuperclassResource to make the coding of the type-free calls easier.  All representations include a t virtual field which contains the actual subclass of the object being represented.

1. GET <superclass>?t=<subclass>&v=<rep>, GET <superclass>?!t

Purpose is to get all records of a particular subclass in a formatted representation. Spring routes this to the subclass controller. The subclass resource uses getAll to get a (polymorphic) list of objects. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

2. GET <superclass>?t=<subclass>&v=<rep>&q=<search param>, GET <superclass>?!t&v=<rep>&q=<search param>

Purpose is to use the standard query for a subclass to get a formatted representation. Spring routes this to the subclass controller. The subclass resource uses doSearch to get a (polymorphic) list of objects. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

3. GET <superclass>?t=<subclass>&v=<rep>&<custom search>=<search param>

Purpose is to do a custom search that is defined at some level of the class hierarchy.  Spring routes this to the subclass controller. The subclass controller creates a resource for its type and delegates the search to it. The resource uses service methods to do the search to produce a (polymorphic) list of objects. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

4. GET <superclass>/<uuid>?!t&v=<rep>

Purpose is to get all records of a particular subclass in a formatted representation. Spring routes this to the superclass controller. The superclass resource uses getByUuid to get an object. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

5. GET <superclass>/<uuid>?t=<subclass>&v=<rep>

Purpose is to use a representation from a parent class. Like 5, except after getting by Uuid, the subclass resource coerces the result to be its type. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the supertype has been searched, at which point a rep does not exist error is thrown.

6. POST <superclass>?t=<subclass> {body}

Purpose is to create a new object of type <subclass>. The subclass resource creates a new object whose fields are extracted from the body (no need to know. The subclass resource save method uses services method to save the object; validation takes place in the save method.

7. POST <superclass>/<uuid>?!t {body}

Purpose is to update an object of any subclass. The type is determined and the operation is delegated to the resource of the actual type.

8. POST <superclass/<uuid>?t=<subclass> {body}

Purpose is to update an object using only parent-level fields. This is to avoid additional privileges at the actual subclass level. The type is deermined and the operation is delegated to the resource of the declared type. Throws an error if the actual type is not a subclass of the type declared.

9. DELETE <superclass>/<uuid>?!t&!purge, DELETE <superclass>/<uuid>?t=<subclass>&!purge

Purpose is to delete an object of any subclass. The type is determined and the operation is delegated to the resource of the actual type. The subclass, if present, must match the actual type of the resource.

10. DELETE <superclass>/<uuid>?purge&!t, DELETE <superclass>/<uuid>?purge

Purpose is to purge an object of any subclass. The type is determine and the operation is delegated to the resource of the actual type. The subclass, if present, must match the actual type of the resource.


[hidden email] from OpenMRS Developers' mailing list



[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list
Burke Mamlin Burke Mamlin
Reply | Threaded
Open this post in threaded view
|

Re: Sateesh's comment on encounter creation

How are you caching things that don't yet exist?  If, for exampe, you are creating obs by posting to /ws/encounter, then those obs don't exist until you get a response and you will have never made requests at their URIs, right?

I completely agree that you shouldn't be able to update subresources from the parent, but creation doesn't seem like it should be a problem… or at least, we can make exceptions like this when it is both convenient & intuitive.  I think it's safe to say system-wide, that you cannot update a subresource from the parent; however, I think creation of subresources will vary based on what makes sense.  For example, in just the same way we want to create obs when creating an encounter, I'd like to be able to create a Person record when creating a new Patient.  But, I would not expect to be able to create new Patient Identifier Type while creating a Person.

-Burke

On Fri, Apr 27, 2012 at 1:42 PM, Darius Jazayeri <[hidden email]> wrote:
For the client's sake, we have to provide a way to create an encounter with all its obs and orders in a single POST.

(I do worry, because this won't invalidate the caches for obs and orders, but I don't think asking the client to do 101 POSTs to create an encounter is an option...)

-Darius


On Fri, Apr 27, 2012 at 10:25 AM, Burke Mamlin <[hidden email]> wrote:
Well, we have encounterless obs and we definitely want to be able to access observations without going through encounters.  Are you saying that because of this, we can't post a new encounter containing observations (and eventually orders, notes, and form data)?  That, for an encounter with 100 obs, we would have to do 101 posts instead of one?

-Burke


On Fri, Apr 27, 2012 at 12:02 PM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

I believe that Darius, Rafal and I are pretty much in agreement that sub-resources should be creatable from resources, but that full resources should be created separately and then either used as a uuid reference or added explicitly to a multi-object collection.  Since there's no a priori way to know whether a link has been put in the record itself, in a mapping object that's also a (sub-)resource, or a mapping object that's only a mapping object, the REST machinery has difficulty in figuring out what to do; we may need to have add/remove member service or pojo methods to handle collections.  Rafal found that bulk replacement of collection members produces Hibernate errors.

 

My idea in creating the page https://wiki.openmrs.org/display/docs/Standard_Modeling+of+Resource+Relationship+Types was to document the types of relationship we have in OpenMRS and how those relationships should be modeled in REST.  It still needs input.

 

In the case of encounter, the answer to Sateesh's comment depends on whether there can be encounterless obs.  If there are no encounterless obs, then obs should be a sub-resource of encounter and thus creatable.  If there are encounterless obs, then obs could be a full resource (as it is now) and the encounter and the obs should be created separately and then the obs added to the encounter.   However, obs could be a sub-resource of patient (there are no patientless obs), in which case you could create obs while creating a patient.

 

Further, every encounter has an encounter type.  Encounter type is a resource, so it would have to be created separately and then used within the encounter being created.  Likewise, you couldn't create a location or a provider/user while creating the encounter.

 

If there were a way to inform the REST machinery what kind of a linkage exists between two resources, then it might be possible to weaken these constraints.  But I don't think we'd ever want a complete free-for-all; the idea of creating a new encounter type or location on the fly seems pretty dubious to me.

 

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Burke Mamlin
Sent: Friday, April 27, 2012 9:49 AM
To: [hidden email]
Subject: Re: [OPENMRS-DEV] An Alternative Approach to Subclass Implementation

 

Good point, Sateesh.  When creating an encounter, you could be able to create all of the associated data as well within a single call; in fact, this should be the preferred method for creating an encounter.  This would include observations (obs) now, include orders as they are added to REST, and eventually include notes & form data once those are fully realized.

 

Cheers,

 

-Burke

On Fri, Apr 27, 2012 at 4:23 AM, Sateesh Babu <[hidden email]> wrote:

One question (concern) here. For example, if a client has to create a patient encounter with drug and lab orders, the client has to potentially make 2-3 (or even more) REST calls.
What if the first REST call succeeds and the next two fail or a combination like pass-fail-pass, pass-pass-fail etc. The failure can be because of any reason ranging from network failure, data corruption, bugs etc. REST inherently being a stateless protocol would leave the data on the server in an inconsistent state.
Instead, why not send the complete data in the transaction in one single go. So for the same above example, one single data object could be created with the encounter + drug order + lab order. This way if the transaction fails, then atleast the data is left in a consistent state. If data size is a concern, we can always use compression. There could be a meta-data object in the beginning of the object to describe what the contents of the json object are. Something like:
element {
  encounter: {
                   ...
                  }
  drug-order: {
                   ...
                   }
  lab-order: {
                  ...
                }
}

--
Sateesh

 

On Fri, Apr 27, 2012 at 11:22 AM, Darius Jazayeri <[hidden email]> wrote:

Yeah, I coded it such that classes have to actively choose to make themselves subclassable, but it should only take a few lines of code to change that.

 

-Darius

 

On Thu, Apr 26, 2012 at 8:28 PM, Ben Wolfe <[hidden email]> wrote:

So I was in Rogers camp until this argument.  There were three types of subclassing that we defined.  The patient/person subclassing is a first class resource, so both are primary documents. The ProblemList/Allergy subclassing completely hide their parents, so they are primary documents. However, these Order subclasses are second class citizens of sorts. They depend on the parent Order resource.  I don't like introducing yet another *Handler class, but calling them a resource is mixing ideas.

 

I'd favor making every class subclassable by default.  I think you had reversed Darius.

 

Ben

 

On Thu, Apr 26, 2012 at 9:42 PM, Darius Jazayeri <[hidden email]> wrote:

Hi Roger,

 

So, as you can probably imagine, I disagree. :-)

 

I think the whole point is that it's not appropriate to expose subclasses as if they were resources. I'm explicitly saying they're not resources, and that consumers of the REST API should not think of them as resources, but rather should think "the order resource tells me about orders, and drug orders are one type of order".

 

So I don't think that exposing a drugorder URI that redirects to order?t=drugorder is right. Also, using HTTP 307 (Temporary Redirect) means that the client has to make 2 requests to do anything (unless they cheat by going straight to the order resource).

 

What do others think?

 

I refactored my code a bit so now DelegatingSubclassHandler declares save() and purge() methods, which have a default implementation in BaseDelegatingSubclassHandler. Is that a good enough solution?

 

-Darius

 

On Thu, Apr 26, 2012 at 12:50 PM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

So I have been trying to figure out if there is any way to break out of this back and forth.  I have decided that what I really want to see is subclass resources and controllers that act as if they were full-fledged resources and preserve the subclass hierarchy but don't screw up caching.  So I am going to suggest the following:

 

(1) Subclass resources should be addressed like full resources, ws/<subclass>/... but the subclass controller should issue a 307 redirect to ws/<superclass>/...?t=<subclass>.  Subclasses can have child classes represented as sub-resources, and sub-resources can be created when a resource is created.  It is OK for subclass controllers to hand custom queries directly to their resource without passing through the superclass.

 

(2) The superclass should delegate to the subclass for representations, searches and saves.  If the named representation or search does not exist, my choice would be to ascend the class hierarchy.  I understand this might require some registration, but only to the parent.

 

(3) The superclass resource would have to implement the superclass representations, searches and saves.  It would also have to implement the delegation mechanism for subclasses.

 

(4) Subclass name will be returned as part of the standard representations.  Subclass name will not be needed for POST.

 

So given our Order -->  DrugOrder --> HIVDrugOrder class hierarchy

 

POST ws/order {body} creates an order which is filled from the body and validated and saved

POST ws/drugorder {body} is redirected to ws/order?t=drugorder {body} which delegates to the drugorder resource to create a drugorder which is filled from the body and delegated to the drugorder resource for validation and saving

POST ws/hivdrugorder {body} is redirected to ws/order?t=hivdrugorder {body} which delegates to the hivdrugorder resource to create an hivdrugorder which is filled from the body and delegated to the hivdrugorder resource for validation and saving.

 

POST ws/order/uuid {body} retrieves the order which is filled from the body and validated and saved

POST ws/drugorder/uuid {body} is redirected to ws/order?t=drugorder {body} which retrieves the order which is filled from the body and delegated to the drugorder resource for validation and saving

POST ws/hivdrugorder/uuid {body} is redirected to ws/order?t=hivdrugorder {body} which retrieves the order which is filled from the body and delegated to the hivdrugorder resource for validation and saving

 

GET ws/order/uuid?v=<rep> retrieves the order whose type is determined and delegated to the subclass for generation of the rep.  If the object with the uuid is not assignable from order or the rep does not exist, an error is thrown. 

GET ws/drugorder/uuid?v=<rep> is redirected to ws/order/uuid&v=<rep>?t=drugorder.  The search for the representation is done in the resoruce drugorder.

GET ws/hivdrugorder/uuid?v=<rep> is redirected to ws/order/uuid&v=<rep>?t=drugorder.  The search for the representation is done in the resource hivdrugorder.

 

GET ws/order&v=<rep> retrieves all orders; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named rep does not exist, an error is thrown.

GET ws/drugorder&v=<rep> is redirected to ws/order?v=rep&t=drugorder.  The superclass delegates to the class, which retrieves all drugorders; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named rep does not exist, an error is thrown.

GET ws/hivdrugorder&v=<rep> is redirected to ws/order?v=rep&t=hivdrugorder.  The superclass delegates to the class, which retrieves all hivdrugorders; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named rep does not exist, an error is thrown.

 

GET ws/order&<query>=<param> retrieves all orders satisfying the condition; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named query does not exist, an error is thrown.

GET ws/drugorder&<query>=<param> is redirected to ws/order?<query>=<param>&t=drugorder.  The superclass delegates to the subclass to retrieve all orders satisfying the condition; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named query does not exist, an error is thrown.

GET ws/hivdrugorder&<query>=<param> is redirected to ws/order?<query>=<param>&t=hivdrugorder.  The superclass delegates to the subclass to retrieve all orders satisfying the condition; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named query does not exist, an error is thrown.

 

DELETE is handled by the superclass.

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Darius Jazayeri
Sent: Thursday, April 26, 2012 11:34 AM


To: [hidden email]
Subject: Re: [OPENMRS-DEV] An Alternative Approach to Subclass Implementation

 

To be clear, the POSTs that Burke showed a couple of emails ago will work exactly as written, except of course that he simplified the actual properties into just "order". And the response to the GET that he showed will also be as he wrote it (except that uri is within links and the whole thing is in a PageableResults object). And the URIs he gave for the POSTs and GET are exactly right, there is no need for a ?t=type parameter.

 

Responses inline below.

 

-Darius

On Thu, Apr 26, 2012 at 7:14 AM, Burke Mamlin <[hidden email]> wrote:

On Thu, Apr 26, 2012 at 8:32 AM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

Burke --

     Regardless of whether it's Darius or me, each of the posts 1 and 3 would need a t=<type> parameter. 

     In my design, t is always virtual, it is the classname of the object; this avoids issues like having to use a global variable or predefined value or concept map to know the value of order type for drug order.  It also leaves order type available for distinctions that might be important to the facility without affecting the data model (say a narcotic drug order).  I'm not sure where Darius is at on this; at one point he required a discriminator field with a known name (registered?), but I think he has become more virtual.  The point is, you can't change the value of a discriminator field, so why include it in the POST body?  I do return it in the body on GET, as does Darius.

 

You can't change the concept of an order either, so would that move the URL as well?  I don't like the idea of putting some of the properties in the URL and others in the data.  You're right that you can't change the type of order; however, you must specify it when creating an order.

 

I'm also not thrilled about clients needing to know the Java class name for every order type.  There are not an infinite number of orderables.  Most implementations will be able to get by with two up front (drugs & tests).  Even for an enterprise-level inpatient system, you can get by with only a dozen or so (e.g., activities, precautions, diet, call orders, drugs, lab tests, radiology tests, procedures, nursing orders, referrals, education/handouts).  Actually, each of these basic types could be represented by a concept class and that class name could be used.

 

As I've coded it, the java class names are not exposed. To create a drug order you say ' type: "drugorder" '.

(@Roger, as coded you only use a &t=drugorder when you're doing a get-all or search, to say you only want objects of that type.)

 

 

     Also, Darius and I differ about privileges and validation at posts 1 and 3.  In Darius' design, both commands would require the privileges to create an order and would validate per order's requirements.  In my design, post 1 would require the privileges to create a drug order and would validate per drug order's requirements, while post 3 would require the privileges to create a referral order and would validate per referral order's requirements. 

 

There should be a top level privilege for ordering in general.  My assumption in posting the orders was that the REST document received would be passed to the order type-specific handler, so permissions & validation could be type-specific as you describe – i.e., we would have separate validators for each basic type of order.

 

The underlying Java API is built such that if you have Manage Orders privilege, you can do OrderService.saveOrder(anyOrderSubclass). My implementation does the actual save using this.

As coded, the type-specific-handler is only allowed to specify a "delegating resource description" which indicates what fields may be created/edited, it doesn't actually get a "save(T)" call. This is an oversight, due to the fact that none of our core resources actually do any custom save/validation logic that isn't routed through the resource description.

I can add a save(T) method on the subtype handlers, and let the base implementation of it just call the resource's save method.

 

 

    At get 4, both designs would use the default rep associated with the subclass of the object.  However, suppose you wanted to display all the types of order with just the order fields and some combination of subclass-specific fields in a details column.  In my design, you could define a named representation (say, COMPRESSED) that would be defined differently for different subclasses; you could even define it at a parent level (say drug order) and it could be used by children (like HIV drug order).  I don't believe you can do this in Darius' design.

 

Again, I think I agree with you here.  I was assuming that the generation of representation for each order would be delegated to it's type-specific handler, so if you asked for a FOOBAR representation of all the patient's orders, the API would iterate through the orders, asking for the representation for each from its type-specific handler.

 

@Darius – is that not what you're doing?  i.e., were you creating something that would not defer calls to type-specific handlers?

 

I do delegate to type-specific handlers. You can also define named representations on subclasses. What you cannot do is define a representation in the top-level resource, and get a subtype in that representation. E.g. if order defines a "foobar" rep, that is not inherited by drugorder. (Though drugorder could of course define that named representation also, as a one-liner that calls the parent resource.)

 

 

    With Darius, at step 0 (before the drug order is built in), you have to create a resource and controller for the order superclass; you have to create a handler, and a controller if you want custom searches, for drug order.  The superclass resource will contain all the methods of the current design plus the non-final declaration.

 

Yes.  The idea is that drug orders are provided for you.  So "you" will not have to do these steps.

 

  Darius depends on each resource and handler having the variable lists created by RESTWS-226, which I do not.  The drug order handler will contain its registration plus most of the methods of a resource (no save, delete or purge).  At step 2, you would need to build another handler and possibly a controller.

    With me, at step 0 you have to create a resource and controller for the hierarchy, a resource and controller for the superclass (order), and a resource and controller for the subclass (drug order).  The controllers are one-liners unless you want to add custom searches; they are just like the current design.  The hierarchy resource could also be a one-liner, with the actual delegations taking place in an abstract class using generics.  The resource for the superclass and the subclass would look just like the current design.  At step 2, you would create another controller and resource for referral order and that would also look just like the current design.

 

If you're creating a separate resource for each type, then doesn't that imply a different URL for each order type?

 

     In our discussion, Darius and I identified some situations (as I recall relating to validation) where it would be necessary under his design to override the supplied core resource with a user-defined resource that extended the core resource.  This could not be done independently for more than one subclass, and would require a custom build.  This would not be necessary with my design.

 

Can you provide an concrete example?  I'm presuming each order type has an associated handler/validator.

 

-Burke

 

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Burke Mamlin
Sent: Wednesday, April 25, 2012 5:08 PM


To: [hidden email]
Subject: Re: [OPENMRS-DEV] An Alternative Approach to Subclass Implementation

 

Ok, so if I want to create a drug order and a referral order for patient with uuid 123-456, then request all orders for the patient, it would look something like this (simplifying the order details for a simpler example)?

 

1. Create a drug order for my patient; they're built-in:

POST order

{ "patient":"123-456", "type":"drugorder", "order":"penicillin 500 mg orally twice daily for 2 weeks" }

 

2. Install a module to add support for referral orders:

Install a referrals module that adds a referralorder type of order.  Presumably, by introducing a new concept class for referral order, along with a handler for the new order type and, of course, a ReferralOrder class that extends Order.

 

3. Create a referral order for my patient:

POST order

{ "patient":"123-456", "type":"referralorder", "order":"cardiology referral please evaluate for rheumatic heart disease" }

 

4. Find all the active orders for my patient:

GET order?patient=123-456

[

{ "patient":"123-456", "type":"drugorder", "order":"penicillin 500 mg orally twice daily for 2 weeks", "uri":"…" },

{ "patient":"123-456", "type":"referralorder", "order":"cardiology referral please evaluate for rheumatic heart disease", "uri":"…" }

]

 

Is that close?

 

-Burke

 

On Tue, Apr 24, 2012 at 9:21 PM, Darius Jazayeri <[hidden email]> wrote:

On RESTWS-243 I've attached a patch that contains my implementation that I'm now ready to commit. (It's what Roger was responding to in his email.)

 

I'm going to hold off on committing until after tomorrow's design call in case we have time to discuss this a bit.

 

Basically what I've done is allow resources to indicate that they support subclasses, and if they do, they'll have all appropriate DelegatingSubclassHandlers registered with them via spring.

 

The idea is that we're hiding subclasses from the consumer of the REST API. As far as they're concerned there is a single "order" resource, which has documents of different types (e.g. "order", "drugorder", etc.) Behind the scenes each type is a subclass, but in REST that's not visible, rather they're exposed as different document types. 

 

DelegatingSubclassHandler encapsulates the methods subclasses need to specify for their owning resource.

 

Here is the functionality that my approach supports:

 

GET order

  • gets all orders regardless of type
  • each document in the list may have different types
  • the top-level resource does the db query and delegates to the appropriate handler to convert each result

GET order?t=drugorder

  • gets all orders whose type is drugorder
  • the subclass handler does the db query
  • In my implementation GET order?t=order (i.e. get only things that are order stubs) is not allowed

GET order?patient=<uuid>

  • gets all orders for the given patient
  • each document in the list may have different types

GET order?patient=<uuid>&t=drugorder

  • gets all orders for the given patient whose type is drugorder
  • if the subclass handler defines a suitable method, we use it, otherwise the top-level resource queries by patient, and manually filters out by type

GET order/<uuid>

  • returns a specific order
  • representations are defined by the type, e.g. { "type": "drugorder", "dose": "100", ... }

POST order

  • creates a new order
  • you must specify the type in the content, e.g. { "type": "drugorder", "dose": "100", ... }
  • allowed properties are defined by the type

POST order/<uuid>

  • modifies an existing order
  • you are not allowed to change the type
  • allowed properties are defined by the type

DELETE order/<uuid>

  • voids an order
  • handled by the top-level resource, not the subclass handler

DELETE order/<uuid>?purge=true

  • purges an order
  • handled by the top-level resource, not the subclass handler

@Roger, can you please phrase your counterproposal in terms of the additional functionality that you want to expose beyond these methods?

 

-Darius

 

On Mon, Apr 23, 2012 at 9:54 AM, Burke Mamlin <[hidden email]> wrote:

Roger,

 

Could you provide some concrete examples?

 

-Burke

 

On Mon, Apr 23, 2012 at 8:07 AM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

AN ALTERNATIVE APPROACH TO SUBCLASS IMPLEMENTATION

In this approach, subclass resources remain resources, just are mapped via the superclass. Substantially all operations are delegated to the appropriate subclass resource. This is motivated by simplicity, validation and representation considerations. It is also motivated by a privilege issue I just realized – the people authorized to create/update a lab order are likely different from those allowed to create/update a drug order. I have assumed that the privilege of updating a parent class field in subclass record requires only parent class privileges

I don't believe this alternative requires much in the way of changes to existing code other than mappings; adding the controller and resource for each superclass; some changes in asRepresentation where it finds the right representation to use; and some changes where updates are going on to allow the possibility that the fields to be looked for in the request body may come from an object which is a parent class of the object being updated. It eliminates the need for the two variable lists used by Darius (although they could still be used if it was thought they added value). I still think it is a good idea to treat ActiveLists as an abstract class so that the AllergyList and the ProblemList appear to be two different superclasses.

All members of a class hierarchy request a mapping to /<superclass>?t=<subclass>. The superclass also requests a mapping to /<superclass>?!t. I have tried to make all the calls of /<superclass>?!t work the same as calls to /<superclass>?t=<superclass>, if I have missed please let me know.  It might be worthwhile to extend Resource to SuperclassResource to make the coding of the type-free calls easier.  All representations include a t virtual field which contains the actual subclass of the object being represented.

1. GET <superclass>?t=<subclass>&v=<rep>, GET <superclass>?!t

Purpose is to get all records of a particular subclass in a formatted representation. Spring routes this to the subclass controller. The subclass resource uses getAll to get a (polymorphic) list of objects. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

2. GET <superclass>?t=<subclass>&v=<rep>&q=<search param>, GET <superclass>?!t&v=<rep>&q=<search param>

Purpose is to use the standard query for a subclass to get a formatted representation. Spring routes this to the subclass controller. The subclass resource uses doSearch to get a (polymorphic) list of objects. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

3. GET <superclass>?t=<subclass>&v=<rep>&<custom search>=<search param>

Purpose is to do a custom search that is defined at some level of the class hierarchy.  Spring routes this to the subclass controller. The subclass controller creates a resource for its type and delegates the search to it. The resource uses service methods to do the search to produce a (polymorphic) list of objects. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

4. GET <superclass>/<uuid>?!t&v=<rep>

Purpose is to get all records of a particular subclass in a formatted representation. Spring routes this to the superclass controller. The superclass resource uses getByUuid to get an object. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

5. GET <superclass>/<uuid>?t=<subclass>&v=<rep>

Purpose is to use a representation from a parent class. Like 5, except after getting by Uuid, the subclass resource coerces the result to be its type. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the supertype has been searched, at which point a rep does not exist error is thrown.

6. POST <superclass>?t=<subclass> {body}

Purpose is to create a new object of type <subclass>. The subclass resource creates a new object whose fields are extracted from the body (no need to know. The subclass resource save method uses services method to save the object; validation takes place in the save method.

7. POST <superclass>/<uuid>?!t {body}

Purpose is to update an object of any subclass. The type is determined and the operation is delegated to the resource of the actual type.

8. POST <superclass/<uuid>?t=<subclass> {body}

Purpose is to update an object using only parent-level fields. This is to avoid additional privileges at the actual subclass level. The type is deermined and the operation is delegated to the resource of the declared type. Throws an error if the actual type is not a subclass of the type declared.

9. DELETE <superclass>/<uuid>?!t&!purge, DELETE <superclass>/<uuid>?t=<subclass>&!purge

Purpose is to delete an object of any subclass. The type is determined and the operation is delegated to the resource of the actual type. The subclass, if present, must match the actual type of the resource.

10. DELETE <superclass>/<uuid>?purge&!t, DELETE <superclass>/<uuid>?purge

Purpose is to purge an object of any subclass. The type is determine and the operation is delegated to the resource of the actual type. The subclass, if present, must match the actual type of the resource.


[hidden email] from OpenMRS Developers' mailing list



[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list
Darius Jazayeri-3 Darius Jazayeri-3
Reply | Threaded
Open this post in threaded view
|

Re: Sateesh's comment on encounter creation

For what it's worth, today I committed code that does allow creating an encounter, obs, and orders all together. (See test case.)

My concern is for this scenario:

1. GET order?patient=1234
now the cache holds "all orders for patient 1234"

2. POST encounter { patient: "1234", orders: [ ... ] }
this does not invalidate the cache entry, because there was no POST to the order resource

3. GET order?patient=1234
the cache gives us a stale result

I don't really see a workaround besides putting short TTLs on cached data.

-Darius

On Fri, Apr 27, 2012 at 6:14 PM, Burke Mamlin <[hidden email]> wrote:
How are you caching things that don't yet exist?  If, for exampe, you are creating obs by posting to /ws/encounter, then those obs don't exist until you get a response and you will have never made requests at their URIs, right?

I completely agree that you shouldn't be able to update subresources from the parent, but creation doesn't seem like it should be a problem… or at least, we can make exceptions like this when it is both convenient & intuitive.  I think it's safe to say system-wide, that you cannot update a subresource from the parent; however, I think creation of subresources will vary based on what makes sense.  For example, in just the same way we want to create obs when creating an encounter, I'd like to be able to create a Person record when creating a new Patient.  But, I would not expect to be able to create new Patient Identifier Type while creating a Person.

-Burke


On Fri, Apr 27, 2012 at 1:42 PM, Darius Jazayeri <[hidden email]> wrote:
For the client's sake, we have to provide a way to create an encounter with all its obs and orders in a single POST.

(I do worry, because this won't invalidate the caches for obs and orders, but I don't think asking the client to do 101 POSTs to create an encounter is an option...)

-Darius


On Fri, Apr 27, 2012 at 10:25 AM, Burke Mamlin <[hidden email]> wrote:
Well, we have encounterless obs and we definitely want to be able to access observations without going through encounters.  Are you saying that because of this, we can't post a new encounter containing observations (and eventually orders, notes, and form data)?  That, for an encounter with 100 obs, we would have to do 101 posts instead of one?

-Burke


On Fri, Apr 27, 2012 at 12:02 PM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

I believe that Darius, Rafal and I are pretty much in agreement that sub-resources should be creatable from resources, but that full resources should be created separately and then either used as a uuid reference or added explicitly to a multi-object collection.  Since there's no a priori way to know whether a link has been put in the record itself, in a mapping object that's also a (sub-)resource, or a mapping object that's only a mapping object, the REST machinery has difficulty in figuring out what to do; we may need to have add/remove member service or pojo methods to handle collections.  Rafal found that bulk replacement of collection members produces Hibernate errors.

 

My idea in creating the page https://wiki.openmrs.org/display/docs/Standard_Modeling+of+Resource+Relationship+Types was to document the types of relationship we have in OpenMRS and how those relationships should be modeled in REST.  It still needs input.

 

In the case of encounter, the answer to Sateesh's comment depends on whether there can be encounterless obs.  If there are no encounterless obs, then obs should be a sub-resource of encounter and thus creatable.  If there are encounterless obs, then obs could be a full resource (as it is now) and the encounter and the obs should be created separately and then the obs added to the encounter.   However, obs could be a sub-resource of patient (there are no patientless obs), in which case you could create obs while creating a patient.

 

Further, every encounter has an encounter type.  Encounter type is a resource, so it would have to be created separately and then used within the encounter being created.  Likewise, you couldn't create a location or a provider/user while creating the encounter.

 

If there were a way to inform the REST machinery what kind of a linkage exists between two resources, then it might be possible to weaken these constraints.  But I don't think we'd ever want a complete free-for-all; the idea of creating a new encounter type or location on the fly seems pretty dubious to me.

 

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Burke Mamlin
Sent: Friday, April 27, 2012 9:49 AM
To: [hidden email]
Subject: Re: [OPENMRS-DEV] An Alternative Approach to Subclass Implementation

 

Good point, Sateesh.  When creating an encounter, you could be able to create all of the associated data as well within a single call; in fact, this should be the preferred method for creating an encounter.  This would include observations (obs) now, include orders as they are added to REST, and eventually include notes & form data once those are fully realized.

 

Cheers,

 

-Burke

On Fri, Apr 27, 2012 at 4:23 AM, Sateesh Babu <[hidden email]> wrote:

One question (concern) here. For example, if a client has to create a patient encounter with drug and lab orders, the client has to potentially make 2-3 (or even more) REST calls.
What if the first REST call succeeds and the next two fail or a combination like pass-fail-pass, pass-pass-fail etc. The failure can be because of any reason ranging from network failure, data corruption, bugs etc. REST inherently being a stateless protocol would leave the data on the server in an inconsistent state.
Instead, why not send the complete data in the transaction in one single go. So for the same above example, one single data object could be created with the encounter + drug order + lab order. This way if the transaction fails, then atleast the data is left in a consistent state. If data size is a concern, we can always use compression. There could be a meta-data object in the beginning of the object to describe what the contents of the json object are. Something like:
element {
  encounter: {
                   ...
                  }
  drug-order: {
                   ...
                   }
  lab-order: {
                  ...
                }
}

--
Sateesh

 

On Fri, Apr 27, 2012 at 11:22 AM, Darius Jazayeri <[hidden email]> wrote:

Yeah, I coded it such that classes have to actively choose to make themselves subclassable, but it should only take a few lines of code to change that.

 

-Darius

 

On Thu, Apr 26, 2012 at 8:28 PM, Ben Wolfe <[hidden email]> wrote:

So I was in Rogers camp until this argument.  There were three types of subclassing that we defined.  The patient/person subclassing is a first class resource, so both are primary documents. The ProblemList/Allergy subclassing completely hide their parents, so they are primary documents. However, these Order subclasses are second class citizens of sorts. They depend on the parent Order resource.  I don't like introducing yet another *Handler class, but calling them a resource is mixing ideas.

 

I'd favor making every class subclassable by default.  I think you had reversed Darius.

 

Ben

 

On Thu, Apr 26, 2012 at 9:42 PM, Darius Jazayeri <[hidden email]> wrote:

Hi Roger,

 

So, as you can probably imagine, I disagree. :-)

 

I think the whole point is that it's not appropriate to expose subclasses as if they were resources. I'm explicitly saying they're not resources, and that consumers of the REST API should not think of them as resources, but rather should think "the order resource tells me about orders, and drug orders are one type of order".

 

So I don't think that exposing a drugorder URI that redirects to order?t=drugorder is right. Also, using HTTP 307 (Temporary Redirect) means that the client has to make 2 requests to do anything (unless they cheat by going straight to the order resource).

 

What do others think?

 

I refactored my code a bit so now DelegatingSubclassHandler declares save() and purge() methods, which have a default implementation in BaseDelegatingSubclassHandler. Is that a good enough solution?

 

-Darius

 

On Thu, Apr 26, 2012 at 12:50 PM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

So I have been trying to figure out if there is any way to break out of this back and forth.  I have decided that what I really want to see is subclass resources and controllers that act as if they were full-fledged resources and preserve the subclass hierarchy but don't screw up caching.  So I am going to suggest the following:

 

(1) Subclass resources should be addressed like full resources, ws/<subclass>/... but the subclass controller should issue a 307 redirect to ws/<superclass>/...?t=<subclass>.  Subclasses can have child classes represented as sub-resources, and sub-resources can be created when a resource is created.  It is OK for subclass controllers to hand custom queries directly to their resource without passing through the superclass.

 

(2) The superclass should delegate to the subclass for representations, searches and saves.  If the named representation or search does not exist, my choice would be to ascend the class hierarchy.  I understand this might require some registration, but only to the parent.

 

(3) The superclass resource would have to implement the superclass representations, searches and saves.  It would also have to implement the delegation mechanism for subclasses.

 

(4) Subclass name will be returned as part of the standard representations.  Subclass name will not be needed for POST.

 

So given our Order -->  DrugOrder --> HIVDrugOrder class hierarchy

 

POST ws/order {body} creates an order which is filled from the body and validated and saved

POST ws/drugorder {body} is redirected to ws/order?t=drugorder {body} which delegates to the drugorder resource to create a drugorder which is filled from the body and delegated to the drugorder resource for validation and saving

POST ws/hivdrugorder {body} is redirected to ws/order?t=hivdrugorder {body} which delegates to the hivdrugorder resource to create an hivdrugorder which is filled from the body and delegated to the hivdrugorder resource for validation and saving.

 

POST ws/order/uuid {body} retrieves the order which is filled from the body and validated and saved

POST ws/drugorder/uuid {body} is redirected to ws/order?t=drugorder {body} which retrieves the order which is filled from the body and delegated to the drugorder resource for validation and saving

POST ws/hivdrugorder/uuid {body} is redirected to ws/order?t=hivdrugorder {body} which retrieves the order which is filled from the body and delegated to the hivdrugorder resource for validation and saving

 

GET ws/order/uuid?v=<rep> retrieves the order whose type is determined and delegated to the subclass for generation of the rep.  If the object with the uuid is not assignable from order or the rep does not exist, an error is thrown. 

GET ws/drugorder/uuid?v=<rep> is redirected to ws/order/uuid&v=<rep>?t=drugorder.  The search for the representation is done in the resoruce drugorder.

GET ws/hivdrugorder/uuid?v=<rep> is redirected to ws/order/uuid&v=<rep>?t=drugorder.  The search for the representation is done in the resource hivdrugorder.

 

GET ws/order&v=<rep> retrieves all orders; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named rep does not exist, an error is thrown.

GET ws/drugorder&v=<rep> is redirected to ws/order?v=rep&t=drugorder.  The superclass delegates to the class, which retrieves all drugorders; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named rep does not exist, an error is thrown.

GET ws/hivdrugorder&v=<rep> is redirected to ws/order?v=rep&t=hivdrugorder.  The superclass delegates to the class, which retrieves all hivdrugorders; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named rep does not exist, an error is thrown.

 

GET ws/order&<query>=<param> retrieves all orders satisfying the condition; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named query does not exist, an error is thrown.

GET ws/drugorder&<query>=<param> is redirected to ws/order?<query>=<param>&t=drugorder.  The superclass delegates to the subclass to retrieve all orders satisfying the condition; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named query does not exist, an error is thrown.

GET ws/hivdrugorder&<query>=<param> is redirected to ws/order?<query>=<param>&t=hivdrugorder.  The superclass delegates to the subclass to retrieve all orders satisfying the condition; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named query does not exist, an error is thrown.

 

DELETE is handled by the superclass.

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Darius Jazayeri
Sent: Thursday, April 26, 2012 11:34 AM


To: [hidden email]
Subject: Re: [OPENMRS-DEV] An Alternative Approach to Subclass Implementation

 

To be clear, the POSTs that Burke showed a couple of emails ago will work exactly as written, except of course that he simplified the actual properties into just "order". And the response to the GET that he showed will also be as he wrote it (except that uri is within links and the whole thing is in a PageableResults object). And the URIs he gave for the POSTs and GET are exactly right, there is no need for a ?t=type parameter.

 

Responses inline below.

 

-Darius

On Thu, Apr 26, 2012 at 7:14 AM, Burke Mamlin <[hidden email]> wrote:

On Thu, Apr 26, 2012 at 8:32 AM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

Burke --

     Regardless of whether it's Darius or me, each of the posts 1 and 3 would need a t=<type> parameter. 

     In my design, t is always virtual, it is the classname of the object; this avoids issues like having to use a global variable or predefined value or concept map to know the value of order type for drug order.  It also leaves order type available for distinctions that might be important to the facility without affecting the data model (say a narcotic drug order).  I'm not sure where Darius is at on this; at one point he required a discriminator field with a known name (registered?), but I think he has become more virtual.  The point is, you can't change the value of a discriminator field, so why include it in the POST body?  I do return it in the body on GET, as does Darius.

 

You can't change the concept of an order either, so would that move the URL as well?  I don't like the idea of putting some of the properties in the URL and others in the data.  You're right that you can't change the type of order; however, you must specify it when creating an order.

 

I'm also not thrilled about clients needing to know the Java class name for every order type.  There are not an infinite number of orderables.  Most implementations will be able to get by with two up front (drugs & tests).  Even for an enterprise-level inpatient system, you can get by with only a dozen or so (e.g., activities, precautions, diet, call orders, drugs, lab tests, radiology tests, procedures, nursing orders, referrals, education/handouts).  Actually, each of these basic types could be represented by a concept class and that class name could be used.

 

As I've coded it, the java class names are not exposed. To create a drug order you say ' type: "drugorder" '.

(@Roger, as coded you only use a &t=drugorder when you're doing a get-all or search, to say you only want objects of that type.)

 

 

     Also, Darius and I differ about privileges and validation at posts 1 and 3.  In Darius' design, both commands would require the privileges to create an order and would validate per order's requirements.  In my design, post 1 would require the privileges to create a drug order and would validate per drug order's requirements, while post 3 would require the privileges to create a referral order and would validate per referral order's requirements. 

 

There should be a top level privilege for ordering in general.  My assumption in posting the orders was that the REST document received would be passed to the order type-specific handler, so permissions & validation could be type-specific as you describe – i.e., we would have separate validators for each basic type of order.

 

The underlying Java API is built such that if you have Manage Orders privilege, you can do OrderService.saveOrder(anyOrderSubclass). My implementation does the actual save using this.

As coded, the type-specific-handler is only allowed to specify a "delegating resource description" which indicates what fields may be created/edited, it doesn't actually get a "save(T)" call. This is an oversight, due to the fact that none of our core resources actually do any custom save/validation logic that isn't routed through the resource description.

I can add a save(T) method on the subtype handlers, and let the base implementation of it just call the resource's save method.

 

 

    At get 4, both designs would use the default rep associated with the subclass of the object.  However, suppose you wanted to display all the types of order with just the order fields and some combination of subclass-specific fields in a details column.  In my design, you could define a named representation (say, COMPRESSED) that would be defined differently for different subclasses; you could even define it at a parent level (say drug order) and it could be used by children (like HIV drug order).  I don't believe you can do this in Darius' design.

 

Again, I think I agree with you here.  I was assuming that the generation of representation for each order would be delegated to it's type-specific handler, so if you asked for a FOOBAR representation of all the patient's orders, the API would iterate through the orders, asking for the representation for each from its type-specific handler.

 

@Darius – is that not what you're doing?  i.e., were you creating something that would not defer calls to type-specific handlers?

 

I do delegate to type-specific handlers. You can also define named representations on subclasses. What you cannot do is define a representation in the top-level resource, and get a subtype in that representation. E.g. if order defines a "foobar" rep, that is not inherited by drugorder. (Though drugorder could of course define that named representation also, as a one-liner that calls the parent resource.)

 

 

    With Darius, at step 0 (before the drug order is built in), you have to create a resource and controller for the order superclass; you have to create a handler, and a controller if you want custom searches, for drug order.  The superclass resource will contain all the methods of the current design plus the non-final declaration.

 

Yes.  The idea is that drug orders are provided for you.  So "you" will not have to do these steps.

 

  Darius depends on each resource and handler having the variable lists created by RESTWS-226, which I do not.  The drug order handler will contain its registration plus most of the methods of a resource (no save, delete or purge).  At step 2, you would need to build another handler and possibly a controller.

    With me, at step 0 you have to create a resource and controller for the hierarchy, a resource and controller for the superclass (order), and a resource and controller for the subclass (drug order).  The controllers are one-liners unless you want to add custom searches; they are just like the current design.  The hierarchy resource could also be a one-liner, with the actual delegations taking place in an abstract class using generics.  The resource for the superclass and the subclass would look just like the current design.  At step 2, you would create another controller and resource for referral order and that would also look just like the current design.

 

If you're creating a separate resource for each type, then doesn't that imply a different URL for each order type?

 

     In our discussion, Darius and I identified some situations (as I recall relating to validation) where it would be necessary under his design to override the supplied core resource with a user-defined resource that extended the core resource.  This could not be done independently for more than one subclass, and would require a custom build.  This would not be necessary with my design.

 

Can you provide an concrete example?  I'm presuming each order type has an associated handler/validator.

 

-Burke

 

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Burke Mamlin
Sent: Wednesday, April 25, 2012 5:08 PM


To: [hidden email]
Subject: Re: [OPENMRS-DEV] An Alternative Approach to Subclass Implementation

 

Ok, so if I want to create a drug order and a referral order for patient with uuid 123-456, then request all orders for the patient, it would look something like this (simplifying the order details for a simpler example)?

 

1. Create a drug order for my patient; they're built-in:

POST order

{ "patient":"123-456", "type":"drugorder", "order":"penicillin 500 mg orally twice daily for 2 weeks" }

 

2. Install a module to add support for referral orders:

Install a referrals module that adds a referralorder type of order.  Presumably, by introducing a new concept class for referral order, along with a handler for the new order type and, of course, a ReferralOrder class that extends Order.

 

3. Create a referral order for my patient:

POST order

{ "patient":"123-456", "type":"referralorder", "order":"cardiology referral please evaluate for rheumatic heart disease" }

 

4. Find all the active orders for my patient:

GET order?patient=123-456

[

{ "patient":"123-456", "type":"drugorder", "order":"penicillin 500 mg orally twice daily for 2 weeks", "uri":"…" },

{ "patient":"123-456", "type":"referralorder", "order":"cardiology referral please evaluate for rheumatic heart disease", "uri":"…" }

]

 

Is that close?

 

-Burke

 

On Tue, Apr 24, 2012 at 9:21 PM, Darius Jazayeri <[hidden email]> wrote:

On RESTWS-243 I've attached a patch that contains my implementation that I'm now ready to commit. (It's what Roger was responding to in his email.)

 

I'm going to hold off on committing until after tomorrow's design call in case we have time to discuss this a bit.

 

Basically what I've done is allow resources to indicate that they support subclasses, and if they do, they'll have all appropriate DelegatingSubclassHandlers registered with them via spring.

 

The idea is that we're hiding subclasses from the consumer of the REST API. As far as they're concerned there is a single "order" resource, which has documents of different types (e.g. "order", "drugorder", etc.) Behind the scenes each type is a subclass, but in REST that's not visible, rather they're exposed as different document types. 

 

DelegatingSubclassHandler encapsulates the methods subclasses need to specify for their owning resource.

 

Here is the functionality that my approach supports:

 

GET order

  • gets all orders regardless of type
  • each document in the list may have different types
  • the top-level resource does the db query and delegates to the appropriate handler to convert each result

GET order?t=drugorder

  • gets all orders whose type is drugorder
  • the subclass handler does the db query
  • In my implementation GET order?t=order (i.e. get only things that are order stubs) is not allowed

GET order?patient=<uuid>

  • gets all orders for the given patient
  • each document in the list may have different types

GET order?patient=<uuid>&t=drugorder

  • gets all orders for the given patient whose type is drugorder
  • if the subclass handler defines a suitable method, we use it, otherwise the top-level resource queries by patient, and manually filters out by type

GET order/<uuid>

  • returns a specific order
  • representations are defined by the type, e.g. { "type": "drugorder", "dose": "100", ... }

POST order

  • creates a new order
  • you must specify the type in the content, e.g. { "type": "drugorder", "dose": "100", ... }
  • allowed properties are defined by the type

POST order/<uuid>

  • modifies an existing order
  • you are not allowed to change the type
  • allowed properties are defined by the type

DELETE order/<uuid>

  • voids an order
  • handled by the top-level resource, not the subclass handler

DELETE order/<uuid>?purge=true

  • purges an order
  • handled by the top-level resource, not the subclass handler

@Roger, can you please phrase your counterproposal in terms of the additional functionality that you want to expose beyond these methods?

 

-Darius

 

On Mon, Apr 23, 2012 at 9:54 AM, Burke Mamlin <[hidden email]> wrote:

Roger,

 

Could you provide some concrete examples?

 

-Burke

 

On Mon, Apr 23, 2012 at 8:07 AM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

AN ALTERNATIVE APPROACH TO SUBCLASS IMPLEMENTATION

In this approach, subclass resources remain resources, just are mapped via the superclass. Substantially all operations are delegated to the appropriate subclass resource. This is motivated by simplicity, validation and representation considerations. It is also motivated by a privilege issue I just realized – the people authorized to create/update a lab order are likely different from those allowed to create/update a drug order. I have assumed that the privilege of updating a parent class field in subclass record requires only parent class privileges

I don't believe this alternative requires much in the way of changes to existing code other than mappings; adding the controller and resource for each superclass; some changes in asRepresentation where it finds the right representation to use; and some changes where updates are going on to allow the possibility that the fields to be looked for in the request body may come from an object which is a parent class of the object being updated. It eliminates the need for the two variable lists used by Darius (although they could still be used if it was thought they added value). I still think it is a good idea to treat ActiveLists as an abstract class so that the AllergyList and the ProblemList appear to be two different superclasses.

All members of a class hierarchy request a mapping to /<superclass>?t=<subclass>. The superclass also requests a mapping to /<superclass>?!t. I have tried to make all the calls of /<superclass>?!t work the same as calls to /<superclass>?t=<superclass>, if I have missed please let me know.  It might be worthwhile to extend Resource to SuperclassResource to make the coding of the type-free calls easier.  All representations include a t virtual field which contains the actual subclass of the object being represented.

1. GET <superclass>?t=<subclass>&v=<rep>, GET <superclass>?!t

Purpose is to get all records of a particular subclass in a formatted representation. Spring routes this to the subclass controller. The subclass resource uses getAll to get a (polymorphic) list of objects. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

2. GET <superclass>?t=<subclass>&v=<rep>&q=<search param>, GET <superclass>?!t&v=<rep>&q=<search param>

Purpose is to use the standard query for a subclass to get a formatted representation. Spring routes this to the subclass controller. The subclass resource uses doSearch to get a (polymorphic) list of objects. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

3. GET <superclass>?t=<subclass>&v=<rep>&<custom search>=<search param>

Purpose is to do a custom search that is defined at some level of the class hierarchy.  Spring routes this to the subclass controller. The subclass controller creates a resource for its type and delegates the search to it. The resource uses service methods to do the search to produce a (polymorphic) list of objects. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

4. GET <superclass>/<uuid>?!t&v=<rep>

Purpose is to get all records of a particular subclass in a formatted representation. Spring routes this to the superclass controller. The superclass resource uses getByUuid to get an object. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

5. GET <superclass>/<uuid>?t=<subclass>&v=<rep>

Purpose is to use a representation from a parent class. Like 5, except after getting by Uuid, the subclass resource coerces the result to be its type. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the supertype has been searched, at which point a rep does not exist error is thrown.

6. POST <superclass>?t=<subclass> {body}

Purpose is to create a new object of type <subclass>. The subclass resource creates a new object whose fields are extracted from the body (no need to know. The subclass resource save method uses services method to save the object; validation takes place in the save method.

7. POST <superclass>/<uuid>?!t {body}

Purpose is to update an object of any subclass. The type is determined and the operation is delegated to the resource of the actual type.

8. POST <superclass/<uuid>?t=<subclass> {body}

Purpose is to update an object using only parent-level fields. This is to avoid additional privileges at the actual subclass level. The type is deermined and the operation is delegated to the resource of the declared type. Throws an error if the actual type is not a subclass of the type declared.

9. DELETE <superclass>/<uuid>?!t&!purge, DELETE <superclass>/<uuid>?t=<subclass>&!purge

Purpose is to delete an object of any subclass. The type is determined and the operation is delegated to the resource of the actual type. The subclass, if present, must match the actual type of the resource.

10. DELETE <superclass>/<uuid>?purge&!t, DELETE <superclass>/<uuid>?purge

Purpose is to purge an object of any subclass. The type is determine and the operation is delegated to the resource of the actual type. The subclass, if present, must match the actual type of the resource.


[hidden email] from OpenMRS Developers' mailing list



[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list
Sateesh Babu Sateesh Babu
Reply | Threaded
Open this post in threaded view
|

Re: Sateesh's comment on encounter creation

In reply to this post by Friedman, Roger (CDC/CGH/DGHA) (CTR)
I took the example of an encounter, but this question is valid for any REST API. I would feel nervous if I have to split my single transaction into multiple REST queries (the burden is more on the REST API user). Why not have a single, simple REST call for the user and allow the heavy lifting to happen on the server side?

--
Sateesh

On Fri, Apr 27, 2012 at 9:32 PM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

I believe that Darius, Rafal and I are pretty much in agreement that sub-resources should be creatable from resources, but that full resources should be created separately and then either used as a uuid reference or added explicitly to a multi-object collection.  Since there's no a priori way to know whether a link has been put in the record itself, in a mapping object that's also a (sub-)resource, or a mapping object that's only a mapping object, the REST machinery has difficulty in figuring out what to do; we may need to have add/remove member service or pojo methods to handle collections.  Rafal found that bulk replacement of collection members produces Hibernate errors.

 

My idea in creating the page https://wiki.openmrs.org/display/docs/Standard_Modeling+of+Resource+Relationship+Types was to document the types of relationship we have in OpenMRS and how those relationships should be modeled in REST.  It still needs input.

 

In the case of encounter, the answer to Sateesh's comment depends on whether there can be encounterless obs.  If there are no encounterless obs, then obs should be a sub-resource of encounter and thus creatable.  If there are encounterless obs, then obs could be a full resource (as it is now) and the encounter and the obs should be created separately and then the obs added to the encounter.   However, obs could be a sub-resource of patient (there are no patientless obs), in which case you could create obs while creating a patient.

 

Further, every encounter has an encounter type.  Encounter type is a resource, so it would have to be created separately and then used within the encounter being created.  Likewise, you couldn't create a location or a provider/user while creating the encounter.

 

If there were a way to inform the REST machinery what kind of a linkage exists between two resources, then it might be possible to weaken these constraints.  But I don't think we'd ever want a complete free-for-all; the idea of creating a new encounter type or location on the fly seems pretty dubious to me.

 

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Burke Mamlin
Sent: Friday, April 27, 2012 9:49 AM
To: [hidden email]
Subject: Re: [OPENMRS-DEV] An Alternative Approach to Subclass Implementation

 

Good point, Sateesh.  When creating an encounter, you could be able to create all of the associated data as well within a single call; in fact, this should be the preferred method for creating an encounter.  This would include observations (obs) now, include orders as they are added to REST, and eventually include notes & form data once those are fully realized.

 

Cheers,

 

-Burke

On Fri, Apr 27, 2012 at 4:23 AM, Sateesh Babu <[hidden email]> wrote:

One question (concern) here. For example, if a client has to create a patient encounter with drug and lab orders, the client has to potentially make 2-3 (or even more) REST calls.
What if the first REST call succeeds and the next two fail or a combination like pass-fail-pass, pass-pass-fail etc. The failure can be because of any reason ranging from network failure, data corruption, bugs etc. REST inherently being a stateless protocol would leave the data on the server in an inconsistent state.
Instead, why not send the complete data in the transaction in one single go. So for the same above example, one single data object could be created with the encounter + drug order + lab order. This way if the transaction fails, then atleast the data is left in a consistent state. If data size is a concern, we can always use compression. There could be a meta-data object in the beginning of the object to describe what the contents of the json object are. Something like:
element {
  encounter: {
                   ...
                  }
  drug-order: {
                   ...
                   }
  lab-order: {
                  ...
                }
}

--
Sateesh

 

On Fri, Apr 27, 2012 at 11:22 AM, Darius Jazayeri <[hidden email]> wrote:

Yeah, I coded it such that classes have to actively choose to make themselves subclassable, but it should only take a few lines of code to change that.

 

-Darius

 

On Thu, Apr 26, 2012 at 8:28 PM, Ben Wolfe <[hidden email]> wrote:

So I was in Rogers camp until this argument.  There were three types of subclassing that we defined.  The patient/person subclassing is a first class resource, so both are primary documents. The ProblemList/Allergy subclassing completely hide their parents, so they are primary documents. However, these Order subclasses are second class citizens of sorts. They depend on the parent Order resource.  I don't like introducing yet another *Handler class, but calling them a resource is mixing ideas.

 

I'd favor making every class subclassable by default.  I think you had reversed Darius.

 

Ben

 

On Thu, Apr 26, 2012 at 9:42 PM, Darius Jazayeri <[hidden email]> wrote:

Hi Roger,

 

So, as you can probably imagine, I disagree. :-)

 

I think the whole point is that it's not appropriate to expose subclasses as if they were resources. I'm explicitly saying they're not resources, and that consumers of the REST API should not think of them as resources, but rather should think "the order resource tells me about orders, and drug orders are one type of order".

 

So I don't think that exposing a drugorder URI that redirects to order?t=drugorder is right. Also, using HTTP 307 (Temporary Redirect) means that the client has to make 2 requests to do anything (unless they cheat by going straight to the order resource).

 

What do others think?

 

I refactored my code a bit so now DelegatingSubclassHandler declares save() and purge() methods, which have a default implementation in BaseDelegatingSubclassHandler. Is that a good enough solution?

 

-Darius

 

On Thu, Apr 26, 2012 at 12:50 PM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

So I have been trying to figure out if there is any way to break out of this back and forth.  I have decided that what I really want to see is subclass resources and controllers that act as if they were full-fledged resources and preserve the subclass hierarchy but don't screw up caching.  So I am going to suggest the following:

 

(1) Subclass resources should be addressed like full resources, ws/<subclass>/... but the subclass controller should issue a 307 redirect to ws/<superclass>/...?t=<subclass>.  Subclasses can have child classes represented as sub-resources, and sub-resources can be created when a resource is created.  It is OK for subclass controllers to hand custom queries directly to their resource without passing through the superclass.

 

(2) The superclass should delegate to the subclass for representations, searches and saves.  If the named representation or search does not exist, my choice would be to ascend the class hierarchy.  I understand this might require some registration, but only to the parent.

 

(3) The superclass resource would have to implement the superclass representations, searches and saves.  It would also have to implement the delegation mechanism for subclasses.

 

(4) Subclass name will be returned as part of the standard representations.  Subclass name will not be needed for POST.

 

So given our Order -->  DrugOrder --> HIVDrugOrder class hierarchy

 

POST ws/order {body} creates an order which is filled from the body and validated and saved

POST ws/drugorder {body} is redirected to ws/order?t=drugorder {body} which delegates to the drugorder resource to create a drugorder which is filled from the body and delegated to the drugorder resource for validation and saving

POST ws/hivdrugorder {body} is redirected to ws/order?t=hivdrugorder {body} which delegates to the hivdrugorder resource to create an hivdrugorder which is filled from the body and delegated to the hivdrugorder resource for validation and saving.

 

POST ws/order/uuid {body} retrieves the order which is filled from the body and validated and saved

POST ws/drugorder/uuid {body} is redirected to ws/order?t=drugorder {body} which retrieves the order which is filled from the body and delegated to the drugorder resource for validation and saving

POST ws/hivdrugorder/uuid {body} is redirected to ws/order?t=hivdrugorder {body} which retrieves the order which is filled from the body and delegated to the hivdrugorder resource for validation and saving

 

GET ws/order/uuid?v=<rep> retrieves the order whose type is determined and delegated to the subclass for generation of the rep.  If the object with the uuid is not assignable from order or the rep does not exist, an error is thrown. 

GET ws/drugorder/uuid?v=<rep> is redirected to ws/order/uuid&v=<rep>?t=drugorder.  The search for the representation is done in the resoruce drugorder.

GET ws/hivdrugorder/uuid?v=<rep> is redirected to ws/order/uuid&v=<rep>?t=drugorder.  The search for the representation is done in the resource hivdrugorder.

 

GET ws/order&v=<rep> retrieves all orders; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named rep does not exist, an error is thrown.

GET ws/drugorder&v=<rep> is redirected to ws/order?v=rep&t=drugorder.  The superclass delegates to the class, which retrieves all drugorders; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named rep does not exist, an error is thrown.

GET ws/hivdrugorder&v=<rep> is redirected to ws/order?v=rep&t=hivdrugorder.  The superclass delegates to the class, which retrieves all hivdrugorders; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named rep does not exist, an error is thrown.

 

GET ws/order&<query>=<param> retrieves all orders satisfying the condition; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named query does not exist, an error is thrown.

GET ws/drugorder&<query>=<param> is redirected to ws/order?<query>=<param>&t=drugorder.  The superclass delegates to the subclass to retrieve all orders satisfying the condition; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named query does not exist, an error is thrown.

GET ws/hivdrugorder&<query>=<param> is redirected to ws/order?<query>=<param>&t=hivdrugorder.  The superclass delegates to the subclass to retrieve all orders satisfying the condition; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named query does not exist, an error is thrown.

 

DELETE is handled by the superclass.

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Darius Jazayeri
Sent: Thursday, April 26, 2012 11:34 AM


To: [hidden email]
Subject: Re: [OPENMRS-DEV] An Alternative Approach to Subclass Implementation

 

To be clear, the POSTs that Burke showed a couple of emails ago will work exactly as written, except of course that he simplified the actual properties into just "order". And the response to the GET that he showed will also be as he wrote it (except that uri is within links and the whole thing is in a PageableResults object). And the URIs he gave for the POSTs and GET are exactly right, there is no need for a ?t=type parameter.

 

Responses inline below.

 

-Darius

On Thu, Apr 26, 2012 at 7:14 AM, Burke Mamlin <[hidden email]> wrote:

On Thu, Apr 26, 2012 at 8:32 AM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

Burke --

     Regardless of whether it's Darius or me, each of the posts 1 and 3 would need a t=<type> parameter. 

     In my design, t is always virtual, it is the classname of the object; this avoids issues like having to use a global variable or predefined value or concept map to know the value of order type for drug order.  It also leaves order type available for distinctions that might be important to the facility without affecting the data model (say a narcotic drug order).  I'm not sure where Darius is at on this; at one point he required a discriminator field with a known name (registered?), but I think he has become more virtual.  The point is, you can't change the value of a discriminator field, so why include it in the POST body?  I do return it in the body on GET, as does Darius.

 

You can't change the concept of an order either, so would that move the URL as well?  I don't like the idea of putting some of the properties in the URL and others in the data.  You're right that you can't change the type of order; however, you must specify it when creating an order.

 

I'm also not thrilled about clients needing to know the Java class name for every order type.  There are not an infinite number of orderables.  Most implementations will be able to get by with two up front (drugs & tests).  Even for an enterprise-level inpatient system, you can get by with only a dozen or so (e.g., activities, precautions, diet, call orders, drugs, lab tests, radiology tests, procedures, nursing orders, referrals, education/handouts).  Actually, each of these basic types could be represented by a concept class and that class name could be used.

 

As I've coded it, the java class names are not exposed. To create a drug order you say ' type: "drugorder" '.

(@Roger, as coded you only use a &t=drugorder when you're doing a get-all or search, to say you only want objects of that type.)

 

 

     Also, Darius and I differ about privileges and validation at posts 1 and 3.  In Darius' design, both commands would require the privileges to create an order and would validate per order's requirements.  In my design, post 1 would require the privileges to create a drug order and would validate per drug order's requirements, while post 3 would require the privileges to create a referral order and would validate per referral order's requirements. 

 

There should be a top level privilege for ordering in general.  My assumption in posting the orders was that the REST document received would be passed to the order type-specific handler, so permissions & validation could be type-specific as you describe – i.e., we would have separate validators for each basic type of order.

 

The underlying Java API is built such that if you have Manage Orders privilege, you can do OrderService.saveOrder(anyOrderSubclass). My implementation does the actual save using this.

As coded, the type-specific-handler is only allowed to specify a "delegating resource description" which indicates what fields may be created/edited, it doesn't actually get a "save(T)" call. This is an oversight, due to the fact that none of our core resources actually do any custom save/validation logic that isn't routed through the resource description.

I can add a save(T) method on the subtype handlers, and let the base implementation of it just call the resource's save method.

 

 

    At get 4, both designs would use the default rep associated with the subclass of the object.  However, suppose you wanted to display all the types of order with just the order fields and some combination of subclass-specific fields in a details column.  In my design, you could define a named representation (say, COMPRESSED) that would be defined differently for different subclasses; you could even define it at a parent level (say drug order) and it could be used by children (like HIV drug order).  I don't believe you can do this in Darius' design.

 

Again, I think I agree with you here.  I was assuming that the generation of representation for each order would be delegated to it's type-specific handler, so if you asked for a FOOBAR representation of all the patient's orders, the API would iterate through the orders, asking for the representation for each from its type-specific handler.

 

@Darius – is that not what you're doing?  i.e., were you creating something that would not defer calls to type-specific handlers?

 

I do delegate to type-specific handlers. You can also define named representations on subclasses. What you cannot do is define a representation in the top-level resource, and get a subtype in that representation. E.g. if order defines a "foobar" rep, that is not inherited by drugorder. (Though drugorder could of course define that named representation also, as a one-liner that calls the parent resource.)

 

 

    With Darius, at step 0 (before the drug order is built in), you have to create a resource and controller for the order superclass; you have to create a handler, and a controller if you want custom searches, for drug order.  The superclass resource will contain all the methods of the current design plus the non-final declaration.

 

Yes.  The idea is that drug orders are provided for you.  So "you" will not have to do these steps.

 

  Darius depends on each resource and handler having the variable lists created by RESTWS-226, which I do not.  The drug order handler will contain its registration plus most of the methods of a resource (no save, delete or purge).  At step 2, you would need to build another handler and possibly a controller.

    With me, at step 0 you have to create a resource and controller for the hierarchy, a resource and controller for the superclass (order), and a resource and controller for the subclass (drug order).  The controllers are one-liners unless you want to add custom searches; they are just like the current design.  The hierarchy resource could also be a one-liner, with the actual delegations taking place in an abstract class using generics.  The resource for the superclass and the subclass would look just like the current design.  At step 2, you would create another controller and resource for referral order and that would also look just like the current design.

 

If you're creating a separate resource for each type, then doesn't that imply a different URL for each order type?

 

     In our discussion, Darius and I identified some situations (as I recall relating to validation) where it would be necessary under his design to override the supplied core resource with a user-defined resource that extended the core resource.  This could not be done independently for more than one subclass, and would require a custom build.  This would not be necessary with my design.

 

Can you provide an concrete example?  I'm presuming each order type has an associated handler/validator.

 

-Burke

 

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Burke Mamlin
Sent: Wednesday, April 25, 2012 5:08 PM


To: [hidden email]
Subject: Re: [OPENMRS-DEV] An Alternative Approach to Subclass Implementation

 

Ok, so if I want to create a drug order and a referral order for patient with uuid 123-456, then request all orders for the patient, it would look something like this (simplifying the order details for a simpler example)?

 

1. Create a drug order for my patient; they're built-in:

POST order

{ "patient":"123-456", "type":"drugorder", "order":"penicillin 500 mg orally twice daily for 2 weeks" }

 

2. Install a module to add support for referral orders:

Install a referrals module that adds a referralorder type of order.  Presumably, by introducing a new concept class for referral order, along with a handler for the new order type and, of course, a ReferralOrder class that extends Order.

 

3. Create a referral order for my patient:

POST order

{ "patient":"123-456", "type":"referralorder", "order":"cardiology referral please evaluate for rheumatic heart disease" }

 

4. Find all the active orders for my patient:

GET order?patient=123-456

[

{ "patient":"123-456", "type":"drugorder", "order":"penicillin 500 mg orally twice daily for 2 weeks", "uri":"…" },

{ "patient":"123-456", "type":"referralorder", "order":"cardiology referral please evaluate for rheumatic heart disease", "uri":"…" }

]

 

Is that close?

 

-Burke

 

On Tue, Apr 24, 2012 at 9:21 PM, Darius Jazayeri <[hidden email]> wrote:

On RESTWS-243 I've attached a patch that contains my implementation that I'm now ready to commit. (It's what Roger was responding to in his email.)

 

I'm going to hold off on committing until after tomorrow's design call in case we have time to discuss this a bit.

 

Basically what I've done is allow resources to indicate that they support subclasses, and if they do, they'll have all appropriate DelegatingSubclassHandlers registered with them via spring.

 

The idea is that we're hiding subclasses from the consumer of the REST API. As far as they're concerned there is a single "order" resource, which has documents of different types (e.g. "order", "drugorder", etc.) Behind the scenes each type is a subclass, but in REST that's not visible, rather they're exposed as different document types. 

 

DelegatingSubclassHandler encapsulates the methods subclasses need to specify for their owning resource.

 

Here is the functionality that my approach supports:

 

GET order

  • gets all orders regardless of type
  • each document in the list may have different types
  • the top-level resource does the db query and delegates to the appropriate handler to convert each result

GET order?t=drugorder

  • gets all orders whose type is drugorder
  • the subclass handler does the db query
  • In my implementation GET order?t=order (i.e. get only things that are order stubs) is not allowed

GET order?patient=<uuid>

  • gets all orders for the given patient
  • each document in the list may have different types

GET order?patient=<uuid>&t=drugorder

  • gets all orders for the given patient whose type is drugorder
  • if the subclass handler defines a suitable method, we use it, otherwise the top-level resource queries by patient, and manually filters out by type

GET order/<uuid>

  • returns a specific order
  • representations are defined by the type, e.g. { "type": "drugorder", "dose": "100", ... }

POST order

  • creates a new order
  • you must specify the type in the content, e.g. { "type": "drugorder", "dose": "100", ... }
  • allowed properties are defined by the type

POST order/<uuid>

  • modifies an existing order
  • you are not allowed to change the type
  • allowed properties are defined by the type

DELETE order/<uuid>

  • voids an order
  • handled by the top-level resource, not the subclass handler

DELETE order/<uuid>?purge=true

  • purges an order
  • handled by the top-level resource, not the subclass handler

@Roger, can you please phrase your counterproposal in terms of the additional functionality that you want to expose beyond these methods?

 

-Darius

 

On Mon, Apr 23, 2012 at 9:54 AM, Burke Mamlin <[hidden email]> wrote:

Roger,

 

Could you provide some concrete examples?

 

-Burke

 

On Mon, Apr 23, 2012 at 8:07 AM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

AN ALTERNATIVE APPROACH TO SUBCLASS IMPLEMENTATION

In this approach, subclass resources remain resources, just are mapped via the superclass. Substantially all operations are delegated to the appropriate subclass resource. This is motivated by simplicity, validation and representation considerations. It is also motivated by a privilege issue I just realized – the people authorized to create/update a lab order are likely different from those allowed to create/update a drug order. I have assumed that the privilege of updating a parent class field in subclass record requires only parent class privileges

I don't believe this alternative requires much in the way of changes to existing code other than mappings; adding the controller and resource for each superclass; some changes in asRepresentation where it finds the right representation to use; and some changes where updates are going on to allow the possibility that the fields to be looked for in the request body may come from an object which is a parent class of the object being updated. It eliminates the need for the two variable lists used by Darius (although they could still be used if it was thought they added value). I still think it is a good idea to treat ActiveLists as an abstract class so that the AllergyList and the ProblemList appear to be two different superclasses.

All members of a class hierarchy request a mapping to /<superclass>?t=<subclass>. The superclass also requests a mapping to /<superclass>?!t. I have tried to make all the calls of /<superclass>?!t work the same as calls to /<superclass>?t=<superclass>, if I have missed please let me know.  It might be worthwhile to extend Resource to SuperclassResource to make the coding of the type-free calls easier.  All representations include a t virtual field which contains the actual subclass of the object being represented.

1. GET <superclass>?t=<subclass>&v=<rep>, GET <superclass>?!t

Purpose is to get all records of a particular subclass in a formatted representation. Spring routes this to the subclass controller. The subclass resource uses getAll to get a (polymorphic) list of objects. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

2. GET <superclass>?t=<subclass>&v=<rep>&q=<search param>, GET <superclass>?!t&v=<rep>&q=<search param>

Purpose is to use the standard query for a subclass to get a formatted representation. Spring routes this to the subclass controller. The subclass resource uses doSearch to get a (polymorphic) list of objects. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

3. GET <superclass>?t=<subclass>&v=<rep>&<custom search>=<search param>

Purpose is to do a custom search that is defined at some level of the class hierarchy.  Spring routes this to the subclass controller. The subclass controller creates a resource for its type and delegates the search to it. The resource uses service methods to do the search to produce a (polymorphic) list of objects. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

4. GET <superclass>/<uuid>?!t&v=<rep>

Purpose is to get all records of a particular subclass in a formatted representation. Spring routes this to the superclass controller. The superclass resource uses getByUuid to get an object. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

5. GET <superclass>/<uuid>?t=<subclass>&v=<rep>

Purpose is to use a representation from a parent class. Like 5, except after getting by Uuid, the subclass resource coerces the result to be its type. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the supertype has been searched, at which point a rep does not exist error is thrown.

6. POST <superclass>?t=<subclass> {body}

Purpose is to create a new object of type <subclass>. The subclass resource creates a new object whose fields are extracted from the body (no need to know. The subclass resource save method uses services method to save the object; validation takes place in the save method.

7. POST <superclass>/<uuid>?!t {body}

Purpose is to update an object of any subclass. The type is determined and the operation is delegated to the resource of the actual type.

8. POST <superclass/<uuid>?t=<subclass> {body}

Purpose is to update an object using only parent-level fields. This is to avoid additional privileges at the actual subclass level. The type is deermined and the operation is delegated to the resource of the declared type. Throws an error if the actual type is not a subclass of the type declared.

9. DELETE <superclass>/<uuid>?!t&!purge, DELETE <superclass>/<uuid>?t=<subclass>&!purge

Purpose is to delete an object of any subclass. The type is determined and the operation is delegated to the resource of the actual type. The subclass, if present, must match the actual type of the resource.

10. DELETE <superclass>/<uuid>?purge&!t, DELETE <superclass>/<uuid>?purge

Purpose is to purge an object of any subclass. The type is determine and the operation is delegated to the resource of the actual type. The subclass, if present, must match the actual type of the resource.


[hidden email] from OpenMRS Developers' mailing list



[hidden email] from OpenMRS Developers' mailing list
Darius Jazayeri-2 Darius Jazayeri-2
Reply | Threaded
Open this post in threaded view
|

Re: Sateesh's comment on encounter creation

Can you give a concrete example of what you're talking about, including what the response would look like?

Generally speaking, we allow you to create one resource at a time because the semantics of rest support that: you do a post to the resource URI and you get back the resource created as a response document. (also, any resource should only be created/edited via one URI, to support web-standard caching)

-Darius (by phone)

On Apr 28, 2012 2:23 AM, "Sateesh Babu" <[hidden email]> wrote:
I took the example of an encounter, but this question is valid for any REST API. I would feel nervous if I have to split my single transaction into multiple REST queries (the burden is more on the REST API user). Why not have a single, simple REST call for the user and allow the heavy lifting to happen on the server side?

--
Sateesh

On Fri, Apr 27, 2012 at 9:32 PM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

I believe that Darius, Rafal and I are pretty much in agreement that sub-resources should be creatable from resources, but that full resources should be created separately and then either used as a uuid reference or added explicitly to a multi-object collection.  Since there's no a priori way to know whether a link has been put in the record itself, in a mapping object that's also a (sub-)resource, or a mapping object that's only a mapping object, the REST machinery has difficulty in figuring out what to do; we may need to have add/remove member service or pojo methods to handle collections.  Rafal found that bulk replacement of collection members produces Hibernate errors.

 

My idea in creating the page https://wiki.openmrs.org/display/docs/Standard_Modeling+of+Resource+Relationship+Types was to document the types of relationship we have in OpenMRS and how those relationships should be modeled in REST.  It still needs input.

 

In the case of encounter, the answer to Sateesh's comment depends on whether there can be encounterless obs.  If there are no encounterless obs, then obs should be a sub-resource of encounter and thus creatable.  If there are encounterless obs, then obs could be a full resource (as it is now) and the encounter and the obs should be created separately and then the obs added to the encounter.   However, obs could be a sub-resource of patient (there are no patientless obs), in which case you could create obs while creating a patient.

 

Further, every encounter has an encounter type.  Encounter type is a resource, so it would have to be created separately and then used within the encounter being created.  Likewise, you couldn't create a location or a provider/user while creating the encounter.

 

If there were a way to inform the REST machinery what kind of a linkage exists between two resources, then it might be possible to weaken these constraints.  But I don't think we'd ever want a complete free-for-all; the idea of creating a new encounter type or location on the fly seems pretty dubious to me.

 

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Burke Mamlin
Sent: Friday, April 27, 2012 9:49 AM
To: [hidden email]
Subject: Re: [OPENMRS-DEV] An Alternative Approach to Subclass Implementation

 

Good point, Sateesh.  When creating an encounter, you could be able to create all of the associated data as well within a single call; in fact, this should be the preferred method for creating an encounter.  This would include observations (obs) now, include orders as they are added to REST, and eventually include notes & form data once those are fully realized.

 

Cheers,

 

-Burke

On Fri, Apr 27, 2012 at 4:23 AM, Sateesh Babu <[hidden email]> wrote:

One question (concern) here. For example, if a client has to create a patient encounter with drug and lab orders, the client has to potentially make 2-3 (or even more) REST calls.
What if the first REST call succeeds and the next two fail or a combination like pass-fail-pass, pass-pass-fail etc. The failure can be because of any reason ranging from network failure, data corruption, bugs etc. REST inherently being a stateless protocol would leave the data on the server in an inconsistent state.
Instead, why not send the complete data in the transaction in one single go. So for the same above example, one single data object could be created with the encounter + drug order + lab order. This way if the transaction fails, then atleast the data is left in a consistent state. If data size is a concern, we can always use compression. There could be a meta-data object in the beginning of the object to describe what the contents of the json object are. Something like:
element {
  encounter: {
                   ...
                  }
  drug-order: {
                   ...
                   }
  lab-order: {
                  ...
                }
}

--
Sateesh

 

On Fri, Apr 27, 2012 at 11:22 AM, Darius Jazayeri <[hidden email]> wrote:

Yeah, I coded it such that classes have to actively choose to make themselves subclassable, but it should only take a few lines of code to change that.

 

-Darius

 

On Thu, Apr 26, 2012 at 8:28 PM, Ben Wolfe <[hidden email]> wrote:

So I was in Rogers camp until this argument.  There were three types of subclassing that we defined.  The patient/person subclassing is a first class resource, so both are primary documents. The ProblemList/Allergy subclassing completely hide their parents, so they are primary documents. However, these Order subclasses are second class citizens of sorts. They depend on the parent Order resource.  I don't like introducing yet another *Handler class, but calling them a resource is mixing ideas.

 

I'd favor making every class subclassable by default.  I think you had reversed Darius.

 

Ben

 

On Thu, Apr 26, 2012 at 9:42 PM, Darius Jazayeri <[hidden email]> wrote:

Hi Roger,

 

So, as you can probably imagine, I disagree. :-)

 

I think the whole point is that it's not appropriate to expose subclasses as if they were resources. I'm explicitly saying they're not resources, and that consumers of the REST API should not think of them as resources, but rather should think "the order resource tells me about orders, and drug orders are one type of order".

 

So I don't think that exposing a drugorder URI that redirects to order?t=drugorder is right. Also, using HTTP 307 (Temporary Redirect) means that the client has to make 2 requests to do anything (unless they cheat by going straight to the order resource).

 

What do others think?

 

I refactored my code a bit so now DelegatingSubclassHandler declares save() and purge() methods, which have a default implementation in BaseDelegatingSubclassHandler. Is that a good enough solution?

 

-Darius

 

On Thu, Apr 26, 2012 at 12:50 PM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

So I have been trying to figure out if there is any way to break out of this back and forth.  I have decided that what I really want to see is subclass resources and controllers that act as if they were full-fledged resources and preserve the subclass hierarchy but don't screw up caching.  So I am going to suggest the following:

 

(1) Subclass resources should be addressed like full resources, ws/<subclass>/... but the subclass controller should issue a 307 redirect to ws/<superclass>/...?t=<subclass>.  Subclasses can have child classes represented as sub-resources, and sub-resources can be created when a resource is created.  It is OK for subclass controllers to hand custom queries directly to their resource without passing through the superclass.

 

(2) The superclass should delegate to the subclass for representations, searches and saves.  If the named representation or search does not exist, my choice would be to ascend the class hierarchy.  I understand this might require some registration, but only to the parent.

 

(3) The superclass resource would have to implement the superclass representations, searches and saves.  It would also have to implement the delegation mechanism for subclasses.

 

(4) Subclass name will be returned as part of the standard representations.  Subclass name will not be needed for POST.

 

So given our Order -->  DrugOrder --> HIVDrugOrder class hierarchy

 

POST ws/order {body} creates an order which is filled from the body and validated and saved

POST ws/drugorder {body} is redirected to ws/order?t=drugorder {body} which delegates to the drugorder resource to create a drugorder which is filled from the body and delegated to the drugorder resource for validation and saving

POST ws/hivdrugorder {body} is redirected to ws/order?t=hivdrugorder {body} which delegates to the hivdrugorder resource to create an hivdrugorder which is filled from the body and delegated to the hivdrugorder resource for validation and saving.

 

POST ws/order/uuid {body} retrieves the order which is filled from the body and validated and saved

POST ws/drugorder/uuid {body} is redirected to ws/order?t=drugorder {body} which retrieves the order which is filled from the body and delegated to the drugorder resource for validation and saving

POST ws/hivdrugorder/uuid {body} is redirected to ws/order?t=hivdrugorder {body} which retrieves the order which is filled from the body and delegated to the hivdrugorder resource for validation and saving

 

GET ws/order/uuid?v=<rep> retrieves the order whose type is determined and delegated to the subclass for generation of the rep.  If the object with the uuid is not assignable from order or the rep does not exist, an error is thrown. 

GET ws/drugorder/uuid?v=<rep> is redirected to ws/order/uuid&v=<rep>?t=drugorder.  The search for the representation is done in the resoruce drugorder.

GET ws/hivdrugorder/uuid?v=<rep> is redirected to ws/order/uuid&v=<rep>?t=drugorder.  The search for the representation is done in the resource hivdrugorder.

 

GET ws/order&v=<rep> retrieves all orders; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named rep does not exist, an error is thrown.

GET ws/drugorder&v=<rep> is redirected to ws/order?v=rep&t=drugorder.  The superclass delegates to the class, which retrieves all drugorders; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named rep does not exist, an error is thrown.

GET ws/hivdrugorder&v=<rep> is redirected to ws/order?v=rep&t=hivdrugorder.  The superclass delegates to the class, which retrieves all hivdrugorders; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named rep does not exist, an error is thrown.

 

GET ws/order&<query>=<param> retrieves all orders satisfying the condition; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named query does not exist, an error is thrown.

GET ws/drugorder&<query>=<param> is redirected to ws/order?<query>=<param>&t=drugorder.  The superclass delegates to the subclass to retrieve all orders satisfying the condition; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named query does not exist, an error is thrown.

GET ws/hivdrugorder&<query>=<param> is redirected to ws/order?<query>=<param>&t=hivdrugorder.  The superclass delegates to the subclass to retrieve all orders satisfying the condition; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named query does not exist, an error is thrown.

 

DELETE is handled by the superclass.

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Darius Jazayeri
Sent: Thursday, April 26, 2012 11:34 AM


To: [hidden email]
Subject: Re: [OPENMRS-DEV] An Alternative Approach to Subclass Implementation

 

To be clear, the POSTs that Burke showed a couple of emails ago will work exactly as written, except of course that he simplified the actual properties into just "order". And the response to the GET that he showed will also be as he wrote it (except that uri is within links and the whole thing is in a PageableResults object). And the URIs he gave for the POSTs and GET are exactly right, there is no need for a ?t=type parameter.

 

Responses inline below.

 

-Darius

On Thu, Apr 26, 2012 at 7:14 AM, Burke Mamlin <[hidden email]> wrote:

On Thu, Apr 26, 2012 at 8:32 AM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

Burke --

     Regardless of whether it's Darius or me, each of the posts 1 and 3 would need a t=<type> parameter. 

     In my design, t is always virtual, it is the classname of the object; this avoids issues like having to use a global variable or predefined value or concept map to know the value of order type for drug order.  It also leaves order type available for distinctions that might be important to the facility without affecting the data model (say a narcotic drug order).  I'm not sure where Darius is at on this; at one point he required a discriminator field with a known name (registered?), but I think he has become more virtual.  The point is, you can't change the value of a discriminator field, so why include it in the POST body?  I do return it in the body on GET, as does Darius.

 

You can't change the concept of an order either, so would that move the URL as well?  I don't like the idea of putting some of the properties in the URL and others in the data.  You're right that you can't change the type of order; however, you must specify it when creating an order.

 

I'm also not thrilled about clients needing to know the Java class name for every order type.  There are not an infinite number of orderables.  Most implementations will be able to get by with two up front (drugs & tests).  Even for an enterprise-level inpatient system, you can get by with only a dozen or so (e.g., activities, precautions, diet, call orders, drugs, lab tests, radiology tests, procedures, nursing orders, referrals, education/handouts).  Actually, each of these basic types could be represented by a concept class and that class name could be used.

 

As I've coded it, the java class names are not exposed. To create a drug order you say ' type: "drugorder" '.

(@Roger, as coded you only use a &t=drugorder when you're doing a get-all or search, to say you only want objects of that type.)

 

 

     Also, Darius and I differ about privileges and validation at posts 1 and 3.  In Darius' design, both commands would require the privileges to create an order and would validate per order's requirements.  In my design, post 1 would require the privileges to create a drug order and would validate per drug order's requirements, while post 3 would require the privileges to create a referral order and would validate per referral order's requirements. 

 

There should be a top level privilege for ordering in general.  My assumption in posting the orders was that the REST document received would be passed to the order type-specific handler, so permissions & validation could be type-specific as you describe – i.e., we would have separate validators for each basic type of order.

 

The underlying Java API is built such that if you have Manage Orders privilege, you can do OrderService.saveOrder(anyOrderSubclass). My implementation does the actual save using this.

As coded, the type-specific-handler is only allowed to specify a "delegating resource description" which indicates what fields may be created/edited, it doesn't actually get a "save(T)" call. This is an oversight, due to the fact that none of our core resources actually do any custom save/validation logic that isn't routed through the resource description.

I can add a save(T) method on the subtype handlers, and let the base implementation of it just call the resource's save method.

 

 

    At get 4, both designs would use the default rep associated with the subclass of the object.  However, suppose you wanted to display all the types of order with just the order fields and some combination of subclass-specific fields in a details column.  In my design, you could define a named representation (say, COMPRESSED) that would be defined differently for different subclasses; you could even define it at a parent level (say drug order) and it could be used by children (like HIV drug order).  I don't believe you can do this in Darius' design.

 

Again, I think I agree with you here.  I was assuming that the generation of representation for each order would be delegated to it's type-specific handler, so if you asked for a FOOBAR representation of all the patient's orders, the API would iterate through the orders, asking for the representation for each from its type-specific handler.

 

@Darius – is that not what you're doing?  i.e., were you creating something that would not defer calls to type-specific handlers?

 

I do delegate to type-specific handlers. You can also define named representations on subclasses. What you cannot do is define a representation in the top-level resource, and get a subtype in that representation. E.g. if order defines a "foobar" rep, that is not inherited by drugorder. (Though drugorder could of course define that named representation also, as a one-liner that calls the parent resource.)

 

 

    With Darius, at step 0 (before the drug order is built in), you have to create a resource and controller for the order superclass; you have to create a handler, and a controller if you want custom searches, for drug order.  The superclass resource will contain all the methods of the current design plus the non-final declaration.

 

Yes.  The idea is that drug orders are provided for you.  So "you" will not have to do these steps.

 

  Darius depends on each resource and handler having the variable lists created by RESTWS-226, which I do not.  The drug order handler will contain its registration plus most of the methods of a resource (no save, delete or purge).  At step 2, you would need to build another handler and possibly a controller.

    With me, at step 0 you have to create a resource and controller for the hierarchy, a resource and controller for the superclass (order), and a resource and controller for the subclass (drug order).  The controllers are one-liners unless you want to add custom searches; they are just like the current design.  The hierarchy resource could also be a one-liner, with the actual delegations taking place in an abstract class using generics.  The resource for the superclass and the subclass would look just like the current design.  At step 2, you would create another controller and resource for referral order and that would also look just like the current design.

 

If you're creating a separate resource for each type, then doesn't that imply a different URL for each order type?

 

     In our discussion, Darius and I identified some situations (as I recall relating to validation) where it would be necessary under his design to override the supplied core resource with a user-defined resource that extended the core resource.  This could not be done independently for more than one subclass, and would require a custom build.  This would not be necessary with my design.

 

Can you provide an concrete example?  I'm presuming each order type has an associated handler/validator.

 

-Burke

 

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Burke Mamlin
Sent: Wednesday, April 25, 2012 5:08 PM


To: [hidden email]
Subject: Re: [OPENMRS-DEV] An Alternative Approach to Subclass Implementation

 

Ok, so if I want to create a drug order and a referral order for patient with uuid 123-456, then request all orders for the patient, it would look something like this (simplifying the order details for a simpler example)?

 

1. Create a drug order for my patient; they're built-in:

POST order

{ "patient":"123-456", "type":"drugorder", "order":"penicillin 500 mg orally twice daily for 2 weeks" }

 

2. Install a module to add support for referral orders:

Install a referrals module that adds a referralorder type of order.  Presumably, by introducing a new concept class for referral order, along with a handler for the new order type and, of course, a ReferralOrder class that extends Order.

 

3. Create a referral order for my patient:

POST order

{ "patient":"123-456", "type":"referralorder", "order":"cardiology referral please evaluate for rheumatic heart disease" }

 

4. Find all the active orders for my patient:

GET order?patient=123-456

[

{ "patient":"123-456", "type":"drugorder", "order":"penicillin 500 mg orally twice daily for 2 weeks", "uri":"…" },

{ "patient":"123-456", "type":"referralorder", "order":"cardiology referral please evaluate for rheumatic heart disease", "uri":"…" }

]

 

Is that close?

 

-Burke

 

On Tue, Apr 24, 2012 at 9:21 PM, Darius Jazayeri <[hidden email]> wrote:

On RESTWS-243 I've attached a patch that contains my implementation that I'm now ready to commit. (It's what Roger was responding to in his email.)

 

I'm going to hold off on committing until after tomorrow's design call in case we have time to discuss this a bit.

 

Basically what I've done is allow resources to indicate that they support subclasses, and if they do, they'll have all appropriate DelegatingSubclassHandlers registered with them via spring.

 

The idea is that we're hiding subclasses from the consumer of the REST API. As far as they're concerned there is a single "order" resource, which has documents of different types (e.g. "order", "drugorder", etc.) Behind the scenes each type is a subclass, but in REST that's not visible, rather they're exposed as different document types. 

 

DelegatingSubclassHandler encapsulates the methods subclasses need to specify for their owning resource.

 

Here is the functionality that my approach supports:

 

GET order

  • gets all orders regardless of type
  • each document in the list may have different types
  • the top-level resource does the db query and delegates to the appropriate handler to convert each result

GET order?t=drugorder

  • gets all orders whose type is drugorder
  • the subclass handler does the db query
  • In my implementation GET order?t=order (i.e. get only things that are order stubs) is not allowed

GET order?patient=<uuid>

  • gets all orders for the given patient
  • each document in the list may have different types

GET order?patient=<uuid>&t=drugorder

  • gets all orders for the given patient whose type is drugorder
  • if the subclass handler defines a suitable method, we use it, otherwise the top-level resource queries by patient, and manually filters out by type

GET order/<uuid>

  • returns a specific order
  • representations are defined by the type, e.g. { "type": "drugorder", "dose": "100", ... }

POST order

  • creates a new order
  • you must specify the type in the content, e.g. { "type": "drugorder", "dose": "100", ... }
  • allowed properties are defined by the type

POST order/<uuid>

  • modifies an existing order
  • you are not allowed to change the type
  • allowed properties are defined by the type

DELETE order/<uuid>

  • voids an order
  • handled by the top-level resource, not the subclass handler

DELETE order/<uuid>?purge=true

  • purges an order
  • handled by the top-level resource, not the subclass handler

@Roger, can you please phrase your counterproposal in terms of the additional functionality that you want to expose beyond these methods?

 

-Darius

 

On Mon, Apr 23, 2012 at 9:54 AM, Burke Mamlin <[hidden email]> wrote:

Roger,

 

Could you provide some concrete examples?

 

-Burke

 

On Mon, Apr 23, 2012 at 8:07 AM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

AN ALTERNATIVE APPROACH TO SUBCLASS IMPLEMENTATION

In this approach, subclass resources remain resources, just are mapped via the superclass. Substantially all operations are delegated to the appropriate subclass resource. This is motivated by simplicity, validation and representation considerations. It is also motivated by a privilege issue I just realized – the people authorized to create/update a lab order are likely different from those allowed to create/update a drug order. I have assumed that the privilege of updating a parent class field in subclass record requires only parent class privileges

I don't believe this alternative requires much in the way of changes to existing code other than mappings; adding the controller and resource for each superclass; some changes in asRepresentation where it finds the right representation to use; and some changes where updates are going on to allow the possibility that the fields to be looked for in the request body may come from an object which is a parent class of the object being updated. It eliminates the need for the two variable lists used by Darius (although they could still be used if it was thought they added value). I still think it is a good idea to treat ActiveLists as an abstract class so that the AllergyList and the ProblemList appear to be two different superclasses.

All members of a class hierarchy request a mapping to /<superclass>?t=<subclass>. The superclass also requests a mapping to /<superclass>?!t. I have tried to make all the calls of /<superclass>?!t work the same as calls to /<superclass>?t=<superclass>, if I have missed please let me know.  It might be worthwhile to extend Resource to SuperclassResource to make the coding of the type-free calls easier.  All representations include a t virtual field which contains the actual subclass of the object being represented.

1. GET <superclass>?t=<subclass>&v=<rep>, GET <superclass>?!t

Purpose is to get all records of a particular subclass in a formatted representation. Spring routes this to the subclass controller. The subclass resource uses getAll to get a (polymorphic) list of objects. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

2. GET <superclass>?t=<subclass>&v=<rep>&q=<search param>, GET <superclass>?!t&v=<rep>&q=<search param>

Purpose is to use the standard query for a subclass to get a formatted representation. Spring routes this to the subclass controller. The subclass resource uses doSearch to get a (polymorphic) list of objects. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

3. GET <superclass>?t=<subclass>&v=<rep>&<custom search>=<search param>

Purpose is to do a custom search that is defined at some level of the class hierarchy.  Spring routes this to the subclass controller. The subclass controller creates a resource for its type and delegates the search to it. The resource uses service methods to do the search to produce a (polymorphic) list of objects. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

4. GET <superclass>/<uuid>?!t&v=<rep>

Purpose is to get all records of a particular subclass in a formatted representation. Spring routes this to the superclass controller. The superclass resource uses getByUuid to get an object. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

5. GET <superclass>/<uuid>?t=<subclass>&v=<rep>

Purpose is to use a representation from a parent class. Like 5, except after getting by Uuid, the subclass resource coerces the result to be its type. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the supertype has been searched, at which point a rep does not exist error is thrown.

6. POST <superclass>?t=<subclass> {body}

Purpose is to create a new object of type <subclass>. The subclass resource creates a new object whose fields are extracted from the body (no need to know. The subclass resource save method uses services method to save the object; validation takes place in the save method.

7. POST <superclass>/<uuid>?!t {body}

Purpose is to update an object of any subclass. The type is determined and the operation is delegated to the resource of the actual type.

8. POST <superclass/<uuid>?t=<subclass> {body}

Purpose is to update an object using only parent-level fields. This is to avoid additional privileges at the actual subclass level. The type is deermined and the operation is delegated to the resource of the declared type. Throws an error if the actual type is not a subclass of the type declared.

9. DELETE <superclass>/<uuid>?!t&!purge, DELETE <superclass>/<uuid>?t=<subclass>&!purge

Purpose is to delete an object of any subclass. The type is determined and the operation is delegated to the resource of the actual type. The subclass, if present, must match the actual type of the resource.

10. DELETE <superclass>/<uuid>?purge&!t, DELETE <superclass>/<uuid>?purge

Purpose is to purge an object of any subclass. The type is determine and the operation is delegated to the resource of the actual type. The subclass, if present, must match the actual type of the resource.


[hidden email] from OpenMRS Developers' mailing list



[hidden email] from OpenMRS Developers' mailing list

[hidden email] from OpenMRS Developers' mailing list
Sateesh Babu Sateesh Babu
Reply | Threaded
Open this post in threaded view
|

Re: Sateesh's comment on encounter creation

An example scenario is as under:
1) Doctor retrieves the patient record
2) Updates the record with some new observations and also creates an encounter
3) Prescribes drugs for the health issue
4) Closes the record

At step 4, the expectation is that the patient record is persisted to the server backend. Steps 2,3 happens in the same webpage and for the doctor it is one single transaction. Meaning, the doctor expects complete consistency of all the data starting from steps 1-4. But if I understood correctly from the current REST API, step 2 would involve about three REST calls and step 3 would involve one more REST call. In total 4 REST calls.

So going back to my earlier comment, that if any of these calls fails for whatever reason, the data is left in an inconsistent state. And for the end-user (doctor here), it becomes a irritating factor. The reason I want to stress on this point is, the doctors in India are quite used to using paper and pen for all the encounters and digitisation happens offline (if at all if). If we have to make the doctors use the system, ensuring a consistent behaviour is very important.

The way I see the REST API is designed was around the data model and not around the transaction model (I could be completely wrong here, please correct if that is the case). Transaction model looks more about the end usecases. Had it not been REST, but another transactional protocol, like RMI, then it would be perfectly fine, since all the 3-4 individual transactions could still be wound over one single transaction call.

--
Sateesh

On Sat, Apr 28, 2012 at 9:02 PM, Darius Jazayeri <[hidden email]> wrote:

Can you give a concrete example of what you're talking about, including what the response would look like?

Generally speaking, we allow you to create one resource at a time because the semantics of rest support that: you do a post to the resource URI and you get back the resource created as a response document. (also, any resource should only be created/edited via one URI, to support web-standard caching)

-Darius (by phone)

On Apr 28, 2012 2:23 AM, "Sateesh Babu" <[hidden email]> wrote:
I took the example of an encounter, but this question is valid for any REST API. I would feel nervous if I have to split my single transaction into multiple REST queries (the burden is more on the REST API user). Why not have a single, simple REST call for the user and allow the heavy lifting to happen on the server side?

--
Sateesh

On Fri, Apr 27, 2012 at 9:32 PM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

I believe that Darius, Rafal and I are pretty much in agreement that sub-resources should be creatable from resources, but that full resources should be created separately and then either used as a uuid reference or added explicitly to a multi-object collection.  Since there's no a priori way to know whether a link has been put in the record itself, in a mapping object that's also a (sub-)resource, or a mapping object that's only a mapping object, the REST machinery has difficulty in figuring out what to do; we may need to have add/remove member service or pojo methods to handle collections.  Rafal found that bulk replacement of collection members produces Hibernate errors.

 

My idea in creating the page https://wiki.openmrs.org/display/docs/Standard_Modeling+of+Resource+Relationship+Types was to document the types of relationship we have in OpenMRS and how those relationships should be modeled in REST.  It still needs input.

 

In the case of encounter, the answer to Sateesh's comment depends on whether there can be encounterless obs.  If there are no encounterless obs, then obs should be a sub-resource of encounter and thus creatable.  If there are encounterless obs, then obs could be a full resource (as it is now) and the encounter and the obs should be created separately and then the obs added to the encounter.   However, obs could be a sub-resource of patient (there are no patientless obs), in which case you could create obs while creating a patient.

 

Further, every encounter has an encounter type.  Encounter type is a resource, so it would have to be created separately and then used within the encounter being created.  Likewise, you couldn't create a location or a provider/user while creating the encounter.

 

If there were a way to inform the REST machinery what kind of a linkage exists between two resources, then it might be possible to weaken these constraints.  But I don't think we'd ever want a complete free-for-all; the idea of creating a new encounter type or location on the fly seems pretty dubious to me.

 

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Burke Mamlin
Sent: Friday, April 27, 2012 9:49 AM
To: [hidden email]
Subject: Re: [OPENMRS-DEV] An Alternative Approach to Subclass Implementation

 

Good point, Sateesh.  When creating an encounter, you could be able to create all of the associated data as well within a single call; in fact, this should be the preferred method for creating an encounter.  This would include observations (obs) now, include orders as they are added to REST, and eventually include notes & form data once those are fully realized.

 

Cheers,

 

-Burke

On Fri, Apr 27, 2012 at 4:23 AM, Sateesh Babu <[hidden email]> wrote:

One question (concern) here. For example, if a client has to create a patient encounter with drug and lab orders, the client has to potentially make 2-3 (or even more) REST calls.
What if the first REST call succeeds and the next two fail or a combination like pass-fail-pass, pass-pass-fail etc. The failure can be because of any reason ranging from network failure, data corruption, bugs etc. REST inherently being a stateless protocol would leave the data on the server in an inconsistent state.
Instead, why not send the complete data in the transaction in one single go. So for the same above example, one single data object could be created with the encounter + drug order + lab order. This way if the transaction fails, then atleast the data is left in a consistent state. If data size is a concern, we can always use compression. There could be a meta-data object in the beginning of the object to describe what the contents of the json object are. Something like:
element {
  encounter: {
                   ...
                  }
  drug-order: {
                   ...
                   }
  lab-order: {
                  ...
                }
}

--
Sateesh

 

On Fri, Apr 27, 2012 at 11:22 AM, Darius Jazayeri <[hidden email]> wrote:

Yeah, I coded it such that classes have to actively choose to make themselves subclassable, but it should only take a few lines of code to change that.

 

-Darius

 

On Thu, Apr 26, 2012 at 8:28 PM, Ben Wolfe <[hidden email]> wrote:

So I was in Rogers camp until this argument.  There were three types of subclassing that we defined.  The patient/person subclassing is a first class resource, so both are primary documents. The ProblemList/Allergy subclassing completely hide their parents, so they are primary documents. However, these Order subclasses are second class citizens of sorts. They depend on the parent Order resource.  I don't like introducing yet another *Handler class, but calling them a resource is mixing ideas.

 

I'd favor making every class subclassable by default.  I think you had reversed Darius.

 

Ben

 

On Thu, Apr 26, 2012 at 9:42 PM, Darius Jazayeri <[hidden email]> wrote:

Hi Roger,

 

So, as you can probably imagine, I disagree. :-)

 

I think the whole point is that it's not appropriate to expose subclasses as if they were resources. I'm explicitly saying they're not resources, and that consumers of the REST API should not think of them as resources, but rather should think "the order resource tells me about orders, and drug orders are one type of order".

 

So I don't think that exposing a drugorder URI that redirects to order?t=drugorder is right. Also, using HTTP 307 (Temporary Redirect) means that the client has to make 2 requests to do anything (unless they cheat by going straight to the order resource).

 

What do others think?

 

I refactored my code a bit so now DelegatingSubclassHandler declares save() and purge() methods, which have a default implementation in BaseDelegatingSubclassHandler. Is that a good enough solution?

 

-Darius

 

On Thu, Apr 26, 2012 at 12:50 PM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

So I have been trying to figure out if there is any way to break out of this back and forth.  I have decided that what I really want to see is subclass resources and controllers that act as if they were full-fledged resources and preserve the subclass hierarchy but don't screw up caching.  So I am going to suggest the following:

 

(1) Subclass resources should be addressed like full resources, ws/<subclass>/... but the subclass controller should issue a 307 redirect to ws/<superclass>/...?t=<subclass>.  Subclasses can have child classes represented as sub-resources, and sub-resources can be created when a resource is created.  It is OK for subclass controllers to hand custom queries directly to their resource without passing through the superclass.

 

(2) The superclass should delegate to the subclass for representations, searches and saves.  If the named representation or search does not exist, my choice would be to ascend the class hierarchy.  I understand this might require some registration, but only to the parent.

 

(3) The superclass resource would have to implement the superclass representations, searches and saves.  It would also have to implement the delegation mechanism for subclasses.

 

(4) Subclass name will be returned as part of the standard representations.  Subclass name will not be needed for POST.

 

So given our Order -->  DrugOrder --> HIVDrugOrder class hierarchy

 

POST ws/order {body} creates an order which is filled from the body and validated and saved

POST ws/drugorder {body} is redirected to ws/order?t=drugorder {body} which delegates to the drugorder resource to create a drugorder which is filled from the body and delegated to the drugorder resource for validation and saving

POST ws/hivdrugorder {body} is redirected to ws/order?t=hivdrugorder {body} which delegates to the hivdrugorder resource to create an hivdrugorder which is filled from the body and delegated to the hivdrugorder resource for validation and saving.

 

POST ws/order/uuid {body} retrieves the order which is filled from the body and validated and saved

POST ws/drugorder/uuid {body} is redirected to ws/order?t=drugorder {body} which retrieves the order which is filled from the body and delegated to the drugorder resource for validation and saving

POST ws/hivdrugorder/uuid {body} is redirected to ws/order?t=hivdrugorder {body} which retrieves the order which is filled from the body and delegated to the hivdrugorder resource for validation and saving

 

GET ws/order/uuid?v=<rep> retrieves the order whose type is determined and delegated to the subclass for generation of the rep.  If the object with the uuid is not assignable from order or the rep does not exist, an error is thrown. 

GET ws/drugorder/uuid?v=<rep> is redirected to ws/order/uuid&v=<rep>?t=drugorder.  The search for the representation is done in the resoruce drugorder.

GET ws/hivdrugorder/uuid?v=<rep> is redirected to ws/order/uuid&v=<rep>?t=drugorder.  The search for the representation is done in the resource hivdrugorder.

 

GET ws/order&v=<rep> retrieves all orders; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named rep does not exist, an error is thrown.

GET ws/drugorder&v=<rep> is redirected to ws/order?v=rep&t=drugorder.  The superclass delegates to the class, which retrieves all drugorders; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named rep does not exist, an error is thrown.

GET ws/hivdrugorder&v=<rep> is redirected to ws/order?v=rep&t=hivdrugorder.  The superclass delegates to the class, which retrieves all hivdrugorders; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named rep does not exist, an error is thrown.

 

GET ws/order&<query>=<param> retrieves all orders satisfying the condition; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named query does not exist, an error is thrown.

GET ws/drugorder&<query>=<param> is redirected to ws/order?<query>=<param>&t=drugorder.  The superclass delegates to the subclass to retrieve all orders satisfying the condition; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named query does not exist, an error is thrown.

GET ws/hivdrugorder&<query>=<param> is redirected to ws/order?<query>=<param>&t=hivdrugorder.  The superclass delegates to the subclass to retrieve all orders satisfying the condition; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named query does not exist, an error is thrown.

 

DELETE is handled by the superclass.

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Darius Jazayeri
Sent: Thursday, April 26, 2012 11:34 AM


To: [hidden email]
Subject: Re: [OPENMRS-DEV] An Alternative Approach to Subclass Implementation

 

To be clear, the POSTs that Burke showed a couple of emails ago will work exactly as written, except of course that he simplified the actual properties into just "order". And the response to the GET that he showed will also be as he wrote it (except that uri is within links and the whole thing is in a PageableResults object). And the URIs he gave for the POSTs and GET are exactly right, there is no need for a ?t=type parameter.

 

Responses inline below.

 

-Darius

On Thu, Apr 26, 2012 at 7:14 AM, Burke Mamlin <[hidden email]> wrote:

On Thu, Apr 26, 2012 at 8:32 AM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

Burke --

     Regardless of whether it's Darius or me, each of the posts 1 and 3 would need a t=<type> parameter. 

     In my design, t is always virtual, it is the classname of the object; this avoids issues like having to use a global variable or predefined value or concept map to know the value of order type for drug order.  It also leaves order type available for distinctions that might be important to the facility without affecting the data model (say a narcotic drug order).  I'm not sure where Darius is at on this; at one point he required a discriminator field with a known name (registered?), but I think he has become more virtual.  The point is, you can't change the value of a discriminator field, so why include it in the POST body?  I do return it in the body on GET, as does Darius.

 

You can't change the concept of an order either, so would that move the URL as well?  I don't like the idea of putting some of the properties in the URL and others in the data.  You're right that you can't change the type of order; however, you must specify it when creating an order.

 

I'm also not thrilled about clients needing to know the Java class name for every order type.  There are not an infinite number of orderables.  Most implementations will be able to get by with two up front (drugs & tests).  Even for an enterprise-level inpatient system, you can get by with only a dozen or so (e.g., activities, precautions, diet, call orders, drugs, lab tests, radiology tests, procedures, nursing orders, referrals, education/handouts).  Actually, each of these basic types could be represented by a concept class and that class name could be used.

 

As I've coded it, the java class names are not exposed. To create a drug order you say ' type: "drugorder" '.

(@Roger, as coded you only use a &t=drugorder when you're doing a get-all or search, to say you only want objects of that type.)

 

 

     Also, Darius and I differ about privileges and validation at posts 1 and 3.  In Darius' design, both commands would require the privileges to create an order and would validate per order's requirements.  In my design, post 1 would require the privileges to create a drug order and would validate per drug order's requirements, while post 3 would require the privileges to create a referral order and would validate per referral order's requirements. 

 

There should be a top level privilege for ordering in general.  My assumption in posting the orders was that the REST document received would be passed to the order type-specific handler, so permissions & validation could be type-specific as you describe – i.e., we would have separate validators for each basic type of order.

 

The underlying Java API is built such that if you have Manage Orders privilege, you can do OrderService.saveOrder(anyOrderSubclass). My implementation does the actual save using this.

As coded, the type-specific-handler is only allowed to specify a "delegating resource description" which indicates what fields may be created/edited, it doesn't actually get a "save(T)" call. This is an oversight, due to the fact that none of our core resources actually do any custom save/validation logic that isn't routed through the resource description.

I can add a save(T) method on the subtype handlers, and let the base implementation of it just call the resource's save method.

 

 

    At get 4, both designs would use the default rep associated with the subclass of the object.  However, suppose you wanted to display all the types of order with just the order fields and some combination of subclass-specific fields in a details column.  In my design, you could define a named representation (say, COMPRESSED) that would be defined differently for different subclasses; you could even define it at a parent level (say drug order) and it could be used by children (like HIV drug order).  I don't believe you can do this in Darius' design.

 

Again, I think I agree with you here.  I was assuming that the generation of representation for each order would be delegated to it's type-specific handler, so if you asked for a FOOBAR representation of all the patient's orders, the API would iterate through the orders, asking for the representation for each from its type-specific handler.

 

@Darius – is that not what you're doing?  i.e., were you creating something that would not defer calls to type-specific handlers?

 

I do delegate to type-specific handlers. You can also define named representations on subclasses. What you cannot do is define a representation in the top-level resource, and get a subtype in that representation. E.g. if order defines a "foobar" rep, that is not inherited by drugorder. (Though drugorder could of course define that named representation also, as a one-liner that calls the parent resource.)

 

 

    With Darius, at step 0 (before the drug order is built in), you have to create a resource and controller for the order superclass; you have to create a handler, and a controller if you want custom searches, for drug order.  The superclass resource will contain all the methods of the current design plus the non-final declaration.

 

Yes.  The idea is that drug orders are provided for you.  So "you" will not have to do these steps.

 

  Darius depends on each resource and handler having the variable lists created by RESTWS-226, which I do not.  The drug order handler will contain its registration plus most of the methods of a resource (no save, delete or purge).  At step 2, you would need to build another handler and possibly a controller.

    With me, at step 0 you have to create a resource and controller for the hierarchy, a resource and controller for the superclass (order), and a resource and controller for the subclass (drug order).  The controllers are one-liners unless you want to add custom searches; they are just like the current design.  The hierarchy resource could also be a one-liner, with the actual delegations taking place in an abstract class using generics.  The resource for the superclass and the subclass would look just like the current design.  At step 2, you would create another controller and resource for referral order and that would also look just like the current design.

 

If you're creating a separate resource for each type, then doesn't that imply a different URL for each order type?

 

     In our discussion, Darius and I identified some situations (as I recall relating to validation) where it would be necessary under his design to override the supplied core resource with a user-defined resource that extended the core resource.  This could not be done independently for more than one subclass, and would require a custom build.  This would not be necessary with my design.

 

Can you provide an concrete example?  I'm presuming each order type has an associated handler/validator.

 

-Burke

 

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Burke Mamlin
Sent: Wednesday, April 25, 2012 5:08 PM


To: [hidden email]
Subject: Re: [OPENMRS-DEV] An Alternative Approach to Subclass Implementation

 

Ok, so if I want to create a drug order and a referral order for patient with uuid 123-456, then request all orders for the patient, it would look something like this (simplifying the order details for a simpler example)?

 

1. Create a drug order for my patient; they're built-in:

POST order

{ "patient":"123-456", "type":"drugorder", "order":"penicillin 500 mg orally twice daily for 2 weeks" }

 

2. Install a module to add support for referral orders:

Install a referrals module that adds a referralorder type of order.  Presumably, by introducing a new concept class for referral order, along with a handler for the new order type and, of course, a ReferralOrder class that extends Order.

 

3. Create a referral order for my patient:

POST order

{ "patient":"123-456", "type":"referralorder", "order":"cardiology referral please evaluate for rheumatic heart disease" }

 

4. Find all the active orders for my patient:

GET order?patient=123-456

[

{ "patient":"123-456", "type":"drugorder", "order":"penicillin 500 mg orally twice daily for 2 weeks", "uri":"…" },

{ "patient":"123-456", "type":"referralorder", "order":"cardiology referral please evaluate for rheumatic heart disease", "uri":"…" }

]

 

Is that close?

 

-Burke

 

On Tue, Apr 24, 2012 at 9:21 PM, Darius Jazayeri <[hidden email]> wrote:

On RESTWS-243 I've attached a patch that contains my implementation that I'm now ready to commit. (It's what Roger was responding to in his email.)

 

I'm going to hold off on committing until after tomorrow's design call in case we have time to discuss this a bit.

 

Basically what I've done is allow resources to indicate that they support subclasses, and if they do, they'll have all appropriate DelegatingSubclassHandlers registered with them via spring.

 

The idea is that we're hiding subclasses from the consumer of the REST API. As far as they're concerned there is a single "order" resource, which has documents of different types (e.g. "order", "drugorder", etc.) Behind the scenes each type is a subclass, but in REST that's not visible, rather they're exposed as different document types. 

 

DelegatingSubclassHandler encapsulates the methods subclasses need to specify for their owning resource.

 

Here is the functionality that my approach supports:

 

GET order

  • gets all orders regardless of type
  • each document in the list may have different types
  • the top-level resource does the db query and delegates to the appropriate handler to convert each result

GET order?t=drugorder

  • gets all orders whose type is drugorder
  • the subclass handler does the db query
  • In my implementation GET order?t=order (i.e. get only things that are order stubs) is not allowed

GET order?patient=<uuid>

  • gets all orders for the given patient
  • each document in the list may have different types

GET order?patient=<uuid>&t=drugorder

  • gets all orders for the given patient whose type is drugorder
  • if the subclass handler defines a suitable method, we use it, otherwise the top-level resource queries by patient, and manually filters out by type

GET order/<uuid>

  • returns a specific order
  • representations are defined by the type, e.g. { "type": "drugorder", "dose": "100", ... }

POST order

  • creates a new order
  • you must specify the type in the content, e.g. { "type": "drugorder", "dose": "100", ... }
  • allowed properties are defined by the type

POST order/<uuid>

  • modifies an existing order
  • you are not allowed to change the type
  • allowed properties are defined by the type

DELETE order/<uuid>

  • voids an order
  • handled by the top-level resource, not the subclass handler

DELETE order/<uuid>?purge=true

  • purges an order
  • handled by the top-level resource, not the subclass handler

@Roger, can you please phrase your counterproposal in terms of the additional functionality that you want to expose beyond these methods?

 

-Darius

 

On Mon, Apr 23, 2012 at 9:54 AM, Burke Mamlin <[hidden email]> wrote:

Roger,

 

Could you provide some concrete examples?

 

-Burke

 

On Mon, Apr 23, 2012 at 8:07 AM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

AN ALTERNATIVE APPROACH TO SUBCLASS IMPLEMENTATION

In this approach, subclass resources remain resources, just are mapped via the superclass. Substantially all operations are delegated to the appropriate subclass resource. This is motivated by simplicity, validation and representation considerations. It is also motivated by a privilege issue I just realized – the people authorized to create/update a lab order are likely different from those allowed to create/update a drug order. I have assumed that the privilege of updating a parent class field in subclass record requires only parent class privileges

I don't believe this alternative requires much in the way of changes to existing code other than mappings; adding the controller and resource for each superclass; some changes in asRepresentation where it finds the right representation to use; and some changes where updates are going on to allow the possibility that the fields to be looked for in the request body may come from an object which is a parent class of the object being updated. It eliminates the need for the two variable lists used by Darius (although they could still be used if it was thought they added value). I still think it is a good idea to treat ActiveLists as an abstract class so that the AllergyList and the ProblemList appear to be two different superclasses.

All members of a class hierarchy request a mapping to /<superclass>?t=<subclass>. The superclass also requests a mapping to /<superclass>?!t. I have tried to make all the calls of /<superclass>?!t work the same as calls to /<superclass>?t=<superclass>, if I have missed please let me know.  It might be worthwhile to extend Resource to SuperclassResource to make the coding of the type-free calls easier.  All representations include a t virtual field which contains the actual subclass of the object being represented.

1. GET <superclass>?t=<subclass>&v=<rep>, GET <superclass>?!t

Purpose is to get all records of a particular subclass in a formatted representation. Spring routes this to the subclass controller. The subclass resource uses getAll to get a (polymorphic) list of objects. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

2. GET <superclass>?t=<subclass>&v=<rep>&q=<search param>, GET <superclass>?!t&v=<rep>&q=<search param>

Purpose is to use the standard query for a subclass to get a formatted representation. Spring routes this to the subclass controller. The subclass resource uses doSearch to get a (polymorphic) list of objects. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

3. GET <superclass>?t=<subclass>&v=<rep>&<custom search>=<search param>

Purpose is to do a custom search that is defined at some level of the class hierarchy.  Spring routes this to the subclass controller. The subclass controller creates a resource for its type and delegates the search to it. The resource uses service methods to do the search to produce a (polymorphic) list of objects. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

4. GET <superclass>/<uuid>?!t&v=<rep>

Purpose is to get all records of a particular subclass in a formatted representation. Spring routes this to the superclass controller. The superclass resource uses getByUuid to get an object. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

5. GET <superclass>/<uuid>?t=<subclass>&v=<rep>

Purpose is to use a representation from a parent class. Like 5, except after getting by Uuid, the subclass resource coerces the result to be its type. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the supertype has been searched, at which point a rep does not exist error is thrown.

6. POST <superclass>?t=<subclass> {body}

Purpose is to create a new object of type <subclass>. The subclass resource creates a new object whose fields are extracted from the body (no need to know. The subclass resource save method uses services method to save the object; validation takes place in the save method.

7. POST <superclass>/<uuid>?!t {body}

Purpose is to update an object of any subclass. The type is determined and the operation is delegated to the resource of the actual type.

8. POST <superclass/<uuid>?t=<subclass> {body}

Purpose is to update an object using only parent-level fields. This is to avoid additional privileges at the actual subclass level. The type is deermined and the operation is delegated to the resource of the declared type. Throws an error if the actual type is not a subclass of the type declared.

9. DELETE <superclass>/<uuid>?!t&!purge, DELETE <superclass>/<uuid>?t=<subclass>&!purge

Purpose is to delete an object of any subclass. The type is determined and the operation is delegated to the resource of the actual type. The subclass, if present, must match the actual type of the resource.

10. DELETE <superclass>/<uuid>?purge&!t, DELETE <superclass>/<uuid>?purge

Purpose is to purge an object of any subclass. The type is determine and the operation is delegated to the resource of the actual type. The subclass, if present, must match the actual type of the resource.


[hidden email] from OpenMRS Developers' mailing list



[hidden email] from OpenMRS Developers' mailing list

[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list
Burke Mamlin Burke Mamlin
Reply | Threaded
Open this post in threaded view
|

Re: Sateesh's comment on encounter creation

Sateesh,

The clinical transaction that you describe is the OpenMRS "encounter" (aka encounter transaction) – i.e., the doctor creates a series of zero-to-n observations, orders, notes, and/or form data within a single session, which we call an "encounter."  As orders are added to encounters along with observations, we should be able to record this transaction in a single REST call (i.e., a single POST of an encounter).

-Burke 

On Sat, Apr 28, 2012 at 12:45 PM, Sateesh Babu <[hidden email]> wrote:
An example scenario is as under:
1) Doctor retrieves the patient record
2) Updates the record with some new observations and also creates an encounter
3) Prescribes drugs for the health issue
4) Closes the record

At step 4, the expectation is that the patient record is persisted to the server backend. Steps 2,3 happens in the same webpage and for the doctor it is one single transaction. Meaning, the doctor expects complete consistency of all the data starting from steps 1-4. But if I understood correctly from the current REST API, step 2 would involve about three REST calls and step 3 would involve one more REST call. In total 4 REST calls.

So going back to my earlier comment, that if any of these calls fails for whatever reason, the data is left in an inconsistent state. And for the end-user (doctor here), it becomes a irritating factor. The reason I want to stress on this point is, the doctors in India are quite used to using paper and pen for all the encounters and digitisation happens offline (if at all if). If we have to make the doctors use the system, ensuring a consistent behaviour is very important.

The way I see the REST API is designed was around the data model and not around the transaction model (I could be completely wrong here, please correct if that is the case). Transaction model looks more about the end usecases. Had it not been REST, but another transactional protocol, like RMI, then it would be perfectly fine, since all the 3-4 individual transactions could still be wound over one single transaction call.

--
Sateesh


On Sat, Apr 28, 2012 at 9:02 PM, Darius Jazayeri <[hidden email]> wrote:

Can you give a concrete example of what you're talking about, including what the response would look like?

Generally speaking, we allow you to create one resource at a time because the semantics of rest support that: you do a post to the resource URI and you get back the resource created as a response document. (also, any resource should only be created/edited via one URI, to support web-standard caching)

-Darius (by phone)

On Apr 28, 2012 2:23 AM, "Sateesh Babu" <[hidden email]> wrote:
I took the example of an encounter, but this question is valid for any REST API. I would feel nervous if I have to split my single transaction into multiple REST queries (the burden is more on the REST API user). Why not have a single, simple REST call for the user and allow the heavy lifting to happen on the server side?

--
Sateesh

On Fri, Apr 27, 2012 at 9:32 PM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

I believe that Darius, Rafal and I are pretty much in agreement that sub-resources should be creatable from resources, but that full resources should be created separately and then either used as a uuid reference or added explicitly to a multi-object collection.  Since there's no a priori way to know whether a link has been put in the record itself, in a mapping object that's also a (sub-)resource, or a mapping object that's only a mapping object, the REST machinery has difficulty in figuring out what to do; we may need to have add/remove member service or pojo methods to handle collections.  Rafal found that bulk replacement of collection members produces Hibernate errors.

 

My idea in creating the page https://wiki.openmrs.org/display/docs/Standard_Modeling+of+Resource+Relationship+Types was to document the types of relationship we have in OpenMRS and how those relationships should be modeled in REST.  It still needs input.

 

In the case of encounter, the answer to Sateesh's comment depends on whether there can be encounterless obs.  If there are no encounterless obs, then obs should be a sub-resource of encounter and thus creatable.  If there are encounterless obs, then obs could be a full resource (as it is now) and the encounter and the obs should be created separately and then the obs added to the encounter.   However, obs could be a sub-resource of patient (there are no patientless obs), in which case you could create obs while creating a patient.

 

Further, every encounter has an encounter type.  Encounter type is a resource, so it would have to be created separately and then used within the encounter being created.  Likewise, you couldn't create a location or a provider/user while creating the encounter.

 

If there were a way to inform the REST machinery what kind of a linkage exists between two resources, then it might be possible to weaken these constraints.  But I don't think we'd ever want a complete free-for-all; the idea of creating a new encounter type or location on the fly seems pretty dubious to me.

 

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Burke Mamlin
Sent: Friday, April 27, 2012 9:49 AM
To: [hidden email]
Subject: Re: [OPENMRS-DEV] An Alternative Approach to Subclass Implementation

 

Good point, Sateesh.  When creating an encounter, you could be able to create all of the associated data as well within a single call; in fact, this should be the preferred method for creating an encounter.  This would include observations (obs) now, include orders as they are added to REST, and eventually include notes & form data once those are fully realized.

 

Cheers,

 

-Burke

On Fri, Apr 27, 2012 at 4:23 AM, Sateesh Babu <[hidden email]> wrote:

One question (concern) here. For example, if a client has to create a patient encounter with drug and lab orders, the client has to potentially make 2-3 (or even more) REST calls.
What if the first REST call succeeds and the next two fail or a combination like pass-fail-pass, pass-pass-fail etc. The failure can be because of any reason ranging from network failure, data corruption, bugs etc. REST inherently being a stateless protocol would leave the data on the server in an inconsistent state.
Instead, why not send the complete data in the transaction in one single go. So for the same above example, one single data object could be created with the encounter + drug order + lab order. This way if the transaction fails, then atleast the data is left in a consistent state. If data size is a concern, we can always use compression. There could be a meta-data object in the beginning of the object to describe what the contents of the json object are. Something like:
element {
  encounter: {
                   ...
                  }
  drug-order: {
                   ...
                   }
  lab-order: {
                  ...
                }
}

--
Sateesh

 

On Fri, Apr 27, 2012 at 11:22 AM, Darius Jazayeri <[hidden email]> wrote:

Yeah, I coded it such that classes have to actively choose to make themselves subclassable, but it should only take a few lines of code to change that.

 

-Darius

 

On Thu, Apr 26, 2012 at 8:28 PM, Ben Wolfe <[hidden email]> wrote:

So I was in Rogers camp until this argument.  There were three types of subclassing that we defined.  The patient/person subclassing is a first class resource, so both are primary documents. The ProblemList/Allergy subclassing completely hide their parents, so they are primary documents. However, these Order subclasses are second class citizens of sorts. They depend on the parent Order resource.  I don't like introducing yet another *Handler class, but calling them a resource is mixing ideas.

 

I'd favor making every class subclassable by default.  I think you had reversed Darius.

 

Ben

 

On Thu, Apr 26, 2012 at 9:42 PM, Darius Jazayeri <[hidden email]> wrote:

Hi Roger,

 

So, as you can probably imagine, I disagree. :-)

 

I think the whole point is that it's not appropriate to expose subclasses as if they were resources. I'm explicitly saying they're not resources, and that consumers of the REST API should not think of them as resources, but rather should think "the order resource tells me about orders, and drug orders are one type of order".

 

So I don't think that exposing a drugorder URI that redirects to order?t=drugorder is right. Also, using HTTP 307 (Temporary Redirect) means that the client has to make 2 requests to do anything (unless they cheat by going straight to the order resource).

 

What do others think?

 

I refactored my code a bit so now DelegatingSubclassHandler declares save() and purge() methods, which have a default implementation in BaseDelegatingSubclassHandler. Is that a good enough solution?

 

-Darius

 

On Thu, Apr 26, 2012 at 12:50 PM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

So I have been trying to figure out if there is any way to break out of this back and forth.  I have decided that what I really want to see is subclass resources and controllers that act as if they were full-fledged resources and preserve the subclass hierarchy but don't screw up caching.  So I am going to suggest the following:

 

(1) Subclass resources should be addressed like full resources, ws/<subclass>/... but the subclass controller should issue a 307 redirect to ws/<superclass>/...?t=<subclass>.  Subclasses can have child classes represented as sub-resources, and sub-resources can be created when a resource is created.  It is OK for subclass controllers to hand custom queries directly to their resource without passing through the superclass.

 

(2) The superclass should delegate to the subclass for representations, searches and saves.  If the named representation or search does not exist, my choice would be to ascend the class hierarchy.  I understand this might require some registration, but only to the parent.

 

(3) The superclass resource would have to implement the superclass representations, searches and saves.  It would also have to implement the delegation mechanism for subclasses.

 

(4) Subclass name will be returned as part of the standard representations.  Subclass name will not be needed for POST.

 

So given our Order -->  DrugOrder --> HIVDrugOrder class hierarchy

 

POST ws/order {body} creates an order which is filled from the body and validated and saved

POST ws/drugorder {body} is redirected to ws/order?t=drugorder {body} which delegates to the drugorder resource to create a drugorder which is filled from the body and delegated to the drugorder resource for validation and saving

POST ws/hivdrugorder {body} is redirected to ws/order?t=hivdrugorder {body} which delegates to the hivdrugorder resource to create an hivdrugorder which is filled from the body and delegated to the hivdrugorder resource for validation and saving.

 

POST ws/order/uuid {body} retrieves the order which is filled from the body and validated and saved

POST ws/drugorder/uuid {body} is redirected to ws/order?t=drugorder {body} which retrieves the order which is filled from the body and delegated to the drugorder resource for validation and saving

POST ws/hivdrugorder/uuid {body} is redirected to ws/order?t=hivdrugorder {body} which retrieves the order which is filled from the body and delegated to the hivdrugorder resource for validation and saving

 

GET ws/order/uuid?v=<rep> retrieves the order whose type is determined and delegated to the subclass for generation of the rep.  If the object with the uuid is not assignable from order or the rep does not exist, an error is thrown. 

GET ws/drugorder/uuid?v=<rep> is redirected to ws/order/uuid&v=<rep>?t=drugorder.  The search for the representation is done in the resoruce drugorder.

GET ws/hivdrugorder/uuid?v=<rep> is redirected to ws/order/uuid&v=<rep>?t=drugorder.  The search for the representation is done in the resource hivdrugorder.

 

GET ws/order&v=<rep> retrieves all orders; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named rep does not exist, an error is thrown.

GET ws/drugorder&v=<rep> is redirected to ws/order?v=rep&t=drugorder.  The superclass delegates to the class, which retrieves all drugorders; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named rep does not exist, an error is thrown.

GET ws/hivdrugorder&v=<rep> is redirected to ws/order?v=rep&t=hivdrugorder.  The superclass delegates to the class, which retrieves all hivdrugorders; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named rep does not exist, an error is thrown.

 

GET ws/order&<query>=<param> retrieves all orders satisfying the condition; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named query does not exist, an error is thrown.

GET ws/drugorder&<query>=<param> is redirected to ws/order?<query>=<param>&t=drugorder.  The superclass delegates to the subclass to retrieve all orders satisfying the condition; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named query does not exist, an error is thrown.

GET ws/hivdrugorder&<query>=<param> is redirected to ws/order?<query>=<param>&t=hivdrugorder.  The superclass delegates to the subclass to retrieve all orders satisfying the condition; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named query does not exist, an error is thrown.

 

DELETE is handled by the superclass.

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Darius Jazayeri
Sent: Thursday, April 26, 2012 11:34 AM


To: [hidden email]
Subject: Re: [OPENMRS-DEV] An Alternative Approach to Subclass Implementation

 

To be clear, the POSTs that Burke showed a couple of emails ago will work exactly as written, except of course that he simplified the actual properties into just "order". And the response to the GET that he showed will also be as he wrote it (except that uri is within links and the whole thing is in a PageableResults object). And the URIs he gave for the POSTs and GET are exactly right, there is no need for a ?t=type parameter.

 

Responses inline below.

 

-Darius

On Thu, Apr 26, 2012 at 7:14 AM, Burke Mamlin <[hidden email]> wrote:

On Thu, Apr 26, 2012 at 8:32 AM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

Burke --

     Regardless of whether it's Darius or me, each of the posts 1 and 3 would need a t=<type> parameter. 

     In my design, t is always virtual, it is the classname of the object; this avoids issues like having to use a global variable or predefined value or concept map to know the value of order type for drug order.  It also leaves order type available for distinctions that might be important to the facility without affecting the data model (say a narcotic drug order).  I'm not sure where Darius is at on this; at one point he required a discriminator field with a known name (registered?), but I think he has become more virtual.  The point is, you can't change the value of a discriminator field, so why include it in the POST body?  I do return it in the body on GET, as does Darius.

 

You can't change the concept of an order either, so would that move the URL as well?  I don't like the idea of putting some of the properties in the URL and others in the data.  You're right that you can't change the type of order; however, you must specify it when creating an order.

 

I'm also not thrilled about clients needing to know the Java class name for every order type.  There are not an infinite number of orderables.  Most implementations will be able to get by with two up front (drugs & tests).  Even for an enterprise-level inpatient system, you can get by with only a dozen or so (e.g., activities, precautions, diet, call orders, drugs, lab tests, radiology tests, procedures, nursing orders, referrals, education/handouts).  Actually, each of these basic types could be represented by a concept class and that class name could be used.

 

As I've coded it, the java class names are not exposed. To create a drug order you say ' type: "drugorder" '.

(@Roger, as coded you only use a &t=drugorder when you're doing a get-all or search, to say you only want objects of that type.)

 

 

     Also, Darius and I differ about privileges and validation at posts 1 and 3.  In Darius' design, both commands would require the privileges to create an order and would validate per order's requirements.  In my design, post 1 would require the privileges to create a drug order and would validate per drug order's requirements, while post 3 would require the privileges to create a referral order and would validate per referral order's requirements. 

 

There should be a top level privilege for ordering in general.  My assumption in posting the orders was that the REST document received would be passed to the order type-specific handler, so permissions & validation could be type-specific as you describe – i.e., we would have separate validators for each basic type of order.

 

The underlying Java API is built such that if you have Manage Orders privilege, you can do OrderService.saveOrder(anyOrderSubclass). My implementation does the actual save using this.

As coded, the type-specific-handler is only allowed to specify a "delegating resource description" which indicates what fields may be created/edited, it doesn't actually get a "save(T)" call. This is an oversight, due to the fact that none of our core resources actually do any custom save/validation logic that isn't routed through the resource description.

I can add a save(T) method on the subtype handlers, and let the base implementation of it just call the resource's save method.

 

 

    At get 4, both designs would use the default rep associated with the subclass of the object.  However, suppose you wanted to display all the types of order with just the order fields and some combination of subclass-specific fields in a details column.  In my design, you could define a named representation (say, COMPRESSED) that would be defined differently for different subclasses; you could even define it at a parent level (say drug order) and it could be used by children (like HIV drug order).  I don't believe you can do this in Darius' design.

 

Again, I think I agree with you here.  I was assuming that the generation of representation for each order would be delegated to it's type-specific handler, so if you asked for a FOOBAR representation of all the patient's orders, the API would iterate through the orders, asking for the representation for each from its type-specific handler.

 

@Darius – is that not what you're doing?  i.e., were you creating something that would not defer calls to type-specific handlers?

 

I do delegate to type-specific handlers. You can also define named representations on subclasses. What you cannot do is define a representation in the top-level resource, and get a subtype in that representation. E.g. if order defines a "foobar" rep, that is not inherited by drugorder. (Though drugorder could of course define that named representation also, as a one-liner that calls the parent resource.)

 

 

    With Darius, at step 0 (before the drug order is built in), you have to create a resource and controller for the order superclass; you have to create a handler, and a controller if you want custom searches, for drug order.  The superclass resource will contain all the methods of the current design plus the non-final declaration.

 

Yes.  The idea is that drug orders are provided for you.  So "you" will not have to do these steps.

 

  Darius depends on each resource and handler having the variable lists created by RESTWS-226, which I do not.  The drug order handler will contain its registration plus most of the methods of a resource (no save, delete or purge).  At step 2, you would need to build another handler and possibly a controller.

    With me, at step 0 you have to create a resource and controller for the hierarchy, a resource and controller for the superclass (order), and a resource and controller for the subclass (drug order).  The controllers are one-liners unless you want to add custom searches; they are just like the current design.  The hierarchy resource could also be a one-liner, with the actual delegations taking place in an abstract class using generics.  The resource for the superclass and the subclass would look just like the current design.  At step 2, you would create another controller and resource for referral order and that would also look just like the current design.

 

If you're creating a separate resource for each type, then doesn't that imply a different URL for each order type?

 

     In our discussion, Darius and I identified some situations (as I recall relating to validation) where it would be necessary under his design to override the supplied core resource with a user-defined resource that extended the core resource.  This could not be done independently for more than one subclass, and would require a custom build.  This would not be necessary with my design.

 

Can you provide an concrete example?  I'm presuming each order type has an associated handler/validator.

 

-Burke

 

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Burke Mamlin
Sent: Wednesday, April 25, 2012 5:08 PM


To: [hidden email]
Subject: Re: [OPENMRS-DEV] An Alternative Approach to Subclass Implementation

 

Ok, so if I want to create a drug order and a referral order for patient with uuid 123-456, then request all orders for the patient, it would look something like this (simplifying the order details for a simpler example)?

 

1. Create a drug order for my patient; they're built-in:

POST order

{ "patient":"123-456", "type":"drugorder", "order":"penicillin 500 mg orally twice daily for 2 weeks" }

 

2. Install a module to add support for referral orders:

Install a referrals module that adds a referralorder type of order.  Presumably, by introducing a new concept class for referral order, along with a handler for the new order type and, of course, a ReferralOrder class that extends Order.

 

3. Create a referral order for my patient:

POST order

{ "patient":"123-456", "type":"referralorder", "order":"cardiology referral please evaluate for rheumatic heart disease" }

 

4. Find all the active orders for my patient:

GET order?patient=123-456

[

{ "patient":"123-456", "type":"drugorder", "order":"penicillin 500 mg orally twice daily for 2 weeks", "uri":"…" },

{ "patient":"123-456", "type":"referralorder", "order":"cardiology referral please evaluate for rheumatic heart disease", "uri":"…" }

]

 

Is that close?

 

-Burke

 

On Tue, Apr 24, 2012 at 9:21 PM, Darius Jazayeri <[hidden email]> wrote:

On RESTWS-243 I've attached a patch that contains my implementation that I'm now ready to commit. (It's what Roger was responding to in his email.)

 

I'm going to hold off on committing until after tomorrow's design call in case we have time to discuss this a bit.

 

Basically what I've done is allow resources to indicate that they support subclasses, and if they do, they'll have all appropriate DelegatingSubclassHandlers registered with them via spring.

 

The idea is that we're hiding subclasses from the consumer of the REST API. As far as they're concerned there is a single "order" resource, which has documents of different types (e.g. "order", "drugorder", etc.) Behind the scenes each type is a subclass, but in REST that's not visible, rather they're exposed as different document types. 

 

DelegatingSubclassHandler encapsulates the methods subclasses need to specify for their owning resource.

 

Here is the functionality that my approach supports:

 

GET order

  • gets all orders regardless of type
  • each document in the list may have different types
  • the top-level resource does the db query and delegates to the appropriate handler to convert each result

GET order?t=drugorder

  • gets all orders whose type is drugorder
  • the subclass handler does the db query
  • In my implementation GET order?t=order (i.e. get only things that are order stubs) is not allowed

GET order?patient=<uuid>

  • gets all orders for the given patient
  • each document in the list may have different types

GET order?patient=<uuid>&t=drugorder

  • gets all orders for the given patient whose type is drugorder
  • if the subclass handler defines a suitable method, we use it, otherwise the top-level resource queries by patient, and manually filters out by type

GET order/<uuid>

  • returns a specific order
  • representations are defined by the type, e.g. { "type": "drugorder", "dose": "100", ... }

POST order

  • creates a new order
  • you must specify the type in the content, e.g. { "type": "drugorder", "dose": "100", ... }
  • allowed properties are defined by the type

POST order/<uuid>

  • modifies an existing order
  • you are not allowed to change the type
  • allowed properties are defined by the type

DELETE order/<uuid>

  • voids an order
  • handled by the top-level resource, not the subclass handler

DELETE order/<uuid>?purge=true

  • purges an order
  • handled by the top-level resource, not the subclass handler

@Roger, can you please phrase your counterproposal in terms of the additional functionality that you want to expose beyond these methods?

 

-Darius

 

On Mon, Apr 23, 2012 at 9:54 AM, Burke Mamlin <[hidden email]> wrote:

Roger,

 

Could you provide some concrete examples?

 

-Burke

 

On Mon, Apr 23, 2012 at 8:07 AM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

AN ALTERNATIVE APPROACH TO SUBCLASS IMPLEMENTATION

In this approach, subclass resources remain resources, just are mapped via the superclass. Substantially all operations are delegated to the appropriate subclass resource. This is motivated by simplicity, validation and representation considerations. It is also motivated by a privilege issue I just realized – the people authorized to create/update a lab order are likely different from those allowed to create/update a drug order. I have assumed that the privilege of updating a parent class field in subclass record requires only parent class privileges

I don't believe this alternative requires much in the way of changes to existing code other than mappings; adding the controller and resource for each superclass; some changes in asRepresentation where it finds the right representation to use; and some changes where updates are going on to allow the possibility that the fields to be looked for in the request body may come from an object which is a parent class of the object being updated. It eliminates the need for the two variable lists used by Darius (although they could still be used if it was thought they added value). I still think it is a good idea to treat ActiveLists as an abstract class so that the AllergyList and the ProblemList appear to be two different superclasses.

All members of a class hierarchy request a mapping to /<superclass>?t=<subclass>. The superclass also requests a mapping to /<superclass>?!t. I have tried to make all the calls of /<superclass>?!t work the same as calls to /<superclass>?t=<superclass>, if I have missed please let me know.  It might be worthwhile to extend Resource to SuperclassResource to make the coding of the type-free calls easier.  All representations include a t virtual field which contains the actual subclass of the object being represented.

1. GET <superclass>?t=<subclass>&v=<rep>, GET <superclass>?!t

Purpose is to get all records of a particular subclass in a formatted representation. Spring routes this to the subclass controller. The subclass resource uses getAll to get a (polymorphic) list of objects. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

2. GET <superclass>?t=<subclass>&v=<rep>&q=<search param>, GET <superclass>?!t&v=<rep>&q=<search param>

Purpose is to use the standard query for a subclass to get a formatted representation. Spring routes this to the subclass controller. The subclass resource uses doSearch to get a (polymorphic) list of objects. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

3. GET <superclass>?t=<subclass>&v=<rep>&<custom search>=<search param>

Purpose is to do a custom search that is defined at some level of the class hierarchy.  Spring routes this to the subclass controller. The subclass controller creates a resource for its type and delegates the search to it. The resource uses service methods to do the search to produce a (polymorphic) list of objects. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

4. GET <superclass>/<uuid>?!t&v=<rep>

Purpose is to get all records of a particular subclass in a formatted representation. Spring routes this to the superclass controller. The superclass resource uses getByUuid to get an object. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

5. GET <superclass>/<uuid>?t=<subclass>&v=<rep>

Purpose is to use a representation from a parent class. Like 5, except after getting by Uuid, the subclass resource coerces the result to be its type. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the supertype has been searched, at which point a rep does not exist error is thrown.

6. POST <superclass>?t=<subclass> {body}

Purpose is to create a new object of type <subclass>. The subclass resource creates a new object whose fields are extracted from the body (no need to know. The subclass resource save method uses services method to save the object; validation takes place in the save method.

7. POST <superclass>/<uuid>?!t {body}

Purpose is to update an object of any subclass. The type is determined and the operation is delegated to the resource of the actual type.

8. POST <superclass/<uuid>?t=<subclass> {body}

Purpose is to update an object using only parent-level fields. This is to avoid additional privileges at the actual subclass level. The type is deermined and the operation is delegated to the resource of the declared type. Throws an error if the actual type is not a subclass of the type declared.

9. DELETE <superclass>/<uuid>?!t&!purge, DELETE <superclass>/<uuid>?t=<subclass>&!purge

Purpose is to delete an object of any subclass. The type is determined and the operation is delegated to the resource of the actual type. The subclass, if present, must match the actual type of the resource.

10. DELETE <superclass>/<uuid>?purge&!t, DELETE <superclass>/<uuid>?purge

Purpose is to purge an object of any subclass. The type is determine and the operation is delegated to the resource of the actual type. The subclass, if present, must match the actual type of the resource.


[hidden email] from OpenMRS Developers' mailing list



[hidden email] from OpenMRS Developers' mailing list

[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list
Friedman, Roger (CDC/CGH/DGHA) (CTR) Friedman, Roger (CDC/CGH/DGHA) (CTR)
Reply | Threaded
Open this post in threaded view
|

Re: Sateesh's comment on encounter creation

The problem is that not only must records be created for each OpenMRS object, but also they must be linked to each other.  In the case of a subresource, we know that the resource object is connected to the subresource object by a link in the subresource object.  But in the case of two resources, there are a variety of possible connections.

 

I think we could have a rule that any resource (e.g. patient) that has a collection property containing another resource (e.g. encounter) wherein the second resource object has a link to the first resource object is creatable.  From a programming point of view, the situation looks just like the subresource situation.  However, as Darius notes, caches of the contained resource may not work correctly.  And it would enable some weird-seeming situations, like creating encounters while creating users/providers, or creating an obs that creates an encounter that creates other obs.

 

But where there are self-joins or mapping tables, we can’t create on the fly without some way for the resource to inform the REST machinery as to how the two objects are to be linked.  So we couldn’t create an entire location hierarchy at one fell swoop.

 

My question is, just how heavyweight are our REST calls?  What is the time difference between 1 huge, complex transaction and 101 small transactions?  We have somewhat pushed to the side the question of what happens when the client has to iterate through a collection to GET related data in order to select which collection elements will be displayed.  If there is a problem in the create scenario, then there will be a problem in the select scenario as well.

 

From my recent work on REST, I have seen that the way in which objects are handled varies a lot.  Most of this variance appears to come from people predicting how a particular user would want an object to work.  These predictions are not written down, they only exist in code, which they make hard to maintain.  I think that, as a framework, the core code should emphasize standardized methods and behavior, with convenience methods available to handle particular use cases.   

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Burke Mamlin
Sent: Saturday, April 28, 2012 2:16 PM
To: [hidden email]
Subject: Re: [OPENMRS-DEV] Sateesh's comment on encounter creation

 

Sateesh,

 

The clinical transaction that you describe is the OpenMRS "encounter" (aka encounter transaction) – i.e., the doctor creates a series of zero-to-n observations, orders, notes, and/or form data within a single session, which we call an "encounter."  As orders are added to encounters along with observations, we should be able to record this transaction in a single REST call (i.e., a single POST of an encounter).

 

-Burke 

On Sat, Apr 28, 2012 at 12:45 PM, Sateesh Babu <[hidden email]> wrote:

An example scenario is as under:
1) Doctor retrieves the patient record
2) Updates the record with some new observations and also creates an encounter
3) Prescribes drugs for the health issue
4) Closes the record

At step 4, the expectation is that the patient record is persisted to the server backend. Steps 2,3 happens in the same webpage and for the doctor it is one single transaction. Meaning, the doctor expects complete consistency of all the data starting from steps 1-4. But if I understood correctly from the current REST API, step 2 would involve about three REST calls and step 3 would involve one more REST call. In total 4 REST calls.

So going back to my earlier comment, that if any of these calls fails for whatever reason, the data is left in an inconsistent state. And for the end-user (doctor here), it becomes a irritating factor. The reason I want to stress on this point is, the doctors in India are quite used to using paper and pen for all the encounters and digitisation happens offline (if at all if). If we have to make the doctors use the system, ensuring a consistent behaviour is very important.

The way I see the REST API is designed was around the data model and not around the transaction model (I could be completely wrong here, please correct if that is the case). Transaction model looks more about the end usecases. Had it not been REST, but another transactional protocol, like RMI, then it would be perfectly fine, since all the 3-4 individual transactions could still be wound over one single transaction call.

--
Sateesh

 

On Sat, Apr 28, 2012 at 9:02 PM, Darius Jazayeri <[hidden email]> wrote:

Can you give a concrete example of what you're talking about, including what the response would look like?

Generally speaking, we allow you to create one resource at a time because the semantics of rest support that: you do a post to the resource URI and you get back the resource created as a response document. (also, any resource should only be created/edited via one URI, to support web-standard caching)

-Darius (by phone)

On Apr 28, 2012 2:23 AM, "Sateesh Babu" <[hidden email]> wrote:

I took the example of an encounter, but this question is valid for any REST API. I would feel nervous if I have to split my single transaction into multiple REST queries (the burden is more on the REST API user). Why not have a single, simple REST call for the user and allow the heavy lifting to happen on the server side?

--
Sateesh

On Fri, Apr 27, 2012 at 9:32 PM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

I believe that Darius, Rafal and I are pretty much in agreement that sub-resources should be creatable from resources, but that full resources should be created separately and then either used as a uuid reference or added explicitly to a multi-object collection.  Since there's no a priori way to know whether a link has been put in the record itself, in a mapping object that's also a (sub-)resource, or a mapping object that's only a mapping object, the REST machinery has difficulty in figuring out what to do; we may need to have add/remove member service or pojo methods to handle collections.  Rafal found that bulk replacement of collection members produces Hibernate errors.

 

My idea in creating the page <a href="https://wiki.openmrs.org/display/docs/Standard_Modeling&#43;of&#43;Resource&#43;Relationship&#43;Types" target="_blank"> https://wiki.openmrs.org/display/docs/Standard_Modeling+of+Resource+Relationship+Types was to document the types of relationship we have in OpenMRS and how those relationships should be modeled in REST.  It still needs input.

 

In the case of encounter, the answer to Sateesh's comment depends on whether there can be encounterless obs.  If there are no encounterless obs, then obs should be a sub-resource of encounter and thus creatable.  If there are encounterless obs, then obs could be a full resource (as it is now) and the encounter and the obs should be created separately and then the obs added to the encounter.   However, obs could be a sub-resource of patient (there are no patientless obs), in which case you could create obs while creating a patient.

 

Further, every encounter has an encounter type.  Encounter type is a resource, so it would have to be created separately and then used within the encounter being created.  Likewise, you couldn't create a location or a provider/user while creating the encounter.

 

If there were a way to inform the REST machinery what kind of a linkage exists between two resources, then it might be possible to weaken these constraints.  But I don't think we'd ever want a complete free-for-all; the idea of creating a new encounter type or location on the fly seems pretty dubious to me.

 

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Burke Mamlin
Sent: Friday, April 27, 2012 9:49 AM
To: [hidden email]
Subject: Re: [OPENMRS-DEV] An Alternative Approach to Subclass Implementation

 

Good point, Sateesh.  When creating an encounter, you could be able to create all of the associated data as well within a single call; in fact, this should be the preferred method for creating an encounter.  This would include observations (obs) now, include orders as they are added to REST, and eventually include notes & form data once those are fully realized.

 

Cheers,

 

-Burke

On Fri, Apr 27, 2012 at 4:23 AM, Sateesh Babu <[hidden email]> wrote:

One question (concern) here. For example, if a client has to create a patient encounter with drug and lab orders, the client has to potentially make 2-3 (or even more) REST calls.
What if the first REST call succeeds and the next two fail or a combination like pass-fail-pass, pass-pass-fail etc. The failure can be because of any reason ranging from network failure, data corruption, bugs etc. REST inherently being a stateless protocol would leave the data on the server in an inconsistent state.
Instead, why not send the complete data in the transaction in one single go. So for the same above example, one single data object could be created with the encounter + drug order + lab order. This way if the transaction fails, then atleast the data is left in a consistent state. If data size is a concern, we can always use compression. There could be a meta-data object in the beginning of the object to describe what the contents of the json object are. Something like:
element {
  encounter: {
                   ...
                  }
  drug-order: {
                   ...
                   }
  lab-order: {
                  ...
                }
}

--
Sateesh

 

On Fri, Apr 27, 2012 at 11:22 AM, Darius Jazayeri <[hidden email]> wrote:

Yeah, I coded it such that classes have to actively choose to make themselves subclassable, but it should only take a few lines of code to change that.

 

-Darius

 

On Thu, Apr 26, 2012 at 8:28 PM, Ben Wolfe <[hidden email]> wrote:

So I was in Rogers camp until this argument.  There were three types of subclassing that we defined.  The patient/person subclassing is a first class resource, so both are primary documents. The ProblemList/Allergy subclassing completely hide their parents, so they are primary documents. However, these Order subclasses are second class citizens of sorts. They depend on the parent Order resource.  I don't like introducing yet another *Handler class, but calling them a resource is mixing ideas.

 

I'd favor making every class subclassable by default.  I think you had reversed Darius.

 

Ben

 

On Thu, Apr 26, 2012 at 9:42 PM, Darius Jazayeri <[hidden email]> wrote:

Hi Roger,

 

So, as you can probably imagine, I disagree. :-)

 

I think the whole point is that it's not appropriate to expose subclasses as if they were resources. I'm explicitly saying they're not resources, and that consumers of the REST API should not think of them as resources, but rather should think "the order resource tells me about orders, and drug orders are one type of order".

 

So I don't think that exposing a drugorder URI that redirects to order?t=drugorder is right. Also, using HTTP 307 (Temporary Redirect) means that the client has to make 2 requests to do anything (unless they cheat by going straight to the order resource).

 

What do others think?

 

I refactored my code a bit so now DelegatingSubclassHandler declares save() and purge() methods, which have a default implementation in BaseDelegatingSubclassHandler. Is that a good enough solution?

 

-Darius

 

On Thu, Apr 26, 2012 at 12:50 PM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

So I have been trying to figure out if there is any way to break out of this back and forth.  I have decided that what I really want to see is subclass resources and controllers that act as if they were full-fledged resources and preserve the subclass hierarchy but don't screw up caching.  So I am going to suggest the following:

 

(1) Subclass resources should be addressed like full resources, ws/<subclass>/... but the subclass controller should issue a 307 redirect to ws/<superclass>/...?t=<subclass>.  Subclasses can have child classes represented as sub-resources, and sub-resources can be created when a resource is created.  It is OK for subclass controllers to hand custom queries directly to their resource without passing through the superclass.

 

(2) The superclass should delegate to the subclass for representations, searches and saves.  If the named representation or search does not exist, my choice would be to ascend the class hierarchy.  I understand this might require some registration, but only to the parent.

 

(3) The superclass resource would have to implement the superclass representations, searches and saves.  It would also have to implement the delegation mechanism for subclasses.

 

(4) Subclass name will be returned as part of the standard representations.  Subclass name will not be needed for POST.

 

So given our Order -->  DrugOrder --> HIVDrugOrder class hierarchy

 

POST ws/order {body} creates an order which is filled from the body and validated and saved

POST ws/drugorder {body} is redirected to ws/order?t=drugorder {body} which delegates to the drugorder resource to create a drugorder which is filled from the body and delegated to the drugorder resource for validation and saving

POST ws/hivdrugorder {body} is redirected to ws/order?t=hivdrugorder {body} which delegates to the hivdrugorder resource to create an hivdrugorder which is filled from the body and delegated to the hivdrugorder resource for validation and saving.

 

POST ws/order/uuid {body} retrieves the order which is filled from the body and validated and saved

POST ws/drugorder/uuid {body} is redirected to ws/order?t=drugorder {body} which retrieves the order which is filled from the body and delegated to the drugorder resource for validation and saving

POST ws/hivdrugorder/uuid {body} is redirected to ws/order?t=hivdrugorder {body} which retrieves the order which is filled from the body and delegated to the hivdrugorder resource for validation and saving

 

GET ws/order/uuid?v=<rep> retrieves the order whose type is determined and delegated to the subclass for generation of the rep.  If the object with the uuid is not assignable from order or the rep does not exist, an error is thrown. 

GET ws/drugorder/uuid?v=<rep> is redirected to ws/order/uuid&v=<rep>?t=drugorder.  The search for the representation is done in the resoruce drugorder.

GET ws/hivdrugorder/uuid?v=<rep> is redirected to ws/order/uuid&v=<rep>?t=drugorder.  The search for the representation is done in the resource hivdrugorder.

 

GET ws/order&v=<rep> retrieves all orders; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named rep does not exist, an error is thrown.

GET ws/drugorder&v=<rep> is redirected to ws/order?v=rep&t=drugorder.  The superclass delegates to the class, which retrieves all drugorders; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named rep does not exist, an error is thrown.

GET ws/hivdrugorder&v=<rep> is redirected to ws/order?v=rep&t=hivdrugorder.  The superclass delegates to the class, which retrieves all hivdrugorders; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named rep does not exist, an error is thrown.

 

GET ws/order&<query>=<param> retrieves all orders satisfying the condition; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named query does not exist, an error is thrown.

GET ws/drugorder&<query>=<param> is redirected to ws/order?<query>=<param>&t=drugorder.  The superclass delegates to the subclass to retrieve all orders satisfying the condition; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named query does not exist, an error is thrown.

GET ws/hivdrugorder&<query>=<param> is redirected to ws/order?<query>=<param>&t=hivdrugorder.  The superclass delegates to the subclass to retrieve all orders satisfying the condition; one at a time, their type is determined and delegated to the subclass for generation of the rep.  If the named query does not exist, an error is thrown.

 

DELETE is handled by the superclass.

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Darius Jazayeri
Sent: Thursday, April 26, 2012 11:34 AM


To: [hidden email]
Subject: Re: [OPENMRS-DEV] An Alternative Approach to Subclass Implementation

 

To be clear, the POSTs that Burke showed a couple of emails ago will work exactly as written, except of course that he simplified the actual properties into just "order". And the response to the GET that he showed will also be as he wrote it (except that uri is within links and the whole thing is in a PageableResults object). And the URIs he gave for the POSTs and GET are exactly right, there is no need for a ?t=type parameter.

 

Responses inline below.

 

-Darius

On Thu, Apr 26, 2012 at 7:14 AM, Burke Mamlin <[hidden email]> wrote:

On Thu, Apr 26, 2012 at 8:32 AM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

Burke --

     Regardless of whether it's Darius or me, each of the posts 1 and 3 would need a t=<type> parameter. 

     In my design, t is always virtual, it is the classname of the object; this avoids issues like having to use a global variable or predefined value or concept map to know the value of order type for drug order.  It also leaves order type available for distinctions that might be important to the facility without affecting the data model (say a narcotic drug order).  I'm not sure where Darius is at on this; at one point he required a discriminator field with a known name (registered?), but I think he has become more virtual.  The point is, you can't change the value of a discriminator field, so why include it in the POST body?  I do return it in the body on GET, as does Darius.

 

You can't change the concept of an order either, so would that move the URL as well?  I don't like the idea of putting some of the properties in the URL and others in the data.  You're right that you can't change the type of order; however, you must specify it when creating an order.

 

I'm also not thrilled about clients needing to know the Java class name for every order type.  There are not an infinite number of orderables.  Most implementations will be able to get by with two up front (drugs & tests).  Even for an enterprise-level inpatient system, you can get by with only a dozen or so (e.g., activities, precautions, diet, call orders, drugs, lab tests, radiology tests, procedures, nursing orders, referrals, education/handouts).  Actually, each of these basic types could be represented by a concept class and that class name could be used.

 

As I've coded it, the java class names are not exposed. To create a drug order you say ' type: "drugorder" '.

(@Roger, as coded you only use a &t=drugorder when you're doing a get-all or search, to say you only want objects of that type.)

 

 

     Also, Darius and I differ about privileges and validation at posts 1 and 3.  In Darius' design, both commands would require the privileges to create an order and would validate per order's requirements.  In my design, post 1 would require the privileges to create a drug order and would validate per drug order's requirements, while post 3 would require the privileges to create a referral order and would validate per referral order's requirements. 

 

There should be a top level privilege for ordering in general.  My assumption in posting the orders was that the REST document received would be passed to the order type-specific handler, so permissions & validation could be type-specific as you describe – i.e., we would have separate validators for each basic type of order.

 

The underlying Java API is built such that if you have Manage Orders privilege, you can do OrderService.saveOrder(anyOrderSubclass). My implementation does the actual save using this.

As coded, the type-specific-handler is only allowed to specify a "delegating resource description" which indicates what fields may be created/edited, it doesn't actually get a "save(T)" call. This is an oversight, due to the fact that none of our core resources actually do any custom save/validation logic that isn't routed through the resource description.

I can add a save(T) method on the subtype handlers, and let the base implementation of it just call the resource's save method.

 

 

    At get 4, both designs would use the default rep associated with the subclass of the object.  However, suppose you wanted to display all the types of order with just the order fields and some combination of subclass-specific fields in a details column.  In my design, you could define a named representation (say, COMPRESSED) that would be defined differently for different subclasses; you could even define it at a parent level (say drug order) and it could be used by children (like HIV drug order).  I don't believe you can do this in Darius' design.

 

Again, I think I agree with you here.  I was assuming that the generation of representation for each order would be delegated to it's type-specific handler, so if you asked for a FOOBAR representation of all the patient's orders, the API would iterate through the orders, asking for the representation for each from its type-specific handler.

 

@Darius – is that not what you're doing?  i.e., were you creating something that would not defer calls to type-specific handlers?

 

I do delegate to type-specific handlers. You can also define named representations on subclasses. What you cannot do is define a representation in the top-level resource, and get a subtype in that representation. E.g. if order defines a "foobar" rep, that is not inherited by drugorder. (Though drugorder could of course define that named representation also, as a one-liner that calls the parent resource.)

 

 

    With Darius, at step 0 (before the drug order is built in), you have to create a resource and controller for the order superclass; you have to create a handler, and a controller if you want custom searches, for drug order.  The superclass resource will contain all the methods of the current design plus the non-final declaration.

 

Yes.  The idea is that drug orders are provided for you.  So "you" will not have to do these steps.

 

  Darius depends on each resource and handler having the variable lists created by RESTWS-226, which I do not.  The drug order handler will contain its registration plus most of the methods of a resource (no save, delete or purge).  At step 2, you would need to build another handler and possibly a controller.

    With me, at step 0 you have to create a resource and controller for the hierarchy, a resource and controller for the superclass (order), and a resource and controller for the subclass (drug order).  The controllers are one-liners unless you want to add custom searches; they are just like the current design.  The hierarchy resource could also be a one-liner, with the actual delegations taking place in an abstract class using generics.  The resource for the superclass and the subclass would look just like the current design.  At step 2, you would create another controller and resource for referral order and that would also look just like the current design.

 

If you're creating a separate resource for each type, then doesn't that imply a different URL for each order type?

 

     In our discussion, Darius and I identified some situations (as I recall relating to validation) where it would be necessary under his design to override the supplied core resource with a user-defined resource that extended the core resource.  This could not be done independently for more than one subclass, and would require a custom build.  This would not be necessary with my design.

 

Can you provide an concrete example?  I'm presuming each order type has an associated handler/validator.

 

-Burke

 

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Burke Mamlin
Sent: Wednesday, April 25, 2012 5:08 PM


To: [hidden email]
Subject: Re: [OPENMRS-DEV] An Alternative Approach to Subclass Implementation

 

Ok, so if I want to create a drug order and a referral order for patient with uuid 123-456, then request all orders for the patient, it would look something like this (simplifying the order details for a simpler example)?

 

1. Create a drug order for my patient; they're built-in:

POST order

{ "patient":"123-456", "type":"drugorder", "order":"penicillin 500 mg orally twice daily for 2 weeks" }

 

2. Install a module to add support for referral orders:

Install a referrals module that adds a referralorder type of order.  Presumably, by introducing a new concept class for referral order, along with a handler for the new order type and, of course, a ReferralOrder class that extends Order.

 

3. Create a referral order for my patient:

POST order

{ "patient":"123-456", "type":"referralorder", "order":"cardiology referral please evaluate for rheumatic heart disease" }

 

4. Find all the active orders for my patient:

GET order?patient=123-456

[

{ "patient":"123-456", "type":"drugorder", "order":"penicillin 500 mg orally twice daily for 2 weeks", "uri":"…" },

{ "patient":"123-456", "type":"referralorder", "order":"cardiology referral please evaluate for rheumatic heart disease", "uri":"…" }

]

 

Is that close?

 

-Burke

 

On Tue, Apr 24, 2012 at 9:21 PM, Darius Jazayeri <[hidden email]> wrote:

On RESTWS-243 I've attached a patch that contains my implementation that I'm now ready to commit. (It's what Roger was responding to in his email.)

 

I'm going to hold off on committing until after tomorrow's design call in case we have time to discuss this a bit.

 

Basically what I've done is allow resources to indicate that they support subclasses, and if they do, they'll have all appropriate DelegatingSubclassHandlers registered with them via spring.

 

The idea is that we're hiding subclasses from the consumer of the REST API. As far as they're concerned there is a single "order" resource, which has documents of different types (e.g. "order", "drugorder", etc.) Behind the scenes each type is a subclass, but in REST that's not visible, rather they're exposed as different document types. 

 

DelegatingSubclassHandler encapsulates the methods subclasses need to specify for their owning resource.

 

Here is the functionality that my approach supports:

 

GET order

  • gets all orders regardless of type
  • each document in the list may have different types
  • the top-level resource does the db query and delegates to the appropriate handler to convert each result

GET order?t=drugorder

  • gets all orders whose type is drugorder
  • the subclass handler does the db query
  • In my implementation GET order?t=order (i.e. get only things that are order stubs) is not allowed

GET order?patient=<uuid>

  • gets all orders for the given patient
  • each document in the list may have different types

GET order?patient=<uuid>&t=drugorder

  • gets all orders for the given patient whose type is drugorder
  • if the subclass handler defines a suitable method, we use it, otherwise the top-level resource queries by patient, and manually filters out by type

GET order/<uuid>

  • returns a specific order
  • representations are defined by the type, e.g. { "type": "drugorder", "dose": "100", ... }

POST order

  • creates a new order
  • you must specify the type in the content, e.g. { "type": "drugorder", "dose": "100", ... }
  • allowed properties are defined by the type

POST order/<uuid>

  • modifies an existing order
  • you are not allowed to change the type
  • allowed properties are defined by the type

DELETE order/<uuid>

  • voids an order
  • handled by the top-level resource, not the subclass handler

DELETE order/<uuid>?purge=true

  • purges an order
  • handled by the top-level resource, not the subclass handler

@Roger, can you please phrase your counterproposal in terms of the additional functionality that you want to expose beyond these methods?

 

-Darius

 

On Mon, Apr 23, 2012 at 9:54 AM, Burke Mamlin <[hidden email]> wrote:

Roger,

 

Could you provide some concrete examples?

 

-Burke

 

On Mon, Apr 23, 2012 at 8:07 AM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

AN ALTERNATIVE APPROACH TO SUBCLASS IMPLEMENTATION

In this approach, subclass resources remain resources, just are mapped via the superclass. Substantially all operations are delegated to the appropriate subclass resource. This is motivated by simplicity, validation and representation considerations. It is also motivated by a privilege issue I just realized – the people authorized to create/update a lab order are likely different from those allowed to create/update a drug order. I have assumed that the privilege of updating a parent class field in subclass record requires only parent class privileges

I don't believe this alternative requires much in the way of changes to existing code other than mappings; adding the controller and resource for each superclass; some changes in asRepresentation where it finds the right representation to use; and some changes where updates are going on to allow the possibility that the fields to be looked for in the request body may come from an object which is a parent class of the object being updated. It eliminates the need for the two variable lists used by Darius (although they could still be used if it was thought they added value). I still think it is a good idea to treat ActiveLists as an abstract class so that the AllergyList and the ProblemList appear to be two different superclasses.

All members of a class hierarchy request a mapping to /<superclass>?t=<subclass>. The superclass also requests a mapping to /<superclass>?!t. I have tried to make all the calls of /<superclass>?!t work the same as calls to /<superclass>?t=<superclass>, if I have missed please let me know.  It might be worthwhile to extend Resource to SuperclassResource to make the coding of the type-free calls easier.  All representations include a t virtual field which contains the actual subclass of the object being represented.

1. GET <superclass>?t=<subclass>&v=<rep>, GET <superclass>?!t

Purpose is to get all records of a particular subclass in a formatted representation. Spring routes this to the subclass controller. The subclass resource uses getAll to get a (polymorphic) list of objects. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

2. GET <superclass>?t=<subclass>&v=<rep>&q=<search param>, GET <superclass>?!t&v=<rep>&q=<search param>

Purpose is to use the standard query for a subclass to get a formatted representation. Spring routes this to the subclass controller. The subclass resource uses doSearch to get a (polymorphic) list of objects. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

3. GET <superclass>?t=<subclass>&v=<rep>&<custom search>=<search param>

Purpose is to do a custom search that is defined at some level of the class hierarchy.  Spring routes this to the subclass controller. The subclass controller creates a resource for its type and delegates the search to it. The resource uses service methods to do the search to produce a (polymorphic) list of objects. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

4. GET <superclass>/<uuid>?!t&v=<rep>

Purpose is to get all records of a particular subclass in a formatted representation. Spring routes this to the superclass controller. The superclass resource uses getByUuid to get an object. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the t subclass has been searched, at which point a rep does not exist error is thrown.

5. GET <superclass>/<uuid>?t=<subclass>&v=<rep>

Purpose is to use a representation from a parent class. Like 5, except after getting by Uuid, the subclass resource coerces the result to be its type. asRepresentation determines the class of each object and looks for the requested representation in that subclass resource; if it exists, it is used; if not, the parent class resource (if any) is searched for a representation; this continues until the supertype has been searched, at which point a rep does not exist error is thrown.

6. POST <superclass>?t=<subclass> {body}

Purpose is to create a new object of type <subclass>. The subclass resource creates a new object whose fields are extracted from the body (no need to know. The subclass resource save method uses services method to save the object; validation takes place in the save method.

7. POST <superclass>/<uuid>?!t {body}

Purpose is to update an object of any subclass. The type is determined and the operation is delegated to the resource of the actual type.

8. POST <superclass/<uuid>?t=<subclass> {body}

Purpose is to update an object using only parent-level fields. This is to avoid additional privileges at the actual subclass level. The type is deermined and the operation is delegated to the resource of the declared type. Throws an error if the actual type is not a subclass of the type declared.

9. DELETE <superclass>/<uuid>?!t&!purge, DELETE <superclass>/<uuid>?t=<subclass>&!purge

Purpose is to delete an object of any subclass. The type is determined and the operation is delegated to the resource of the actual type. The subclass, if present, must match the actual type of the resource.

10. DELETE <superclass>/<uuid>?purge&!t, DELETE <superclass>/<uuid>?purge

Purpose is to purge an object of any subclass. The type is determine and the operation is delegated to the resource of the actual type. The subclass, if present, must match the actual type of the resource.


[hidden email] from OpenMRS Developers' mailing list

 


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list

 


[hidden email] from OpenMRS Developers' mailing list

 


[hidden email] from OpenMRS Developers' mailing list