views:

135

answers:

3

We have recently added version control capabilities to our system, we are having hard time modeling these capabilities in a RESTful way.

The system works as follows, when it's configured in "version-control" mode, before making modification to an entity one must check it out first (a private copy is created for that user). After modifying the local copy the user can then check it in to commit the changes or undo the check out to discard the changes.

We are debating what is the right approach to model the "check-in", "check-out", "get-version" and "undo check-out" operations.

so lets say we have a resource (named my-resource)

  http://my-system/my-resources/{id}

and we want to add version control capabilities on top of it, we are arguing between the two following approaches:

  1. Operation oriented

    Check In: POST on http://my-system/my-resources/{id}/check-in

    Check-Out: POST on http://my-system/my-resources/{id}/check-out

    Get Version: GET http://my-system/my-resources/{id}/versions/{version-id}

    Undo Check-Out: POST on http://my-system/my-resources/{id}/undo-check-out

  2. Resource Oriented

    Check In: POST on http://my-system/my-resources/{id}/versions

    Check-Out: POST on http://my-system/my-resources/{id}/check-out

    Get Version: GET http://my-system/my-resources/{id}/versions/{version-id}

    Undo Check-Out: DELETE on http://my-system/my-resources/{id}/check-out

What do you think? Do you have a suggestion how to model these operations? Are you familiar with similar public APIs to consult with?

+1  A: 

You've just described two RPC APIs. If you want a RESTful API, try something like this:

GET {resource url}
200 OK
Location: {resource url}
<resource>
    <!-- Rest of your resource goes here -->
    <operation id="check-out" href="{check-out url}" method="post">
</resource>

Using the URL and method in the resource's representation, you can check out the resource to create a working copy:

POST {check-out url}
200 OK
Location: {working copy url}
<working-copy>
    <!-- Info about the working copy goes here -->
    <operation id="check-in" href="{check-in url}" method="post">
    <operation id="discard" href="{undo check-out url}" method="post">
    <!-- Additional operations, as needed, to modify the working copy -->
</working-copy>

Use the operations defined in the working copy to either check it back in when finished:

POST {check-in url}
200 OK
Location: {resource url}
<resource>
    <!-- Modified resource data goes here -->
    <operation id="check-out" href="{check-out url}" method="post">
</resource>

or discard it:

POST {undo check-out url}
200 OK
Location: {resource url}
<resource>
    <!-- Original resource data goes here -->
    <operation id="check-out" href="{check-out url}" method="post">
</resource>

Now, your resources each are self-describing, and have their own URLs to uniquely identify them.

John Millikin
+1  A: 

This looks a lot like a simple RCS-style version control model. Assuming the same base resource URL structure as you proposed, I would model it as follows in a RESTful API:

GET http://my-system/my-resources/{id} => return current state of resource 'id', with check-out lock status encoded in HTTP extension header (X-checkout-status: unlocked, for example)

POST http://my-system/my-resources/{id}/versions => attempt check-out

  • if successful, return a HTTP 201 Created status with a Location: header pointing to the new temporary version resource (see below for "version" URL format)
  • otherwise, return HTTP 409 Conflict status code, which indicates the resource is already checked out

GET http://my-system/my-resources/{id}/versions/{version} => get checked-out version of resource

PUT http://my-system/my-resources/{id}/versions/{version} => save changes to checked-out resource

POST http://my-system/my-resources/{id}/versions/{version} => commit changes to checked-out resource, saving them to the main resource in the process (possibly requiring an X-checkout-status: unlocked header line for confirmation)

DELETE http://my-system/my-resources/{id}/versions/{version} => release a checkout lock without saving changes back to main resource

DELETE http://my-system/my-resources/{id}/versions => release all locks on a file; should require administrative privileges (but useful to clean up stale locks)

There are a few differences from your design. First, I've been explicit about how updates to a checked-out resource are actually accomplished. Second, I've used the X-checkout-status header and various HTTP codes to indicate whether a file is currently locked, and the success or failure of various version control operations.

The most critical difference, however, is in thinking of the "versions" sub-URL space as an explicit collection, and therefore using the normal POST/DELETE/etc. operations to manage check-outs.

rcoder
A: 

Not REST ( though Fielding is on the committee ), but WEBDAV is the standard way of doing version control over HTTP.

Another Apache project, Sling, appears to contain a REST API over a repository ( though I couldn't see a full description of the HTTP API anywhere, only small examples )

A third place it might be worth looking is at the Atom Publishing Protocol which allows simple publishing to a resource.

Pete Kirkham
This is a great suggestion, but the WebDAV resource locking model is different from the simple RCS style of maintaining explicit versions. If the domain model can change a bit, though, it would certainly be better to not reinvent the wheel with yet another version control protocol implemented atop HTTP.
rcoder