Commit e9edfa2d authored by Aurel's avatar Aurel

Merge remote-tracking branch 'romain/wip' into nodejs-romain

Conflicts:
	src/jio.storage/erp5storage.js
parents bf8de85a 241cd969
...@@ -12527,35 +12527,30 @@ return new Parser; ...@@ -12527,35 +12527,30 @@ return new Parser;
return tx; return tx;
} }
function handleCursor(request, callback) { function handleCursor(request, callback, resolve, reject) {
function resolver(resolve, reject) { request.onerror = function (error) {
// Open DB // if (request.transaction) {
request.onerror = function (error) { request.transaction.abort();
if (request.transaction) { }
request.transaction.abort(); reject(error);
} };
reject(error);
};
request.onsuccess = function (evt) {
var cursor = evt.target.result;
if (cursor) {
// XXX Wait for result
try {
callback(cursor);
} catch (error) {
reject(error);
}
// continue to next iteration request.onsuccess = function (evt) {
cursor["continue"](); var cursor = evt.target.result;
} else { if (cursor) {
resolve(); // XXX Wait for result
try {
callback(cursor);
} catch (error) {
reject(error);
} }
};
} // continue to next iteration
// XXX Canceller??? cursor["continue"]();
return new RSVP.Promise(resolver); } else {
resolve();
}
};
} }
IndexedDBStorage.prototype.buildQuery = function (options) { IndexedDBStorage.prototype.buildQuery = function (options) {
...@@ -12577,40 +12572,45 @@ return new Parser; ...@@ -12577,40 +12572,45 @@ return new Parser;
} }
return openIndexedDB(this) return openIndexedDB(this)
.push(function (db) { .push(function (db) {
var tx = openTransaction(db, ["metadata"], "readonly"); return new RSVP.Promise(function (resolve, reject) {
if (options.include_docs === true) { var tx = openTransaction(db, ["metadata"], "readonly");
return handleCursor(tx.objectStore("metadata").index("_id") if (options.include_docs === true) {
.openCursor(), pushIncludedMetadata); handleCursor(tx.objectStore("metadata").index("_id").openCursor(),
} pushIncludedMetadata, resolve, reject);
return handleCursor(tx.objectStore("metadata").index("_id") } else {
.openKeyCursor(), pushMetadata); handleCursor(tx.objectStore("metadata").index("_id")
.openKeyCursor(), pushMetadata, resolve, reject);
}
});
}) })
.push(function () { .push(function () {
return result_list; return result_list;
}); });
}; };
function handleGet(request) { function handleGet(request, resolve, reject) {
function resolver(resolve, reject) { request.onerror = reject;
request.onerror = reject; request.onsuccess = function () {
request.onsuccess = function () { if (request.result) {
if (request.result) { resolve(request.result);
resolve(request.result); } else {
}
// XXX How to get ID // XXX How to get ID
reject(new jIO.util.jIOError("Cannot find document", 404)); reject(new jIO.util.jIOError("Cannot find document", 404));
}; }
} };
return new RSVP.Promise(resolver);
} }
IndexedDBStorage.prototype.get = function (id) { IndexedDBStorage.prototype.get = function (id) {
return openIndexedDB(this) return openIndexedDB(this)
.push(function (db) { .push(function (db) {
var transaction = openTransaction(db, ["metadata"], return new RSVP.Promise(function (resolve, reject) {
"readonly"); var transaction = openTransaction(db, ["metadata"], "readonly");
return handleGet(transaction.objectStore("metadata").get(id)); handleGet(
transaction.objectStore("metadata").get(id),
resolve,
reject
);
});
}) })
.push(function (result) { .push(function (result) {
return result.doc; return result.doc;
...@@ -12626,37 +12626,51 @@ return new Parser; ...@@ -12626,37 +12626,51 @@ return new Parser;
return openIndexedDB(this) return openIndexedDB(this)
.push(function (db) { .push(function (db) {
var transaction = openTransaction(db, ["metadata", "attachment"], return new RSVP.Promise(function (resolve, reject) {
"readonly"); var transaction = openTransaction(db, ["metadata", "attachment"],
return RSVP.all([ "readonly");
handleGet(transaction.objectStore("metadata").get(id)), function getAttachments() {
handleCursor(transaction.objectStore("attachment").index("_id") handleCursor(
.openCursor(IDBKeyRange.only(id)), addEntry) transaction.objectStore("attachment").index("_id")
]); .openCursor(IDBKeyRange.only(id)),
addEntry,
resolve,
reject
);
}
handleGet(
transaction.objectStore("metadata").get(id),
getAttachments,
reject
);
});
}) })
.push(function () { .push(function () {
return attachment_dict; return attachment_dict;
}); });
}; };
function handleRequest(request) { function handleRequest(request, resolve, reject) {
function resolver(resolve, reject) { request.onerror = reject;
request.onerror = reject; request.onsuccess = function () {
request.onsuccess = function () { resolve(request.result);
resolve(request.result); };
};
}
return new RSVP.Promise(resolver);
} }
IndexedDBStorage.prototype.put = function (id, metadata) { IndexedDBStorage.prototype.put = function (id, metadata) {
return openIndexedDB(this) return openIndexedDB(this)
.push(function (db) { .push(function (db) {
var transaction = openTransaction(db, ["metadata"], "readwrite"); return new RSVP.Promise(function (resolve, reject) {
return handleRequest(transaction.objectStore("metadata").put({ var transaction = openTransaction(db, ["metadata"], "readwrite");
"_id": id, handleRequest(
"doc": metadata transaction.objectStore("metadata").put({
})); "_id": id,
"doc": metadata
}),
resolve,
reject
);
});
}); });
}; };
...@@ -12665,19 +12679,38 @@ return new Parser; ...@@ -12665,19 +12679,38 @@ return new Parser;
} }
IndexedDBStorage.prototype.remove = function (id) { IndexedDBStorage.prototype.remove = function (id) {
var resolved_amount = 0;
return openIndexedDB(this) return openIndexedDB(this)
.push(function (db) { .push(function (db) {
var transaction = openTransaction(db, ["metadata", "attachment", return new RSVP.Promise(function (resolve, reject) {
"blob"], "readwrite"); function resolver() {
return RSVP.all([ if (resolved_amount < 2) {
handleRequest(transaction resolved_amount += 1;
.objectStore("metadata")["delete"](id)), } else {
resolve();
}
}
var transaction = openTransaction(db, ["metadata", "attachment",
"blob"], "readwrite");
handleRequest(
transaction.objectStore("metadata")["delete"](id),
resolver,
reject
);
// XXX Why not possible to delete with KeyCursor? // XXX Why not possible to delete with KeyCursor?
handleCursor(transaction.objectStore("attachment").index("_id") handleCursor(transaction.objectStore("attachment").index("_id")
.openCursor(IDBKeyRange.only(id)), deleteEntry), .openCursor(IDBKeyRange.only(id)),
deleteEntry,
resolver,
reject
);
handleCursor(transaction.objectStore("blob").index("_id") handleCursor(transaction.objectStore("blob").index("_id")
.openCursor(IDBKeyRange.only(id)), deleteEntry) .openCursor(IDBKeyRange.only(id)),
]); deleteEntry,
resolver,
reject
);
});
}); });
}; };
...@@ -12691,48 +12724,64 @@ return new Parser; ...@@ -12691,48 +12724,64 @@ return new Parser;
} }
return openIndexedDB(this) return openIndexedDB(this)
.push(function (db) { .push(function (db) {
transaction = openTransaction(db, ["attachment", "blob"], "readonly"); return new RSVP.Promise(function (resolve, reject) {
// XXX Should raise if key is not good transaction = openTransaction(
return handleGet(transaction.objectStore("attachment") db,
.get(buildKeyPath([id, name]))); ["attachment", "blob"],
}) "readonly"
.push(function (attachment) {
var total_length = attachment.info.length,
i,
promise_list = [],
store = transaction.objectStore("blob"),
start_index,
end_index;
type = attachment.info.content_type;
start = options.start || 0;
end = options.end || total_length;
if (end > total_length) {
end = total_length;
}
if (start < 0 || end < 0) {
throw new jIO.util.jIOError("_start and _end must be positive",
400);
}
if (start > end) {
throw new jIO.util.jIOError("_start is greater than _end",
400);
}
start_index = Math.floor(start / UNITE);
end_index = Math.floor(end / UNITE);
if (end % UNITE === 0) {
end_index -= 1;
}
for (i = start_index; i <= end_index; i += 1) {
promise_list.push(
handleGet(store.get(buildKeyPath([id,
name, i])))
); );
} function getBlob(attachment) {
return RSVP.all(promise_list); var total_length = attachment.info.length,
result_list = [],
store = transaction.objectStore("blob"),
start_index,
end_index;
type = attachment.info.content_type;
start = options.start || 0;
end = options.end || total_length;
if (end > total_length) {
end = total_length;
}
if (start < 0 || end < 0) {
throw new jIO.util.jIOError(
"_start and _end must be positive",
400
);
}
if (start > end) {
throw new jIO.util.jIOError("_start is greater than _end",
400);
}
start_index = Math.floor(start / UNITE);
end_index = Math.floor(end / UNITE) - 1;
if (end % UNITE === 0) {
end_index -= 1;
}
function resolver(result) {
result_list.push(result);
resolve(result_list);
}
function getPart(i) {
return function (result) {
if (result) {
result_list.push(result);
}
i += 1;
handleGet(store.get(buildKeyPath([id, name, i])),
(i <= end_index) ? getPart(i) : resolver,
reject
);
};
}
getPart(start_index - 1)();
}
// XXX Should raise if key is not good
handleGet(transaction.objectStore("attachment")
.get(buildKeyPath([id, name])),
getBlob,
reject
);
});
}) })
.push(function (result_list) { .push(function (result_list) {
var array_buffer_list = [], var array_buffer_list = [],
...@@ -12753,19 +12802,24 @@ return new Parser; ...@@ -12753,19 +12802,24 @@ return new Parser;
}); });
}; };
function removeAttachment(transaction, id, name) { function removeAttachment(transaction, id, name, resolve, reject) {
return RSVP.all([
// XXX How to get the right attachment // XXX How to get the right attachment
handleRequest(transaction.objectStore("attachment")["delete"]( function deleteContent() {
handleCursor(
transaction.objectStore("blob").index("_id_attachment")
.openCursor(IDBKeyRange.only([id, name])),
deleteEntry,
resolve,
reject
);
}
handleRequest(
transaction.objectStore("attachment")["delete"](
buildKeyPath([id, name]) buildKeyPath([id, name])
)), ),
handleCursor(transaction.objectStore("blob").index("_id_attachment") deleteContent,
.openCursor(IDBKeyRange.only( reject
[id, name] );
)),
deleteEntry
)
]);
} }
IndexedDBStorage.prototype.putAttachment = function (id, name, blob) { IndexedDBStorage.prototype.putAttachment = function (id, name, blob) {
...@@ -12793,38 +12847,43 @@ return new Parser; ...@@ -12793,38 +12847,43 @@ return new Parser;
// Remove previous attachment // Remove previous attachment
transaction = openTransaction(db, ["attachment", "blob"], "readwrite"); transaction = openTransaction(db, ["attachment", "blob"], "readwrite");
return removeAttachment(transaction, id, name); return new RSVP.Promise(function (resolve, reject) {
}) function write() {
.push(function () { var len = blob_part.length - 1,
attachment_store = transaction.objectStore("attachment"),
var promise_list = [ blob_store = transaction.objectStore("blob");
handleRequest(transaction.objectStore("attachment").put({ function putBlobPart(i) {
"_key_path": buildKeyPath([id, name]), return function () {
"_id": id, i += 1;
"_attachment": name, handleRequest(
"info": { blob_store.put({
"content_type": blob.type, "_key_path": buildKeyPath([id, name, i]),
"length": blob.size "_id" : id,
} "_attachment" : name,
})) "_part" : i,
], "blob": blob_part[i]
len = blob_part.length, }),
blob_store = transaction.objectStore("blob"), (i < len) ? putBlobPart(i) : resolve,
i; reject
for (i = 0; i < len; i += 1) { );
promise_list.push( };
handleRequest(blob_store.put({ }
"_key_path": buildKeyPath([id, name, handleRequest(
i]), attachment_store.put({
"_id" : id, "_key_path": buildKeyPath([id, name]),
"_attachment" : name, "_id": id,
"_part" : i, "_attachment": name,
"blob": blob_part[i] "info": {
})) "content_type": blob.type,
); "length": blob.size
} }
// Store all new data }),
return RSVP.all(promise_list); putBlobPart(-1),
reject
);
}
removeAttachment(transaction, id, name, write, reject);
});
}); });
}; };
...@@ -12833,7 +12892,9 @@ return new Parser; ...@@ -12833,7 +12892,9 @@ return new Parser;
.push(function (db) { .push(function (db) {
var transaction = openTransaction(db, ["attachment", "blob"], var transaction = openTransaction(db, ["attachment", "blob"],
"readwrite"); "readwrite");
return removeAttachment(transaction, id, name); return new RSVP.Promise(function (resolve, reject) {
removeAttachment(transaction, id, name, resolve, reject);
});
}); });
}; };
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
{ {
"name": "jio", "name": "jio",
"version": "v3.16.0", "version": "v3.17.0",
"license": "LGPLv3", "license": "LGPLv3",
"author": "Nexedi SA", "author": "Nexedi SA",
"contributors": [ "contributors": [
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
"cloud" "cloud"
], ],
"dependencies": { "dependencies": {
"rsvp": "git+https://lab.nexedi.com/nexedi/rsvp.js.git", "rsvp": "git+https://lab.nexedi.com/romain/rsvp.js.git#wip",
"uritemplate": "git+https://lab.nexedi.com/nexedi/uritemplate-js.git", "uritemplate": "git+https://lab.nexedi.com/nexedi/uritemplate-js.git",
"moment": "2.13.0", "moment": "2.13.0",
"rusha": "0.8.2", "rusha": "0.8.2",
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
"sinon": "~2.1.0" "sinon": "~2.1.0"
}, },
"devDependencies": { "devDependencies": {
"renderjs": "git+https://lab.nexedi.com/nexedi/renderjs.git", "renderjs": "git+https://lab.nexedi.com/romain/renderjs.git#wip",
"grunt": "0.4.x", "grunt": "0.4.x",
"grunt-cli": "~0.1.11", "grunt-cli": "~0.1.11",
"grunt-contrib-concat": "0.3.x", "grunt-contrib-concat": "0.3.x",
......
...@@ -105,12 +105,12 @@ ...@@ -105,12 +105,12 @@
}) })
.push(function (dataURL) { .push(function (dataURL) {
//string->arraybuffer //string->arraybuffer
var strLen = dataURL.currentTarget.result.length, var strLen = dataURL.target.result.length,
buf = new ArrayBuffer(strLen), buf = new ArrayBuffer(strLen),
bufView = new Uint8Array(buf), bufView = new Uint8Array(buf),
i; i;
dataURL = dataURL.currentTarget.result; dataURL = dataURL.target.result;
for (i = 0; i < strLen; i += 1) { for (i = 0; i < strLen; i += 1) {
bufView[i] = dataURL.charCodeAt(i); bufView[i] = dataURL.charCodeAt(i);
} }
...@@ -147,7 +147,7 @@ ...@@ -147,7 +147,7 @@
.push(function (coded) { .push(function (coded) {
var initializaton_vector; var initializaton_vector;
coded = coded.currentTarget.result; coded = coded.target.result;
initializaton_vector = new Uint8Array(coded.slice(0, 12)); initializaton_vector = new Uint8Array(coded.slice(0, 12));
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function () {
......
...@@ -171,57 +171,6 @@ ...@@ -171,57 +171,6 @@
}); });
}; };
ERP5Storage.prototype.bulk = function (request_list) {
var i,
storage = this,
bulk_list = [];
for (i = 0; i < request_list.length; i += 1) {
if (request_list[i].method !== "get") {
throw new Error("ERP5Storage: not supported " +
request_list[i].method + " in bulk");
}
bulk_list.push({
relative_url: request_list[i].parameter_list[0],
view: storage._default_view_reference
});
}
return getSiteDocument(storage)
.push(function (site_hal) {
var form_data = new FormData();
form_data.append("bulk_list", JSON.stringify(bulk_list));
return jIO.util.ajax({
"type": "POST",
"url": site_hal._actions.bulk.href,
"data": form_data,
// "headers": {
// "Content-Type": "application/json"
// },
"xhrFields": {
withCredentials: storage._thisCredentials
},
"headers": storage._headers
});
})
.push(function (response) {
var result_list = [],
hateoas = JSON.parse(response.target.responseText);
function pushResult(json) {
return extractPropertyFromFormJSON(json)
.push(function (json2) {
return convertJSONToGet(json2);
});
}
for (i = 0; i < hateoas.result_list.length; i += 1) {
result_list.push(pushResult(hateoas.result_list[i]));
}
return RSVP.all(result_list);
});
};
ERP5Storage.prototype.post = function (data) { ERP5Storage.prototype.post = function (data) {
var storage = this, var storage = this,
new_id; new_id;
...@@ -457,7 +406,7 @@ ...@@ -457,7 +406,7 @@
ERP5Storage.prototype.hasCapacity = function (name) { ERP5Storage.prototype.hasCapacity = function (name) {
return ((name === "list") || (name === "query") || return ((name === "list") || (name === "query") ||
(name === "select") || (name === "limit") || (name === "select") || (name === "limit") ||
(name === "sort")) || (name === "bulk_get"); (name === "sort"));
}; };
function isSingleLocalRoles(parsed_query) { function isSingleLocalRoles(parsed_query) {
......
...@@ -148,35 +148,30 @@ ...@@ -148,35 +148,30 @@
return tx; return tx;
} }
function handleCursor(request, callback) { function handleCursor(request, callback, resolve, reject) {
function resolver(resolve, reject) { request.onerror = function (error) {
// Open DB // if (request.transaction) {
request.onerror = function (error) { request.transaction.abort();
if (request.transaction) { }
request.transaction.abort(); reject(error);
} };
reject(error);
};
request.onsuccess = function (evt) {
var cursor = evt.target.result;
if (cursor) {
// XXX Wait for result
try {
callback(cursor);
} catch (error) {
reject(error);
}
// continue to next iteration request.onsuccess = function (evt) {
cursor["continue"](); var cursor = evt.target.result;
} else { if (cursor) {
resolve(); // XXX Wait for result
try {
callback(cursor);
} catch (error) {
reject(error);
} }
};
} // continue to next iteration
// XXX Canceller??? cursor["continue"]();
return new RSVP.Promise(resolver); } else {
resolve();
}
};
} }
IndexedDBStorage.prototype.buildQuery = function (options) { IndexedDBStorage.prototype.buildQuery = function (options) {
...@@ -198,40 +193,45 @@ ...@@ -198,40 +193,45 @@
} }
return openIndexedDB(this) return openIndexedDB(this)
.push(function (db) { .push(function (db) {
var tx = openTransaction(db, ["metadata"], "readonly"); return new RSVP.Promise(function (resolve, reject) {
if (options.include_docs === true) { var tx = openTransaction(db, ["metadata"], "readonly");
return handleCursor(tx.objectStore("metadata").index("_id") if (options.include_docs === true) {
.openCursor(), pushIncludedMetadata); handleCursor(tx.objectStore("metadata").index("_id").openCursor(),
} pushIncludedMetadata, resolve, reject);
return handleCursor(tx.objectStore("metadata").index("_id") } else {
.openKeyCursor(), pushMetadata); handleCursor(tx.objectStore("metadata").index("_id")
.openKeyCursor(), pushMetadata, resolve, reject);
}
});
}) })
.push(function () { .push(function () {
return result_list; return result_list;
}); });
}; };
function handleGet(request) { function handleGet(request, resolve, reject) {
function resolver(resolve, reject) { request.onerror = reject;
request.onerror = reject; request.onsuccess = function () {
request.onsuccess = function () { if (request.result) {
if (request.result) { resolve(request.result);
resolve(request.result); } else {
}
// XXX How to get ID // XXX How to get ID
reject(new jIO.util.jIOError("Cannot find document", 404)); reject(new jIO.util.jIOError("Cannot find document", 404));
}; }
} };
return new RSVP.Promise(resolver);
} }
IndexedDBStorage.prototype.get = function (id) { IndexedDBStorage.prototype.get = function (id) {
return openIndexedDB(this) return openIndexedDB(this)
.push(function (db) { .push(function (db) {
var transaction = openTransaction(db, ["metadata"], return new RSVP.Promise(function (resolve, reject) {
"readonly"); var transaction = openTransaction(db, ["metadata"], "readonly");
return handleGet(transaction.objectStore("metadata").get(id)); handleGet(
transaction.objectStore("metadata").get(id),
resolve,
reject
);
});
}) })
.push(function (result) { .push(function (result) {
return result.doc; return result.doc;
...@@ -247,37 +247,51 @@ ...@@ -247,37 +247,51 @@
return openIndexedDB(this) return openIndexedDB(this)
.push(function (db) { .push(function (db) {
var transaction = openTransaction(db, ["metadata", "attachment"], return new RSVP.Promise(function (resolve, reject) {
"readonly"); var transaction = openTransaction(db, ["metadata", "attachment"],
return RSVP.all([ "readonly");
handleGet(transaction.objectStore("metadata").get(id)), function getAttachments() {
handleCursor(transaction.objectStore("attachment").index("_id") handleCursor(
.openCursor(IDBKeyRange.only(id)), addEntry) transaction.objectStore("attachment").index("_id")
]); .openCursor(IDBKeyRange.only(id)),
addEntry,
resolve,
reject
);
}
handleGet(
transaction.objectStore("metadata").get(id),
getAttachments,
reject
);
});
}) })
.push(function () { .push(function () {
return attachment_dict; return attachment_dict;
}); });
}; };
function handleRequest(request) { function handleRequest(request, resolve, reject) {
function resolver(resolve, reject) { request.onerror = reject;
request.onerror = reject; request.onsuccess = function () {
request.onsuccess = function () { resolve(request.result);
resolve(request.result); };
};
}
return new RSVP.Promise(resolver);
} }
IndexedDBStorage.prototype.put = function (id, metadata) { IndexedDBStorage.prototype.put = function (id, metadata) {
return openIndexedDB(this) return openIndexedDB(this)
.push(function (db) { .push(function (db) {
var transaction = openTransaction(db, ["metadata"], "readwrite"); return new RSVP.Promise(function (resolve, reject) {
return handleRequest(transaction.objectStore("metadata").put({ var transaction = openTransaction(db, ["metadata"], "readwrite");
"_id": id, handleRequest(
"doc": metadata transaction.objectStore("metadata").put({
})); "_id": id,
"doc": metadata
}),
resolve,
reject
);
});
}); });
}; };
...@@ -286,19 +300,38 @@ ...@@ -286,19 +300,38 @@
} }
IndexedDBStorage.prototype.remove = function (id) { IndexedDBStorage.prototype.remove = function (id) {
var resolved_amount = 0;
return openIndexedDB(this) return openIndexedDB(this)
.push(function (db) { .push(function (db) {
var transaction = openTransaction(db, ["metadata", "attachment", return new RSVP.Promise(function (resolve, reject) {
"blob"], "readwrite"); function resolver() {
return RSVP.all([ if (resolved_amount < 2) {
handleRequest(transaction resolved_amount += 1;
.objectStore("metadata")["delete"](id)), } else {
resolve();
}
}
var transaction = openTransaction(db, ["metadata", "attachment",
"blob"], "readwrite");
handleRequest(
transaction.objectStore("metadata")["delete"](id),
resolver,
reject
);
// XXX Why not possible to delete with KeyCursor? // XXX Why not possible to delete with KeyCursor?
handleCursor(transaction.objectStore("attachment").index("_id") handleCursor(transaction.objectStore("attachment").index("_id")
.openCursor(IDBKeyRange.only(id)), deleteEntry), .openCursor(IDBKeyRange.only(id)),
deleteEntry,
resolver,
reject
);
handleCursor(transaction.objectStore("blob").index("_id") handleCursor(transaction.objectStore("blob").index("_id")
.openCursor(IDBKeyRange.only(id)), deleteEntry) .openCursor(IDBKeyRange.only(id)),
]); deleteEntry,
resolver,
reject
);
});
}); });
}; };
...@@ -312,48 +345,64 @@ ...@@ -312,48 +345,64 @@
} }
return openIndexedDB(this) return openIndexedDB(this)
.push(function (db) { .push(function (db) {
transaction = openTransaction(db, ["attachment", "blob"], "readonly"); return new RSVP.Promise(function (resolve, reject) {
// XXX Should raise if key is not good transaction = openTransaction(
return handleGet(transaction.objectStore("attachment") db,
.get(buildKeyPath([id, name]))); ["attachment", "blob"],
}) "readonly"
.push(function (attachment) {
var total_length = attachment.info.length,
i,
promise_list = [],
store = transaction.objectStore("blob"),
start_index,
end_index;
type = attachment.info.content_type;
start = options.start || 0;
end = options.end || total_length;
if (end > total_length) {
end = total_length;
}
if (start < 0 || end < 0) {
throw new jIO.util.jIOError("_start and _end must be positive",
400);
}
if (start > end) {
throw new jIO.util.jIOError("_start is greater than _end",
400);
}
start_index = Math.floor(start / UNITE);
end_index = Math.floor(end / UNITE);
if (end % UNITE === 0) {
end_index -= 1;
}
for (i = start_index; i <= end_index; i += 1) {
promise_list.push(
handleGet(store.get(buildKeyPath([id,
name, i])))
); );
} function getBlob(attachment) {
return RSVP.all(promise_list); var total_length = attachment.info.length,
result_list = [],
store = transaction.objectStore("blob"),
start_index,
end_index;
type = attachment.info.content_type;
start = options.start || 0;
end = options.end || total_length;
if (end > total_length) {
end = total_length;
}
if (start < 0 || end < 0) {
throw new jIO.util.jIOError(
"_start and _end must be positive",
400
);
}
if (start > end) {
throw new jIO.util.jIOError("_start is greater than _end",
400);
}
start_index = Math.floor(start / UNITE);
end_index = Math.floor(end / UNITE) - 1;
if (end % UNITE === 0) {
end_index -= 1;
}
function resolver(result) {
result_list.push(result);
resolve(result_list);
}
function getPart(i) {
return function (result) {
if (result) {
result_list.push(result);
}
i += 1;
handleGet(store.get(buildKeyPath([id, name, i])),
(i <= end_index) ? getPart(i) : resolver,
reject
);
};
}
getPart(start_index - 1)();
}
// XXX Should raise if key is not good
handleGet(transaction.objectStore("attachment")
.get(buildKeyPath([id, name])),
getBlob,
reject
);
});
}) })
.push(function (result_list) { .push(function (result_list) {
var array_buffer_list = [], var array_buffer_list = [],
...@@ -374,19 +423,24 @@ ...@@ -374,19 +423,24 @@
}); });
}; };
function removeAttachment(transaction, id, name) { function removeAttachment(transaction, id, name, resolve, reject) {
return RSVP.all([
// XXX How to get the right attachment // XXX How to get the right attachment
handleRequest(transaction.objectStore("attachment")["delete"]( function deleteContent() {
handleCursor(
transaction.objectStore("blob").index("_id_attachment")
.openCursor(IDBKeyRange.only([id, name])),
deleteEntry,
resolve,
reject
);
}
handleRequest(
transaction.objectStore("attachment")["delete"](
buildKeyPath([id, name]) buildKeyPath([id, name])
)), ),
handleCursor(transaction.objectStore("blob").index("_id_attachment") deleteContent,
.openCursor(IDBKeyRange.only( reject
[id, name] );
)),
deleteEntry
)
]);
} }
IndexedDBStorage.prototype.putAttachment = function (id, name, blob) { IndexedDBStorage.prototype.putAttachment = function (id, name, blob) {
...@@ -414,38 +468,43 @@ ...@@ -414,38 +468,43 @@
// Remove previous attachment // Remove previous attachment
transaction = openTransaction(db, ["attachment", "blob"], "readwrite"); transaction = openTransaction(db, ["attachment", "blob"], "readwrite");
return removeAttachment(transaction, id, name); return new RSVP.Promise(function (resolve, reject) {
}) function write() {
.push(function () { var len = blob_part.length - 1,
attachment_store = transaction.objectStore("attachment"),
var promise_list = [ blob_store = transaction.objectStore("blob");
handleRequest(transaction.objectStore("attachment").put({ function putBlobPart(i) {
"_key_path": buildKeyPath([id, name]), return function () {
"_id": id, i += 1;
"_attachment": name, handleRequest(
"info": { blob_store.put({
"content_type": blob.type, "_key_path": buildKeyPath([id, name, i]),
"length": blob.size "_id" : id,
} "_attachment" : name,
})) "_part" : i,
], "blob": blob_part[i]
len = blob_part.length, }),
blob_store = transaction.objectStore("blob"), (i < len) ? putBlobPart(i) : resolve,
i; reject
for (i = 0; i < len; i += 1) { );
promise_list.push( };
handleRequest(blob_store.put({ }
"_key_path": buildKeyPath([id, name, handleRequest(
i]), attachment_store.put({
"_id" : id, "_key_path": buildKeyPath([id, name]),
"_attachment" : name, "_id": id,
"_part" : i, "_attachment": name,
"blob": blob_part[i] "info": {
})) "content_type": blob.type,
); "length": blob.size
} }
// Store all new data }),
return RSVP.all(promise_list); putBlobPart(-1),
reject
);
}
removeAttachment(transaction, id, name, write, reject);
});
}); });
}; };
...@@ -454,7 +513,9 @@ ...@@ -454,7 +513,9 @@
.push(function (db) { .push(function (db) {
var transaction = openTransaction(db, ["attachment", "blob"], var transaction = openTransaction(db, ["attachment", "blob"],
"readwrite"); "readwrite");
return removeAttachment(transaction, id, name); return new RSVP.Promise(function (resolve, reject) {
removeAttachment(transaction, id, name, resolve, reject);
});
}); });
}; };
......
...@@ -28,6 +28,15 @@ ...@@ -28,6 +28,15 @@
CONFLICT_KEEP_REMOTE = 2, CONFLICT_KEEP_REMOTE = 2,
CONFLICT_CONTINUE = 3; CONFLICT_CONTINUE = 3;
function SkipError(message) {
if ((message !== undefined) && (typeof message !== "string")) {
throw new TypeError('You must pass a string.');
}
this.message = message || "Skip some asynchronous code";
}
SkipError.prototype = new Error();
SkipError.prototype.constructor = SkipError;
/**************************************************** /****************************************************
Use a local jIO to read/write/search documents Use a local jIO to read/write/search documents
Synchronize in background those document with a remote jIO. Synchronize in background those document with a remote jIO.
...@@ -46,20 +55,33 @@ ...@@ -46,20 +55,33 @@
function ReplicateStorage(spec) { function ReplicateStorage(spec) {
this._query_options = spec.query || {}; this._query_options = spec.query || {};
if (spec.signature_select_metadata !== undefined) {
this._query_options.select_list = [spec.signature_select_metadata];
}
this._signature_select_metadata = spec.signature_select_metadata;
this._local_sub_storage = jIO.createJIO(spec.local_sub_storage); this._local_sub_storage = jIO.createJIO(spec.local_sub_storage);
this._remote_sub_storage = jIO.createJIO(spec.remote_sub_storage); this._remote_sub_storage = jIO.createJIO(spec.remote_sub_storage);
this._signature_hash = "_replicate_" + generateHash( if (spec.hasOwnProperty('signature_sub_storage')) {
stringify(spec.local_sub_storage) + this._signature_sub_storage = jIO.createJIO(spec.signature_sub_storage);
stringify(spec.remote_sub_storage) + this._custom_signature_sub_storage = true;
stringify(this._query_options) } else {
); this._signature_hash = "_replicate_" + generateHash(
this._signature_sub_storage = jIO.createJIO({ stringify(spec.local_sub_storage) +
type: "document", stringify(spec.remote_sub_storage) +
document_id: this._signature_hash, stringify(this._query_options)
sub_storage: spec.signature_storage || spec.local_sub_storage );
}); this._signature_sub_storage = jIO.createJIO({
type: "query",
sub_storage: {
type: "document",
document_id: this._signature_hash,
sub_storage: spec.local_sub_storage
}
});
this._custom_signature_sub_storage = false;
}
this._use_remote_post = spec.use_remote_post || false; this._use_remote_post = spec.use_remote_post || false;
// Number of request we allow browser execution for attachments // Number of request we allow browser execution for attachments
...@@ -202,798 +224,867 @@ ...@@ -202,798 +224,867 @@
arguments); arguments);
}; };
ReplicateStorage.prototype.repair = function () { function dispatchQueue(context, function_used, argument_list,
var context = this, number_queue) {
argument_list = arguments, var result_promise_list = [],
skip_document_dict = {}; i;
// Do not sync the signature document
skip_document_dict[context._signature_hash] = null;
function dispatchQueue(function_used, argument_list, number_queue) {
var result_promise_list = [],
i;
function pushAndExecute(queue) { function pushAndExecute(queue) {
queue queue
.push(function () {
if (argument_list.length > 0) {
var argument_array = argument_list.shift(),
sub_queue = new RSVP.Queue();
argument_array[0] = sub_queue;
function_used.apply(context, argument_array);
pushAndExecute(queue);
return sub_queue;
}
});
}
for (i = 0; i < number_queue; i += 1) {
result_promise_list.push(new RSVP.Queue());
pushAndExecute(result_promise_list[i]);
}
if (number_queue > 1) {
return RSVP.all(result_promise_list);
}
return result_promise_list[0];
}
function propagateAttachmentDeletion(skip_attachment_dict,
destination,
id, name) {
return destination.removeAttachment(id, name)
.push(function () {
return context._signature_sub_storage.removeAttachment(id, name);
})
.push(function () { .push(function () {
skip_attachment_dict[name] = null; if (argument_list.length > 0) {
var argument_array = argument_list.shift(),
sub_queue = new RSVP.Queue();
argument_array[0] = sub_queue;
function_used.apply(context, argument_array);
pushAndExecute(queue);
return sub_queue;
}
}); });
} }
for (i = 0; i < number_queue; i += 1) {
function propagateAttachmentModification(skip_attachment_dict, result_promise_list.push(new RSVP.Queue());
destination, pushAndExecute(result_promise_list[i]);
blob, hash, id, name) {
return destination.putAttachment(id, name, blob)
.push(function () {
return context._signature_sub_storage.putAttachment(id, name,
JSON.stringify({
hash: hash
}));
})
.push(function () {
skip_attachment_dict[name] = null;
});
} }
if (number_queue > 1) {
return RSVP.all(result_promise_list);
}
return result_promise_list[0];
}
function checkAndPropagateAttachment(skip_attachment_dict, function callAllDocsOnStorage(context, storage, cache, cache_key) {
status_hash, local_hash, blob, return new RSVP.Queue()
source, destination, id, name, .push(function () {
conflict_force, conflict_revert, if (!cache.hasOwnProperty(cache_key)) {
conflict_ignore) { return storage.allDocs(context._query_options)
var remote_blob; .push(function (result) {
return destination.getAttachment(id, name) var i,
.push(function (result) { cache_entry = {};
remote_blob = result; for (i = 0; i < result.data.total_rows; i += 1) {
return jIO.util.readBlobAsArrayBuffer(remote_blob); cache_entry[result.data.rows[i].id] = result.data.rows[i].value;
}) }
.push(function (evt) { cache[cache_key] = cache_entry;
return generateHashFromArrayBuffer( });
evt.target.result }
); })
}, function (error) { .push(function () {
if ((error instanceof jIO.util.jIOError) && return cache[cache_key];
(error.status_code === 404)) { });
remote_blob = null; }
return null;
}
throw error;
})
.push(function (remote_hash) {
if (local_hash === remote_hash) {
// Same modifications on both side
if (local_hash === null) {
// Deleted on both side, drop signature
return context._signature_sub_storage.removeAttachment(id, name)
.push(function () {
skip_attachment_dict[id] = null;
});
}
return context._signature_sub_storage.putAttachment(id, name, function propagateAttachmentDeletion(context, skip_attachment_dict,
JSON.stringify({ destination,
hash: local_hash id, name) {
})) return destination.removeAttachment(id, name)
.push(function () {
return context._signature_sub_storage.removeAttachment(id, name);
})
.push(function () {
skip_attachment_dict[name] = null;
});
}
function propagateAttachmentModification(context, skip_attachment_dict,
destination,
blob, hash, id, name) {
return destination.putAttachment(id, name, blob)
.push(function () {
return context._signature_sub_storage.putAttachment(id, name,
JSON.stringify({
hash: hash
}));
})
.push(function () {
skip_attachment_dict[name] = null;
});
}
function checkAndPropagateAttachment(context,
skip_attachment_dict,
status_hash, local_hash, blob,
source, destination, id, name,
conflict_force, conflict_revert,
conflict_ignore) {
var remote_blob;
return destination.getAttachment(id, name)
.push(function (result) {
remote_blob = result;
return jIO.util.readBlobAsArrayBuffer(remote_blob);
})
.push(function (evt) {
return generateHashFromArrayBuffer(
evt.target.result
);
}, function (error) {
if ((error instanceof jIO.util.jIOError) &&
(error.status_code === 404)) {
remote_blob = null;
return null;
}
throw error;
})
.push(function (remote_hash) {
if (local_hash === remote_hash) {
// Same modifications on both side
if (local_hash === null) {
// Deleted on both side, drop signature
return context._signature_sub_storage.removeAttachment(id, name)
.push(function () { .push(function () {
skip_document_dict[id] = null; skip_attachment_dict[name] = null;
}); });
} }
if ((remote_hash === status_hash) || (conflict_force === true)) { return context._signature_sub_storage.putAttachment(id, name,
// Modified only locally. No conflict or force JSON.stringify({
if (local_hash === null) { hash: local_hash
// Deleted locally }))
return propagateAttachmentDeletion(skip_attachment_dict, .push(function () {
destination, skip_attachment_dict[name] = null;
id, name); });
} }
return propagateAttachmentModification(skip_attachment_dict,
destination, blob,
local_hash, id, name);
}
// Conflict cases if ((remote_hash === status_hash) || (conflict_force === true)) {
if (conflict_ignore === true) { // Modified only locally. No conflict or force
return; if (local_hash === null) {
// Deleted locally
return propagateAttachmentDeletion(context, skip_attachment_dict,
destination,
id, name);
} }
return propagateAttachmentModification(context,
skip_attachment_dict,
destination, blob,
local_hash, id, name);
}
if ((conflict_revert === true) || (local_hash === null)) { // Conflict cases
// Automatically resolve conflict or force revert if (conflict_ignore === true) {
if (remote_hash === null) { return;
// Deleted remotely }
return propagateAttachmentDeletion(skip_attachment_dict,
source, id, name);
}
return propagateAttachmentModification(
skip_attachment_dict,
source,
remote_blob,
remote_hash,
id,
name
);
}
// Minimize conflict if it can be resolved if ((conflict_revert === true) || (local_hash === null)) {
// Automatically resolve conflict or force revert
if (remote_hash === null) { if (remote_hash === null) {
// Copy remote modification remotely // Deleted remotely
return propagateAttachmentModification(skip_attachment_dict, return propagateAttachmentDeletion(context, skip_attachment_dict,
destination, blob, source, id, name);
local_hash, id, name);
} }
throw new jIO.util.jIOError("Conflict on '" + id + return propagateAttachmentModification(
"' with attachment '" + context,
name + "'", skip_attachment_dict,
409); source,
}); remote_blob,
} remote_hash,
id,
name
);
}
function checkAttachmentSignatureDifference(queue, skip_attachment_dict, // Minimize conflict if it can be resolved
source, if (remote_hash === null) {
destination, id, name, // Copy remote modification remotely
conflict_force, return propagateAttachmentModification(context,
conflict_revert, skip_attachment_dict,
conflict_ignore, destination, blob,
is_creation, is_modification) { local_hash, id, name);
var blob, }
status_hash; throw new jIO.util.jIOError("Conflict on '" + id +
queue "' with attachment '" +
.push(function () { name + "'",
// Optimisation to save a get call to signature storage 409);
if (is_creation === true) { });
return RSVP.all([ }
source.getAttachment(id, name),
{hash: null}
]);
}
if (is_modification === true) {
return RSVP.all([
source.getAttachment(id, name),
context._signature_sub_storage.getAttachment(
id,
name,
{format: 'json'}
)
]);
}
throw new jIO.util.jIOError("Unexpected call of"
+ " checkAttachmentSignatureDifference",
409);
})
.push(function (result_list) {
blob = result_list[0];
status_hash = result_list[1].hash;
return jIO.util.readBlobAsArrayBuffer(blob);
})
.push(function (evt) {
var array_buffer = evt.target.result,
local_hash = generateHashFromArrayBuffer(array_buffer);
if (local_hash !== status_hash) { function checkAttachmentSignatureDifference(queue, context,
return checkAndPropagateAttachment(skip_attachment_dict, skip_attachment_dict,
status_hash, local_hash, blob, source,
source, destination, id, name, destination, id, name,
conflict_force, conflict_revert, conflict_force,
conflict_ignore); conflict_revert,
} conflict_ignore,
}); is_creation, is_modification) {
} var blob,
status_hash;
queue
.push(function () {
// Optimisation to save a get call to signature storage
if (is_creation === true) {
return RSVP.all([
source.getAttachment(id, name),
{hash: null}
]);
}
if (is_modification === true) {
return RSVP.all([
source.getAttachment(id, name),
context._signature_sub_storage.getAttachment(
id,
name,
{format: 'json'}
)
]);
}
throw new jIO.util.jIOError("Unexpected call of"
+ " checkAttachmentSignatureDifference",
409);
})
.push(function (result_list) {
blob = result_list[0];
status_hash = result_list[1].hash;
return jIO.util.readBlobAsArrayBuffer(blob);
})
.push(function (evt) {
var array_buffer = evt.target.result,
local_hash = generateHashFromArrayBuffer(array_buffer);
function checkAttachmentLocalDeletion(queue, skip_attachment_dict, if (local_hash !== status_hash) {
destination, id, name, source, return checkAndPropagateAttachment(context,
conflict_force, conflict_revert, skip_attachment_dict,
conflict_ignore) { status_hash, local_hash, blob,
var status_hash; source, destination, id, name,
queue conflict_force, conflict_revert,
.push(function () { conflict_ignore);
return context._signature_sub_storage.getAttachment(id, name, }
{format: 'json'}); });
}) }
.push(function (result) {
status_hash = result.hash;
return checkAndPropagateAttachment(skip_attachment_dict,
status_hash, null, null,
source, destination, id, name,
conflict_force, conflict_revert,
conflict_ignore);
});
}
function pushDocumentAttachment(skip_attachment_dict, id, source, function checkAttachmentLocalDeletion(queue, context,
destination, options) { skip_attachment_dict,
var queue = new RSVP.Queue(), destination, id, name, source,
local_dict = {}, conflict_force, conflict_revert,
signature_dict = {}; conflict_ignore) {
var status_hash;
queue
.push(function () {
return context._signature_sub_storage.getAttachment(id, name,
{format: 'json'});
})
.push(function (result) {
status_hash = result.hash;
return checkAndPropagateAttachment(context,
skip_attachment_dict,
status_hash, null, null,
source, destination, id, name,
conflict_force, conflict_revert,
conflict_ignore);
});
}
return queue function pushDocumentAttachment(context,
.push(function () { skip_attachment_dict, id, source,
return RSVP.all([ destination, signature_allAttachments,
source.allAttachments(id) options) {
.push(undefined, function (error) { var local_dict = {},
if ((error instanceof jIO.util.jIOError) && signature_dict = {};
(error.status_code === 404)) { return source.allAttachments(id)
return {}; .push(undefined, function (error) {
} if ((error instanceof jIO.util.jIOError) &&
throw error; (error.status_code === 404)) {
}), return {};
context._signature_sub_storage.allAttachments(id) }
.push(undefined, function (error) { throw error;
if ((error instanceof jIO.util.jIOError) && })
(error.status_code === 404)) { .push(function (source_allAttachments) {
return {}; var is_modification,
} is_creation,
throw error; key,
}) argument_list = [];
]); for (key in source_allAttachments) {
}) if (source_allAttachments.hasOwnProperty(key)) {
.push(function (result_list) { if (!skip_attachment_dict.hasOwnProperty(key)) {
var is_modification, local_dict[key] = null;
is_creation,
key,
argument_list = [];
for (key in result_list[0]) {
if (result_list[0].hasOwnProperty(key)) {
if (!skip_attachment_dict.hasOwnProperty(key)) {
local_dict[key] = null;
}
} }
} }
for (key in result_list[1]) { }
if (result_list[1].hasOwnProperty(key)) { for (key in signature_allAttachments) {
if (!skip_attachment_dict.hasOwnProperty(key)) { if (signature_allAttachments.hasOwnProperty(key)) {
signature_dict[key] = null; if (!skip_attachment_dict.hasOwnProperty(key)) {
} signature_dict[key] = null;
} }
} }
}
for (key in local_dict) { for (key in local_dict) {
if (local_dict.hasOwnProperty(key)) { if (local_dict.hasOwnProperty(key)) {
is_modification = signature_dict.hasOwnProperty(key) is_modification = signature_dict.hasOwnProperty(key)
&& options.check_modification; && options.check_modification;
is_creation = !signature_dict.hasOwnProperty(key) is_creation = !signature_dict.hasOwnProperty(key)
&& options.check_creation; && options.check_creation;
if (is_modification === true || is_creation === true) { if (is_modification === true || is_creation === true) {
argument_list.push([undefined,
context,
skip_attachment_dict,
source,
destination, id, key,
options.conflict_force,
options.conflict_revert,
options.conflict_ignore,
is_creation,
is_modification]);
}
}
}
return dispatchQueue(
context,
checkAttachmentSignatureDifference,
argument_list,
context._parallel_operation_attachment_amount
);
})
.push(function () {
var key, argument_list = [];
if (options.check_deletion === true) {
for (key in signature_dict) {
if (signature_dict.hasOwnProperty(key)) {
if (!local_dict.hasOwnProperty(key)) {
argument_list.push([undefined, argument_list.push([undefined,
skip_attachment_dict, context,
source, skip_attachment_dict,
destination, id, key, destination, id, key,
options.conflict_force, source,
options.conflict_revert, options.conflict_force,
options.conflict_ignore, options.conflict_revert,
is_creation, options.conflict_ignore]);
is_modification]);
} }
} }
} }
return dispatchQueue( return dispatchQueue(
checkAttachmentSignatureDifference, context,
checkAttachmentLocalDeletion,
argument_list, argument_list,
context._parallel_operation_attachment_amount context._parallel_operation_attachment_amount
); );
}) }
.push(function () { });
var key, argument_list = []; }
if (options.check_deletion === true) {
for (key in signature_dict) {
if (signature_dict.hasOwnProperty(key)) {
if (!local_dict.hasOwnProperty(key)) {
argument_list.push([undefined,
skip_attachment_dict,
destination, id, key,
source,
options.conflict_force,
options.conflict_revert,
options.conflict_ignore]);
}
}
}
return dispatchQueue(
checkAttachmentLocalDeletion,
argument_list,
context._parallel_operation_attachment_amount
);
}
});
}
function repairDocumentAttachment(context, id) {
var skip_attachment_dict = {};
return new RSVP.Queue()
.push(function () {
if (context._check_local_attachment_modification ||
context._check_local_attachment_creation ||
context._check_local_attachment_deletion ||
context._check_remote_attachment_modification ||
context._check_remote_attachment_creation ||
context._check_remote_attachment_deletion) {
return context._signature_sub_storage.allAttachments(id);
}
return {};
})
.push(undefined, function (error) {
if ((error instanceof jIO.util.jIOError) &&
(error.status_code === 404)) {
return {};
}
throw error;
})
.push(function (signature_allAttachments) {
if (context._check_local_attachment_modification ||
context._check_local_attachment_creation ||
context._check_local_attachment_deletion) {
return pushDocumentAttachment(
context,
skip_attachment_dict,
id,
context._local_sub_storage,
context._remote_sub_storage,
signature_allAttachments,
{
conflict_force: (context._conflict_handling ===
CONFLICT_KEEP_LOCAL),
conflict_revert: (context._conflict_handling ===
CONFLICT_KEEP_REMOTE),
conflict_ignore: (context._conflict_handling ===
CONFLICT_CONTINUE),
check_modification:
context._check_local_attachment_modification,
check_creation: context._check_local_attachment_creation,
check_deletion: context._check_local_attachment_deletion
}
)
.push(function () {
return signature_allAttachments;
});
}
return signature_allAttachments;
})
.push(function (signature_allAttachments) {
if (context._check_remote_attachment_modification ||
context._check_remote_attachment_creation ||
context._check_remote_attachment_deletion) {
return pushDocumentAttachment(
context,
skip_attachment_dict,
id,
context._remote_sub_storage,
context._local_sub_storage,
signature_allAttachments,
{
use_revert_post: context._use_remote_post,
conflict_force: (context._conflict_handling ===
CONFLICT_KEEP_REMOTE),
conflict_revert: (context._conflict_handling ===
CONFLICT_KEEP_LOCAL),
conflict_ignore: (context._conflict_handling ===
CONFLICT_CONTINUE),
check_modification:
context._check_remote_attachment_modification,
check_creation: context._check_remote_attachment_creation,
check_deletion: context._check_remote_attachment_deletion
}
);
}
});
}
function repairDocumentAttachment(id) { function propagateModification(context, source, destination, doc, hash, id,
var skip_attachment_dict = {}; skip_document_dict,
return new RSVP.Queue() options) {
var result = new RSVP.Queue(),
post_id,
to_skip = true;
if (options === undefined) {
options = {};
}
if (doc === null) {
result
.push(function () { .push(function () {
if (context._check_local_attachment_modification || return source.get(id);
context._check_local_attachment_creation ||
context._check_local_attachment_deletion) {
return pushDocumentAttachment(
skip_attachment_dict,
id,
context._local_sub_storage,
context._remote_sub_storage,
{
conflict_force: (context._conflict_handling ===
CONFLICT_KEEP_LOCAL),
conflict_revert: (context._conflict_handling ===
CONFLICT_KEEP_REMOTE),
conflict_ignore: (context._conflict_handling ===
CONFLICT_CONTINUE),
check_modification:
context._check_local_attachment_modification,
check_creation: context._check_local_attachment_creation,
check_deletion: context._check_local_attachment_deletion
}
);
}
}) })
.push(function () { .push(function (source_doc) {
if (context._check_remote_attachment_modification || doc = source_doc;
context._check_remote_attachment_creation || }, function (error) {
context._check_remote_attachment_deletion) { if ((error instanceof jIO.util.jIOError) &&
return pushDocumentAttachment( (error.status_code === 404)) {
skip_attachment_dict, throw new SkipError(id);
id,
context._remote_sub_storage,
context._local_sub_storage,
{
use_revert_post: context._use_remote_post,
conflict_force: (context._conflict_handling ===
CONFLICT_KEEP_REMOTE),
conflict_revert: (context._conflict_handling ===
CONFLICT_KEEP_LOCAL),
conflict_ignore: (context._conflict_handling ===
CONFLICT_CONTINUE),
check_modification:
context._check_remote_attachment_modification,
check_creation: context._check_remote_attachment_creation,
check_deletion: context._check_remote_attachment_deletion
}
);
} }
throw error;
}); });
} }
if (options.use_post) {
function propagateModification(source, destination, doc, hash, id, result
options) {
var result,
post_id,
to_skip = true;
if (options === undefined) {
options = {};
}
if (options.use_post) {
result = destination.post(doc)
.push(function (new_id) {
to_skip = false;
post_id = new_id;
return source.put(post_id, doc);
})
.push(function () {
// Copy all attachments
// This is not related to attachment replication
// It's just about not losing user data
return source.allAttachments(id);
})
.push(function (attachment_dict) {
var key,
copy_queue = new RSVP.Queue();
function copyAttachment(name) {
copy_queue
.push(function () {
return source.getAttachment(id, name);
})
.push(function (blob) {
return source.putAttachment(post_id, name, blob);
});
}
for (key in attachment_dict) {
if (attachment_dict.hasOwnProperty(key)) {
copyAttachment(key);
}
}
return copy_queue;
})
.push(function () {
return source.remove(id);
})
.push(function () {
return context._signature_sub_storage.remove(id);
})
.push(function () {
to_skip = true;
return context._signature_sub_storage.put(post_id, {
"hash": hash
});
})
.push(function () {
skip_document_dict[post_id] = null;
});
} else {
result = destination.put(id, doc)
.push(function () {
return context._signature_sub_storage.put(id, {
"hash": hash
});
});
}
return result
.push(function () { .push(function () {
if (to_skip) { return destination.post(doc);
skip_document_dict[id] = null; })
} .push(function (new_id) {
}); to_skip = false;
} post_id = new_id;
return source.put(post_id, doc);
function propagateDeletion(destination, id) { })
// Do not delete a document if it has an attachment
// ie, replication should prevent losing user data
// Synchronize attachments before, to ensure
// all of them will be deleted too
return repairDocumentAttachment(id)
.push(function () { .push(function () {
return destination.allAttachments(id); // Copy all attachments
// This is not related to attachment replication
// It's just about not losing user data
return source.allAttachments(id);
}) })
.push(function (attachment_dict) { .push(function (attachment_dict) {
if (JSON.stringify(attachment_dict) === "{}") { var key,
return destination.remove(id) copy_queue = new RSVP.Queue();
function copyAttachment(name) {
copy_queue
.push(function () { .push(function () {
return context._signature_sub_storage.remove(id); return source.getAttachment(id, name);
})
.push(function (blob) {
return source.putAttachment(post_id, name, blob);
}); });
} }
}, function (error) {
if ((error instanceof jIO.util.jIOError) && for (key in attachment_dict) {
(error.status_code === 404)) { if (attachment_dict.hasOwnProperty(key)) {
return; copyAttachment(key);
}
} }
throw error; return copy_queue;
}) })
.push(function () { .push(function () {
skip_document_dict[id] = null; return source.remove(id);
})
.push(function () {
return context._signature_sub_storage.remove(id);
})
.push(function () {
to_skip = true;
return context._signature_sub_storage.put(post_id, {
"hash": hash
});
})
.push(function () {
skip_document_dict[post_id] = null;
});
} else {
result
.push(function () {
return destination.put(id, doc);
})
.push(function () {
return context._signature_sub_storage.put(id, {
"hash": hash
});
}); });
} }
return result
.push(function () {
if (to_skip) {
skip_document_dict[id] = null;
}
})
.push(undefined, function (error) {
if (error instanceof SkipError) {
return;
}
throw error;
});
}
function checkAndPropagate(status_hash, local_hash, doc, function propagateDeletion(context, destination, id, skip_document_dict) {
source, destination, id, // Do not delete a document if it has an attachment
conflict_force, conflict_revert, // ie, replication should prevent losing user data
conflict_ignore, // Synchronize attachments before, to ensure
options) { // all of them will be deleted too
return destination.get(id) return repairDocumentAttachment(context, id)
.push(function (remote_doc) { .push(function () {
return [remote_doc, generateHash(stringify(remote_doc))]; return destination.allAttachments(id);
}, function (error) { })
if ((error instanceof jIO.util.jIOError) && .push(function (attachment_dict) {
(error.status_code === 404)) { if (JSON.stringify(attachment_dict) === "{}") {
return [null, null]; return destination.remove(id)
} .push(function () {
throw error; return context._signature_sub_storage.remove(id);
}) });
.push(function (remote_list) { }
var remote_doc = remote_list[0], }, function (error) {
remote_hash = remote_list[1]; if ((error instanceof jIO.util.jIOError) &&
(error.status_code === 404)) {
return;
}
throw error;
})
.push(function () {
skip_document_dict[id] = null;
});
}
if (local_hash === remote_hash) { function checkAndPropagate(context, skip_document_dict,
// Same modifications on both side cache, destination_key,
if (local_hash === null) { status_hash, local_hash, doc,
// Deleted on both side, drop signature source, destination, id,
return context._signature_sub_storage.remove(id) conflict_force, conflict_revert,
.push(function () { conflict_ignore,
skip_document_dict[id] = null; options) {
}); return new RSVP.Queue()
.push(function () {
if (options.signature_select_metadata !== undefined) {
return callAllDocsOnStorage(context, destination,
cache, destination_key)
.push(function (result) {
if (result.hasOwnProperty(id)) {
return [null, result[id][options.signature_select_metadata]];
}
return [null, null];
});
}
return destination.get(id)
.push(function (remote_doc) {
return [remote_doc, generateHash(stringify(remote_doc))];
}, function (error) {
if ((error instanceof jIO.util.jIOError) &&
(error.status_code === 404)) {
return [null, null];
} }
throw error;
});
})
return context._signature_sub_storage.put(id, { .push(function (remote_list) {
"hash": local_hash var remote_doc = remote_list[0],
}) remote_hash = remote_list[1];
if (local_hash === remote_hash) {
// Same modifications on both side
if (local_hash === null) {
// Deleted on both side, drop signature
return context._signature_sub_storage.remove(id)
.push(function () { .push(function () {
skip_document_dict[id] = null; skip_document_dict[id] = null;
}); });
} }
if ((remote_hash === status_hash) || (conflict_force === true)) { return context._signature_sub_storage.put(id, {
// Modified only locally. No conflict or force "hash": local_hash
if (local_hash === null) { })
// Deleted locally .push(function () {
return propagateDeletion(destination, id); skip_document_dict[id] = null;
} });
return propagateModification(source, destination, doc, }
local_hash, id,
{use_post: ((options.use_post) &&
(remote_hash === null))});
}
// Conflict cases if ((remote_hash === status_hash) || (conflict_force === true)) {
if (conflict_ignore === true) { // Modified only locally. No conflict or force
return; if (local_hash === null) {
// Deleted locally
return propagateDeletion(context, destination, id,
skip_document_dict);
} }
return propagateModification(context, source, destination, doc,
local_hash, id, skip_document_dict,
{use_post: ((options.use_post) &&
(remote_hash === null))});
}
if ((conflict_revert === true) || (local_hash === null)) { // Conflict cases
// Automatically resolve conflict or force revert if (conflict_ignore === true) {
if (remote_hash === null) { return;
// Deleted remotely }
return propagateDeletion(source, id);
}
return propagateModification(
destination,
source,
remote_doc,
remote_hash,
id,
{use_post: ((options.use_revert_post) &&
(local_hash === null))}
);
}
// Minimize conflict if it can be resolved if ((conflict_revert === true) || (local_hash === null)) {
// Automatically resolve conflict or force revert
if (remote_hash === null) { if (remote_hash === null) {
// Copy remote modification remotely // Deleted remotely
return propagateModification(source, destination, doc, return propagateDeletion(context, source, id, skip_document_dict);
local_hash, id,
{use_post: options.use_post});
} }
throw new jIO.util.jIOError("Conflict on '" + id + "': " + return propagateModification(
stringify(doc || '') + " !== " + context,
stringify(remote_doc || ''), destination,
409); source,
}); remote_doc,
} remote_hash,
id,
skip_document_dict,
{use_post: ((options.use_revert_post) &&
(local_hash === null))}
);
}
function checkLocalDeletion(queue, destination, id, source, // Minimize conflict if it can be resolved
conflict_force, conflict_revert, if (remote_hash === null) {
conflict_ignore, options) { // Copy remote modification remotely
var status_hash; return propagateModification(context, source, destination, doc,
queue local_hash, id, skip_document_dict,
.push(function () { {use_post: options.use_post});
return context._signature_sub_storage.get(id); }
}) doc = doc || local_hash;
.push(function (result) { remote_doc = remote_doc || remote_hash;
status_hash = result.hash; throw new jIO.util.jIOError("Conflict on '" + id + "': " +
return checkAndPropagate(status_hash, null, null, stringify(doc) + " !== " +
stringify(remote_doc),
409);
});
}
function checkLocalDeletion(queue, context, skip_document_dict,
cache, destination_key,
destination, id, source,
conflict_force, conflict_revert,
conflict_ignore, options) {
var status_hash;
queue
.push(function () {
return context._signature_sub_storage.get(id);
})
.push(function (result) {
status_hash = result.hash;
return checkAndPropagate(context, skip_document_dict,
cache, destination_key,
status_hash, null, null,
source, destination, id,
conflict_force, conflict_revert,
conflict_ignore,
options);
});
}
function checkSignatureDifference(queue, context, skip_document_dict,
cache, destination_key,
source, destination, id,
conflict_force, conflict_revert,
conflict_ignore,
local_hash, status_hash,
options) {
queue
.push(function () {
if (local_hash === null) {
// Hash was not provided by the allDocs query
return source.get(id);
}
return null;
})
.push(function (doc) {
if (local_hash === null) {
// Hash was not provided by the allDocs query
local_hash = generateHash(stringify(doc));
}
if (local_hash !== status_hash) {
return checkAndPropagate(context, skip_document_dict,
cache, destination_key,
status_hash, local_hash, doc,
source, destination, id, source, destination, id,
conflict_force, conflict_revert, conflict_force, conflict_revert,
conflict_ignore, conflict_ignore,
options); options);
}); }
} });
}
function checkSignatureDifference(queue, source, destination, id,
conflict_force, conflict_revert,
conflict_ignore,
is_creation, is_modification,
getMethod, options) {
queue
.push(function () {
// Optimisation to save a get call to signature storage
if (is_creation === true) {
return RSVP.all([
getMethod(id),
{hash: null}
]);
}
if (is_modification === true) {
return RSVP.all([
getMethod(id),
context._signature_sub_storage.get(id)
]);
}
throw new jIO.util.jIOError("Unexpected call of"
+ " checkSignatureDifference",
409);
})
.push(function (result_list) {
var doc = result_list[0],
local_hash = generateHash(stringify(doc)),
status_hash = result_list[1].hash;
if (local_hash !== status_hash) { function pushStorage(context, skip_document_dict,
return checkAndPropagate(status_hash, local_hash, doc, cache, source_key, destination_key,
source, destination, id, source, destination, signature_allDocs, options) {
conflict_force, conflict_revert, var argument_list = [],
conflict_ignore, argument_list_deletion = [];
options); if (!options.hasOwnProperty("use_post")) {
} options.use_post = false;
});
} }
if (!options.hasOwnProperty("use_revert_post")) {
function checkBulkSignatureDifference(queue, source, destination, id_list, options.use_revert_post = false;
document_status_list, options,
conflict_force, conflict_revert,
conflict_ignore) {
queue
.push(function () {
return source.bulk(id_list);
})
.push(function (result_list) {
var i,
argument_list = [];
function getResult(j) {
return function (id) {
if (id !== id_list[j].parameter_list[0]) {
throw new Error("Does not access expected ID " + id);
}
return result_list[j];
};
}
for (i = 0; i < result_list.length; i += 1) {
argument_list[i] = [undefined, source, destination,
id_list[i].parameter_list[0],
conflict_force, conflict_revert,
conflict_ignore,
document_status_list[i].is_creation,
document_status_list[i].is_modification,
getResult(i), options];
}
return dispatchQueue(
checkSignatureDifference,
argument_list,
options.operation_amount
);
});
} }
return callAllDocsOnStorage(context, source, cache, source_key)
function pushStorage(source, destination, options) { .push(function (source_allDocs) {
var queue = new RSVP.Queue(), var i,
argument_list = [], local_dict = {},
argument_list_deletion = []; signature_dict = {},
if (!options.hasOwnProperty("use_post")) { is_modification,
options.use_post = false; is_creation,
} status_hash,
if (!options.hasOwnProperty("use_revert_post")) { local_hash,
options.use_revert_post = false; key,
} queue = new RSVP.Queue();
return queue for (key in source_allDocs) {
.push(function () { if (source_allDocs.hasOwnProperty(key)) {
return RSVP.all([ if (!skip_document_dict.hasOwnProperty(key)) {
source.allDocs(context._query_options), local_dict[key] = source_allDocs[key];
context._signature_sub_storage.allDocs()
]);
})
.push(function (result_list) {
var i,
local_dict = {},
document_list = [],
document_status_list = [],
signature_dict = {},
is_modification,
is_creation,
key;
for (i = 0; i < result_list[0].data.total_rows; i += 1) {
if (!skip_document_dict.hasOwnProperty(
result_list[0].data.rows[i].id
)) {
local_dict[result_list[0].data.rows[i].id] = i;
} }
} }
for (i = 0; i < result_list[1].data.total_rows; i += 1) { }
if (!skip_document_dict.hasOwnProperty( /*
result_list[1].data.rows[i].id for (i = 0; i < source_allDocs.data.total_rows; i += 1) {
)) { if (!skip_document_dict.hasOwnProperty(
signature_dict[result_list[1].data.rows[i].id] = i; source_allDocs.data.rows[i].id
} )) {
local_dict[source_allDocs.data.rows[i].id] =
source_allDocs.data.rows[i].value;
} }
i = 0; }
for (key in local_dict) { */
if (local_dict.hasOwnProperty(key)) { for (i = 0; i < signature_allDocs.data.total_rows; i += 1) {
is_modification = signature_dict.hasOwnProperty(key) if (!skip_document_dict.hasOwnProperty(
&& options.check_modification; signature_allDocs.data.rows[i].id
is_creation = !signature_dict.hasOwnProperty(key) )) {
&& options.check_creation; signature_dict[signature_allDocs.data.rows[i].id] =
if (is_modification === true || is_creation === true) { signature_allDocs.data.rows[i].value.hash;
if (options.use_bulk_get === true) { }
document_list.push({ }
method: "get", for (key in local_dict) {
parameter_list: [key] if (local_dict.hasOwnProperty(key)) {
}); is_modification = signature_dict.hasOwnProperty(key)
document_status_list.push({ && options.check_modification;
is_creation: is_creation, is_creation = !signature_dict.hasOwnProperty(key)
is_modification: is_modification && options.check_creation;
});
} else { if (is_creation === true) {
argument_list[i] = [undefined, source, destination, status_hash = null;
key, } else if (is_modification === true) {
options.conflict_force, status_hash = signature_dict[key];
options.conflict_revert, }
options.conflict_ignore,
is_creation, is_modification, local_hash = null;
source.get.bind(source), if (options.signature_select_metadata !== undefined) {
options]; local_hash = local_dict[key][options.signature_select_metadata];
i += 1; if (is_modification === true) {
// Bypass fetching all documents and calculating the sha
// Compare the select list values returned by allDocs calls
is_modification = false;
if (local_hash !== status_hash) {
is_modification = true;
} }
} }
} }
if (is_modification === true || is_creation === true) {
argument_list.push([undefined, context, skip_document_dict,
cache, destination_key,
source, destination,
key,
options.conflict_force,
options.conflict_revert,
options.conflict_ignore,
local_hash, status_hash,
options]);
}
} }
queue }
.push(function () { queue
return dispatchQueue( .push(function () {
checkSignatureDifference, return dispatchQueue(
argument_list, context,
options.operation_amount checkSignatureDifference,
); argument_list,
}); options.operation_amount
if (options.check_deletion === true) { );
i = 0; });
for (key in signature_dict) { if (options.check_deletion === true) {
if (signature_dict.hasOwnProperty(key)) { for (key in signature_dict) {
if (!local_dict.hasOwnProperty(key)) { if (signature_dict.hasOwnProperty(key)) {
argument_list_deletion[i] = [undefined, if (!local_dict.hasOwnProperty(key)) {
destination, key, argument_list_deletion.push([undefined,
source, context,
options.conflict_force, skip_document_dict,
options.conflict_revert, cache, destination_key,
options.conflict_ignore, destination, key,
options]; source,
i += 1; options.conflict_force,
} options.conflict_revert,
options.conflict_ignore,
options]);
} }
} }
queue.push(function () {
return dispatchQueue(
checkLocalDeletion,
argument_list_deletion,
options.operation_amount
);
});
} }
if ((options.use_bulk_get === true) && (document_list.length !== 0)) { queue.push(function () {
checkBulkSignatureDifference(queue, source, destination, return dispatchQueue(
document_list, document_status_list, context,
options, checkLocalDeletion,
options.conflict_force, argument_list_deletion,
options.conflict_revert, options.operation_amount
options.conflict_ignore); );
} });
}); }
} return queue;
function repairDocument(queue, id) {
queue.push(function () {
return repairDocumentAttachment(id);
}); });
} }
function repairDocument(queue, context, id) {
queue.push(function () {
return repairDocumentAttachment(context, id);
});
}
ReplicateStorage.prototype.repair = function () {
var context = this,
argument_list = arguments,
skip_document_dict = {},
cache = {};
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function () {
// Ensure that the document storage is usable // Ensure that the document storage is usable
return context._signature_sub_storage.__storage._sub_storage.get( if (context._custom_signature_sub_storage === false) {
context._signature_hash // Do not sync the signature document
); skip_document_dict[context._signature_hash] = null;
return context._signature_sub_storage.__storage._sub_storage
.__storage._sub_storage.get(
context._signature_hash
);
}
}) })
.push(undefined, function (error) { .push(undefined, function (error) {
if ((error instanceof jIO.util.jIOError) && if ((error instanceof jIO.util.jIOError) &&
(error.status_code === 404)) { (error.status_code === 404)) {
return context._signature_sub_storage.__storage._sub_storage.put( return context._signature_sub_storage.__storage._sub_storage
context._signature_hash, .__storage._sub_storage.put(
{} context._signature_hash,
); {}
);
} }
throw error; throw error;
}) })
...@@ -1017,11 +1108,27 @@ ...@@ -1017,11 +1108,27 @@
}) })
.push(function () { .push(function () {
if (context._check_local_modification ||
context._check_local_creation ||
context._check_local_deletion ||
context._check_remote_modification ||
context._check_remote_creation ||
context._check_remote_deletion) {
return context._signature_sub_storage.allDocs({
select_list: ['hash']
});
}
})
.push(function (signature_allDocs) {
if (context._check_local_modification || if (context._check_local_modification ||
context._check_local_creation || context._check_local_creation ||
context._check_local_deletion) { context._check_local_deletion) {
return pushStorage(context._local_sub_storage, return pushStorage(context, skip_document_dict,
cache, 'local', 'remote',
context._local_sub_storage,
context._remote_sub_storage, context._remote_sub_storage,
signature_allDocs,
{ {
use_post: context._use_remote_post, use_post: context._use_remote_post,
conflict_force: (context._conflict_handling === conflict_force: (context._conflict_handling ===
...@@ -1033,28 +1140,24 @@ ...@@ -1033,28 +1140,24 @@
check_modification: context._check_local_modification, check_modification: context._check_local_modification,
check_creation: context._check_local_creation, check_creation: context._check_local_creation,
check_deletion: context._check_local_deletion, check_deletion: context._check_local_deletion,
operation_amount: context._parallel_operation_amount operation_amount: context._parallel_operation_amount,
signature_select_metadata: context._signature_select_metadata
})
.push(function () {
return signature_allDocs;
}); });
} }
return signature_allDocs;
}) })
.push(function () { .push(function (signature_allDocs) {
// Autoactivate bulk if substorage implements it
// Keep it like this until the bulk API is stabilized
var use_bulk_get = false;
try {
use_bulk_get = context._remote_sub_storage.hasCapacity("bulk_get");
} catch (error) {
if (!((error instanceof jIO.util.jIOError) &&
(error.status_code === 501))) {
throw error;
}
}
if (context._check_remote_modification || if (context._check_remote_modification ||
context._check_remote_creation || context._check_remote_creation ||
context._check_remote_deletion) { context._check_remote_deletion) {
return pushStorage(context._remote_sub_storage, return pushStorage(context, skip_document_dict,
context._local_sub_storage, { cache, 'remote', 'local',
use_bulk_get: use_bulk_get, context._remote_sub_storage,
context._local_sub_storage,
signature_allDocs, {
use_revert_post: context._use_remote_post, use_revert_post: context._use_remote_post,
conflict_force: (context._conflict_handling === conflict_force: (context._conflict_handling ===
CONFLICT_KEEP_REMOTE), CONFLICT_KEEP_REMOTE),
...@@ -1065,7 +1168,8 @@ ...@@ -1065,7 +1168,8 @@
check_modification: context._check_remote_modification, check_modification: context._check_remote_modification,
check_creation: context._check_remote_creation, check_creation: context._check_remote_creation,
check_deletion: context._check_remote_deletion, check_deletion: context._check_remote_deletion,
operation_amount: context._parallel_operation_amount operation_amount: context._parallel_operation_amount,
signature_select_metadata: context._signature_select_metadata
}); });
} }
}) })
...@@ -1081,17 +1185,18 @@ ...@@ -1081,17 +1185,18 @@
return context._signature_sub_storage.allDocs() return context._signature_sub_storage.allDocs()
.push(function (result) { .push(function (result) {
var i, var i,
argument_list = [], local_argument_list = [],
len = result.data.total_rows; len = result.data.total_rows;
for (i = 0; i < len; i += 1) { for (i = 0; i < len; i += 1) {
argument_list.push( local_argument_list.push(
[undefined, result.data.rows[i].id] [undefined, context, result.data.rows[i].id]
); );
} }
return dispatchQueue( return dispatchQueue(
context,
repairDocument, repairDocument,
argument_list, local_argument_list,
context._parallel_operation_amount context._parallel_operation_amount
); );
}); });
......
...@@ -179,7 +179,7 @@ ...@@ -179,7 +179,7 @@
return jIO.util.readBlobAsDataURL(blob); return jIO.util.readBlobAsDataURL(blob);
}) })
.push(function (strBlob) { .push(function (strBlob) {
argument_list[index + 2].push(strBlob.currentTarget.result); argument_list[index + 2].push(strBlob.target.result);
return; return;
}); });
} }
......
...@@ -458,7 +458,7 @@ ...@@ -458,7 +458,7 @@
.push(function (coded) { .push(function (coded) {
var iv; var iv;
coded = coded.currentTarget.result; coded = coded.target.result;
iv = new Uint8Array(coded.slice(0, 12)); iv = new Uint8Array(coded.slice(0, 12));
return crypto.subtle.decrypt({name : "AES-GCM", iv : iv}, return crypto.subtle.decrypt({name : "AES-GCM", iv : iv},
decryptKey, coded.slice(12)); decryptKey, coded.slice(12));
......
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
search_template = domain + "?mode=search{&query,select_list*,limit*," + search_template = domain + "?mode=search{&query,select_list*,limit*," +
"sort_on*,local_roles*}", "sort_on*,local_roles*}",
add_url = domain + "lets?add=somedocument", add_url = domain + "lets?add=somedocument",
bulk_url = domain + "lets?run=bulk",
root_hateoas = JSON.stringify({ root_hateoas = JSON.stringify({
"_links": { "_links": {
traverse: { traverse: {
...@@ -30,9 +29,6 @@ ...@@ -30,9 +29,6 @@
"_actions": { "_actions": {
add: { add: {
href: add_url href: add_url
},
bulk: {
href: bulk_url
} }
} }
}); });
...@@ -1964,202 +1960,4 @@ ...@@ -1964,202 +1960,4 @@
}); });
}); });
/////////////////////////////////////////////////////////////////
// erp5Storage.bulk
/////////////////////////////////////////////////////////////////
module("erp5Storage.bulk", {
setup: function () {
this.server = sinon.fakeServer.create();
this.server.autoRespond = true;
this.server.autoRespondAfter = 5;
this.spy = sinon.spy(FormData.prototype, "append");
this.jio = jIO.createJIO({
type: "erp5",
url: domain,
default_view_reference: "bar_view"
});
},
teardown: function () {
this.server.restore();
delete this.server;
this.spy.restore();
delete this.spy;
}
});
test("bulk get ERP5 document list", function () {
var id = "person_module/20150119_azerty",
id2 = "person_module/20150219_azerty",
context = this,
document_hateoas = JSON.stringify({
// Kept property
"title": "foo",
// Remove all _ properties
"_bar": "john doo",
"_links": {
type: {
name: "Person"
},
parent: {
href: "urn:jio:get:bar_module"
}
},
"_embedded": {
"_view": {
form_id: {
key: "form_id",
"default": "Base_view"
},
my_title: {
key: "field_my_title",
"default": "foo",
editable: true,
type: "StringField"
},
my_id: {
key: "field_my_id",
"default": "",
editable: true,
type: "StringField"
},
my_title_non_editable: {
key: "field_my_title_non_editable",
"default": "foo",
editable: false,
type: "StringField"
},
my_start_date: {
key: "field_my_start_date",
"default": "foo",
editable: true,
type: "DateTimeField"
},
your_reference: {
key: "field_your_reference",
"default": "bar",
editable: true,
type: "StringField"
},
your_reference_non_editable: {
key: "field_your_reference_non_editable",
"default": "bar",
editable: false,
type: "StringField"
},
sort_index: {
key: "field_sort_index",
"default": "foobar",
editable: true,
type: "StringField"
},
"_actions": {
put: {
href: "one erp5 url"
}
}
}
}
}),
document_hateoas2 = JSON.stringify({
// Kept property
"title": "foo2",
// Remove all _ properties
"_bar": "john doo2",
"_links": {
type: {
name: "Person"
}
},
"_embedded": {
"_view": {
form_id: {
key: "form_id",
"default": "Base_view"
},
"_actions": {
put: {
href: "one erp5 url"
}
}
}
}
}),
bulk_hateoas = JSON.parse(root_hateoas),
server = this.server;
bulk_hateoas.result_list = [
JSON.parse(document_hateoas),
JSON.parse(document_hateoas2)
];
bulk_hateoas = JSON.stringify(bulk_hateoas);
this.server.respondWith("GET", domain, [200, {
"Content-Type": "application/hal+json"
}, root_hateoas]);
this.server.respondWith("POST", bulk_url, [200, {
"Content-Type": "application/hal+json"
}, bulk_hateoas]);
stop();
expect(15);
this.jio.bulk([{
method: "get",
parameter_list: [id]
}, {
method: "get",
parameter_list: [id2]
}])
.then(function (result_list) {
equal(server.requests.length, 2);
equal(server.requests[0].method, "GET");
equal(server.requests[0].url, domain);
equal(server.requests[0].requestBody, undefined);
equal(server.requests[0].withCredentials, true);
equal(server.requests[1].method, "POST");
equal(server.requests[1].url, bulk_url);
// XXX Check form data
ok(server.requests[1].requestBody instanceof FormData);
ok(context.spy.calledOnce, "FormData.append count " +
context.spy.callCount);
equal(context.spy.firstCall.args[0], "bulk_list", "First append call");
equal(context.spy.firstCall.args[1],
JSON.stringify([{
relative_url: "person_module/20150119_azerty",
view: "bar_view"
}, {
relative_url: "person_module/20150219_azerty",
view: "bar_view"
}]),
"First append call");
equal(server.requests[1].withCredentials, true);
var result = result_list[0],
result2 = result_list[1];
equal(result_list.length, 2);
deepEqual(result, {
portal_type: "Person",
parent_relative_url: "bar_module",
reference: "bar",
reference_non_editable: "bar",
title: "foo"
}, "Check document");
deepEqual(result2, {
portal_type: "Person"
}, "Check document2");
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
}(jIO, QUnit, Blob, sinon, encodeURIComponent, FormData)); }(jIO, QUnit, Blob, sinon, encodeURIComponent, FormData));
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
/*jslint nomen: true*/
/*global Blob, RSVP*/
(function (jIO, QUnit, Blob, RSVP) {
"use strict";
var test = QUnit.test,
stop = QUnit.stop,
start = QUnit.start,
ok = QUnit.ok,
expect = QUnit.expect,
deepEqual = QUnit.deepEqual,
equal = QUnit.equal,
module = QUnit.module,
big_string = "",
j;
for (j = 0; j < 30; j += 1) {
big_string += "a";
}
/////////////////////////////////////////////////////////////////
// replicateStorage.repair use cases
/////////////////////////////////////////////////////////////////
module("replicateStorage.repair.document", {
setup: function () {
// Uses memory substorage, so that it is flushed after each run
this.jio = jIO.createJIO({
type: "replicate",
local_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
},
remote_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
}
});
}
});
/////////////////////////////////////////////////////////////////
// document replication
/////////////////////////////////////////////////////////////////
test("local document creation", function () {
stop();
expect(2);
var id,
context = this;
context.jio.post({"title": "foo"})
.then(function (result) {
id = result;
return context.jio.repair();
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
title: "foo"
});
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
hash: "5ea9013447539ad65de308cbd75b5826a2ae30e5"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("local document creation not checked", function () {
stop();
expect(6);
this.jio = jIO.createJIO({
type: "replicate",
check_local_creation: false,
local_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
},
remote_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
}
});
var id,
context = this;
context.jio.post({"title": "foo"})
.then(function (result) {
id = result;
return context.jio.repair();
})
.then(function () {
return context.jio.get(id);
})
.then(function (result) {
deepEqual(result, {
title: "foo"
});
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get(id);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find document: " + id);
equal(error.status_code, 404);
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(id);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.status_code, 404);
})
.always(function () {
start();
});
});
test("local document creation and use remote post", function () {
stop();
expect(13);
var id,
post_id,
context = this,
blob = new Blob([big_string]);
this.jio = jIO.createJIO({
type: "replicate",
use_remote_post: true,
local_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
},
remote_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
}
});
context.jio.post({"title": "foo"})
.then(function (result) {
id = result;
return context.jio.putAttachment(id, 'foo', blob);
})
.then(function () {
return context.jio.repair();
})
// Document 'id' has been deleted in both storages
.then(function () {
return context.jio.__storage._remote_sub_storage.get(id);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find document: " + id);
equal(error.status_code, 404);
})
.then(function () {
return context.jio.__storage._local_sub_storage.get(id);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find document: " + id);
equal(error.status_code, 404);
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(id);
})
.then(function () {
ok(false, "Signature should have been deleted");
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.status_code, 404);
})
// But another document should have been created
.then(function () {
return context.jio.__storage._remote_sub_storage.allDocs();
})
.then(function (result) {
equal(result.data.total_rows, 1);
post_id = result.data.rows[0].id;
return context.jio.__storage._remote_sub_storage.get(post_id);
})
.then(function (result) {
deepEqual(result, {
title: "foo"
});
})
.then(function () {
return context.jio.__storage._local_sub_storage.get(post_id);
})
.then(function (result) {
deepEqual(result, {
title: "foo"
});
// Attachment should be kept
return context.jio.__storage._local_sub_storage
.getAttachment(post_id, 'foo', {format: 'text'});
})
.then(function (result) {
equal(result, big_string);
return context.jio.__storage._signature_sub_storage.get(post_id);
})
.then(function (result) {
deepEqual(result, {
hash: "5ea9013447539ad65de308cbd75b5826a2ae30e5"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("local document creation, remote post and delayed allDocs", function () {
stop();
expect(11);
var id,
post_id = "_foobar",
context = this;
function Storage200DelayedAllDocs(spec) {
this._sub_storage = jIO.createJIO(spec.sub_storage);
}
Storage200DelayedAllDocs.prototype.get = function () {
return this._sub_storage.get.apply(this._sub_storage, arguments);
};
Storage200DelayedAllDocs.prototype.post = function (param) {
return this.put(post_id, param);
};
Storage200DelayedAllDocs.prototype.put = function () {
return this._sub_storage.put.apply(this._sub_storage, arguments);
};
Storage200DelayedAllDocs.prototype.hasCapacity = function () {
return true;
};
Storage200DelayedAllDocs.prototype.buildQuery = function () {
return [];
};
jIO.addStorage(
'replicatestorage200delayedalldocs',
Storage200DelayedAllDocs
);
this.jio = jIO.createJIO({
type: "replicate",
use_remote_post: true,
local_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
},
remote_sub_storage: {
type: "replicatestorage200delayedalldocs",
sub_storage: {
type: "memory"
}
}
});
context.jio.post({"title": "foo"})
.then(function (result) {
id = result;
return context.jio.repair();
})
// Document 'id' has been deleted in both storages
.then(function () {
return context.jio.__storage._remote_sub_storage.get(id);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find document: " + id);
equal(error.status_code, 404);
})
.then(function () {
return context.jio.__storage._local_sub_storage.get(id);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find document: " + id);
equal(error.status_code, 404);
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(id);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.status_code, 404);
})
// But another document should have been created
.then(function () {
return context.jio.__storage._remote_sub_storage.get(post_id);
})
.then(function (result) {
deepEqual(result, {
title: "foo"
});
})
.then(function () {
return context.jio.__storage._local_sub_storage.get(post_id);
})
.then(function (result) {
deepEqual(result, {
title: "foo"
});
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(post_id);
})
.then(function (result) {
deepEqual(result, {
hash: "5ea9013447539ad65de308cbd75b5826a2ae30e5"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("remote document creation", function () {
stop();
expect(2);
var id,
context = this;
context.jio.__storage._remote_sub_storage.post({"title": "bar"})
.then(function (result) {
id = result;
return context.jio.repair();
})
.then(function () {
return context.jio.get(id);
})
.then(function (result) {
deepEqual(result, {
title: "bar"
});
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
hash: "6799f3ea80e325b89f19589282a343c376c1f1af"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("remote document creation not checked", function () {
stop();
expect(6);
var id,
context = this;
this.jio = jIO.createJIO({
type: "replicate",
check_remote_creation: false,
local_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
},
remote_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
}
});
context.jio.__storage._remote_sub_storage.post({"title": "bar"})
.then(function (result) {
id = result;
return context.jio.repair();
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
title: "bar"
});
})
.then(function () {
return context.jio.get(id);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find document: " + id);
equal(error.status_code, 404);
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(id);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.status_code, 404);
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("local and remote document creations", function () {
stop();
expect(5);
var context = this;
RSVP.all([
context.jio.put("conflict", {"title": "foo"}),
context.jio.__storage._remote_sub_storage.put("conflict",
{"title": "bar"})
])
.then(function () {
return context.jio.repair();
})
.then(function () {
ok(false);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Conflict on 'conflict': " +
"{\"title\":\"foo\"} !== {\"title\":\"bar\"}");
equal(error.status_code, 409);
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get("conflict");
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
// equal(error.message, "Cannot find document: conflict");
equal(error.status_code, 404);
})
.always(function () {
start();
});
});
test("local and remote document creations: keep local", function () {
stop();
expect(3);
var context = this;
this.jio = jIO.createJIO({
type: "replicate",
local_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
},
remote_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
},
conflict_handling: 1
});
RSVP.all([
context.jio.put("conflict", {"title": "foo"}),
context.jio.__storage._remote_sub_storage.put("conflict",
{"title": "bar"})
])
.then(function () {
return context.jio.repair();
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get("conflict");
})
.then(function (result) {
deepEqual(result, {
hash: "5ea9013447539ad65de308cbd75b5826a2ae30e5"
});
})
.then(function () {
return context.jio.get("conflict");
})
.then(function (result) {
deepEqual(result, {
title: "foo"
});
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get("conflict");
})
.then(function (result) {
deepEqual(result, {
title: "foo"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("local and remote document creations: keep local, remote post",
function () {
stop();
expect(3);
var context = this;
this.jio = jIO.createJIO({
type: "replicate",
use_remote_post: 1,
local_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
},
remote_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
},
conflict_handling: 1
});
RSVP.all([
context.jio.put("conflict", {"title": "foo"}),
context.jio.__storage._remote_sub_storage.put("conflict",
{"title": "bar"})
])
.then(function () {
return context.jio.repair();
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get("conflict");
})
.then(function (result) {
deepEqual(result, {
hash: "5ea9013447539ad65de308cbd75b5826a2ae30e5"
});
})
.then(function () {
return context.jio.get("conflict");
})
.then(function (result) {
deepEqual(result, {
title: "foo"
});
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get("conflict");
})
.then(function (result) {
deepEqual(result, {
title: "foo"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("local and remote document creations: keep local, " +
"local not matching query", function () {
stop();
expect(3);
var context = this;
this.jio = jIO.createJIO({
type: "replicate",
local_sub_storage: {
type: "query",
sub_storage: {
type: "memory"
}
},
remote_sub_storage: {
type: "query",
sub_storage: {
type: "memory"
}
},
query: {query: 'type: "foobar"'},
conflict_handling: 1
});
RSVP.all([
context.jio.put("conflict", {"title": "foo"}),
context.jio.__storage._remote_sub_storage.put("conflict",
{"title": "bar",
"type": "foobar"})
])
.then(function () {
return context.jio.repair();
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get("conflict");
})
.then(function (result) {
deepEqual(result, {
hash: "5ea9013447539ad65de308cbd75b5826a2ae30e5"
});
})
.then(function () {
return context.jio.get("conflict");
})
.then(function (result) {
deepEqual(result, {
title: "foo"
});
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get("conflict");
})
.then(function (result) {
deepEqual(result, {
title: "foo"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("local and remote document creations: keep local, " +
"remote not matching query", function () {
stop();
expect(3);
var context = this;
this.jio = jIO.createJIO({
type: "replicate",
local_sub_storage: {
type: "query",
sub_storage: {
type: "memory"
}
},
remote_sub_storage: {
type: "query",
sub_storage: {
type: "memory"
}
},
query: {query: 'type: "foobar"'},
conflict_handling: 1
});
RSVP.all([
context.jio.put("conflict", {"title": "foo", "type": "foobar"}),
context.jio.__storage._remote_sub_storage.put("conflict",
{"title": "bar"})
])
.then(function () {
return context.jio.repair();
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get("conflict");
})
.then(function (result) {
deepEqual(result, {
hash: "a0a1b37cee3709101b752c56e59b9d66cce09961"
});
})
.then(function () {
return context.jio.get("conflict");
})
.then(function (result) {
deepEqual(result, {
title: "foo",
type: "foobar"
});
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get("conflict");
})
.then(function (result) {
deepEqual(result, {
title: "foo",
type: "foobar"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("local and remote document creations: keep remote", function () {
stop();
expect(3);
var context = this;
this.jio = jIO.createJIO({
type: "replicate",
local_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
},
remote_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
},
conflict_handling: 2
});
RSVP.all([
context.jio.put("conflict", {"title": "foo"}),
context.jio.__storage._remote_sub_storage.put("conflict",
{"title": "bar"})
])
.then(function () {
return context.jio.repair();
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get("conflict");
})
.then(function (result) {
deepEqual(result, {
hash: "6799f3ea80e325b89f19589282a343c376c1f1af"
});
})
.then(function () {
return context.jio.get("conflict");
})
.then(function (result) {
deepEqual(result, {
title: "bar"
});
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get("conflict");
})
.then(function (result) {
deepEqual(result, {
title: "bar"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("local and remote document creations: keep remote, remote post",
function () {
stop();
expect(3);
var context = this;
this.jio = jIO.createJIO({
type: "replicate",
use_remote_post: 1,
local_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
},
remote_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
},
conflict_handling: 2
});
RSVP.all([
context.jio.put("conflict", {"title": "foo"}),
context.jio.__storage._remote_sub_storage.put("conflict",
{"title": "bar"})
])
.then(function () {
return context.jio.repair();
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get("conflict");
})
.then(function (result) {
deepEqual(result, {
hash: "6799f3ea80e325b89f19589282a343c376c1f1af"
});
})
.then(function () {
return context.jio.get("conflict");
})
.then(function (result) {
deepEqual(result, {
title: "bar"
});
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get("conflict");
})
.then(function (result) {
deepEqual(result, {
title: "bar"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("local and remote document creations: keep remote, " +
"local not matching query", function () {
stop();
expect(3);
var context = this;
this.jio = jIO.createJIO({
type: "replicate",
local_sub_storage: {
type: "query",
sub_storage: {
type: "memory"
}
},
remote_sub_storage: {
type: "query",
sub_storage: {
type: "memory"
}
},
query: {query: 'type: "foobar"'},
conflict_handling: 2
});
RSVP.all([
context.jio.put("conflict", {"title": "foo"}),
context.jio.__storage._remote_sub_storage.put("conflict",
{"title": "bar",
"type": "foobar"})
])
.then(function () {
return context.jio.repair();
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get("conflict");
})
.then(function (result) {
deepEqual(result, {
hash: "45efa2292d54cc4ce1f726ea197bc0b9721fc1dc"
});
})
.then(function () {
return context.jio.get("conflict");
})
.then(function (result) {
deepEqual(result, {
title: "bar",
type: "foobar"
});
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get("conflict");
})
.then(function (result) {
deepEqual(result, {
title: "bar",
type: "foobar"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("local and remote document creations: keep remote, " +
"remote not matching query", function () {
stop();
expect(3);
var context = this;
this.jio = jIO.createJIO({
type: "replicate",
local_sub_storage: {
type: "query",
sub_storage: {
type: "memory"
}
},
remote_sub_storage: {
type: "query",
sub_storage: {
type: "memory"
}
},
query: {query: 'type: "foobar"'},
conflict_handling: 2
});
RSVP.all([
context.jio.put("conflict", {"title": "foo", "type": "foobar"}),
context.jio.__storage._remote_sub_storage.put("conflict",
{"title": "bar"})
])
.then(function () {
return context.jio.repair();
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get("conflict");
})
.then(function (result) {
deepEqual(result, {
hash: "6799f3ea80e325b89f19589282a343c376c1f1af"
});
})
.then(function () {
return context.jio.get("conflict");
})
.then(function (result) {
deepEqual(result, {
title: "bar"
});
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get("conflict");
})
.then(function (result) {
deepEqual(result, {
title: "bar"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("local and remote document creations: continue", function () {
stop();
expect(4);
var context = this;
this.jio = jIO.createJIO({
type: "replicate",
local_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
},
remote_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
},
conflict_handling: 3
});
RSVP.all([
context.jio.put("conflict", {"title": "foo"}),
context.jio.__storage._remote_sub_storage.put("conflict",
{"title": "bar"})
])
.then(function () {
return context.jio.repair();
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get("conflict");
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
// equal(error.message, "Cannot find document: conflict");
equal(error.status_code, 404);
})
.then(function () {
return context.jio.get("conflict");
})
.then(function (result) {
deepEqual(result, {
title: "foo"
});
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get("conflict");
})
.then(function (result) {
deepEqual(result, {
title: "bar"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("local and remote document creations: continue, remote post",
function () {
stop();
expect(4);
var context = this;
this.jio = jIO.createJIO({
type: "replicate",
use_remote_post: 1,
local_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
},
remote_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
},
conflict_handling: 3
});
RSVP.all([
context.jio.put("conflict", {"title": "foo"}),
context.jio.__storage._remote_sub_storage.put("conflict",
{"title": "bar"})
])
.then(function () {
return context.jio.repair();
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get("conflict");
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
// equal(error.message, "Cannot find document: conflict");
equal(error.status_code, 404);
})
.then(function () {
return context.jio.get("conflict");
})
.then(function (result) {
deepEqual(result, {
title: "foo"
});
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get("conflict");
})
.then(function (result) {
deepEqual(result, {
title: "bar"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("local and remote same document creations", function () {
stop();
expect(1);
var context = this;
RSVP.all([
context.jio.put("conflict", {"title": "foo"}),
context.jio.__storage._remote_sub_storage.put("conflict",
{"title": "foo"})
])
.then(function () {
return context.jio.repair();
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get("conflict");
})
.then(function (result) {
deepEqual(result, {
hash: "5ea9013447539ad65de308cbd75b5826a2ae30e5"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("no modification", function () {
stop();
expect(2);
var id,
context = this;
context.jio.post({"title": "foo"})
.then(function (result) {
id = result;
return context.jio.repair();
})
.then(function () {
return context.jio.repair();
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
title: "foo"
});
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
hash: "5ea9013447539ad65de308cbd75b5826a2ae30e5"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("local document modification", function () {
stop();
expect(2);
var id,
context = this;
context.jio.post({"title": "foo"})
.then(function (result) {
id = result;
return context.jio.repair();
})
.then(function () {
return context.jio.put(id, {"title": "foo2"});
})
.then(function () {
return context.jio.repair();
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
title: "foo2"
});
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
hash: "9819187e39531fdc9bcfd40dbc6a7d3c78fe8dab"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("local document modification: use remote post", function () {
stop();
expect(2);
var id,
context = this;
this.jio = jIO.createJIO({
type: "replicate",
use_remote_post: true,
local_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
},
remote_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
}
});
context.jio.__storage._remote_sub_storage.post({"title": "foo"})
.then(function (result) {
id = result;
return context.jio.repair();
})
.then(function () {
return context.jio.put(id, {"title": "foo2"});
})
.then(function () {
return context.jio.repair();
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
title: "foo2"
});
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
hash: "9819187e39531fdc9bcfd40dbc6a7d3c78fe8dab"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("local document modification not checked", function () {
stop();
expect(3);
var id,
context = this;
this.jio = jIO.createJIO({
type: "replicate",
check_local_modification: false,
local_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
},
remote_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
}
});
context.jio.post({"title": "foo"})
.then(function (result) {
id = result;
return context.jio.repair();
})
.then(function () {
return context.jio.put(id, {"title": "foo2"});
})
.then(function () {
return context.jio.repair();
})
.then(function () {
return context.jio.get(id);
})
.then(function (result) {
deepEqual(result, {
title: "foo2"
});
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
title: "foo"
});
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
hash: "5ea9013447539ad65de308cbd75b5826a2ae30e5"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("remote document modification", function () {
stop();
expect(2);
var id,
context = this;
context.jio.post({"title": "foo"})
.then(function (result) {
id = result;
return context.jio.repair();
})
.then(function () {
return context.jio.__storage._remote_sub_storage.put(
id,
{"title": "foo3"}
);
})
.then(function () {
return context.jio.repair();
})
.then(function () {
return context.jio.get(id);
})
.then(function (result) {
deepEqual(result, {
title: "foo3"
});
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
hash: "4b1dde0f80ac38514771a9d25b5278e38f560e0f"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("remote document modification not checked", function () {
stop();
expect(3);
var id,
context = this;
this.jio = jIO.createJIO({
type: "replicate",
check_remote_modification: false,
local_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
},
remote_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
}
});
context.jio.post({"title": "foo"})
.then(function (result) {
id = result;
return context.jio.repair();
})
.then(function () {
return context.jio.__storage._remote_sub_storage.put(
id,
{"title": "foo3"}
);
})
.then(function () {
return context.jio.repair();
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
title: "foo3"
});
})
.then(function () {
return context.jio.get(id);
})
.then(function (result) {
deepEqual(result, {
title: "foo"
});
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
hash: "5ea9013447539ad65de308cbd75b5826a2ae30e5"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("local and remote document modifications", function () {
stop();
expect(4);
var id,
context = this;
context.jio.post({"title": "foo"})
.then(function (result) {
id = result;
return context.jio.repair();
})
.then(function () {
return RSVP.all([
context.jio.put(id, {"title": "foo4"}),
context.jio.__storage._remote_sub_storage.put(id, {"title": "foo5"})
]);
})
.then(function () {
return context.jio.repair();
})
.then(function () {
ok(false);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Conflict on '" + id + "': " +
"{\"title\":\"foo4\"} !== {\"title\":\"foo5\"}");
equal(error.status_code, 409);
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
hash: "5ea9013447539ad65de308cbd75b5826a2ae30e5"
});
})
.always(function () {
start();
});
});
test("local and remote document modifications: keep local", function () {
stop();
expect(3);
var id,
context = this;
this.jio = jIO.createJIO({
type: "replicate",
local_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
},
remote_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
},
conflict_handling: 1
});
context.jio.post({"title": "foo"})
.then(function (result) {
id = result;
return context.jio.repair();
})
.then(function () {
return RSVP.all([
context.jio.put(id, {"title": "foo4"}),
context.jio.__storage._remote_sub_storage.put(id, {"title": "foo5"})
]);
})
.then(function () {
return context.jio.repair();
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
hash: "6f700e813022233a785692585484c21cb5a412fd"
});
})
.then(function () {
return context.jio.get(id);
})
.then(function (result) {
deepEqual(result, {
title: "foo4"
});
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
title: "foo4"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("local and remote document modifications: keep remote", function () {
stop();
expect(3);
var id,
context = this;
this.jio = jIO.createJIO({
type: "replicate",
local_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
},
remote_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
},
conflict_handling: 2
});
context.jio.post({"title": "foo"})
.then(function (result) {
id = result;
return context.jio.repair();
})
.then(function () {
return RSVP.all([
context.jio.put(id, {"title": "foo4"}),
context.jio.__storage._remote_sub_storage.put(id, {"title": "foo5"})
]);
})
.then(function () {
return context.jio.repair();
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
hash: "7bea6f87fd1dda14e340e5b14836cc8578fd615f"
});
})
.then(function () {
return context.jio.get(id);
})
.then(function (result) {
deepEqual(result, {
title: "foo5"
});
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
title: "foo5"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("local and remote document modifications: continue", function () {
stop();
expect(3);
var id,
context = this;
this.jio = jIO.createJIO({
type: "replicate",
local_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
},
remote_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
},
conflict_handling: 3
});
context.jio.post({"title": "foo"})
.then(function (result) {
id = result;
return context.jio.repair();
})
.then(function () {
return RSVP.all([
context.jio.put(id, {"title": "foo4"}),
context.jio.__storage._remote_sub_storage.put(id, {"title": "foo5"})
]);
})
.then(function () {
return context.jio.repair();
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
hash: "5ea9013447539ad65de308cbd75b5826a2ae30e5"
});
})
.then(function () {
return context.jio.get(id);
})
.then(function (result) {
deepEqual(result, {
title: "foo4"
});
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
title: "foo5"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("local and remote document same modifications", function () {
stop();
expect(1);
var id,
context = this;
context.jio.post({"title": "foo"})
.then(function (result) {
id = result;
return context.jio.repair();
})
.then(function () {
return RSVP.all([
context.jio.put(id, {"title": "foo99"}),
context.jio.__storage._remote_sub_storage.put(id, {"title": "foo99"})
]);
})
.then(function () {
return context.jio.repair();
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
hash: "8ed3a474128b6e0c0c7d3dd51b1a06ebfbf6722f"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("local document deletion", function () {
stop();
expect(6);
var id,
context = this;
context.jio.post({"title": "foo"})
.then(function (result) {
id = result;
return context.jio.repair();
})
.then(function () {
return context.jio.remove(id);
})
.then(function () {
return context.jio.repair();
})
.then(function () {
ok(true, "Removal correctly synced");
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get(id);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find document: " + id);
equal(error.status_code, 404);
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(id)
.then(function () {
ok(false, "Signature should be deleted");
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
// equal(error.message, "Cannot find document: " + id);
equal(error.status_code, 404);
});
})
.always(function () {
start();
});
});
test("local document deletion not checked", function () {
stop();
expect(6);
var id,
context = this;
this.jio = jIO.createJIO({
type: "replicate",
check_local_deletion: false,
local_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
},
remote_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
}
});
context.jio.post({"title": "foo"})
.then(function (result) {
id = result;
return context.jio.repair();
})
.then(function () {
return context.jio.remove(id);
})
.then(function () {
return context.jio.repair();
})
.then(function () {
ok(true, "Removal correctly synced");
})
.then(function () {
return context.jio.get(id);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find document: " + id);
equal(error.status_code, 404);
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
title: "foo"
});
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
hash: "5ea9013447539ad65de308cbd75b5826a2ae30e5"
});
})
.always(function () {
start();
});
});
test("local document deletion with attachment", function () {
stop();
expect(7);
var id,
context = this,
blob = new Blob([big_string]);
context.jio.post({"title": "foo"})
.then(function (result) {
id = result;
return context.jio.repair();
})
.then(function () {
return context.jio.remove(id);
})
.then(function () {
return context.jio.__storage._remote_sub_storage
.putAttachment(id, 'foo', blob);
})
.then(function () {
return context.jio.repair();
})
.then(function () {
ok(true, "Removal correctly synced");
})
.then(function () {
return context.jio.get(id);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find document: " + id);
equal(error.status_code, 404);
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
title: "foo"
});
})
.then(function () {
return context.jio.__storage._remote_sub_storage
.getAttachment(id, 'foo', {format: 'text'});
})
.then(function (result) {
equal(result, big_string);
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
hash: "5ea9013447539ad65de308cbd75b5826a2ae30e5"
});
})
.always(function () {
start();
});
});
test("remote document deletion", function () {
stop();
expect(6);
var id,
context = this;
context.jio.post({"title": "foo"})
.then(function (result) {
id = result;
return context.jio.repair();
})
.then(function () {
return context.jio.__storage._remote_sub_storage.remove(id);
})
.then(function () {
return context.jio.repair();
})
.then(function () {
ok(true, "Removal correctly synced");
})
.then(function () {
return context.jio.get(id);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find document: " + id);
equal(error.status_code, 404);
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(id)
.then(function () {
ok(false, "Signature should be deleted");
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
// equal(error.message, "Cannot find document: " + id);
equal(error.status_code, 404);
});
})
.always(function () {
start();
});
});
test("remote document deletion not checked", function () {
stop();
expect(6);
var id,
context = this;
this.jio = jIO.createJIO({
type: "replicate",
check_remote_deletion: false,
local_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
},
remote_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
}
});
context.jio.post({"title": "foo"})
.then(function (result) {
id = result;
return context.jio.repair();
})
.then(function () {
return context.jio.__storage._remote_sub_storage.remove(id);
})
.then(function () {
return context.jio.repair();
})
.then(function () {
ok(true, "Removal correctly synced");
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get(id);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find document: " + id);
equal(error.status_code, 404);
})
.then(function () {
return context.jio.get(id);
})
.then(function (result) {
deepEqual(result, {
title: "foo"
});
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
hash: "5ea9013447539ad65de308cbd75b5826a2ae30e5"
});
})
.always(function () {
start();
});
});
test("remote document deletion with attachment", function () {
stop();
expect(7);
var id,
context = this,
blob = new Blob([big_string]);
context.jio.post({"title": "foo"})
.then(function (result) {
id = result;
return context.jio.repair();
})
.then(function () {
return context.jio.__storage._remote_sub_storage.remove(id);
})
.then(function () {
return context.jio.putAttachment(id, 'foo', blob);
})
.then(function () {
return context.jio.repair();
})
.then(function () {
ok(true, "Removal correctly synced");
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get(id);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find document: " + id);
equal(error.status_code, 404);
})
.then(function () {
return context.jio.get(id);
})
.then(function (result) {
deepEqual(result, {
title: "foo"
});
})
.then(function () {
return context.jio.getAttachment(id, 'foo', {format: 'text'});
})
.then(function (result) {
equal(result, big_string);
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
hash: "5ea9013447539ad65de308cbd75b5826a2ae30e5"
});
})
.always(function () {
start();
});
});
test("local and remote document deletions", function () {
stop();
expect(8);
var id,
context = this;
context.jio.post({"title": "foo"})
.then(function (result) {
id = result;
return context.jio.repair();
})
.then(function () {
return RSVP.all([
context.jio.remove(id),
context.jio.__storage._remote_sub_storage.remove(id)
]);
})
.then(function () {
return context.jio.repair();
})
.then(function () {
return context.jio.get(id)
.then(function () {
ok(false, "Document should be locally deleted");
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find document: " + id);
equal(error.status_code, 404);
});
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get(id)
.then(function () {
ok(false, "Document should be remotely deleted");
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find document: " + id);
equal(error.status_code, 404);
});
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(id)
.then(function () {
ok(false, "Signature should be deleted");
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
// equal(error.message, "Cannot find document: " + id);
equal(error.status_code, 404);
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("local deletion and remote modifications", function () {
stop();
expect(2);
var id,
context = this;
context.jio.post({"title": "foo"})
.then(function (result) {
id = result;
return context.jio.repair();
})
.then(function () {
return RSVP.all([
context.jio.remove(id),
context.jio.__storage._remote_sub_storage.put(id, {"title": "foo99"})
]);
})
.then(function () {
return context.jio.repair();
})
.then(function () {
return context.jio.get(id);
})
.then(function (result) {
deepEqual(result, {
title: "foo99"
});
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
hash: "8ed3a474128b6e0c0c7d3dd51b1a06ebfbf6722f"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("local deletion and remote modifications: keep local", function () {
stop();
expect(9);
var id,
context = this;
this.jio = jIO.createJIO({
type: "replicate",
conflict_handling: 1,
local_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
},
remote_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
}
});
context.jio.post({"title": "foo"})
.then(function (result) {
id = result;
return context.jio.repair();
})
.then(function () {
return RSVP.all([
context.jio.remove(id),
context.jio.__storage._remote_sub_storage.put(id, {"title": "foo99"})
]);
})
.then(function () {
return context.jio.repair();
})
.then(function () {
return context.jio.get(id);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find document: " + id);
equal(error.status_code, 404);
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get(id);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find document: " + id);
equal(error.status_code, 404);
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(id);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(
error.message.indexOf(
"Cannot find attachment: " +
"_replicate_b9296354cdf1dbe0046de11f57a5a24f8f6a78a8 , " +
"jio_document/"
),
0
);
equal(error.status_code, 404);
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("local deletion and remote modifications: keep remote", function () {
stop();
expect(3);
var id,
context = this;
this.jio = jIO.createJIO({
type: "replicate",
conflict_handling: 2,
local_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
},
remote_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
}
});
context.jio.post({"title": "foo"})
.then(function (result) {
id = result;
return context.jio.repair();
})
.then(function () {
return RSVP.all([
context.jio.remove(id),
context.jio.__storage._remote_sub_storage.put(id, {"title": "foo99"})
]);
})
.then(function () {
return context.jio.repair();
})
.then(function () {
return context.jio.get(id);
})
.then(function (result) {
deepEqual(result, {
title: "foo99"
});
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
title: "foo99"
});
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
hash: "8ed3a474128b6e0c0c7d3dd51b1a06ebfbf6722f"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("local deletion and remote modifications: ignore", function () {
stop();
expect(5);
var id,
context = this;
this.jio = jIO.createJIO({
type: "replicate",
conflict_handling: 3,
local_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
},
remote_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
}
});
context.jio.post({"title": "foo"})
.then(function (result) {
id = result;
return context.jio.repair();
})
.then(function () {
return RSVP.all([
context.jio.remove(id),
context.jio.__storage._remote_sub_storage.put(id, {"title": "foo99"})
]);
})
.then(function () {
return context.jio.repair();
})
.then(function () {
return context.jio.get(id);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find document: " + id);
equal(error.status_code, 404);
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
title: "foo99"
});
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
hash: "5ea9013447539ad65de308cbd75b5826a2ae30e5"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("local modifications and remote deletion", function () {
stop();
expect(2);
var id,
context = this;
context.jio.post({"title": "foo"})
.then(function (result) {
id = result;
return context.jio.repair();
})
.then(function () {
return RSVP.all([
context.jio.put(id, {"title": "foo99"}),
context.jio.__storage._remote_sub_storage.remove(id)
]);
})
.then(function () {
return context.jio.repair();
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
title: "foo99"
});
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
hash: "8ed3a474128b6e0c0c7d3dd51b1a06ebfbf6722f"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("local modifications and remote deletion: use remote post", function () {
stop();
expect(13);
var id,
context = this,
post_id;
this.jio = jIO.createJIO({
type: "replicate",
use_remote_post: true,
local_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
},
remote_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
}
});
context.jio.__storage._remote_sub_storage.post({"title": "foo"})
.then(function (result) {
id = result;
return context.jio.repair();
})
.then(function () {
return RSVP.all([
context.jio.put(id, {"title": "foo99"}),
context.jio.__storage._remote_sub_storage.remove(id)
]);
})
.then(function () {
return context.jio.repair();
})
// Old id deleted
.then(function () {
return context.jio.get(id);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find document: " + id);
equal(error.status_code, 404);
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get(id);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find document: " + id);
equal(error.status_code, 404);
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(id);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(
error.message.indexOf(
"Cannot find attachment: " +
"_replicate_b9296354cdf1dbe0046de11f57a5a24f8f6a78a8 , " +
"jio_document/"
),
0
);
equal(error.status_code, 404);
})
// Check new id
.then(function () {
return context.jio.allDocs({include_docs: true});
})
.then(function (result) {
equal(result.data.total_rows, 1);
post_id = result.data.rows[0].id;
return context.jio.__storage._remote_sub_storage.get(post_id);
})
.then(function (result) {
deepEqual(result, {
"title": "foo99"
});
})
.then(function () {
return context.jio.__storage._local_sub_storage.get(post_id);
})
.then(function (result) {
deepEqual(result, {
"title": "foo99"
});
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(post_id);
})
.then(function (result) {
deepEqual(result, {
hash: "8ed3a474128b6e0c0c7d3dd51b1a06ebfbf6722f"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("local modif, remote del: remote post, no check loc mod", function () {
stop();
expect(13);
var id,
context = this,
post_id;
this.jio = jIO.createJIO({
type: "replicate",
use_remote_post: true,
check_local_modification: false,
local_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
},
remote_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
}
});
context.jio.__storage._remote_sub_storage.post({"title": "foo"})
.then(function (result) {
id = result;
return context.jio.repair();
})
.then(function () {
return RSVP.all([
context.jio.put(id, {"title": "foo99"}),
context.jio.__storage._remote_sub_storage.remove(id)
]);
})
.then(function () {
return context.jio.repair();
})
// Old id deleted
.then(function () {
return context.jio.get(id);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find document: " + id);
equal(error.status_code, 404);
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get(id);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find document: " + id);
equal(error.status_code, 404);
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(id);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(
error.message.indexOf(
"Cannot find attachment: " +
"_replicate_b9296354cdf1dbe0046de11f57a5a24f8f6a78a8 , " +
"jio_document/"
),
0
);
equal(error.status_code, 404);
})
// Check new id
.then(function () {
return context.jio.allDocs({include_docs: true});
})
.then(function (result) {
equal(result.data.total_rows, 1);
post_id = result.data.rows[0].id;
return context.jio.__storage._remote_sub_storage.get(post_id);
})
.then(function (result) {
deepEqual(result, {
"title": "foo99"
});
})
.then(function () {
return context.jio.__storage._local_sub_storage.get(post_id);
})
.then(function (result) {
deepEqual(result, {
"title": "foo99"
});
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(post_id);
})
.then(function (result) {
deepEqual(result, {
hash: "8ed3a474128b6e0c0c7d3dd51b1a06ebfbf6722f"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("local modifications and remote deletion: keep local", function () {
stop();
expect(3);
var id,
context = this;
this.jio = jIO.createJIO({
type: "replicate",
conflict_handling: 1,
local_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
},
remote_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
}
});
context.jio.post({"title": "foo"})
.then(function (result) {
id = result;
return context.jio.repair();
})
.then(function () {
return RSVP.all([
context.jio.put(id, {"title": "foo99"}),
context.jio.__storage._remote_sub_storage.remove(id)
]);
})
.then(function () {
return context.jio.repair();
})
// Old id deleted
.then(function () {
return context.jio.get(id);
})
.then(function (result) {
deepEqual(result, {
"title": "foo99"
});
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
"title": "foo99"
});
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
hash: "8ed3a474128b6e0c0c7d3dd51b1a06ebfbf6722f"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("local modif and remote del: keep local, use remote post", function () {
stop();
expect(13);
var id,
context = this,
post_id;
this.jio = jIO.createJIO({
type: "replicate",
conflict_handling: 1,
use_remote_post: true,
local_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
},
remote_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
}
});
context.jio.__storage._remote_sub_storage.post({"title": "foo"})
.then(function (result) {
id = result;
return context.jio.repair();
})
.then(function () {
return RSVP.all([
context.jio.put(id, {"title": "foo99"}),
context.jio.__storage._remote_sub_storage.remove(id)
]);
})
.then(function () {
return context.jio.repair();
})
// Old id deleted
.then(function () {
return context.jio.get(id);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find document: " + id);
equal(error.status_code, 404);
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get(id);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find document: " + id);
equal(error.status_code, 404);
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(id);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(
error.message.indexOf(
"Cannot find attachment: " +
"_replicate_b9296354cdf1dbe0046de11f57a5a24f8f6a78a8 , " +
"jio_document/"
),
0
);
equal(error.status_code, 404);
})
// Check new id
.then(function () {
return context.jio.allDocs({include_docs: true});
})
.then(function (result) {
equal(result.data.total_rows, 1);
post_id = result.data.rows[0].id;
return context.jio.__storage._remote_sub_storage.get(post_id);
})
.then(function (result) {
deepEqual(result, {
"title": "foo99"
});
})
.then(function () {
return context.jio.__storage._local_sub_storage.get(post_id);
})
.then(function (result) {
deepEqual(result, {
"title": "foo99"
});
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(post_id);
})
.then(function (result) {
deepEqual(result, {
hash: "8ed3a474128b6e0c0c7d3dd51b1a06ebfbf6722f"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("local modifications and remote deletion: keep remote", function () {
stop();
expect(9);
var id,
context = this;
this.jio = jIO.createJIO({
type: "replicate",
conflict_handling: 2,
local_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
},
remote_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
}
});
context.jio.post({"title": "foo"})
.then(function (result) {
id = result;
return context.jio.repair();
})
.then(function () {
return RSVP.all([
context.jio.put(id, {"title": "foo99"}),
context.jio.__storage._remote_sub_storage.remove(id)
]);
})
.then(function () {
return context.jio.repair();
})
// id deleted
.then(function () {
return context.jio.get(id);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find document: " + id);
equal(error.status_code, 404);
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get(id);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find document: " + id);
equal(error.status_code, 404);
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(id);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(
error.message.indexOf(
"Cannot find attachment: " +
"_replicate_b9296354cdf1dbe0046de11f57a5a24f8f6a78a8 , " +
"jio_document/"
),
0
);
equal(error.status_code, 404);
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("local modif and remote del: keep remote, not check modif", function () {
stop();
expect(9);
var id,
context = this;
this.jio = jIO.createJIO({
type: "replicate",
conflict_handling: 2,
check_local_modification: false,
local_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
},
remote_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
}
});
context.jio.post({"title": "foo"})
.then(function (result) {
id = result;
return context.jio.repair();
})
.then(function () {
return RSVP.all([
context.jio.put(id, {"title": "foo99"}),
context.jio.__storage._remote_sub_storage.remove(id)
]);
})
.then(function () {
return context.jio.repair();
})
// id deleted
.then(function () {
return context.jio.get(id);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find document: " + id);
equal(error.status_code, 404);
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get(id);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find document: " + id);
equal(error.status_code, 404);
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(id);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(
error.message.indexOf(
"Cannot find attachment: " +
"_replicate_b9296354cdf1dbe0046de11f57a5a24f8f6a78a8 , " +
"jio_document/"
),
0
);
equal(error.status_code, 404);
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("local modifications and remote deletion: ignore", function () {
stop();
expect(5);
var id,
context = this;
this.jio = jIO.createJIO({
type: "replicate",
conflict_handling: 3,
local_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
},
remote_sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
}
});
context.jio.post({"title": "foo"})
.then(function (result) {
id = result;
return context.jio.repair();
})
.then(function () {
return RSVP.all([
context.jio.put(id, {"title": "foo99"}),
context.jio.__storage._remote_sub_storage.remove(id)
]);
})
.then(function () {
return context.jio.repair();
})
// id deleted
.then(function () {
return context.jio.get(id);
})
.then(function (result) {
deepEqual(result, {
"title": "foo99"
});
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get(id);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find document: " + id);
equal(error.status_code, 404);
})
.then(function () {
return context.jio.__storage._signature_sub_storage.get(id);
})
.then(function (result) {
deepEqual(result, {
hash: "5ea9013447539ad65de308cbd75b5826a2ae30e5"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("signature document is not synced", function () {
stop();
expect(6);
var context = this;
// Uses sessionstorage substorage, so that signature are stored
// in the same local sub storage
this.jio = jIO.createJIO({
type: "replicate",
local_sub_storage: {
type: "uuid",
sub_storage: {
type: "document",
document_id: "/",
sub_storage: {
type: "local",
sessiononly: true
}
}
},
remote_sub_storage: {
type: "memory"
}
});
context.jio.post({"title": "foo"})
.then(function () {
return context.jio.repair();
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get(
context.jio.__storage._signature_hash
);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find document: " +
"_replicate_8662994dcefb3a2ceec61e86953efda8ec6520d6");
equal(error.status_code, 404);
})
.then(function () {
return context.jio.repair();
})
.then(function () {
return context.jio.__storage._remote_sub_storage.get(
context.jio.__storage._signature_hash
);
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find document: " +
"_replicate_8662994dcefb3a2ceec61e86953efda8ec6520d6");
equal(error.status_code, 404);
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("substorages are repaired too", function () {
stop();
expect(8);
var context = this,
first_call = true,
options = {foo: "bar"};
function Storage200CheckRepair() {
return this;
}
Storage200CheckRepair.prototype.get = function () {
ok(true, "get 200 check repair called");
return {};
};
Storage200CheckRepair.prototype.hasCapacity = function () {
return true;
};
Storage200CheckRepair.prototype.buildQuery = function () {
ok(true, "buildQuery 200 check repair called");
return [];
};
Storage200CheckRepair.prototype.allAttachments = function () {
ok(true, "allAttachments 200 check repair called");
return {};
};
Storage200CheckRepair.prototype.repair = function (kw) {
if (first_call) {
deepEqual(
this,
context.jio.__storage._local_sub_storage.__storage,
"local substorage repair"
);
first_call = false;
} else {
deepEqual(
this,
context.jio.__storage._remote_sub_storage.__storage,
"remote substorage repair"
);
}
deepEqual(kw, options, "substorage repair parameters provided");
};
jIO.addStorage(
'replicatestorage200chechrepair',
Storage200CheckRepair
);
this.jio = jIO.createJIO({
type: "replicate",
local_sub_storage: {
type: "replicatestorage200chechrepair"
},
remote_sub_storage: {
type: "replicatestorage200chechrepair"
}
});
context.jio.repair(options)
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("sync all documents by default", function () {
stop();
expect(4);
var context = this;
function Storage200DefaultQuery() {
return this;
}
Storage200DefaultQuery.prototype.get = function () {
ok(true, "get 200 check repair called");
return {};
};
Storage200DefaultQuery.prototype.hasCapacity = function () {
return true;
};
Storage200DefaultQuery.prototype.buildQuery = function (query) {
deepEqual(query, {});
return [];
};
Storage200DefaultQuery.prototype.allAttachments = function () {
ok(true, "allAttachments 200 check repair called");
return {};
};
jIO.addStorage(
'replicatestorage200defaultquery',
Storage200DefaultQuery
);
this.jio = jIO.createJIO({
type: "replicate",
local_sub_storage: {
type: "replicatestorage200defaultquery"
},
remote_sub_storage: {
type: "replicatestorage200defaultquery"
}
});
return context.jio.repair()
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("sync can be restricted to some documents", function () {
stop();
expect(4);
var context = this,
query = {query: 'portal_type: "Foo"', limit: [0, 1234567890]};
function Storage200CustomQuery() {
return this;
}
Storage200CustomQuery.prototype.get = function () {
ok(true, "get 200 check repair called");
return {};
};
Storage200CustomQuery.prototype.hasCapacity = function () {
return true;
};
Storage200CustomQuery.prototype.buildQuery = function (options) {
deepEqual(options, query);
return [];
};
Storage200CustomQuery.prototype.allAttachments = function () {
ok(true, "allAttachments 200 check repair called");
return {};
};
jIO.addStorage(
'replicatestorage200customquery',
Storage200CustomQuery
);
this.jio = jIO.createJIO({
type: "replicate",
local_sub_storage: {
type: "replicatestorage200customquery"
},
remote_sub_storage: {
type: "replicatestorage200customquery"
},
query: {query: 'portal_type: "Foo"', limit: [0, 1234567890]}
});
return context.jio.repair()
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("use 1 parallel operation", function () {
stop();
expect(16);
var context = this,
order_number = 0,
expected_order_list = [
'start get 0',
'stop get 0',
'start put 0',
'stop put 0',
'start get 1',
'stop get 1',
'start put 1',
'stop put 1',
'start get 2',
'stop get 2',
'start put 2',
'stop put 2',
'start get 3',
'stop get 3',
'start put 3',
'stop put 3'
];
function assertExecutionOrder(text) {
equal(text, expected_order_list[order_number],
expected_order_list[order_number]);
order_number += 1;
}
function StorageOneParallelOperation() {
this._sub_storage = jIO.createJIO({type: "memory"});
return this;
}
StorageOneParallelOperation.prototype.put = function (id, doc) {
assertExecutionOrder('start put ' + id);
var storage = this;
return storage._sub_storage.put(id, doc)
.push(function (result) {
assertExecutionOrder('stop put ' + id);
return result;
});
};
StorageOneParallelOperation.prototype.get = function (id) {
assertExecutionOrder('start get ' + id);
var storage = this;
return storage._sub_storage.get(id)
.push(undefined, function (error) {
assertExecutionOrder('stop get ' + id);
throw error;
});
};
StorageOneParallelOperation.prototype.buildQuery = function () {
return this._sub_storage.buildQuery.apply(this._sub_storage, arguments);
};
StorageOneParallelOperation.prototype.hasCapacity = function () {
return this._sub_storage.hasCapacity.apply(this._sub_storage, arguments);
};
jIO.addStorage(
'one_parallel',
StorageOneParallelOperation
);
this.jio = jIO.createJIO({
type: "replicate",
local_sub_storage: {
type: "memory"
},
remote_sub_storage: {
type: "one_parallel"
}
});
return RSVP.all([
context.jio.put("0", {"title": "foo"}),
context.jio.put("1", {"title": "foo1"}),
context.jio.put("2", {"title": "foo2"}),
context.jio.put("3", {"title": "foo3"})
])
.then(function () {
return context.jio.repair();
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("use 2 parallel operations", function () {
stop();
expect(16);
var context = this,
order_number = 0,
expected_order_list = [
'start get 0',
'start get 1',
'stop get 1',
'start put 1',
'stop put 1',
'start get 2',
'stop get 2',
'start put 2',
'stop get 0',
'start put 0',
'stop put 0',
'start get 3',
'stop get 3',
'start put 3',
'stop put 3',
'stop put 2'
],
defer0,
defer2;
function assertExecutionOrder(text) {
equal(text, expected_order_list[order_number],
expected_order_list[order_number]);
order_number += 1;
}
function StorageTwoParallelOperation() {
this._sub_storage = jIO.createJIO({type: "memory"});
return this;
}
StorageTwoParallelOperation.prototype.put = function (id, doc) {
var storage = this;
assertExecutionOrder('start put ' + id);
return new RSVP.Queue()
.push(function () {
if (id === "2") {
defer0.resolve();
defer2 = RSVP.defer();
return defer2.promise;
}
})
.push(function () {
return storage._sub_storage.put(id, doc);
})
.push(function (result) {
if (id === "3") {
defer2.resolve();
}
assertExecutionOrder('stop put ' + id);
return result;
});
};
StorageTwoParallelOperation.prototype.get = function (id) {
var storage = this;
assertExecutionOrder('start get ' + id);
return new RSVP.Queue()
.push(function () {
if (id === "0") {
defer0 = RSVP.defer();
return defer0.promise;
}
})
.push(function () {
return storage._sub_storage.get(id);
})
.push(undefined, function (error) {
assertExecutionOrder('stop get ' + id);
throw error;
});
};
StorageTwoParallelOperation.prototype.buildQuery = function () {
return this._sub_storage.buildQuery.apply(this._sub_storage, arguments);
};
StorageTwoParallelOperation.prototype.hasCapacity = function () {
return this._sub_storage.hasCapacity.apply(this._sub_storage, arguments);
};
jIO.addStorage(
'two_parallel',
StorageTwoParallelOperation
);
this.jio = jIO.createJIO({
type: "replicate",
parallel_operation_amount: 2,
local_sub_storage: {
type: "memory"
},
remote_sub_storage: {
type: "two_parallel"
}
});
return RSVP.all([
context.jio.put("0", {"title": "foo"}),
context.jio.put("1", {"title": "foo1"}),
context.jio.put("2", {"title": "foo2"}),
context.jio.put("3", {"title": "foo3"})
])
.then(function () {
return context.jio.repair();
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("use 4 parallel operations", function () {
stop();
expect(16);
var context = this,
order_number = 0,
expected_order_list = [
'start get 0',
'start get 1',
'start get 2',
'start get 3',
'stop get 0',
'stop get 1',
'stop get 2',
'stop get 3',
'start put 0',
'start put 1',
'start put 2',
'start put 3',
'stop put 0',
'stop put 1',
'stop put 2',
'stop put 3'
];
function assertExecutionOrder(text) {
equal(text, expected_order_list[order_number],
expected_order_list[order_number]);
order_number += 1;
}
function StorageFourParallelOperation() {
this._sub_storage = jIO.createJIO({type: "memory"});
return this;
}
StorageFourParallelOperation.prototype.put = function (id, doc) {
assertExecutionOrder('start put ' + id);
var storage = this;
return storage._sub_storage.put(id, doc)
.push(function (result) {
assertExecutionOrder('stop put ' + id);
return result;
});
};
StorageFourParallelOperation.prototype.get = function (id) {
assertExecutionOrder('start get ' + id);
var storage = this;
return storage._sub_storage.get(id)
.push(undefined, function (error) {
assertExecutionOrder('stop get ' + id);
throw error;
});
};
StorageFourParallelOperation.prototype.buildQuery = function () {
return this._sub_storage.buildQuery.apply(this._sub_storage, arguments);
};
StorageFourParallelOperation.prototype.hasCapacity = function () {
return this._sub_storage.hasCapacity.apply(this._sub_storage, arguments);
};
jIO.addStorage(
'four_parallel',
StorageFourParallelOperation
);
this.jio = jIO.createJIO({
type: "replicate",
parallel_operation_amount: 4,
local_sub_storage: {
type: "memory"
},
remote_sub_storage: {
type: "four_parallel"
}
});
return RSVP.all([
context.jio.put("0", {"title": "foo"}),
context.jio.put("1", {"title": "foo1"}),
context.jio.put("2", {"title": "foo2"}),
context.jio.put("3", {"title": "foo3"})
])
.then(function () {
return context.jio.repair();
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
}(jIO, QUnit, Blob, RSVP));
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -39,6 +39,9 @@ ...@@ -39,6 +39,9 @@
<script src="jio.storage/indexeddbstorage.tests.js"></script> <script src="jio.storage/indexeddbstorage.tests.js"></script>
<script src="jio.storage/uuidstorage.tests.js"></script> <script src="jio.storage/uuidstorage.tests.js"></script>
<script src="jio.storage/replicatestorage.tests.js"></script> <script src="jio.storage/replicatestorage.tests.js"></script>
<script src="jio.storage/replicatestorage_repair.tests.js"></script>
<script src="jio.storage/replicatestorage_repairattachment.tests.js"></script>
<script src="jio.storage/replicatestorage_fastrepair.tests.js"></script>
<script src="jio.storage/shastorage.tests.js"></script> <script src="jio.storage/shastorage.tests.js"></script>
<script src="jio.storage/mappingstorage.tests.js"></script> <script src="jio.storage/mappingstorage.tests.js"></script>
<!--script src="jio.storage/qiniustorage.tests.js"></script--> <!--script src="jio.storage/qiniustorage.tests.js"></script-->
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment