On Thu, 2017-12-28 at 15:25 -0500, Barry Warsaw wrote:
Abhilash Raj wrote:
Core's REST API is versioned and any change that break backwards- compatibility cause the version to bump so that clients can take care of that.
However, one question that I have been thinking about recently is how to handle additions to REST API that don't necessarily break the backwards compatibility.
This problem is analogous to adding arguments to functions. This is usually handled in backward compatible ways in Python by appending new arguments to the end of the parameter list and giving them default values. Or to use keyword-only arguments. The analogy breaks down in one specific case though.
For example, Core added
max_message_sizeattribute to MailingList's REST endpoint, but it hasn't made into any released version yet. Also, Postorius added max_message_size in
Message Acceptancesettings. The problem here is that the entire PUT/PATCH request is going to fail if the currently running version of Core doesn't have
max_message_sizeattribute exposed (Unknown Attribute Error).
PATCH won't fail because it allows for partial representations. PUT does fail because it requires the entire new representation to be included in the request (it's a complete replacement). This is where the analogy to function arguments break down.
I don't really know of any good way to handle this that still conforms to REST principles. I don't think we want to rev the API in these cases since that'll result in a lot of version churn.
I agree that we don't want to rev the API for additions.
PATCH won't fail when running partial updates, but it won't silently drop the attributes that it doesn't support. So from client side, there is no real way to understand when to include that attribute which was added in a later version.
There is no easy way to check for whether the Core has this attribute as API is versioned at 3.1 for both cases.
So, how do we actually handle this and maybe future cases like this?
- The result of queries can be viewed as dictionaries
- New endpoints (urls) can be added anytime
- No endpoint is removed without a version bump
- Existing dict keys will not be dropped without a version bump
- The format of values assigned to existing keys will not change without
a version bump
- New keys (and values) can be added anytime
This is pretty much the criteria I've used in the past, and it works well enough in practice except for the PUT exception. A couple of thoughts on how to handle this include, using PATCH in preference to PUT, using PUT but catch any exception then fall back to PATCH, do a GET first to get the list of keys. None of those are great options, although some caching might help.
As I mentioned using PATCH doesn't really solve the problem as we don't know when we can update that particular attribute, however, it does help update other attributes in the same resource.
Exception caching might work, but then it would probably involve parsing the error message to find out which field is the problem before making a PATCH request without that attribute.
Doing a GET first sounds expensive to me, we would definitely need some caching to be able to use this idea.
We need some way to associate attributes with a minimum Core version, which we
can get from
/system endpoint. Although, for now, it probably is an overkill.
I will do some static stuff to take care of this.
Mailman's REST API is very dynamic so we don't even have a static representation of it that can be queried. I did a quick scan of RESTful Web APIs (Richardson & Amundsen - my go to bible for REST design philosophy) and didn't find a specific discussion on this topic.
How bad do you think would it be for Core to silently drop extra attributes and only use the ones that it needs?