References of documents in the Virtual File System¶
What we want?¶
Cozy applications can use data from the Data System and files from the Virtual File System. Of course, sometimes a link between data and files can be useful. For example, the application can have an album with photos. The album will be a document in CouchDB (with a title and other fields), but il will also list the files to use as photos.
A direct way to do that is storing the files IDs in the album document. It’s simple and will work pretty well if the files are manipulated only from this application. But, files are often accessed from other apps, like cozy-desktop and cozy-drive. To improve the User eXperience, it should be nice to alert the user when a file in an album is modified or deleted.
When a file is modified, we can offer the user the choice between keeping the original version in the album, or using the new modified file. When a file is moved to the trash, we can alert the user and let him/her restore the file.
Cozy-desktop, cozy-drive, and the other apps can’t scan all the documents with many different doctypes to find all the references to a file to detect such cases. The goal of this document is to offer a way to do that, and it is called References.
The references of a file are listed in its JSON-API representation in the
references
field, within the relationships
object of data
.
Example¶
{ "data": { "type": "io.cozy.files", "id": "9152d568-7e7c-11e6-a377-37cbfb190b4b", "meta": { "rev": "1-0e6d5b72" }, "attributes": { "type": "file", "name": "hello.txt", "md5sum": "ODZmYjI2OWQxOTBkMmM4NQo=", "created_at": "2016-09-19T12:38:04Z", "updated_at": "2016-09-19T12:38:04Z", "tags": [], "size": 12, "executable": false, "class": "document", "mime": "text/plain" }, "relationships": { "parent": { "links": { "related": "/files/fce1a6c0-dfc5-11e5-8d1a-1f854d4aaf81" }, "data": { "type": "io.cozy.files", "id": "fce1a6c0-dfc5-11e5-8d1a-1f854d4aaf81" } }, "referenced_by": { "links": { "self": "/files/fce1a6c0-dfc5-11e5-8d1a-1f854d4aaf81/relationships/references" }, "data": [ { "type": "io.cozy.playlists", "id": "94375086-e2e2-11e6-81b9-5bc0b9dd4aa4" } ] } }, "links": { "self": "/files/9152d568-7e7c-11e6-a377-37cbfb190b4b" } } }
Routes¶
POST /files/:file-id/relationships/referenced_by¶
Add on a file one or more references to documents
Request¶
POST /files/9152d568-7e7c-11e6-a377-37cbfb190b4b/relationships/referenced_by HTTP/1.1 Content-Type: application/vnd.api+json Accept: application/vnd.api+json
{ "data": [ { "type": "io.cozy.playlists", "id": "f2625cc0-e2d6-11e6-a0d5-cfbbfb141af0" } ] }
Response¶
HTTP/1.1 200 OK Content-Type: application/vnd.api+json
{ "meta": { "rev": "2-de8d0ba2", "count": 2 }, "data": [ { "type": "io.cozy.playlists", "id": "94375086-e2e2-11e6-81b9-5bc0b9dd4aa4" }, { "type": "io.cozy.playlists", "id": "f2625cc0-e2d6-11e6-a0d5-cfbbfb141af0" } ] }
DELETE /files/:file-id/relationships/referenced_by¶
Remove one or more references to documents on a file
Request¶
DELETE /files/9152d568-7e7c-11e6-a377-37cbfb190b4b/relationships/referenced_by HTTP/1.1 Content-Type: application/vnd.api+json Accept: application/vnd.api+json
{ "data": [ { "type": "io.cozy.playlists", "id": "f2625cc0-e2d6-11e6-a0d5-cfbbfb141af0" } ] }
Response¶
HTTP/1.1 200 OK Content-Type: application/vnd.api+json
{ "meta": { "rev": "3-7ab812c0", "count": 1 }, "data": [ { "type": "io.cozy.playlists", "id": "94375086-e2e2-11e6-81b9-5bc0b9dd4aa4" } ] }
GET /data/:type/:doc-id/relationships/references¶
Returns all the files id associated to an album or playlist.
Contents is paginated following jsonapi conventions. The default limit is 100 entries. The maximal number of entries per page is 1000.
It’s also possible to sort the files by their datetime (for photos) with the
sort
query parameter: ?sort=datetime
.
Request¶
GET /data/io.cozy.playlists/e9308dc2-e2e3-11e6-b685-fb88662613d4/relationships/references HTTP/1.1 Content-Type: application/vnd.api+json Accept: application/vnd.api+json
Response¶
HTTP/1.1 200 OK Content-Type: application/vnd.api+json
{ "data": [ { "type": "io.cozy.files", "id": "417c4e58-e2e4-11e6-b7dc-2b68ed7b77f4" }, { "type": "io.cozy.files", "id": "4504f55c-e2e4-11e6-88f2-d77aeecab549" }, { "type": "io.cozy.files", "id": "4587727a-e2e4-11e6-bfe9-ef1be7df7f26" }, { "type": "io.cozy.files", "id": "45d591d0-e2e4-11e6-ab9a-ff3b218e31cc" } ] }
POST /data/:type/:doc-id/relationships/references¶
When creating an album or a playlist, it’s tedious to add the references to it for each file individually. This route allows to make it in bulk.
Request¶
POST /data/io.cozy.playlists/e9308dc2-e2e3-11e6-b685-fb88662613d4/relationships/references HTTP/1.1 Content-Type: application/vnd.api+json Accept: application/vnd.api+json
{ "data": [ { "type": "io.cozy.files", "id": "417c4e58-e2e4-11e6-b7dc-2b68ed7b77f4" }, { "type": "io.cozy.files", "id": "4504f55c-e2e4-11e6-88f2-d77aeecab549" }, { "type": "io.cozy.files", "id": "4587727a-e2e4-11e6-bfe9-ef1be7df7f26" }, { "type": "io.cozy.files", "id": "45d591d0-e2e4-11e6-ab9a-ff3b218e31cc" } ] }
Response¶
HTTP/1.1 204 No Content Content-Type: application/vnd.api+json
DELETE /data/:type/:doc-id/relationships/references¶
This bulk deletion of references on many files can be useful when an album or playlist is deleted.
Request¶
DELETE /data/io.cozy.playlists/e9308dc2-e2e3-11e6-b685-fb88662613d4/relationships/references HTTP/1.1 Content-Type: application/vnd.api+json Accept: application/vnd.api+json
{ "data": [ { "type": "io.cozy.files", "id": "417c4e58-e2e4-11e6-b7dc-2b68ed7b77f4" }, { "type": "io.cozy.files", "id": "4504f55c-e2e4-11e6-88f2-d77aeecab549" }, { "type": "io.cozy.files", "id": "4587727a-e2e4-11e6-bfe9-ef1be7df7f26" }, { "type": "io.cozy.files", "id": "45d591d0-e2e4-11e6-ab9a-ff3b218e31cc" } ] }
Response¶
HTTP/1.1 204 No Content Content-Type: application/vnd.api+json
Usage¶
Modification of a referenced file¶
Before an application updates a file, it can check if the file has some references. If it is the case, it may offer to the user two choices:
- update the file with the new version (the albums and playlists will use the new version)
- save the new version as a new file and preserve the old file (the old file
may be moved to a
originals
directory).
Moving a referenced file to the trash¶
Before an application moves a file to the trash, if the file has some references, it should ask the user if they really want to trash the file. And it should also removes the references before trashing the file.
Implementation¶
The references are persisted in the io.cozy.files
documents in CouchDB. A
mango index is used to fetch all the files that are associated to a given
document (for GET /data/:type/:doc-id/relationships/references
).
For request to update or move to trash a file, it is easy to fetch its CouchDB document to see if it has a reference. But it is more difficult when moving a folder to trash. To do that, we need two requests to fetch the number of references in a folder.
1/ Get all descendant folders from a given folder, with a CouchDB View:
map = function(doc) { if (doc.type === "folder") emit(doc.path); }; query = { startkey: parent_folder_path + "/", endkey: parent_folder_path + "/\uFFFF" };
2/ Get the total number of “referenced” file for this folders list, with a map/reduce CouchDB view:
map = function(doc) { if(doc.referenced != null) emit(doc.parentID) } reduce = count query = {keys: [list from above]}