5monkeys/django-bananas.js

Support API fencing

Opened this issue · 1 comments

I've introduced API fencing into django-bananas to handle the "lost update problem", that is dealing with the issue of "simultaneous" edits from multiple admins overwriting each other's changes.

It would be nice to build automatic support for this into django-bananas.js. There are now two builtin fences in django-bananas: allow_if_unmodified_since rejects updates when the given If-Unmodified-Since header is less than the stored date_modified on the model, and allow_if_match rejects updates when the given If-Match header does not contain the version of the stored model (version can be a stored or a computed value). Fences are exposed as in-header arguments in the OpenAPI schema and should be discoverable.

I'm thinking we preferably want to build in support so that these work out of the box given the resource exposes the respective fields, but with support for overriding (or composing) the source of the tokens. In the case of allow_if_unmodified_since we want to use the exposed date_modified field by default and if it exists. For allow_if_match we want to use the exposed version field by default and if it exists.

It's also possible to compose arbitrary fences that are not shipped by default (e.g. for If-Match or whatever) and we should try and build in corresponding composable building blocks into the client.

In Python composing a fence looks like this:

allow_if_not_modified_since = Fence(
    # Function that takes a request and returns the If-Modified-Since header
    # as a parsed datetime object.
    get_token=header_date_parser("If-Modified-Since"),
    # Function for comparing the header value with the stored value to
    # determine whether to  allow or reject the request.
    compare=operator.gt,
    # function that takes a model instance and returns its date_modified
    get_version=parse_date_modified,
    openapi_parameter=openapi.Parameter(...),
    # Specifies status code of rejected requests
    rejection=NotModified("The resource is unmodified"),
)

What do you think? Is this feasible to build automatic support for? If we need to prioritize I think automatic discovery and support for the two builtin fences should be supported, but it'd be really nice to get composability for arbitrary fences from the get-go.

I'd be happy to write this if no-one else bites, but I'd probably need some pointers to understand where something like this could be plugged in.

Also, different fences have different status codes for rejection, e.g. If-Unmodified-Since and If-Match use 412 Precondition Failed but headers like If-Modified-Since use 304 Not Modified. So support for specifying the status code for rejection and showing a reasonable error should preferably be builtin too.