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,347 +53,159 @@ ...@@ -52,347 +53,159 @@
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 () { .push(function () {
return not_bryan.allDocs({ return not_bryan.allDocs({
query: "", query: "",
sort_on: [["_timestamp", "ascending"]] sort_on: [["timestamp", "ascending"]]
}); });
}) })
.push(function (results) { .push(function (results) {
equal(results.data.rows.length, equal(results.data.rows.length,
6, 9,
"Storage contains all 5 revisions plus the most recent one."); "All nine versions exist in storage");
return not_bryan.get(results.data.rows[1].id); return not_bryan.get(results.data.rows[0].id);
})
.push(function (result) {
deepEqual(result, {
title: "foo0",
_doc_id: "bar",
_timestamp: result._timestamp,
_deprecated: "true"
},
"Query returns the first edition of the document");
})
.fail(function (error) {
//console.log(error);
ok(false, error);
}) })
.always(function () {start(); }); .push(function (results) {
}); deepEqual(results, {
doc_id: "bar",
doc: {
///////////////////////////////////////////////////////////////// title: "foo0"
// bryanStorage revision history
/////////////////////////////////////////////////////////////////
module("bryanStorage.revision_history");
test("put and get the correct version", function () {
stop();
expect(7);
var dbname = "rev_hist_db" + Date.now(),
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 = timestamp: results.timestamp,
{ op: "put"
query: 'title: "rev1"', }, "The first item in the log is pushing bar's title to 'foo0'");
sort_on: [['_timestamp', 'descending']] return jio.remove("bar");
};
jio.put("doc1", {
"title": "rev0",
"subtitle": "subrev0"
}) })
.push(function () { .push(function () {
return jio.put("doc1", { return jio.get("bar");
"title": "rev1",
"subtitle": "subrev1"
});
}) })
.push(function () { .push(function () {
return jio.put("doc1", { return jio.get("barbar");
"title": "rev2", }, function (error) {
"subtitle": "subrev2" deepEqual(
}); error.message,
}) "bryanstorage: cannot find object 'bar' (removed)",
.push(function () { "Appropriate error is sent explaining object has been removed"
return jio.put("doc1", { );
"title": "rev3", return jio.get("barbar");
"subtitle": "subrev3"
});
}) })
.push(function () {return jio.get("doc1"); })
.push(function (result) { .push(function (result) {
deepEqual(result, { ok(result.title !== undefined, "barbar exists and has proper form");
"title": "rev3", return not_bryan.allDocs({
"subtitle": "subrev3" query: "",
}, "Retrieve first edition of document correctly"); sort_on: [["op", "descending"]]
}) });
.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) { .push(function (results) {
equal(results.data.rows.length, equal(results.data.rows.length,
1, 10,
"bryanstorage only sees latest version"); "Remove operation is recorded");
return jio.get(results.data.rows[0].id); return not_bryan.get(results.data.rows[0].id);
}) })
.push(function (result) { .push(function (result) {
deepEqual(result, { deepEqual(result, {
"title": "rev3", doc_id: "bar",
"subtitle": "subrev3" timestamp: result.timestamp,
}, "Retrieve latest version correctly with bryanstorage"); op: "remove"
});
}) })
.fail(function (error) { .fail(function (error) {
//console.log(error); //console.log(error);
ok(false, error); ok(false, error);
}) })
.always(function () { .always(function () {start(); });
start();
});
}); });
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// bryanStorage.revision_history_multiple_edits // bryanStorage.querying_from_bryanstorage
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
module("bryanStorage.revision_history_multiple_edits"); module("bryanStorage.querying_from_bryanstorage");
test("modify first version but save both", function () { test("verifying the correct results are returned from bryanStorage.allDocs",
function () {
stop(); stop();
expect(10); expect(1);
var dbname = "rev_hist_mult_db" + Date.now(),
jio = jIO.createJIO({ // create storage of type "bryan" with memory as substorage
var 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", {
"title": "rev0",
"subtitle": "subrev0"
})
.push(function () {
return jio.put("other_doc", {
"attr": "version0",
"subattr": "subversion0"
});
})
.push(function () {
return jio.put("other_doc", {
"attr": "version1",
"subattr": "subversion1"
}); });
}) jio.put("bar", {"title": "foo0"})
.push(function () { .push(function () {
return jio.put("main_doc", { return RSVP.all([
"title": "rev1", jio.put("bar", {"title": "foo1"}),
"subtitle": "subrev1" jio.put("bar", {"title": "foo2"}),
}); jio.put("bar", {"title": "foo3"}),
jio.put("barbar", {"title": "attr0"}),
jio.put("barbar", {"title": "attr1"}),
jio.put("barbar", {"title": "attr2"})
]);
}) })
// Make two final puts so we know what to expect as the current state of
// each document.
.push(function () { .push(function () {
return jio.put("main_doc", { return jio.put("bar", {"title": "foo4"});
"title": "rev2",
"subtitle": "subrev2"
});
}) })
.push(function () { .push(function () {
return jio.put("main_doc", { return jio.put("barbar", {"title": "attr3"});
"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");
}) })
// Queries should only include information about the final two versions
.push(function () { .push(function () {
return jio.allDocs({ return jio.allDocs({
query: "" query: "",
sort_on: [["title", "ascending"]]
}); });
}) })
.push(function (result) { .push(function (results) {
equal(result.data.rows.length, equal(results.data.rows.length,
2, 2,
"Empty query returns only non-deprecated docs"); "Empty query yields two results since there are two unique docs");
}) return jio.get(results.data.rows[0].id);
.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) { .push(function (result) {
deepEqual(result, { deepEqual(result, {
"attr": "version1", title: "attr3"
"subattr": "subversion1" }, "Retrieve the first title in the correct format (no metadata)");
}, "Only get most recent edit"); },
}) function () {
.push(function () { return ok(false, "Couldn't find document in storage");
return jio.allDocs({
query: '(_doc_id: "other_doc")'
});
})
.push(function (result) {
equal(result.data.rows.length, 0, "Correct number of results returned");
}) })
// Querying for a specific id
.push(function () { .push(function () {
return jio.allDocs({ return jio.allDocs({
query: '' query: "id: bar"
}); });
}) })
.push(function (result) { .push(function (result) {
equal(result.data.rows.length, 2, "Correct number of results returned"); deepEqual(result, {
}) title: "foo4"
}, "Retrieve correct document in correct format (no metadata)");
// When not_bryan queries the storage, all documents are returned.
.push(function () {
var options = {
query: "_doc_id: main_doc",
sort_on: [["_timestamp", "ascending"]]
};
return not_bryan.allDocs(options);
})
.push(function (results) {
equal(results.data.rows.length,
4,
"should get all 3 deprecated versions plus one copy of the latest.");
return not_bryan.get(results.data.rows[0].id);
})
.push(function (results) {
deepEqual(results, {
"title": "rev0",
"subtitle": "subrev0",
"_doc_id": "main_doc",
"_timestamp": results._timestamp,
"_deprecated": "true"
},
"Get the earliest copy of the doc with all metadata.");
}) })
// When not_bryan queries the storage, all documents are returned.
.push(function () {
var options = {
query: "_doc_id: main_doc",
sort_on: [["_timestamp", "ascending"]]
};
return not_bryan.allDocs(options);
})
.push(function (results) {
return not_bryan.get(results.data.rows[1].id);
})
.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) { .fail(function (error) {
//console.log(error); //console.log(error);
ok(false, error); ok(false, error);
}) })
.always(function () { .always(function () {start(); });
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