History of changes¶
When the built-in plugin kinto.plugins.history
is enabled in configuration,
it becomes possible to track the history of changes via a new endpoint GET /buckets/<bid>/history
.
Clients can check for the history
capability in the root URL endpoint.
Note
In terms of performance, enabling this plugin generates three additional queries on backends per request (i.e. per transaction).
- Creations/updates/deletions of every kind of object is tracked per bucket;
- Both data and permissions changes are provided in history entries;
- Only the new version of the record is stored in the history entry for each action;
- The history only shows the actions that were performed on objects where the user had read or write permission;
- When requested anonymously, the history only shows actions on publicly readable/writable objects;
- The
default_bucket
plugin is supported; - Entries can be filtered and sorted as any list endpoint.
Retrieve history¶
-
GET
/buckets/
(bucket_id)/history
¶ synopsis: Retrieve the history, ordered by -last_modified by default. Optional authentication
Example Request
$ http GET http://localhost:8888/v1/buckets/blog/history --auth="token:bob-token" --verbose
GET /v1/buckets/blog/history HTTP/1.1 Accept: */* Accept-Encoding: gzip, deflate Authorization: Basic dG9rZW46Ym9iLXRva2Vu Connection: keep-alive Host: localhost:8888
Example Response
HTTP/1.1 200 OK Access-Control-Expose-Headers: Content-Length, Expires, Alert, Retry-After, Last-Modified, Total-Records, ETag, Pragma, Cache-Control, Backoff, Next-Page Cache-Control: no-cache, no-store Content-Length: 1906 Content-Type: application/json; charset=UTF-8 Date: Wed, 20 Jul 2016 09:15:02 GMT Etag: "1469006098757" Last-Modified: Wed, 20 Jul 2016 09:14:58 GMT Server: waitress Total-Records: 4 { "data": [ { "action": "update", "collection_id": "articles", "date": "2016-07-20T11:18:36.530281", "id": "cb98ecd7-a66f-4f9d-82c5-73d06930f4f2", "last_modified": 1469006316530, "record_id": "b3b76c56-b6df-4195-8189-d79da4a128e1", "resource_name": "record", "target": { "data": { "id": "b3b76c56-b6df-4195-8189-d79da4a128e1", "last_modified": 1469006316529, "title": "Modified title" }, "permissions": { "write": [ "basicauth:43181ac0ae7581a23288c25a98786ef9db86433c62a04fd6071d11653ee69089" ] } }, "timestamp": 1469006098757, "uri": "/buckets/blog/collections/articles/records/b3b76c56-b6df-4195-8189-d79da4a128e1", "user_id": "basicauth:43181ac0ae7581a23288c25a98786ef9db86433c62a04fd6071d11653ee69089", } ] }
As other list endpoints, the entries can be filtered and sorted using the querystring.
?_since="<timestamp>"
and?_before="<timestamp>"
to filter by timestamp/last_modified?_limit=<N>
: limits to N entries (useNext-Page
response header for pagination)?uri=<URI>
: to filter on a particular object?collection_id=<id>
: to filter on a particular collection?resource_name=<bucket|group|collection|record>
: to filter by object type- See Filtering, Sorting, Paginating and Selecting fields.
Note
If the server defines a kinto.paginate_by
setting, the list will be limited by default.
Purge history¶
-
DELETE
/buckets/
(bucket_id)/history
¶ synopsis: Delete the writable history entries Optional authentication
Example Request
$ http DELETE "http://localhost:8888/v1/buckets/blog/history" --auth user:pass --verbose
DELETE /v1/buckets/blog/history HTTP/1.1 Accept: */* Accept-Encoding: gzip, deflate Authorization: Basic dXNlcjpwYXNz Connection: keep-alive Content-Length: 0 Host: localhost:8888 User-Agent: HTTPie/0.9.2
Example Response
HTTP/1.1 200 OK Access-Control-Expose-Headers: Retry-After, Content-Length, Alert, Backoff Content-Length: 283 Content-Type: application/json; charset=UTF-8 Date: Thu, 01 Dec 2016 17:05:11 GMT Server: waitress { "data": [ { "deleted": true, "id": "518c3e21-357d-4166-b6d9-d0b6ace22dfd", "last_modified": 1480611911546 }, { "deleted": true, "id": "f107f592-b9f4-466e-a7ea-52885bef1879", "last_modified": 1480611911546 }, { "deleted": true, "id": "8c549209-37ce-4509-b4ec-c6a4d831a8b6", "last_modified": 1480611911546 } ] }
Using the same querystring parameters as the GET endpoint, the deletion can be partial.
Conflict resolution¶
Having the journal of operations of an object possibly allows to resolve update conflicts automatically.
For example, if Alice receives a 412 Precondition Failed
error response when she tries to update a record,
she can use the history entries for this particular record filtering from a the timestamp of her local copy, in order
to merge the changes that happened remotely with her local ones.
$ RECORD_URI="buckets/blog/collections/articles/records/xyz"
$ LOCAL_TIMESTAMP="1469006098757"
$ http GET http://localhost:8888/v1/buckets/blog/history?uri=$RECORD_URI&_since=$LOCAL_TIMESTAMP --auth="token:bob-token" --verbose
Each entries gives the state in which the record was modified. Computing the difference between two steps and applying it to the local record is a possible way of solving conflicts automatically.
Configuration¶
It is possible to exclude certain resources from being tracked by history using the following setting:
kinto.history.exclude_resources = /buckets/preview
/buckets/signed/collections/certificates