...
 
Commits (19)
......@@ -26,7 +26,7 @@ TESTDIR = test
EXAMPLEDIR = examples
EXTERNALDIR = external
VERSION = 3.35.0
VERSION = 3.37.0
JIOVERSION = ${DISTDIR}/jio-v${VERSION}.js
JIOLATEST = ${DISTDIR}/jio-latest.js
JIONODEVERSION = ${DISTDIR}/jio-v${VERSION}-node.js
......@@ -108,6 +108,7 @@ lint: $(patsubst ${TESTDIR}/jio.storage/%.js, ${LINTDIR}/${TESTDIR}/jio.storage/
${LINTDIR}/node/jio.js \
${LINTDIR}/${TESTDIR}/node.js \
${LINTDIR}/${TESTDIR}/node/node-require.js \
${LINTDIR}/${TESTDIR}/node/ajax.tests.js \
$(patsubst ${SRCDIR}/jio.storage/%.js, ${LINTDIR}/jio.storage/%.js, $(wildcard ${SRCDIR}/jio.storage/*.js))
#############################################
......@@ -141,6 +142,7 @@ ${JIOVERSION}: ${EXTERNALDIR}/URI.js \
${SRCDIR}/jio.storage/davstorage.js \
${SRCDIR}/jio.storage/gdrivestorage.js \
${SRCDIR}/jio.storage/unionstorage.js \
${SRCDIR}/jio.storage/linsharestorage.js \
${SRCDIR}/jio.storage/erp5storage.js \
${SRCDIR}/jio.storage/querystorage.js \
${SRCDIR}/jio.storage/drivetojiomapping.js \
......@@ -148,7 +150,6 @@ ${JIOVERSION}: ${EXTERNALDIR}/URI.js \
${SRCDIR}/jio.storage/localstorage.js \
${SRCDIR}/jio.storage/indexeddbstorage.js \
${SRCDIR}/jio.storage/cryptstorage.js \
${SRCDIR}/jio.storage/websqlstorage.js \
${SRCDIR}/jio.storage/fbstorage.js \
${SRCDIR}/jio.storage/cloudooostorage.js
@mkdir -p $(@D)
......@@ -253,6 +254,7 @@ ${EXTERNALDIR}/renderjs-latest.js:
clean:
rm -rf ${LINTDIR}
rm ${SRCDIR}/queries/build/parser.js
forceclean: clean
rm -rf ${EXTERNALDIR} ${SRCDIR}/queries/build/parser.js
......@@ -7870,11 +7870,11 @@ var arrayExtend = function () {
}
return {type:"complex",operator:operator,query_list:query_list2};
}, simpleQuerySetKey = function (query, key) {
}, querySetKey = function (query, key) {
var i;
if (query.type === "complex") {
for (i = 0; i < query.query_list.length; ++i) {
simpleQuerySetKey (query.query_list[i],key);
querySetKey(query.query_list[i], key);
}
return true;
}
......@@ -7995,7 +7995,7 @@ case 12:
this.$ = $$[$0-1];
break;
case 13:
simpleQuerySetKey($$[$0], $$[$0-2]); this.$ = $$[$0];
querySetKey($$[$0], $$[$0-2]); this.$ = $$[$0];
break;
case 15:
$$[$0].operator = $$[$0-1] ; this.$ = $$[$0];
......@@ -9124,21 +9124,8 @@ return new Parser;
* #crossLink "Query/toString:method"
*/
ComplexQuery.prototype.toString = function () {
var str_list = [], this_operator = this.operator;
if (this.operator === "NOT") {
str_list.push("NOT (");
str_list.push(this.query_list[0].toString());
str_list.push(")");
return str_list.join(" ");
}
this.query_list.forEach(function (query) {
str_list.push("(");
str_list.push(query.toString());
str_list.push(")");
str_list.push(this_operator);
});
str_list.length -= 1;
return str_list.join(" ");
/*global objectToSearchText */
return objectToSearchText(this.toJSON());
};
/**
......@@ -9237,21 +9224,60 @@ return new Parser;
};
function objectToSearchText(query) {
var str_list = [];
if (query.type === "complex") {
str_list.push("(");
(query.query_list || []).forEach(function (sub_query) {
str_list.push(objectToSearchText(sub_query));
str_list.push(query.operator);
});
str_list.length -= 1;
str_list.push(")");
return str_list.join(" ");
}
var i = 0,
query_list = null,
string_list = null,
operator = "",
common_key = "";
if (query.type === "simple") {
return (query.key ? query.key + ": " : "") +
(query.operator || "") + ' "' + query.value + '"';
}
if (query.type === "complex") {
query_list = query.query_list;
if (!query_list || query_list.length === 0) {
return "";
}
operator = query.operator || "";
if (operator === "NOT") {
// fallback to AND operator if several queries are given
// i.e. `NOT ( a AND b )`
return "NOT ( " + objectToSearchText(
{type: "complex", operator: "AND", query_list: query_list}
) + " )";
}
if (query_list.length === 1) {
return objectToSearchText(query_list[0]);
}
common_key = query_list[i].key;
for (i = 1; i < query_list.length; i += 1) {
if (query_list[i].type !== "simple" ||
query_list[i].key !== common_key) {
break;
}
}
string_list = [];
if (i === query_list.length) {
for (i = 0; i < query_list.length; i += 1) {
string_list.push(
(query_list[i].operator || "") +
' "' + query_list[i].value + '"'
);
}
} else {
common_key = "";
for (i = 0; i < query_list.length; i += 1) {
string_list.push(objectToSearchText(query_list[i]));
}
}
if (string_list.length > 1) {
return (common_key ? common_key + ": " : "") +
"( " + string_list.join(" " + operator + " ") + " )";
}
return (common_key ? common_key + ": " : "") +
string_list[0];
}
throw new TypeError("This object is not a query");
}
......@@ -9419,8 +9445,7 @@ return new Parser;
* #crossLink "Query/toString:method"
*/
SimpleQuery.prototype.toString = function () {
return (this.key ? this.key + ":" : "") +
(this.operator ? " " + this.operator : "") + ' "' + this.value + '"';
return objectToSearchText(this.toJSON());
};
/**
......@@ -10090,17 +10115,11 @@ var XMLHttpRequest = global.XMLHttpRequest || module.exports,
* See https://www.nexedi.com/licensing for rationale and options.
*/
/*global window, RSVP, Blob, XMLHttpRequest, QueryFactory, Query, atob,
FileReader, ArrayBuffer, Uint8Array, navigator */
FileReader, ArrayBuffer, Uint8Array */
(function (window, RSVP, Blob, QueryFactory, Query, atob,
FileReader, ArrayBuffer, Uint8Array, navigator) {
FileReader, ArrayBuffer, Uint8Array) {
"use strict";
if (window.openDatabase === undefined) {
window.openDatabase = function () {
throw new Error('WebSQL is not supported by ' + navigator.userAgent);
};
}
/* Safari does not define DOMError */
if (window.DOMError === undefined) {
window.DOMError = {};
......@@ -10546,6 +10565,7 @@ var XMLHttpRequest = global.XMLHttpRequest || module.exports,
if (context.hasCapacity("list") &&
((options.query === undefined) || context.hasCapacity("query")) &&
((options.sort_on === undefined) || context.hasCapacity("sort")) &&
((options.group_by === undefined) || context.hasCapacity("group")) &&
((options.select_list === undefined) ||
context.hasCapacity("select")) &&
((options.include_docs === undefined) ||
......@@ -10632,7 +10652,7 @@ var XMLHttpRequest = global.XMLHttpRequest || module.exports,
window.jIO = jIO;
}(window, RSVP, Blob, QueryFactory, Query, atob,
FileReader, ArrayBuffer, Uint8Array, navigator));
FileReader, ArrayBuffer, Uint8Array));
/*
* Copyright 2018, Nexedi SA
*
......@@ -14178,7 +14198,7 @@ var jIO = window.jIO,
ERP5Storage.prototype.hasCapacity = function (name) {
return ((name === "list") || (name === "query") ||
(name === "select") || (name === "limit") ||
(name === "sort"));
(name === "sort") || (name === "group"));
};
function isSingleLocalRoles(parsed_query) {
......@@ -14246,7 +14266,8 @@ var jIO = window.jIO,
local_roles,
local_role_found = false,
selection_domain,
sort_list = [];
sort_list = [],
group_list = [];
if (options.query) {
parsed_query = jIO.QueryFactory.create(options.query);
result_list = isSingleLocalRoles(parsed_query);
......@@ -14318,6 +14339,10 @@ var jIO = window.jIO,
}
}
if (options.group_by) {
group_list = options.group_by;
}
if (selection_domain) {
selection_domain = JSON.stringify(selection_domain);
}
......@@ -14331,6 +14356,7 @@ var jIO = window.jIO,
select_list: options.select_list || ["title", "reference"],
limit: options.limit,
sort_on: sort_list,
group_by: group_list,
local_roles: local_roles,
selection_domain: selection_domain
})
......
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
......@@ -71,6 +71,23 @@
});
///////////////////////////
// Linshare storage
///////////////////////////
// return g.run({
// type: "query",
// sub_storage: {
// type: "uuid",
// sub_storage: {
// type: "linshare",
// url: "https://demo.linshare.org/",
// credential_token: window.btoa(
// 'user1@linshare.org' + ':' + 'password1'
// )
// }
// }
// });
///////////////////////////
// WebSQL storage
///////////////////////////
// return g.run({
......
// Generated by CoffeeScript 1.12.2
// Generated by CoffeeScript 1.12.7
(function() {
var InvalidStateError, NetworkError, ProgressEvent, SecurityError, SyntaxError, XMLHttpRequest, XMLHttpRequestEventTarget, XMLHttpRequestUpload, http, https, os, url,
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
......@@ -323,7 +323,7 @@
'access-control-request-method': true,
connection: true,
'content-length': true,
// cookie: true,
cookie: true,
cookie2: true,
date: true,
dnt: true,
......@@ -341,7 +341,7 @@
};
XMLHttpRequest.prototype._privateHeaders = {
// 'set-cookie': true,
'set-cookie': true,
'set-cookie2': true
};
......@@ -363,6 +363,7 @@
};
XMLHttpRequest.prototype._sendHttp = function(data) {
var that;
if (this._sync) {
throw new Error("Synchronous XHR processing not implemented");
}
......@@ -372,9 +373,11 @@
} else {
data || (data = '');
}
this.upload._setData(data);
this._finalizeHeaders();
this._sendHxxpRequest();
that = this;
this.upload._setData(data, function() {
that._finalizeHeaders();
return that._sendHxxpRequest();
});
return void 0;
};
......@@ -709,11 +712,13 @@
XMLHttpRequest.XMLHttpRequest = XMLHttpRequest;
XMLHttpRequest.FormData = FormData;
SecurityError = (function(superClass) {
extend(SecurityError, superClass);
function SecurityError() {
SecurityError.__super__.constructor.apply(this, arguments);
SecurityError.__super__.constructor.call(this);
}
return SecurityError;
......@@ -726,7 +731,7 @@
extend(InvalidStateError, superClass);
function InvalidStateError() {
InvalidStateError.__super__.constructor.apply(this, arguments);
InvalidStateError.__super__.constructor.call(this);
}
return InvalidStateError;
......@@ -750,7 +755,7 @@
extend(NetworkError, superClass);
function NetworkError() {
NetworkError.__super__.constructor.apply(this, arguments);
NetworkError.__super__.constructor.call(this);
}
return NetworkError;
......@@ -763,7 +768,7 @@
extend(SyntaxError, superClass);
function SyntaxError() {
SyntaxError.__super__.constructor.apply(this, arguments);
SyntaxError.__super__.constructor.call(this);
}
return SyntaxError;
......@@ -813,10 +818,10 @@
return void 0;
};
XMLHttpRequestUpload.prototype._setData = function(data) {
var body, i, j, k, offset, ref, ref1, view;
XMLHttpRequestUpload.prototype._setData = function(data, cb) {
var body, i, j, k, offset, ref, ref1, that, view;
if (typeof data === 'undefined' || data === null) {
return;
return cb();
}
if (typeof data === 'string') {
if (data.length !== 0) {
......@@ -840,10 +845,23 @@
body[i] = view[i + offset];
}
this._body = body;
} else if (data instanceof FormData) {
body = '';
this._contentType = data.getHeaders()['content-type'];
data.on('data', function(data) {
return body += data.toString();
});
that = this;
data.on('end', function() {
that._body = body;
return cb();
});
data.resume();
return;
} else {
throw new Error("Unsupported send() data " + data);
}
return void 0;
return cb();
};
XMLHttpRequestUpload.prototype._finalizeHeaders = function(headers, loweredHeaders) {
......@@ -872,4 +890,4 @@
XMLHttpRequest.XMLHttpRequestUpload = XMLHttpRequestUpload;
}).call(this);
\ No newline at end of file
}).call(this);
{
"name": "jio",
"version": "v3.35.0",
"version": "v3.37.0",
"license": "GPLv3+",
"author": "Nexedi SA",
"contributors": [
......
......@@ -18,17 +18,11 @@
* See https://www.nexedi.com/licensing for rationale and options.
*/
/*global window, RSVP, Blob, XMLHttpRequest, QueryFactory, Query, atob,
FileReader, ArrayBuffer, Uint8Array, navigator */
FileReader, ArrayBuffer, Uint8Array */
(function (window, RSVP, Blob, QueryFactory, Query, atob,
FileReader, ArrayBuffer, Uint8Array, navigator) {
FileReader, ArrayBuffer, Uint8Array) {
"use strict";
if (window.openDatabase === undefined) {
window.openDatabase = function () {
throw new Error('WebSQL is not supported by ' + navigator.userAgent);
};
}
/* Safari does not define DOMError */
if (window.DOMError === undefined) {
window.DOMError = {};
......@@ -474,6 +468,7 @@
if (context.hasCapacity("list") &&
((options.query === undefined) || context.hasCapacity("query")) &&
((options.sort_on === undefined) || context.hasCapacity("sort")) &&
((options.group_by === undefined) || context.hasCapacity("group")) &&
((options.select_list === undefined) ||
context.hasCapacity("select")) &&
((options.include_docs === undefined) ||
......@@ -560,4 +555,4 @@
window.jIO = jIO;
}(window, RSVP, Blob, QueryFactory, Query, atob,
FileReader, ArrayBuffer, Uint8Array, navigator));
FileReader, ArrayBuffer, Uint8Array));
......@@ -42,6 +42,7 @@
if (options.headers === undefined) {
options.headers = {};
}
options.headers.Accept = "*/*";
options.headers['X-ACCESS-TOKEN'] = storage._access_token;
options.xhrFields.withCredentials = false;
} else {
......@@ -419,7 +420,7 @@
ERP5Storage.prototype.hasCapacity = function (name) {
return ((name === "list") || (name === "query") ||
(name === "select") || (name === "limit") ||
(name === "sort"));
(name === "sort") || (name === "group"));
};
function isSingleLocalRoles(parsed_query) {
......@@ -487,7 +488,8 @@
local_roles,
local_role_found = false,
selection_domain,
sort_list = [];
sort_list = [],
group_list = [];
if (options.query) {
parsed_query = jIO.QueryFactory.create(options.query);
result_list = isSingleLocalRoles(parsed_query);
......@@ -559,6 +561,10 @@
}
}
if (options.group_by) {
group_list = options.group_by;
}
if (selection_domain) {
selection_domain = JSON.stringify(selection_domain);
}
......@@ -572,6 +578,7 @@
select_list: options.select_list || ["title", "reference"],
limit: options.limit,
sort_on: sort_list,
group_by: group_list,
local_roles: local_roles,
selection_domain: selection_domain
})
......
......@@ -97,13 +97,19 @@
}
function waitForOpenIndexedDB(db_name, callback) {
var request;
function canceller() {
if ((request !== undefined) && (request.result !== undefined)) {
request.result.close();
}
}
function resolver(resolve, reject) {
// Open DB //
var request = indexedDB.open(db_name);
request = indexedDB.open(db_name);
request.onerror = function (error) {
if (request.result) {
request.result.close();
}
canceller();
if ((error !== undefined) &&
(error.target instanceof IDBOpenDBRequest) &&
(error.target.error instanceof DOMError)) {
......@@ -115,17 +121,16 @@
};
request.onabort = function () {
request.result.close();
canceller();
reject("Aborting connection to: " + db_name);
};
request.ontimeout = function () {
request.result.close();
reject("Connection to: " + db_name + " timeout");
};
request.onblocked = function () {
request.result.close();
canceller();
reject("Connection to: " + db_name + " was blocked");
};
......@@ -133,26 +138,32 @@
request.onupgradeneeded = handleUpgradeNeeded;
request.onversionchange = function () {
request.result.close();
canceller();
reject(db_name + " was upgraded");
};
request.onsuccess = function () {
var result;
try {
result = callback(request.result);
} catch (error) {
reject(error);
}
return new RSVP.Queue()
.push(function () {
return callback(request.result);
return result;
})
.push(function (result) {
request.result.close();
resolve(result);
.push(function (final_result) {
canceller();
resolve(final_result);
}, function (error) {
request.result.close();
canceller();
reject(error);
});
};
}
return new RSVP.Promise(resolver);
return new RSVP.Promise(resolver, canceller);
}
function waitForTransaction(db, stores, flag, callback) {
......@@ -182,14 +193,8 @@
reject(error);
});
};
tx.onerror = function (error) {
canceller();
reject(error);
};
tx.onabort = function (evt) {
reject(evt.target);
};
return tx;
tx.onerror = reject;
tx.onabort = reject;
}
return new RSVP.Promise(resolver, canceller);
}
......
/*
* Copyright 2019, Nexedi SA
*
* This program is free software: you can Use, Study, Modify and Redistribute
* it under the terms of the GNU General Public License version 3, or (at your
* option) any later version, as published by the Free Software Foundation.
*
* You can also Link and Combine this program with other software covered by
* the terms of any of the Free Software licenses or any of the Open Source
* Initiative approved licenses and Convey the resulting work. Corresponding
* source of such a combination shall include the source code for all other
* software used.
*
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See COPYING file for full licensing terms.
* See https://www.nexedi.com/licensing for rationale and options.
*/
/**
* JIO Linshare Storage. Type = "linshare".
* Linshare "database" storage.
* http://download.linshare.org/components/linshare-core/2.2.2/
* Can't set up id, implied can't put new document
*/
/*global jIO, RSVP, UriTemplate, FormData, Blob*/
/*jslint nomen: true*/
(function (jIO, RSVP, UriTemplate, FormData, Blob) {
"use strict";
function makeRequest(storage, uuid, options, download) {
if (options === undefined) {
options = {};
}
if (options.xhrFields === undefined) {
options.xhrFields = {};
}
if (options.headers === undefined) {
options.headers = {};
}
// Prefer JSON by default
if (download === true) {
options.url = storage._blob_template.expand({uuid: uuid});
options.dataType = 'blob';
} else {
options.url = storage._url_template.expand({uuid: uuid || ""});
if (!options.headers.hasOwnProperty('Accept')) {
options.headers.Accept = 'application/json';
options.dataType = 'json';
}
}
// Use cookie based auth
if (storage.hasOwnProperty('_access_token')) {
options.headers.Authorization = "Basic " + storage._access_token;
} else {
options.xhrFields.withCredentials = true;
}
return new RSVP.Queue()
.push(function () {
return jIO.util.ajax(options);
})
.push(function (event) {
if (download === true) {
return (
event.target.response ||
// sinon does not fill the response attribute
new Blob([event.target.responseText], {type: 'text/plain'})
);
}
return (
event.target.response ||
// sinon does not fill the response attribute
JSON.parse(event.target.responseText)
);
});
}
/**
* The JIO Linshare Storage extension
*
* @class LinshareStorage
* @constructor
*/
function LinshareStorage(spec) {
if (typeof spec.url !== "string" || !spec.url) {
throw new TypeError("Linshare 'url' must be a string " +
"which contains more than one character.");
}
this._url_template = UriTemplate.parse(
spec.url + '/linshare/webservice/rest/user/v2/documents/{uuid}'
);
this._blob_template = UriTemplate.parse(
spec.url + '/linshare/webservice/rest/user/v2/documents/{uuid}/download'
);
if (spec.hasOwnProperty('access_token')) {
this._access_token = spec.access_token;
}
}
var capacity_list = ['list', 'include'];
LinshareStorage.prototype.hasCapacity = function (name) {
return (capacity_list.indexOf(name) !== -1);
};
function sortByModificationDate(entry1, entry2) {
var date1 = entry1.modificationDate,
date2 = entry2.modificationDate;
return (date1 === date2) ? 0 : ((date1 < date2) ? 1 : -1);
}
function getDocumentList(storage, options) {
return makeRequest(storage, "", {
type: "GET"
})
.push(function (entry_list) {
// Linshare only allow to get the full list of documents
// First, sort the entries by modificationDate in order to
// drop the 'old' entries with the same 'name'
// (as linshare does not to update an existing doc)
entry_list.sort(sortByModificationDate);
// Only return one document per name
// Keep the newer document
var entry_dict = {},
i,
len = entry_list.length,
entry_name,
entry,
result_list = [];
for (i = 0; i < len; i += 1) {
entry_name = entry_list[i].name;
// If we only need one precise name, no need to check the others
if (!options.hasOwnProperty('only_id') ||
(options.only_id === entry_name)) {
if (!entry_dict.hasOwnProperty(entry_name)) {
entry = {
id: entry_name,
value: {},
_linshare_uuid: entry_list[i].uuid
};
if (options.include_docs === true) {
try {
entry.doc = JSON.parse(entry_list[i].metaData) || {};
} catch (error) {
// Metadata are not always JSON
entry.doc = {};
}
}
result_list.push(entry);
if (options.all_revision !== true) {
// If we only want to fetch 'one revision',
// ie, the latest document matching this id
entry_dict[entry_name] = null;
if (options.only_id === entry_name) {
// Document has been found, no need to check all the others
break;
}
}
}
}
}
return result_list;
});
}
LinshareStorage.prototype.buildQuery = function (options) {
return getDocumentList(this, {
include_docs: options.include_docs
});
};
LinshareStorage.prototype.get = function (id) {
// It is not possible to get a document by its name
// The only way is to list all of them, and find it manually
return getDocumentList(this, {
include_docs: true,
only_id: id
})
.push(function (result_list) {
if (result_list.length === 1) {
return result_list[0].doc;
}
throw new jIO.util.jIOError(
"Can't find document with id : " + id,
404
);
});
};
function createLinshareDocument(storage, id, doc, blob) {
var data = new FormData();
data.append('file', blob, id);
data.append('filesize', blob.size);
data.append('filename', id);
data.append('description', doc.title || doc.description || '');
data.append('metadata', jIO.util.stringify(doc));
return makeRequest(storage, '', {
type: 'POST',
data: data
});
}
LinshareStorage.prototype.put = function (id, doc) {
var storage = this;
return getDocumentList(storage, {
include_docs: true,
only_id: id
})
.push(function (result_list) {
if (result_list.length === 1) {
// Update existing document metadata
var data = {
uuid: result_list[0]._linshare_uuid,
metaData: jIO.util.stringify(doc),
name: id,
description: doc.title || doc.description || ''
};
return makeRequest(storage, result_list[0]._linshare_uuid, {
type: 'PUT',
headers: {'Content-Type': 'application/json'},
data: jIO.util.stringify(data)
});
}
// Create a new one
return createLinshareDocument(storage, id, doc, new Blob());
});
};
LinshareStorage.prototype.remove = function (id) {
var storage = this;
// Delete all entries matching the id
return getDocumentList(storage, {
only_id: id,
all_revision: true
})
.push(function (result_list) {
var promise_list = [],
i,
len = result_list.length;
for (i = 0; i < len; i += 1) {
promise_list.push(
makeRequest(storage, result_list[i]._linshare_uuid, {
type: "DELETE"
})
);
}
return RSVP.all(promise_list);
});
};
LinshareStorage.prototype.allAttachments = function (id) {
return this.get(id)
.push(function () {
return {enclosure: {}};
});
};
function restrictAttachmentId(name) {
if (name !== "enclosure") {
throw new jIO.util.jIOError(
"attachment name " + name + " is forbidden in linshare",
400
);
}
}
LinshareStorage.prototype.putAttachment = function (id, name, blob) {
restrictAttachmentId(name);
var storage = this;
return storage.get(id)
.push(function (doc) {
// Create a new document with the same id but a different blob content
return createLinshareDocument(storage, id, doc, blob);
});
};
LinshareStorage.prototype.getAttachment = function (id, name) {
restrictAttachmentId(name);
var storage = this;
// It is not possible to get a document by its name
// The only way is to list all of them, and find it manually
return getDocumentList(storage, {
only_id: id
})
.push(function (result_list) {
if (result_list.length === 1) {
return makeRequest(storage, result_list[0]._linshare_uuid, {
}, true);
}
throw new jIO.util.jIOError(
"Can't find document with id : " + id,
404
);
});
};
jIO.addStorage('linshare', LinshareStorage);
}(jIO, RSVP, UriTemplate, FormData, Blob));
......@@ -190,11 +190,10 @@
try {
return window.atob(str);
} catch (err) {
var buffer;
if (str instanceof Buffer) {
buffer = str;
} else {
buffer = Buffer.from(str.toString(), 'base64');
var buffer = Buffer.from(str.toString(), 'base64');
// Provide the same behaviour than the browser atob
if (buffer.toString('base64') !== str) {
throw new Error('The string to be decoded is not correctly encoded.');
}
return buffer.toString('binary');
}
......@@ -206,13 +205,7 @@
try {
return window.btoa(str);
} catch (err) {
var buffer;
if (str instanceof Buffer) {
buffer = str;
} else {
buffer = Buffer.from(str.toString(), 'binary');
}
return buffer.toString('base64');
return Buffer.from(str.toString(), 'binary').toString('base64');
}
}
......@@ -220,8 +213,8 @@
}(window, WeakMap, ArrayBuffer, Uint8Array));
var XMLHttpRequest = global.XMLHttpRequest || module.exports,
Blob = window.Blob,
global.XMLHttpRequest = module.exports;
var Blob = window.Blob,
atob = window.atob,
btoa = window.btoa,
FileReader = window.FileReader,
......
......@@ -19,7 +19,7 @@
*/
/*global window */
(function (window, jIO, Blob) {
(function (window, jIO, Blob, RSVP) {
"use strict";
var FormData,
......@@ -41,32 +41,71 @@
};
window.FormData = FormData;
function convertToBlob(promise, convert) {
if (!convert) {
return promise;
}
var result;
if (promise instanceof RSVP.Queue) {
result = promise;
} else {
result = new RSVP.Queue()
.push(function () {
return promise;
});
}
return result
.push(function (evt) {
evt.target.response = new Blob(
[evt.target.response || evt.target.responseText],
{type: evt.target.getResponseHeader('Content-Type')}
);
return evt;
});
}
originalAjax = jIO.util.ajax;
jIO.util.ajax = function ajax(param) {
var result,
need_convertion = (param.dataType === 'blob');
// Copy the param dict document (no need for deep copy) to
// allow tests to check them
param = Object.assign({}, param);
if (need_convertion) {
param.dataType = 'arraybuffer';
}
if (param.data instanceof Blob) {
// Blob is not supported by xhr2, so convert to ArrayBuffer instead
return jIO.util.readBlobAsArrayBuffer(param.data).then(function (data) {
param.data = data.target.result;
return originalAjax(param);
});
}
if (param.data instanceof FormData) {
result = new RSVP.Queue()
.push(function () {
return jIO.util.readBlobAsArrayBuffer(param.data);
})
.push(function (evt) {
param.data = evt.target.result;
return originalAjax(param);
});
} else if (param.data instanceof FormData) {
// Implement minimal FormData for erp5storage
if (!param.hasOwnProperty('headers')) {
param.headers = {};
} else {
// Copy the param dict document (no need for deep copy) to
// allow tests to check them
param.headers = Object.assign({}, param.headers);
}
param.headers["Content-Type"] = "multipart\/form-data; boundary=" +
param.data.boundary;
param.data.body += '--' + param.data.boundary + '--\r\n';
param.data = param.data.body;
return originalAjax(param);
result = originalAjax(param);
} else {
result = originalAjax(param);
}
return originalAjax(param);
return convertToBlob(result, need_convertion);
};
}(window, window.jIO, window.Blob));
}(window, window.jIO, window.Blob, window.RSVP));
// Define a global variable to allow storages to access jIO
var jIO = window.jIO,
......
......@@ -105,7 +105,7 @@ case 12:
this.$ = $$[$0-1];
break;
case 13:
simpleQuerySetKey($$[$0], $$[$0-2]); this.$ = $$[$0];
querySetKey($$[$0], $$[$0-2]); this.$ = $$[$0];
break;
case 15:
$$[$0].operator = $$[$0-1] ; this.$ = $$[$0];
......@@ -162,7 +162,6 @@ parse: function parse(input) {
vstack.length = vstack.length - n;
lstack.length = lstack.length - n;
}
_token_stack:
var lex = function () {
var token;
token = lexer.lex() || EOF;
......
......@@ -61,7 +61,7 @@ boolean_expression
expression
: LEFT_PARENTHESE search_text RIGHT_PARENTHESE { $$ = $2; }
| WORD DEFINITION expression { simpleQuerySetKey($3, $1); $$ = $3; }
| WORD DEFINITION expression { querySetKey($3, $1); $$ = $3; }
| value { $$ = $1; }
;
......
......@@ -58,11 +58,11 @@ var arrayExtend = function () {
}
return {type:"complex",operator:operator,query_list:query_list2};
}, simpleQuerySetKey = function (query, key) {
}, querySetKey = function (query, key) {
var i;
if (query.type === "complex") {
for (i = 0; i < query.query_list.length; ++i) {
simpleQuerySetKey (query.query_list[i],key);
querySetKey(query.query_list[i], key);
}
return true;
}
......
......@@ -573,21 +573,8 @@
* #crossLink "Query/toString:method"
*/
ComplexQuery.prototype.toString = function () {
var str_list = [], this_operator = this.operator;
if (this.operator === "NOT") {
str_list.push("NOT (");
str_list.push(this.query_list[0].toString());
str_list.push(")");
return str_list.join(" ");
}
this.query_list.forEach(function (query) {
str_list.push("(");
str_list.push(query.toString());
str_list.push(")");
str_list.push(this_operator);
});
str_list.length -= 1;
return str_list.join(" ");
/*global objectToSearchText */
return objectToSearchText(this.toJSON());
};
/**
......@@ -686,21 +673,60 @@
};
function objectToSearchText(query) {
var str_list = [];
if (query.type === "complex") {
str_list.push("(");
(query.query_list || []).forEach(function (sub_query) {
str_list.push(objectToSearchText(sub_query));
str_list.push(query.operator);
});
str_list.length -= 1;
str_list.push(")");
return str_list.join(" ");
}
var i = 0,
query_list = null,
string_list = null,
operator = "",
common_key = "";
if (query.type === "simple") {
return (query.key ? query.key + ": " : "") +
(query.operator || "") + ' "' + query.value + '"';
}
if (query.type === "complex") {
query_list = query.query_list;
if (!query_list || query_list.length === 0) {
return "";
}
operator = query.operator || "";
if (operator === "NOT") {
// fallback to AND operator if several queries are given
// i.e. `NOT ( a AND b )`
return "NOT ( " + objectToSearchText(
{type: "complex", operator: "AND", query_list: query_list}
) + " )";
}
if (query_list.length === 1) {
return objectToSearchText(query_list[0]);
}
common_key = query_list[i].key;
for (i = 1; i < query_list.length; i += 1) {
if (query_list[i].type !== "simple" ||
query_list[i].key !== common_key) {
break;
}
}
string_list = [];
if (i === query_list.length) {
for (i = 0; i < query_list.length; i += 1) {
string_list.push(
(query_list[i].operator || "") +
' "' + query_list[i].value + '"'
);
}
} else {
common_key = "";
for (i = 0; i < query_list.length; i += 1) {
string_list.push(objectToSearchText(query_list[i]));
}
}
if (string_list.length > 1) {
return (common_key ? common_key + ": " : "") +
"( " + string_list.join(" " + operator + " ") + " )";
}
return (common_key ? common_key + ": " : "") +
string_list[0];
}
throw new TypeError("This object is not a query");
}
......@@ -868,8 +894,7 @@
* #crossLink "Query/toString:method"
*/
SimpleQuery.prototype.toString = function () {
return (this.key ? this.key + ":" : "") +
(this.operator ? " " + this.operator : "") + ' "' + this.value + '"';
return objectToSearchText(this.toJSON());
};
/**
......
......@@ -709,6 +709,8 @@
this.server.autoRespond = true;
this.server.autoRespondAfter = 5;
this.spy_ajax = sinon.spy(jIO.util, "ajax");
this.jio = jIO.createJIO({
type: "dropbox",
access_token: token
......@@ -717,6 +719,8 @@
teardown: function () {
this.server.restore();
delete this.server;
this.spy_ajax.restore();
delete this.spy_ajax;
}
});
......@@ -789,14 +793,15 @@
test("putAttachment document", function () {
var blob = new Blob(["foo"], {"type": "xapplication/foo"}),
url_put_att = "https://content.dropboxapi.com/2/files/upload",
server = this.server;
server = this.server,
context = this;
this.server.respondWith("POST", url_put_att, [204, {
"Content-Type": "text/xml"
}, ""]);
stop();
expect(7);
expect(11);
this.jio.putAttachment(
"/putAttachment1/",
......@@ -804,20 +809,26 @@
blob
)
.then(function () {
ok(context.spy_ajax.calledOnce, "ajax count " +
context.spy_ajax.callCount);
equal(context.spy_ajax.firstCall.args[0].type, "POST");
equal(context.spy_ajax.firstCall.args[0].url, url_put_att);
deepEqual(context.spy_ajax.firstCall.args[0].xhrFields, undefined);
deepEqual(context.spy_ajax.firstCall.args[0].headers, {
"Authorization": "Bearer sample_token",
"Content-Type": "application/octet-stream",
"Dropbox-API-Arg": '{"path":"/putAttachment1/attachment1",' +
'"mode":"overwrite",' +
'"autorename":false,"mute":false}'
});
equal(context.spy_ajax.firstCall.args[0].data, blob);
equal(server.requests.length, 1);
equal(server.requests[0].method, "POST");
equal(server.requests[0].url, url_put_att);
equal(server.requests[0].status, 204);
equal(server.requests[0].responseText, "");
deepEqual(server.requests[0].requestHeaders, {
"Authorization": "Bearer sample_token",
"Content-Type": "application/octet-stream;charset=utf-8",
"Dropbox-API-Arg": '{"path":"/putAttachment1/attachment1",' +
'"mode":"overwrite",' +
'"autorename":false,"mute":false}'
});
equal(server.requests[0].requestBody, blob);
})
.fail(function (error) {
ok(false, error);
......@@ -992,8 +1003,12 @@
type: "dropbox",
access_token: token
});
this.spy_ajax = sinon.spy(jIO.util, "ajax");
},
teardown: function () {
this.spy_ajax.restore();
delete this.spy_ajax;
this.server.restore();
delete this.server;
}
......@@ -1064,7 +1079,7 @@
test("getAttachment document", function () {
var url = "https://content.dropboxapi.com/2/files/download",
server = this.server;
context = this;
this.server.respondWith("POST", url, [200, {
"Content-Type": "text/xplain"
}, "foo\nbaré"]);
......@@ -1077,20 +1092,20 @@
"attachment1"
)
.then(function (result) {
equal(server.requests.length, 1);
equal(server.requests[0].method, "POST");
equal(server.requests[0].url, url);
equal(server.requests[0].status, 200);
equal(server.requests[0].requestBody, undefined);
equal(server.requests[0].responseText, "foo\nbaré");
deepEqual(server.requests[0].requestHeaders, {
equal(context.spy_ajax.callCount, 1);
equal(context.spy_ajax.firstCall.args[0].type, "POST");
equal(context.spy_ajax.firstCall.args[0].url, url);
equal(context.spy_ajax.firstCall.args[0].data, undefined);
equal(context.spy_ajax.firstCall.args[0].dataType, 'blob');
deepEqual(context.spy_ajax.firstCall.args[0].xhrFields, undefined);
deepEqual(context.spy_ajax.firstCall.args[0].headers, {
"Authorization": "Bearer sample_token",
"Content-Type": "text/plain;charset=utf-8",
"Dropbox-API-Arg": '{"path":"/getAttachment1/attachment1"}'
});
ok(result instanceof Blob, "Data is Blob");
deepEqual(result.type, "text/xplain", "Check mimetype");
return jIO.util.readBlobAsText(result);
})
.then(function (result) {
......
......@@ -427,6 +427,8 @@
this.server.autoRespond = true;
this.server.autoRespondAfter = 5;
this.spy_ajax = sinon.spy(jIO.util, "ajax");
this.jio = jIO.createJIO({
type: "gdrive",
access_token: token
......@@ -435,6 +437,8 @@
teardown: function () {
this.server.restore();
delete this.server;
this.spy_ajax.restore();
delete this.spy_ajax;
}
});
......@@ -464,13 +468,14 @@
var blob = new Blob(["foo"]),
url_put_att = domain + "/upload/drive/v2/files/sampleId?" +
"uploadType=media&access_token=" + token,
server = this.server;
server = this.server,
context = this;
this.server.respondWith("PUT", url_put_att, [204, {
"Content-Type": "text/xml"
}, '{"mimeType": "text/xml"}']);
stop();
expect(7);
expect(11);
this.jio.putAttachment(
"sampleId",
......@@ -478,16 +483,20 @@
blob
)
.then(function () {
ok(context.spy_ajax.calledOnce, "ajax count " +
context.spy_ajax.callCount);
equal(context.spy_ajax.firstCall.args[0].type, "PUT");
equal(context.spy_ajax.firstCall.args[0].url, url_put_att);
deepEqual(context.spy_ajax.firstCall.args[0].xhrFields, undefined);
deepEqual(context.spy_ajax.firstCall.args[0].headers, undefined);
equal(context.spy_ajax.firstCall.args[0].data, blob);
equal(server.requests.length, 1);
equal(server.requests[0].method, "PUT");
equal(server.requests[0].url, url_put_att);
equal(server.requests[0].status, 204);
equal(server.requests[0].responseText, "{\"mimeType\": \"text/xml\"}");
deepEqual(server.requests[0].requestHeaders, {
"Content-Type": "text/plain;charset=utf-8"
});
equal(server.requests[0].requestBody, blob);
})
.fail(function (error) {
ok(false, error);
......
......@@ -964,6 +964,44 @@
start();
});
});
test("group_by is not handled", function () {
stop();
expect(3);
function StorageGroupCapacity() {
return this;
}
StorageGroupCapacity.prototype.hasCapacity = function (capacity) {
return ((capacity === "list") || (capacity === "group"));
};
jIO.addStorage('querystoragegroupcapacity', StorageGroupCapacity);
var jio = jIO.createJIO({
type: "query",
sub_storage: {
type: "querystoragegroupcapacity"
}
});
jio.allDocs({
group_by: ["title"]
})
.then(function () {
ok(false, 'Must fail as group is not handled');
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.status_code, 501);
equal(error.message,
"Capacity 'group' is not implemented on 'query'");
})
.always(function () {
start();
});
});
/////////////////////////////////////////////////////////////////
// queryStorage.repair
/////////////////////////////////////////////////////////////////
......
......@@ -3825,7 +3825,7 @@
test("signature document is not synced", function () {
stop();
expect(6);
expect(7);
var context = this;
......@@ -3836,41 +3836,50 @@
report_level: 1000,
signature_hash_key: 'foo_etag',
local_sub_storage: {
type: "uuid",
type: "storagealldocsdynamicselect",
sub_storage: {
type: "storagealldocsdynamicselect",
type: "query",
sub_storage: {
type: "query",
sub_storage: {
type: "document",
document_id: "/",
sub_storage: {
type: "local",
sessiononly: true
}
}
type: "memory"
}
}
},
remote_sub_storage: {
type: "uuid",
type: "storagealldocsdynamicselect",
sub_storage: {
type: "storagealldocsdynamicselect",
type: "query",
sub_storage: {
type: "query",
sub_storage: {
type: "memory"
}
type: "memory"
}
}
}
});
context.jio.post({title: "foo", foo_etag: "foo etag"})
// Hack to ensure that the signature is stored in the
// same local storage, even if memory is used
context.jio.__storage._signature_sub_storage
.__storage._sub_storage
.__storage._sub_storage
.__storage._sub_storage
.__storage._sub_storage
.__storage._database =
context.jio.__storage._local_sub_storage
.__storage._sub_storage
.__storage._sub_storage
.__storage._database;
context.jio.put('barfoo', {title: "foo", foo_etag: "foo etag"})
.then(function () {
return context.jio.repair();
})
.then(function () {
// Check that signature is a local document
// Otherwise, the test is meaningless
return context.jio.__storage._local_sub_storage.get(
context.jio.__storage._signature_hash
);
})
.then(function (result) {
deepEqual(result, {});
return context.jio.__storage._remote_sub_storage.get(
context.jio.__storage._signature_hash
);
......@@ -3878,7 +3887,7 @@
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find document: " +
"_replicate_bc03c70b5346672bb87b14c4e17ed1e407676a41");
"_replicate_e9fa6706a8a6a961db3f9de41d44bdf11f25fb30");
equal(error.status_code, 404);
})
.then(function () {
......@@ -3892,7 +3901,7 @@
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find document: " +
"_replicate_bc03c70b5346672bb87b14c4e17ed1e407676a41");
"_replicate_e9fa6706a8a6a961db3f9de41d44bdf11f25fb30");
equal(error.status_code, 404);
})
.fail(function (error) {
......
......@@ -3298,7 +3298,7 @@
test("signature document is not synced", function () {
stop();
expect(6);
expect(7);
var context = this;
......@@ -3307,26 +3307,34 @@
this.jio = jIO.createJIO({
type: "replicate",
local_sub_storage: {
type: "uuid",
sub_storage: {
type: "document",
document_id: "/",
sub_storage: {
type: "local",
sessiononly: true
}
}
type: "memory"
},
remote_sub_storage: {
type: "memory"
}
});
// Hack to ensure that the signature is stored in the
// same local storage, even if memory is used
context.jio.__storage._signature_sub_storage
.__storage._sub_storage
.__storage._sub_storage