Commit f89b3df8 authored by Sven Franck's avatar Sven Franck

indexStorage: POST new API and qunit tests

parent a9c06bba
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */ /*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global jIO: true, localStorage: true, setTimeout: true */ /*global jIO: true, localStorage: true, setTimeout: true */
/**
* JIO Index Storage.
* Manages indexes for specified storages.
* Description:
* {
* "type": "index",
* "indices": [
* {"indexA",["field_A"]},
* {"indexAB",["field_A","field_B"]}
* ],
* "storage": [
* <sub storage description>,
* ...
* ]
* }
* Index file will contain
* {
* "_id": "ipost_indices.json",
* "indexA": {
* "keyword_abc": ["some_id","some_other_id",...]
* },
* "indexAB": {
* "keyword_abc": ["some_id"],
* "keyword_def": ["some_id"]
* }
* }
*/
jIO.addStorageType('indexed', function (spec, my) { jIO.addStorageType('indexed', function (spec, my) {
"use strict";
var that, priv = {}, spec;
spec = spec || {}; spec = spec || {};
var that = my.basicStorage(spec, my), that = my.basicStorage(spec, my);
priv = {},
validatestate_sub_storage = spec.storage || false,
storage_object_name = 'jio/indexed_storage_object',
storage_file_object_name,
super_serialized = that.serialized;
priv.sub_storage_spec = spec.storage || { priv.indices = spec.indices;
type: 'base' priv.substorage_key = "sub_storage";
}; priv.substorage = spec[priv.substorage_key];
priv.sub_storage_string = JSON.stringify(priv.sub_storage_spec); priv.index_indicator = spec.sub_storage.application_name || "index";
priv.index_suffix = priv.index_indicator + "_indices.json";
storage_file_object_name = 'jio/indexed_file_object/' + my.env = my.env || spec.env || {};
priv.sub_storage_string;
that.serialized = function () { that.specToStore = function () {
var o = super_serialized(); var o = {};
o.storage = priv.sub_storage_spec; o[priv.substorage_key] = priv.substorage;
o.env = my.env;
return o; return o;
}; };
that.validateState = function () { /**
if (!validatestate_sub_storage) { * Generate a new uuid
return 'Need at least one parameter: "storage" ' + * @method generateUuid
'containing storage specifications.'; * @return {string} The new uuid
*/
priv.generateUuid = function () {
var S4 = function () {
var i, string = Math.floor(
Math.random() * 0x10000 /* 65536 */
).toString(16);
for (i = string.length; i < 4; i += 1) {
string = "0" + string;
} }
return ''; return string;
};
return S4() + S4() + "-" +
S4() + "-" +
S4() + "-" +
S4() + "-" +
S4() + S4() + S4();
}; };
priv.secureDocId = function (string) { /**
var split = string.split('/'), * Escape string before storing
i; * @method sanitizeValue
if (split[0] === '') { * @param {string} s The string to be sanitized
split = split.slice(1); * @return {string} The sanitized string
*/
priv.sanitizeValue = function (s) {
return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')
};
/**
* Get number of elements in object
* @method getObjectSize
* @param {object} obj The object to check
* @return {number} size The amount of elements in the object
*/
priv.getObjectSize = function (obj) {
var size = 0, key;
for (key in obj) {
if (obj.hasOwnProperty(key)) {
size += 1;
} }
for (i = 0; i < split.length; i += 1) { }
if (split[i] === '') { return size;
return ''; };
/**
* Creates an empty indices array
* @method createEmptyIndexArray
* @param {array} indices An array of indices (optional)
* @return {object} The new index array
*/
priv.createEmptyIndexArray = function (indices) {
var obj, i, j = priv.indices.length,
new_index_object = {}, new_index_name;
if (indices === undefined) {
for (i = 0; i < j; i += 1) {
new_index_name = priv.indices[i]["name"];
new_index_object[new_index_name] = {};
} }
} }
return split.join('%2F'); return new_index_object;
}; };
priv.indexStorage = function () { /**
var obj = localStorage.getItem(storage_object_name) || {}; * Find id in indices
obj[priv.sub_storage_spec] = new Date().getTime(); * @method docidInIndex
localStorage.setItem(storage_object_name, obj); * @param {object} indices The file containing the indeces
* @param {object} doc The document which should be added to the index
* @return {boolean} true/false
*/
priv.docidInIndex = function (indices, doc) {
var i, j, l = priv.indices.length, elements_to_check,
index, index_name, index_length;
// loop indices
for (i = 0; i < l; i += 1) {
index = priv.indices[i];
index_name = index["name"];
index_length = index.fields.length;
elements_to_check = priv.getObjectSize(indices[index_name]);
if (elements_to_check > 0) {
for (var key in indices[index_name]) {
var obj = indices[index_name][key];
for (var prop in obj) {
if (obj[prop] === doc._id) {
return true;
}
}
}
}
}
return false;
}
/**
* Adds entries to indices
* @method createEmptyIndexArray
* @param {object} indices The file containing the indeces
* @param {object} doc The document which should be added to the index
*/
priv.updateIndeces = function (indices, doc) {
var i, j, k, m, value,
index, index_name, index_length, index_field_array,
l = priv.indices.length,
docid = doc._id;
// loop indices
for (i = 0; i < l; i += 1) {
index = priv.indices[i];
index_name = index["name"];
index_length = index.fields.length;
index_field_array = [];
// loop index fields [keywords]
for (j = 0; j < index_length; j += 1) {
value = doc[index.fields[j]];
if (value !== undefined) {
index_field_array.push(value);
}
}
m = index_field_array.length;
if (m) {
for (k = 0; k < m; k += 1) {
if (indices[index_name] !== undefined) {
if (indices[index_name][index_field_array[k]] === undefined) {
indices[index_name][index_field_array[k]] = [];
}
indices[index_name][index_field_array[k]].push(docid);
}
}
}
}
return indices;
}; };
/**
* Post document to substorage and create/update index file(s)
* @method post
* @param {object} command The JIO command
*/
that.post = function (command) {
var f = {}, indices, doc, docid;
doc = command.cloneDoc();
docid = command.getDocId();
if (typeof docid !== "string") {
doc._id = priv.generateUuid();
docid = doc._id;
}
f.getIndices = function () {
var option = command.cloneOption();
if (option.max_retry === 0) {
option.max_retry = 3;
}
that.addJob(
"get",
priv.substorage,
priv.index_suffix,
option,
function (response) {
indices = response;
f.postDocument("put");
},
function (err) {
switch (err.status) {
case 404:
indices = priv.createEmptyIndexArray();
f.postDocument("post");
break;
default:
err.message = "Cannot retrieve index array";
that.error(err);
break;
}
}
);
};
f.postDocument = function (index_update_method) {
// if the index file already has an entry with this id,
// the document already exists
if (priv.docidInIndex(indices, doc)) {
// POST the document already exists
that.error({
"status": 409,
"statusText": "Conflicts",
"error": "conflicts",
"message": "Cannot create a new document",
"reason": "Document already exists"
});
return;
} else {
indices = priv.updateIndeces(indices, doc);
that.addJob(
"post",
priv.substorage,
doc,
command.cloneOption(),
function () {
f.sendIndices(index_update_method);
},
function (err) {
switch (err.status) {
case 409:
// file already exists
f.sendIndices(index_update_method);
break;
default:
err.message = "Cannot upload document";
that.error(err);
break;
}
}
);
}
};
f.sendIndices = function (method) {
indices._id = priv.index_suffix;
that.addJob(
method,
priv.substorage,
indices,
command.cloneOption(),
function () {
that.success({
"ok": true,
"id": docid
});
},
function (err) {
// xxx do we try to delete the posted document ?
err.message = "Cannot save index file";
that.error(err);
}
);
};
f.getIndices();
};
/*
/**
* @method formatToFileObject
* @param {} row
* @return {} obj
*//*
priv.formatToFileObject = function (row) { priv.formatToFileObject = function (row) {
var k, obj = { var k, obj = {
_id: row.id _id: row.id
...@@ -62,7 +309,12 @@ jIO.addStorageType('indexed', function (spec, my) { ...@@ -62,7 +309,12 @@ jIO.addStorageType('indexed', function (spec, my) {
} }
return obj; return obj;
}; };
*/
/**
* @method allDocs
* @param {} files_object
* @return {} obj
*//*
priv.allDocs = function (files_object) { priv.allDocs = function (files_object) {
var k, obj = { var k, obj = {
rows: [] rows: []
...@@ -79,7 +331,11 @@ jIO.addStorageType('indexed', function (spec, my) { ...@@ -79,7 +331,11 @@ jIO.addStorageType('indexed', function (spec, my) {
obj.total_rows = obj.rows.length; obj.total_rows = obj.rows.length;
return obj; return obj;
}; };
*/
/**
* @method setFileArray
* @param {} file_array
*//*
priv.setFileArray = function (file_array) { priv.setFileArray = function (file_array) {
var i, obj = {}; var i, obj = {};
for (i = 0; i < file_array.length; i += 1) { for (i = 0; i < file_array.length; i += 1) {
...@@ -87,67 +343,84 @@ jIO.addStorageType('indexed', function (spec, my) { ...@@ -87,67 +343,84 @@ jIO.addStorageType('indexed', function (spec, my) {
} }
localStorage.setItem(storage_file_object_name, obj); localStorage.setItem(storage_file_object_name, obj);
}; };
*/
/**
* @method getFileObject
* @param {} docid
* @return {} obj
*//*
priv.getFileObject = function (docid) { priv.getFileObject = function (docid) {
var obj = localStorage.getItem(storage_file_object_name) || {}; var obj = localStorage.getItem(storage_file_object_name) || {};
return obj[docid]; return obj[docid];
}; };
*/
/**
* @method addFile
* @param {} file_obj
*//*
priv.addFile = function (file_obj) { priv.addFile = function (file_obj) {
var obj = localStorage.getItem(storage_file_object_name) || {}; var obj = localStorage.getItem(storage_file_object_name) || {};
obj[file_obj._id] = file_obj; obj[file_obj._id] = file_obj;
localStorage.setItem(storage_file_object_name, obj); localStorage.setItem(storage_file_object_name, obj);
}; };
*/
/**
* @method removeFile
* @param {} docid
*//*
priv.removeFile = function (docid) { priv.removeFile = function (docid) {
var obj = localStorage.getItem(storage_file_object_name) || {}; var obj = localStorage.getItem(storage_file_object_name) || {};
delete obj[docid]; delete obj[docid];
localStorage.setItem(storage_file_object_name, obj); localStorage.setItem(storage_file_object_name, obj);
}; };
*/
/** /**
* updates the storage. * updates the storage.
* It will retreive all files from a storage. It is an asynchronous task * It will retreive all files from a storage. It is an asynchronous task
* so the update can be on going even if IndexedStorage has already * so the update can be on going even if IndexedStorage has already
* returned the result. * returned the result.
* @method update * @method update
*/ *//*
priv.update = function () { priv.update = function () {
var success = function (val) { that.addJob(
priv.setFileArray(val.rows); 'allDocs',
}; priv.sub_storage_spec,
that.addJob('allDocs', priv.sub_storage_spec, null, { null,
max_retry: 3 {max_retry: 3},
}, success, function () {}); function (response) {
}; priv.setFileArray(response.rows);
},
that.post = function (command) { function () {}
that.put(command); );
}; };
*/
/** /**
* Saves a document. * Add put job to substorage and create/update index file(s)
* @method put * @method put
*/ * @param {object} command The JIO command
*//*
that.put = function (command) { that.put = function (command) {
var cloned_doc = command.cloneDoc(), var cloned_doc = command.cloneDoc(),
cloned_option = command.cloneOption(), cloned_option = command.cloneOption();
success = function (val) { // create/update indexStorage
// fwd job
that.addJob('put', priv.sub_storage_spec, cloned_doc,
cloned_option,
function (response) {
priv.update(); priv.update();
that.success(val); that.success(response);
}, },
error = function (err) { function (error) {
that.error(err); that.error(error);
}
);
}; };
priv.indexStorage();
that.addJob('put', priv.sub_storage_spec, cloned_doc,
cloned_option, success, error);
}; // end put
/** *//**
* Loads a document. * Loads a document.
* @method get * @method get
*/ *//*
that.get = function (command) { that.get = function (command) {
// jslint unused var file_array // jslint unused var file_array
var success = function (val) { var success = function (val) {
...@@ -177,11 +450,27 @@ jIO.addStorageType('indexed', function (spec, my) { ...@@ -177,11 +450,27 @@ jIO.addStorageType('indexed', function (spec, my) {
get(); get();
} }
}; // end get }; // end get
*//**
/** * Removes a document.
* @method remove
*//*
that.remove = function (command) {
var success = function (val) {
priv.removeFile(command.getDocId());
priv.update();
that.success(val);
},
error = function (err) {
that.error(err);
};
that.addJob('remove', priv.sub_storage_spec, command.cloneDoc(),
command.cloneOption(), success, error);
};
*//**
* Gets a document list. * Gets a document list.
* @method allDocs * @method allDocs
*/ */
/*
that.allDocs = function (command) { that.allDocs = function (command) {
var obj = localStorage.getItem(storage_file_object_name), var obj = localStorage.getItem(storage_file_object_name),
success, success,
...@@ -204,23 +493,6 @@ jIO.addStorageType('indexed', function (spec, my) { ...@@ -204,23 +493,6 @@ jIO.addStorageType('indexed', function (spec, my) {
command.cloneOption(), success, error); command.cloneOption(), success, error);
} }
}; // end allDocs }; // end allDocs
/**
* Removes a document.
* @method remove
*/ */
that.remove = function (command) {
var success = function (val) {
priv.removeFile(command.getDocId());
priv.update();
that.success(val);
},
error = function (err) {
that.error(err);
};
that.addJob('remove', priv.sub_storage_spec, command.cloneDoc(),
command.cloneOption(), success, error);
}; // end remove
return that; return that;
}); });
...@@ -2619,9 +2619,64 @@ test ('Remove document', function () { ...@@ -2619,9 +2619,64 @@ test ('Remove document', function () {
o.mytest('DummyStorageAllOK,3tries: remove document.',{ok:true,id:'file'}); o.mytest('DummyStorageAllOK,3tries: remove document.',{ok:true,id:'file'});
o.jio.stop(); o.jio.stop();
}); });
*/
module ("Jio IndexStorage");
test ("Post", function () {
module ('Jio IndexedStorage'); var o = generateTools(this);
o.jio = JIO.newJio({
"type": "indexed",
"indices": [{
"name":"indexA",
"fields":["findMeA"]
},{
"name":"indexAB",
"fields":["findMeA","findMeB"]
}],
"sub_storage": {
"type": "local",
"username": "ipost",
"application_name": "ipost"
}
});
// post without id
o.spy (o, "status", undefined, "Post without id");
o.jio.post({}, o.f);
o.tick(o);
// post non empty document
o.doc = {"_id": "some_id", "title": "myPost1",
"findMeA":"keyword_abc", "findMeB":"keyword_def"
};
o.spy (o, "value", {"ok": true, "id": "some_id"}, "Post document");
o.jio.post(o.doc, o.f);
o.tick(o);
// check document
o.indexPost = {
"indexAB": {"keyword_abc":["some_id"], "keyword_def":["some_id"]},
"indexA": {"keyword_abc":["some_id"]}
};
deepEqual(
o.jio.get("ipost_indices.json"),
o.indexPost,
"Check index file"
);
// post and document already exists
o.doc = {"_id": "some_id", "title": "myPost2",
"findMeA":"keyword_ghi", "findMeB":"keyword_jkl"
}
o.spy (o, "status", 409, "Post and document already exists");
o.jio.post(o.doc, o.f);
o.tick(o);
o.jio.stop();
});
/*
test ('Document load', function () { test ('Document load', function () {
var o = {}; o.clock = this.sandbox.useFakeTimers(); var o = {}; o.clock = this.sandbox.useFakeTimers();
o.clock.tick(base_tick); o.clock.tick(base_tick);
...@@ -2665,27 +2720,6 @@ test ('Document load', function () { ...@@ -2665,27 +2720,6 @@ test ('Document load', function () {
o.jio.stop(); o.jio.stop();
}); });
test ('Document save', function () {
var o = {}; o.clock = this.sandbox.useFakeTimers();
o.clock.tick(base_tick);
o.jio = JIO.newJio({type:'indexed',
storage:{type:'dummyall3tries',
username:'indexsave'}});
o.f = function (err,val) {
if (err) {
err = err.status;
}
deepEqual (err || val,{ok:true,id:'file'},'document save');
};
this.spy(o,'f');
o.jio.put({_id:'file',content:'content'},{max_retry:3},o.f);
o.clock.tick(2000);
if (!o.f.calledOnce){
ok (false, 'no response / too much results');
}
o.jio.stop();
});
test ('Get document list', function () { test ('Get document list', function () {
var o = {}; o.clock = this.sandbox.useFakeTimers(); var o = {}; o.clock = this.sandbox.useFakeTimers();
o.clock.tick(base_tick); o.clock.tick(base_tick);
...@@ -2742,7 +2776,8 @@ test ('Remove document', function () { ...@@ -2742,7 +2776,8 @@ test ('Remove document', function () {
o.jio.stop(); o.jio.stop();
}); });
*/
/*
module ('Jio CryptedStorage'); module ('Jio CryptedStorage');
test ('Document save' , function () { test ('Document save' , function () {
......
...@@ -28,9 +28,11 @@ ...@@ -28,9 +28,11 @@
src="../src/jio.storage/replicaterevisionstorage.js"> src="../src/jio.storage/replicaterevisionstorage.js">
</script> </script>
<!-- webDav -->
<script type="text/javascript" src="../lib/jquery/jquery.min.js"></script> <script type="text/javascript" src="../lib/jquery/jquery.min.js"></script>
<script type="text/javascript" src="../src/jio.storage/davstorage.js"> <script type="text/javascript" src="../src/jio.storage/davstorage.js">
</script>
<script type="text/javascript" src="../src/jio.storage/indexstorage.js">
</script>
</script> </script>
<script type="text/javascript" src="./jiotests.js"></script> <script type="text/javascript" src="./jiotests.js"></script>
......
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