Commit bc17b228 authored by Sven Franck's avatar Sven Franck

jslint pass revisionstorage.js

parent e45edf9f
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global jIO: true, hex_sha256: true, setTimeout: true */
/** /**
* JIO Revision Storage. * JIO Revision Storage.
* It manages document version and can generate conflicts. * It manages document version and can generate conflicts.
...@@ -8,869 +10,875 @@ ...@@ -8,869 +10,875 @@
* } * }
*/ */
jIO.addStorageType('revision', function (spec, my) { jIO.addStorageType('revision', function (spec, my) {
"use strict"; "use strict";
var that, priv = {}; var that, priv = {};
spec = spec || {}; spec = spec || {};
that = my.basicStorage(spec, my); that = my.basicStorage(spec, my);
priv.substorage_key = "sub_storage"; priv.substorage_key = "sub_storage";
priv.doctree_suffix = ".revision_tree.json"; priv.doctree_suffix = ".revision_tree.json";
priv.substorage = spec[priv.substorage_key]; priv.substorage = spec[priv.substorage_key];
that.specToStore = function () { that.specToStore = function () {
var o = {}; var o = {};
o[priv.substorage_key] = priv.substorage; o[priv.substorage_key] = priv.substorage;
return o; return o;
};
/**
* Generate a new uuid
* @method generateUuid
* @return {string} The new uuid
*/
priv.generateUuid = function () {
var S4 = function () {
/* 65536 */
var i, string = Math.floor(
Math.random() * 0x10000
).toString(16);
for (i = string.length; i < 4; i += 1) {
string = '0' + string;
}
return string;
}; };
return S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() +
/** S4() + S4();
* Generate a new uuid };
* @method generateUuid
* @return {string} The new uuid /**
*/ * Generates a hash code of a string
priv.generateUuid = function () { * @method hashCode
var S4 = function () { * @param {string} string The string to hash
var i, string = Math.floor( * @return {string} The string hash code
Math.random() * 0x10000 /* 65536 */ */
).toString(16); priv.hashCode = function (string) {
for (i = string.length; i < 4; i += 1) { return hex_sha256(string);
string = '0'+string; };
}
return string; /**
}; * Returns an array version of a revision string
return S4() + S4() + "-" + * @method revisionToArray
S4() + "-" + * @param {string} revision The revision string
S4() + "-" + * @return {array} Array containing a revision number and a hash
S4() + "-" + */
S4() + S4() + S4(); priv.revisionToArray = function (revision) {
if (typeof revision === "string") {
return [parseInt(revision.split('-')[0], 10),
revision.split('-')[1]];
}
return revision;
};
/**
* Generates the next revision of [previous_revision]. [string] helps us
* to generate a hash code.
* @methode generateNextRev
* @param {string} previous_revision The previous revision
* @param {object} doc The document metadata
* @param {object} revisions The revision history
* @param {boolean} deleted_flag The deleted flag
* @return {array} 0:The next revision number and 1:the hash code
*/
priv.generateNextRevision = function (previous_revision,
doc, revisions, deleted_flag) {
var string = JSON.stringify(doc) + JSON.stringify(revisions) +
JSON.stringify(deleted_flag ? true : false);
if (typeof previous_revision === "number") {
return [previous_revision + 1, priv.hashCode(string)];
}
previous_revision = priv.revisionToArray(previous_revision);
return [previous_revision[0] + 1, priv.hashCode(string)];
};
/**
* Checks a revision format
* @method checkRevisionFormat
* @param {string} revision The revision string
* @return {boolean} True if ok, else false
*/
priv.checkRevisionFormat = function (revision) {
return (/^[0-9]+-[0-9a-zA-Z]+$/.test(revision));
};
/**
* Creates an empty document tree
* @method createDocumentTree
* @param {array} children An array of children (optional)
* @return {object} The new document tree
*/
priv.createDocumentTree = function (children) {
return {
"children": children || []
}; };
};
/**
* Generates a hash code of a string /**
* @method hashCode * Creates a new document tree node
* @param {string} string The string to hash * @method createDocumentTreeNode
* @return {string} The string hash code * @param {string} revision The node revision
*/ * @param {string} status The node status
priv.hashCode = function (string) { * @param {array} children An array of children (optional)
return hex_sha256(string); * @return {object} The new document tree node
*/
priv.createDocumentTreeNode = function (revision, status, children) {
return {
"rev": revision,
"status": status,
"children": children || []
}; };
};
/**
* Returns an array version of a revision string /**
* @method revisionToArray * Gets the specific revision from a document tree.
* @param {string} revision The revision string * @method getRevisionFromDocumentTree
* @return {array} Array containing a revision number and a hash * @param {object} document_tree The document tree
*/ * @param {string} revision The specific revision
priv.revisionToArray = function (revision) { * @return {array} The good revs info array
if (typeof revision === "string") { */
return [parseInt(revision.split('-')[0],10), priv.getRevisionFromDocumentTree = function (document_tree, revision) {
revision.split('-')[1]] var result, search, revs_info = [];
result = [];
// search method fills "result" with the good revs info
search = function (document_tree) {
var i;
if (document_tree.rev !== undefined) {
// node is not root
revs_info.unshift({
"rev": document_tree.rev,
"status": document_tree.status
});
if (document_tree.rev === revision) {
result = revs_info;
return;
} }
return revision; }
}; // This node has children
for (i = 0; i < document_tree.children.length; i += 1) {
/** // searching deeper to find the good rev
* Generates the next revision of [previous_revision]. [string] helps us search(document_tree.children[i]);
* to generate a hash code. if (result.length > 0) {
* @methode generateNextRev // The result is already found
* @param {string} previous_revision The previous revision return;
* @param {object} doc The document metadata
* @param {object} revisions The revision history
* @param {boolean} deleted_flag The deleted flag
* @return {array} 0:The next revision number and 1:the hash code
*/
priv.generateNextRevision = function (previous_revision,
doc, revisions, deleted_flag) {
var string = JSON.stringify(doc) + JSON.stringify(revisions) +
JSON.stringify(deleted_flag? true: false);
if (typeof previous_revision === "number") {
return [previous_revision + 1, priv.hashCode(string)];
} }
previous_revision = priv.revisionToArray(previous_revision); revs_info.shift();
return [previous_revision[0]+1, priv.hashCode(string)]; }
};
/**
* Checks a revision format
* @method checkRevisionFormat
* @param {string} revision The revision string
* @return {boolean} True if ok, else false
*/
priv.checkRevisionFormat = function (revision) {
return (/^[0-9]+-[0-9a-zA-Z]+$/.test(revision));
};
/**
* Creates an empty document tree
* @method createDocumentTree
* @param {array} children An array of children (optional)
* @return {object} The new document tree
*/
priv.createDocumentTree = function(children) {
return {"children":children || []};
}; };
search(document_tree);
/** return result;
* Creates a new document tree node };
* @method createDocumentTreeNode
* @param {string} revision The node revision /**
* @param {string} status The node status * Gets the winner revision from a document tree.
* @param {array} children An array of children (optional) * The winner is the deeper revision on the left.
* @return {object} The new document tree node * @method getWinnerRevisionFromDocumentTree
*/ * @param {object} document_tree The document tree
priv.createDocumentTreeNode = function(revision,status,children) { * @return {array} The winner revs info array
return {"rev":revision,"status":status,"children":children || []}; */
}; priv.getWinnerRevisionFromDocumentTree = function (document_tree) {
var result, search, revs_info = [];
/** result = [];
* Gets the specific revision from a document tree. // search method fills "result" with the winner revs info
* @method getRevisionFromDocumentTree search = function (document_tree, deep) {
* @param {object} document_tree The document tree var i;
* @param {string} revision The specific revision if (document_tree.rev !== undefined) {
* @return {array} The good revs info array // node is not root
*/ revs_info.unshift({
priv.getRevisionFromDocumentTree = function (document_tree, revision) { "rev": document_tree.rev,
var i, result, search, revs_info = []; "status": document_tree.status
result = []; });
// search method fills "result" with the good revs info }
search = function (document_tree) { if (document_tree.children.length === 0 && document_tree.status !==
var i; "deleted") {
if (typeof document_tree.rev !== "undefined") { // This node is a leaf
// node is not root if (result.length < deep) {
revs_info.unshift({ // The leaf is deeper than result
"rev":document_tree.rev, result = [];
"status":document_tree.status for (i = 0; i < revs_info.length; i += 1) {
}); result.push(revs_info[i]);
if (document_tree.rev === revision) { }
result = revs_info;
return;
}
}
// This node has children
for (i = 0; i < document_tree.children.length; i += 1) {
// searching deeper to find the good rev
search(document_tree.children[i]);
if (result.length > 0) {
// The result is already found
return;
}
revs_info.shift();
}
};
search(document_tree);
return result;
};
/**
* Gets the winner revision from a document tree.
* The winner is the deeper revision on the left.
* @method getWinnerRevisionFromDocumentTree
* @param {object} document_tree The document tree
* @return {array} The winner revs info array
*/
priv.getWinnerRevisionFromDocumentTree = function (document_tree) {
var i, result, search, revs_info = [];
result = [];
// search method fills "result" with the winner revs info
search = function (document_tree, deep) {
var i;
if (typeof document_tree.rev !== "undefined") {
// node is not root
revs_info.unshift({
"rev":document_tree.rev,
"status":document_tree.status
});
}
if (document_tree.children.length === 0 &&
document_tree.status !== "deleted" ) {
// This node is a leaf
if (result.length < deep) {
// The leaf is deeper than result
result = [];
for (i = 0; i < revs_info.length; i += 1) {
result.push(revs_info[i]);
}
}
return;
}
// This node has children
for (i = 0; i < document_tree.children.length; i += 1) {
// searching deeper to find the deeper leaf
search(document_tree.children[i], deep+1);
revs_info.shift();
}
};
search(document_tree, 0);
return result;
};
/**
* Add a document revision to the document tree
* @method postToDocumentTree
* @param {object} doctree The document tree object
* @param {object} doc The document object
* @param {boolean} set_node_to_deleted Set the revision to deleted
* @return {array} The added document revs_info
*/
priv.postToDocumentTree = function (doctree, doc, set_node_to_deleted) {
var i, revs_info, next_rev, next_rev_str, selectNode, selected_node,
flag;
flag = set_node_to_deleted === true ? "deleted" : "available";
revs_info = [];
selected_node = doctree;
selectNode = function (node) {
var i;
if (typeof node.rev !== "undefined") {
// node is not root
revs_info.unshift({"rev":node.rev,"status":node.status});
}
if (node.rev === doc._rev) {
selected_node = node;
return "node_selected";
} else {
for (i = 0; i < node.children.length; i += 1) {
if (selectNode(node.children[i]) === "node_selected") {
return "node_selected";
}
revs_info.shift();
}
}
};
if (typeof doc._rev === "string") {
// document has a previous revision
if (selectNode(selected_node) !== "node_selected") {
// no node was selected, so add a node with a specific rev
revs_info.unshift({
"rev": doc._rev,
"status": "missing"
});
selected_node.children.unshift({
"rev": doc._rev,
"status": "missing",
"children": []
});
selected_node = selected_node.children[0];
}
} }
next_rev = priv.generateNextRevision( return;
doc._rev || 0, doc, priv.revsInfoToHistory(revs_info), }
set_node_to_deleted); // This node has children
next_rev_str = next_rev.join("-"); for (i = 0; i < document_tree.children.length; i += 1) {
// don't add if the next rev already exists // searching deeper to find the deeper leaf
for (i = 0; i < selected_node.children.length; i += 1) { search(document_tree.children[i], deep + 1);
if (selected_node.children[i].rev === next_rev_str) { revs_info.shift();
revs_info.unshift({ }
"rev": next_rev_str, };
"status": flag search(document_tree, 0);
}); return result;
if (selected_node.children[i].status !== flag) { };
selected_node.children[i].status = flag;
} /**
return revs_info; * Add a document revision to the document tree
} * @method postToDocumentTree
* @param {object} doctree The document tree object
* @param {object} doc The document object
* @param {boolean} set_node_to_deleted Set the revision to deleted
* @return {array} The added document revs_info
*/
priv.postToDocumentTree = function (doctree, doc, set_node_to_deleted) {
var i, revs_info, next_rev, next_rev_str, selectNode, selected_node,
flag;
flag = set_node_to_deleted === true ? "deleted" : "available";
revs_info = [];
selected_node = doctree;
selectNode = function (node) {
var i;
if (node.rev !== undefined) {
// node is not root
revs_info.unshift({
"rev": node.rev,
"status": node.status
});
}
if (node.rev === doc._rev) {
selected_node = node;
return "node_selected";
}
for (i = 0; i < node.children.length; i += 1) {
if (selectNode(node.children[i]) === "node_selected") {
return "node_selected";
} }
revs_info.shift();
}
};
if (typeof doc._rev === "string") {
// document has a previous revision
if (selectNode(selected_node) !== "node_selected") {
// no node was selected, so add a node with a specific rev
revs_info.unshift({ revs_info.unshift({
"rev": next_rev.join('-'), "rev": doc._rev,
"status": flag "status": "missing"
}); });
selected_node.children.unshift({ selected_node.children.unshift({
"rev": next_rev.join('-'), "rev": doc._rev,
"status": flag, "status": "missing",
"children": [] "children": []
}); });
selected_node = selected_node.children[0];
}
}
next_rev = priv.generateNextRevision(
doc._rev || 0,
doc,
priv.revsInfoToHistory(revs_info),
set_node_to_deleted
);
next_rev_str = next_rev.join("-");
// don't add if the next rev already exists
for (i = 0; i < selected_node.children.length; i += 1) {
if (selected_node.children[i].rev === next_rev_str) {
revs_info.unshift({
"rev": next_rev_str,
"status": flag
});
if (selected_node.children[i].status !== flag) {
selected_node.children[i].status = flag;
}
return revs_info; return revs_info;
}
}
revs_info.unshift({
"rev": next_rev.join('-'),
"status": flag
});
selected_node.children.unshift({
"rev": next_rev.join('-'),
"status": flag,
"children": []
});
return revs_info;
};
/**
* Gets an array of leaves revisions from document tree
* @method getLeavesFromDocumentTree
* @param {object} document_tree The document tree
* @param {string} except The revision to except
* @return {array} The array of leaves revisions
*/
priv.getLeavesFromDocumentTree = function (document_tree, except) {
var result, search;
result = [];
// search method fills [result] with the winner revision
search = function (document_tree) {
var i;
if (except !== undefined && except === document_tree.rev) {
return;
}
if (document_tree.children.length === 0 && document_tree.status !==
"deleted") {
// This node is a leaf
result.push(document_tree.rev);
return;
}
// This node has children
for (i = 0; i < document_tree.children.length; i += 1) {
// searching deeper to find the deeper leaf
search(document_tree.children[i]);
}
}; };
search(document_tree);
/** return result;
* Gets an array of leaves revisions from document tree };
* @method getLeavesFromDocumentTree
* @param {object} document_tree The document tree /**
* @param {string} except The revision to except * Check if revision is a leaf
* @return {array} The array of leaves revisions * @method isRevisionALeaf
*/ * @param {string} revision revision to check
priv.getLeavesFromDocumentTree = function (document_tree, except) { * @param {array} leaves all leaves on tree
var i, result, search; * @return {boolean} true/false
result = []; */
// search method fills [result] with the winner revision priv.isRevisionALeaf = function (document_tree, revision) {
search = function (document_tree) { var result, search;
var i; result = undefined;
if (except !== undefined && except === document_tree.rev) { // search method fills "result" with the good revs info
return; search = function (document_tree) {
} var i;
if (document_tree.children.length === 0 && if (document_tree.rev !== undefined) {
document_tree.status !== "deleted") { // node is not root
// This node is a leaf if (document_tree.rev === revision) {
result.push(document_tree.rev); if (document_tree.children.length === 0) {
return; // This node is a leaf
} result = true;
// This node has children return;
for (i = 0; i < document_tree.children.length; i += 1) { }
// searching deeper to find the deeper leaf result = false;
search(document_tree.children[i]); return;
}
};
search(document_tree);
return result;
};
/**
* Check if revision is a leaf
* @method isRevisionALeaf
* @param {string} revision revision to check
* @param {array} leaves all leaves on tree
* @return {boolean} true/false
*/
priv.isRevisionALeaf = function (document_tree, revision) {
var i, result, search;
result = undefined;
// search method fills "result" with the good revs info
search = function (document_tree) {
var i;
if (typeof document_tree.rev !== "undefined") {
// node is not root
if (document_tree.rev === revision) {
if (document_tree.children.length === 0) {
// This node is a leaf
result = true
return;
}
result = false;
return;
}
}
// This node has children
for (i = 0; i < document_tree.children.length; i += 1) {
// searching deeper to find the good rev
search(document_tree.children[i]);
if (result !== undefined) {
// The result is already found
return;
}
}
};
search(document_tree);
return result || false;
};
/**
* Convert revs_info to a simple revisions history
* @method revsInfoToHistory
* @param {array} revs_info The revs info
* @return {object} The revisions history
*/
priv.revsInfoToHistory = function (revs_info) {
var revisions = {"start":0,"ids":[]}, i;
if (revs_info.length > 0) {
revisions.start = parseInt(revs_info[0].rev.split('-')[0],10);
}
for (i = 0; i < revs_info.length; i += 1) {
revisions.ids.push(revs_info[i].rev.split('-')[1]);
} }
return revisions; }
}; // This node has children
for (i = 0; i < document_tree.children.length; i += 1) {
/** // searching deeper to find the good rev
* Returns the revision of the revision position from a revs_info array. search(document_tree.children[i]);
* @method getRevisionFromPosition if (result !== undefined) {
* @param {array} revs_info The revs_info array // The result is already found
* @param {number} rev_pos The revision position number return;
* @return {string} The revision of the good position (empty string if fail)
*/
priv.getRevisionFromPosition = function (revs_info, rev_pos) {
var i;
for (i = revs_info.length - 1; i >= 0; i -= 1) {
if (priv.revisionToArray(revs_info[i].rev)[0] === rev_pos) {
return revs_info[i].rev;
}
} }
return ''; }
}; };
search(document_tree);
/** return result || false;
* Post the document metadata and create or update a document tree. };
* Options:
* - {boolean} keep_revision_history To keep the previous revisions /**
* (false by default) (NYI). * Convert revs_info to a simple revisions history
* @method post * @method revsInfoToHistory
* @param {object} command The JIO command * @param {array} revs_info The revs info
*/ * @return {object} The revisions history
that.post = function (command) { */
var f = {}, doctree, revs_info, doc, docid; priv.revsInfoToHistory = function (revs_info) {
doc = command.cloneDoc(); var revisions = {
docid = command.getDocId(); "start": 0,
"ids": []
if (typeof doc._rev === "string" && }, i;
!priv.checkRevisionFormat(doc._rev)) { if (revs_info.length > 0) {
revisions.start = parseInt(revs_info[0].rev.split('-')[0], 10);
}
for (i = 0; i < revs_info.length; i += 1) {
revisions.ids.push(revs_info[i].rev.split('-')[1]);
}
return revisions;
};
/**
* Returns the revision of the revision position from a revs_info array.
* @method getRevisionFromPosition
* @param {array} revs_info The revs_info array
* @param {number} rev_pos The revision position number
* @return {string} The revision of the good position (empty string if fail)
*/
priv.getRevisionFromPosition = function (revs_info, rev_pos) {
var i;
for (i = revs_info.length - 1; i >= 0; i -= 1) {
if (priv.revisionToArray(revs_info[i].rev)[0] === rev_pos) {
return revs_info[i].rev;
}
}
return '';
};
/**
* Post the document metadata and create or update a document tree.
* Options:
* - {boolean} keep_revision_history To keep the previous revisions
* (false by default) (NYI).
* @method post
* @param {object} command The JIO command
*/
that.post = function (command) {
var f = {}, doctree, revs_info, doc, docid;
doc = command.cloneDoc();
docid = command.getDocId();
if (typeof doc._rev === "string" && !priv.checkRevisionFormat(doc._rev)) {
that.error({
"status": 31,
"statusText": "Wrong Revision Format",
"error": "wrong_revision_format",
"message": "The document previous revision does not match " +
"^[0-9]+-[0-9a-zA-Z]+$",
"reason": "Previous revision is wrong"
});
return;
}
if (typeof docid !== "string") {
doc._id = priv.generateUuid();
docid = doc._id;
}
if (priv.update_doctree_allowed === undefined) {
priv.update_doctree_allowed = true;
}
f.getDocumentTree = function () {
var option = command.cloneOption();
if (option.max_retry === 0) {
option.max_retry = 3;
}
that.addJob(
"get",
priv.substorage,
docid + priv.doctree_suffix,
option,
function (response) {
doctree = response;
if (priv.update_doctree_allowed) {
f.postDocument("put");
} else {
that.error({ that.error({
"status": 31, "status": 409,
"statusText": "Wrong Revision Format", "statusText": "Conflict",
"error": "wrong_revision_format", "error": "conflict",
"message": "The document previous revision does not match "+ "message": "Cannot update a document",
"^[0-9]+-[0-9a-zA-Z]+$", "reason": "Document update conflict"
"reason": "Previous revision is wrong"
}); });
return; }
} },
if (typeof docid !== "string") { function (err) {
doc._id = priv.generateUuid(); switch (err.status) {
docid = doc._id; case 404:
doctree = priv.createDocumentTree();
f.postDocument("post");
break;
default:
err.message = "Cannot get document revision tree";
that.error(err);
break;
}
} }
if (priv.update_doctree_allowed === undefined) { );
priv.update_doctree_allowed = true;
}
f.getDocumentTree = function () {
var option = command.cloneOption();
if (option["max_retry"] === 0) {
option["max_retry"] = 3;
}
that.addJob(
"get",
priv.substorage,
docid+priv.doctree_suffix,
option,
function (response) {
doctree = response;
if (priv.update_doctree_allowed) {
f.postDocument("put");
} else {
that.error({
"status": 409,
"statusText": "Conflict",
"error": "conflict",
"message": "Cannot update a document",
"reason": "Document update conflict"
});
}
}, function (err) {
switch(err.status) {
case 404:
doctree = priv.createDocumentTree();
f.postDocument("post");
break;
default:
err.message = "Cannot get document revision tree";
that.error(err);
break;
}
}
);
};
f.postDocument = function (doctree_update_method) {
revs_info = priv.postToDocumentTree(doctree, doc);
doc._id = docid+"."+revs_info[0].rev;
that.addJob(
"post",
priv.substorage,
doc,
command.cloneOption(),
function (response) {
f.sendDocumentTree (doctree_update_method);
},
function (err) {
switch(err.status) {
case 409:
// file already exists
f.sendDocumentTree (doctree_update_method);
break;
default:
err.message = "Cannot upload document".
that.error(err);
break;
}
}
);
};
f.sendDocumentTree = function (method) {
doctree._id = docid+priv.doctree_suffix;
that.addJob(
method,
priv.substorage,
doctree,
command.cloneOption(),
function (response) {
that.success({
"ok":true,
"id":docid,
"rev":revs_info[0].rev
});
},
function (err) {
// xxx do we try to delete the posted document ?
err.message = "Cannot save document revision tree";
that.error(err);
}
);
};
f.getDocumentTree();
}; };
f.postDocument = function (doctree_update_method) {
/** revs_info = priv.postToDocumentTree(doctree, doc);
* Update the document metadata and update a document tree. doc._id = docid + "." + revs_info[0].rev;
* Options: that.addJob(
* - {boolean} keep_revision_history To keep the previous revisions "post",
* (false by default) (NYI). priv.substorage,
* @method put doc,
* @param {object} command The JIO command command.cloneOption(),
*/ function () {
that.put = function (command) { f.sendDocumentTree(doctree_update_method);
if (command.cloneDoc()._rev === undefined) { },
priv.update_doctree_allowed = false; function (err) {
switch (err.status) {
case 409:
// file already exists
f.sendDocumentTree(doctree_update_method);
break;
default:
err.message = "Cannot upload document";
that.error(err);
break;
}
} }
that.post(command); );
}; };
f.sendDocumentTree = function (method) {
/** doctree._id = docid + priv.doctree_suffix;
* Get the document metadata or attachment. that.addJob(
* Options: method,
* - {boolean} revs Add simple revision history (false by default). priv.substorage,
* - {boolean} revs_info Add revs info (false by default). doctree,
* - {boolean} conflicts Add conflict object (false by default). command.cloneOption(),
* @method get function () {
* @param {object} command The JIO command that.success({
*/ "ok": true,
that.get = function (command) { "id": docid,
var f = {}, doctree, revs_info, prev_rev, option; "rev": revs_info[0].rev
option = command.cloneOption(); });
if (option["max_retry"] === 0) { },
option["max_retry"] = 3; function (err) {
} // xxx do we try to delete the posted document ?
prev_rev = command.getOption("rev"); err.message = "Cannot save document revision tree";
if (typeof prev_rev === "string") { that.error(err);
if (!priv.checkRevisionFormat(prev_rev)) {
that.error({
"status": 31,
"statusText": "Wrong Revision Format",
"error": "wrong_revision_format",
"message": "The document previous revision does not match "+
"[0-9]+-[0-9a-zA-Z]+",
"reason": "Previous revision is wrong"
});
return;
}
}
f.getDocumentTree = function () {
that.addJob(
"get",
priv.substorage,
command.getDocId()+priv.doctree_suffix,
option,
function (response) {
doctree = response;
if (prev_rev === undefined) {
revs_info =
priv.getWinnerRevisionFromDocumentTree(doctree);
prev_rev = revs_info[0].rev;
} else {
revs_info =
priv.getRevisionFromDocumentTree(doctree, prev_rev);
}
f.getDocument(command.getDocId()+"."+prev_rev,
command.getAttachmentId());
}, function (err) {
switch(err.status) {
case 404:
that.error(err);
break;
default:
err.message = "Cannot get document revision tree";
that.error(err);
break;
}
}
);
};
f.getDocument = function (docid, attmtid) {
that.addJob(
"get",
priv.substorage,
docid,
option,
function (response) {
var attmt;
if (typeof response !== "string") {
if (attmtid !== undefined) {
if (response._attachments !== undefined) {
attmt = response._attachments[attmtid];
if (attmt !== undefined) {
prev_rev =
priv.getRevisionFromPosition(
revs_info, attmt.revpos);
f.getDocument(command.getDocId()+"."+
prev_rev+"/"+attmtid);
return;
}
}
that.error({
"status": 404,
"statusText": "Not Found",
"error": "not_found",
"message": "Cannot find the attachment",
"reason": "Attachment is missing"
});
return;
}
response._id = command.getDocId();
response._rev = prev_rev;
if (command.getOption("revs") === true) {
response._revisions =
priv.revsInfoToHistory(revs_info);
}
if (command.getOption("revs_info") === true) {
response._revs_info = revs_info;
}
if (command.getOption("conflicts") === true) {
response._conflicts =
priv.getLeavesFromDocumentTree(
doctree, prev_rev);
if (response._conflicts.length === 0) {
delete response._conflicts;
}
}
}
that.success(response);
}, function (err) {
that.error(err);
}
);
};
if (command.getAttachmentId() && prev_rev !== undefined) {
f.getDocument(command.getDocId()+"."+prev_rev+
"/"+command.getAttachmentId());
} else {
f.getDocumentTree();
} }
);
}; };
f.getDocumentTree();
/** };
* Remove document or attachment.
* Options: /**
* - {boolean} keep_revision_history To keep the previous revisions * Update the document metadata and update a document tree.
* @method remove * Options:
* @param {object} command The JIO command * - {boolean} keep_revision_history To keep the previous revisions
*/ * (false by default) (NYI).
that.remove = function (command) { * @method put
var f = {}, del_rev, option, new_doc, revs_info, new_id; * @param {object} command The JIO command
option = command.cloneOption(); */
if (option["max_retry"] === 0) { that.put = function (command) {
option["max_retry"] = 3; if (command.cloneDoc()._rev === undefined) {
priv.update_doctree_allowed = false;
}
that.post(command);
};
/**
* Get the document metadata or attachment.
* Options:
* - {boolean} revs Add simple revision history (false by default).
* - {boolean} revs_info Add revs info (false by default).
* - {boolean} conflicts Add conflict object (false by default).
* @method get
* @param {object} command The JIO command
*/
that.get = function (command) {
var f = {}, doctree, revs_info, prev_rev, option;
option = command.cloneOption();
if (option.max_retry === 0) {
option.max_retry = 3;
}
prev_rev = command.getOption("rev");
if (typeof prev_rev === "string") {
if (!priv.checkRevisionFormat(prev_rev)) {
that.error({
"status": 31,
"statusText": "Wrong Revision Format",
"error": "wrong_revision_format",
"message": "The document previous revision does not match " +
"[0-9]+-[0-9a-zA-Z]+",
"reason": "Previous revision is wrong"
});
return;
}
}
f.getDocumentTree = function () {
that.addJob(
"get",
priv.substorage,
command.getDocId() + priv.doctree_suffix,
option,
function (response) {
doctree = response;
if (prev_rev === undefined) {
revs_info = priv.getWinnerRevisionFromDocumentTree(doctree);
prev_rev = revs_info[0].rev;
} else {
revs_info = priv.getRevisionFromDocumentTree(doctree, prev_rev);
}
f.getDocument(command.getDocId() + "." + prev_rev,
command.getAttachmentId());
},
function (err) {
switch (err.status) {
case 404:
that.error(err);
break;
default:
err.message = "Cannot get document revision tree";
that.error(err);
break;
}
} }
del_rev = command.getDoc()._rev; );
};
f.removeDocument = function (docid, doctree) { f.getDocument = function (docid, attmtid) {
if (command.getOption("keep_revision_history") !== true) { that.addJob(
if (command.getAttachmentId() === undefined){ "get",
// update tree priv.substorage,
revs_info = priv.postToDocumentTree(doctree, docid,
command.getDoc(), true); option,
function (response) {
// remove revision var attmt;
that.addJob( if (typeof response !== "string") {
"remove", if (attmtid !== undefined) {
priv.substorage, if (response._attachments !== undefined) {
docid, attmt = response._attachments[attmtid];
option, if (attmt !== undefined) {
function (response) { prev_rev = priv.getRevisionFromPosition(
// put tree revs_info,
doctree._id = command.getDocId()+ attmt.revpos
priv.doctree_suffix; );
that.addJob( f.getDocument(command.getDocId() + "." + prev_rev + "/" +
"put", attmtid);
priv.substorage, return;
doctree,
command.cloneOption(),
function (response) {
that.success({
"ok":true,
"id":command.getDocId(),
"rev":revs_info[0].rev
});
},
function (err){
that.error({
"status": 409,
"statusText": "Conflict",
"error": "conflict",
"message": "Document update conflict.",
"reason": "Cannot update document tree"
});
return;
}
);
},
function (err) {
that.error({
"status": 404,
"statusText": "Not Found",
"error": "not_found",
"message": "File not found",
"reason": "Document was not found"
});
return;
}
);
} else {
// get previsous document
that.addJob(
"get",
priv.substorage,
command.getDocId()+"."+del_rev,
option,
function (response) {
// update tree
revs_info = priv.postToDocumentTree(doctree,
command.getDoc());
new_doc = response;
delete new_doc._attachments;
new_doc._id = new_doc._id+"."+revs_info[0].rev;
// post new document version
that.addJob(
"post",
priv.substorage,
new_doc,
command.cloneOption(),
function (response) {
// put tree
doctree._id = command.getDocId()+
priv.doctree_suffix;
that.addJob(
"put",
priv.substorage,
doctree,
command.cloneOption(),
function (response) {
that.success({
"ok":true,
"id":new_doc._id,
"rev":revs_info[0].rev
});
},
function (err) {
err.message =
"Cannot save document revision tree";
that.error(err);
}
);
},
function (err){
that.error({
"status": 409,
"statusText": "Conflict",
"error": "conflict",
"message": "Document update conflict.",
"reason": "Cannot update document"
});
return;
}
);
},
function (err) {
that.error({
"status": 404,
"statusText": "Not Found",
"error": "not_found",
"message": "File not found",
"reason": "Document was not found"
});
return;
}
);
} }
} else { }
// keep history = update document tree only that.error({
"status": 404,
"statusText": "Not Found",
"error": "not_found",
"message": "Cannot find the attachment",
"reason": "Attachment is missing"
});
return;
} }
}; response._id = command.getDocId();
if (typeof del_rev === "string") { response._rev = prev_rev;
if (!priv.checkRevisionFormat(del_rev)) { if (command.getOption("revs") === true) {
that.error({ response._revisions = priv.revsInfoToHistory(revs_info);
"status": 31,
"statusText": "Wrong Revision Format",
"error": "wrong_revision_format",
"message": "The document previous revision does not match "+
"[0-9]+-[0-9a-zA-Z]+",
"reason": "Previous revision is wrong"
});
return;
} }
if (command.getOption("revs_info") === true) {
response._revs_info = revs_info;
}
if (command.getOption("conflicts") === true) {
response._conflicts = priv.getLeavesFromDocumentTree(
doctree,
prev_rev
);
if (response._conflicts.length === 0) {
delete response._conflicts;
}
}
}
that.success(response);
},
function (err) {
that.error(err);
} }
);
// get doctree };
that.addJob( if (command.getAttachmentId() && prev_rev !== undefined) {
f.getDocument(command.getDocId() + "." + prev_rev +
"/" + command.getAttachmentId());
} else {
f.getDocumentTree();
}
};
/**
* Remove document or attachment.
* Options:
* - {boolean} keep_revision_history To keep the previous revisions
* @method remove
* @param {object} command The JIO command
*/
that.remove = function (command) {
var f = {}, del_rev, option, new_doc, revs_info;
option = command.cloneOption();
if (option.max_retry === 0) {
option.max_retry = 3;
}
del_rev = command.getDoc()._rev;
f.removeDocument = function (docid, doctree) {
if (command.getOption("keep_revision_history") !== true) {
if (command.getAttachmentId() === undefined) {
// update tree
revs_info = priv.postToDocumentTree(
doctree,
command.getDoc(),
true
);
// remove revision
that.addJob(
"remove",
priv.substorage,
docid,
option,
function () {
// put tree
doctree._id = command.getDocId() + priv.doctree_suffix;
that.addJob(
"put",
priv.substorage,
doctree,
command.cloneOption(),
function () {
that.success({
"ok": true,
"id": command.getDocId(),
"rev": revs_info[0].rev
});
},
function () {
that.error({
"status": 409,
"statusText": "Conflict",
"error": "conflict",
"message": "Document update conflict.",
"reason": "Cannot update document tree"
});
return;
}
);
},
function () {
that.error({
"status": 404,
"statusText": "Not Found",
"error": "not_found",
"message": "File not found",
"reason": "Document was not found"
});
return;
}
);
} else {
// get previsous document
that.addJob(
"get", "get",
priv.substorage, priv.substorage,
command.getDocId()+priv.doctree_suffix, command.getDocId() + "." + del_rev,
option, option,
function (response) { function (response) {
response._conflicts = priv.getLeavesFromDocumentTree(response); // update tree
revs_info = priv.postToDocumentTree(doctree, command.getDoc());
if (del_rev === undefined) { new_doc = response;
// no revision provided delete new_doc._attachments;
that.error({ new_doc._id = new_doc._id + "." + revs_info[0].rev;
"status": 409,
"statusText": "Conflict", // post new document version
"error": "conflict", that.addJob(
"message": "Document update conflict.", "post",
"reason": "Cannot delete a document without revision" priv.substorage,
}); new_doc,
return; command.cloneOption(),
} else { function () {
// revision provided // put tree
if (priv.isRevisionALeaf( response, del_rev) === true){ doctree._id = command.getDocId() + priv.doctree_suffix;
if (typeof command.getAttachmentId() === "string"){ that.addJob(
f.removeDocument(command.getDocId()+"."+del_rev+ "put",
"/"+command.getAttachmentId(), response); priv.substorage,
} else { doctree,
f.removeDocument(command.getDocId()+"."+ del_rev, command.cloneOption(),
response function () {
); that.success({
} "ok": true,
} else { "id": new_doc._id,
that.error({ "rev": revs_info[0].rev
"status": 409, });
"statusText": "Conflict", },
"error": "conflict", function (err) {
"message": "Document update conflict.", err.message =
"reason": "Trying to remove non-latest revision" "Cannot save document revision tree";
}); that.error(err);
return;
} }
);
},
function () {
that.error({
"status": 409,
"statusText": "Conflict",
"error": "conflict",
"message": "Document update conflict.",
"reason": "Cannot update document"
});
return;
} }
);
}, },
function (err) { function () {
that.error({ that.error({
"status": 404, "status": 404,
"statusText": "Not Found", "statusText": "Not Found",
"error": "not_found", "error": "not_found",
"message": "Document tree not found, please checkdocument ID", "message": "File not found",
"reason": "Incorrect document ID" "reason": "Document was not found"
}); });
return; return;
} }
); );
}
}
}; };
if (typeof del_rev === "string") {
/** if (!priv.checkRevisionFormat(del_rev)) {
* Get all documents that.error({
* @method allDocs "status": 31,
* @param {object} command The JIO command "statusText": "Wrong Revision Format",
*/ "error": "wrong_revision_format",
that.allDocs = function (command) { "message": "The document previous revision does not match " +
setTimeout(function () { "[0-9]+-[0-9a-zA-Z]+",
that.error({ "reason": "Previous revision is wrong"
"status": 405,
"statusText": "Method Not Allowed",
"error": "method_not_allowed",
"message": "Your are not allowed to use this command",
"reason": "LocalStorage forbids AllDocs command executions"
});
}); });
}; return;
}
return that; }
});
// get doctree
that.addJob(
"get",
priv.substorage,
command.getDocId() + priv.doctree_suffix,
option,
function (response) {
response._conflicts = priv.getLeavesFromDocumentTree(response);
if (del_rev === undefined) {
// no revision provided
that.error({
"status": 409,
"statusText": "Conflict",
"error": "conflict",
"message": "Document update conflict.",
"reason": "Cannot delete a document without revision"
});
return;
}
// revision provided
if (priv.isRevisionALeaf(response, del_rev) === true) {
if (typeof command.getAttachmentId() === "string") {
f.removeDocument(command.getDocId() + "." + del_rev +
"/" + command.getAttachmentId(), response);
} else {
f.removeDocument(command.getDocId() + "." + del_rev,
response);
}
} else {
that.error({
"status": 409,
"statusText": "Conflict",
"error": "conflict",
"message": "Document update conflict.",
"reason": "Trying to remove non-latest revision"
});
return;
}
},
function () {
that.error({
"status": 404,
"statusText": "Not Found",
"error": "not_found",
"message": "Document tree not found, please checkdocument ID",
"reason": "Incorrect document ID"
});
return;
}
);
};
/**
* Get all documents
* @method allDocs
* @param {object} command The JIO command
*/
that.allDocs = function () {
setTimeout(function () {
that.error({
"status": 405,
"statusText": "Method Not Allowed",
"error": "method_not_allowed",
"message": "Your are not allowed to use this command",
"reason": "LocalStorage forbids AllDocs command executions"
});
});
};
return that;
});
\ 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