Commit 0d5eba32 authored by Bryan Kaperick's avatar Bryan Kaperick

Revised bryanstorage so that a log of puts and removes is recorded. All tests...

Revised bryanstorage so that a log of puts and removes is recorded.  All tests involving getting, putting, removing, and querying from other storages are passing.  Currently, the allDocs method is not implemented correctly, but there are several placeholder tests written which demonstrate the desired functionality.
parent dc356c98
...@@ -3,8 +3,13 @@ ...@@ -3,8 +3,13 @@
(function (jIO) { (function (jIO) {
"use strict"; "use strict";
// Metadata keys included for internal revisioning, but not shown to user var unique_timestamp = function () {
//var _revision_metadata = ["_revision", "_doc_id"]; // Used to distinguish between operations done within the same millisecond
var uuid = ('0000' + Math.floor(Math.random() * 0x10000)
.toString(16)).slice(-4),
timestamp = Date.now().toString();
return timestamp + "-" + uuid;
};
/** /**
* The jIO BryanStorage extension * The jIO BryanStorage extension
...@@ -21,34 +26,69 @@ ...@@ -21,34 +26,69 @@
} }
BryanStorage.prototype.get = function (id_in) { BryanStorage.prototype.get = function (id_in) {
return this._sub_storage.get(id_in);
// Query to get the last edit made to this document
var substorage = this._sub_storage,
options = {
query: "doc_id: " + id_in,
sort_on: [["timestamp", "descending"]],
limit: [0, 1]
};
return substorage.allDocs(options)
.push(function (results) {
if (results.data.rows.length > 0) {
return substorage.get(results.data.rows[0].id);
}
throw new jIO.util.jIOError(
"bryanstorage: cannot find object '" + id_in + "'",
404
);
})
.push(function (result) {
// If last edit was a remove, throw a 'not found' error
if (result.op === "remove") {
throw new jIO.util.jIOError(
"bryanstorage: cannot find object '" + id_in + "' (removed)",
404
);
}
// If last edit was a put, return the document data
if (result.op === "put") {
return result.doc;
}
});
}; };
BryanStorage.prototype.post = function (metadata) { BryanStorage.prototype.post = function (metadata) {
return this._sub_storage.post(metadata); return this._sub_storage.post(metadata);
}; };
BryanStorage.prototype.put = function (id, metadata) { BryanStorage.prototype.put = function (id, data) {
var storage = this; var timestamp = unique_timestamp(),
metadata = {
return this._sub_storage.put(id, metadata) // XXX: remove this attribute once query can sort_on id
.push(function () { timestamp: timestamp,
doc_id: id,
doc: data,
op: "put"
};
return this._sub_storage.put(timestamp, metadata);
};
// Also push a metadata document recording the posting time BryanStorage.prototype.remove = function (id) {
metadata._deprecated = "true"; var timestamp = unique_timestamp(),
metadata._doc_id = id; metadata = {
metadata._timestamp = Date.now(); // XXX: remove this attribute once query can sort_on id
return storage.post(metadata); timestamp: timestamp,
}); doc_id: id,
op: "remove"
};
return this._sub_storage.put(timestamp, metadata);
}; };
BryanStorage.prototype.allAttachments = function () { BryanStorage.prototype.allAttachments = function () {
return this._sub_storage.allAttachments.apply(this._sub_storage, arguments); return this._sub_storage.allAttachments.apply(this._sub_storage, arguments);
}; };
BryanStorage.prototype.remove = function () {
return this._sub_storage.remove.apply(this._sub_storage, arguments);
};
BryanStorage.prototype.getAttachment = function () { BryanStorage.prototype.getAttachment = function () {
return this._sub_storage.getAttachment.apply(this._sub_storage, arguments); return this._sub_storage.getAttachment.apply(this._sub_storage, arguments);
}; };
...@@ -107,14 +147,6 @@ ...@@ -107,14 +147,6 @@
}; };
BryanStorage.prototype.buildQuery = function (options) { BryanStorage.prototype.buildQuery = function (options) {
if (options === undefined) {
options = {query: ""};
}
if (options.query !== "") {
options.query = "(" + options.query + ") AND ";
}
options.query = options.query + 'NOT (_deprecated: "true")';
return this._sub_storage.buildQuery(options); return this._sub_storage.buildQuery(options);
}; };
......
...@@ -15,11 +15,12 @@ ...@@ -15,11 +15,12 @@
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// _revision parameter updating with RSVP all // _revision parameter updating with RSVP all
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
module("bryanStorage revision with RSVP all");
module("bryanStorage.revision_with_RSVP_all");
test("verifying updates correctly when puts are done in parallel", test("verifying updates correctly when puts are done in parallel",
function () { function () {
stop(); stop();
expect(3); expect(7);
// create storage of type "bryan" with memory as substorage // create storage of type "bryan" with memory as substorage
var dbname = "rsvp_db_" + Date.now(), var dbname = "rsvp_db_" + Date.now(),
...@@ -52,35 +53,72 @@ ...@@ -52,35 +53,72 @@
jio.put("bar", {"title": "foo1"}), jio.put("bar", {"title": "foo1"}),
jio.put("bar", {"title": "foo2"}), jio.put("bar", {"title": "foo2"}),
jio.put("bar", {"title": "foo3"}), jio.put("bar", {"title": "foo3"}),
jio.put("bar", {"title": "foo4"}) jio.put("bar", {"title": "foo4"}),
jio.put("barbar", {"title": "attr0"}),
jio.put("barbar", {"title": "attr1"}),
jio.put("barbar", {"title": "attr2"}),
jio.put("barbar", {"title": "attr3"})
]); ]);
}) })
.push(function () {return jio.get("bar"); }) .push(function () {return jio.get("bar"); })
.push(function (result) { .push(function (result) {
deepEqual(result, { ok(result.title !== "foo0", "Title should have changed from foo0");
"title": "foo4" })
.push(function () {
return not_bryan.allDocs({
query: "",
sort_on: [["timestamp", "ascending"]]
}); });
}) })
.push(function (results) {
equal(results.data.rows.length,
9,
"All nine versions exist in storage");
return not_bryan.get(results.data.rows[0].id);
})
.push(function (results) {
deepEqual(results, {
doc_id: "bar",
doc: {
title: "foo0"
},
timestamp: results.timestamp,
op: "put"
}, "The first item in the log is pushing bar's title to 'foo0'");
return jio.remove("bar");
})
.push(function () { .push(function () {
return jio.get("bar");
})
.push(function () {
return jio.get("barbar");
}, function (error) {
deepEqual(
error.message,
"bryanstorage: cannot find object 'bar' (removed)",
"Appropriate error is sent explaining object has been removed"
);
return jio.get("barbar");
})
.push(function (result) {
ok(result.title !== undefined, "barbar exists and has proper form");
return not_bryan.allDocs({ return not_bryan.allDocs({
query: "", query: "",
sort_on: [["_timestamp", "ascending"]] sort_on: [["op", "descending"]]
}); });
}) })
.push(function (results) { .push(function (results) {
equal(results.data.rows.length, equal(results.data.rows.length,
6, 10,
"Storage contains all 5 revisions plus the most recent one."); "Remove operation is recorded");
return not_bryan.get(results.data.rows[1].id); return not_bryan.get(results.data.rows[0].id);
}) })
.push(function (result) { .push(function (result) {
deepEqual(result, { deepEqual(result, {
title: "foo0", doc_id: "bar",
_doc_id: "bar", timestamp: result.timestamp,
_timestamp: result._timestamp, op: "remove"
_deprecated: "true" });
},
"Query returns the first edition of the document");
}) })
.fail(function (error) { .fail(function (error) {
//console.log(error); //console.log(error);
...@@ -91,308 +129,83 @@ ...@@ -91,308 +129,83 @@
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// bryanStorage revision history // bryanStorage.querying_from_bryanstorage
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
module("bryanStorage.revision_history"); module("bryanStorage.querying_from_bryanstorage");
test("put and get the correct version", function () { test("verifying the correct results are returned from bryanStorage.allDocs",
stop(); function () {
expect(7); stop();
var dbname = "rev_hist_db" + Date.now(), expect(1);
jio = jIO.createJIO({
type: "bryan",
sub_storage: {
type: "uuid",
sub_storage: {
//type: "memory"
type: "indexeddb",
database: dbname
}
}
}),
not_bryan = jIO.createJIO({
type: "uuid",
sub_storage: {
type: "query",
sub_storage: {
//type: "memory"
type: "indexeddb",
database: dbname
}
}
}),
query_input =
{
query: 'NOT (_deprecated: "true")',
sort_on: [['_timestamp', 'descending']]
},
query_input2 =
{
query: 'title: "rev1"',
sort_on: [['_timestamp', 'descending']]
};
jio.put("doc1", {
"title": "rev0",
"subtitle": "subrev0"
})
.push(function () {
return jio.put("doc1", {
"title": "rev1",
"subtitle": "subrev1"
});
})
.push(function () {
return jio.put("doc1", {
"title": "rev2",
"subtitle": "subrev2"
});
})
.push(function () {
return jio.put("doc1", {
"title": "rev3",
"subtitle": "subrev3"
});
})
.push(function () {return jio.get("doc1"); })
.push(function (result) {
deepEqual(result, {
"title": "rev3",
"subtitle": "subrev3"
}, "Retrieve first edition of document correctly");
})
.push(function () {
return not_bryan.allDocs(query_input);
})
.push(function (results) {
equal(results.data.rows.length, 1, "Only 1 version isn't _deprecated");
return jio.get(results.data.rows[0].id);
})
.push(function (result) {
deepEqual(result, {
"title": "rev3",
"subtitle": "subrev3"
}, "Retrieve most recent edition by querying NOT _deprecated");
})
.push(function () {
return not_bryan.allDocs(query_input2);
})
.push(function (results) {
equal(results.data.rows.length, 1, "Only one version is titled 'rev1'");
return jio.get(results.data.rows[0].id);
})
.push(function (result) {
deepEqual(result, {
"title": "rev1",
"subtitle": "subrev1",
"_deprecated": "true",
"_timestamp": result._timestamp,
"_doc_id": "doc1"
},
"Retrieve 1st edit by querying for title: 'rev1' with other storage");
})
.push(function () {
return jio.allDocs({query: ''});
})
.push(function (results) {
equal(results.data.rows.length,
1,
"bryanstorage only sees latest version");
return jio.get(results.data.rows[0].id);
})
.push(function (result) {
deepEqual(result, {
"title": "rev3",
"subtitle": "subrev3"
}, "Retrieve latest version correctly with bryanstorage");
})
.fail(function (error) {
//console.log(error);
ok(false, error);
})
.always(function () {
start();
});
});
/////////////////////////////////////////////////////////////////
// bryanStorage.revision_history_multiple_edits
/////////////////////////////////////////////////////////////////
module("bryanStorage.revision_history_multiple_edits"); // create storage of type "bryan" with memory as substorage
test("modify first version but save both", function () { var jio = jIO.createJIO({
stop();
expect(10);
var dbname = "rev_hist_mult_db" + Date.now(),
jio = jIO.createJIO({
type: "bryan", type: "bryan",
sub_storage: { sub_storage: {
type: "uuid", type: "uuid",
sub_storage: { sub_storage: {
type: "indexeddb", type: "memory"
database: dbname
}
}
}),
not_bryan = jIO.createJIO({
type: "query",
sub_storage: {
type: "uuid",
sub_storage: {
type: "indexeddb",
database: dbname
} }
} }
}); });
jio.put("main_doc", { jio.put("bar", {"title": "foo0"})
"title": "rev0", .push(function () {
"subtitle": "subrev0" return RSVP.all([
}) jio.put("bar", {"title": "foo1"}),
.push(function () { jio.put("bar", {"title": "foo2"}),
return jio.put("other_doc", { jio.put("bar", {"title": "foo3"}),
"attr": "version0", jio.put("barbar", {"title": "attr0"}),
"subattr": "subversion0" jio.put("barbar", {"title": "attr1"}),
}); jio.put("barbar", {"title": "attr2"})
}) ]);
.push(function () { })
return jio.put("other_doc", { // Make two final puts so we know what to expect as the current state of
"attr": "version1", // each document.
"subattr": "subversion1" .push(function () {
}); return jio.put("bar", {"title": "foo4"});
}) })
.push(function () { .push(function () {
return jio.put("main_doc", { return jio.put("barbar", {"title": "attr3"});
"title": "rev1", })
"subtitle": "subrev1"
});
})
.push(function () {
return jio.put("main_doc", {
"title": "rev2",
"subtitle": "subrev2"
});
})
.push(function () {
return jio.put("main_doc", {
"title": "rev3",
"subtitle": "subrev3"
});
})
.push(function () {return jio.get("main_doc"); })
.push(function (result) {
deepEqual(result, {
"title": "rev3",
"subtitle": "subrev3"
}, "Retrieve main document correctly");
})
.push(function () {return jio.get("other_doc"); })
.push(function (result) {
deepEqual(result, {
"attr": "version1",
"subattr": "subversion1"
}, "Retrieve other document correctly");
})
.push(function () {
return jio.allDocs({
query: ""
});
})
.push(function (result) {
equal(result.data.rows.length,
2,
"Empty query returns only non-deprecated docs");
})
.push(function () {
return jio.allDocs({
query: 'attr: "version1"'
});
})
.push(function (result) {
equal(result.data.rows.length,
1,
"No deprecated results are returned.");
if (result.data.rows.length > 0) {
return jio.get(result.data.rows[0].id);
}
})
.push(function (result) {
deepEqual(result, {
"attr": "version1",
"subattr": "subversion1"
}, "Only get most recent edit");
})
.push(function () {
return jio.allDocs({
query: '(_doc_id: "other_doc")'
});
})
.push(function (result) {
equal(result.data.rows.length, 0, "Correct number of results returned");
})
.push(function () {
return jio.allDocs({
query: ''
});
})
.push(function (result) {
equal(result.data.rows.length, 2, "Correct number of results returned");
})
// When not_bryan queries the storage, all documents are returned. // Queries should only include information about the final two versions
.push(function () { .push(function () {
var options = { return jio.allDocs({
query: "_doc_id: main_doc", query: "",
sort_on: [["_timestamp", "ascending"]] sort_on: [["title", "ascending"]]
}; });
return not_bryan.allDocs(options); })
}) .push(function (results) {
.push(function (results) { equal(results.data.rows.length,
equal(results.data.rows.length, 2,
4, "Empty query yields two results since there are two unique docs");
"should get all 3 deprecated versions plus one copy of the latest."); return jio.get(results.data.rows[0].id);
return not_bryan.get(results.data.rows[0].id); })
}) .push(function (result) {
.push(function (results) { deepEqual(result, {
deepEqual(results, { title: "attr3"
"title": "rev0", }, "Retrieve the first title in the correct format (no metadata)");
"subtitle": "subrev0",
"_doc_id": "main_doc",
"_timestamp": results._timestamp,
"_deprecated": "true"
}, },
"Get the earliest copy of the doc with all metadata."); function () {
}) return ok(false, "Couldn't find document in storage");
})
// When not_bryan queries the storage, all documents are returned. // Querying for a specific id
.push(function () { .push(function () {
var options = { return jio.allDocs({
query: "_doc_id: main_doc", query: "id: bar"
sort_on: [["_timestamp", "ascending"]] });
}; })
return not_bryan.allDocs(options); .push(function (result) {
}) deepEqual(result, {
.push(function (results) { title: "foo4"
return not_bryan.get(results.data.rows[1].id); }, "Retrieve correct document in correct format (no metadata)");
}) })
.push(function (results) {
deepEqual(results, {
"title": "rev1",
"subtitle": "subrev1",
"_doc_id": "main_doc",
"_timestamp": results._timestamp,
"_deprecated": "true"
},
"Get the earliest copy of the doc with all metadata.");
})
.fail(function (error) {
//console.log(error);
ok(false, error);
})
.always(function () {
start();
});
});
.fail(function (error) {
//console.log(error);
ok(false, error);
})
.always(function () {start(); });
});
}(jIO, QUnit)); }(jIO, QUnit));
\ No newline at end of file
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