Commit d00c7a79 authored by Vincent Bechu's avatar Vincent Bechu

[erp5_core] Release Jio 3.22.1

/reviewed-on !409
parent c176ff4a
...@@ -5716,7 +5716,7 @@ case 5: case 8: case 11: case 14: case 16: ...@@ -5716,7 +5716,7 @@ case 5: case 8: case 11: case 14: case 16:
this.$ = $$[$0]; this.$ = $$[$0];
break; break;
case 6: case 6:
this.$ = mkComplexQuery('OR', [$$[$0-1], $$[$0]]); this.$ = mkComplexQuery('AND', [$$[$0-1], $$[$0]]);
break; break;
case 7: case 7:
this.$ = mkComplexQuery('OR', [$$[$0-2], $$[$0]]); this.$ = mkComplexQuery('OR', [$$[$0-2], $$[$0]]);
...@@ -6683,8 +6683,8 @@ return new Parser; ...@@ -6683,8 +6683,8 @@ return new Parser;
return new RegExp("^" + stringEscapeRegexpCharacters(string) + "$"); return new RegExp("^" + stringEscapeRegexpCharacters(string) + "$");
} }
return new RegExp("^" + stringEscapeRegexpCharacters(string) return new RegExp("^" + stringEscapeRegexpCharacters(string)
.replace(regexp_percent, '.*') .replace(regexp_percent, '[\\s\\S]*')
.replace(regexp_underscore, '.') + "$"); .replace(regexp_underscore, '.') + "$", "i");
} }
/** /**
...@@ -6985,7 +6985,8 @@ return new Parser; ...@@ -6985,7 +6985,8 @@ return new Parser;
matchMethod = null, matchMethod = null,
operator = this.operator, operator = this.operator,
value = null, value = null,
key = this.key; key = this.key,
k;
if (!(regexp_comparaison.test(operator))) { if (!(regexp_comparaison.test(operator))) {
// `operator` is not correct, we have to change it to "like" or "=" // `operator` is not correct, we have to change it to "like" or "="
...@@ -7004,6 +7005,22 @@ return new Parser; ...@@ -7004,6 +7005,22 @@ return new Parser;
key = this._key_schema.key_set[key]; key = this._key_schema.key_set[key];
} }
// match with all the fields if key is empty
if (key === '') {
matchMethod = this.like;
value = '%' + this.value + '%';
for (k in item) {
if (item.hasOwnProperty(k)) {
if (k !== '__id' && item[k]) {
if (matchMethod(item[k], value) === true) {
return true;
}
}
}
}
return false;
}
if (typeof key === 'object') { if (typeof key === 'object') {
checkKey(key); checkKey(key);
object_value = item[key.read_from]; object_value = item[key.read_from];
...@@ -8428,6 +8445,15 @@ return new Parser; ...@@ -8428,6 +8445,15 @@ return new Parser;
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.
...@@ -8446,20 +8472,33 @@ return new Parser; ...@@ -8446,20 +8472,33 @@ return new Parser;
function ReplicateStorage(spec) { function ReplicateStorage(spec) {
this._query_options = spec.query || {}; this._query_options = spec.query || {};
if (spec.signature_hash_key !== undefined) {
this._query_options.select_list = [spec.signature_hash_key];
}
this._signature_hash_key = spec.signature_hash_key;
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);
if (spec.hasOwnProperty('signature_sub_storage')) {
this._signature_sub_storage = jIO.createJIO(spec.signature_sub_storage);
this._custom_signature_sub_storage = true;
} else {
this._signature_hash = "_replicate_" + generateHash( this._signature_hash = "_replicate_" + generateHash(
stringify(spec.local_sub_storage) + stringify(spec.local_sub_storage) +
stringify(spec.remote_sub_storage) + stringify(spec.remote_sub_storage) +
stringify(this._query_options) stringify(this._query_options)
); );
this._signature_sub_storage = jIO.createJIO({ this._signature_sub_storage = jIO.createJIO({
type: "query",
sub_storage: {
type: "document", type: "document",
document_id: this._signature_hash, document_id: this._signature_hash,
sub_storage: spec.signature_storage || spec.local_sub_storage 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
...@@ -8602,15 +8641,8 @@ return new Parser; ...@@ -8602,15 +8641,8 @@ return new Parser;
arguments); arguments);
}; };
ReplicateStorage.prototype.repair = function () { function dispatchQueue(context, function_used, argument_list,
var context = this, number_queue) {
argument_list = arguments,
skip_document_dict = {};
// 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 = [], var result_promise_list = [],
i; i;
...@@ -8637,7 +8669,27 @@ return new Parser; ...@@ -8637,7 +8669,27 @@ return new Parser;
return result_promise_list[0]; return result_promise_list[0];
} }
function propagateAttachmentDeletion(skip_attachment_dict, function callAllDocsOnStorage(context, storage, cache, cache_key) {
return new RSVP.Queue()
.push(function () {
if (!cache.hasOwnProperty(cache_key)) {
return storage.allDocs(context._query_options)
.push(function (result) {
var i,
cache_entry = {};
for (i = 0; i < result.data.total_rows; i += 1) {
cache_entry[result.data.rows[i].id] = result.data.rows[i].value;
}
cache[cache_key] = cache_entry;
});
}
})
.push(function () {
return cache[cache_key];
});
}
function propagateAttachmentDeletion(context, skip_attachment_dict,
destination, destination,
id, name) { id, name) {
return destination.removeAttachment(id, name) return destination.removeAttachment(id, name)
...@@ -8649,7 +8701,7 @@ return new Parser; ...@@ -8649,7 +8701,7 @@ return new Parser;
}); });
} }
function propagateAttachmentModification(skip_attachment_dict, function propagateAttachmentModification(context, skip_attachment_dict,
destination, destination,
blob, hash, id, name) { blob, hash, id, name) {
return destination.putAttachment(id, name, blob) return destination.putAttachment(id, name, blob)
...@@ -8664,7 +8716,8 @@ return new Parser; ...@@ -8664,7 +8716,8 @@ return new Parser;
}); });
} }
function checkAndPropagateAttachment(skip_attachment_dict, function checkAndPropagateAttachment(context,
skip_attachment_dict,
status_hash, local_hash, blob, status_hash, local_hash, blob,
source, destination, id, name, source, destination, id, name,
conflict_force, conflict_revert, conflict_force, conflict_revert,
...@@ -8694,7 +8747,7 @@ return new Parser; ...@@ -8694,7 +8747,7 @@ return new Parser;
// Deleted on both side, drop signature // Deleted on both side, drop signature
return context._signature_sub_storage.removeAttachment(id, name) return context._signature_sub_storage.removeAttachment(id, name)
.push(function () { .push(function () {
skip_attachment_dict[id] = null; skip_attachment_dict[name] = null;
}); });
} }
...@@ -8703,7 +8756,7 @@ return new Parser; ...@@ -8703,7 +8756,7 @@ return new Parser;
hash: local_hash hash: local_hash
})) }))
.push(function () { .push(function () {
skip_document_dict[id] = null; skip_attachment_dict[name] = null;
}); });
} }
...@@ -8711,11 +8764,12 @@ return new Parser; ...@@ -8711,11 +8764,12 @@ return new Parser;
// Modified only locally. No conflict or force // Modified only locally. No conflict or force
if (local_hash === null) { if (local_hash === null) {
// Deleted locally // Deleted locally
return propagateAttachmentDeletion(skip_attachment_dict, return propagateAttachmentDeletion(context, skip_attachment_dict,
destination, destination,
id, name); id, name);
} }
return propagateAttachmentModification(skip_attachment_dict, return propagateAttachmentModification(context,
skip_attachment_dict,
destination, blob, destination, blob,
local_hash, id, name); local_hash, id, name);
} }
...@@ -8729,10 +8783,11 @@ return new Parser; ...@@ -8729,10 +8783,11 @@ return new Parser;
// Automatically resolve conflict or force revert // Automatically resolve conflict or force revert
if (remote_hash === null) { if (remote_hash === null) {
// Deleted remotely // Deleted remotely
return propagateAttachmentDeletion(skip_attachment_dict, return propagateAttachmentDeletion(context, skip_attachment_dict,
source, id, name); source, id, name);
} }
return propagateAttachmentModification( return propagateAttachmentModification(
context,
skip_attachment_dict, skip_attachment_dict,
source, source,
remote_blob, remote_blob,
...@@ -8745,7 +8800,8 @@ return new Parser; ...@@ -8745,7 +8800,8 @@ return new Parser;
// Minimize conflict if it can be resolved // Minimize conflict if it can be resolved
if (remote_hash === null) { if (remote_hash === null) {
// Copy remote modification remotely // Copy remote modification remotely
return propagateAttachmentModification(skip_attachment_dict, return propagateAttachmentModification(context,
skip_attachment_dict,
destination, blob, destination, blob,
local_hash, id, name); local_hash, id, name);
} }
...@@ -8756,7 +8812,8 @@ return new Parser; ...@@ -8756,7 +8812,8 @@ return new Parser;
}); });
} }
function checkAttachmentSignatureDifference(queue, skip_attachment_dict, function checkAttachmentSignatureDifference(queue, context,
skip_attachment_dict,
source, source,
destination, id, name, destination, id, name,
conflict_force, conflict_force,
...@@ -8798,7 +8855,8 @@ return new Parser; ...@@ -8798,7 +8855,8 @@ return new Parser;
local_hash = generateHashFromArrayBuffer(array_buffer); local_hash = generateHashFromArrayBuffer(array_buffer);
if (local_hash !== status_hash) { if (local_hash !== status_hash) {
return checkAndPropagateAttachment(skip_attachment_dict, return checkAndPropagateAttachment(context,
skip_attachment_dict,
status_hash, local_hash, blob, status_hash, local_hash, blob,
source, destination, id, name, source, destination, id, name,
conflict_force, conflict_revert, conflict_force, conflict_revert,
...@@ -8807,7 +8865,8 @@ return new Parser; ...@@ -8807,7 +8865,8 @@ return new Parser;
}); });
} }
function checkAttachmentLocalDeletion(queue, skip_attachment_dict, function checkAttachmentLocalDeletion(queue, context,
skip_attachment_dict,
destination, id, name, source, destination, id, name, source,
conflict_force, conflict_revert, conflict_force, conflict_revert,
conflict_ignore) { conflict_ignore) {
...@@ -8819,7 +8878,8 @@ return new Parser; ...@@ -8819,7 +8878,8 @@ return new Parser;
}) })
.push(function (result) { .push(function (result) {
status_hash = result.hash; status_hash = result.hash;
return checkAndPropagateAttachment(skip_attachment_dict, return checkAndPropagateAttachment(context,
skip_attachment_dict,
status_hash, null, null, status_hash, null, null,
source, destination, id, name, source, destination, id, name,
conflict_force, conflict_revert, conflict_force, conflict_revert,
...@@ -8827,24 +8887,13 @@ return new Parser; ...@@ -8827,24 +8887,13 @@ return new Parser;
}); });
} }
function pushDocumentAttachment(skip_attachment_dict, id, source, function pushDocumentAttachment(context,
destination, options) { skip_attachment_dict, id, source,
var queue = new RSVP.Queue(), destination, signature_allAttachments,
local_dict = {}, options) {
var local_dict = {},
signature_dict = {}; signature_dict = {};
return source.allAttachments(id)
return queue
.push(function () {
return RSVP.all([
source.allAttachments(id)
.push(undefined, function (error) {
if ((error instanceof jIO.util.jIOError) &&
(error.status_code === 404)) {
return {};
}
throw error;
}),
context._signature_sub_storage.allAttachments(id)
.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)) {
...@@ -8852,22 +8901,20 @@ return new Parser; ...@@ -8852,22 +8901,20 @@ return new Parser;
} }
throw error; throw error;
}) })
]); .push(function (source_allAttachments) {
})
.push(function (result_list) {
var is_modification, var is_modification,
is_creation, is_creation,
key, key,
argument_list = []; argument_list = [];
for (key in result_list[0]) { for (key in source_allAttachments) {
if (result_list[0].hasOwnProperty(key)) { if (source_allAttachments.hasOwnProperty(key)) {
if (!skip_attachment_dict.hasOwnProperty(key)) { if (!skip_attachment_dict.hasOwnProperty(key)) {
local_dict[key] = null; local_dict[key] = null;
} }
} }
} }
for (key in result_list[1]) { for (key in signature_allAttachments) {
if (result_list[1].hasOwnProperty(key)) { if (signature_allAttachments.hasOwnProperty(key)) {
if (!skip_attachment_dict.hasOwnProperty(key)) { if (!skip_attachment_dict.hasOwnProperty(key)) {
signature_dict[key] = null; signature_dict[key] = null;
} }
...@@ -8882,6 +8929,7 @@ return new Parser; ...@@ -8882,6 +8929,7 @@ return new Parser;
&& options.check_creation; && options.check_creation;
if (is_modification === true || is_creation === true) { if (is_modification === true || is_creation === true) {
argument_list.push([undefined, argument_list.push([undefined,
context,
skip_attachment_dict, skip_attachment_dict,
source, source,
destination, id, key, destination, id, key,
...@@ -8894,6 +8942,7 @@ return new Parser; ...@@ -8894,6 +8942,7 @@ return new Parser;
} }
} }
return dispatchQueue( return dispatchQueue(
context,
checkAttachmentSignatureDifference, checkAttachmentSignatureDifference,
argument_list, argument_list,
context._parallel_operation_attachment_amount context._parallel_operation_attachment_amount
...@@ -8906,6 +8955,7 @@ return new Parser; ...@@ -8906,6 +8955,7 @@ return new Parser;
if (signature_dict.hasOwnProperty(key)) { if (signature_dict.hasOwnProperty(key)) {
if (!local_dict.hasOwnProperty(key)) { if (!local_dict.hasOwnProperty(key)) {
argument_list.push([undefined, argument_list.push([undefined,
context,
skip_attachment_dict, skip_attachment_dict,
destination, id, key, destination, id, key,
source, source,
...@@ -8916,6 +8966,7 @@ return new Parser; ...@@ -8916,6 +8966,7 @@ return new Parser;
} }
} }
return dispatchQueue( return dispatchQueue(
context,
checkAttachmentLocalDeletion, checkAttachmentLocalDeletion,
argument_list, argument_list,
context._parallel_operation_attachment_amount context._parallel_operation_attachment_amount
...@@ -8924,19 +8975,215 @@ return new Parser; ...@@ -8924,19 +8975,215 @@ return new Parser;
}); });
} }
function propagateFastAttachmentDeletion(queue, id, name, storage) {
return queue
.push(function () {
return storage.removeAttachment(id, name);
});
}
function propagateFastAttachmentModification(queue, id, key, source,
destination, signature, hash) {
return queue
.push(function () {
return signature.getAttachment(id, key, {format: 'json'})
.push(undefined, function (error) {
if ((error instanceof jIO.util.jIOError) &&
(error.status_code === 404)) {
return {hash: null};
}
throw error;
})
.push(function (result) {
if (result.hash !== hash) {
return source.getAttachment(id, key)
.push(function (blob) {
return destination.putAttachment(id, key, blob);
})
.push(function () {
return signature.putAttachment(id, key, JSON.stringify({
hash: hash
}));
});
}
});
});
}
function repairFastDocumentAttachment(context, id,
signature_hash,
signature_attachment_hash,
signature_from_local) {
if (signature_hash === signature_attachment_hash) {
// No replication to do
return;
}
return new RSVP.Queue()
.push(function () {
return RSVP.all([
context._signature_sub_storage.allAttachments(id),
context._local_sub_storage.allAttachments(id),
context._remote_sub_storage.allAttachments(id)
]);
})
.push(function (result_list) {
var key,
source_attachment_dict,
destination_attachment_dict,
source,
destination,
push_argument_list = [],
delete_argument_list = [],
signature_attachment_dict = result_list[0],
local_attachment_dict = result_list[1],
remote_attachment_list = result_list[2],
check_local_modification =
context._check_local_attachment_modification,
check_local_creation = context._check_local_attachment_creation,
check_local_deletion = context._check_local_attachment_deletion,
check_remote_modification =
context._check_remote_attachment_modification,
check_remote_creation = context._check_remote_attachment_creation,
check_remote_deletion = context._check_remote_attachment_deletion;
if (signature_from_local) {
source_attachment_dict = local_attachment_dict;
destination_attachment_dict = remote_attachment_list;
source = context._local_sub_storage;
destination = context._remote_sub_storage;
} else {
source_attachment_dict = remote_attachment_list;
destination_attachment_dict = local_attachment_dict;
source = context._remote_sub_storage;
destination = context._local_sub_storage;
check_local_modification = check_remote_modification;
check_local_creation = check_remote_creation;
check_local_deletion = check_remote_deletion;
check_remote_creation = check_local_creation;
check_remote_deletion = check_local_deletion;
}
// Push all source attachments
for (key in source_attachment_dict) {
if (source_attachment_dict.hasOwnProperty(key)) {
if ((check_local_creation &&
!signature_attachment_dict.hasOwnProperty(key)) ||
(check_local_modification &&
signature_attachment_dict.hasOwnProperty(key))) {
push_argument_list.push([
undefined,
id,
key,
source,
destination,
context._signature_sub_storage,
signature_hash
]);
}
}
}
// Delete remaining signature + remote attachments
for (key in signature_attachment_dict) {
if (signature_attachment_dict.hasOwnProperty(key)) {
if (check_local_deletion &&
!source_attachment_dict.hasOwnProperty(key)) {
delete_argument_list.push([
undefined,
id,
key,
context._signature_sub_storage
]);
}
}
}
for (key in destination_attachment_dict) {
if (destination_attachment_dict.hasOwnProperty(key)) {
if (!source_attachment_dict.hasOwnProperty(key)) {
if ((check_local_deletion &&
signature_attachment_dict.hasOwnProperty(key)) ||
(check_remote_creation &&
!signature_attachment_dict.hasOwnProperty(key))) {
delete_argument_list.push([
undefined,
id,
key,
destination
]);
}
}
}
}
return RSVP.all([
dispatchQueue(
context,
propagateFastAttachmentModification,
push_argument_list,
context._parallel_operation_attachment_amount
),
dispatchQueue(
context,
propagateFastAttachmentDeletion,
delete_argument_list,
context._parallel_operation_attachment_amount
)
]);
})
.push(function () {
// Mark that all attachments have been synchronized
return context._signature_sub_storage.put(id, {
hash: signature_hash,
attachment_hash: signature_hash,
from_local: signature_from_local
});
});
}
function repairDocumentAttachment(context, id, signature_hash_key,
signature_hash,
signature_attachment_hash,
signature_from_local) {
if (signature_hash_key !== undefined) {
return repairFastDocumentAttachment(context, id,
signature_hash,
signature_attachment_hash,
signature_from_local);
}
function repairDocumentAttachment(id) {
var skip_attachment_dict = {}; var skip_attachment_dict = {};
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .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 || if (context._check_local_attachment_modification ||
context._check_local_attachment_creation || context._check_local_attachment_creation ||
context._check_local_attachment_deletion) { context._check_local_attachment_deletion) {
return pushDocumentAttachment( return pushDocumentAttachment(
context,
skip_attachment_dict, skip_attachment_dict,
id, id,
context._local_sub_storage, context._local_sub_storage,
context._remote_sub_storage, context._remote_sub_storage,
signature_allAttachments,
{ {
conflict_force: (context._conflict_handling === conflict_force: (context._conflict_handling ===
CONFLICT_KEEP_LOCAL), CONFLICT_KEEP_LOCAL),
...@@ -8949,18 +9196,24 @@ return new Parser; ...@@ -8949,18 +9196,24 @@ return new Parser;
check_creation: context._check_local_attachment_creation, check_creation: context._check_local_attachment_creation,
check_deletion: context._check_local_attachment_deletion check_deletion: context._check_local_attachment_deletion
} }
); )
.push(function () {
return signature_allAttachments;
});
} }
return signature_allAttachments;
}) })
.push(function () { .push(function (signature_allAttachments) {
if (context._check_remote_attachment_modification || if (context._check_remote_attachment_modification ||
context._check_remote_attachment_creation || context._check_remote_attachment_creation ||
context._check_remote_attachment_deletion) { context._check_remote_attachment_deletion) {
return pushDocumentAttachment( return pushDocumentAttachment(
context,
skip_attachment_dict, skip_attachment_dict,
id, id,
context._remote_sub_storage, context._remote_sub_storage,
context._local_sub_storage, context._local_sub_storage,
signature_allAttachments,
{ {
use_revert_post: context._use_remote_post, use_revert_post: context._use_remote_post,
conflict_force: (context._conflict_handling === conflict_force: (context._conflict_handling ===
...@@ -8979,16 +9232,39 @@ return new Parser; ...@@ -8979,16 +9232,39 @@ return new Parser;
}); });
} }
function propagateModification(source, destination, doc, hash, id, function propagateModification(context, source, destination, doc, hash, id,
skip_document_dict,
skip_deleted_document_dict,
options) { options) {
var result, var result = new RSVP.Queue(),
post_id, post_id,
to_skip = true; to_skip = true,
from_local;
if (options === undefined) { if (options === undefined) {
options = {}; options = {};
} }
from_local = options.from_local;
if (doc === null) {
result
.push(function () {
return source.get(id);
})
.push(function (source_doc) {
doc = source_doc;
}, function (error) {
if ((error instanceof jIO.util.jIOError) &&
(error.status_code === 404)) {
throw new SkipError(id);
}
throw error;
});
}
if (options.use_post) { if (options.use_post) {
result = destination.post(doc) result
.push(function () {
return destination.post(doc);
})
.push(function (new_id) { .push(function (new_id) {
to_skip = false; to_skip = false;
post_id = new_id; post_id = new_id;
...@@ -9030,17 +9306,30 @@ return new Parser; ...@@ -9030,17 +9306,30 @@ return new Parser;
.push(function () { .push(function () {
to_skip = true; to_skip = true;
return context._signature_sub_storage.put(post_id, { return context._signature_sub_storage.put(post_id, {
"hash": hash hash: hash,
from_local: from_local
}); });
}) })
.push(function () { .push(function () {
skip_document_dict[post_id] = null; skip_document_dict[post_id] = null;
}); });
} else { } else {
result = destination.put(id, doc) result
.push(function () {
// Drop signature if the destination document was empty
// but a signature exists
if (options.create_new_document === true) {
delete skip_deleted_document_dict[id];
return context._signature_sub_storage.remove(id);
}
})
.push(function () {
return destination.put(id, doc);
})
.push(function () { .push(function () {
return context._signature_sub_storage.put(id, { return context._signature_sub_storage.put(id, {
"hash": hash hash: hash,
from_local: from_local
}); });
}); });
} }
...@@ -9049,15 +9338,29 @@ return new Parser; ...@@ -9049,15 +9338,29 @@ return new Parser;
if (to_skip) { if (to_skip) {
skip_document_dict[id] = null; skip_document_dict[id] = null;
} }
})
.push(undefined, function (error) {
if (error instanceof SkipError) {
return;
}
throw error;
}); });
} }
function propagateDeletion(destination, id) { function propagateDeletion(context, destination, id, skip_document_dict,
skip_deleted_document_dict) {
// Do not delete a document if it has an attachment // Do not delete a document if it has an attachment
// ie, replication should prevent losing user data // ie, replication should prevent losing user data
// Synchronize attachments before, to ensure // Synchronize attachments before, to ensure
// all of them will be deleted too // all of them will be deleted too
return repairDocumentAttachment(id) var result;
if (context._signature_hash_key !== undefined) {
result = destination.remove(id)
.push(function () {
return context._signature_sub_storage.remove(id);
});
} else {
result = repairDocumentAttachment(context, id)
.push(function () { .push(function () {
return destination.allAttachments(id); return destination.allAttachments(id);
}) })
...@@ -9074,17 +9377,37 @@ return new Parser; ...@@ -9074,17 +9377,37 @@ return new Parser;
return; return;
} }
throw error; throw error;
}) });
}
return result
.push(function () { .push(function () {
skip_document_dict[id] = null; skip_document_dict[id] = null;
// No need to sync attachment twice on this document
skip_deleted_document_dict[id] = null;
}); });
} }
function checkAndPropagate(status_hash, local_hash, doc, function checkAndPropagate(context, skip_document_dict,
skip_deleted_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) {
var from_local = options.from_local;
return new RSVP.Queue()
.push(function () {
if (options.signature_hash_key !== undefined) {
return callAllDocsOnStorage(context, destination,
cache, destination_key)
.push(function (result) {
if (result.hasOwnProperty(id)) {
return [null, result[id][options.signature_hash_key]];
}
return [null, null];
});
}
return destination.get(id) return destination.get(id)
.push(function (remote_doc) { .push(function (remote_doc) {
return [remote_doc, generateHash(stringify(remote_doc))]; return [remote_doc, generateHash(stringify(remote_doc))];
...@@ -9094,11 +9417,12 @@ return new Parser; ...@@ -9094,11 +9417,12 @@ return new Parser;
return [null, null]; return [null, null];
} }
throw error; throw error;
});
}) })
.push(function (remote_list) { .push(function (remote_list) {
var remote_doc = remote_list[0], var remote_doc = remote_list[0],
remote_hash = remote_list[1]; remote_hash = remote_list[1];
if (local_hash === remote_hash) { if (local_hash === remote_hash) {
// Same modifications on both side // Same modifications on both side
if (local_hash === null) { if (local_hash === null) {
...@@ -9110,7 +9434,8 @@ return new Parser; ...@@ -9110,7 +9434,8 @@ return new Parser;
} }
return context._signature_sub_storage.put(id, { return context._signature_sub_storage.put(id, {
"hash": local_hash hash: local_hash,
from_local: from_local
}) })
.push(function () { .push(function () {
skip_document_dict[id] = null; skip_document_dict[id] = null;
...@@ -9121,12 +9446,20 @@ return new Parser; ...@@ -9121,12 +9446,20 @@ return new Parser;
// Modified only locally. No conflict or force // Modified only locally. No conflict or force
if (local_hash === null) { if (local_hash === null) {
// Deleted locally // Deleted locally
return propagateDeletion(destination, id); return propagateDeletion(context, destination, id,
skip_document_dict,
skip_deleted_document_dict);
} }
return propagateModification(source, destination, doc, return propagateModification(context, source, destination, doc,
local_hash, id, local_hash, id, skip_document_dict,
skip_deleted_document_dict,
{use_post: ((options.use_post) && {use_post: ((options.use_post) &&
(remote_hash === null))}); (remote_hash === null)),
from_local: from_local,
create_new_document:
((remote_hash === null) &&
(status_hash !== null))
});
} }
// Conflict cases // Conflict cases
...@@ -9138,34 +9471,50 @@ return new Parser; ...@@ -9138,34 +9471,50 @@ return new Parser;
// Automatically resolve conflict or force revert // Automatically resolve conflict or force revert
if (remote_hash === null) { if (remote_hash === null) {
// Deleted remotely // Deleted remotely
return propagateDeletion(source, id); return propagateDeletion(context, source, id, skip_document_dict,
skip_deleted_document_dict);
} }
return propagateModification( return propagateModification(
context,
destination, destination,
source, source,
remote_doc, remote_doc,
remote_hash, remote_hash,
id, id,
skip_document_dict,
skip_deleted_document_dict,
{use_post: ((options.use_revert_post) && {use_post: ((options.use_revert_post) &&
(local_hash === null))} (local_hash === null)),
from_local: !from_local,
create_new_document: ((local_hash === null) &&
(status_hash !== null))}
); );
} }
// Minimize conflict if it can be resolved // Minimize conflict if it can be resolved
if (remote_hash === null) { if (remote_hash === null) {
// Copy remote modification remotely // Copy remote modification remotely
return propagateModification(source, destination, doc, return propagateModification(context, source, destination, doc,
local_hash, id, local_hash, id, skip_document_dict,
{use_post: options.use_post}); skip_deleted_document_dict,
} {use_post: options.use_post,
from_local: from_local,
create_new_document:
(status_hash !== null)});
}
doc = doc || local_hash;
remote_doc = remote_doc || remote_hash;
throw new jIO.util.jIOError("Conflict on '" + id + "': " + throw new jIO.util.jIOError("Conflict on '" + id + "': " +
stringify(doc || '') + " !== " + stringify(doc) + " !== " +
stringify(remote_doc || ''), stringify(remote_doc),
409); 409);
}); });
} }
function checkLocalDeletion(queue, destination, id, source, function checkLocalDeletion(queue, context, skip_document_dict,
skip_deleted_document_dict,
cache, destination_key,
destination, id, source,
conflict_force, conflict_revert, conflict_force, conflict_revert,
conflict_ignore, options) { conflict_ignore, options) {
var status_hash; var status_hash;
...@@ -9175,7 +9524,10 @@ return new Parser; ...@@ -9175,7 +9524,10 @@ return new Parser;
}) })
.push(function (result) { .push(function (result) {
status_hash = result.hash; status_hash = result.hash;
return checkAndPropagate(status_hash, null, null, return checkAndPropagate(context, skip_document_dict,
skip_deleted_document_dict,
cache, destination_key,
status_hash, null, null,
source, destination, id, source, destination, id,
conflict_force, conflict_revert, conflict_force, conflict_revert,
conflict_ignore, conflict_ignore,
...@@ -9183,37 +9535,33 @@ return new Parser; ...@@ -9183,37 +9535,33 @@ return new Parser;
}); });
} }
function checkSignatureDifference(queue, source, destination, id, function checkSignatureDifference(queue, context, skip_document_dict,
skip_deleted_document_dict,
cache, destination_key,
source, destination, id,
conflict_force, conflict_revert, conflict_force, conflict_revert,
conflict_ignore, conflict_ignore,
is_creation, is_modification, local_hash, status_hash,
getMethod, options) { options) {
queue queue
.push(function () { .push(function () {
// Optimisation to save a get call to signature storage if (local_hash === null) {
if (is_creation === true) { // Hash was not provided by the allDocs query
return RSVP.all([ return source.get(id);
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" return null;
+ " checkSignatureDifference",
409);
}) })
.push(function (result_list) { .push(function (doc) {
var doc = result_list[0], if (local_hash === null) {
local_hash = generateHash(stringify(doc)), // Hash was not provided by the allDocs query
status_hash = result_list[1].hash; local_hash = generateHash(stringify(doc));
}
if (local_hash !== status_hash) { if (local_hash !== status_hash) {
return checkAndPropagate(status_hash, local_hash, doc, return checkAndPropagate(context, skip_document_dict,
skip_deleted_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,
...@@ -9222,47 +9570,11 @@ return new Parser; ...@@ -9222,47 +9570,11 @@ return new Parser;
}); });
} }
function checkBulkSignatureDifference(queue, source, destination, id_list, function pushStorage(context, skip_document_dict,
document_status_list, options, skip_deleted_document_dict,
conflict_force, conflict_revert, cache, source_key, destination_key,
conflict_ignore) { source, destination, signature_allDocs, options) {
queue var argument_list = [],
.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
);
});
}
function pushStorage(source, destination, options) {
var queue = new RSVP.Queue(),
argument_list = [],
argument_list_deletion = []; argument_list_deletion = [];
if (!options.hasOwnProperty("use_post")) { if (!options.hasOwnProperty("use_post")) {
options.use_post = false; options.use_post = false;
...@@ -9270,127 +9582,162 @@ return new Parser; ...@@ -9270,127 +9582,162 @@ return new Parser;
if (!options.hasOwnProperty("use_revert_post")) { if (!options.hasOwnProperty("use_revert_post")) {
options.use_revert_post = false; options.use_revert_post = false;
} }
return queue return callAllDocsOnStorage(context, source, cache, source_key)
.push(function () { .push(function (source_allDocs) {
return RSVP.all([
source.allDocs(context._query_options),
context._signature_sub_storage.allDocs()
]);
})
.push(function (result_list) {
var i, var i,
local_dict = {}, local_dict = {},
document_list = [],
document_status_list = [],
signature_dict = {}, signature_dict = {},
is_modification, is_modification,
is_creation, is_creation,
key; status_hash,
for (i = 0; i < result_list[0].data.total_rows; i += 1) { local_hash,
key,
queue = new RSVP.Queue();
for (key in source_allDocs) {
if (source_allDocs.hasOwnProperty(key)) {
if (!skip_document_dict.hasOwnProperty(key)) {
local_dict[key] = source_allDocs[key];
}
}
}
/*
for (i = 0; i < source_allDocs.data.total_rows; i += 1) {
if (!skip_document_dict.hasOwnProperty( if (!skip_document_dict.hasOwnProperty(
result_list[0].data.rows[i].id source_allDocs.data.rows[i].id
)) { )) {
local_dict[result_list[0].data.rows[i].id] = i; local_dict[source_allDocs.data.rows[i].id] =
source_allDocs.data.rows[i].value;
} }
} }
for (i = 0; i < result_list[1].data.total_rows; i += 1) { */
for (i = 0; i < signature_allDocs.data.total_rows; i += 1) {
if (!skip_document_dict.hasOwnProperty( if (!skip_document_dict.hasOwnProperty(
result_list[1].data.rows[i].id signature_allDocs.data.rows[i].id
)) { )) {
signature_dict[result_list[1].data.rows[i].id] = i; signature_dict[signature_allDocs.data.rows[i].id] =
signature_allDocs.data.rows[i].value.hash;
} }
} }
i = 0;
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 (options.use_bulk_get === true) { if (is_creation === true) {
document_list.push({ status_hash = null;
method: "get", } else if (is_modification === true) {
parameter_list: [key] status_hash = signature_dict[key];
}); }
document_status_list.push({
is_creation: is_creation, local_hash = null;
is_modification: is_modification if (options.signature_hash_key !== undefined) {
}); local_hash = local_dict[key][options.signature_hash_key];
} else { if (is_modification === true) {
argument_list[i] = [undefined, source, destination, // 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,
skip_deleted_document_dict,
cache, destination_key,
source, destination,
key, key,
options.conflict_force, options.conflict_force,
options.conflict_revert, options.conflict_revert,
options.conflict_ignore, options.conflict_ignore,
is_creation, is_modification, local_hash, status_hash,
source.get.bind(source), options]);
options];
i += 1;
}
} }
} }
} }
queue queue
.push(function () { .push(function () {
return dispatchQueue( return dispatchQueue(
context,
checkSignatureDifference, checkSignatureDifference,
argument_list, argument_list,
options.operation_amount options.operation_amount
); );
}); });
if (options.check_deletion === true) {
i = 0;
for (key in signature_dict) { for (key in signature_dict) {
if (signature_dict.hasOwnProperty(key)) { if (signature_dict.hasOwnProperty(key)) {
if (!local_dict.hasOwnProperty(key)) { if (!local_dict.hasOwnProperty(key)) {
argument_list_deletion[i] = [undefined, if (options.check_deletion === true) {
argument_list_deletion.push([undefined,
context,
skip_document_dict,
skip_deleted_document_dict,
cache, destination_key,
destination, key, destination, key,
source, source,
options.conflict_force, options.conflict_force,
options.conflict_revert, options.conflict_revert,
options.conflict_ignore, options.conflict_ignore,
options]; options]);
i += 1; } else {
skip_deleted_document_dict[key] = null;
} }
} }
} }
}
if (argument_list_deletion.length !== 0) {
queue.push(function () { queue.push(function () {
return dispatchQueue( return dispatchQueue(
context,
checkLocalDeletion, checkLocalDeletion,
argument_list_deletion, argument_list_deletion,
options.operation_amount options.operation_amount
); );
}); });
} }
if ((options.use_bulk_get === true) && (document_list.length !== 0)) { return queue;
checkBulkSignatureDifference(queue, source, destination,
document_list, document_status_list,
options,
options.conflict_force,
options.conflict_revert,
options.conflict_ignore);
}
}); });
} }
function repairDocument(queue, id) { function repairDocument(queue, context, id, signature_hash_key,
signature_hash, signature_attachment_hash,
signature_from_local) {
queue.push(function () { queue.push(function () {
return repairDocumentAttachment(id); return repairDocumentAttachment(context, id, signature_hash_key,
signature_hash,
signature_attachment_hash,
signature_from_local);
}); });
} }
ReplicateStorage.prototype.repair = function () {
var context = this,
argument_list = arguments,
skip_document_dict = {},
skip_deleted_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) {
// 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 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
.__storage._sub_storage.put(
context._signature_hash, context._signature_hash,
{} {}
); );
...@@ -9417,11 +9764,28 @@ return new Parser; ...@@ -9417,11 +9764,28 @@ return new Parser;
}) })
.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,
skip_deleted_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 ===
...@@ -9433,28 +9797,26 @@ return new Parser; ...@@ -9433,28 +9797,26 @@ return new Parser;
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_hash_key: context._signature_hash_key,
} from_local: true
}) })
.push(function () { .push(function () {
// Autoactivate bulk if substorage implements it return signature_allDocs;
// 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;
}
} }
return signature_allDocs;
})
.push(function (signature_allDocs) {
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, { skip_deleted_document_dict,
use_bulk_get: use_bulk_get, cache, 'remote', 'local',
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),
...@@ -9465,7 +9827,9 @@ return new Parser; ...@@ -9465,7 +9827,9 @@ return new Parser;
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_hash_key: context._signature_hash_key,
from_local: false
}); });
} }
}) })
...@@ -9478,20 +9842,31 @@ return new Parser; ...@@ -9478,20 +9842,31 @@ return new Parser;
context._check_remote_attachment_deletion) { context._check_remote_attachment_deletion) {
// Attachments are synchronized if and only if their parent document // Attachments are synchronized if and only if their parent document
// has been also marked as synchronized. // has been also marked as synchronized.
return context._signature_sub_storage.allDocs() return context._signature_sub_storage.allDocs({
select_list: ['hash', 'attachment_hash', 'from_local']
})
.push(function (result) { .push(function (result) {
var i, var i,
argument_list = [], local_argument_list = [],
row,
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( row = result.data.rows[i];
[undefined, result.data.rows[i].id] // Do not synchronize attachment if one version of the document
// is deleted but not pushed to the other storage
if (!skip_deleted_document_dict.hasOwnProperty(row.id)) {
local_argument_list.push(
[undefined, context, row.id, context._signature_hash_key,
row.value.hash, row.value.attachment_hash,
row.value.from_local]
); );
} }
}
return dispatchQueue( return dispatchQueue(
context,
repairDocument, repairDocument,
argument_list, local_argument_list,
context._parallel_operation_amount context._parallel_operation_amount
); );
}); });
...@@ -11154,56 +11529,6 @@ return new Parser; ...@@ -11154,56 +11529,6 @@ return new Parser;
}); });
}; };
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: true
}
});
})
.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 context = this, var context = this,
new_id; new_id;
...@@ -11430,7 +11755,7 @@ return new Parser; ...@@ -11430,7 +11755,7 @@ return new Parser;
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) {
...@@ -12041,10 +12366,9 @@ return new Parser; ...@@ -12041,10 +12366,9 @@ return new Parser;
}(jIO, RSVP, Blob)); }(jIO, RSVP, Blob));
;/*jslint nomen: true*/ ;/*jslint nomen: true*/
/*global Blob, atob, btoa, RSVP*/ /*global Blob, RSVP, unescape, escape*/
(function (jIO, Blob, atob, btoa, RSVP) { (function (jIO, Blob, RSVP, unescape, escape) {
"use strict"; "use strict";
/** /**
* The jIO DocumentStorage extension * The jIO DocumentStorage extension
* *
...@@ -12060,7 +12384,13 @@ return new Parser; ...@@ -12060,7 +12384,13 @@ return new Parser;
var DOCUMENT_EXTENSION = ".json", var DOCUMENT_EXTENSION = ".json",
DOCUMENT_REGEXP = new RegExp("^jio_document/([\\w=]+)" + DOCUMENT_REGEXP = new RegExp("^jio_document/([\\w=]+)" +
DOCUMENT_EXTENSION + "$"), DOCUMENT_EXTENSION + "$"),
ATTACHMENT_REGEXP = new RegExp("^jio_attachment/([\\w=]+)/([\\w=]+)$"); ATTACHMENT_REGEXP = new RegExp("^jio_attachment/([\\w=]+)/([\\w=]+)$"),
btoa = function (str) {
return window.btoa(unescape(encodeURIComponent(str)));
},
atob = function (str) {
return decodeURIComponent(escape(window.atob(str)));
};
function getSubAttachmentIdFromParam(id, name) { function getSubAttachmentIdFromParam(id, name) {
if (name === undefined) { if (name === undefined) {
...@@ -12267,7 +12597,7 @@ return new Parser; ...@@ -12267,7 +12597,7 @@ return new Parser;
jIO.addStorage('document', DocumentStorage); jIO.addStorage('document', DocumentStorage);
}(jIO, Blob, atob, btoa, RSVP)); }(jIO, Blob, RSVP, unescape, escape));
;/* ;/*
* Copyright 2013, Nexedi SA * Copyright 2013, Nexedi SA
* Released under the LGPL license. * Released under the LGPL license.
...@@ -12527,9 +12857,7 @@ return new Parser; ...@@ -12527,9 +12857,7 @@ return new Parser;
return tx; return tx;
} }
function handleCursor(request, callback) { function handleCursor(request, callback, resolve, reject) {
function resolver(resolve, reject) {
// Open DB //
request.onerror = function (error) { request.onerror = function (error) {
if (request.transaction) { if (request.transaction) {
request.transaction.abort(); request.transaction.abort();
...@@ -12554,9 +12882,6 @@ return new Parser; ...@@ -12554,9 +12882,6 @@ return new Parser;
} }
}; };
} }
// XXX Canceller???
return new RSVP.Promise(resolver);
}
IndexedDBStorage.prototype.buildQuery = function (options) { IndexedDBStorage.prototype.buildQuery = function (options) {
var result_list = []; var result_list = [];
...@@ -12577,40 +12902,50 @@ return new Parser; ...@@ -12577,40 +12902,50 @@ return new Parser;
} }
return openIndexedDB(this) return openIndexedDB(this)
.push(function (db) { .push(function (db) {
return new RSVP.Promise(function (resolve, reject) {
var tx = openTransaction(db, ["metadata"], "readonly"); var tx = openTransaction(db, ["metadata"], "readonly");
if (options.include_docs === true) { if (options.include_docs === true) {
return handleCursor(tx.objectStore("metadata").index("_id") handleCursor(tx.objectStore("metadata").index("_id").openCursor(),
.openCursor(), pushIncludedMetadata); pushIncludedMetadata, resolve, reject);
} else {
handleCursor(tx.objectStore("metadata").index("_id")
.openKeyCursor(), pushMetadata, resolve, reject);
} }
return handleCursor(tx.objectStore("metadata").index("_id") });
.openKeyCursor(), pushMetadata);
}) })
.push(function () { .push(function () {
return result_list; return result_list;
}); });
}; };
function handleGet(request) { function handleGet(store, id, resolve, reject) {
function resolver(resolve, reject) { var request = store.get(id);
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 {
reject(new jIO.util.jIOError(
"IndexedDB: cannot find object '" + id + "' in the '" +
store.name + "' store",
404
));
} }
// XXX How to get ID
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"),
id,
resolve,
reject
);
});
}) })
.push(function (result) { .push(function (result) {
return result.doc; return result.doc;
...@@ -12626,37 +12961,52 @@ return new Parser; ...@@ -12626,37 +12961,52 @@ return new Parser;
return openIndexedDB(this) return openIndexedDB(this)
.push(function (db) { .push(function (db) {
return new RSVP.Promise(function (resolve, reject) {
var transaction = openTransaction(db, ["metadata", "attachment"], var transaction = openTransaction(db, ["metadata", "attachment"],
"readonly"); "readonly");
return RSVP.all([ function getAttachments() {
handleGet(transaction.objectStore("metadata").get(id)), handleCursor(
handleCursor(transaction.objectStore("attachment").index("_id") transaction.objectStore("attachment").index("_id")
.openCursor(IDBKeyRange.only(id)), addEntry) .openCursor(IDBKeyRange.only(id)),
]); addEntry,
resolve,
reject
);
}
handleGet(
transaction.objectStore("metadata"),
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) {
return new RSVP.Promise(function (resolve, reject) {
var transaction = openTransaction(db, ["metadata"], "readwrite"); var transaction = openTransaction(db, ["metadata"], "readwrite");
return handleRequest(transaction.objectStore("metadata").put({ handleRequest(
transaction.objectStore("metadata").put({
"_id": id, "_id": id,
"doc": metadata "doc": metadata
})); }),
resolve,
reject
);
});
}); });
}; };
...@@ -12665,19 +13015,38 @@ return new Parser; ...@@ -12665,19 +13015,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) {
return new RSVP.Promise(function (resolve, reject) {
function resolver() {
if (resolved_amount < 2) {
resolved_amount += 1;
} else {
resolve();
}
}
var transaction = openTransaction(db, ["metadata", "attachment", var transaction = openTransaction(db, ["metadata", "attachment",
"blob"], "readwrite"); "blob"], "readwrite");
return RSVP.all([ handleRequest(
handleRequest(transaction transaction.objectStore("metadata")["delete"](id),
.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 +13060,67 @@ return new Parser; ...@@ -12691,48 +13060,67 @@ 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) { );
function getBlob(attachment) {
var total_length = attachment.info.length, var total_length = attachment.info.length,
i, result_list = [],
promise_list = [],
store = transaction.objectStore("blob"), store = transaction.objectStore("blob"),
start_index, start_index,
end_index; end_index;
type = attachment.info.content_type; type = attachment.info.content_type;
start = options.start || 0; start = options.start || 0;
end = options.end || total_length; end = options.end || total_length;
if (end > total_length) { if (end > total_length) {
end = total_length; end = total_length;
} }
if (start < 0 || end < 0) { if (start < 0 || end < 0) {
throw new jIO.util.jIOError("_start and _end must be positive", throw new jIO.util.jIOError(
400); "_start and _end must be positive",
400
);
} }
if (start > end) { if (start > end) {
throw new jIO.util.jIOError("_start is greater than _end", throw new jIO.util.jIOError("_start is greater than _end",
400); 400);
} }
start_index = Math.floor(start / UNITE); start_index = Math.floor(start / UNITE);
end_index = Math.floor(end / UNITE); end_index = Math.floor(end / UNITE) - 1;
if (end % UNITE === 0) { if (end % UNITE === 0) {
end_index -= 1; end_index -= 1;
} }
function resolver(result) {
for (i = start_index; i <= end_index; i += 1) { if (result.blob !== undefined) {
promise_list.push( result_list.push(result);
handleGet(store.get(buildKeyPath([id, }
name, i]))) resolve(result_list);
}
function getPart(i) {
return function (result) {
if (result) {
result_list.push(result);
}
i += 1;
handleGet(store,
buildKeyPath([id, name, i]),
(i <= end_index) ? getPart(i) : resolver,
reject
); );
};
} }
return RSVP.all(promise_list); getPart(start_index - 1)();
}
// XXX Should raise if key is not good
handleGet(transaction.objectStore("attachment"),
buildKeyPath([id, name]),
getBlob,
reject
);
});
}) })
.push(function (result_list) { .push(function (result_list) {
var array_buffer_list = [], var array_buffer_list = [],
...@@ -12753,19 +13141,24 @@ return new Parser; ...@@ -12753,19 +13141,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,12 +13186,29 @@ return new Parser; ...@@ -12793,12 +13186,29 @@ 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) {
return function () {
i += 1;
handleRequest(
blob_store.put({
"_key_path": buildKeyPath([id, name, i]),
"_id" : id,
"_attachment" : name,
"_part" : i,
"blob": blob_part[i]
}),
(i < len) ? putBlobPart(i) : resolve,
reject
);
};
}
handleRequest(
attachment_store.put({
"_key_path": buildKeyPath([id, name]), "_key_path": buildKeyPath([id, name]),
"_id": id, "_id": id,
"_attachment": name, "_attachment": name,
...@@ -12806,25 +13216,13 @@ return new Parser; ...@@ -12806,25 +13216,13 @@ return new Parser;
"content_type": blob.type, "content_type": blob.type,
"length": blob.size "length": blob.size
} }
})) }),
], putBlobPart(-1),
len = blob_part.length, reject
blob_store = transaction.objectStore("blob"),
i;
for (i = 0; i < len; i += 1) {
promise_list.push(
handleRequest(blob_store.put({
"_key_path": buildKeyPath([id, name,
i]),
"_id" : id,
"_attachment" : name,
"_part" : i,
"blob": blob_part[i]
}))
); );
} }
// Store all new data removeAttachment(transaction, id, name, write, reject);
return RSVP.all(promise_list); });
}); });
}; };
...@@ -12833,7 +13231,9 @@ return new Parser; ...@@ -12833,7 +13231,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);
});
}); });
}; };
...@@ -12851,24 +13251,29 @@ return new Parser; ...@@ -12851,24 +13251,29 @@ return new Parser;
(function (jIO, RSVP, DOMException, Blob, crypto, Uint8Array, ArrayBuffer) { (function (jIO, RSVP, DOMException, Blob, crypto, Uint8Array, ArrayBuffer) {
"use strict"; "use strict";
/*
The cryptography system used by this storage is AES-GCM.
Here is an example of how to generate a key to the json format:
// you the cryptography system used by this storage is AES-GCM. return new RSVP.Queue()
// here is an example of how to generate a key to the json format. .push(function () {
return crypto.subtle.generateKey({name: "AES-GCM", length: 256},
// var key, true, ["encrypt", "decrypt"]);
// jsonKey; })
// crypto.subtle.generateKey({name: "AES-GCM",length: 256}, .push(function (key) {
// (true), ["encrypt", "decrypt"]) return crypto.subtle.exportKey("jwk", key);
// .then(function(res){key = res;}); })
// .push(function (json_key) {
// window.crypto.subtle.exportKey("jwk", key) var jio = jIO.createJIO({
// .then(function(res){jsonKey = val}) type: "crypt",
// key: json_key,
//var storage = jIO.createJIO({type: "crypt", key: jsonKey, sub_storage: {storage_definition}
// sub_storage: {...}}); });
});
// find more informations about this cryptography system on Find more informations about this cryptography system on
// https://github.com/diafygi/webcrypto-examples#aes-gcm https://github.com/diafygi/webcrypto-examples#aes-gcm
*/
/** /**
* The JIO Cryptography Storage extension * The JIO Cryptography Storage extension
...@@ -12946,12 +13351,12 @@ return new Parser; ...@@ -12946,12 +13351,12 @@ return new Parser;
}) })
.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);
} }
...@@ -12988,7 +13393,7 @@ return new Parser; ...@@ -12988,7 +13393,7 @@ return new Parser;
.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 () {
...@@ -13207,7 +13612,7 @@ return new Parser; ...@@ -13207,7 +13612,7 @@ return new Parser;
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;
}); });
} }
...@@ -13391,3 +13796,117 @@ return new Parser; ...@@ -13391,3 +13796,117 @@ return new Parser;
jIO.addStorage('websql', WebSQLStorage); jIO.addStorage('websql', WebSQLStorage);
}(jIO, RSVP, Blob, openDatabase)); }(jIO, RSVP, Blob, openDatabase));
;/*jslint nomen: true */
/*global RSVP, UriTemplate*/
(function (jIO, RSVP, UriTemplate) {
"use strict";
var GET_POST_URL = "https://graph.facebook.com/v2.9/{+post_id}" +
"?fields={+fields}&access_token={+access_token}",
get_post_template = UriTemplate.parse(GET_POST_URL),
GET_FEED_URL = "https://graph.facebook.com/v2.9/{+user_id}/feed" +
"?fields={+fields}&limit={+limit}&since={+since}&access_token=" +
"{+access_token}",
get_feed_template = UriTemplate.parse(GET_FEED_URL);
function FBStorage(spec) {
if (typeof spec.access_token !== 'string' || !spec.access_token) {
throw new TypeError("Access Token must be a string " +
"which contains more than one character.");
}
if (typeof spec.user_id !== 'string' || !spec.user_id) {
throw new TypeError("User ID must be a string " +
"which contains more than one character.");
}
this._access_token = spec.access_token;
this._user_id = spec.user_id;
this._default_field_list = spec.default_field_list || [];
this._default_limit = spec.default_limit || 500;
}
FBStorage.prototype.get = function (id) {
var that = this;
return new RSVP.Queue()
.push(function () {
return jIO.util.ajax({
type: "GET",
url: get_post_template.expand({post_id: id,
fields: that._default_field_list, access_token: that._access_token})
});
})
.push(function (result) {
return JSON.parse(result.target.responseText);
});
};
function paginateResult(url, result, select_list) {
return new RSVP.Queue()
.push(function () {
return jIO.util.ajax({
type: "GET",
url: url
});
})
.push(function (response) {
return JSON.parse(response.target.responseText);
},
function (err) {
throw new jIO.util.jIOError("Getting feed failed " + err.toString(),
err.target.status);
})
.push(function (response) {
if (response.data.length === 0) {
return result;
}
var i, j, obj = {};
for (i = 0; i < response.data.length; i += 1) {
obj.id = response.data[i].id;
obj.value = {};
for (j = 0; j < select_list.length; j += 1) {
obj.value[select_list[j]] = response.data[i][select_list[j]];
}
result.push(obj);
obj = {};
}
return paginateResult(response.paging.next, result, select_list);
});
}
FBStorage.prototype.buildQuery = function (query) {
var that = this, fields = [], limit = this._default_limit,
template_argument = {
user_id: this._user_id,
limit: limit,
access_token: this._access_token
};
if (query.include_docs) {
fields = fields.concat(that._default_field_list);
}
if (query.select_list) {
fields = fields.concat(query.select_list);
}
if (query.limit) {
limit = query.limit[1];
}
template_argument.fields = fields;
template_argument.limit = limit;
return paginateResult(get_feed_template.expand(template_argument), [],
fields)
.push(function (result) {
if (!query.limit) {
return result;
}
return result.slice(query.limit[0], query.limit[1]);
});
};
FBStorage.prototype.hasCapacity = function (name) {
var this_storage_capacity_list = ["list", "select", "include", "limit"];
if (this_storage_capacity_list.indexOf(name) !== -1) {
return true;
}
};
jIO.addStorage('facebook', FBStorage);
}(jIO, RSVP, UriTemplate));
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment