...
 
Commits (14)
......@@ -26,7 +26,7 @@ TESTDIR = test
EXAMPLEDIR = examples
EXTERNALDIR = external
VERSION = 3.36.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))
#############################################
......@@ -149,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)
......@@ -254,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.
{
"name": "jio",
"version": "v3.36.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));
......@@ -419,7 +419,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 +487,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 +560,10 @@
}
}
if (options.group_by) {
group_list = options.group_by;
}
if (selection_domain) {
selection_domain = JSON.stringify(selection_domain);
}
......@@ -572,6 +577,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);
}
......
......@@ -150,7 +150,12 @@
_linshare_uuid: entry_list[i].uuid
};
if (options.include_docs === true) {
entry.doc = JSON.parse(entry_list[i].metaData) || {};
try {
entry.doc = JSON.parse(entry_list[i].metaData) || {};
} catch (error) {
// Metadata are not always JSON
entry.doc = {};
}
}
result_list.push(entry);
......
......@@ -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];
......
......@@ -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);
......
......@@ -294,6 +294,55 @@
});
});
test("get all documents, include docs and unexpected metadata", function () {
var search_url = domain + "/linshare/webservice/rest/user/v2/documents/",
search_result = JSON.stringify([
{
uuid: 'uuid1',
name: 'foo1',
modificationDate: '2',
metaData: 'unexpectedfoo'
}
]),
server = this.server;
this.server.respondWith("GET", search_url, [200, {
"Content-Type": "application/json"
}, search_result]);
stop();
expect(7);
this.jio.allDocs({include_docs: true})
.then(function (result) {
deepEqual(result, {
data: {
rows: [{
_linshare_uuid: "uuid1",
id: "foo1",
value: {},
doc: {}
}],
total_rows: 1
}
}, "Check document");
equal(server.requests.length, 1);
equal(server.requests[0].method, "GET");
equal(server.requests[0].url, search_url);
equal(server.requests[0].requestBody, undefined);
equal(server.requests[0].withCredentials, true);
deepEqual(server.requests[0].requestHeaders, {
"Accept": "application/json"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("get all documents and keep only one doc per name", function () {
var search_url = domain + "/linshare/webservice/rest/user/v2/documents/",
search_result = JSON.stringify([
......
......@@ -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
.__storage._database =
context.jio.__storage._local_sub_storage
.__storage._database;
context.jio.post({"title": "foo"})
context.jio.put('barfoo', {"title": "foo"})
.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
);
......@@ -3334,7 +3342,7 @@
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find document: " +
"_replicate_8662994dcefb3a2ceec61e86953efda8ec6520d6");
"_replicate_a0538a9def720b35ac7fd813d2ca008a5183375a");
equal(error.status_code, 404);
})
.then(function () {
......@@ -3348,7 +3356,7 @@
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Cannot find document: " +
"_replicate_8662994dcefb3a2ceec61e86953efda8ec6520d6");
"_replicate_a0538a9def720b35ac7fd813d2ca008a5183375a");
equal(error.status_code, 404);
})
.fail(function (error) {
......
......@@ -3713,7 +3713,7 @@
test("use 4 parallel operation", function () {
stop();
expect(40);
expect(24);
var context = this,
order_number = 0,
......@@ -3728,20 +3728,9 @@
'stop put 1',
'start getAttachment 00',
'stop getAttachment 00',
'start putAttachment 00',
'start getAttachment 01',
'stop getAttachment 01',
'start putAttachment 01',
'start getAttachment 02',
'stop getAttachment 02',
'start putAttachment 02',
'start getAttachment 03',
'stop getAttachment 03',
'start putAttachment 03',
'stop putAttachment 00',
'stop putAttachment 01',
......@@ -3749,20 +3738,9 @@
'stop putAttachment 03',
'start getAttachment 10',
'stop getAttachment 10',
'start putAttachment 10',
'start getAttachment 11',
'stop getAttachment 11',
'start putAttachment 11',
'start getAttachment 12',
'stop getAttachment 12',
'start putAttachment 12',
'start getAttachment 13',
'stop getAttachment 13',
'start putAttachment 13',
'stop putAttachment 10',
'stop putAttachment 11',
......@@ -3808,14 +3786,14 @@
var storage = this;
return storage._sub_storage.getAttachment(id, name)
.push(undefined, function (error) {
assertExecutionOrder('stop getAttachment ' + name);
// assertExecutionOrder('stop getAttachment ' + name);
throw error;
});
};
StorageFourParallelOperation.prototype.putAttachment = function (id, name,
blob) {
assertExecutionOrder('start putAttachment ' + name);
// assertExecutionOrder('start putAttachment ' + name);
var storage = this;
return storage._sub_storage.putAttachment(id, name, blob)
.push(function (result) {
......
......@@ -17,15 +17,15 @@
* See COPYING file for full licensing terms.
* See https://www.nexedi.com/licensing for rationale and options.
*/
/*global setTimeout*/
(function (jIO, QUnit, setTimeout) {
(function (jIO, QUnit) {
"use strict";
var test = QUnit.test,
equal = QUnit.equal,
stop = QUnit.stop,
start = QUnit.start,
expect = QUnit.expect,
module = QUnit.module;
module = QUnit.module,
ok = QUnit.ok;
/////////////////////////////////////////////////////////////////
// util.stringify
......@@ -55,44 +55,29 @@
/////////////////////////////////////////////////////////////////
// util.ajax
/////////////////////////////////////////////////////////////////
module("util.ajax", {
setup: function () {
var context = this;
this.jioerror = jIO.util.jIOError;
this.error_spy = {};
function fakejIOError(message, status_code) {
context.error_spy.message = message;
context.error_spy.status_code = status_code;
}
fakejIOError.prototype = new Error();
fakejIOError.prototype.constructor = fakejIOError;
jIO.util.jIOError = fakejIOError;
},
teardown: function () {
jIO.util.jIOError = this.jioerror;
}
});
module("util.ajax");
test("ajax timeout", function () {
var timeout = 1,
context = this;
var timeout = 1;
stop();
expect(2);
jIO.util.ajax({
type: 'GET',
url: "//www.foo/com/bar",
timeout: timeout
});
setTimeout(function () {
start();
equal(context.error_spy.message, "Gateway Timeout");
equal(context.error_spy.status_code, 504);
}, 10);
expect(3);
return new RSVP.Queue()
.then(function () {
return jIO.util.ajax({
type: 'GET',
url: "https://www.example.org/com/bar",
timeout: timeout
});
})
.fail(function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.message, "Gateway Timeout");
equal(error.status_code, 504);
})
.always(function () {
start();
});
});
}(jIO, QUnit, setTimeout));
\ No newline at end of file
}(jIO, QUnit));
\ No newline at end of file
......@@ -48,6 +48,7 @@
testrunner.run({
code: 'test/node/node-require.js',
tests: [
'test/node/ajax.tests.js',
'test/jio/util.js',
'test/queries/key.tests.js',
'test/queries/key-schema.tests.js',
......
/*
* 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.
*/
/*global FormData, sinon, RSVP, jIO, QUnit, XMLHttpRequest, Blob, ArrayBuffer*/
(function (jIO, QUnit, FormData, Blob, ArrayBuffer) {
"use strict";
var test = QUnit.test,
equal = QUnit.equal,
stop = QUnit.stop,
start = QUnit.start,
expect = QUnit.expect,
ok = QUnit.ok,
module = QUnit.module;
/////////////////////////////////////////////////////////////////
// util.ajax
/////////////////////////////////////////////////////////////////
module("node.ajax", {
setup: function () {
this.server = sinon.fakeServer.create();
this.server.autoRespond = true;
this.server.autoRespondAfter = 5;
this.spySetRequestHeader = sinon.spy(XMLHttpRequest.prototype,
"setRequestHeader");
},
teardown: function () {
this.spySetRequestHeader.restore();
delete this.spySetRequestHeader;
this.server.restore();
delete this.server;
}
});
test("Blob data handling", function () {
stop();
expect(5);
var url = "https://www.example.org/com/bar",
server = this.server,
blob = new Blob(['abc']);
this.server.respondWith("POST", url, [200, {}, 'OK']);
return new RSVP.Queue()
.then(function () {
return jIO.util.ajax({
type: 'POST',
url: url,
data: blob
});
})
.then(function () {
equal(server.requests.length, 1);
equal(server.requests[0].method, "POST");
equal(server.requests[0].url, url);
ok(server.requests[0].requestBody instanceof ArrayBuffer);
return jIO.util.readBlobAsText(
new Blob([server.requests[0].requestBody])
);
})
.then(function (evt) {
equal(evt.target.result, 'abc');
})
.always(function () {
start();
});
});
test("FormData handling without headers", function () {
stop();
expect(7);
var url = "https://www.example.org/com/bar",
server = this.server,
context = this;
this.server.respondWith("POST", url, [200, {}, 'OK']);
return new RSVP.Queue()
.then(function () {
var form_data = new FormData();
form_data.append("foo", "bar");
form_data.append("foo2", "bar2", "barfilename2");
return jIO.util.ajax({
type: 'POST',
url: url,
data: form_data
});
})
.then(function () {
var content_type =
"multipart/form-data; boundary=----------------------------",
boundary;
equal(context.spySetRequestHeader.callCount, 1);
equal(context.spySetRequestHeader.firstCall.args[0], "Content-Type");
equal(context.spySetRequestHeader.firstCall.args[1].length,
content_type.length + 10);
boundary = context.spySetRequestHeader.firstCall.args[1].slice(
"multipart/form-data; boundary=".length
);
equal(server.requests.length, 1);
equal(server.requests[0].method, "POST");
equal(server.requests[0].url, url);
equal(server.requests[0].requestBody,
'--' + boundary +
'\r\nContent-Disposition: form-data; name="foo"\r\n\r\nbar\r\n' +
'--' + boundary +
'\r\nContent-Disposition: form-data; name="foo2"; ' +
'filename="barfilename2"\r\n\r\nbar2\r\n' +
'--' + boundary + '--\r\n'
);
})
.always(function () {
start();
});
});
test("FormData handling with headers", function () {
stop();
expect(9);
var url = "https://www.example.org/com/bar",
server = this.server,
context = this;
this.server.respondWith("POST", url, [200, {}, 'OK']);
return new RSVP.Queue()
.then(function () {
var form_data = new FormData();
form_data.append("foo", "bar");
form_data.append("foo2", "bar2", "barfilename2");
return jIO.util.ajax({
type: 'POST',
url: url,
data: form_data,
headers: {"bar": "foo"}
});
})
.then(function () {
var content_type =
"multipart/form-data; boundary=----------------------------",
boundary;
equal(context.spySetRequestHeader.callCount, 2);
equal(context.spySetRequestHeader.firstCall.args[0], "bar");
equal(context.spySetRequestHeader.firstCall.args[1], "foo");
equal(context.spySetRequestHeader.secondCall.args[0], "Content-Type");
equal(context.spySetRequestHeader.secondCall.args[1].length,
content_type.length + 10);
boundary = context.spySetRequestHeader.secondCall.args[1].slice(
"multipart/form-data; boundary=".length
);
equal(server.requests.length, 1);
equal(server.requests[0].method, "POST");
equal(server.requests[0].url, url);
equal(server.requests[0].requestBody,
'--' + boundary +
'\r\nContent-Disposition: form-data; name="foo"\r\n\r\nbar\r\n' +
'--' + boundary +
'\r\nContent-Disposition: form-data; name="foo2"; ' +
'filename="barfilename2"\r\n\r\nbar2\r\n' +
'--' + boundary + '--\r\n'
);
})
.always(function () {
start();
});
});
test("Blob responseType handling", function () {
stop();
expect(6);
var url = "https://www.example.org/com/bar",
server = this.server;
this.server.respondWith("POST", url, [200, {}, 'OK']);
return new RSVP.Queue()
.then(function () {
return jIO.util.ajax({
type: 'POST',
url: url,
dataType: 'blob'
});
})
.then(function (evt) {
equal(server.requests.length, 1);
equal(server.requests[0].method, "POST");
equal(server.requests[0].url, url);
equal(server.requests[0].responseType, 'arraybuffer');
equal(server.requests[0].responseText, 'OK');
ok(evt.target.response instanceof Blob, evt.target.response);
})
.always(function () {
start();
});
});
}(jIO, QUnit, FormData, Blob, ArrayBuffer));
\ No newline at end of file
......@@ -21,15 +21,14 @@
(function (require, global, Object) {
"use strict";
var sinon = require('./sinon-require'),
jIO;
global.sinon = sinon;
global.XMLHttpRequest = sinon.FakeXMLHttpRequest;