Merge lp:~jamesh/bindwood/sync-item-to-couch into lp:bindwood

Proposed by James Henstridge
Status: Merged
Approved by: dobey
Approved revision: 47
Merged at revision: 35
Proposed branch: lp:~jamesh/bindwood/sync-item-to-couch
Merge into: lp:bindwood
Prerequisite: lp:~jamesh/bindwood/sync-from-couch
Diff against target: 309 lines (+278/-2)
2 files modified
modules/sync.jsm (+93/-2)
mozmill/tests/test_sync_to_couch.js (+185/-0)
To merge this branch: bzr merge lp:~jamesh/bindwood/sync-item-to-couch
Reviewer Review Type Date Requested Status
Manuel de la Peña (community) Approve
Stuart Langridge (community) Approve
Review via email: mp+50609@code.launchpad.net

Commit message

Add code to the new synchroniser for exporting local bookmark items to CouchDB.

Description of the change

Add code to the new synchroniser to export local bookmark items to CouchDB. It makes sure to only update the CouchDB record if there are real local changes.

To post a comment you must log in.
Revision history for this message
Stuart Langridge (sil) wrote :

All tests now pass (natty, FF4). Looking good.

Revision history for this message
Stuart Langridge (sil) :
review: Approve
Revision history for this message
Manuel de la Peña (mandel) wrote :

Loks good and tests pass. I wonder if we should use the bindwood name in the application annotations rather than firefox... but for the rest, everything looks cool.

review: Approve
Revision history for this message
James Henstridge (jamesh) wrote :

The current Bindwood code (and the website code that reads the user's bookmarks database) uses the Firefox namespace for application annotations.

While it would probably be cleaner to use "Bindwood" as a namespace, I don't think it is worth breaking the existing code (at present, the changes I've made to the schema have been fairly minor).

Revision history for this message
Ubuntu One Auto Pilot (otto-pilot) wrote :

The prerequisite lp:~jamesh/bindwood/sync-from-couch has not yet been merged into lp:bindwood.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'modules/sync.jsm'
2--- modules/sync.jsm 2011-02-22 11:12:10 +0000
3+++ modules/sync.jsm 2011-02-22 11:12:10 +0000
4@@ -243,7 +243,8 @@
5 this.folders_to_reorder[doc._id] = doc.children;
6 break;
7 case TYPE_FEED:
8- var site_uri = ioService.newURI(doc.site_uri, null, null);
9+ var site_uri = doc.site_uri ?
10+ ioService.newURI(doc.site_uri, null, null) : null;
11 var feed_uri = ioService.newURI(doc.feed_uri, null, null);
12 item_id = livemarkService.createLivemark(
13 parent_id, doc.title, site_uri, feed_uri,
14@@ -294,7 +295,8 @@
15 this.folders_to_reorder[doc._id] = doc.children;
16 break;
17 case TYPE_FEED:
18- var site_uri = ioService.newURI(doc.site_uri, null, null);
19+ var site_uri = doc.site_uri ?
20+ ioService.newURI(doc.site_uri, null, null) : null;
21 var feed_uri = ioService.newURI(doc.feed_uri, null, null);
22 bookmarksService.setItemTitle(item_id, doc.title);
23 livemarkService.setSiteURI(item_id, site_uri);
24@@ -374,6 +376,95 @@
25 }
26 this.folders_to_reorder = {}
27 },
28+
29+ exportItem: function(item_guid) {
30+ var item_id = this.guid_to_id(item_guid);
31+ var item_type = null, deleted = false;
32+ if (item_id) {
33+ // Get the item type, which also checks whether the item exists.
34+ try {
35+ item_type = bookmarksService.getItemType(item_id);
36+ } catch (e) {
37+ deleted = true;
38+ }
39+ } else {
40+ deleted = true;
41+ }
42+
43+ var doc = this.couch.open(item_guid);
44+ if (deleted) {
45+ if (doc != null) {
46+ this.couch.deleteDoc(doc);
47+ }
48+ delete this.guid_item_map[item_guid];
49+ return;
50+ }
51+
52+ var changed = false;
53+ if (doc == null) {
54+ doc = { _id: item_guid };
55+ changed = true;
56+ }
57+
58+ var _setattr = function(doc, attr, value) {
59+ if (attr == 'children') {
60+ // Special handling, since 'children' is an array
61+ if (!doc[attr] || value.join('\n') != doc[attr].join('\n')) {
62+ doc[attr] = value;
63+ changed = true;
64+ }
65+ } else if (doc[attr] != value) {
66+ doc[attr] = value;
67+ changed = true;
68+ }
69+ }
70+
71+ if (!doc.application_annotations)
72+ doc.application_annotations = {};
73+ if (!doc.application_annotations.Firefox)
74+ doc.application_annotations.Firefox = {};
75+ _setattr(doc.application_annotations.Firefox, "profile", this.profile);
76+
77+ var parent_id = bookmarksService.getFolderIdForItem(item_id);
78+ if (parent_id > 0) {
79+ var parent_guid = this.guid_from_id(parent_id);
80+ _setattr(doc, "parent_guid", parent_guid);
81+ }
82+ switch (item_type) {
83+ case bookmarksService.TYPE_BOOKMARK:
84+ _setattr(doc, "record_type", TYPE_BOOKMARK);
85+ _setattr(doc, "uri", bookmarksService.getBookmarkURI(item_id).spec);
86+ _setattr(doc, "title", bookmarksService.getItemTitle(item_id));
87+ break;
88+ case bookmarksService.TYPE_FOLDER:
89+ _setattr(doc, "title", bookmarksService.getItemTitle(item_id));
90+ if (livemarkService.isLivemark(item_id)) {
91+ _setattr(doc, "record_type", TYPE_FEED);
92+ var site_uri = livemarkService.getSiteURI(item_id);
93+ if (site_uri)
94+ _setattr(doc, "site_uri", site_uri.spec);
95+ _setattr(
96+ doc, "feed_uri", livemarkService.getFeedURI(item_id).spec);
97+ } else {
98+ _setattr(doc, "record_type", TYPE_FOLDER);
99+ var children = [
100+ this.guid_from_id(child_id)
101+ for each (child_id in this.get_folder_children(item_id))];
102+ _setattr(doc, "children", children);
103+ }
104+ break;
105+ case bookmarksService.TYPE_SEPARATOR:
106+ _setattr(doc, "record_type", TYPE_SEPARATOR);
107+ break;
108+ default:
109+ dump("Can not handle item " + item_id + " of type " +
110+ item_type + "\n");
111+ return;
112+ }
113+ if (changed) {
114+ this.couch.save(doc);
115+ }
116+ },
117 };
118
119
120
121=== added file 'mozmill/tests/test_sync_to_couch.js'
122--- mozmill/tests/test_sync_to_couch.js 1970-01-01 00:00:00 +0000
123+++ mozmill/tests/test_sync_to_couch.js 2011-02-22 11:12:10 +0000
124@@ -0,0 +1,185 @@
125+var bm = require("../shared-modules/bookmarks");
126+
127+const TIMEOUT = 5000;
128+
129+const LOCAL_TEST_FOLDER = collector.addHttpResource('../test-files/');
130+const LOCAL_TEST_PAGE = LOCAL_TEST_FOLDER + 'test.html';
131+const LOCAL_TEST_FEED = LOCAL_TEST_FOLDER + 'feed.atom';
132+
133+var setupModule = function(module) {
134+ module.controller = mozmill.getBrowserController();
135+ module.jum = {};
136+ module.desktopcouch = {};
137+ module.sync = {};
138+ Cu.import("resource://mozmill/modules/jum.js", module.jum);
139+ Cu.import("resource://bindwood/desktopcouch.jsm", module.desktopcouch);
140+ Cu.import("resource://bindwood/sync.jsm", module.sync);
141+ bm.clearBookmarks();
142+ module.couch = null;
143+ module.synchroniser = null;
144+};
145+
146+
147+var setupTest = function(test) {
148+ var done = false;
149+ desktopcouch.connect_desktopcouch("test_bookmarks", function(db) {
150+ couch = db;
151+ done = true;
152+ }, function (message) {});
153+ controller.waitFor(
154+ function() { return done; }, "Could not connect to CouchDB", TIMEOUT);
155+ jum.assertNotEquals(couch, null);
156+
157+ try {
158+ couch.createDb();
159+ } catch (e) {
160+ if (e.error != 'file_exists')
161+ throw(e);
162+ }
163+ synchroniser = new sync.Synchroniser(couch, "profile_name");
164+};
165+
166+
167+var teardownTest = function(test) {
168+ bm.clearBookmarks();
169+ couch.deleteDb();
170+};
171+
172+
173+var test_export_bookmark = function() {
174+ var item_id = bm.bookmarksService.insertBookmark(
175+ bm.bookmarksService.toolbarFolder, bm.createURI(LOCAL_TEST_PAGE),
176+ bm.bookmarksService.DEFAULT_INDEX, "Bookmark title");
177+
178+ var item_guid = synchroniser.guid_from_id(item_id);
179+ synchroniser.exportItem(item_guid);
180+ var doc = couch.open(item_guid);
181+ jum.assertEquals(doc.record_type, "http://www.freedesktop.org/wiki/" +
182+ "Specifications/desktopcouch/bookmark");
183+ jum.assertEquals(doc.application_annotations.Firefox.profile,
184+ "profile_name");
185+ jum.assertEquals(doc.parent_guid, "toolbar_profile_name");
186+ jum.assertEquals(doc.uri, LOCAL_TEST_PAGE);
187+ jum.assertEquals(doc.title, "Bookmark title");
188+
189+ // Make a change to the folder and re-export it.
190+ bm.bookmarksService.setItemTitle(item_id, "New title");
191+ synchroniser.exportItem(item_guid);
192+ doc = couch.open(item_guid);
193+ jum.assertEquals(doc.title, "New title");
194+
195+ // A second sync with no changes does not update the revision ID.
196+ var old_revid = doc._rev;
197+ synchroniser.exportItem(item_guid);
198+ doc = couch.open(item_guid);
199+ jum.assertEquals(doc._rev, old_revid);
200+};
201+
202+var test_export_folder = function() {
203+ var folder_id = bm.bookmarksService.createFolder(
204+ bm.bookmarksService.toolbarFolder, "Folder",
205+ bm.bookmarksService.DEFAULT_INDEX);
206+ var child1_id = bm.bookmarksService.insertBookmark(
207+ folder_id, bm.createURI(LOCAL_TEST_PAGE + "#one"),
208+ bm.bookmarksService.DEFAULT_INDEX, "Bookmark 1");
209+ var child2_id = bm.bookmarksService.insertBookmark(
210+ folder_id, bm.createURI(LOCAL_TEST_PAGE + "#two"),
211+ bm.bookmarksService.DEFAULT_INDEX, "Bookmark 2");
212+
213+ var folder_guid = synchroniser.guid_from_id(folder_id);
214+ synchroniser.exportItem(folder_guid);
215+ var doc = couch.open(folder_guid);
216+ jum.assertEquals(doc.record_type, "http://www.freedesktop.org/wiki/" +
217+ "Specifications/desktopcouch/folder");
218+ jum.assertEquals(doc.application_annotations.Firefox.profile,
219+ "profile_name");
220+ jum.assertEquals(doc.parent_guid, "toolbar_profile_name");
221+ jum.assertEquals(doc.title, "Folder");
222+ jum.assertEquals(doc.children.length, 2);
223+ jum.assertEquals(doc.children[0], synchroniser.guid_from_id(child1_id));
224+ jum.assertEquals(doc.children[1], synchroniser.guid_from_id(child2_id));
225+
226+ // Make a change to the folder and re-export it.
227+ bm.bookmarksService.setItemTitle(folder_id, "New title");
228+ synchroniser.exportItem(folder_guid);
229+ doc = couch.open(folder_guid);
230+ jum.assertEquals(doc.title, "New title");
231+
232+ // A second sync with no changes does not update the revision ID.
233+ var old_revid = doc._rev;
234+ synchroniser.exportItem(folder_guid);
235+ doc = couch.open(folder_guid);
236+ jum.assertEquals(doc._rev, old_revid);
237+};
238+
239+var test_export_feed = function() {
240+ var item_id = bm.livemarkService.createLivemark(
241+ bm.bookmarksService.toolbarFolder, "Feed",
242+ null, bm.createURI(LOCAL_TEST_FEED),
243+ bm.bookmarksService.DEFAULT_INDEX);
244+ // Wait for the livemark to populate.
245+ controller.waitFor(function() {
246+ return bm.bookmarksService.getIdForItemAt(
247+ item_id, 0) >= 0;
248+ }, TIMEOUT);
249+
250+ var item_guid = synchroniser.guid_from_id(item_id);
251+ synchroniser.exportItem(item_guid);
252+ var doc = couch.open(item_guid);
253+ jum.assertEquals(doc.record_type, "http://www.freedesktop.org/wiki/" +
254+ "Specifications/desktopcouch/feed");
255+ jum.assertEquals(doc.application_annotations.Firefox.profile,
256+ "profile_name");
257+ jum.assertEquals(doc.parent_guid, "toolbar_profile_name");
258+ jum.assertEquals(doc.site_uri, "http://www.example.com/");
259+ jum.assertEquals(doc.feed_uri, LOCAL_TEST_FEED);
260+ jum.assertEquals(doc.title, "Feed");
261+
262+ // Make a change to the folder and re-export it.
263+ bm.bookmarksService.setItemTitle(item_id, "New title");
264+ synchroniser.exportItem(item_guid);
265+ doc = couch.open(item_guid);
266+ jum.assertEquals(doc.title, "New title");
267+
268+ // A second sync with no changes does not update the revision ID.
269+ var old_revid = doc._rev;
270+ synchroniser.exportItem(item_guid);
271+ doc = couch.open(item_guid);
272+ jum.assertEquals(doc._rev, old_revid);
273+};
274+
275+var test_export_separator = function() {
276+ var item_id = bm.bookmarksService.insertSeparator(
277+ bm.bookmarksService.toolbarFolder, bm.createURI(LOCAL_TEST_PAGE));
278+
279+ var item_guid = synchroniser.guid_from_id(item_id);
280+ synchroniser.exportItem(item_guid);
281+ var doc = couch.open(item_guid);
282+ jum.assertEquals(doc.record_type, "http://www.freedesktop.org/wiki/" +
283+ "Specifications/desktopcouch/separator");
284+ jum.assertEquals(doc.application_annotations.Firefox.profile,
285+ "profile_name");
286+ jum.assertEquals(doc.parent_guid, "toolbar_profile_name");
287+
288+ // A second sync with no changes does not update the revision ID.
289+ var old_revid = doc._rev;
290+ synchroniser.exportItem(item_guid);
291+ doc = couch.open(item_guid);
292+ jum.assertEquals(doc._rev, old_revid);
293+};
294+
295+var test_export_deleted_item = function() {
296+ var item_id = bm.bookmarksService.insertBookmark(
297+ bm.bookmarksService.toolbarFolder, bm.createURI(LOCAL_TEST_PAGE),
298+ bm.bookmarksService.DEFAULT_INDEX, "Bookmark title");
299+
300+ var item_guid = synchroniser.guid_from_id(item_id);
301+ synchroniser.exportItem(item_guid);
302+
303+ bm.bookmarksService.removeItem(item_id);
304+ synchroniser.exportItem(item_guid);
305+
306+ // The document has been deleted in CouchDB.
307+ var doc = couch.open(item_guid);
308+ jum.assertNull(doc);
309+};

Subscribers

People subscribed via source and target branches