Commit c1c73f90 authored by Tristan Cavelier's avatar Tristan Cavelier

revisionstorage.js amd compatible now

parent e3d3b88f
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */ /*jslint indent: 2, maxlen: 80, nomen: true */
/*global jIO: true, hex_sha256: true, setTimeout: true */ /*global jIO, hex_sha256, setTimeout, define */
/** /**
* JIO Revision Storage. * JIO Revision Storage.
* It manages document version and can generate conflicts. * It manages document version and can generate conflicts.
...@@ -9,1014 +10,1033 @@ ...@@ -9,1014 +10,1033 @@
* "sub_storage": <sub storage description> * "sub_storage": <sub storage description>
* } * }
*/ */
jIO.addStorageType("revision", function (spec, my) { // define([module_name], [dependencies], module);
(function (dependencies, module) {
"use strict"; "use strict";
var that = {}, priv = {}; if (typeof define === 'function' && define.amd) {
spec = spec || {}; return define(dependencies, module);
that = my.basicStorage(spec, my); }
// ATTRIBUTES // module(jIO, hex_sha256);
priv.doc_tree_suffix = ".revision_tree.json"; }(['jio', 'sha256'], function (jIO, hex_sha256) {
priv.sub_storage = spec.sub_storage; "use strict";
// METHODS //
/** jIO.addStorageType("revision", function (spec, my) {
* Constructor
*/ var that = {}, priv = {};
priv.RevisionStorage = function () { spec = spec || {};
// no init that = my.basicStorage(spec, my);
}; // ATTRIBUTES //
priv.doc_tree_suffix = ".revision_tree.json";
/** priv.sub_storage = spec.sub_storage;
* Description to store in order to be restored later // METHODS //
* @method specToStore /**
* @return {object} Descriptions to store * Constructor
*/ */
that.specToStore = function () { priv.RevisionStorage = function () {
return { // no init
"sub_storage": priv.sub_storage };
};
}; /**
* Description to store in order to be restored later
/** * @method specToStore
* Clones an object in deep (without functions) * @return {object} Descriptions to store
* @method clone */
* @param {any} object The object to clone that.specToStore = function () {
* @return {any} The cloned object
*/
priv.clone = function (object) {
var tmp = JSON.stringify(object);
if (tmp === undefined) {
return undefined;
}
return JSON.parse(tmp);
};
/**
* 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();
};
/**
* Generates a hash code of a string
* @method hashCode
* @param {string} string The string to hash
* @return {string} The string hash code
*/
priv.hashCode = function (string) {
return hex_sha256(string);
};
/**
* Checks a revision format
* @method checkDocumentRevisionFormat
* @param {object} doc The document object
* @return {object} null if ok, else error object
*/
priv.checkDocumentRevisionFormat = function (doc) {
var send_error = function (message) {
return { return {
"status": 31, "sub_storage": priv.sub_storage
"statusText": "Wrong Revision Format",
"error": "wrong_revision_format",
"message": message,
"reason": "Revision is wrong"
}; };
}; };
if (typeof doc._rev === "string") {
if (/^[0-9]+-[0-9a-zA-Z]+$/.test(doc._rev) === false) { /**
return send_error("The document revision does not match " + * Clones an object in deep (without functions)
"^[0-9]+-[0-9a-zA-Z]+$"); * @method clone
} * @param {any} object The object to clone
} * @return {any} The cloned object
if (typeof doc._revs === "object") { */
if (typeof doc._revs.start !== "number" || priv.clone = function (object) {
typeof doc._revs.ids !== "object" || var tmp = JSON.stringify(object);
typeof doc._revs.ids.length !== "number") { if (tmp === undefined) {
return send_error("The document revision history is not well formated"); return undefined;
}
}
if (typeof doc._revs_info === "object") {
if (typeof doc._revs_info.length !== "number") {
return send_error("The document revision information " +
"is not well formated");
}
}
};
/**
* Creates a new document tree
* @method newDocTree
* @return {object} The new document tree
*/
priv.newDocTree = function () {
return {"children": []};
};
/**
* 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 i, revisions = {
"start": 0,
"ids": []
};
revs_info = revs_info || [];
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;
};
/**
* Convert the revision history object to an array of revisions.
* @method revisionHistoryToList
* @param {object} revs The revision history
* @return {array} The revision array
*/
priv.revisionHistoryToList = function (revs) {
var i, start = revs.start, new_list = [];
for (i = 0; i < revs.ids.length; i += 1, start -= 1) {
new_list.push(start + "-" + revs.ids[i]);
}
return new_list;
};
/**
* Convert revision list to revs info.
* @method revisionListToRevsInfo
* @param {array} revision_list The revision list
* @param {object} doc_tree The document tree
* @return {array} The document revs info
*/
priv.revisionListToRevsInfo = function (revision_list, doc_tree) {
var revisionListToRevsInfoRec, revs_info = [], j;
for (j = 0; j < revision_list.length; j += 1) {
revs_info.push({"rev": revision_list[j], "status": "missing"});
}
revisionListToRevsInfoRec = function (index, doc_tree) {
var child, i;
if (index < 0) {
return;
} }
for (i = 0; i < doc_tree.children.length; i += 1) { return JSON.parse(tmp);
child = doc_tree.children[i]; };
if (child.rev === revision_list[index]) {
revs_info[index].status = child.status; /**
revisionListToRevsInfoRec(index - 1, child); * 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();
}; };
revisionListToRevsInfoRec(revision_list.length - 1, doc_tree);
return revs_info; /**
}; * Generates a hash code of a string
* @method hashCode
/** * @param {string} string The string to hash
* Update a document metadata revision properties * @return {string} The string hash code
* @method fillDocumentRevisionProperties */
* @param {object} doc The document object priv.hashCode = function (string) {
* @param {object} doc_tree The document tree return hex_sha256(string);
*/ };
priv.fillDocumentRevisionProperties = function (doc, doc_tree) {
if (doc._revs_info) { /**
doc._revs = priv.revsInfoToHistory(doc._revs_info); * Checks a revision format
} else if (doc._revs) { * @method checkDocumentRevisionFormat
doc._revs_info = priv.revisionListToRevsInfo( * @param {object} doc The document object
priv.revisionHistoryToList(doc._revs), * @return {object} null if ok, else error object
doc_tree */
); priv.checkDocumentRevisionFormat = function (doc) {
} else if (doc._rev) { var send_error = function (message) {
doc._revs_info = priv.getRevisionInfo(doc._rev, doc_tree); return {
doc._revs = priv.revsInfoToHistory(doc._revs_info); "status": 31,
} else { "statusText": "Wrong Revision Format",
doc._revs_info = []; "error": "wrong_revision_format",
doc._revs = {"start": 0, "ids": []}; "message": message,
} "reason": "Revision is wrong"
if (doc._revs.start > 0) { };
doc._rev = doc._revs.start + "-" + doc._revs.ids[0]; };
} else { if (typeof doc._rev === "string") {
delete doc._rev; if (/^[0-9]+-[0-9a-zA-Z]+$/.test(doc._rev) === false) {
} return send_error("The document revision does not match " +
}; "^[0-9]+-[0-9a-zA-Z]+$");
/**
* Generates the next revision of a document.
* @methode generateNextRevision
* @param {object} doc The document metadata
* @param {boolean} deleted_flag The deleted flag
* @return {array} 0:The next revision number and 1:the hash code
*/
priv.generateNextRevision = function (doc, deleted_flag) {
var string, revision_history, revs_info, pseudo_revision;
doc = priv.clone(doc) || {};
revision_history = doc._revs;
revs_info = doc._revs_info;
delete doc._rev;
delete doc._revs;
delete doc._revs_info;
string = JSON.stringify(doc) + JSON.stringify(revision_history) +
JSON.stringify(deleted_flag ? true : false);
revision_history.start += 1;
revision_history.ids.unshift(priv.hashCode(string));
doc._revs = revision_history;
doc._rev = revision_history.start + "-" + revision_history.ids[0];
revs_info.unshift({
"rev": doc._rev,
"status": deleted_flag ? "deleted" : "available"
});
doc._revs_info = revs_info;
return doc;
};
/**
* Gets the revs info from the document tree
* @method getRevisionInfo
* @param {string} revision The revision to search for
* @param {object} doc_tree The document tree
* @return {array} The revs info
*/
priv.getRevisionInfo = function (revision, doc_tree) {
var getRevisionInfoRec;
getRevisionInfoRec = function (doc_tree) {
var i, child, revs_info;
for (i = 0; i < doc_tree.children.length; i += 1) {
child = doc_tree.children[i];
if (child.rev === revision) {
return [{"rev": child.rev, "status": child.status}];
}
revs_info = getRevisionInfoRec(child);
if (revs_info.length > 0 || revision === undefined) {
revs_info.push({"rev": child.rev, "status": child.status});
return revs_info;
} }
} }
return []; if (typeof doc._revs === "object") {
}; if (typeof doc._revs.start !== "number" ||
return getRevisionInfoRec(doc_tree); typeof doc._revs.ids !== "object" ||
}; typeof doc._revs.ids.length !== "number") {
return send_error(
priv.updateDocumentTree = function (doc, doc_tree) { "The document revision history is not well formated"
var revs_info, updateDocumentTreeRec, next_rev; );
doc = priv.clone(doc);
revs_info = doc._revs_info;
updateDocumentTreeRec = function (doc_tree, revs_info) {
var i, child, info;
if (revs_info.length === 0) {
return;
}
info = revs_info.pop();
for (i = 0; i < doc_tree.children.length; i += 1) {
child = doc_tree.children[i];
if (child.rev === info.rev) {
return updateDocumentTreeRec(child, revs_info);
} }
} }
doc_tree.children.unshift({ if (typeof doc._revs_info === "object") {
"rev": info.rev, if (typeof doc._revs_info.length !== "number") {
"status": info.status, return send_error("The document revision information " +
"children": [] "is not well formated");
}); }
updateDocumentTreeRec(doc_tree.children[0], revs_info);
};
updateDocumentTreeRec(doc_tree, priv.clone(revs_info));
};
priv.send = function (method, doc, option, callback) {
that.addJob(
method,
priv.sub_storage,
doc,
option,
function (success) {
callback(undefined, success);
},
function (err) {
callback(err, undefined);
} }
); };
};
/**
priv.getWinnerRevsInfo = function (doc_tree) { * Creates a new document tree
var revs_info = [], getWinnerRevsInfoRec; * @method newDocTree
getWinnerRevsInfoRec = function (doc_tree, tmp_revs_info) { * @return {object} The new document tree
var i; */
if (doc_tree.rev) { priv.newDocTree = function () {
tmp_revs_info.unshift({"rev": doc_tree.rev, "status": doc_tree.status}); return {"children": []};
};
/**
* 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 i, revisions = {
"start": 0,
"ids": []
};
revs_info = revs_info || [];
if (revs_info.length > 0) {
revisions.start = parseInt(revs_info[0].rev.split('-')[0], 10);
} }
if (doc_tree.children.length === 0) { for (i = 0; i < revs_info.length; i += 1) {
if (revs_info.length === 0 || revisions.ids.push(revs_info[i].rev.split('-')[1]);
(revs_info[0].status !== "available" &&
tmp_revs_info[0].status === "available") ||
(tmp_revs_info[0].status === "available" &&
revs_info.length < tmp_revs_info.length)) {
revs_info = priv.clone(tmp_revs_info);
}
} }
for (i = 0; i < doc_tree.children.length; i += 1) { return revisions;
getWinnerRevsInfoRec(doc_tree.children[i], tmp_revs_info); };
/**
* Convert the revision history object to an array of revisions.
* @method revisionHistoryToList
* @param {object} revs The revision history
* @return {array} The revision array
*/
priv.revisionHistoryToList = function (revs) {
var i, start = revs.start, new_list = [];
for (i = 0; i < revs.ids.length; i += 1, start -= 1) {
new_list.push(start + "-" + revs.ids[i]);
} }
tmp_revs_info.shift(); return new_list;
}; };
getWinnerRevsInfoRec(doc_tree, []);
return revs_info; /**
}; * Convert revision list to revs info.
* @method revisionListToRevsInfo
priv.getConflicts = function (revision, doc_tree) { * @param {array} revision_list The revision list
var conflicts = [], getConflictsRec; * @param {object} doc_tree The document tree
getConflictsRec = function (doc_tree) { * @return {array} The document revs info
var i; */
if (doc_tree.rev === revision) { priv.revisionListToRevsInfo = function (revision_list, doc_tree) {
return; var revisionListToRevsInfoRec, revs_info = [], j;
for (j = 0; j < revision_list.length; j += 1) {
revs_info.push({"rev": revision_list[j], "status": "missing"});
} }
if (doc_tree.children.length === 0) { revisionListToRevsInfoRec = function (index, doc_tree) {
if (doc_tree.status !== "deleted") { var child, i;
conflicts.push(doc_tree.rev); if (index < 0) {
return;
}
for (i = 0; i < doc_tree.children.length; i += 1) {
child = doc_tree.children[i];
if (child.rev === revision_list[index]) {
revs_info[index].status = child.status;
revisionListToRevsInfoRec(index - 1, child);
}
} }
};
revisionListToRevsInfoRec(revision_list.length - 1, doc_tree);
return revs_info;
};
/**
* Update a document metadata revision properties
* @method fillDocumentRevisionProperties
* @param {object} doc The document object
* @param {object} doc_tree The document tree
*/
priv.fillDocumentRevisionProperties = function (doc, doc_tree) {
if (doc._revs_info) {
doc._revs = priv.revsInfoToHistory(doc._revs_info);
} else if (doc._revs) {
doc._revs_info = priv.revisionListToRevsInfo(
priv.revisionHistoryToList(doc._revs),
doc_tree
);
} else if (doc._rev) {
doc._revs_info = priv.getRevisionInfo(doc._rev, doc_tree);
doc._revs = priv.revsInfoToHistory(doc._revs_info);
} else {
doc._revs_info = [];
doc._revs = {"start": 0, "ids": []};
} }
for (i = 0; i < doc_tree.children.length; i += 1) { if (doc._revs.start > 0) {
getConflictsRec(doc_tree.children[i]); doc._rev = doc._revs.start + "-" + doc._revs.ids[0];
} else {
delete doc._rev;
} }
}; };
getConflictsRec(doc_tree);
return conflicts.length === 0 ? undefined : conflicts; /**
}; * Generates the next revision of a document.
* @methode generateNextRevision
priv.get = function (doc, option, callback) { * @param {object} doc The document metadata
priv.send("get", doc, option, callback); * @param {boolean} deleted_flag The deleted flag
}; * @return {array} 0:The next revision number and 1:the hash code
priv.put = function (doc, option, callback) { */
priv.send("put", doc, option, callback); priv.generateNextRevision = function (doc, deleted_flag) {
}; var string, revision_history, revs_info, pseudo_revision;
priv.remove = function (doc, option, callback) { doc = priv.clone(doc) || {};
priv.send("remove", doc, option, callback); revision_history = doc._revs;
}; revs_info = doc._revs_info;
priv.getAttachment = function (attachment, option, callback) { delete doc._rev;
priv.send("getAttachment", attachment, option, callback); delete doc._revs;
}; delete doc._revs_info;
priv.putAttachment = function (attachment, option, callback) { string = JSON.stringify(doc) + JSON.stringify(revision_history) +
priv.send("putAttachment", attachment, option, callback); JSON.stringify(deleted_flag ? true : false);
}; revision_history.start += 1;
priv.removeAttachment = function (attachment, option, callback) { revision_history.ids.unshift(priv.hashCode(string));
priv.send("removeAttachment", attachment, option, callback); doc._revs = revision_history;
}; doc._rev = revision_history.start + "-" + revision_history.ids[0];
revs_info.unshift({
priv.getDocument = function (doc, option, callback) { "rev": doc._rev,
doc = priv.clone(doc); "status": deleted_flag ? "deleted" : "available"
doc._id = doc._id + "." + doc._rev; });
delete doc._attachment; doc._revs_info = revs_info;
delete doc._rev; return doc;
delete doc._revs; };
delete doc._revs_info;
priv.get(doc, option, callback); /**
}; * Gets the revs info from the document tree
priv.putDocument = function (doc, option, callback) { * @method getRevisionInfo
doc = priv.clone(doc); * @param {string} revision The revision to search for
doc._id = doc._id + "." + doc._rev; * @param {object} doc_tree The document tree
delete doc._attachment; * @return {array} The revs info
delete doc._data; */
delete doc._mimetype; priv.getRevisionInfo = function (revision, doc_tree) {
delete doc._rev; var getRevisionInfoRec;
delete doc._revs; getRevisionInfoRec = function (doc_tree) {
delete doc._revs_info; var i, child, revs_info;
priv.put(doc, option, callback); for (i = 0; i < doc_tree.children.length; i += 1) {
}; child = doc_tree.children[i];
if (child.rev === revision) {
priv.getRevisionTree = function (doc, option, callback) { return [{"rev": child.rev, "status": child.status}];
doc = priv.clone(doc); }
doc._id = doc._id + priv.doc_tree_suffix; revs_info = getRevisionInfoRec(child);
priv.get(doc, option, callback); if (revs_info.length > 0 || revision === undefined) {
}; revs_info.push({"rev": child.rev, "status": child.status});
return revs_info;
priv.getAttachmentList = function (doc, option, callback) { }
var attachment_id, dealResults, state = "ok", result_list = [], count = 0; }
dealResults = function (attachment_id, attachment_meta) { return [];
return function (err, attachment) { };
if (state !== "ok") { return getRevisionInfoRec(doc_tree);
};
priv.updateDocumentTree = function (doc, doc_tree) {
var revs_info, updateDocumentTreeRec, next_rev;
doc = priv.clone(doc);
revs_info = doc._revs_info;
updateDocumentTreeRec = function (doc_tree, revs_info) {
var i, child, info;
if (revs_info.length === 0) {
return; return;
} }
count -= 1; info = revs_info.pop();
if (err) { for (i = 0; i < doc_tree.children.length; i += 1) {
if (err.status === 404) { child = doc_tree.children[i];
result_list.push(undefined); if (child.rev === info.rev) {
} else { return updateDocumentTreeRec(child, revs_info);
state = "error";
return callback(err, undefined);
} }
} }
result_list.push({ doc_tree.children.unshift({
"_attachment": attachment_id, "rev": info.rev,
"_data": attachment, "status": info.status,
"_mimetype": attachment_meta.content_type "children": []
}); });
if (count === 0) { updateDocumentTreeRec(doc_tree.children[0], revs_info);
state = "finished"; };
callback(undefined, result_list); updateDocumentTreeRec(doc_tree, priv.clone(revs_info));
};
priv.send = function (method, doc, option, callback) {
that.addJob(
method,
priv.sub_storage,
doc,
option,
function (success) {
callback(undefined, success);
},
function (err) {
callback(err, undefined);
} }
);
};
priv.getWinnerRevsInfo = function (doc_tree) {
var revs_info = [], getWinnerRevsInfoRec;
getWinnerRevsInfoRec = function (doc_tree, tmp_revs_info) {
var i;
if (doc_tree.rev) {
tmp_revs_info.unshift({
"rev": doc_tree.rev,
"status": doc_tree.status
});
}
if (doc_tree.children.length === 0) {
if (revs_info.length === 0 ||
(revs_info[0].status !== "available" &&
tmp_revs_info[0].status === "available") ||
(tmp_revs_info[0].status === "available" &&
revs_info.length < tmp_revs_info.length)) {
revs_info = priv.clone(tmp_revs_info);
}
}
for (i = 0; i < doc_tree.children.length; i += 1) {
getWinnerRevsInfoRec(doc_tree.children[i], tmp_revs_info);
}
tmp_revs_info.shift();
}; };
getWinnerRevsInfoRec(doc_tree, []);
return revs_info;
}; };
for (attachment_id in doc._attachments) {
if (doc._attachments.hasOwnProperty(attachment_id)) { priv.getConflicts = function (revision, doc_tree) {
count += 1; var conflicts = [], getConflictsRec;
priv.getAttachment( getConflictsRec = function (doc_tree) {
{"_id": doc._id, "_attachment": attachment_id}, var i;
option, if (doc_tree.rev === revision) {
dealResults(attachment_id, doc._attachments[attachment_id])
);
}
}
if (count === 0) {
callback(undefined, []);
}
};
priv.putAttachmentList = function (doc, option, attachment_list, callback) {
var i, dealResults, state = "ok", count = 0, attachment;
attachment_list = attachment_list || [];
dealResults = function (index) {
return function (err, response) {
if (state !== "ok") {
return; return;
} }
count -= 1; if (doc_tree.children.length === 0) {
if (err) { if (doc_tree.status !== "deleted") {
state = "error"; conflicts.push(doc_tree.rev);
return callback(err, undefined); }
} }
if (count === 0) { for (i = 0; i < doc_tree.children.length; i += 1) {
state = "finished"; getConflictsRec(doc_tree.children[i]);
callback(undefined, {"id": doc._id, "ok": true});
} }
}; };
getConflictsRec(doc_tree);
return conflicts.length === 0 ? undefined : conflicts;
}; };
for (i = 0; i < attachment_list.length; i += 1) {
attachment = attachment_list[i]; priv.get = function (doc, option, callback) {
if (attachment !== undefined) { priv.send("get", doc, option, callback);
count += 1;
attachment._id = doc._id + "." + doc._rev;
priv.putAttachment(attachment, option, dealResults(i));
}
}
if (count === 0) {
return callback(undefined, {"id": doc._id, "ok": true});
}
};
priv.putDocumentTree = function (doc, option, doc_tree, callback) {
doc_tree = priv.clone(doc_tree);
doc_tree._id = doc._id + priv.doc_tree_suffix;
priv.put(doc_tree, option, callback);
};
priv.notFoundError = function (message, reason) {
return {
"status": 404,
"statusText": "Not Found",
"error": "not_found",
"message": message,
"reason": reason
};
};
priv.conflictError = function (message, reason) {
return {
"status": 409,
"statusText": "Conflict",
"error": "conflict",
"message": message,
"reason": reason
};
};
priv.revisionGenericRequest = function (doc, option,
specific_parameter, onEnd) {
var prev_doc, doc_tree, attachment_list, callback = {};
if (specific_parameter.doc_id) {
doc._id = specific_parameter.doc_id;
}
if (specific_parameter.attachment_id) {
doc._attachment = specific_parameter.attachment_id;
}
callback.begin = function () {
var check_error;
doc._id = doc._id || priv.generateUuid();
if (specific_parameter.revision_needed && !doc._rev) {
return onEnd(priv.conflictError(
"Document update conflict",
"No document revision was provided"
), undefined);
}
// check revision format
check_error = priv.checkDocumentRevisionFormat(doc);
if (check_error !== undefined) {
return onEnd(check_error, undefined);
}
priv.getRevisionTree(doc, option, callback.getRevisionTree);
}; };
callback.getRevisionTree = function (err, response) { priv.put = function (doc, option, callback) {
var winner_info, previous_revision = doc._rev, priv.send("put", doc, option, callback);
generate_new_revision = doc._revs || doc._revs_info ? false : true; };
if (err) { priv.remove = function (doc, option, callback) {
if (err.status !== 404) { priv.send("remove", doc, option, callback);
err.message = "Cannot get document revision tree"; };
return onEnd(err, undefined); priv.getAttachment = function (attachment, option, callback) {
} priv.send("getAttachment", attachment, option, callback);
} };
doc_tree = response || priv.newDocTree(); priv.putAttachment = function (attachment, option, callback) {
if (specific_parameter.get || specific_parameter.getAttachment) { priv.send("putAttachment", attachment, option, callback);
if (!doc._rev) { };
winner_info = priv.getWinnerRevsInfo(doc_tree); priv.removeAttachment = function (attachment, option, callback) {
if (winner_info.length === 0) { priv.send("removeAttachment", attachment, option, callback);
return onEnd(priv.notFoundError( };
"Document not found",
"missing" priv.getDocument = function (doc, option, callback) {
), undefined); doc = priv.clone(doc);
doc._id = doc._id + "." + doc._rev;
delete doc._attachment;
delete doc._rev;
delete doc._revs;
delete doc._revs_info;
priv.get(doc, option, callback);
};
priv.putDocument = function (doc, option, callback) {
doc = priv.clone(doc);
doc._id = doc._id + "." + doc._rev;
delete doc._attachment;
delete doc._data;
delete doc._mimetype;
delete doc._rev;
delete doc._revs;
delete doc._revs_info;
priv.put(doc, option, callback);
};
priv.getRevisionTree = function (doc, option, callback) {
doc = priv.clone(doc);
doc._id = doc._id + priv.doc_tree_suffix;
priv.get(doc, option, callback);
};
priv.getAttachmentList = function (doc, option, callback) {
var attachment_id, dealResults, state = "ok", result_list = [], count = 0;
dealResults = function (attachment_id, attachment_meta) {
return function (err, attachment) {
if (state !== "ok") {
return;
} }
if (winner_info[0].status === "deleted") { count -= 1;
return onEnd(priv.notFoundError( if (err) {
"Document not found", if (err.status === 404) {
"deleted" result_list.push(undefined);
), undefined); } else {
state = "error";
return callback(err, undefined);
}
} }
doc._rev = winner_info[0].rev; result_list.push({
"_attachment": attachment_id,
"_data": attachment,
"_mimetype": attachment_meta.content_type
});
if (count === 0) {
state = "finished";
callback(undefined, result_list);
}
};
};
for (attachment_id in doc._attachments) {
if (doc._attachments.hasOwnProperty(attachment_id)) {
count += 1;
priv.getAttachment(
{"_id": doc._id, "_attachment": attachment_id},
option,
dealResults(attachment_id, doc._attachments[attachment_id])
);
} }
priv.fillDocumentRevisionProperties(doc, doc_tree);
return priv.getDocument(doc, option, callback.getDocument);
} }
priv.fillDocumentRevisionProperties(doc, doc_tree); if (count === 0) {
if (generate_new_revision) { callback(undefined, []);
if (previous_revision && doc._revs_info.length === 0) {
// the document history has changed, it means that the document
// revision was wrong. Add a pseudo history to the document
doc._rev = previous_revision;
doc._revs = {
"start": parseInt(previous_revision.split("-")[0], 10),
"ids": [previous_revision.split("-")[1]]
};
doc._revs_info = [{"rev": previous_revision, "status": "missing"}];
}
doc = priv.generateNextRevision(
doc,
specific_parameter.remove
);
} }
if (doc._revs_info.length > 1) { };
prev_doc = {
"_id": doc._id, priv.putAttachmentList = function (doc, option, attachment_list, callback) {
"_rev": doc._revs_info[1].rev var i, dealResults, state = "ok", count = 0, attachment;
attachment_list = attachment_list || [];
dealResults = function (index) {
return function (err, response) {
if (state !== "ok") {
return;
}
count -= 1;
if (err) {
state = "error";
return callback(err, undefined);
}
if (count === 0) {
state = "finished";
callback(undefined, {"id": doc._id, "ok": true});
}
}; };
if (!generate_new_revision && specific_parameter.putAttachment) { };
prev_doc._rev = doc._revs_info[0].rev; for (i = 0; i < attachment_list.length; i += 1) {
attachment = attachment_list[i];
if (attachment !== undefined) {
count += 1;
attachment._id = doc._id + "." + doc._rev;
priv.putAttachment(attachment, option, dealResults(i));
} }
} }
// force revs_info status if (count === 0) {
doc._revs_info[0].status = (specific_parameter.remove ? return callback(undefined, {"id": doc._id, "ok": true});
"deleted" : "available"); }
priv.updateDocumentTree(doc, doc_tree); };
if (prev_doc) {
return priv.getDocument(prev_doc, option, callback.getDocument); priv.putDocumentTree = function (doc, option, doc_tree, callback) {
doc_tree = priv.clone(doc_tree);
doc_tree._id = doc._id + priv.doc_tree_suffix;
priv.put(doc_tree, option, callback);
};
priv.notFoundError = function (message, reason) {
return {
"status": 404,
"statusText": "Not Found",
"error": "not_found",
"message": message,
"reason": reason
};
};
priv.conflictError = function (message, reason) {
return {
"status": 409,
"statusText": "Conflict",
"error": "conflict",
"message": message,
"reason": reason
};
};
priv.revisionGenericRequest = function (doc, option,
specific_parameter, onEnd) {
var prev_doc, doc_tree, attachment_list, callback = {};
if (specific_parameter.doc_id) {
doc._id = specific_parameter.doc_id;
} }
if (specific_parameter.remove || specific_parameter.removeAttachment) { if (specific_parameter.attachment_id) {
return onEnd(priv.notFoundError( doc._attachment = specific_parameter.attachment_id;
"Unable to remove an inexistent document",
"missing"
), undefined);
} }
priv.putDocument(doc, option, callback.putDocument); callback.begin = function () {
}; var check_error;
callback.getDocument = function (err, res_doc) { doc._id = doc._id || priv.generateUuid();
var k, conflicts; if (specific_parameter.revision_needed && !doc._rev) {
if (err) { return onEnd(priv.conflictError(
if (err.status === 404) { "Document update conflict",
if (specific_parameter.remove || "No document revision was provided"
specific_parameter.removeAttachment) { ), undefined);
return onEnd(priv.conflictError(
"Document update conflict",
"Document is missing"
), undefined);
}
if (specific_parameter.get) {
return onEnd(priv.notFoundError(
"Unable to find the document",
"missing"
), undefined);
}
res_doc = {};
} else {
err.message = "Cannot get document";
return onEnd(err, undefined);
} }
} // check revision format
if (specific_parameter.get) { check_error = priv.checkDocumentRevisionFormat(doc);
res_doc._id = doc._id; if (check_error !== undefined) {
res_doc._rev = doc._rev; return onEnd(check_error, undefined);
if (option.conflicts === true) { }
conflicts = priv.getConflicts(doc._rev, doc_tree); priv.getRevisionTree(doc, option, callback.getRevisionTree);
if (conflicts) { };
res_doc._conflicts = conflicts; callback.getRevisionTree = function (err, response) {
var winner_info, previous_revision, generate_new_revision;
previous_revision = doc._rew;
generate_new_revision = doc._revs || doc._revs_info ? false : true;
if (err) {
if (err.status !== 404) {
err.message = "Cannot get document revision tree";
return onEnd(err, undefined);
} }
} }
if (option.revs === true) { doc_tree = response || priv.newDocTree();
res_doc._revisions = doc._revs; if (specific_parameter.get || specific_parameter.getAttachment) {
if (!doc._rev) {
winner_info = priv.getWinnerRevsInfo(doc_tree);
if (winner_info.length === 0) {
return onEnd(priv.notFoundError(
"Document not found",
"missing"
), undefined);
}
if (winner_info[0].status === "deleted") {
return onEnd(priv.notFoundError(
"Document not found",
"deleted"
), undefined);
}
doc._rev = winner_info[0].rev;
}
priv.fillDocumentRevisionProperties(doc, doc_tree);
return priv.getDocument(doc, option, callback.getDocument);
} }
if (option.revs_info === true) { priv.fillDocumentRevisionProperties(doc, doc_tree);
res_doc._revs_info = doc._revs_info; if (generate_new_revision) {
if (previous_revision && doc._revs_info.length === 0) {
// the document history has changed, it means that the document
// revision was wrong. Add a pseudo history to the document
doc._rev = previous_revision;
doc._revs = {
"start": parseInt(previous_revision.split("-")[0], 10),
"ids": [previous_revision.split("-")[1]]
};
doc._revs_info = [{"rev": previous_revision, "status": "missing"}];
}
doc = priv.generateNextRevision(
doc,
specific_parameter.remove
);
} }
return onEnd(undefined, res_doc); if (doc._revs_info.length > 1) {
} prev_doc = {
if (specific_parameter.putAttachment || "_id": doc._id,
specific_parameter.removeAttachment) { "_rev": doc._revs_info[1].rev
// copy metadata (not beginning by "_" to document };
for (k in res_doc) { if (!generate_new_revision && specific_parameter.putAttachment) {
if (res_doc.hasOwnProperty(k) && !k.match("^_")) { prev_doc._rev = doc._revs_info[0].rev;
doc[k] = res_doc[k];
} }
} }
} // force revs_info status
if (specific_parameter.remove) { doc._revs_info[0].status = (specific_parameter.remove ?
priv.putDocumentTree(doc, option, doc_tree, callback.putDocumentTree); "deleted" : "available");
} else { priv.updateDocumentTree(doc, doc_tree);
priv.getAttachmentList(res_doc, option, callback.getAttachmentList); if (prev_doc) {
} return priv.getDocument(prev_doc, option, callback.getDocument);
}; }
callback.getAttachmentList = function (err, res_list) { if (specific_parameter.remove || specific_parameter.removeAttachment) {
var i, attachment_found = false;
if (err) {
err.message = "Cannot get attachment";
return onEnd(err, undefined);
}
attachment_list = res_list || [];
if (specific_parameter.getAttachment) {
// getting specific attachment
for (i = 0; i < attachment_list.length; i += 1) {
if (attachment_list[i] &&
doc._attachment ===
attachment_list[i]._attachment) {
return onEnd(undefined, attachment_list[i]._data);
}
}
return onEnd(priv.notFoundError(
"Unable to get an inexistent attachment",
"missing"
), undefined);
}
if (specific_parameter.remove_from_attachment_list) {
// removing specific attachment
for (i = 0; i < attachment_list.length; i += 1) {
if (attachment_list[i] &&
specific_parameter.remove_from_attachment_list._attachment ===
attachment_list[i]._attachment) {
attachment_found = true;
attachment_list[i] = undefined;
break;
}
}
if (!attachment_found) {
return onEnd(priv.notFoundError( return onEnd(priv.notFoundError(
"Unable to remove an inexistent attachment", "Unable to remove an inexistent document",
"missing" "missing"
), undefined); ), undefined);
} }
} priv.putDocument(doc, option, callback.putDocument);
priv.putDocument(doc, option, callback.putDocument); };
}; callback.getDocument = function (err, res_doc) {
callback.putDocument = function (err, response) { var k, conflicts;
var i, attachment_found = false; if (err) {
if (err) { if (err.status === 404) {
err.message = "Cannot post the document"; if (specific_parameter.remove ||
return onEnd(err, undefined); specific_parameter.removeAttachment) {
} return onEnd(priv.conflictError(
if (specific_parameter.add_to_attachment_list) { "Document update conflict",
// adding specific attachment "Document is missing"
attachment_list = attachment_list || []; ), undefined);
for (i = 0; i < attachment_list.length; i += 1) { }
if (attachment_list[i] && if (specific_parameter.get) {
specific_parameter.add_to_attachment_list._attachment === return onEnd(priv.notFoundError(
attachment_list[i]._attachment) { "Unable to find the document",
attachment_found = true; "missing"
attachment_list[i] = specific_parameter.add_to_attachment_list; ), undefined);
break; }
res_doc = {};
} else {
err.message = "Cannot get document";
return onEnd(err, undefined);
} }
} }
if (!attachment_found) { if (specific_parameter.get) {
attachment_list.unshift(specific_parameter.add_to_attachment_list); res_doc._id = doc._id;
res_doc._rev = doc._rev;
if (option.conflicts === true) {
conflicts = priv.getConflicts(doc._rev, doc_tree);
if (conflicts) {
res_doc._conflicts = conflicts;
}
}
if (option.revs === true) {
res_doc._revisions = doc._revs;
}
if (option.revs_info === true) {
res_doc._revs_info = doc._revs_info;
}
return onEnd(undefined, res_doc);
}
if (specific_parameter.putAttachment ||
specific_parameter.removeAttachment) {
// copy metadata (not beginning by "_" to document
for (k in res_doc) {
if (res_doc.hasOwnProperty(k) && !k.match("^_")) {
doc[k] = res_doc[k];
}
}
}
if (specific_parameter.remove) {
priv.putDocumentTree(doc, option, doc_tree, callback.putDocumentTree);
} else {
priv.getAttachmentList(res_doc, option, callback.getAttachmentList);
} }
}
priv.putAttachmentList(
doc,
option,
attachment_list,
callback.putAttachmentList
);
};
callback.putAttachmentList = function (err, response) {
if (err) {
err.message = "Cannot copy attacments to the document";
return onEnd(err, undefined);
}
priv.putDocumentTree(doc, option, doc_tree, callback.putDocumentTree);
};
callback.putDocumentTree = function (err, response) {
var response_object;
if (err) {
err.message = "Cannot update the document history";
return onEnd(err, undefined);
}
response_object = {
"ok": true,
"id": doc._id,
"rev": doc._rev
}; };
if (specific_parameter.putAttachment || callback.getAttachmentList = function (err, res_list) {
specific_parameter.removeAttachment || var i, attachment_found = false;
specific_parameter.getAttachment) {
response_object.attachment = doc._attachment;
}
onEnd(undefined, response_object);
// if (option.keep_revision_history !== true) {
// // priv.remove(prev_doc, option, function () {
// // - change "available" status to "deleted"
// // - remove attachments
// // - done, no callback
// // });
// }
};
callback.begin();
};
/**
* 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) {
priv.revisionGenericRequest(
command.cloneDoc(),
command.cloneOption(),
{},
function (err, response) {
if (err) { if (err) {
return that.error(err); err.message = "Cannot get attachment";
return onEnd(err, undefined);
} }
that.success(response); attachment_list = res_list || [];
} if (specific_parameter.getAttachment) {
); // getting specific attachment
}; for (i = 0; i < attachment_list.length; i += 1) {
if (attachment_list[i] &&
/** doc._attachment ===
* Put the document metadata and create or update a document tree. attachment_list[i]._attachment) {
* Options: return onEnd(undefined, attachment_list[i]._data);
* - {boolean} keep_revision_history To keep the previous revisions }
* (false by default) (NYI). }
* @method put return onEnd(priv.notFoundError(
* @param {object} command The JIO command "Unable to get an inexistent attachment",
*/ "missing"
that.put = function (command) { ), undefined);
priv.revisionGenericRequest(
command.cloneDoc(),
command.cloneOption(),
{},
function (err, response) {
if (err) {
return that.error(err);
} }
that.success(response); if (specific_parameter.remove_from_attachment_list) {
} // removing specific attachment
); for (i = 0; i < attachment_list.length; i += 1) {
}; if (attachment_list[i] &&
specific_parameter.remove_from_attachment_list._attachment ===
attachment_list[i]._attachment) {
that.putAttachment = function (command) { attachment_found = true;
priv.revisionGenericRequest( attachment_list[i] = undefined;
command.cloneDoc(), break;
command.cloneOption(), }
{ }
"doc_id": command.getDocId(), if (!attachment_found) {
"attachment_id": command.getAttachmentId(), return onEnd(priv.notFoundError(
"add_to_attachment_list": { "Unable to remove an inexistent attachment",
"_attachment": command.getAttachmentId(), "missing"
"_mimetype": command.getAttachmentMimeType(), ), undefined);
"_data": command.getAttachmentData() }
},
"putAttachment": true
},
function (err, response) {
if (err) {
return that.error(err);
} }
that.success(response); priv.putDocument(doc, option, callback.putDocument);
} };
); callback.putDocument = function (err, response) {
}; var i, attachment_found = false;
that.remove = function (command) {
if (command.getAttachmentId()) {
return that.removeAttachment(command);
}
priv.revisionGenericRequest(
command.cloneDoc(),
command.cloneOption(),
{
"revision_needed": true,
"remove": true
},
function (err, response) {
if (err) { if (err) {
return that.error(err); err.message = "Cannot post the document";
return onEnd(err, undefined);
} }
that.success(response); if (specific_parameter.add_to_attachment_list) {
} // adding specific attachment
); attachment_list = attachment_list || [];
}; for (i = 0; i < attachment_list.length; i += 1) {
if (attachment_list[i] &&
that.removeAttachment = function (command) { specific_parameter.add_to_attachment_list._attachment ===
priv.revisionGenericRequest( attachment_list[i]._attachment) {
command.cloneDoc(), attachment_found = true;
command.cloneOption(), attachment_list[i] = specific_parameter.add_to_attachment_list;
{ break;
"doc_id": command.getDocId(), }
"attachment_id": command.getAttachmentId(), }
"revision_needed": true, if (!attachment_found) {
"removeAttachment": true, attachment_list.unshift(specific_parameter.add_to_attachment_list);
"remove_from_attachment_list": { }
"_attachment": command.getAttachmentId()
}
},
function (err, response) {
if (err) {
return that.error(err);
} }
that.success(response); priv.putAttachmentList(
} doc,
); option,
}; attachment_list,
callback.putAttachmentList
that.get = function (command) { );
if (command.getAttachmentId()) { };
return that.getAttachment(command); callback.putAttachmentList = function (err, response) {
}
priv.revisionGenericRequest(
command.cloneDoc(),
command.cloneOption(),
{
"get": true
},
function (err, response) {
if (err) { if (err) {
return that.error(err); err.message = "Cannot copy attacments to the document";
return onEnd(err, undefined);
} }
that.success(response); priv.putDocumentTree(doc, option, doc_tree, callback.putDocumentTree);
} };
); callback.putDocumentTree = function (err, response) {
}; var response_object;
that.getAttachment = function (command) {
priv.revisionGenericRequest(
command.cloneDoc(),
command.cloneOption(),
{
"doc_id": command.getDocId(),
"attachment_id": command.getAttachmentId(),
"getAttachment": true
},
function (err, response) {
if (err) { if (err) {
return that.error(err); err.message = "Cannot update the document history";
return onEnd(err, undefined);
} }
that.success(response); response_object = {
} "ok": true,
); "id": doc._id,
}; "rev": doc._rev
};
that.allDocs = function (command) { if (specific_parameter.putAttachment ||
var rows, result = {"total_rows": 0, "rows": []}, functions = {}; specific_parameter.removeAttachment ||
functions.finished = 0; specific_parameter.getAttachment) {
functions.falseResponseGenerator = function (response, callback) { response_object.attachment = doc._attachment;
callback(undefined, response);
};
functions.fillResultGenerator = function (doc_id) {
return function (err, doc_tree) {
var document_revision, row, revs_info;
if (err) {
return that.error(err);
} }
revs_info = priv.getWinnerRevsInfo(doc_tree); onEnd(undefined, response_object);
document_revision = // if (option.keep_revision_history !== true) {
rows.document_revisions[doc_id + "." + revs_info[0].rev]; // // priv.remove(prev_doc, option, function () {
if (document_revision) { // // - change "available" status to "deleted"
row = { // // - remove attachments
"id": doc_id, // // - done, no callback
"key": doc_id, // // });
"value": { // }
"rev": revs_info[0].rev };
} callback.begin();
}; };
if (document_revision.doc && command.getOption("include_docs")) {
document_revision.doc._id = doc_id; /**
document_revision.doc._rev = revs_info[0].rev; * Post the document metadata and create or update a document tree.
row.doc = document_revision.doc; * 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) {
priv.revisionGenericRequest(
command.cloneDoc(),
command.cloneOption(),
{},
function (err, response) {
if (err) {
return that.error(err);
} }
result.rows.push(row); that.success(response);
result.total_rows += 1;
} }
functions.success(); );
}; };
/**
* Put 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 put
* @param {object} command The JIO command
*/
that.put = function (command) {
priv.revisionGenericRequest(
command.cloneDoc(),
command.cloneOption(),
{},
function (err, response) {
if (err) {
return that.error(err);
}
that.success(response);
}
);
};
that.putAttachment = function (command) {
priv.revisionGenericRequest(
command.cloneDoc(),
command.cloneOption(),
{
"doc_id": command.getDocId(),
"attachment_id": command.getAttachmentId(),
"add_to_attachment_list": {
"_attachment": command.getAttachmentId(),
"_mimetype": command.getAttachmentMimeType(),
"_data": command.getAttachmentData()
},
"putAttachment": true
},
function (err, response) {
if (err) {
return that.error(err);
}
that.success(response);
}
);
}; };
functions.success = function () {
functions.finished -= 1; that.remove = function (command) {
if (functions.finished === 0) { if (command.getAttachmentId()) {
that.success(result); return that.removeAttachment(command);
} }
priv.revisionGenericRequest(
command.cloneDoc(),
command.cloneOption(),
{
"revision_needed": true,
"remove": true
},
function (err, response) {
if (err) {
return that.error(err);
}
that.success(response);
}
);
};
that.removeAttachment = function (command) {
priv.revisionGenericRequest(
command.cloneDoc(),
command.cloneOption(),
{
"doc_id": command.getDocId(),
"attachment_id": command.getAttachmentId(),
"revision_needed": true,
"removeAttachment": true,
"remove_from_attachment_list": {
"_attachment": command.getAttachmentId()
}
},
function (err, response) {
if (err) {
return that.error(err);
}
that.success(response);
}
);
}; };
priv.send("allDocs", null, command.cloneOption(), function (err, response) {
var i, j, row, selector, selected; that.get = function (command) {
if (err) { if (command.getAttachmentId()) {
return that.error(err); return that.getAttachment(command);
} }
selector = /\.revision_tree\.json$/; priv.revisionGenericRequest(
rows = { command.cloneDoc(),
"revision_trees": { command.cloneOption(),
// id.revision_tree.json: { {
// id: blabla "get": true
// doc: {...}
// }
}, },
"document_revisions": { function (err, response) {
// id.rev: { if (err) {
// id: blabla return that.error(err);
// rev: 1-1 }
// doc: {...} that.success(response);
// }
} }
);
};
that.getAttachment = function (command) {
priv.revisionGenericRequest(
command.cloneDoc(),
command.cloneOption(),
{
"doc_id": command.getDocId(),
"attachment_id": command.getAttachmentId(),
"getAttachment": true
},
function (err, response) {
if (err) {
return that.error(err);
}
that.success(response);
}
);
};
that.allDocs = function (command) {
var rows, result = {"total_rows": 0, "rows": []}, functions = {};
functions.finished = 0;
functions.falseResponseGenerator = function (response, callback) {
callback(undefined, response);
}; };
while (response.rows.length > 0) { functions.fillResultGenerator = function (doc_id) {
// filling rows return function (err, doc_tree) {
row = response.rows.shift(); var document_revision, row, revs_info;
selected = selector.exec(row.id); if (err) {
if (selected) { return that.error(err);
selected = selected.input.substring(0, selected.index);
// this is a revision tree
rows.revision_trees[row.id] = {
"id": selected
};
if (row.doc) {
rows.revision_trees[row.id].doc = row.doc;
} }
} else { revs_info = priv.getWinnerRevsInfo(doc_tree);
// this is a simple revision document_revision =
rows.document_revisions[row.id] = { rows.document_revisions[doc_id + "." + revs_info[0].rev];
"id": row.id.split(".").slice(0, -1), if (document_revision) {
"rev": row.id.split(".").slice(-1) row = {
}; "id": doc_id,
if (row.doc) { "key": doc_id,
rows.document_revisions[row.id].doc = row.doc; "value": {
"rev": revs_info[0].rev
}
};
if (document_revision.doc && command.getOption("include_docs")) {
document_revision.doc._id = doc_id;
document_revision.doc._rev = revs_info[0].rev;
row.doc = document_revision.doc;
}
result.rows.push(row);
result.total_rows += 1;
} }
functions.success();
};
};
functions.success = function () {
functions.finished -= 1;
if (functions.finished === 0) {
that.success(result);
} }
} };
functions.finished += 1; priv.send("allDocs", null, command.cloneOption(
for (i in rows.revision_trees) { ), function (err, response) {
if (rows.revision_trees.hasOwnProperty(i)) { var i, j, row, selector, selected;
functions.finished += 1; if (err) {
if (rows.revision_trees[i].doc) { return that.error(err);
functions.falseResponseGenerator( }
rows.revision_trees[i].doc, selector = /\.revision_tree\.json$/;
functions.fillResultGenerator(rows.revision_trees[i].id) rows = {
); "revision_trees": {
// id.revision_tree.json: {
// id: blabla
// doc: {...}
// }
},
"document_revisions": {
// id.rev: {
// id: blabla
// rev: 1-1
// doc: {...}
// }
}
};
while (response.rows.length > 0) {
// filling rows
row = response.rows.shift();
selected = selector.exec(row.id);
if (selected) {
selected = selected.input.substring(0, selected.index);
// this is a revision tree
rows.revision_trees[row.id] = {
"id": selected
};
if (row.doc) {
rows.revision_trees[row.id].doc = row.doc;
}
} else { } else {
priv.getRevisionTree( // this is a simple revision
{"_id": rows.revision_trees[i].id}, rows.document_revisions[row.id] = {
command.cloneOption(), "id": row.id.split(".").slice(0, -1),
functions.fillResultGenerator(rows.revision_trees[i].id) "rev": row.id.split(".").slice(-1)
); };
if (row.doc) {
rows.document_revisions[row.id].doc = row.doc;
}
} }
} }
} functions.finished += 1;
functions.success(); for (i in rows.revision_trees) {
}); if (rows.revision_trees.hasOwnProperty(i)) {
}; functions.finished += 1;
if (rows.revision_trees[i].doc) {
// END // functions.falseResponseGenerator(
priv.RevisionStorage(); rows.revision_trees[i].doc,
return that; functions.fillResultGenerator(rows.revision_trees[i].id)
}); // end RevisionStorage );
} else {
priv.getRevisionTree(
{"_id": rows.revision_trees[i].id},
command.cloneOption(),
functions.fillResultGenerator(rows.revision_trees[i].id)
);
}
}
}
functions.success();
});
};
// END //
priv.RevisionStorage();
return that;
}); // end RevisionStorage
}));
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