Commit 9dfafb7f authored by preetwinder's avatar preetwinder

migrations, additonal capacities and put test

parent 3fecea79
......@@ -29,34 +29,63 @@
if (typeof description.database !== "string" ||
description.database === "") {
throw new TypeError("IndexStorage2 'database' description property " +
"must be a non-empty string");
"must be a non-empty string");
}
this._sub_storage = jIO.createJIO(description.sub_storage);
this._database_name = "jio:" + description.database;
this._index_keys = description.index_keys || [];
this._version = 1;
}
IndexStorage2.prototype.hasCapacity = function (name) {
return ((name === "list") || (name === "query")) || (name === "limit");
return ((name === "list") || (name === "query")) || (name === "limit") ||
(name === "select");
};
function checkArrayEquality(array1, array2) {
if (array1.length !== array2.length) {
return false;
}
var i;
array1 = array1.sort();
array2 = array2.sort();
for (i = 0; i < array1.length; i += 1) {
if (array1[i] !== array2[i]) {
return false;
}
}
return true;
}
function handleUpgradeNeeded(evt, index_keys) {
var db = evt.target.result, store, i;
var db = evt.target.result, store, i, current_indices;
store = db.createObjectStore("index-store", {
keyPath: "id",
autoIncrement: false
});
if (!(db.objectStoreNames[0])) {
store = db.createObjectStore("index-store", {
keyPath: "id",
autoIncrement: false
});
} else {
store = evt.target.transaction.objectStore('index-store');
}
current_indices = new Set(store.indexNames);
for (i = 0; i < index_keys.length; i += 1) {
store.createIndex("Index-" + index_keys[i], "doc." + index_keys[i],
{unique: false});
if (!(current_indices.has('Index-' + index_keys[i]))) {
store.createIndex('Index-' + index_keys[i],
'doc.' + index_keys[i], { unique: false });
}
current_indices.delete('Index-' + index_keys[i]);
}
current_indices = Array.from(current_indices);
for (i = 0; i < current_indices.length; i += 1) {
store.deleteIndex(current_indices[i]);
}
}
function waitForOpenIndexedDB(db_name, index_keys, callback) {
function waitForOpenIndexedDB(db_name, version, index_keys, callback) {
function resolver(resolve, reject) {
// Open DB //
var request = indexedDB.open(db_name);
var request = indexedDB.open(db_name, version);
request.onerror = function (error) {
if (request.result) {
request.result.close();
......@@ -65,7 +94,7 @@
(error.target instanceof IDBOpenDBRequest) &&
(error.target.error instanceof DOMError)) {
reject("Connection to: " + db_name + " failed: " +
error.target.error.message);
error.target.error.message);
} else {
reject(error);
}
......@@ -81,11 +110,6 @@
reject("Connection to: " + db_name + " timeout");
};
request.onblocked = function () {
request.result.close();
reject("Connection to: " + db_name + " was blocked");
};
// Create DB if necessary //
request.onupgradeneeded = function (evt) {
handleUpgradeNeeded(evt, index_keys);
......@@ -99,9 +123,23 @@
request.onsuccess = function () {
return new RSVP.Queue()
.push(function () {
var store, current_indices, db_version, required_indices;
store = request.result.transaction('index-store')
.objectStore('index-store');
current_indices = store.indexNames;
db_version = request.result.version;
required_indices = index_keys.map(
function (value) {return 'Index-' + value; }
);
if (!(checkArrayEquality(required_indices,
Array.from(current_indices)))) {
request.result.close();
return waitForOpenIndexedDB(db_name, db_version + 1,
index_keys, callback);
}
return callback(request.result);
})
.push(function (result) {
.push(function (result) {
request.result.close();
resolve(result);
}, function (error) {
......@@ -184,17 +222,18 @@
context._sub_storage.hasCapacity("query");
} catch (error) {
throw new jIO.util.jIOError(
"No index for this key and substorage doesn't support queries"
"No index for this key and substorage doesn't support queries",
404
);
}
return context._sub_storage.buildQuery(
{"query": index + ":" + value}
{ "query": index + ":" + value }
);
}
return waitForOpenIndexedDB(context._database_name, context._index_keys,
function (db) {
return waitForOpenIndexedDB(context._database_name, undefined,
context._index_keys, function (db) {
return waitForTransaction(db, ["index-store"], "readonly",
function (tx) {
function (tx) {
return waitForIDBRequest(tx.objectStore("index-store")
.index("Index-" + index).getAll(value, limit))
.then(function (evt) {
......@@ -255,19 +294,27 @@
options.limit)
.push(function (result) {
return result.map(function (value) {
return {"id": value.id, "value": {} };
return {
"id": value.id,
"value": filterDocValues(value.doc,
options.select_list || [])
};
});
});
}
return waitForOpenIndexedDB(context._database_name, context._index_keys,
function (db) {
return waitForOpenIndexedDB(context._database_name, undefined,
context._index_keys, function (db) {
return waitForTransaction(db, ["index-store"], "readonly",
function (tx) {
return waitForIDBRequest(tx.objectStore("index-store")
.getAll(undefined, options.limit))
.then(function (evt) {
return evt.target.result.map(function (value) {
return {"id": value.id, "value": {} };
return {
"id": value.id,
"value": filterDocValues(value.doc,
options.select_list || [])
};
});
});
});
......@@ -282,10 +329,10 @@
var context = this;
return context._sub_storage.put(id, value)
.push(function () {
return waitForOpenIndexedDB(context._database_name, context._index_keys,
function (db) {
return waitForOpenIndexedDB(context._database_name, undefined,
context._index_keys, function (db) {
return waitForTransaction(db, ["index-store"], "readwrite",
function (tx) {
function (tx) {
return waitForIDBRequest(tx.objectStore("index-store").put({
"id": id,
"doc": filterDocValues(value, context._index_keys)
......@@ -299,10 +346,10 @@
var context = this;
return context._sub_storage.post(value)
.push(function (id) {
return waitForOpenIndexedDB(context._database_name, context._index_keys,
function (db) {
return waitForOpenIndexedDB(context._database_name, undefined,
context._index_keys, function (db) {
return waitForTransaction(db, ["index-store"], "readwrite",
function (tx) {
function (tx) {
return waitForIDBRequest(tx.objectStore("index-store").put({
"id": id,
"doc": filterDocValues(value, context._index_keys)
......@@ -318,16 +365,13 @@
IndexStorage2.prototype.remove = function (id) {
var context = this;
return context._sub_storage.remove(id)
.push(function (result) {
return waitForOpenIndexedDB(context._database_name, context._index_keys,
function (db) {
.push(function () {
return waitForOpenIndexedDB(context._database_name, undefined,
context._index_keys, function (db) {
return waitForTransaction(db, ["index-store"], "readwrite",
function (tx) {
return waitForIDBRequest(tx.objectStore("index-store")
.delete(id))
.then(function () {
return result;
});
.delete(id));
});
});
});
......
......@@ -42,7 +42,7 @@
});
}
function id_compare(value1, value2) {
function idCompare(value1, value2) {
if (value1.id > value2.id) {
return 1;
}
......@@ -253,12 +253,12 @@
})
.then(function (result) {
equal(result.data.total_rows, 3);
deepEqual(result.data.rows.sort(id_compare),
deepEqual(result.data.rows.sort(idCompare),
[
{"id": "32", "value": {}},
{"id": "21", "value": {}},
{"id": "3", "value": {}}
].sort(id_compare));
].sort(idCompare));
})
.fail(function (error) {
console.log(error);
......@@ -279,7 +279,7 @@
}
});
stop();
expect(4);
expect(5);
DummyStorage3.prototype.put = function (id, value) {
equal(id, "32");
......@@ -295,10 +295,8 @@
.then(function () {
return context.jio.allDocs({query: 'b:"2"'});
})
.then(function () {
return deleteIndexedDB(context.jio);
})
.fail(function (error) {
equal(error.status_code, 404);
equal(error.message,
"No index for this key and substorage doesn't support queries");
})
......@@ -387,9 +385,9 @@
})
.then(function (result) {
equal(result.data.total_rows, 2);
deepEqual(result.data.rows.sort(id_compare),
deepEqual(result.data.rows.sort(idCompare),
[{"id": "32", "value": {}}, {"id": "3", "value": {}}]
.sort(id_compare));
.sort(idCompare));
})
.fail(function (error) {
console.log(error);
......@@ -513,6 +511,132 @@
});
});
test("Select without query", function () {
var context = this;
context.jio = jIO.createJIO({
type: "index2",
database: "index2_test",
index_keys: ["a", "b", "c"],
sub_storage: {
type: "dummystorage3"
}
});
stop();
expect(2);
DummyStorage3.prototype.put = function (id) {
return id;
};
RSVP.all([
context.jio.put("1", {"a": "55", "b": "2", "c": "try"}),
context.jio.put("2", {"a": "98", "b": "2", "c": "adverse"}),
context.jio.put("3", {"a": "75", "b": "2", "c": "invite"}),
context.jio.put("8", {"a": "43", "b": "2", "c": "absolve"}),
context.jio.put("6", {"a": "21", "b": "2", "c": "defy"})
])
.then(function () {
return context.jio.allDocs({select_list: ["a", "c"]});
})
.then(function (result) {
equal(result.data.total_rows, 5);
deepEqual(result.data.rows.sort(idCompare), [
{id: "1", value: {"a": "55", "c": "try"}},
{id: "2", value: {"a": "98", "c": "adverse"}},
{id: "3", value: {"a": "75", "c": "invite"}},
{id: "8", value: {"a": "43", "c": "absolve"}},
{id: "6", value: {"a": "21", "c": "defy"}}
].sort(idCompare));
})
.fail(function (error) {
console.log(error);
})
.always(function () {
start();
});
});
test("Select with query and limit", function () {
var context = this;
context.jio = jIO.createJIO({
type: "index2",
database: "index2_test",
index_keys: ["a", "b", "c"],
sub_storage: {
type: "dummystorage3"
}
});
stop();
expect(2);
DummyStorage3.prototype.put = function (id) {
return id;
};
RSVP.all([
context.jio.put("1", {"a": "55", "b": "2", "c": "try"}),
context.jio.put("2", {"a": "98", "b": "2", "c": "adverse"}),
context.jio.put("3", {"a": "75", "b": "2", "c": "invite"}),
context.jio.put("8", {"a": "43", "b": "3", "c": "absolve"}),
context.jio.put("6", {"a": "21", "b": "2", "c": "defy"}),
context.jio.put("4", {"a": "65", "b": "3", "c": "odd"})
])
.then(function () {
return context.jio.allDocs({select_list: ["a", "c"],
query: "b:2"});
})
.then(function (result) {
equal(result.data.total_rows, 4);
deepEqual(result.data.rows.sort(idCompare), [
{id: "1", value: {"a": "55", "c": "try"}},
{id: "2", value: {"a": "98", "c": "adverse"}},
{id: "3", value: {"a": "75", "c": "invite"}},
{id: "6", value: {"a": "21", "c": "defy"}}
].sort(idCompare));
})
.fail(function (error) {
console.log(error);
})
.always(function () {
start();
});
});
test("Select_list key is not present in the index", function () {
var context = this;
context.jio = jIO.createJIO({
type: "index2",
database: "index2_test",
index_keys: ["a", "c"],
sub_storage: {
type: "dummystorage3"
}
});
stop();
expect(2);
DummyStorage3.prototype.put = function (id) {
return id;
};
RSVP.all([
context.jio.put("1", {"a": "55", "b": "2", "c": "try"}),
context.jio.put("2", {"a": "55", "b": "5", "c": "adverse"}),
context.jio.put("3", {"a": "55", "b": "1", "c": "invite"})
])
.then(function () {
return context.jio.allDocs({select_list: ["a", "b"],
query: "a:55"});
})
.fail(function (error) {
equal(error.status_code, 404);
equal(error.message, "Index key 'b' not found in document");
})
.always(function () {
start();
});
});
test("Complex queries", function () {
var context = this;
context.jio = jIO.createJIO({
......@@ -571,7 +695,7 @@
.then(function (result) {
equal(result.data.total_rows, 2);
deepEqual(result.data.rows.sort(), [{"id": "23", "value": {}},
{"id": "14", "value": {}}].sort(id_compare));
{"id": "14", "value": {}}].sort(idCompare));
})
.then(function () {
return context.jio.allDocs({"query": "name:envision AND user:Mann"});
......@@ -585,9 +709,9 @@
})
.then(function (result) {
equal(result.data.total_rows, 3);
deepEqual(result.data.rows.sort(id_compare),
deepEqual(result.data.rows.sort(idCompare),
[{"id": "23", "value": {}}, {"id": "38", "value": {}},
{"id": "45", "value": {}}].sort(id_compare));
{"id": "45", "value": {}}].sort(idCompare));
})
.then(function () {
return context.jio.allDocs(
......@@ -607,64 +731,95 @@
});
});
/* test("Index keys modified", function () {
test("Index keys modified", function () {
var context = this;
context.jio = jIO.createJIO({
type: "index2",
database: "index2_test",
index_keys: ["a"],
index_keys: ["a", "b"],
sub_storage: {
type: "dummystorage3"
}
});
stop();
expect(5);
expect(6);
DummyStorage3.prototype.put = function (id, value) {
equal(id, "32");
deepEqual(value, {a: "3", b: "2"});
DummyStorage3.prototype.put = function (id) {
return id;
};
context.jio.put("32", {"a": "3", "b": "2"})
DummyStorage3.prototype.hasCapacity = function () {
return false;
};
RSVP.all([
context.jio.put("32", {"a": "3", "b": "2", "c": "inverse"}),
context.jio.put("5", {"a": "6", "b": "2", "c": "strong"}),
context.jio.put("14", {"a": "67", "b": "3", "c": "disolve"})
])
.then(function () {
return context.jio.allDocs({query: 'a: "3"'});
return context.jio.allDocs({query: 'b: "3"'});
})
.then(function (result) {
deepEqual(result.data.rows[0], {"id": "32", "value": {}});
deepEqual(result.data.rows, [{"id": "14", "value": {}}]);
})
.then(function () {
return context.jio.allDocs({query: 'a: "6"'});
})
.then(function (result) {
deepEqual(result.data.rows, [{"id": "5", "value": {}}]);
})
.then(function () {
context.jio = jIO.createJIO({
type: "index2",
database: "index2_test",
index_keys: ["b"],
index_keys: ["b", "c"],
sub_storage: {
type: "dummystorage3"
}
});
})
.then(function () {
console.log(context.jio.__storage._index_keys);
return context.jio.put("32", {"a": "3", "b": "2"});
return RSVP.all([
context.jio.put("18", {"a": "2", "b": "3", "c": "align"}),
context.jio.put("62", {"a": "6", "b": "2", "c": "disolve"})
]);
})
.then(function () {
return context.jio.allDocs({query: 'b: "2"'});
return context.jio.allDocs({query: 'b: "3"'});
})
.then(function (result) {
deepEqual(result.data.rows[0], {"id": "32", "value": {}});
deepEqual(result.data.rows.sort(idCompare),
[{"id": "14", "value": {}}, {"id": "18", "value": {}}]
.sort(idCompare));
})
.then(function () {
return context.jio.allDocs({query: 'c: "disolve"'});
})
.then(function (result) {
deepEqual(result.data.rows, [{"id": "62", "value": {}}]);
})
.then(function () {
return context.jio.allDocs({query: 'a: "3"'});
})
.fail(function (error) {
console.log(error);
equal(error.status_code, 404);
equal(error.message,
"No index for this key and substorage doesn't support queries");
})
.always(function () {
start();
});
});*/
});
/////////////////////////////////////////////////////////////////
// IndexStorage2.getAttachment
/////////////////////////////////////////////////////////////////
module("IndexStorage2.getAttachment");
module("IndexStorage2.getAttachment", {
teardown: function () {
deleteIndexedDB(this.jio);
}
});
test("getAttachment called substorage getAttachment", function () {
stop();
expect(3);
......@@ -700,7 +855,11 @@
/////////////////////////////////////////////////////////////////
// IndexStorage2.putAttachment
/////////////////////////////////////////////////////////////////
module("IndexStorage2.putAttachment");
module("IndexStorage2.putAttachment", {
teardown: function () {
deleteIndexedDB(this.jio);
}
});
test("putAttachment called substorage putAttachment", function () {
stop();
expect(4);
......@@ -735,9 +894,13 @@
});
/////////////////////////////////////////////////////////////////
// IndexStorage3.removeAttachment
// IndexStorage2.removeAttachment
/////////////////////////////////////////////////////////////////
module("IndexStorage3.removeAttachment");
module("IndexStorage2.removeAttachment", {
teardown: function () {
deleteIndexedDB(this.jio);
}
});
test("removeAttachment called substorage removeAttachment", function () {
stop();
expect(3);
......@@ -769,4 +932,252 @@
});
});
/////////////////////////////////////////////////////////////////
// indexStorage2.put
/////////////////////////////////////////////////////////////////
module("indexStorage2.put", {
teardown: function () {
deleteIndexedDB(this.jio);
}
});
test("Put creates index", function () {
var context = this, request, store, records;
context.jio = jIO.createJIO({
type: "index2",
database: "index2_test",
index_keys: ["a", "b"],
sub_storage: {
type: "dummystorage3"
}
});
stop();
expect(12);
DummyStorage3.prototype.put = function (id) {
return id;
};
RSVP.all([
context.jio.put("32", {"a": "894", "b": "inversion", "c": 2}),
context.jio.put("33", {"a": "65", "b": "division", "c": 4}),
context.jio.put("34", {"a": "65", "b": "prolong", "c": 8})
])
.then(function () {
return new RSVP.Promise(function (resolve) {
request = indexedDB.open('jio:index2_test');
request.onsuccess = function () {
resolve(request.result);
};
});
})
.then(function (result) {
equal(result.version, 1);
equal(result.name, 'jio:index2_test');
equal(result.objectStoreNames.length, 1);
equal(result.objectStoreNames[0], 'index-store');
store = result.transaction('index-store').objectStore('index-store');
equal(store.indexNames.length, 2);
equal(store.keyPath, "id");
deepEqual(Array.from(store.indexNames).sort(), ['Index-a', 'Index-b']);
equal(store.index('Index-a').keyPath, 'doc.a');
equal(store.index('Index-b').keyPath, 'doc.b');
equal(store.index('Index-a').unique, false);
equal(store.index('Index-b').unique, false);
return new RSVP.Promise(function (resolve) {
records = store.getAll();
records.onsuccess = function () {
resolve(records);
};
});
})
.then(function (values) {
deepEqual(values.result.sort(idCompare), [
{"id": "32", "doc": {"a": "894", "b": "inversion"}},
{"id": "33", "doc": {"a": "65", "b": "division"}},
{"id": "34", "doc": {"a": "65", "b": "prolong"}}
]);
})
.then(function () {
request.result.close();
})
.fail(function (error) {
console.log(error);
})
.always(function () {
start();
});
});
/////////////////////////////////////////////////////////////////
// indexStorage2.post
/////////////////////////////////////////////////////////////////
module("indexStorage2.post", {
teardown: function () {
deleteIndexedDB(this.jio);
}
});
test("Post creates index", function () {
var context = this, request, store, records;
context.jio = jIO.createJIO({
type: "index2",
database: "index2_test",
index_keys: ["a", "b"],
sub_storage: {
type: "dummystorage3"
}
});
stop();
expect(12);
DummyStorage3.prototype.post = function (value) {
if (value.a === "5") {
return "1";
}
if (value.a === "62") {
return "2";
}
if (value.a === "37") {
return "3";
}
};
RSVP.all([
context.jio.post({"a": "5", "b": "inversion", "c": 2}),
context.jio.post({"a": "62", "b": "division", "c": 4}),
context.jio.post({"a": "37", "b": "prolong", "c": 8})
])
.then(function () {
return new RSVP.Promise(function (resolve) {
request = indexedDB.open('jio:index2_test');
request.onsuccess = function () {
resolve(request.result);
};
});
})
.then(function (result) {
equal(result.version, 1);
equal(result.name, 'jio:index2_test');
equal(result.objectStoreNames.length, 1);
equal(result.objectStoreNames[0], 'index-store');
store = result.transaction('index-store').objectStore('index-store');
equal(store.indexNames.length, 2);
equal(store.keyPath, "id");
deepEqual(Array.from(store.indexNames).sort(), ['Index-a', 'Index-b']);
equal(store.index('Index-a').keyPath, 'doc.a');
equal(store.index('Index-b').keyPath, 'doc.b');
equal(store.index('Index-a').unique, false);
equal(store.index('Index-b').unique, false);
return new RSVP.Promise(function (resolve) {
records = store.getAll();
records.onsuccess = function () {
resolve(records);
};
});
})
.then(function (values) {
deepEqual(values.result.sort(idCompare), [
{"id": "1", "doc": {"a": "5", "b": "inversion"}},
{"id": "2", "doc": {"a": "62", "b": "division"}},
{"id": "3", "doc": {"a": "37", "b": "prolong"}}
]);
})
.then(function () {
request.result.close();
})
.fail(function (error) {
console.log(error);
})
.always(function () {
start();
});
});
/////////////////////////////////////////////////////////////////
// indexStorage2.remove
/////////////////////////////////////////////////////////////////
module("indexStorage2.remove", {
teardown: function () {
deleteIndexedDB(this.jio);
}
});
test("Remove values", function () {
var context = this, request, store, records;
context.jio = jIO.createJIO({
type: "index2",
database: "index2_test",
index_keys: ["a", "b"],
sub_storage: {
type: "dummystorage3"
}
});
stop();
expect(3);
DummyStorage3.prototype.put = function (id) {
return id;
};
DummyStorage3.prototype.remove = function (id) {
equal(id, "33");
};
RSVP.all([
context.jio.put("32", {"a": "894", "b": "inversion", "c": 2}),
context.jio.put("33", {"a": "65", "b": "division", "c": 4}),
context.jio.put("34", {"a": "65", "b": "prolong", "c": 8})
])
.then(function () {
return new RSVP.Promise(function (resolve) {
request = indexedDB.open('jio:index2_test');
request.onsuccess = function () {
resolve(request.result);
};
});
})
.then(function (result) {
store = result.transaction('index-store').objectStore('index-store');
return new RSVP.Promise(function (resolve) {
records = store.getAll();
records.onsuccess = function () {
resolve(records);
};
});
})
.then(function (values) {
deepEqual(values.result.sort(idCompare), [
{"id": "32", "doc": {"a": "894", "b": "inversion"}},
{"id": "33", "doc": {"a": "65", "b": "division"}},
{"id": "34", "doc": {"a": "65", "b": "prolong"}}
]);
})
.then(function () {
return context.jio.remove("33");
})
.then(function () {
store = request.result.transaction('index-store')
.objectStore('index-store');
return new RSVP.Promise(function (resolve) {
records = store.getAll();
records.onsuccess = function () {
resolve(records);
};
});
})
.then(function (values) {
deepEqual(values.result.sort(idCompare), [
{"id": "32", "doc": {"a": "894", "b": "inversion"}},
{"id": "34", "doc": {"a": "65", "b": "prolong"}}
]);
})
.then(function () {
request.result.close();
})
.fail(function (error) {
console.log(error);
})
.always(function () {
start();
});
});
}(jIO, QUnit, indexedDB, Blob));
\ 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