Commit eae4943a authored by Alain Takoudjou's avatar Alain Takoudjou

replicated opml storage: improvements, update scenario test

parent a5515a0a
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
<link rel="stylesheet" href="../node_modules/grunt-contrib-qunit/test/libs/qunit.css" type="text/css" media="screen"/> <link rel="stylesheet" href="../node_modules/grunt-contrib-qunit/test/libs/qunit.css" type="text/css" media="screen"/>
<script src="../node_modules/grunt-contrib-qunit/test/libs/qunit.js" type="text/javascript"></script> <script src="../node_modules/grunt-contrib-qunit/test/libs/qunit.js" type="text/javascript"></script>
<script src="../node_modules/sinon/pkg/sinon.js" type="text/javascript"></script>
<script src="scenario_monitor.js"></script> <script src="scenario_monitor.js"></script>
</head> </head>
......
/*global Blob, Rusha, console*/ /*global Blob, Rusha, sinon, console*/
/*jslint nomen: true, maxlen: 80*/ /*jslint nomen: true, maxlen: 80*/
(function (QUnit, jIO, Blob, Rusha, console) { (function (QUnit, jIO, Blob, Rusha, sinon, console) {
"use strict"; "use strict";
var test = QUnit.test, var test = QUnit.test,
// equal = QUnit.equal, // equal = QUnit.equal,
...@@ -29,15 +29,6 @@ ...@@ -29,15 +29,6 @@
type: "rss", type: "rss",
url: "http://example.com/rss.xml" url: "http://example.com/rss.xml"
}); });
this._sub_storage = jIO.createJIO({
type: "query",
sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
}
});
this._options = spec.options; this._options = spec.options;
resetCount(spec.options.count); resetCount(spec.options.count);
} }
...@@ -49,16 +40,7 @@ ...@@ -49,16 +40,7 @@
function WEBMockStorage(spec) { function WEBMockStorage(spec) {
this._web_storage = jIO.createJIO({ this._web_storage = jIO.createJIO({
type: "webhttp", type: "webhttp",
url: "http://example.com/private/" url: "http://example.com/"
});
this._sub_storage = jIO.createJIO({
type: "query",
sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
}
}); });
this._options = spec.options; this._options = spec.options;
resetCount(spec.options.count); resetCount(spec.options.count);
...@@ -73,15 +55,6 @@ ...@@ -73,15 +55,6 @@
type: "opml", type: "opml",
url: "http://example.com/opml.xml" url: "http://example.com/opml.xml"
}); });
this._sub_storage = jIO.createJIO({
type: "query",
sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
}
});
this._options = spec.options; this._options = spec.options;
resetCount(spec.options.count); resetCount(spec.options.count);
} }
...@@ -96,21 +69,21 @@ ...@@ -96,21 +69,21 @@
if (this._options.mock.hasOwnProperty(name)) { if (this._options.mock.hasOwnProperty(name)) {
return this._options.mock[name].apply(this, arguments); return this._options.mock[name].apply(this, arguments);
} }
return this._sub_storage[name].apply(this._sub_storage, arguments); return this._web_storage[name].apply(this._web_storage, arguments);
}; };
RSSMockStorage.prototype[name] = function () { RSSMockStorage.prototype[name] = function () {
this._options.count[name] += 1; this._options.count[name] += 1;
if (this._options.mock.hasOwnProperty(name)) { if (this._options.mock.hasOwnProperty(name)) {
return this._options.mock[name].apply(this, arguments); return this._options.mock[name].apply(this, arguments);
} }
return this._sub_storage[name].apply(this._sub_storage, arguments); return this._rss_storage[name].apply(this._rss_storage, arguments);
}; };
OPMLMockStorage.prototype[name] = function () { OPMLMockStorage.prototype[name] = function () {
this._options.count[name] += 1; this._options.count[name] += 1;
if (this._options.mock.hasOwnProperty(name)) { if (this._options.mock.hasOwnProperty(name)) {
return this._options.mock[name].apply(this, arguments); return this._options.mock[name].apply(this, arguments);
} }
return this._sub_storage[name].apply(this._sub_storage, arguments); return this._opml_storage[name].apply(this._opml_storage, arguments);
}; };
} }
...@@ -128,9 +101,6 @@ ...@@ -128,9 +101,6 @@
function generateHash(str) { function generateHash(str) {
return rusha.digestFromString(str); return rusha.digestFromString(str);
} }
function putFullDoc(storage, id, doc) {
return storage.put(id, doc);
}
function equalStorage(storage, doc_tuple_list) { function equalStorage(storage, doc_tuple_list) {
return storage.allDocs({include_docs: true}) return storage.allDocs({include_docs: true})
...@@ -166,59 +136,39 @@ ...@@ -166,59 +136,39 @@
deepEqual(mock_count, expected_count, 'Expected method call count'); deepEqual(mock_count, expected_count, 'Expected method call count');
} }
function getOpmlElement(doc, doc_id, url) {
var element,
id,
parent_id;
parent_id = generateHash(url);
id = generateHash(parent_id + doc_id);
element = {
name: doc_id,
opml_title: doc.opml_title,
parent_id: parent_id,
reference: id,
creation_date: doc.created_date,
title: doc.title,
type: "opml-item",
url: url
};
return {
id: id,
doc: element
};
}
function getSubOpmlElement(doc, doc_id, opml_doc, url, type) {
var id = generateHash(opml_doc.reference + url + doc_id);
return {
id: id,
doc: {
name: doc_id,
opml_title: opml_doc.opml_title,
parent_title: opml_doc.title,
parent_id: opml_doc.reference,
reference: id,
title: doc.title,
type: doc.type || type + "-item",
url: url,
status: doc.status,
creation_date: doc.date
}
};
}
/////////////////////////////////////////////////////// ///////////////////////////////////////////////////////
// Module // Module
/////////////////////////////////////////////////////// ///////////////////////////////////////////////////////
module("scenario_monitor", { module("scenario_monitor", {
setup: function () { setup: function () {
this.server = sinon.fakeServer.create();
this.server.autoRespond = true;
this.server.autoRespondAfter = 5;
this.rss_mock_options = { this.rss_mock_options = {
mock: { mock: {
remove: function () { remove: function () {
throw new Error('remove not supported'); throw new Error('remove not supported');
}, },
removehas_include_docs: function () { removeAttachment: function () {
throw new Error('removeAttachment not supported');
},
allAttachments: function () {
return {data: null};
},
putAttachment: function () {
throw new Error('putAttachment not supported');
}
},
count: {}
};
this.opml_mock_options = {
mock: {
remove: function () {
throw new Error('remove not supported');
},
removeAttachment: function () {
throw new Error('removeAttachment not supported'); throw new Error('removeAttachment not supported');
}, },
allAttachments: function () { allAttachments: function () {
...@@ -230,9 +180,6 @@ ...@@ -230,9 +180,6 @@
}, },
count: {} count: {}
}; };
this.opml_mock_options = JSON.parse(
JSON.stringify(this.rss_mock_options)
);
this.web_mock_options = { this.web_mock_options = {
mock: { mock: {
remove: function () { remove: function () {
...@@ -263,7 +210,7 @@ ...@@ -263,7 +210,7 @@
}, },
{ {
type: "webmock", type: "webmock",
url: "http://example.com/data/", url: "http://example.com/",
has_include_docs: true, has_include_docs: true,
options: this.web_mock_options options: this.web_mock_options
} }
...@@ -285,6 +232,66 @@ ...@@ -285,6 +232,66 @@
} }
} }
}); });
this.server.respondWith("GET", "http://example.com/opml.xml", [200,
{ "Content-Type": "text/xml" },
'<?xml version="1.0" encoding="ISO-8859-1"?>' +
'<opml version="1.0">' +
'<head>' +
'<title>opml foo</title>' +
'<dateCreated>Thu, 12 Sep 2003 23:35:52 GMT</dateCreated>' +
'<dateModified>Fri, 12 Sep 2003 23:45:37 GMT</dateModified>' +
'</head>' +
'<body>' +
'<outline text="OPML Item List">' +
'<outline text="instance foo" type="link" url="http://example.com/' +
'rss.xml" dateCreated="Thu, 12 Sep 2003 23:35:52 GMT" ' +
'htmlUrl="http://example.com/" title="opml item foo" />' +
'</outline>' +
'</body>' +
'</opml>'
]);
this.server.respondWith("GET", "http://example.com/rss.xml", [200,
{ "Content-Type": "text/xml" },
'<?xml version="1.0" encoding="UTF-8" ?>' +
'<rss version="2.0">' +
'<channel>' +
'<title>instance foo</title>' +
'<description>This is an example of an RSS feed</description>' +
'<link>http://www.domain.com/link.htm</link>' +
'<lastBuildDate>Mon, 28 Aug 2006 11:12:55 -0400 </lastBuildDate>' +
'<pubDate>Tue, 29 Aug 2006 09:00:00 -0400</pubDate>' +
'<item>' +
'<title>Item Example</title>' +
'<category>ERROR</category>' +
'<description>This is an example of an Item</description>' +
'<link>http://www.domain.com/link.htm</link>' +
'<guid isPermaLink="false">1102345</guid>' +
'<pubDate>Tue, 29 Aug 2006 09:00:00 -0400</pubDate>' +
'</item>' +
'</channel>' +
'</rss>'
]);
this.server.respondWith("GET", "http://example.com/_document_list", [200,
{ "Content-Type": "text/plain" },
'monitor.status'
]);
this.server.respondWith(
"GET",
"http://example.com/monitor.status.json",
[200,
{ "Content-Type": "application/json" },
'{"title": "document fooo", "status": "ERROR",' +
'"date": "Tue, 29 Aug 2006 09:00:00 -0400",' +
'"type": "global", "foo_p": "fooo parameter",' +
'"bar_p": "bar parameter", "total_error": 12345}']
);
},
teardown: function () {
this.server.restore();
delete this.server;
} }
}); });
...@@ -297,6 +304,9 @@ ...@@ -297,6 +304,9 @@
var test = this; var test = this;
test.opml_mock_options.mock.buildQuery = function () {
return [];
};
this.jio.repair() this.jio.repair()
.then(function () { .then(function () {
return RSVP.all([ return RSVP.all([
...@@ -324,52 +334,117 @@ ...@@ -324,52 +334,117 @@
}); });
/////////////////////////////////////////////////////// ///////////////////////////////////////////////////////
// sync done (XXX - to finish) // complete sync - one opml, 2 sub storages
/////////////////////////////////////////////////////// ///////////////////////////////////////////////////////
test("allready synced: nothing to do", function () { test("complete storage sync", function () {
expect(2); expect(4);
stop(); stop();
var test = this, var test = this,
key, doc_id = "http://example.com/rss.xml",
doc_id = 'opml_foo',
doc = { doc = {
title: "opml item foo", title: "opml item foo",
htmlurl: "http://example.com/",
url: "http://example.com/rss.xml", url: "http://example.com/rss.xml",
modified_date: "aftttt", text: "instance foo",
created_date: "adddb", type: "link",
opml_title: "opml foo" opml_title: "opml foo",
created_date: "Thu, 12 Sep 2003 23:35:52 GMT",
modified_date: "Fri, 12 Sep 2003 23:45:37 GMT"
},
parent_id = generateHash(test.sub_opml_storage.url),
opml_item_id = generateHash(parent_id + doc_id),
opml_item = {
name: doc_id,
opml_title: doc.opml_title,
parent_id: parent_id,
reference: generateHash(parent_id + doc_id),
creation_date: doc.created_date,
title: doc.title,
type: "opml-item",
url: test.sub_opml_storage.url,
signature: generateHash(JSON.stringify(doc))
},
full_opml = new Blob([JSON.stringify(doc)]),
rss_id = "1102345",
rss_doc = {
"link": "http://www.domain.com/link.htm",
"date": "Tue, 29 Aug 2006 09:00:00 -0400",
"title": "Item Example",
"category": "ERROR",
"description": "This is an example of an Item",
"guid": "1102345",
"siteTitle": "instance foo",
"reference": "This is an example of an RSS feed",
"siteLink": "http://www.domain.com/link.htm",
"lastBuildDate": "Mon, 28 Aug 2006 11:12:55 -0400 "
},
// Sub OPML document (rss)
rss_feed_url = "http://example.com/rss.xml",
rss_item_id = generateHash(opml_item.reference + rss_feed_url + rss_id),
rss_item = {
name: rss_id,
opml_title: opml_item.opml_title,
parent_title: opml_item.title,
parent_id: opml_item.reference,
reference: rss_item_id,
title: rss_doc.title,
type: rss_doc.type || "rssmock-item",
url: rss_feed_url,
status: rss_doc.category,
creation_date: rss_doc.date,
signature: generateHash(JSON.stringify(rss_doc))
}, },
blob = new Blob([JSON.stringify(doc)]); full_rss = new Blob([JSON.stringify(rss_doc)]),
json_id = "monitor.status",
json_doc = {
title: "document fooo",
status: "ERROR",
date: "Tue, 29 Aug 2006 09:00:00 -0400",
type: "global",
foo_p: "fooo parameter",
bar_p: "bar parameter",
total_error: 12345
},
// Sub OPML document (webhttp)
http_url = "http://example.com/",
json_item_id = generateHash(opml_item.reference + http_url + json_id),
json_item = {
name: json_id,
opml_title: opml_item.opml_title,
parent_title: opml_item.title,
parent_id: opml_item.reference,
reference: json_item_id,
title: json_doc.title,
type: json_doc.type,
url: http_url,
status: json_doc.status,
creation_date: json_doc.date,
signature: generateHash(JSON.stringify(json_doc))
},
full_json = new Blob([JSON.stringify(json_doc)]);
// initialise this storage here so we can put data test.jio.repair()
key = generateHash("http://example.com/opml.xml");
this.jio.__storage._remote_storage_dict[key] = jIO.createJIO({
type: "opmlmock",
options: this.opml_mock_options
});
putFullDoc(this.jio.__storage._remote_storage_dict[key], doc_id, doc)
.then(function () {
return test.jio.repair();
})
.then(function () { .then(function () {
resetCount(test.opml_mock_options.count);
return test.jio.repair();
})
.then(function () {
var storage_doc = getOpmlElement(doc,
doc_id,
test.sub_opml_storage.url);
return RSVP.all([ return RSVP.all([
equalStorage(test.jio, [[storage_doc.id, storage_doc.doc, blob]]), equalStorage(test.jio, [[opml_item_id, opml_item, full_opml],
[rss_item_id, rss_item, full_rss],
[json_item_id, json_item, full_json]]),
equalsubStorageCallCount( equalsubStorageCallCount(
test.opml_mock_options.count, test.opml_mock_options.count,
{buildQuery: 1} {buildQuery: 1}
),
equalsubStorageCallCount(
test.rss_mock_options.count,
{buildQuery: 1}
),
equalsubStorageCallCount(
test.web_mock_options.count,
{buildQuery: 1}
) )
]); ]);
}) })
.fail(function (error) { .fail(function (error) {
console.log(error);
ok(false, error); ok(false, error);
}) })
.always(function () { .always(function () {
...@@ -378,96 +453,110 @@ ...@@ -378,96 +453,110 @@
}); });
/////////////////////////////////////////////////////// ///////////////////////////////////////////////////////
// complete sync - one opml, 2 sub storages // document update
/////////////////////////////////////////////////////// ///////////////////////////////////////////////////////
test("complete storage sync", function () { test("remote document modified", function () {
expect(4); expect(4);
stop(); stop();
var test = this, var test = this,
key, doc_id = "http://example.com/rss.xml",
doc_id = 'opml_foo',
doc = { doc = {
title: "opml item foo", title: "opml item foo",
htmlurl: "http://example.com/",
url: "http://example.com/rss.xml", url: "http://example.com/rss.xml",
modified_date: "aftttt", text: "instance foo",
created_date: "adddb", type: "link",
opml_title: "opml foo" opml_title: "opml foo",
created_date: "Thu, 12 Sep 2003 23:35:52 GMT",
modified_date: "Fri, 12 Sep 2003 23:45:37 GMT"
},
parent_id = generateHash(test.sub_opml_storage.url),
opml_item_id = generateHash(parent_id + doc_id),
opml_item = {
name: doc_id,
opml_title: doc.opml_title,
parent_id: parent_id,
reference: generateHash(parent_id + doc_id),
creation_date: doc.created_date,
title: doc.title,
type: "opml-item",
url: test.sub_opml_storage.url,
signature: generateHash(JSON.stringify(doc))
}, },
blob = new Blob([JSON.stringify(doc)]), full_opml = new Blob([JSON.stringify(doc)]),
rss_id = "1102345", rss_id = "1102345",
rss_doc = { rss_doc = {
date: "Tue, 29 Aug 2006 09:00:00 -0400", "link": "http://www.domain.com/link.htm",
description: "This is an example of an Item", "date": "Tue, 29 Aug 2006 09:00:00 -0400",
guid: "1102345", "title": "Item Example",
lastBuildDate: "Mon, 28 Aug 2006 11:12:55 -0400 ", "category": "ERROR",
reference: "This is an example of an RSS feed", "description": "This is an example of an Item",
siteTitle: "RSS Example", "guid": "1102345",
title: "Item Example", "siteTitle": "instance foo",
status: "OK" "reference": "This is an example of an RSS feed",
"siteLink": "http://www.domain.com/link.htm",
"lastBuildDate": "Mon, 28 Aug 2006 11:12:55 -0400 "
}, },
rss_blob = new Blob([JSON.stringify(rss_doc)]), // Sub OPML document (rss)
json_id = "promise_runner.status", rss_feed_url = test.sub_opml_storage.sub_storage_list[0].url,
rss_item_id = generateHash(opml_item.reference + rss_feed_url + rss_id),
rss_item2 = {
name: rss_id,
opml_title: opml_item.opml_title,
parent_title: opml_item.title,
parent_id: opml_item.reference,
reference: rss_item_id,
title: rss_doc.title,
type: rss_doc.type || "rssmock-item",
url: rss_feed_url,
status: rss_doc.category,
creation_date: rss_doc.date
},
rss_doc2 = JSON.parse(JSON.stringify(rss_doc)),
full_rss2,
json_id = "monitor.status",
json_doc = { json_doc = {
title: "promise fooo", title: "document fooo",
status: "ERROR", status: "ERROR",
date: "Tue, 29 Aug 2006 09:00:00 -0400", date: "Tue, 29 Aug 2006 09:00:00 -0400",
type: "promise", type: "global",
foo_p: "fooo parameter", foo_p: "fooo parameter",
bar_p: "bar parameter" bar_p: "bar parameter",
total_error: 12345
}, },
json_blob = new Blob([JSON.stringify(json_doc)]), // Sub OPML document (webhttp)
opml_gen, http_url = "http://example.com/",
rss_gen, json_item_id = generateHash(opml_item.reference + http_url + json_id),
json_gen; json_item = {
name: json_id,
opml_gen = getOpmlElement(doc, doc_id, test.sub_opml_storage.url); opml_title: opml_item.opml_title,
rss_gen = getSubOpmlElement( parent_title: opml_item.title,
rss_doc, parent_id: opml_item.reference,
rss_id, reference: json_item_id,
opml_gen.doc, title: json_doc.title,
test.sub_opml_storage.sub_storage_list[0].url, type: json_doc.type,
"rssmock" url: http_url,
); status: json_doc.status,
json_gen = getSubOpmlElement( creation_date: json_doc.date,
json_doc, signature: generateHash(JSON.stringify(json_doc))
json_id, },
opml_gen.doc, full_json = new Blob([JSON.stringify(json_doc)]);
test.sub_opml_storage.sub_storage_list[1].url,
"webmock"
);
// initialise this storage here so we can put data
key = generateHash(test.sub_opml_storage.url);
this.jio.__storage._remote_storage_dict[key] = jIO.createJIO({
type: "opmlmock",
options: this.opml_mock_options
});
putFullDoc(this.jio.__storage._remote_storage_dict[key], doc_id, doc) /* Update rss document */
.then(function () { rss_doc2.date = "new rss date";
// put rss doc // new signature
key = generateHash(opml_gen.doc.reference + rss_item2.signature = generateHash(JSON.stringify(rss_doc2));
test.sub_opml_storage.sub_storage_list[0].url); // modified date
test.jio.__storage._remote_storage_dict[key] = jIO.createJIO({ rss_item2.creation_date = rss_doc2.date;
type: "rssmock", // get the full rss item
options: test.rss_mock_options full_rss2 = new Blob([JSON.stringify(rss_doc2)]);
});
return putFullDoc(test.jio.__storage._remote_storage_dict[key], test.jio.repair()
rss_id, rss_doc);
})
.then(function () {
// put json doc
key = generateHash(opml_gen.doc.reference +
test.sub_opml_storage.sub_storage_list[1].url);
test.jio.__storage._remote_storage_dict[key] = jIO.createJIO({
type: "webmock",
options: test.web_mock_options
});
return putFullDoc(test.jio.__storage._remote_storage_dict[key],
json_id, json_doc);
})
.then(function () { .then(function () {
test.rss_mock_options.mock.buildQuery = function () {
return [{id: rss_id, doc: rss_doc2, value: {}}];
};
resetCount(test.opml_mock_options.count); resetCount(test.opml_mock_options.count);
resetCount(test.rss_mock_options.count); resetCount(test.rss_mock_options.count);
resetCount(test.web_mock_options.count); resetCount(test.web_mock_options.count);
...@@ -475,9 +564,9 @@ ...@@ -475,9 +564,9 @@
}) })
.then(function () { .then(function () {
return RSVP.all([ return RSVP.all([
equalStorage(test.jio, [[opml_gen.id, opml_gen.doc, blob], equalStorage(test.jio, [[opml_item_id, opml_item, full_opml],
[rss_gen.id, rss_gen.doc, rss_blob], [rss_item_id, rss_item2, full_rss2],
[json_gen.id, json_gen.doc, json_blob]]), [json_item_id, json_item, full_json]]),
equalsubStorageCallCount( equalsubStorageCallCount(
test.opml_mock_options.count, test.opml_mock_options.count,
{buildQuery: 1} {buildQuery: 1}
...@@ -502,83 +591,120 @@ ...@@ -502,83 +591,120 @@
}); });
/////////////////////////////////////////////////////// ///////////////////////////////////////////////////////
// document update // remote document deleted - non exist in result
/////////////////////////////////////////////////////// ///////////////////////////////////////////////////////
test("remote document modified", function () { test("remote document deleted: empty result", function () {
expect(4); expect(5);
stop(); stop();
var test = this, var test = this,
key, doc_id = "http://example.com/rss.xml",
doc_id = 'opml_foo',
doc = { doc = {
title: "opml item foo", title: "opml item foo",
htmlurl: "http://example.com/",
url: "http://example.com/rss.xml", url: "http://example.com/rss.xml",
modified_date: "aftttt", text: "instance foo",
created_date: "adddb", type: "link",
opml_title: "opml foo" opml_title: "opml foo",
created_date: "Thu, 12 Sep 2003 23:35:52 GMT",
modified_date: "Fri, 12 Sep 2003 23:45:37 GMT"
},
parent_id = generateHash(test.sub_opml_storage.url),
opml_item_id = generateHash(parent_id + doc_id),
opml_item = {
name: doc_id,
opml_title: doc.opml_title,
parent_id: parent_id,
reference: generateHash(parent_id + doc_id),
creation_date: doc.created_date,
title: doc.title,
type: "opml-item",
url: test.sub_opml_storage.url,
signature: generateHash(JSON.stringify(doc))
}, },
blob = new Blob([JSON.stringify(doc)]), full_opml = new Blob([JSON.stringify(doc)]),
rss_id = "1102345", rss_id = "1102345",
rss_doc = { rss_doc = {
"link": "http://www.domain.com/link.htm",
"date": "Tue, 29 Aug 2006 09:00:00 -0400",
"title": "Item Example",
"category": "ERROR",
"description": "This is an example of an Item",
"guid": "1102345",
"siteTitle": "instance foo",
"reference": "This is an example of an RSS feed",
"siteLink": "http://www.domain.com/link.htm",
"lastBuildDate": "Mon, 28 Aug 2006 11:12:55 -0400 "
},
// Sub OPML document (rss)
rss_feed_url = "http://example.com/rss.xml",
rss_item_id = generateHash(opml_item.reference + rss_feed_url + rss_id),
rss_item = {
name: rss_id,
opml_title: opml_item.opml_title,
parent_title: opml_item.title,
parent_id: opml_item.reference,
reference: rss_item_id,
title: rss_doc.title,
type: rss_doc.type || "rssmock-item",
url: rss_feed_url,
status: rss_doc.category,
creation_date: rss_doc.date,
signature: generateHash(JSON.stringify(rss_doc))
},
full_rss = new Blob([JSON.stringify(rss_doc)]),
json_id = "monitor.status",
json_doc = {
title: "document fooo",
status: "ERROR",
date: "Tue, 29 Aug 2006 09:00:00 -0400", date: "Tue, 29 Aug 2006 09:00:00 -0400",
description: "This is an example of an Item", type: "global",
guid: "1102345", foo_p: "fooo parameter",
lastBuildDate: "Mon, 28 Aug 2006 11:12:55 -0400 ", bar_p: "bar parameter",
reference: "This is an example of an RSS feed", total_error: 12345
siteTitle: "RSS Example",
title: "Item Example",
status: "OK"
}, },
opml_gen, // Sub OPML document (webhttp)
rss_gen, http_url = "http://example.com/",
rss_doc2 = JSON.parse(JSON.stringify(rss_doc)), json_item_id = generateHash(opml_item.reference + http_url + json_id),
rss_blob2 = new Blob([JSON.stringify(rss_doc2)]); json_item = {
name: json_id,
opml_gen = getOpmlElement(doc, doc_id, test.sub_opml_storage.url); opml_title: opml_item.opml_title,
rss_gen = getSubOpmlElement( parent_title: opml_item.title,
rss_doc2, parent_id: opml_item.reference,
rss_id, reference: json_item_id,
opml_gen.doc, title: json_doc.title,
test.sub_opml_storage.sub_storage_list[0].url, type: json_doc.type,
"rssmock" url: http_url,
); status: json_doc.status,
creation_date: json_doc.date,
// initialise this storage here so we can put data signature: generateHash(JSON.stringify(json_doc))
key = generateHash(test.sub_opml_storage.url); },
this.jio.__storage._remote_storage_dict[key] = jIO.createJIO({ full_json = new Blob([JSON.stringify(json_doc)]);
type: "opmlmock",
options: this.opml_mock_options
});
putFullDoc(this.jio.__storage._remote_storage_dict[key], doc_id, doc) new RSVP.Queue()
.then(function () {
// put rss doc
key = generateHash(opml_gen.doc.reference +
test.sub_opml_storage.sub_storage_list[0].url);
test.jio.__storage._remote_storage_dict[key] = jIO.createJIO({
type: "rssmock",
options: test.rss_mock_options
});
return putFullDoc(test.jio.__storage._remote_storage_dict[key],
rss_id, rss_doc);
})
.then(function () { .then(function () {
return test.jio.repair(); return test.jio.repair();
}) })
.then(function () { .then(function () {
return putFullDoc(test.jio.__storage._remote_storage_dict[key], return RSVP.all([
rss_id, rss_doc2); equalStorage(test.jio, [[opml_item_id, opml_item, full_opml],
[rss_item_id, rss_item, full_rss],
[json_item_id, json_item, full_json]])
]);
}) })
.then(function () { .then(function () {
test.rss_mock_options.mock.buildQuery = function () {
return [];
};
resetCount(test.opml_mock_options.count); resetCount(test.opml_mock_options.count);
resetCount(test.rss_mock_options.count); resetCount(test.rss_mock_options.count);
resetCount(test.web_mock_options.count);
return test.jio.repair(); return test.jio.repair();
}) })
.then(function () { .then(function () {
return RSVP.all([ return RSVP.all([
equalStorage(test.jio, [[opml_gen.id, opml_gen.doc, blob], equalStorage(test.jio, [[opml_item_id, opml_item, full_opml],
[rss_gen.id, rss_gen.doc, rss_blob2]]), [json_item_id, json_item, full_json]]),
equalsubStorageCallCount( equalsubStorageCallCount(
test.opml_mock_options.count, test.opml_mock_options.count,
{buildQuery: 1} {buildQuery: 1}
...@@ -589,12 +715,11 @@ ...@@ -589,12 +715,11 @@
), ),
equalsubStorageCallCount( equalsubStorageCallCount(
test.web_mock_options.count, test.web_mock_options.count,
{} {buildQuery: 1}
) )
]); ]);
}) })
.fail(function (error) { .fail(function (error) {
console.log(error);
ok(false, error); ok(false, error);
}) })
.always(function () { .always(function () {
...@@ -603,86 +728,126 @@ ...@@ -603,86 +728,126 @@
}); });
/////////////////////////////////////////////////////// ///////////////////////////////////////////////////////
// document remove // some document remove - id has changed
/////////////////////////////////////////////////////// ///////////////////////////////////////////////////////
test("remote document deleted", function () { test("remote document removed", function () {
expect(5); expect(5);
stop(); stop();
var test = this, var test = this,
key, doc_id = "http://example.com/rss.xml",
doc_id = 'opml_foo',
doc = { doc = {
title: "opml item foo", title: "opml item foo",
htmlurl: "http://example.com/",
url: "http://example.com/rss.xml", url: "http://example.com/rss.xml",
modified_date: "aftttt", text: "instance foo",
created_date: "adddb", type: "link",
opml_title: "opml foo" opml_title: "opml foo",
created_date: "Thu, 12 Sep 2003 23:35:52 GMT",
modified_date: "Fri, 12 Sep 2003 23:45:37 GMT"
},
parent_id = generateHash(test.sub_opml_storage.url),
opml_item_id = generateHash(parent_id + doc_id),
opml_item = {
name: doc_id,
opml_title: doc.opml_title,
parent_id: parent_id,
reference: generateHash(parent_id + doc_id),
creation_date: doc.created_date,
title: doc.title,
type: "opml-item",
url: test.sub_opml_storage.url,
signature: generateHash(JSON.stringify(doc))
}, },
blob = new Blob([JSON.stringify(doc)]), full_opml = new Blob([JSON.stringify(doc)]),
rss_id = "1102345", rss_id = "1102345",
rss_doc = { rss_doc = {
"link": "http://www.domain.com/link.htm",
"date": "Tue, 29 Aug 2006 09:00:00 -0400",
"title": "Item Example",
"category": "ERROR",
"description": "This is an example of an Item",
"guid": "1102345",
"siteTitle": "instance foo",
"reference": "This is an example of an RSS feed",
"siteLink": "http://www.domain.com/link.htm",
"lastBuildDate": "Mon, 28 Aug 2006 11:12:55 -0400 "
},
// Sub OPML document (rss)
rss_feed_url = "http://example.com/rss.xml",
rss_item_id = generateHash(opml_item.reference + rss_feed_url + rss_id),
rss_item = {
name: rss_id,
opml_title: opml_item.opml_title,
parent_title: opml_item.title,
parent_id: opml_item.reference,
reference: rss_item_id,
title: rss_doc.title,
type: rss_doc.type || "rssmock-item",
url: rss_feed_url,
status: rss_doc.category,
creation_date: rss_doc.date,
signature: generateHash(JSON.stringify(rss_doc))
},
full_rss = new Blob([JSON.stringify(rss_doc)]),
json_id = "monitor.status",
json_doc = {
title: "document fooo",
status: "ERROR",
date: "Tue, 29 Aug 2006 09:00:00 -0400", date: "Tue, 29 Aug 2006 09:00:00 -0400",
description: "This is an example of an Item", type: "global",
guid: "1102345", foo_p: "fooo parameter",
lastBuildDate: "Mon, 28 Aug 2006 11:12:55 -0400 ", bar_p: "bar parameter",
reference: "This is an example of an RSS feed", total_error: 12345
siteTitle: "RSS Example",
title: "Item Example",
status: "OK"
}, },
rss_blob = new Blob([JSON.stringify(rss_doc)]), // Sub OPML document (webhttp)
opml_gen, http_url = "http://example.com/",
rss_gen; json_item_id = generateHash(opml_item.reference + http_url + json_id),
json_item = {
opml_gen = getOpmlElement(doc, doc_id, test.sub_opml_storage.url); name: json_id,
rss_gen = getSubOpmlElement( opml_title: opml_item.opml_title,
rss_doc, parent_title: opml_item.title,
rss_id, parent_id: opml_item.reference,
opml_gen.doc, reference: json_item_id,
test.sub_opml_storage.sub_storage_list[0].url, title: json_doc.title,
"rssmock" type: json_doc.type,
); url: http_url,
status: json_doc.status,
// initialise this storage here so we can put data creation_date: json_doc.date,
key = generateHash(test.sub_opml_storage.url); signature: generateHash(JSON.stringify(json_doc))
this.jio.__storage._remote_storage_dict[key] = jIO.createJIO({ },
type: "opmlmock", full_json = new Blob([JSON.stringify(json_doc)]),
options: this.opml_mock_options /* rss doc 2 with different id */
}); rss_id2 = "1102345-new",
rss_item2_id = generateHash(opml_item.reference + rss_feed_url + rss_id2),
rss_item2 = JSON.parse(JSON.stringify(rss_item));
putFullDoc(this.jio.__storage._remote_storage_dict[key], doc_id, doc) rss_item2.name = rss_id2;
.then(function () { rss_item2.reference = rss_item2_id;
// put rss doc
key = generateHash(opml_gen.doc.reference + test.jio.repair()
test.sub_opml_storage.sub_storage_list[0].url);
test.jio.__storage._remote_storage_dict[key] = jIO.createJIO({
type: "rssmock",
options: test.rss_mock_options
});
return putFullDoc(test.jio.__storage._remote_storage_dict[key],
rss_id, rss_doc);
})
.then(function () {
return test.jio.repair();
})
.then(function () { .then(function () {
return RSVP.all([ return RSVP.all([
equalStorage(test.jio, [[opml_gen.id, opml_gen.doc, blob], equalStorage(test.jio, [[opml_item_id, opml_item, full_opml],
[rss_gen.id, rss_gen.doc, rss_blob]]) [rss_item_id, rss_item, full_rss],
[json_item_id, json_item, full_json]])
]); ]);
}) })
.then(function () { .then(function () {
test.rss_mock_options.mock.buildQuery = function () { test.rss_mock_options.mock.buildQuery = function () {
return []; // return a different document
return [{id: rss_id2, doc: rss_doc, value: {}}];
}; };
resetCount(test.opml_mock_options.count); resetCount(test.opml_mock_options.count);
resetCount(test.rss_mock_options.count); resetCount(test.rss_mock_options.count);
resetCount(test.web_mock_options.count);
return test.jio.repair(); return test.jio.repair();
}) })
.then(function () { .then(function () {
return RSVP.all([ return RSVP.all([
equalStorage(test.jio, [[opml_gen.id, opml_gen.doc, blob]]), equalStorage(test.jio, [[opml_item_id, opml_item, full_opml],
[json_item_id, json_item, full_json],
[rss_item2_id, rss_item2, full_rss]]),
equalsubStorageCallCount( equalsubStorageCallCount(
test.opml_mock_options.count, test.opml_mock_options.count,
{buildQuery: 1} {buildQuery: 1}
...@@ -693,7 +858,7 @@ ...@@ -693,7 +858,7 @@
), ),
equalsubStorageCallCount( equalsubStorageCallCount(
test.web_mock_options.count, test.web_mock_options.count,
{} {buildQuery: 1}
) )
]); ]);
}) })
...@@ -707,67 +872,77 @@ ...@@ -707,67 +872,77 @@
}); });
/////////////////////////////////////////////////////// ///////////////////////////////////////////////////////
// complete sync - many opml (2 opmls, 4 sub storages) // complete sync - 2 opmls, 3 sub storages
/////////////////////////////////////////////////////// ///////////////////////////////////////////////////////
test("multi opml storage sync", function () { test("multi opml storage sync", function () {
expect(7); expect(6);
stop(); stop();
var test = this, var test = this,
key, doc_id = "http://example.com/rss.xml",
doc_id = 'opml_foo',
doc = { doc = {
title: "opml item foo", title: "opml item foo",
htmlurl: "http://example.com/",
url: "http://example.com/rss.xml", url: "http://example.com/rss.xml",
modified_date: "aftttt", text: "instance foo",
created_date: "adddb", type: "link",
opml_title: "opml foo" opml_title: "opml foo",
created_date: "Thu, 12 Sep 2003 23:35:52 GMT",
modified_date: "Fri, 12 Sep 2003 23:45:37 GMT"
}, },
blob = new Blob([JSON.stringify(doc)]), full_opml = new Blob([JSON.stringify(doc)]),
rss_id = "1102345", rss_id = "1102345",
rss_doc = { rss_doc = {
date: "Tue, 29 Aug 2006 09:00:00 -0400", "link": "http://www.domain.com/link.htm",
description: "This is an example of an Item", "date": "Tue, 29 Aug 2006 09:00:00 -0400",
guid: "1102345", "title": "Item Example",
lastBuildDate: "Mon, 28 Aug 2006 11:12:55 -0400 ", "category": "ERROR",
reference: "This is an example of an RSS feed", "description": "This is an example of an Item",
siteTitle: "RSS Example", "guid": "1102345",
title: "Item Example", "siteTitle": "instance foo",
status: "OK" "reference": "This is an example of an RSS feed",
"siteLink": "http://www.domain.com/link.htm",
"lastBuildDate": "Mon, 28 Aug 2006 11:12:55 -0400 "
}, },
rss_blob = new Blob([JSON.stringify(rss_doc)]), full_rss = new Blob([JSON.stringify(rss_doc)]),
json_id = "promise_runner.status", json_id = "monitor.status",
json_doc = { json_doc = {
title: "promise fooo", title: "document fooo",
status: "ERROR", status: "ERROR",
date: "Tue, 29 Aug 2006 09:00:00 -0400", date: "Tue, 29 Aug 2006 09:00:00 -0400",
type: "promise", type: "global",
foo_p: "fooo parameter", foo_p: "fooo parameter",
bar_p: "bar parameter" bar_p: "bar parameter",
total_error: 12345
}, },
json_blob = new Blob([JSON.stringify(json_doc)]), full_json = new Blob([JSON.stringify(json_doc)]),
gen_dict = {}; item_dict = {},
rss_url = "http://example.com/rss.xml",
rss2_url = "http://example2.com/rss.xml",
http_url = "http://example.com/";
// update storage with 2 opmls // update storage with 2 opmls
// opml2 has only rss feed substorage
this.sub_opml_storage2 = { this.sub_opml_storage2 = {
type: "opmlmock", type: "opmlmock",
options: JSON.parse(JSON.stringify(this.opml_mock_options)), options: {
mock: {
},
count: {}
},
url: "http://example2.com/opml.xml", url: "http://example2.com/opml.xml",
sub_storage_list: [ sub_storage_list: [
{ {
type: "rssmock", type: "rssmock",
url: "http://example2.com/rss.xml", url: "http://example2.com/rss.xml",
has_include_docs: true, has_include_docs: true,
options: JSON.parse(JSON.stringify(this.rss_mock_options)) options: {
}, mock: {
{ },
type: "webmock", count: {}
url: "http://example2.com/data/", }
has_include_docs: true,
options: JSON.parse(JSON.stringify(this.web_mock_options))
} }
], ]
basic_login: "YWRtaW46endfEzrJUZGw="
}; };
this.jio = jIO.createJIO({ this.jio = jIO.createJIO({
type: "replicatedopml", type: "replicatedopml",
...@@ -786,117 +961,83 @@ ...@@ -786,117 +961,83 @@
} }
}); });
gen_dict.opml = getOpmlElement(doc, doc_id, test.sub_opml_storage.url); /* Expected item in indexeddb*/
gen_dict.opml2 = getOpmlElement(doc, doc_id, test.sub_opml_storage2.url); item_dict.opml = {
gen_dict.rss = getSubOpmlElement( parent_id: generateHash(test.sub_opml_storage.url),
rss_doc, reference: generateHash(generateHash(test.sub_opml_storage.url) + doc_id),
rss_id, name: doc_id,
gen_dict.opml.doc, opml_title: doc.opml_title,
test.sub_opml_storage.sub_storage_list[0].url, creation_date: doc.created_date,
"rssmock" title: doc.title,
); type: "opml-item",
gen_dict.json = getSubOpmlElement( url: test.sub_opml_storage.url,
json_doc, signature: generateHash(JSON.stringify(doc))
json_id, };
gen_dict.opml.doc, item_dict.opml2 = {
test.sub_opml_storage.sub_storage_list[1].url, parent_id: generateHash(test.sub_opml_storage2.url),
"webmock" reference: generateHash(
); generateHash(test.sub_opml_storage2.url) + doc_id
gen_dict.rss2 = getSubOpmlElement( ),
rss_doc, name: doc_id,
rss_id, opml_title: doc.opml_title,
gen_dict.opml2.doc, creation_date: doc.created_date,
test.sub_opml_storage2.sub_storage_list[0].url, title: doc.title,
"rssmock" type: "opml-item",
); url: test.sub_opml_storage2.url,
gen_dict.json2 = getSubOpmlElement( signature: generateHash(JSON.stringify(doc))
json_doc, };
json_id, item_dict.rss = {
gen_dict.opml2.doc, name: rss_id,
test.sub_opml_storage2.sub_storage_list[1].url, opml_title: item_dict.opml.opml_title,
"webmock" parent_title: item_dict.opml.title,
); parent_id: item_dict.opml.reference,
reference: generateHash(item_dict.opml.reference + rss_url + rss_id),
// initialise this storage here so we can put data title: rss_doc.title,
key = generateHash(test.sub_opml_storage.url); type: rss_doc.type || "rssmock-item",
this.jio.__storage._remote_storage_dict[key] = jIO.createJIO({ url: rss_url,
type: "opmlmock", status: rss_doc.category,
options: this.opml_mock_options creation_date: rss_doc.date,
}); signature: generateHash(JSON.stringify(rss_doc))
};
item_dict.rss2 = {
name: rss_id,
opml_title: item_dict.opml2.opml_title,
parent_title: item_dict.opml2.title,
parent_id: item_dict.opml2.reference,
reference: generateHash(item_dict.opml2.reference + rss2_url + rss_id),
title: rss_doc.title,
type: "rssmock-item",
url: rss2_url,
status: rss_doc.category,
creation_date: rss_doc.date,
signature: generateHash(JSON.stringify(rss_doc))
};
item_dict.json = {
name: json_id,
opml_title: item_dict.opml.opml_title,
parent_title: item_dict.opml.title,
parent_id: item_dict.opml.reference,
reference: generateHash(item_dict.opml.reference + http_url + json_id),
title: json_doc.title,
type: json_doc.type,
url: http_url,
status: json_doc.status,
creation_date: json_doc.date,
signature: generateHash(JSON.stringify(json_doc))
};
putFullDoc(this.jio.__storage._remote_storage_dict[key], doc_id, doc) new RSVP.Queue()
.then(function () { .then(function () {
// put second opml doc
key = generateHash(test.sub_opml_storage2.url);
test.jio.__storage._remote_storage_dict[key] = jIO.createJIO({
type: "opmlmock",
options: test.sub_opml_storage2.options
});
return putFullDoc(test.jio.__storage._remote_storage_dict[key],
doc_id, doc);
})
.then(function () {
// put rss doc1
key = generateHash(gen_dict.opml.doc.reference +
test.sub_opml_storage.sub_storage_list[0].url);
test.jio.__storage._remote_storage_dict[key] = jIO.createJIO({
type: "rssmock",
options: test.rss_mock_options
});
return putFullDoc(test.jio.__storage._remote_storage_dict[key],
rss_id, rss_doc);
})
.then(function () {
// put json doc1
key = generateHash(gen_dict.opml.doc.reference +
test.sub_opml_storage.sub_storage_list[1].url);
test.jio.__storage._remote_storage_dict[key] = jIO.createJIO({
type: "webmock",
options: test.web_mock_options
});
return putFullDoc(test.jio.__storage._remote_storage_dict[key],
json_id, json_doc);
})
.then(function () {
// put rss doc2
key = generateHash(gen_dict.opml2.doc.reference +
test.sub_opml_storage2.sub_storage_list[0].url);
test.jio.__storage._remote_storage_dict[key] = jIO.createJIO({
type: "rssmock",
options: test.sub_opml_storage2.sub_storage_list[0].options
});
return putFullDoc(test.jio.__storage._remote_storage_dict[key],
rss_id, rss_doc);
})
.then(function () {
// put json doc2
key = generateHash(gen_dict.opml2.doc.reference +
test.sub_opml_storage2.sub_storage_list[1].url);
test.jio.__storage._remote_storage_dict[key] = jIO.createJIO({
type: "webmock",
options: test.sub_opml_storage2.sub_storage_list[1].options
});
return putFullDoc(test.jio.__storage._remote_storage_dict[key],
json_id, json_doc);
})
.then(function () {
resetCount(test.opml_mock_options.count);
resetCount(test.rss_mock_options.count);
resetCount(test.web_mock_options.count);
resetCount(test.sub_opml_storage2.options.count);
resetCount(test.sub_opml_storage2.sub_storage_list[0].options.count);
resetCount(test.sub_opml_storage2.sub_storage_list[1].options.count);
return test.jio.repair(); return test.jio.repair();
}) })
.then(function () { .then(function () {
return RSVP.all([ return RSVP.all([
equalStorage(test.jio, equalStorage(test.jio,
[[gen_dict.opml.id, gen_dict.opml.doc, blob], [[item_dict.opml.reference, item_dict.opml, full_opml],
[gen_dict.opml2.id, gen_dict.opml2.doc, blob], [item_dict.rss.reference, item_dict.rss, full_rss],
[gen_dict.rss.id, gen_dict.rss.doc, rss_blob], [item_dict.json.reference, item_dict.json, full_json],
[gen_dict.rss2.id, gen_dict.rss2.doc, rss_blob], [item_dict.opml2.reference, item_dict.opml2, full_opml],
[gen_dict.json.id, gen_dict.json.doc, json_blob], [item_dict.rss2.reference, item_dict.rss2, full_rss]]
[gen_dict.json2.id, gen_dict.json2.doc, json_blob]]
), ),
equalsubStorageCallCount( equalsubStorageCallCount(
test.opml_mock_options.count, test.opml_mock_options.count,
...@@ -917,10 +1058,6 @@ ...@@ -917,10 +1058,6 @@
equalsubStorageCallCount( equalsubStorageCallCount(
test.sub_opml_storage2.sub_storage_list[0].options.count, test.sub_opml_storage2.sub_storage_list[0].options.count,
{buildQuery: 1} {buildQuery: 1}
),
equalsubStorageCallCount(
test.sub_opml_storage2.sub_storage_list[1].options.count,
{buildQuery: 1}
) )
]); ]);
}) })
...@@ -933,4 +1070,4 @@ ...@@ -933,4 +1070,4 @@
}); });
}); });
}(QUnit, jIO, Blob, Rusha, console)); }(QUnit, jIO, Blob, Rusha, sinon, console));
\ No newline at end of file \ No newline at end of file
...@@ -14,15 +14,15 @@ ...@@ -14,15 +14,15 @@
function getOpmlOutlineAsDict(outline) { function getOpmlOutlineAsDict(outline) {
var outline_dict = { var outline_dict = {
title: outline.getAttribute('title') || '', title: outline.getAttribute('title') || dummy.textContent,
htmlurl: outline.getAttribute('htmlUrl') || '', htmlurl: outline.getAttribute('htmlUrl') || dummy.textContent,
xmlurl: outline.getAttribute('xmlUrl') || '', xmlurl: outline.getAttribute('xmlUrl') || dummy.textContent,
url: outline.getAttribute('url') || '', url: outline.getAttribute('url') || dummy.textContent,
text: outline.getAttribute('text') || '', text: outline.getAttribute('text') || dummy.textContent,
type: outline.getAttribute('type') || '', type: outline.getAttribute('type') || dummy.textContent,
version: outline.getAttribute('version') || '', version: outline.getAttribute('version') || dummy.textContent,
created: outline.getAttribute('created') || '', created: outline.getAttribute('created') || dummy.textContent,
category: outline.getAttribute('category') || '' category: outline.getAttribute('category') || dummy.textContent
}; };
return outline_dict; return outline_dict;
} }
...@@ -122,6 +122,9 @@ ...@@ -122,6 +122,9 @@
.push(function (response) { .push(function (response) {
var element, var element,
result; result;
if (!response.target.responseText) {
throw new jIO.util.jIOError("Cannot find document", 404);
}
element = new DOMParser().parseFromString( element = new DOMParser().parseFromString(
response.target.responseText, response.target.responseText,
"text/xml" "text/xml"
......
...@@ -122,7 +122,11 @@ ...@@ -122,7 +122,11 @@
};*/ };*/
ReplicatedOPMLStorage.prototype.hasCapacity = function (capacity) { ReplicatedOPMLStorage.prototype.hasCapacity = function (capacity) {
return (capacity === "list") || (capacity === "include"); if (capacity === 'include') {
return true;
}
return this._local_sub_storage.hasCapacity.apply(this._local_sub_storage,
arguments);
}; };
ReplicatedOPMLStorage.prototype.getAttachment = function () { ReplicatedOPMLStorage.prototype.getAttachment = function () {
...@@ -135,10 +139,8 @@ ...@@ -135,10 +139,8 @@
arguments); arguments);
}; };
function getSubOpmlStorageDescription(spec, opml_doc, basic_login) { function getSubOpmlStorageDescription(storage_spec, opml_doc, basic_login) {
var storage_spec;
storage_spec = JSON.parse(JSON.stringify(spec));
if (storage_spec.basic_login === undefined && basic_login !== undefined) { if (storage_spec.basic_login === undefined && basic_login !== undefined) {
storage_spec.basic_login = basic_login; storage_spec.basic_login = basic_login;
} }
...@@ -146,7 +148,6 @@ ...@@ -146,7 +148,6 @@
if (storage_spec.url_attribute !== undefined && if (storage_spec.url_attribute !== undefined &&
opml_doc.hasOwnProperty(storage_spec.url_attribute)) { opml_doc.hasOwnProperty(storage_spec.url_attribute)) {
storage_spec.url = opml_doc[storage_spec.url_attribute]; storage_spec.url = opml_doc[storage_spec.url_attribute];
delete storage_spec.url_attribute;
} else if (storage_spec.url_path !== undefined) { } else if (storage_spec.url_path !== undefined) {
storage_spec.url_path = storage_spec.url_path.replace( storage_spec.url_path = storage_spec.url_path.replace(
new RegExp("^[/]+"), new RegExp("^[/]+"),
...@@ -156,7 +157,6 @@ ...@@ -156,7 +157,6 @@
new RegExp("[/]+$"), new RegExp("[/]+$"),
"" ""
) + "/" + storage_spec.url_path; ) + "/" + storage_spec.url_path;
delete storage_spec.url_path;
} }
// XXX - for compatibility, remove url with jio_private path // XXX - for compatibility, remove url with jio_private path
storage_spec.url = storage_spec.url.replace("jio_private", "private"); storage_spec.url = storage_spec.url.replace("jio_private", "private");
...@@ -215,6 +215,7 @@ ...@@ -215,6 +215,7 @@
parent_title: opml_doc.title, parent_title: opml_doc.title,
opml_title: opml_doc.opml_title, opml_title: opml_doc.opml_title,
type: storage_spec.type, type: storage_spec.type,
current_doc: {},
result: { result: {
data: { data: {
total_rows: 0 total_rows: 0
...@@ -223,16 +224,35 @@ ...@@ -223,16 +224,35 @@
url: url url: url
}; };
return sub_storage.allDocs(options) return sub_storage.allDocs(options)
.push(function (result) { .push(undefined, function (error) {
result_dict.result = result;
return result_dict;
}, function (error) {
if ((error instanceof jIO.util.jIOError) && if ((error instanceof jIO.util.jIOError) &&
(error.status_code === 404)) { (error.status_code === 404)) {
console.log(error); console.log(error);
return result_dict; return undefined;
} }
throw error; //throw error;
// throw will cancel all others allDocs, this is not wanted
console.log(error);
return undefined;
})
.push(function (result) {
if (result === undefined) {
return {data: {total_rows: 0}};
}
result_dict.result = result;
return context._local_sub_storage.allDocs({
select_list: ["signature"],
query: '(parent_id: "' + parent_id + '") AND (url:"' + url + '")'
});
})
.push(function (all_document) {
var i;
for (i = 0; i < all_document.data.total_rows; i += 1) {
result_dict.current_doc[
all_document.data.rows[i].id
] = all_document.data.rows[i].value;
}
return result_dict;
}); });
} }
...@@ -240,6 +260,8 @@ ...@@ -240,6 +260,8 @@
var opml_storage, var opml_storage,
opml_document_list = [], opml_document_list = [],
document_attachment_dict = {}, document_attachment_dict = {},
delete_key_list = [],
current_opml_dict = {},
id; id;
id = generateHash(opml_url); id = generateHash(opml_url);
...@@ -248,40 +270,44 @@ ...@@ -248,40 +270,44 @@
.push(undefined, function (error) { .push(undefined, function (error) {
if ((error instanceof jIO.util.jIOError) && if ((error instanceof jIO.util.jIOError) &&
(error.status_code === 404)) { (error.status_code === 404)) {
return [[], {}]; return {data: {total_rows: 0}};
}
//throw error;
// throw will cancel all remaning tasks
return {data: {total_rows: 0}};
})
.push(function (opml_result) {
return RSVP.all([
opml_result,
context._local_sub_storage.allDocs({
select_list: ["signature"],
query: '(parent_id: "' + id + '")'
})
]);
})
.push(function (result_list) {
var i;
for (i = 0; i < result_list[1].data.total_rows; i += 1) {
current_opml_dict[
result_list[1].data.rows[i].id
] = result_list[1].data.rows[i].value;
} }
throw error; return result_list[0];
}) })
.push(function (opml_result_list) { .push(function (opml_result_list) {
var j, var j,
i, i,
item, item,
signature,
skip_add = false,
id_hash, id_hash,
result_list = []; result_list = [];
for (i = 0; i < opml_result_list.data.total_rows; i += 1) { for (i = 0; i < opml_result_list.data.total_rows; i += 1) {
item = opml_result_list.data.rows[i]; item = opml_result_list.data.rows[i];
id_hash = generateHash(id + item.id); id_hash = generateHash(id + item.id);
signature = generateHash(JSON.stringify(item.doc));
opml_document_list.push({
id: id_hash,
doc: {
type: "opml-item",
name: item.id,
reference: id_hash,
parent_id: id,
creation_date: item.doc.created_date,
url: opml_url,
title: item.doc.title,
parent_title: undefined,
opml_title: item.doc.opml_title,
status: undefined
}
});
document_attachment_dict[id_hash] = {
name: item.id,
doc: item.doc
};
for (j = 0; j < opml_spec.sub_storage_list.length; j += 1) { for (j = 0; j < opml_spec.sub_storage_list.length; j += 1) {
result_list.push(loadSubStorage( result_list.push(loadSubStorage(
context, context,
...@@ -291,7 +317,40 @@ ...@@ -291,7 +317,40 @@
opml_spec.basic_login opml_spec.basic_login
)); ));
} }
if (current_opml_dict.hasOwnProperty(id_hash)) {
if (current_opml_dict[id_hash].signature === signature) {
// the document was not modified, delete and skip add
delete current_opml_dict[id_hash];
skip_add = true;
}
delete current_opml_dict[id_hash];
}
if (!skip_add) {
opml_document_list.push({
id: id_hash,
doc: {
type: "opml-item",
name: item.id,
reference: id_hash,
parent_id: id,
creation_date: item.doc.created_date,
url: opml_url,
title: item.doc.title,
parent_title: undefined,
opml_title: item.doc.opml_title,
status: undefined,
signature: signature
}
});
document_attachment_dict[id_hash] = {
name: item.id,
doc: item.doc
};
}
} }
delete_key_list.push.apply(delete_key_list,
Object.keys(current_opml_dict));
return RSVP.all(result_list); return RSVP.all(result_list);
}) })
.push(function (result_list) { .push(function (result_list) {
...@@ -300,7 +359,8 @@ ...@@ -300,7 +359,8 @@
function applyItemToTree(item, item_result) { function applyItemToTree(item, item_result) {
var id_hash, var id_hash,
element; element,
signature;
id_hash = generateHash(item_result.parent_id + id_hash = generateHash(item_result.parent_id +
item_result.url + item.id); item_result.url + item.id);
...@@ -309,6 +369,19 @@ ...@@ -309,6 +369,19 @@
} else { } else {
element = item.value; element = item.value;
} }
// Generating document signature
signature = generateHash(JSON.stringify(element));
if (item_result.current_doc.hasOwnProperty(id_hash)) {
if (item_result.current_doc[id_hash].signature === signature) {
// the document was not modified delete and return
delete item_result.current_doc[id_hash];
return;
}
// the document exists and has changed
delete item_result.current_doc[id_hash];
}
opml_document_list.push({ opml_document_list.push({
id: id_hash, id: id_hash,
doc: { doc: {
...@@ -321,7 +394,8 @@ ...@@ -321,7 +394,8 @@
status: (element.status || element.category), status: (element.status || element.category),
title: (element.source || element.title), title: (element.source || element.title),
parent_title: item_result.parent_title, parent_title: item_result.parent_title,
opml_title: item_result.opml_title opml_title: item_result.opml_title,
signature: signature
} }
}); });
document_attachment_dict[id_hash] = { document_attachment_dict[id_hash] = {
...@@ -337,112 +411,106 @@ ...@@ -337,112 +411,106 @@
result_list[i] result_list[i]
); );
} }
delete_key_list.push.apply(delete_key_list,
Object.keys(result_list[i].current_doc));
} }
return [opml_document_list, document_attachment_dict]; return [opml_document_list, document_attachment_dict, delete_key_list];
}); });
} }
function syncOpmlStorage(context) { function pushDocumentToStorage(context, document_list, attachment_dict,
var i, delete_key_list) {
promise_list = []; var document_queue = new RSVP.Queue(),
for (i = 0; i < context._opml_storage_list.length; i += 1) { i;
promise_list.push(
getOpmlTree(context, function pushDocument(id, element, attachment) {
context._opml_storage_list[i].url, document_queue
context._opml_storage_list[i]) .push(function () {
); return context._local_sub_storage.put(id, element);
})
.push(function () {
return context._local_sub_storage.putAttachment(
id,
attachment.name,
new Blob([JSON.stringify(attachment.doc)])
);
});
} }
return RSVP.all(promise_list);
}
function repairLocalStorage(context, new_document_list, document_key_list) { for (i = 0; i < document_list.length; i += 1) {
var j, pushDocument(
promise_list = []; document_list[i].id,
document_list[i].doc,
attachment_dict[document_list[i].id]
);
}
return document_queue
.push(function () {
var k,
remove_queue = new RSVP.Queue();
// remove all document which were not updated
function removeDocument(key) {
remove_queue
.push(function () {
return context._local_sub_storage.get(key);
})
.push(undefined, function (error) {
throw error;
})
.push(function (element) {
return context._local_sub_storage.removeAttachment(
key,
element.name
);
})
.push(function () {
return context._local_sub_storage.remove(key);
})
.push(undefined, function (error) {
if ((error instanceof jIO.util.jIOError) &&
(error.status_code === 404)) {
return {};
}
throw error;
});
}
function pushDocumentToStorage(document_list, attachment_dict) { for (k = 0; k < delete_key_list.length; k += 1) {
var document_queue = new RSVP.Queue(), removeDocument(delete_key_list[k]);
doc_index, }
i; return remove_queue;
});
}
function pushDocument(id, element, attachment) { function syncOpmlStorage(context) {
document_queue var i,
.push(function () { opml_queue = new RSVP.Queue();
return context._local_sub_storage.put(id, element);
})
.push(function () {
return context._local_sub_storage.putAttachment(
id,
attachment.name,
new Blob([JSON.stringify(attachment.doc)])
);
});
}
for (i = 0; i < document_list.length; i += 1) { function syncFullOpml(url, storage_spec) {
doc_index = document_key_list.indexOf(document_list[i].id); return opml_queue
if (doc_index !== -1) {
delete document_key_list[doc_index];
}
pushDocument(
document_list[i].id,
document_list[i].doc,
attachment_dict[document_list[i].id]
);
}
return document_queue
.push(function () { .push(function () {
var k, return getOpmlTree(context, url, storage_spec);
remove_queue = new RSVP.Queue(); })
.push(function (result_list) {
// remove all document which were not updated return pushDocumentToStorage(
function removeDocument(key) { context,
remove_queue result_list[0],
.push(function () { result_list[1],
return context._local_sub_storage.get(key); result_list[2]
}) );
.push(undefined, function (error) {
throw error;
})
.push(function (element) {
return context._local_sub_storage.removeAttachment(
key,
element.name
);
})
.push(function () {
return context._local_sub_storage.remove(key);
})
.push(undefined, function (error) {
if ((error instanceof jIO.util.jIOError) &&
(error.status_code === 404)) {
return {};
}
throw error;
});
}
for (k = 0; k < document_key_list.length; k += 1) {
if (document_key_list[k] !== undefined) {
removeDocument(document_key_list[k]);
}
}
return remove_queue;
}); });
} }
for (i = 0; i < context._opml_storage_list.length; i += 1) {
for (j = 0; j < new_document_list.length; j += 1) { syncFullOpml(context._opml_storage_list[i].url,
promise_list.push(pushDocumentToStorage( context._opml_storage_list[i]);
new_document_list[j][0],
new_document_list[j][1]
));
} }
return RSVP.all(promise_list); return opml_queue;
} }
ReplicatedOPMLStorage.prototype.repair = function () { ReplicatedOPMLStorage.prototype.repair = function () {
var context = this, var context = this,
argument_list = arguments, argument_list = arguments;
local_storage_index_list = [];
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function () {
...@@ -452,21 +520,8 @@ ...@@ -452,21 +520,8 @@
); );
}) })
.push(function () { .push(function () {
return context._local_sub_storage.allDocs();
})
.push(function (document_index) {
var i;
for (i = 0; i < document_index.data.total_rows; i += 1) {
local_storage_index_list.push(document_index.data.rows[i].id);
}
return syncOpmlStorage(context); return syncOpmlStorage(context);
})
.push(function (result_list) {
return repairLocalStorage(context,
result_list,
local_storage_index_list);
}); });
}; };
jIO.addStorage('replicatedopml', ReplicatedOPMLStorage); jIO.addStorage('replicatedopml', ReplicatedOPMLStorage);
......
...@@ -152,6 +152,9 @@ ...@@ -152,6 +152,9 @@
.push(function (response) { .push(function (response) {
var element, var element,
item; item;
if (!response.target.responseText) {
throw new jIO.util.jIOError("Cannot find document", 404);
}
element = new DOMParser().parseFromString( element = new DOMParser().parseFromString(
response.target.responseText, response.target.responseText,
"text/xml" "text/xml"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment