Commit 91a84412 authored by Tristan Cavelier's avatar Tristan Cavelier

Update ConflictManagerStorage and Jio tests

parent 36e09bbc
...@@ -30,90 +30,79 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -30,90 +30,79 @@ var newConflictManagerStorage = function ( spec, my ) {
var cloned_option = command.cloneOption (); var cloned_option = command.cloneOption ();
cloned_option.metadata_only = false; cloned_option.metadata_only = false;
cloned_option.max_retry = command.getOption('max_retry') || 3; cloned_option.max_retry = command.getOption('max_retry') || 3;
cloned_option.error = error; that.addJob ('get',priv.secondstorage_spec,path,cloned_option,
cloned_option.success = success; success, error);
var newcommand = that.newCommand(
'loadDocument',{path:path,
option:cloned_option});
that.addJob ( that.newStorage (priv.secondstorage_spec),
newcommand );
}; };
priv.saveMetadataToDistant = function (command,path,content,success,error) { priv.saveMetadataToDistant = function (command,path,content,success,error) {
var cloned_option = command.cloneOption (); // max_retry:0 // inf
cloned_option.error = error; that.addJob ('put',priv.secondstorage_spec,
cloned_option.success = success; {_id:path,content:JSON.stringify (content)},
var newcommand = that.newCommand( command.cloneOption(),success,error);
'saveDocument',{path:path,
content:JSON.stringify (content),
option:cloned_option});
// newcommand.setMaxRetry (0); // inf
that.addJob ( that.newStorage (priv.secondstorage_spec),
newcommand );
}; };
priv.saveNewRevision = function (command,path,content,success,error) { priv.saveNewRevision = function (command,path,content,success,error) {
var cloned_option = command.cloneOption (); that.addJob ('put',priv.secondstorage_spec,{_id:path,content:content},
cloned_option.error = error; command.cloneOption(),success,error);
cloned_option.success = success;
var newcommand = that.newCommand(
'saveDocument',{path:path,
content:content,
option:cloned_option});
that.addJob ( that.newStorage (priv.secondstorage_spec),
newcommand );
}; };
priv.loadRevision = function (command,path,success,error) { priv.loadRevision = function (command,path,success,error) {
var cloned_option = command.cloneOption (); that.addJob('get',priv.secondstorage_spec,path,command.cloneOption(),
cloned_option.error = error; success, error);
cloned_option.success = success;
var newcommand = that.newCommand (
'loadDocument',{path:path,
option:cloned_option});
that.addJob ( that.newStorage (priv.secondstorage_spec),
newcommand );
}; };
priv.deleteAFile = function (command,path,success,error) { priv.deleteAFile = function (command,path,success,error) {
var cloned_option = command.cloneOption(); var cloned_option = command.cloneOption();
cloned_option.max_retry = 0; // inf cloned_option.max_retry = 0; // inf
cloned_option.error = error; that.addJob ('remove',priv.secondstorage_spec,{_id:path},
cloned_option.success = success; command.cloneOption(), success, error);
var newcommand = that.newCommand(
'removeDocument',{path:path,
option:cloned_option});
that.addJob ( that.newStorage (priv.secondstorage_spec),
newcommand );
}; };
priv.chooseARevision = function (metadata) { priv.chooseARevision = function (metadata) {
var tmp_last_modified = 0, ret_rev = ''; var tmp_last_modified = 0, ret_rev = '', rev;
for (var rev in metadata) { for (rev in metadata) {
if (tmp_last_modified < if (tmp_last_modified <
metadata[rev].last_modified) { metadata[rev]._last_modified) {
tmp_last_modified = tmp_last_modified =
metadata[rev].last_modified; metadata[rev]._last_modified;
ret_rev = rev; ret_rev = rev;
} }
} }
return ret_rev; return ret_rev;
}; };
priv.solveConflict = function (path,content,option) { priv._revs = function (metadata,revision) {
if (metadata[revision]) {
return {start:metadata[revision]._revisions.length,
ids:metadata[revision]._revisions};
} else {
return null;
}
};
priv._revs_info = function (metadata) {
var k, l = [];
for (k in metadata) {
l.push({
rev:k,status:(metadata[k]?(
metadata[k]._deleted?'deleted':'available'):'missing')
});
}
return l;
};
priv.solveConflict = function (doc,option,param) {
var o = {}, am = priv.newAsyncModule(), var o = {}, am = priv.newAsyncModule(),
command = option.command, command = param.command,
metadata_file_path = path + '.metadata', metadata_file_path = param.docid + '.metadata',
current_revision = '', current_revision = '',
current_revision_file_path = '', current_revision_file_path = '',
metadata_file_content = null, metadata_file_content = null,
on_conflict = false, conflict_object = {}, on_conflict = false, conflict_object = {total_rows:0,rows:[]},
on_remove = option.deleted, on_remove = param._deleted,
previous_revision = option.previous_revision, previous_revision = param.previous_revision,
previous_revision_object = option.revision_remove_object || {}, previous_revision_content_object = null,
previous_revision_content_object = previous_revision_object[
previous_revision] || {},
now = new Date(), now = new Date(),
failerror; failerror;
...@@ -126,11 +115,13 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -126,11 +115,13 @@ var newConflictManagerStorage = function ( spec, my ) {
metadata_file_content = JSON.parse (result.content); metadata_file_content = JSON.parse (result.content);
// set current revision // set current revision
current_revision = (previous_revision_number + 1) + '-' + current_revision = (previous_revision_number + 1) + '-' +
hex_sha256 ('' + content + hex_sha256 ('' + doc.content +
previous_revision + previous_revision +
JSON.stringify (metadata_file_content)); JSON.stringify (metadata_file_content));
current_revision_file_path = path + '.' + current_revision_file_path = param.docid + '.' +
current_revision; current_revision;
previous_revision_content_object = metadata_file_content[
previous_revision] || {};
if (!on_remove) { if (!on_remove) {
am.wait(o,'saveMetadataOnDistant',1); am.wait(o,'saveMetadataOnDistant',1);
am.call(o,'saveNewRevision'); am.call(o,'saveNewRevision');
...@@ -143,7 +134,7 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -143,7 +134,7 @@ var newConflictManagerStorage = function ( spec, my ) {
}; };
o.saveNewRevision = function (){ o.saveNewRevision = function (){
priv.saveNewRevision ( priv.saveNewRevision (
command, current_revision_file_path, content, command, current_revision_file_path, doc.content,
function (result) { function (result) {
am.call(o,'saveMetadataOnDistant'); am.call(o,'saveMetadataOnDistant');
}, function (error) { }, function (error) {
...@@ -152,18 +143,20 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -152,18 +143,20 @@ var newConflictManagerStorage = function ( spec, my ) {
); );
}; };
o.previousUpdateMetadata = function () { o.previousUpdateMetadata = function () {
for (var prev_rev in previous_revision_object) { var i;
delete metadata_file_content[prev_rev]; for (i = 0; i < param.key.length; i+= 1) {
delete metadata_file_content[param.key[i]];
} }
am.call(o,'checkForConflicts'); am.call(o,'checkForConflicts');
}; };
o.checkForConflicts = function () { o.checkForConflicts = function () {
for (var rev in metadata_file_content) { var rev;
for (rev in metadata_file_content) {
var revision_index; var revision_index;
on_conflict = true; on_conflict = true;
failerror = { failerror = {
status:20, status:409,error:'conflict',
statusText:'Conflict', statusText:'Conflict',reason:'document update conflict',
message:'There is one or more conflicts' message:'There is one or more conflicts'
}; };
break; break;
...@@ -171,22 +164,29 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -171,22 +164,29 @@ var newConflictManagerStorage = function ( spec, my ) {
am.call(o,'updateMetadata'); am.call(o,'updateMetadata');
}; };
o.updateMetadata = function (){ o.updateMetadata = function (){
var revision_history, id = '';
id = current_revision.split('-'); id.shift(); id = id.join('-');
revision_history = previous_revision_content_object._revisions;
revision_history.unshift(id);
metadata_file_content[current_revision] = { metadata_file_content[current_revision] = {
creation_date: previous_revision_content_object.creation_date || _creation_date:previous_revision_content_object._creation_date||
now.getTime(), now.getTime(),
last_modified: now.getTime(), _last_modified: now.getTime(),
conflict: on_conflict, _revisions: revision_history,
deleted: on_remove _conflict: on_conflict,
_deleted: on_remove
}; };
if (on_conflict) {
conflict_object = conflict_object =
priv.createConflictObject( priv.createConflictObject(
command, metadata_file_content, current_revision command, metadata_file_content, current_revision
); );
}
am.call(o,'saveMetadataOnDistant'); am.call(o,'saveMetadataOnDistant');
}; };
o.saveMetadataOnDistant = function (){ o.saveMetadataOnDistant = function (){
priv.saveMetadataToDistant( priv.saveMetadataToDistant(
command, metadata_file_path,metadata_file_content, command, metadata_file_path, metadata_file_content,
function (result) { function (result) {
am.call(o,'deleteAllConflictingRevision'); am.call(o,'deleteAllConflictingRevision');
if (on_conflict) { if (on_conflict) {
...@@ -200,58 +200,115 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -200,58 +200,115 @@ var newConflictManagerStorage = function ( spec, my ) {
); );
}; };
o.deleteAllConflictingRevision = function (){ o.deleteAllConflictingRevision = function (){
for (var prev_rev in previous_revision_object) { var i;
for (i = 0; i < param.key.length; i+= 1) {
priv.deleteAFile ( priv.deleteAFile (
command, path+'.'+prev_rev, empty_fun, empty_fun ); command, param.docid+'.'+param.key[i], empty_fun,empty_fun);
} }
}; };
o.success = function (){ o.success = function (){
var a = {ok:true,id:param.docid,rev:current_revision};
am.neverCall(o,'error'); am.neverCall(o,'error');
am.neverCall(o,'success'); am.neverCall(o,'success');
if (option.success) {option.success({revision:current_revision});} if (option.revs) {
a.revisions = priv._revs(metadata_file_content,
current_revision);
}
if (option.revs_info) {
a.revs_info = priv._revs_info(metadata_file_content);
}
if (option.conflicts) {
a.conflicts = conflict_object;
}
param.success(a);
}; };
o.error = function (error){ o.error = function (error){
var gooderror = error || failerror || {}; var err = error || failerror ||
if (on_conflict) { {status:0,statusText:'Unknown',error:'unknown_error',
gooderror.conflict_object = conflict_object; message:'Unknown error.',reason:'unknown error'};
if (current_revision) {
err.rev = current_revision;
}
if (option.revs) {
err.revisions = priv._revs(metadata_file_content,
current_revision);
}
if (option.revs_info) {
err.revs_info = priv._revs_info(metadata_file_content);
}
if (option.conflicts) {
err.conflicts = conflict_object;
} }
am.neverCall(o,'error'); am.neverCall(o,'error');
am.neverCall(o,'success'); am.neverCall(o,'success');
if (option.error) {option.error(gooderror);} param.error(err);
}; };
am.call(o,'getDistantMetadata'); am.call(o,'getDistantMetadata');
}; };
priv.createConflictObject = function (command, metadata, revision) { priv.createConflictObject = function (command, metadata, revision) {
var cloned_command = command.clone(); return {
var conflict_object = { total_rows:1,
path: command.getPath(), rows:[priv.createConflictRow(command,command.getDocId(),
revision: revision, metadata,revision)]
revision_object: metadata, };
getConflictRevisionList: function () { };
return this.revision_object;
}, priv.getParam = function (list) {
solveConflict: function (content,option) { var param = {}, i = 0;
if (typeof content === 'undefined') { if (typeof list[i] === 'string') {
option = option || {}; param.content = list[i];
option.deleted = true; i ++;
} else if (typeof content === 'object') { }
option = content; if (typeof list[i] === 'object') {
option.deleted = true; param.options = list[i];
i ++;
} else { } else {
option = option || {}; param.options = {};
option.deleted = false;
content = content || '';
} }
option.previous_revision = this.revision; param.callback = function (err,val){};
option.revision_remove_object = this.revision_object; param.success = function (val) {
option.command = cloned_command; param.callback(undefined,val);
};
param.error = function (err) {
param.callback(err,undefined);
};
if (typeof list[i] === 'function') {
if (typeof list[i+1] === 'function') {
param.success = list[i];
param.error = list[i+1];
} else {
param.callback = list[i];
}
}
return param;
};
priv.createConflictRow = function (command, docid, metadata, revision) {
var row = {id:docid,key:[],value:{
_solveConflict: function (/*content, option, success, error*/) {
var param = {}, got = priv.getParam(arguments);
if (got.content === undefined) {
param._deleted = true;
} else {
param._deleted = false;
}
param.success = got.success;
param.error = got.error;
param.previous_revision = revision;
param.docid = docid;
param.key = row.key;
param.command = command.clone();
return priv.solveConflict ( return priv.solveConflict (
this.path, content, option {_id:docid,content:got.content,_rev:revision},
got.options,param
); );
} }
}; }}, k;
return conflict_object; for (k in metadata) {
row.key.push(k);
}
return row;
}; };
priv.newAsyncModule = function () { priv.newAsyncModule = function () {
...@@ -282,31 +339,28 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -282,31 +339,28 @@ var newConflictManagerStorage = function ( spec, my ) {
return async; return async;
}; };
that.post = function (command) {
that.put (command);
};
/** /**
* Save a document and can manage conflicts. * Save a document and can manage conflicts.
* @method saveDocument * @method put
*/ */
that.saveDocument = function (command) { that.put = function (command) {
var o = {}, am = priv.newAsyncModule(), var o = {}, am = priv.newAsyncModule(),
metadata_file_path = command.getPath() + '.metadata', metadata_file_path = command.getDocId() + '.metadata',
current_revision = '', current_revision = '',
current_revision_file_path = '', current_revision_file_path = '',
metadata_file_content = null, metadata_file_content = null,
on_conflict = false, conflict_object = {}, on_conflict = false, conflict_object = {total_rows:0,rows:[]},
previous_revision = command.getOption('previous_revision'), previous_revision = command.getDocInfo('_rev') || '0',
previous_revision_file_path = command.getPath() + '.' + previous_revision_file_path = command.getDocId() + '.' +
previous_revision, previous_revision,
now = new Date(), now = new Date(),
failerror; failerror;
if (!previous_revision) {
return setTimeout(function () {
that.error({status:0,statusText:'Parameter missing',
message:'Need a previous revision.'});
});
}
o.getDistantMetadata = function (){ o.getDistantMetadata = function (){
priv.getDistantMetadata ( priv.getDistantMetadata (
command,metadata_file_path, command,metadata_file_path,
...@@ -316,10 +370,10 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -316,10 +370,10 @@ var newConflictManagerStorage = function ( spec, my ) {
metadata_file_content = JSON.parse (result.content); metadata_file_content = JSON.parse (result.content);
// set current revision // set current revision
current_revision = (previous_revision_number + 1) + '-' + current_revision = (previous_revision_number + 1) + '-' +
hex_sha256 ('' + command.getContent() + hex_sha256 ('' + command.getDocContent() +
previous_revision + previous_revision +
JSON.stringify (metadata_file_content)); JSON.stringify (metadata_file_content));
current_revision_file_path = command.getPath() + '.' + current_revision_file_path = command.getDocId() + '.' +
current_revision; current_revision;
am.wait(o,'saveMetadataOnDistant',1); am.wait(o,'saveMetadataOnDistant',1);
am.call(o,'saveNewRevision'); am.call(o,'saveNewRevision');
...@@ -327,8 +381,8 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -327,8 +381,8 @@ var newConflictManagerStorage = function ( spec, my ) {
},function (error) { },function (error) {
if (error.status === 404) { if (error.status === 404) {
current_revision = '1-' + current_revision = '1-' +
hex_sha256 (command.getContent()); hex_sha256 (command.getDocContent());
current_revision_file_path = command.getPath() + '.' + current_revision_file_path = command.getDocId() + '.' +
current_revision; current_revision;
am.wait(o,'saveMetadataOnDistant',1); am.wait(o,'saveMetadataOnDistant',1);
am.call(o,'saveNewRevision'); am.call(o,'saveNewRevision');
...@@ -341,7 +395,7 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -341,7 +395,7 @@ var newConflictManagerStorage = function ( spec, my ) {
}; };
o.saveNewRevision = function (){ o.saveNewRevision = function (){
priv.saveNewRevision ( priv.saveNewRevision (
command,current_revision_file_path,command.getContent(), command,current_revision_file_path,command.getDocContent(),
function (result) { function (result) {
am.call(o,'saveMetadataOnDistant'); am.call(o,'saveMetadataOnDistant');
}, function (error) { }, function (error) {
...@@ -350,13 +404,14 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -350,13 +404,14 @@ var newConflictManagerStorage = function ( spec, my ) {
); );
}; };
o.checkForConflicts = function () { o.checkForConflicts = function () {
for (var rev in metadata_file_content) { var rev;
for (rev in metadata_file_content) {
if (rev !== previous_revision) { if (rev !== previous_revision) {
on_conflict = true; on_conflict = true;
failerror = { failerror = {
status:20, status:409,error:'conflict',
statusText:'Conflict', statusText:'Conflict',reason:'document update conflict',
message:'There is one or more conflicts' message:'Document update conflict.'
}; };
break; break;
} }
...@@ -364,41 +419,47 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -364,41 +419,47 @@ var newConflictManagerStorage = function ( spec, my ) {
am.call(o,'updateMetadata'); am.call(o,'updateMetadata');
}; };
o.createMetadata = function (){ o.createMetadata = function (){
var id = current_revision;
id = id.split('-'); id.shift(); id = id.join('-');
metadata_file_content = {}; metadata_file_content = {};
metadata_file_content[current_revision] = { metadata_file_content[current_revision] = {
creation_date: now.getTime(), _creation_date: now.getTime(),
last_modified: now.getTime(), _last_modified: now.getTime(),
conflict: false, _revisions: [id],
deleted: false _conflict: false,
_deleted: false
}; };
am.call(o,'saveMetadataOnDistant'); am.call(o,'saveMetadataOnDistant');
}; };
o.updateMetadata = function (){ o.updateMetadata = function (){
var previous_creation_date; var previous_creation_date, revision_history = [], id = '';
if (metadata_file_content[previous_revision]) { if (metadata_file_content[previous_revision]) {
previous_creation_date = metadata_file_content[ previous_creation_date = metadata_file_content[
previous_revision].creation_date; previous_revision]._creation_date;
revision_history = metadata_file_content[
previous_revision]._revisions;
delete metadata_file_content[previous_revision]; delete metadata_file_content[previous_revision];
} }
id = current_revision.split('-'); id.shift(); id = id.join('-');
revision_history.unshift(id);
metadata_file_content[current_revision] = { metadata_file_content[current_revision] = {
creation_date: previous_creation_date || now.getTime(), _creation_date: previous_creation_date || now.getTime(),
last_modified: now.getTime(), _last_modified: now.getTime(),
conflict: on_conflict, _revisions: revision_history,
deleted: false _conflict: on_conflict,
_deleted: false
}; };
if (on_conflict) { if (on_conflict) {
conflict_object = conflict_object =
priv.createConflictObject( priv.createConflictObject(
command, command, metadata_file_content, current_revision
metadata_file_content,
current_revision
); );
} }
am.call(o,'saveMetadataOnDistant'); am.call(o,'saveMetadataOnDistant');
}; };
o.saveMetadataOnDistant = function (){ o.saveMetadataOnDistant = function (){
priv.saveMetadataToDistant( priv.saveMetadataToDistant(
command,metadata_file_path,metadata_file_content, command, metadata_file_path, metadata_file_content,
function (result) { function (result) {
am.call(o,'deletePreviousRevision'); am.call(o,'deletePreviousRevision');
if (on_conflict) { if (on_conflict) {
...@@ -418,44 +479,65 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -418,44 +479,65 @@ var newConflictManagerStorage = function ( spec, my ) {
empty_fun,empty_fun); empty_fun,empty_fun);
} }
}; };
o.success = function (){ o.success = function () {
var a = {ok:true,id:command.getDocId(),rev:current_revision};
am.neverCall(o,'error'); am.neverCall(o,'error');
am.neverCall(o,'success'); am.neverCall(o,'success');
that.success({revision:current_revision}); if (command.getOption('revs')) {
a.revisions = priv._revs(metadata_file_content,
current_revision);
}
if (command.getOption('revs_info')) {
a.revs_info = priv._revs_info(metadata_file_content);
}
if (command.getOption('conflicts')) {
a.conflicts = conflict_object;
}
that.success(a);
}; };
o.error = function (error){ o.error = function (error) {
var gooderror = error || failerror || var err = error || failerror ||
{status:0,statusText:'Unknown', {status:0,statusText:'Unknown',error:'unknown_error',
message:'Unknown error.'}; message:'Unknown error.',reason:'unknown error'};
if (on_conflict) { if (current_revision) {
gooderror.conflict_object = conflict_object; err.rev = current_revision;
}
if (command.getOption('revs')) {
err.revisions = priv._revs(metadata_file_content,
current_revision);
}
if (command.getOption('revs_info')) {
err.revs_info = priv._revs_info(metadata_file_content);
}
if (command.getOption('conflicts')) {
err.conflicts = conflict_object;
} }
am.neverCall(o,'error'); am.neverCall(o,'error');
am.neverCall(o,'success'); am.neverCall(o,'success');
that.error(gooderror); that.error(err);
}; };
am.call(o,'getDistantMetadata'); am.call(o,'getDistantMetadata');
}; }; // end put
/** /**
* Load a document from several storages, and send the first retreived * Load a document from several storages, and send the first retreived
* document. * document.
* @method loadDocument * @method get
*/ */
that.loadDocument = function (command) { that.get = function (command) {
var o = {}, am = priv.newAsyncModule(), var o = {}, am = priv.newAsyncModule(),
metadata_file_path = command.getPath() + '.metadata', metadata_file_path = command.getDocId() + '.metadata',
current_revision = command.getOption('revision') || '', current_revision = command.getOption('rev') || '',
metadata_file_content = null, metadata_file_content = null,
metadata_only = command.getOption('metadata_only'), metadata_only = command.getOption('metadata_only'),
on_conflict = false, conflict_object = {}, on_conflict = false, conflict_object = {total_rows:0,rows:[]},
now = new Date(), now = new Date(),
doc = {name:command.getPath()}, doc = {_id:command.getDocId()},
call404 = function (message) { call404 = function (message) {
am.call(o,'error',[{ am.call(o,'error',[{
status:404,statusText:'Not Found', status:404,statusText:'Not Found',error:'not_found',
message:message message:message,reason:message
}]); }]);
}; };
...@@ -482,12 +564,18 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -482,12 +564,18 @@ var newConflictManagerStorage = function ( spec, my ) {
} else { } else {
current_revision = priv.chooseARevision(metadata_file_content); current_revision = priv.chooseARevision(metadata_file_content);
} }
doc.last_modified = doc._last_modified =
metadata_file_content[current_revision].last_modified; metadata_file_content[current_revision]._last_modified;
doc.creation_date = doc._creation_date =
metadata_file_content[current_revision].creation_date; metadata_file_content[current_revision]._creation_date;
doc.revision = current_revision; doc._rev = current_revision;
doc.revision_object = metadata_file_content; if (command.getOption('revs')) {
doc._revisions = priv._revs(metadata_file_content,
current_revision);
}
if (command.getOption('revs_info')) {
doc._revs_info = priv._revs_info(metadata_file_content);
}
if (metadata_only) { if (metadata_only) {
am.call(o,'success'); am.call(o,'success');
} else { } else {
...@@ -496,11 +584,11 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -496,11 +584,11 @@ var newConflictManagerStorage = function ( spec, my ) {
}; };
o.loadRevision = function (){ o.loadRevision = function (){
if (!current_revision || if (!current_revision ||
metadata_file_content[current_revision].deleted) { metadata_file_content[current_revision]._deleted) {
return call404('Document has been removed.'); return call404('Document has been removed.');
} }
priv.loadRevision ( priv.loadRevision (
command, doc.name+'.'+current_revision, command, doc._id+'.'+current_revision,
function (result) { function (result) {
doc.content = result.content; doc.content = result.content;
am.call(o,'success'); am.call(o,'success');
...@@ -518,7 +606,9 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -518,7 +606,9 @@ var newConflictManagerStorage = function ( spec, my ) {
metadata_file_content, metadata_file_content,
current_revision current_revision
); );
doc.conflict_object = conflict_object; }
if (command.getOption('conflicts')) {
doc._conflicts = conflict_object;
} }
am.call(o,'success'); am.call(o,'success');
}; };
...@@ -543,35 +633,30 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -543,35 +633,30 @@ var newConflictManagerStorage = function ( spec, my ) {
/** /**
* Get a document list from several storages, and returns the first * Get a document list from several storages, and returns the first
* retreived document list. * retreived document list.
* @method getDocumentList * @method allDocs
*/ */
that.getDocumentList = function (command) { that.allDocs = function (command) {
var o = {}, am = priv.newAsyncModule(), var o = {}, am = priv.newAsyncModule(),
metadata_only = command.getOption('metadata_only'), metadata_only = command.getOption('metadata_only'),
result_list = [], result_list = [],
nb_loaded_file = 0, nb_loaded_file = 0,
success_count = 0, success_max = 0; success_count = 0, success_max = 0;
o.retreiveList = function () { o.retreiveList = function () {
var cloned_option = command.cloneOption (); var cloned_option = command.cloneOption (),
cloned_option.metadata_only = true; success = function (result) {
cloned_option.error = function (error) {
am.call(o,'error',[error]);
};
cloned_option.success = function (result) {
am.call(o,'filterTheList',[result]); am.call(o,'filterTheList',[result]);
},error = function (error) {
am.call(o,'error',[error]);
}; };
var newcommand = that.newCommand( cloned_option.metadata_only = true;
'getDocumentList',{ that.addJob ('allDocs',priv.secondstorage_spec,null,cloned_option,
path:command.getPath(),option:cloned_option success,error);
});
that.addJob ( that.newStorage (priv.secondstorage_spec),
newcommand );
}; };
o.filterTheList = function (result) { o.filterTheList = function (result) {
var i; var i;
success_max ++; success_max ++;
for (i = 0; i < result.length; i+= 1) { for (i = 0; i < result.total_rows; i+= 1) {
var splitname = result[i].name.split('.') || []; var splitname = result.rows[i].id.split('.') || [];
if (splitname.length > 0 && if (splitname.length > 0 &&
splitname[splitname.length-1] === 'metadata') { splitname[splitname.length-1] === 'metadata') {
success_max ++; success_max ++;
...@@ -587,7 +672,7 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -587,7 +672,7 @@ var newConflictManagerStorage = function ( spec, my ) {
function (data) { function (data) {
data = JSON.parse (data.content); data = JSON.parse (data.content);
var revision = priv.chooseARevision(data); var revision = priv.chooseARevision(data);
if (!data[revision].deleted) { if (!data[revision]._deleted) {
am.call( am.call(
o,'loadFile', o,'loadFile',
[path,revision,data] [path,revision,data]
...@@ -602,15 +687,22 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -602,15 +687,22 @@ var newConflictManagerStorage = function ( spec, my ) {
}; };
o.loadFile = function (path,revision,data) { o.loadFile = function (path,revision,data) {
var doc = { var doc = {
name: path, id: path,key: path,value:{
last_modified:data[revision].last_modified, _last_modified:data[revision]._last_modified,
creation_date:data[revision].creation_date, _creation_date:data[revision]._creation_date,
revision:revision, _rev:revision
revision_object:data }
}; };
if (data[revision].conflict) { if (command.getOption('revs_info')) {
doc.conflict_object = priv.createConflictObject( doc.value._revs_info = priv._revs_info(data,revision);
}
if (command.getOption('conflicts')) {
if (data[revision]._conflict) {
doc.value._conflicts = priv.createConflictObject(
command, data, revision ); command, data, revision );
} else {
doc.value._conflicts = {total_rows:0,rows:[]};
}
} }
if (!metadata_only) { if (!metadata_only) {
priv.loadRevision ( priv.loadRevision (
...@@ -631,7 +723,7 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -631,7 +723,7 @@ var newConflictManagerStorage = function ( spec, my ) {
success_count ++; success_count ++;
if (success_count >= success_max) { if (success_count >= success_max) {
am.end(); am.end();
that.success(result_list); that.success({total_rows:result_list.length,rows:result_list});
} }
}; };
o.error = function (error){ o.error = function (error){
...@@ -639,33 +731,26 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -639,33 +731,26 @@ var newConflictManagerStorage = function ( spec, my ) {
that.error(error); that.error(error);
}; };
am.call(o,'retreiveList'); am.call(o,'retreiveList');
}; }; // end allDocs
/** /**
* Remove a document from several storages. * Remove a document from several storages.
* @method removeDocument * @method remove
*/ */
that.removeDocument = function (command) { that.remove = function (command) {
var o = {}, am = priv.newAsyncModule(), var o = {}, am = priv.newAsyncModule(),
metadata_file_path = command.getPath() + '.metadata', metadata_file_path = command.getDocId() + '.metadata',
current_revision = '', current_revision = '',
current_revision_file_path = '', current_revision_file_path = '',
metadata_file_content = null, metadata_file_content = null,
on_conflict = false, conflict_object = {}, on_conflict = false, conflict_object = {total_rows:0,rows:[]},
previous_revision = command.getOption('revision'), previous_revision = command.getOption('rev') || '0',
previous_revision_file_path = command.getPath() + '.' + previous_revision_file_path = command.getDocId() + '.' +
previous_revision, previous_revision,
now = new Date(), now = new Date(),
failerror; failerror;
if (!previous_revision) {
return setTimeout(function () {
that.error({status:0,statusText:'Parameter missing',
message:'Need a revision.'});
});
}
o.getDistantMetadata = function (){ o.getDistantMetadata = function (){
priv.getDistantMetadata ( priv.getDistantMetadata (
command,metadata_file_path, command,metadata_file_path,
...@@ -674,21 +759,25 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -674,21 +759,25 @@ var newConflictManagerStorage = function ( spec, my ) {
if (previous_revision === 'last') { if (previous_revision === 'last') {
previous_revision = previous_revision =
priv.chooseARevision (metadata_file_content); priv.chooseARevision (metadata_file_content);
previous_revision_file_path = command.getPath() + '.' + previous_revision_file_path = command.getDocId() + '.' +
previous_revision; previous_revision;
} }
var previous_revision_number = var previous_revision_number =
parseInt(previous_revision.split('-')[0],10); parseInt(previous_revision.split('-')[0],10) || 0;
// set current revision // set current revision
current_revision = (previous_revision_number + 1) + '-' + current_revision = (previous_revision_number + 1) + '-' +
hex_sha256 ('' + previous_revision + hex_sha256 ('' + previous_revision +
JSON.stringify (metadata_file_content)); JSON.stringify (metadata_file_content));
current_revision_file_path = command.getPath() + '.' + current_revision_file_path = command.getDocId() + '.' +
current_revision; current_revision;
am.call(o,'checkForConflicts'); am.call(o,'checkForConflicts');
},function (error) { },function (error) {
if (error.status === 404) { if (error.status === 404) {
am.call(o,'success',['0']); am.call(o,'error',[{
status:404,statusText:'Not Found',
error:'not_found',reason:'missing',
message:'Document not found.'
}]);
} else { } else {
am.call(o,'error',[error]); am.call(o,'error',[error]);
} }
...@@ -696,12 +785,13 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -696,12 +785,13 @@ var newConflictManagerStorage = function ( spec, my ) {
); );
}; };
o.checkForConflicts = function () { o.checkForConflicts = function () {
for (var rev in metadata_file_content) { var rev;
for (rev in metadata_file_content) {
if (rev !== previous_revision) { if (rev !== previous_revision) {
on_conflict = true; on_conflict = true;
failerror = { failerror = {
status:20, status:409,error:'conflict',
statusText:'Conflict', statusText:'Conflict',reason:'document update conflict',
message:'There is one or more conflicts' message:'There is one or more conflicts'
}; };
break; break;
...@@ -710,31 +800,35 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -710,31 +800,35 @@ var newConflictManagerStorage = function ( spec, my ) {
am.call(o,'updateMetadata'); am.call(o,'updateMetadata');
}; };
o.updateMetadata = function (){ o.updateMetadata = function (){
var previous_creation_date; var previous_creation_date, revision_history = [], id = '';
if (metadata_file_content[previous_revision]) { if (metadata_file_content[previous_revision]) {
previous_creation_date = metadata_file_content[ previous_creation_date = metadata_file_content[
previous_revision].creation_date; previous_revision]._creation_date;
revision_history = metadata_file_content[
previous_revision]._revisions;
delete metadata_file_content[previous_revision]; delete metadata_file_content[previous_revision];
} }
id = current_revision;
id = id.split('-'); id.shift(); id = id.join('-');
revision_history.unshift(id);
metadata_file_content[current_revision] = { metadata_file_content[current_revision] = {
creation_date: previous_creation_date || now.getTime(), _creation_date: previous_creation_date || now.getTime(),
last_modified: now.getTime(), _last_modified: now.getTime(),
conflict: on_conflict, _revisions: revision_history,
deleted: true _conflict: on_conflict,
_deleted: true
}; };
if (on_conflict) { if (on_conflict) {
conflict_object = conflict_object =
priv.createConflictObject( priv.createConflictObject(
command, command, metadata_file_content, current_revision
metadata_file_content,
current_revision
); );
} }
am.call(o,'saveMetadataOnDistant'); am.call(o,'saveMetadataOnDistant');
}; };
o.saveMetadataOnDistant = function (){ o.saveMetadataOnDistant = function (){
priv.saveMetadataToDistant( priv.saveMetadataToDistant(
command,metadata_file_path,metadata_file_content, command, metadata_file_path, metadata_file_content,
function (result) { function (result) {
am.call(o,'deletePreviousRevision'); am.call(o,'deletePreviousRevision');
if (on_conflict) { if (on_conflict) {
...@@ -755,23 +849,45 @@ var newConflictManagerStorage = function ( spec, my ) { ...@@ -755,23 +849,45 @@ var newConflictManagerStorage = function ( spec, my ) {
} }
}; };
o.success = function (revision){ o.success = function (revision){
var a = {ok:true,id:command.getDocId(),
rev:revision || current_revision};
am.neverCall(o,'error'); am.neverCall(o,'error');
am.neverCall(o,'success'); am.neverCall(o,'success');
that.success({revision:revision || current_revision}); if (command.getOption('revs')) {
a.revisions = priv._revs(metadata_file_content,
current_revision);
}
if (command.getOption('revs_info')) {
a.revs_info = priv._revs_info(metadata_file_content);
}
if (command.getOption('conflicts')) {
a.conflicts = conflict_object;
}
that.success(a);
}; };
o.error = function (error){ o.error = function (error){
var gooderror = error || failerror || var err = error || failerror ||
{status:0,statusText:'Unknown', {status:0,statusText:'Unknown',error:'unknown_error',
message:'Unknown error.'}; message:'Unknown error.',reason:'unknown error'};
if (on_conflict) { if (current_revision) {
gooderror.conflict_object = conflict_object; err.rev = current_revision;
}
if (command.getOption('revs')) {
err.revisions = priv._revs(metadata_file_content,
current_revision);
}
if (command.getOption('revs_info')) {
err.revs_info = priv._revs_info(metadata_file_content);
}
if (command.getOption('conflicts')) {
err.conflicts = conflict_object;
} }
am.neverCall(o,'error'); am.neverCall(o,'error');
am.neverCall(o,'success'); am.neverCall(o,'success');
that.error(gooderror); that.error(err);
}; };
am.call(o,'getDistantMetadata'); am.call(o,'getDistantMetadata');
}; }; // end remove
return that; return that;
}; };
......
...@@ -136,6 +136,56 @@ removeFileFromLocalStorage = function (user,appid,file) { ...@@ -136,6 +136,56 @@ removeFileFromLocalStorage = function (user,appid,file) {
newarray); newarray);
LocalOrCookieStorage.deleteItem( LocalOrCookieStorage.deleteItem(
'jio/local/'+user+'/'+appid+'/'+file._id); 'jio/local/'+user+'/'+appid+'/'+file._id);
},
makeRevsAccordingToRevsInfo = function (revs,revs_info) {
var i, j;
for (i = 0; i < revs.start; i+= 1) {
for (j = 0; j < revs_info.length; j+= 1) {
var id = revs_info[j].rev.split('-'); id.shift(); id = id.join('-');
if (revs.ids[i] === id) {
revs.ids[i] = revs_info[j].rev.split('-')[0];
break;
}
}
}
},
checkRev = function (rev) {
if (typeof rev === 'string') {
if (parseInt(rev.split('-')[0],10) > 0) {
return rev;
}
}
return 'ERROR: not a good revision!';
},
checkConflictRow = function (row) {
var fun;
if (typeof row === 'object') {
if (row.value && typeof row.value._solveConflict === 'function') {
fun = row.value._solveConflict;
row.value._solveConflict = 'function';
}
}
return fun;
},
getHashFromRev = function (rev) {
var id = rev;
if (typeof id === 'string') {
id = id.split('-');
id.shift(); id = id.join('-');
}
return id;
},
revs_infoContains = function (revs_info, rev) {
var i;
if (typeof revs_info !== 'object') {
return undefined;
}
for (i = 0; i < revs_info.length || 0; i+= 1) {
if (revs_info[i].rev && revs_info[i].rev === rev) {
return true;
}
}
return false;
}; };
//// end tools //// end tools
...@@ -1302,19 +1352,9 @@ test ('Simple methods', function () { ...@@ -1302,19 +1352,9 @@ test ('Simple methods', function () {
var o = {}; o.clock = this.sandbox.useFakeTimers(); o.t = this; var o = {}; o.clock = this.sandbox.useFakeTimers(); o.t = this;
o.clock.tick(base_tick); o.clock.tick(base_tick);
o.spy = function(res,value,message,before) { o.spy = function(value,message) {
o.f = function(result) { o.f = function(err,val) {
if (res === 'status') { deepEqual (err || val,value,message);
if (result && result.conflict_object) {
result = 'conflict';
} else if (result && typeof result.status !== 'undefined') {
result = 'fail';
} else {
result = 'done';
}
}
if (before) { before (result); }
deepEqual (result,value,message);
}; };
o.t.spy(o,'f'); o.t.spy(o,'f');
}; };
...@@ -1333,79 +1373,154 @@ test ('Simple methods', function () { ...@@ -1333,79 +1373,154 @@ test ('Simple methods', function () {
storage:{type:'local', storage:{type:'local',
username:'conflictmethods', username:'conflictmethods',
applicationname:'jiotests'}}); applicationname:'jiotests'}});
o.spy('status','done','saving "file.doc".'); // PUT
o.jio.saveDocument('file.doc','content1',{ o.spy({ok:true,id:'file.doc',rev:'1'},'saving "file.doc".');
previous_revision: '0', o.jio.put({_id:'file.doc',content:'content1'},function (err,val) {
success:function (result) { if (val) {
o.new_rev = result.revision; o.rev1 = val.rev;
o.f (result); val.rev = val.rev.split('-')[0];
}, }
error:o.f o.f (err,val);
}); });
o.tick(); o.tick();
// PUT with options
o.spy('status','done','saving "file2.doc".'); o.spy({ok:true,id:'file2.doc',rev:'1',
o.jio.saveDocument('file2.doc','yes',{ conflicts:{total_rows:0,rows:[]},
previous_revision: '0', revisions:{start:1,ids:['1']},
success:o.f, revs_info:[{rev:'1',status:'available'}]},
error:o.f 'saving "file2.doc".');
o.jio.put({_id:'file2.doc',content:'yes'},
{revs:true,revs_info:true,conflicts:true},
function (err,val) {
if (val) {
o.rev2 = val.rev;
val.rev = val.rev.split('-')[0];
if (val.revs_info) {
if (val.revisions) {
makeRevsAccordingToRevsInfo(
val.revisions,val.revs_info);
}
val.revs_info[0].rev =
val.revs_info[0].rev.split('-')[0];
}
}
o.f (err,val);
}); });
o.tick(); o.tick();
// GET
o.spy('value',{name:'file.doc',content:'content1',revision:'rev'}, o.get_callback = function (err,val) {
'loading "file.doc".',function (o) { if (val) {
if (!o) { return; } val._rev = (val._rev?val._rev.split('-')[0]:'/');
if (o.revision) { o.revision = 'rev'; } val._creation_date = (val._creation_date?true:undefined);
if (o.creation_date) { delete o.creation_date; } val._last_modified = (val._last_modified?true:undefined);
else { ok(false, 'creation date missing!'); } }
if (o.last_modified) { delete o.last_modified; } o.f(err,val);
else { ok(false, 'last modified missing!'); } };
if (o.revision_object) { delete o.revision_object; } o.spy({_id:'file.doc',content:'content1',_rev:'1',
else { ok(false, 'revision object missing!'); } _creation_date:true,_last_modified:true},'loading "file.doc".');
}); o.jio.get('file.doc',o.get_callback);
o.jio.loadDocument('file.doc',{success:o.f,error:o.f}); o.tick();
// GET with options
o.get_callback = function (err,val) {
if (val) {
val._rev = (val._rev?val._rev.split('-')[0]:'/');
val._creation_date = (val._creation_date?true:undefined);
val._last_modified = (val._last_modified?true:undefined);
if (val._revs_info) {
if (val._revisions) {
makeRevsAccordingToRevsInfo(
val._revisions,val._revs_info);
}
val._revs_info[0].rev =
val._revs_info[0].rev.split('-')[0];
}
}
o.f(err,val);
};
o.spy({_id:'file2.doc',content:'yes',_rev:'1',
_creation_date:true,_last_modified:true,
_conflicts:{total_rows:0,rows:[]},
_revisions:{start:1,ids:['1']},
_revs_info:[{rev:'1',status:'available'}]},
'loading "file2.doc".');
o.jio.get('file2.doc',{revs:true,revs_info:true,conflicts:true},
o.get_callback);
o.tick(); o.tick();
o.spy('value',[{name:'file.doc',revision:'rev'}, // allDocs
{name:'file2.doc',revision:'rev'}], o.spy({total_rows:2,rows:[{
'getting list.',function (a) { id:'file.doc',key:'file.doc',
value:{_rev:'1',_creation_date:true,_last_modified:true}
},{
id:'file2.doc',key:'file2.doc',
value:{_rev:'1',_creation_date:true,_last_modified:true}
}]},'getting list.');
o.jio.allDocs(function (err,val) {
if (val) {
var i; var i;
if (!a) { return; } for (i = 0; i < val.total_rows; i+= 1) {
for (i = 0; i < a.length; i+= 1) { val.rows[i].value._creation_date =
if (a[i].revision) { a[i].revision = 'rev'; } val.rows[i].value._creation_date?
if (a[i].creation_date) { delete a[i].creation_date; } true:undefined;
else { ok(false, 'creation date missing!'); } val.rows[i].value._last_modified =
if (a[i].last_modified) { delete a[i].last_modified; } val.rows[i].value._last_modified?
else { ok(false, 'last modified missing!'); } true:undefined;
if (a[i].revision_object) { delete a[i].revision_object; } val.rows[i].value._rev = val.rows[i].value._rev.split('-')[0];
else { ok(false, 'revision object missing!'); }
} }
// because the result can be disordered // because the result can be disordered
if (a.length === 2 && a[0].name === 'file2.doc') { if (val.total_rows === 2 && val.rows[0].id === 'file2.doc') {
var tmp = a[0]; var tmp = val.rows[0];
a[0] = a[1]; val.rows[0] = val.rows[1];
a[1] = tmp; val.rows[1] = tmp;
}
} }
o.f(err,val);
}); });
o.jio.getDocumentList('.',{success:o.f,error:o.f});
o.tick(); o.tick();
o.spy('status','done','removing "file.doc"'); // remove
o.jio.removeDocument('file.doc',{ o.spy({ok:true,id:'file.doc',rev:'2'},
success:o.f,error:o.f,revision:o.new_rev 'removing "file.doc"');
o.jio.remove({_id:'file.doc'},{rev:o.rev1},function (err,val) {
if (val) {
val.rev = val.rev?val.rev.split('-')[0]:undefined;
}
o.f(err,val);
}); });
o.tick(); o.tick();
// remove with options
o.spy('status','fail','loading document fail.'); o.spy({
o.jio.loadDocument('file.doc',{ ok:true,id:'file2.doc',rev:'2',
success:o.f,error:function (error) { conflicts:{total_rows:0,rows:[]},
if (error.status === 404) { revisions:{start:2,ids:['2',getHashFromRev(o.rev2)]},
o.f(error); revs_info:[{rev:'2',status:'deleted'}]
} else { },'removing "file2.doc"');
deepEqual (error, '{}', 'An 404 error was expected.'); o.jio.remove(
{_id:'file2.doc'},
{rev:o.rev2,conflicts:true,revs:true,revs_info:true},
function (err,val) {
if (val) {
val.rev = val.rev?val.rev.split('-')[0]:undefined;
if (val.revs_info) {
if (val.revisions) {
makeRevsAccordingToRevsInfo(
val.revisions,val.revs_info);
} }
val.revs_info[0].rev =
val.revs_info[0].rev.split('-')[0];
}
}
o.f(err,val);
});
o.tick();
o.spy(404,'loading document fail.');
o.jio.get('file.doc',function (err,val) {
if (err) {
err = err.status;
} }
o.f(err,val);
}); });
o.tick(); o.tick();
...@@ -1417,33 +1532,9 @@ test ('Revision Conflict', function() { ...@@ -1417,33 +1532,9 @@ test ('Revision Conflict', function() {
var o = {}; o.clock = this.sandbox.useFakeTimers(); o.t = this; var o = {}; o.clock = this.sandbox.useFakeTimers(); o.t = this;
o.clock.tick (base_tick); o.clock.tick (base_tick);
o.spy = function(res,value,message,function_name) { o.spy = basic_spy_function;
function_name = function_name || 'f'; o.tick = basic_tick_function;
o[function_name] = function(result) {
if (res === 'status') {
if (result && result.conflict_object) {
result = 'conflict';
} else if (result && typeof result.status !== 'undefined') {
result = 'fail';
} else {
result = 'done';
}
}
deepEqual (result,value,message);
};
o.t.spy(o,function_name);
};
o.tick = function (tick, function_name) {
function_name = function_name || 'f'
o.clock.tick(tick || 1000);
if (!o[function_name].calledOnce) {
if (o[function_name].called) {
ok(false, 'too much results');
} else {
ok(false, 'no response');
}
}
};
o.localNamespace = 'jio/local/revisionconflict/jiotests/'; o.localNamespace = 'jio/local/revisionconflict/jiotests/';
o.rev={}; o.rev={};
o.checkContent = function (string,message) { o.checkContent = function (string,message) {
...@@ -1461,76 +1552,136 @@ test ('Revision Conflict', function() { ...@@ -1461,76 +1552,136 @@ test ('Revision Conflict', function() {
o.jio = JIO.newJio({type:'conflictmanager', o.jio = JIO.newJio({type:'conflictmanager',
storage:o.secondstorage_spec}); storage:o.secondstorage_spec});
// create a new file // create a new file
o.spy('status','done','new file "file.doc", revision: "0".'); o.spy(o,'value',
o.jio.saveDocument( {ok:true,id:'file.doc',rev:'1',conflicts:{total_rows:0,rows:[]},
'file.doc','content1',{ revs_info:[{rev:'1',status:'available'}],
previous_revision:'0', revisions:{start:1,ids:['1']}},
error:o.f, 'new file "file.doc".');
success:function(value){ o.jio.put(
o.rev.first = value.revision; {_id:'file.doc',content:'content1'},
o.f(value); {revs:true,revs_info:true,conflicts:true},
function (err,val) {
if (val) {
o.rev.first = val.rev;
val.rev = val.rev?val.rev.split('-')[0]:undefined;
if (val.revs_info) {
if (val.revisions) {
makeRevsAccordingToRevsInfo(
val.revisions,val.revs_info);
} }
}); val.revs_info[0].rev =
o.tick(); val.revs_info[0].rev.split('-')[0];
}
}
o.f(err,val);
}
);
o.tick(o);
o.checkContent('file.doc.'+o.rev.first); o.checkContent('file.doc.'+o.rev.first);
// modify the file // modify the file
o.spy('status','done','modify "file.doc", revision: "'+ o.spy(o,'value',
{ok:true,id:'file.doc',rev:'2',
conflicts:{total_rows:0,rows:[]},
revisions:{start:2,ids:['2',getHashFromRev(o.rev.first)]},
revs_info:[{rev:'2',status:'available'}]},
'modify "file.doc", revision: "'+
o.rev.first+'".'); o.rev.first+'".');
o.jio.saveDocument( o.jio.put(
'file.doc','content2',{ {_id:'file.doc',content:'content2',_rev:o.rev.first},
previous_revision:o.rev.first, {revs:true,revs_info:true,conflicts:true},
error:o.f, function (err,val) {
success:function(v) { if (val) {
o.f(v); o.rev.second = val.rev;
o.rev.second = v.revision; val.rev = val.rev?val.rev.split('-')[0]:undefined;
if (val.revs_info) {
if (val.revisions) {
makeRevsAccordingToRevsInfo(
val.revisions,val.revs_info);
} }
}); val.revs_info[0].rev =
o.tick(); val.revs_info[0].rev.split('-')[0];
}
}
o.f(err,val);
}
);
o.tick(o);
o.checkContent('file.doc.'+o.rev.second); o.checkContent('file.doc.'+o.rev.second);
o.checkNoContent('file.doc.'+o.rev.first); o.checkNoContent('file.doc.'+o.rev.first);
// modify the file from the second revision instead of the third // modify the file from the second revision instead of the third
o.spy('status','conflict','modify "file.doc", revision: "'+ o.test_message = 'modify "file.doc", revision: "'+
o.rev.first+'" -> conflict!'); o.rev.first+'" -> conflict!';
o.jio.saveDocument( o.f = o.t.spy();
'file.doc','content3',{ o.jio.put(
previous_revision:o.rev.first, {_id:'file.doc',content:'content3',_rev:o.rev.first},
success:o.f, {revs:true,revs_info:true,conflicts:true},function (err,val) {
error: function (error) { o.f();
o.conflict_object = error.conflict_object; var k;
o.f(error); if (err) {
o.rev.third = '?'; o.rev.third = err.rev;
if (o.conflict_object) { err.rev = checkRev(err.rev);
o.rev.third = o.conflict_object.revision; if (err.conflicts && err.conflicts.rows) {
ok (!o.conflict_object.revision_object[o.new_rev], o.solveConflict = checkConflictRow (err.conflicts.rows[0]);
}
for (k in {'error':0,'message':0,'reason':0,'statusText':0}) {
if (err[k]) {
delete err[k];
} else {
err[k] = 'ERROR: ' + k + ' is missing !';
}
}
}
deepEqual(err||val,{
rev:o.rev.third,
conflicts:{total_rows:1,rows:[
{id:'file.doc',key:[o.rev.second,o.rev.third],
value:{_solveConflict:'function'}}]},
status:409,
// just one revision in the history, it does not keep older
// revisions because it is not a revision manager storage.
revisions:{start:1,ids:[getHashFromRev(o.rev.third)]},
revs_info:[{rev:o.rev.second,status:'available'},
{rev:o.rev.third,status:'available'}]
},o.test_message);
ok (!revs_infoContains(err.revs_info,o.rev.first),
'check if the first revision is not include to '+ 'check if the first revision is not include to '+
'the conflict list.'); 'the conflict list.');
ok (o.conflict_object.revision_object[ ok (revs_infoContains(err.revs_info,err.rev),
o.conflict_object.revision],
'check if the new revision is include to '+ 'check if the new revision is include to '+
'the conflict list.'); 'the conflict list.');
}
}
}); });
o.tick(); o.tick(o);
o.checkContent ('file.doc.'+o.rev.third); o.checkContent ('file.doc.'+o.rev.third);
// loading test // loading test
o.spy('status','conflict','loading "file.doc" -> conflict!'); o.spy(o,'value',{_id:'file.doc',_rev:o.rev.third,content:'content3'},
o.jio.loadDocument('file.doc',{ 'loading "file.doc" -> conflict!');
success:o.f,error:o.f o.jio.get('file.doc',function (err,val) {
var k;
if (val) {
for (k in {'_creation_date':0,'_last_modified':0}) {
if (val[k]) {
delete val[k];
} else {
val[k] = 'ERROR: ' + k + ' is missing !';
}
}
}
o.f(err,val);
}); });
o.tick(); o.tick(o);
if (!o.conflict_object) { return ok(false,'Cannot to continue the tests'); } if (!o.solveConflict) { return ok(false,'Cannot to continue the tests'); }
// solving conflict // solving conflict
o.spy('status','done','solve conflict "file.doc".'); o.spy(o,'value',{ok:true,id:'file.doc',rev:'3'},
o.conflict_object.solveConflict( 'solve conflict "file.doc".');
'content4',{ o.solveConflict(
error:o.f, 'content4',function (err,val) {
success:function (r) { if (val) {
o.f(r); o.rev.forth = val.rev;
o.rev.forth = r.revision; val.rev = val.rev?val.rev.split('-')[0]:undefined;
} }
o.f(err,val);
}); });
o.tick(); o.tick(o);
o.checkContent('file.doc.'+o.rev.forth); o.checkContent('file.doc.'+o.rev.forth);
o.checkNoContent('file.doc.'+o.rev.second); o.checkNoContent('file.doc.'+o.rev.second);
o.checkNoContent('file.doc.'+o.rev.third); o.checkNoContent('file.doc.'+o.rev.third);
...@@ -1540,33 +1691,9 @@ test ('Revision Conflict', function() { ...@@ -1540,33 +1691,9 @@ test ('Revision Conflict', function() {
test ('Conflict in a conflict solving', function () { test ('Conflict in a conflict solving', function () {
var o = {}; o.clock = this.sandbox.useFakeTimers(); o.t = this; var o = {}; o.clock = this.sandbox.useFakeTimers(); o.t = this;
o.clock.tick (base_tick); o.clock.tick (base_tick);
o.spy = function(res,value,message,function_name) { o.spy = basic_spy_function;
function_name = function_name || 'f'; o.tick = basic_tick_function;
o[function_name] = function(result) {
if (res === 'status') {
if (result && result.conflict_object) {
result = 'conflict';
} else if (result && typeof result.status !== 'undefined') {
result = 'fail';
} else {
result = 'done';
}
}
deepEqual (result,value,message);
};
o.t.spy(o,function_name);
};
o.tick = function (tick, function_name) {
function_name = function_name || 'f'
o.clock.tick(tick || 1000);
if (!o[function_name].calledOnce) {
if (o[function_name].called) {
ok(false, 'too much results');
} else {
ok(false, 'no response');
}
}
};
o.localNamespace = 'jio/local/conflictconflict/jiotests/'; o.localNamespace = 'jio/local/conflictconflict/jiotests/';
o.rev={}; o.rev={};
o.checkContent = function (string,message) { o.checkContent = function (string,message) {
...@@ -1584,73 +1711,165 @@ test ('Conflict in a conflict solving', function () { ...@@ -1584,73 +1711,165 @@ test ('Conflict in a conflict solving', function () {
o.jio = JIO.newJio({type:'conflictmanager', o.jio = JIO.newJio({type:'conflictmanager',
storage:o.secondstorage_spec}); storage:o.secondstorage_spec});
// create a new file // create a new file
o.spy('status','done','new file "file.doc", revision: "0".'); o.test_message = 'new file "file.doc", revision: "0".'
o.jio.saveDocument( o.f = o.t.spy();
'file.doc','content1',{ o.jio.put(
previous_revision:'0', {_id:'file.doc',content:'content1'},
error:o.f, {conflicts:true,revs:true,revs_info:true},
success:function(value){ function(err,val) {
o.rev.first = value.revision; o.f();
o.f(value); if (val) {
} o.rev.first = val.rev;
val.rev = checkRev(val.rev);
}
deepEqual(err||val,{
ok:true,id:'file.doc',rev:o.rev.first,
conflicts:{total_rows:0,rows:[]},
revisions:{start:1,ids:[getHashFromRev(o.rev.first)]},
revs_info:[{rev:o.rev.first,status:'available'}]
},o.test_message);
}); });
o.tick(); o.tick(o);
o.checkContent ('file.doc.'+o.rev.first); o.checkContent ('file.doc.'+o.rev.first);
// modify the file from the second revision instead of the third // modify the file from the second revision instead of the third
o.spy('status','conflict','modify "file.doc", revision: "0" -> conflict!'); o.test_message = 'modify "file.doc", revision: "0" -> conflict!';
o.jio.saveDocument( o.f = o.t.spy();
'file.doc','content2',{ o.jio.put(
previous_revision:"0", {_id:'file.doc',content:'content2'},
success:o.f, {conflicts:true,revs:true,revs_info:true},
error: function (error) { function (err,val) {
o.f(error); o.f();
o.conflict_object = error.conflict_object; var k;
o.rev.second = o.conflict_object?o.conflict_object.revision:'?'; if (err) {
} o.rev.second = err.rev;
err.rev = checkRev(err.rev);
if (err.conflicts && err.conflicts.rows) {
o.solveConflict = checkConflictRow (err.conflicts.rows[0]);
}
for (k in {'error':0,'message':0,'reason':0,'statusText':0}) {
if (err[k]) {
delete err[k];
} else {
err[k] = 'ERROR: ' + k + ' is missing !';
}
}
}
deepEqual(err||val,{
rev:o.rev.second,
conflicts:{total_rows:1,rows:[
{id:'file.doc',key:[o.rev.first,o.rev.second],
value:{_solveConflict:'function'}}]},
status:409,
// just one revision in the history, it does not keep older
// revisions because it is not a revision manager storage.
revisions:{start:1,ids:[getHashFromRev(o.rev.second)]},
revs_info:[{rev:o.rev.first,status:'available'},
{rev:o.rev.second,status:'available'}]
},o.test_message);
}); });
o.tick(); o.tick(o);
o.checkContent ('file.doc.'+o.rev.second); o.checkContent ('file.doc.'+o.rev.second);
if (!o.conflict_object) { return ok(false,'Cannot to continue the tests'); } if (!o.solveConflict) { return ok(false,'Cannot to continue the tests'); }
// saving another time // saving another time
o.spy('status','conflict','modify "file.doc" when solving, revision: "'+ o.test_message = 'modify "file.doc" when solving, revision: "'+
o.rev.first+'" -> conflict!'); o.rev.first+'" -> conflict!';
o.jio.saveDocument('file.doc','content3',{ o.f = o.t.spy();
previous_revision: o.rev.first, o.jio.put(
error:function(e){ {_id:'file.doc',content:'content3',_rev:o.rev.first},
o.f(e); {conflicts:true,revs:true,revs_info:true},
o.rev.third = o.conflict_object?o.conflict_object.revision:'?'; function(err,val){
}, o.f();
success:o.f if (err) {
o.rev.third = err.rev;
err.rev = checkRev(err.rev);
if (err.conflicts && err.conflicts.rows) {
checkConflictRow (err.conflicts.rows[0]);
}
for (k in {'error':0,'message':0,'reason':0,'statusText':0}) {
if (err[k]) {
delete err[k];
} else {
err[k] = 'ERROR: ' + k + ' is missing !';
}
}
}
deepEqual(err||val,{
rev:o.rev.third,
conflicts:{total_rows:1,rows:[
{id:'file.doc',key:[o.rev.second,o.rev.third],
value:{_solveConflict:'function'}}]},
status:409,
// just one revision in the history, it does not keep older
// revisions because it is not a revision manager storage.
revisions:{start:2,ids:[getHashFromRev(o.rev.third),
getHashFromRev(o.rev.first)]},
revs_info:[{rev:o.rev.second,status:'available'},
{rev:o.rev.third,status:'available'}]
},o.test_message);
}); });
o.tick(); o.tick(o);
o.checkContent ('file.doc.'+o.rev.third); o.checkContent ('file.doc.'+o.rev.third);
o.checkNoContent ('file.doc.'+o.rev.first); o.checkNoContent ('file.doc.'+o.rev.first);
// solving first conflict // solving first conflict
o.spy('status','conflict','solving conflict "file.doc" -> conflict!'); o.test_message = 'solving conflict "file.doc" -> conflict!';
o.conflict_object.solveConflict('content4',{ o.f = o.t.spy();
success: o.f, o.solveConflict(
error: function (error) { 'content4',{conflicts:true,revs:true,revs_info:true},
o.rev.forth = '?'; function (err,val) {
if (error.conflict_object) { o.f();
o.conflict_object = error.conflict_object; if (err) {
o.rev.forth = o.conflict_object.revision; o.rev.forth = err.rev;
} err.rev = checkRev(err.rev);
o.f(error); if (err.conflicts && err.conflicts.rows) {
} o.solveConflict = checkConflictRow (err.conflicts.rows[0]);
}) }
o.tick(); for (k in {'error':0,'message':0,'reason':0,'statusText':0}) {
if (err[k]) {
delete err[k];
} else {
err[k] = 'ERROR: ' + k + ' is missing !';
}
}
}
deepEqual(err||val,{
rev:o.rev.forth,
conflicts:{total_rows:1,rows:[
{id:'file.doc',key:[o.rev.third,o.rev.forth],
value:{_solveConflict:'function'}}]},
status:409,
// just one revision in the history, it does not keep older
// revisions because it is not a revision manager storage.
revisions:{start:2,ids:[getHashFromRev(o.rev.forth),
getHashFromRev(o.rev.second)]},
revs_info:[{rev:o.rev.third,status:'available'},
{rev:o.rev.forth,status:'available'}]
},o.test_message);
});
o.tick(o);
o.checkContent ('file.doc.'+o.rev.forth); o.checkContent ('file.doc.'+o.rev.forth);
o.checkNoContent ('file.doc.'+o.rev.second); o.checkNoContent ('file.doc.'+o.rev.second);
if (!o.solveConflict) { return ok(false,'Cannot to continue the tests'); }
// solving last conflict // solving last conflict
o.spy('status','done','solving last conflict "file.doc".'); o.test_message = 'solving last conflict "file.doc".';
o.conflict_object.solveConflict('content5',{ o.f = o.t.spy();
error:o.f, o.solveConflict(
success:function (v) { 'content5',{conflicts:true,revs:true,revs_info:true},
o.f(v); function (err,val) {
o.rev.fith = v.revision; if (val) {
} o.rev.fith = val.rev;
val.rev = checkRev(val.rev);
}
deepEqual(err||val,{
ok:true,id:'file.doc',rev:o.rev.fith,
conflicts:{total_rows:0,rows:[]},
revisions:{start:3,ids:[getHashFromRev(o.rev.fith),
getHashFromRev(o.rev.forth),
getHashFromRev(o.rev.second)]},
revs_info:[{rev:o.rev.fith,status:'available'}]
},o.test_message);
o.f();
}); });
o.tick(); o.tick(o);
o.checkContent ('file.doc.'+o.rev.fith); o.checkContent ('file.doc.'+o.rev.fith);
o.jio.stop(); o.jio.stop();
...@@ -1659,33 +1878,9 @@ test ('Conflict in a conflict solving', function () { ...@@ -1659,33 +1878,9 @@ test ('Conflict in a conflict solving', function () {
test ('Remove revision conflict', function () { test ('Remove revision conflict', function () {
var o = {}; o.clock = this.sandbox.useFakeTimers(); o.t = this; var o = {}; o.clock = this.sandbox.useFakeTimers(); o.t = this;
o.clock.tick (base_tick); o.clock.tick (base_tick);
o.spy = function(res,value,message,function_name) { o.spy = basic_spy_function;
function_name = function_name || 'f'; o.tick = basic_tick_function;
o[function_name] = function(result) {
if (res === 'status') {
if (result && result.conflict_object) {
result = 'conflict';
} else if (result && typeof result.status !== 'undefined') {
result = 'fail';
} else {
result = 'done';
}
}
deepEqual (result,value,message);
};
o.t.spy(o,function_name);
};
o.tick = function (tick, function_name) {
function_name = function_name || 'f'
o.clock.tick(tick || 1000);
if (!o[function_name].calledOnce) {
if (o[function_name].called) {
ok(false, 'too much results');
} else {
ok(false, 'no response');
}
}
};
o.localNamespace = 'jio/local/removeconflict/jiotests/'; o.localNamespace = 'jio/local/removeconflict/jiotests/';
o.rev={}; o.rev={};
o.checkContent = function (string,message) { o.checkContent = function (string,message) {
...@@ -1703,77 +1898,189 @@ test ('Remove revision conflict', function () { ...@@ -1703,77 +1898,189 @@ test ('Remove revision conflict', function () {
o.jio = JIO.newJio({type:'conflictmanager', o.jio = JIO.newJio({type:'conflictmanager',
storage:o.secondstorage_spec}); storage:o.secondstorage_spec});
o.spy('status','done','new file "file.doc", revision: "0".'); o.test_message = 'new file "file.doc", revision: "0".';
o.jio.saveDocument( o.f = o.t.spy();
'file.doc','content1',{ o.jio.put(
previous_revision:'0', {_id:'file.doc',content:'content1'},
error:o.f, {conflicts:true,revs:true,revs_info:true},
success:function(value){ function(err,val) {
o.rev.first = value.revision; o.f();
o.f(value); if (val) {
} o.rev.first = val.rev;
val.rev = checkRev(val.rev);
}
deepEqual(err||val,{
ok:true,id:'file.doc',rev:o.rev.first,
conflicts:{total_rows:0,rows:[]},
revisions:{start:1,ids:[getHashFromRev(o.rev.first)]},
revs_info:[{rev:o.rev.first,status:'available'}]
},o.test_message);
}); });
o.tick(); o.tick(o);
o.checkContent ('file.doc.'+o.rev.first); o.checkContent ('file.doc.'+o.rev.first);
o.spy('status','fail','remove "file.doc", revision: "wrong" -> conflict!'); o.test_message = 'remove "file.doc", revision: "wrong" -> conflict!';
o.jio.removeDocument( o.f = o.t.spy();
'file.doc',{ o.jio.remove(
previous_revision:'wrong', {_id:'file.doc'},
success:o.f, {conflicts:true,revs:true,revs_info:true,rev:'wrong'},
error:function (e) { function (err,val) {
o.f(e); o.f();
} if (err) {
o.rev.second = err.rev;
err.rev = checkRev(err.rev);
if (err.conflicts && err.conflicts.rows) {
o.solveConflict = checkConflictRow (err.conflicts.rows[0]);
}
for (k in {'error':0,'message':0,'reason':0,'statusText':0}) {
if (err[k]) {
delete err[k];
} else {
err[k] = 'ERROR: ' + k + ' is missing !';
}
}
}
deepEqual(err||val,{
rev:o.rev.second,
conflicts:{total_rows:1,rows:[
{id:'file.doc',key:[o.rev.first,o.rev.second],
value:{_solveConflict:'function'}}]},
status:409,
// just one revision in the history, it does not keep older
// revisions because it is not a revision manager storage.
revisions:{start:1,ids:[getHashFromRev(o.rev.second)]},
revs_info:[{rev:o.rev.first,status:'available'},
{rev:o.rev.second,status:'deleted'}]
},o.test_message);
}); });
o.tick(); o.tick(o);
o.spy('status','conflict','new file again "file.doc", revision: "0".'); o.test_message = 'new file again "file.doc".';
o.jio.saveDocument( o.f = o.t.spy();
'file.doc','content2',{ o.jio.put(
previous_revision:'0', {_id:'file.doc',content:'content2'},
success:o.f, {conflicts:true,revs:true,revs_info:true},
error:function (error) { function (err,val) {
o.f(error); o.f();
o.rev.second = error.conflict_object ? if (err) {
error.conflict_object.revision : '?'; o.rev.third = err.rev;
} err.rev = checkRev(err.rev);
if (err.conflicts && err.conflicts.rows) {
o.solveConflict = checkConflictRow (err.conflicts.rows[0]);
}
for (k in {'error':0,'message':0,'reason':0,'statusText':0}) {
if (err[k]) {
delete err[k];
} else {
err[k] = 'ERROR: ' + k + ' is missing !';
}
}
}
deepEqual(err||val,{
rev:o.rev.third,
conflicts:{total_rows:1,rows:[
{id:'file.doc',key:[o.rev.first,o.rev.second,o.rev.third],
value:{_solveConflict:'function'}}]},
status:409,
// just one revision in the history, it does not keep older
// revisions because it is not a revision manager storage.
revisions:{start:1,ids:[getHashFromRev(o.rev.third)]},
revs_info:[{rev:o.rev.first,status:'available'},
{rev:o.rev.second,status:'deleted'},
{rev:o.rev.third,status:'available'}]
},o.test_message);
}); });
o.tick(); o.tick(o);
o.checkContent ('file.doc.'+o.rev.second); o.checkContent ('file.doc.'+o.rev.third);
o.spy('status','conflict','remove "file.doc", revision: "'+o.rev.first+ o.test_message = 'remove "file.doc", revision: "'+o.rev.first+
'" -> conflict!'); '" -> conflict!'
o.jio.removeDocument( o.f = o.t.spy();
'file.doc',{ o.jio.remove(
revision:o.rev.first, {_id:'file.doc'},
success:o.f, {conflicts:true,revs:true,revs_info:true,rev:o.rev.first},
error:function (error) { function (err,val) {
o.conflict_object = error.conflict_object; o.f();
o.f(error); if (err) {
o.rev.third = o.conflict_object?o.conflict_object.revision:'?'; o.rev.forth = err.rev;
} err.rev = checkRev(err.rev);
if (err.conflicts && err.conflicts.rows) {
o.solveConflict = checkConflictRow (err.conflicts.rows[0]);
}
for (k in {'error':0,'message':0,'reason':0,'statusText':0}) {
if (err[k]) {
delete err[k];
} else {
err[k] = 'ERROR: ' + k + ' is missing !';
}
}
}
deepEqual(err||val,{
rev:o.rev.forth,
conflicts:{total_rows:1,rows:[
{id:'file.doc',key:[o.rev.second,o.rev.third,o.rev.forth],
value:{_solveConflict:'function'}}]},
status:409,
// just one revision in the history, it does not keep older
// revisions because it is not a revision manager storage.
revisions:{start:2,ids:[getHashFromRev(o.rev.forth),
getHashFromRev(o.rev.first)]},
revs_info:[{rev:o.rev.second,status:'deleted'},
{rev:o.rev.third,status:'available'},
{rev:o.rev.forth,status:'deleted'}]
},o.test_message);
}); });
o.tick(); o.tick(o);
o.checkNoContent ('file.doc.'+o.rev.first); o.checkNoContent ('file.doc.'+o.rev.first);
o.checkNoContent ('file.doc.'+o.rev.third); o.checkNoContent ('file.doc.'+o.rev.forth);
if (!o.conflict_object) { return ok(false, 'Cannot continue the tests'); } if (!o.solveConflict) { return ok(false, 'Cannot continue the tests'); }
o.spy('status','done','solve "file.doc"'); o.test_message = 'solve "file.doc"';
o.conflict_object.solveConflict({ o.f = o.t.spy();
error:o.f, o.solveConflict({conflicts:true,revs:true,revs_info:true},function(err,val){
success:function (v) { o.f();
o.f(v); if (val) {
o.rev.forth = v.revision; o.rev.fith = val.rev;
} val.rev = checkRev(val.rev);
}
deepEqual(err||val,{
ok:true,id:'file.doc',rev:o.rev.fith,
conflicts:{total_rows:0,rows:[]},
revisions:{start:3,ids:[getHashFromRev(o.rev.fith),
getHashFromRev(o.rev.forth),
getHashFromRev(o.rev.first)]},
revs_info:[{rev:o.rev.fith,status:'deleted'}]
},o.test_message);
}); });
o.tick(); o.tick(o);
o.checkNoContent ('file.doc.'+o.rev.second); o.checkNoContent ('file.doc.'+o.rev.second);
o.checkNoContent ('file.doc.'+o.rev.third);
o.checkNoContent ('file.doc.'+o.rev.forth); o.checkNoContent ('file.doc.'+o.rev.forth);
o.checkNoContent ('file.doc.'+o.rev.fith);
o.jio.stop(); o.jio.stop();
}); });
test ('Load Revisions', function () {
var o = {}; o.clock = this.sandbox.useFakeTimers(); o.t = this;
o.clock.tick (base_tick);
o.spy = basic_spy_function;
o.tick = basic_tick_function;
o.secondstorage_spec = {type:'local',
username:'loadrevisions',
applicationname:'jiotests'}
//////////////////////////////////////////////////////////////////////
o.jio = JIO.newJio({type:'conflictmanager',
storage:o.secondstorage_spec});
o.spy(o,'status',404,'load file rev:1,','f'); // 12 === Replaced
o.spy(o,'status',404,'load file rev:2','g');
o.spy(o,'status',404,'and load file rev:3 at the same time','h');
o.jio.get('file',{rev:'1'},o.f);
o.jio.get('file',{rev:'2'},o.g);
o.jio.get('file',{rev:'3'},o.h);
o.tick(o,1000,'f'); o.tick(o,0,'g'); o.tick(o,0,'h');
o.jio.stop();
});
}; // end thisfun }; // end thisfun
if (window.requirejs) { if (window.requirejs) {
......
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