Commit b9464f1a authored by Aurélien Vermylen's avatar Aurélien Vermylen

Merge branch 'master' into clearroad

Conflicts:
	dist/jio-latest.js
	dist/jio-latest.min.js
parents c01fe22d 522f08a4
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "jio",
"version": "v3.23.1",
"version": "v3.25.0",
"license": "LGPLv3",
"author": "Nexedi SA",
"contributors": [
......
......@@ -7,27 +7,18 @@
* JIO Dropbox Storage. Type = "dropbox".
* Dropbox "database" storage.
*/
/*global Blob, jIO, RSVP, UriTemplate*/
/*global Blob, jIO, RSVP*/
/*jslint nomen: true*/
(function (jIO, RSVP, Blob, UriTemplate) {
(function (jIO, RSVP, Blob, JSON) {
"use strict";
var UPLOAD_URL = "https://content.dropboxapi.com/1/files_put/" +
"{+root}{+id}{+name}{?access_token}",
upload_template = UriTemplate.parse(UPLOAD_URL),
CREATE_DIR_URL = "https://api.dropboxapi.com/1/fileops/create_folder" +
"{?access_token,root,path}",
create_dir_template = UriTemplate.parse(CREATE_DIR_URL),
REMOVE_URL = "https://api.dropboxapi.com/1/fileops/delete/" +
"{?access_token,root,path}",
remote_template = UriTemplate.parse(REMOVE_URL),
GET_URL = "https://content.dropboxapi.com/1/files" +
"{/root,id}{+name}{?access_token}",
get_template = UriTemplate.parse(GET_URL),
//LIST_URL = 'https://api.dropboxapi.com/1/metadata/sandbox/';
METADATA_URL = "https://api.dropboxapi.com/1/metadata" +
"{/root}{+id}{?access_token}",
metadata_template = UriTemplate.parse(METADATA_URL);
var GET_URL = "https://content.dropboxapi.com/2/files/download",
UPLOAD_URL = "https://content.dropboxapi.com/2/files/upload",
REMOVE_URL = "https://api.dropboxapi.com/2/files/delete_v2",
CREATE_DIR_URL = "https://api.dropboxapi.com/2/files/create_folder_v2",
METADATA_URL = "https://api.dropboxapi.com/2/files/get_metadata",
LIST_FOLDER_URL = "https://api.dropboxapi.com/2/files/list_folder",
LIST_MORE_URL = "https://api.dropboxapi.com/2/files/list_folder/continue";
function restrictDocumentId(id) {
if (id.indexOf("/") !== 0) {
......@@ -38,7 +29,7 @@
throw new jIO.util.jIOError("id " + id + " is forbidden (no end /)",
400);
}
return id;
return id.slice(0, -1);
}
function restrictAttachmentId(id) {
......@@ -48,6 +39,66 @@
}
}
function recursiveAllAttachments(result, token, id, cursor) {
var data,
url;
if (cursor === undefined) {
data = {
"path": id,
"recursive": false,
"include_media_info": false,
"include_deleted": false,
"include_has_explicit_shared_members": false,
"include_mounted_folders": true
};
url = LIST_FOLDER_URL;
} else {
data = {"cursor": cursor};
url = LIST_MORE_URL;
}
return new RSVP.Queue()
.push(function () {
return jIO.util.ajax({
type: "POST",
url: url,
headers: {
"Authorization": "Bearer " + token,
"Content-Type": "application/json"
},
data: JSON.stringify(data)
});
})
.push(function (evt) {
var obj = JSON.parse(evt.target.response || evt.target.responseText),
i;
for (i = 0; i < obj.entries.length; i += 1) {
if (obj.entries[i][".tag"] === "file") {
result[obj.entries[i].name] = {};
}
}
if (obj.has_more) {
return recursiveAllAttachments(result, token, id, obj.cursor);
}
return result;
}, function (error) {
if (error.target !== undefined && error.target.status === 409) {
var err_content = JSON.parse(error.target.response ||
error.target.responseText);
if ((err_content.error['.tag'] === 'path') &&
(err_content.error.path['.tag'] === 'not_folder')) {
throw new jIO.util.jIOError("Not a directory: " + id + "/",
404);
}
if ((err_content.error['.tag'] === 'path') &&
(err_content.error.path['.tag'] === 'not_found')) {
throw new jIO.util.jIOError("Cannot find document: " + id + "/",
404);
}
}
throw error;
});
}
/**
* The JIO Dropbox Storage extension
*
......@@ -59,12 +110,7 @@
throw new TypeError("Access Token' must be a string " +
"which contains more than one character.");
}
if (typeof spec.root !== 'string' || !spec.root ||
(spec.root !== "dropbox" && spec.root !== "sandbox")) {
throw new TypeError("root must be 'dropbox' or 'sandbox'");
}
this._access_token = spec.access_token;
this._root = spec.root;
}
DropboxStorage.prototype.put = function (id, param) {
......@@ -79,19 +125,25 @@
.push(function () {
return jIO.util.ajax({
type: "POST",
url: create_dir_template.expand({
access_token: that._access_token,
root: that._root,
path: id
})
url: CREATE_DIR_URL,
headers: {
"Authorization": "Bearer " + that._access_token,
"Content-Type": "application/json"
},
data: JSON.stringify({"path": id, "autorename": false})
});
})
.push(undefined, function (err) {
if ((err.target !== undefined) &&
(err.target.status === 405)) {
(err.target.status === 409)) {
var err_content = JSON.parse(err.target.response ||
err.target.responseText);
if ((err_content.error['.tag'] === 'path') &&
(err_content.error.path['.tag'] === 'conflict')) {
// Directory already exists, no need to fail
return;
}
}
throw err;
});
};
......@@ -100,11 +152,12 @@
id = restrictDocumentId(id);
return jIO.util.ajax({
type: "POST",
url: remote_template.expand({
access_token: this._access_token,
root: this._root,
path: id
})
url: REMOVE_URL,
headers: {
"Authorization": "Bearer " + this._access_token,
"Content-Type": "application/json"
},
data: JSON.stringify({"path": id})
});
};
......@@ -119,64 +172,39 @@
return new RSVP.Queue()
.push(function () {
return jIO.util.ajax({
type: "GET",
url: metadata_template.expand({
access_token: that._access_token,
root: that._root,
id: id
})
type: "POST",
url: METADATA_URL,
headers: {
"Authorization": "Bearer " + that._access_token,
"Content-Type": "application/json"
},
data: JSON.stringify({"path": id})
});
})
.push(function (evt) {
var obj = JSON.parse(evt.target.response ||
evt.target.responseText);
if (obj.is_dir) {
if (obj[".tag"] === "folder") {
return {};
}
throw new jIO.util.jIOError("Not a directory: " + id, 404);
throw new jIO.util.jIOError("Not a directory: " + id + "/", 404);
}, function (error) {
if (error.target !== undefined && error.target.status === 404) {
throw new jIO.util.jIOError("Cannot find document: " + id, 404);
if (error.target !== undefined && error.target.status === 409) {
var err_content = JSON.parse(error.target.response ||
error.target.responseText);
if ((err_content.error['.tag'] === 'path') &&
(err_content.error.path['.tag'] === 'not_found')) {
throw new jIO.util.jIOError("Cannot find document: " + id + "/",
404);
}
}
throw error;
});
};
DropboxStorage.prototype.allAttachments = function (id) {
var that = this;
id = restrictDocumentId(id);
return new RSVP.Queue()
.push(function () {
return jIO.util.ajax({
type: "GET",
url: metadata_template.expand({
access_token: that._access_token,
root: that._root,
id: id
})
});
})
.push(function (evt) {
var obj = JSON.parse(evt.target.response || evt.target.responseText),
i,
result = {};
if (!obj.is_dir) {
throw new jIO.util.jIOError("Not a directory: " + id, 404);
}
for (i = 0; i < obj.contents.length; i += 1) {
if (!obj.contents[i].is_dir) {
result[obj.contents[i].path.split("/").pop()] = {};
}
}
return result;
}, function (error) {
if (error.target !== undefined && error.target.status === 404) {
throw new jIO.util.jIOError("Cannot find document: " + id, 404);
}
throw error;
});
return recursiveAllAttachments({}, this._access_token, id);
};
//currently, putAttachment will fail with files larger than 150MB,
......@@ -191,20 +219,24 @@
restrictAttachmentId(name);
return jIO.util.ajax({
type: "PUT",
url: upload_template.expand({
root: this._root,
id: id,
name: name,
access_token: this._access_token
}),
dataType: blob.type,
type: "POST",
url: UPLOAD_URL,
headers: {
"Authorization": "Bearer " + this._access_token,
"Content-Type": "application/octet-stream",
"Dropbox-API-Arg": JSON.stringify({
"path": id + "/" + name,
"mode": "overwrite",
"autorename": false,
"mute": false
})
},
data: blob
});
};
DropboxStorage.prototype.getAttachment = function (id, name) {
var that = this;
var context = this;
id = restrictDocumentId(id);
restrictAttachmentId(name);
......@@ -212,26 +244,48 @@
return new RSVP.Queue()
.push(function () {
return jIO.util.ajax({
type: "GET",
url: GET_URL,
type: "POST",
dataType: "blob",
url: get_template.expand({
root: that._root,
id: id,
name: name,
access_token: that._access_token
})
headers: {
"Authorization": "Bearer " + context._access_token,
"Dropbox-API-Arg": JSON.stringify({"path": id + "/" + name})
}
});
})
.push(function (evt) {
if (evt.target.response instanceof Blob) {
return evt.target.response;
}
return new Blob(
[evt.target.response || evt.target.responseText],
[evt.target.responseText],
{"type": evt.target.getResponseHeader('Content-Type') ||
"application/octet-stream"}
);
}, function (error) {
if (error.target !== undefined && error.target.status === 404) {
if (error.target !== undefined && error.target.status === 409) {
if (!(error.target.response instanceof Blob)) {
var err_content = JSON.parse(error.target.responseText);
if ((err_content.error['.tag'] === 'path') &&
(err_content.error.path['.tag'] === 'not_found')) {
throw new jIO.util.jIOError("Cannot find attachment: " +
id + ", " + name, 404);
id + "/, " + name, 404);
}
throw error;
}
return new RSVP.Queue()
.push(function () {
return jIO.util.readBlobAsText(error.target.response);
})
.push(function (evt) {
var err_content = JSON.parse(evt.target.result);
if ((err_content.error['.tag'] === 'path') &&
(err_content.error.path['.tag'] === 'not_found')) {
throw new jIO.util.jIOError("Cannot find attachment: " +
id + "/, " + name, 404);
}
throw error;
});
}
throw error;
});
......@@ -248,16 +302,22 @@
.push(function () {
return jIO.util.ajax({
type: "POST",
url: remote_template.expand({
access_token: that._access_token,
root: that._root,
path: id + name
})
url: REMOVE_URL,
headers: {
"Authorization": "Bearer " + that._access_token,
"Content-Type": "application/json"
},
data: JSON.stringify({"path": id + "/" + name})
});
}).push(undefined, function (error) {
if (error.target !== undefined && error.target.status === 404) {
if (error.target !== undefined && error.target.status === 409) {
var err_content = JSON.parse(error.target.response ||
error.target.responseText);
if ((err_content.error['.tag'] === 'path_lookup') &&
(err_content.error.path_lookup['.tag'] === 'not_found')) {
throw new jIO.util.jIOError("Cannot find attachment: " +
id + ", " + name, 404);
id + "/, " + name, 404);
}
}
throw error;
});
......@@ -265,4 +325,4 @@
jIO.addStorage('dropbox', DropboxStorage);
}(jIO, RSVP, Blob, UriTemplate));
}(jIO, RSVP, Blob, JSON));
/*jslint nomen: true*/
/*global RSVP*/
(function (jIO, RSVP) {
/*global RSVP, jiodate*/
(function (jIO, RSVP, jiodate) {
"use strict";
function dateType(str) {
return jiodate.JIODate(new Date(str).toISOString());
}
function initKeySchema(storage, spec) {
var property;
for (property in spec.schema) {
if (spec.schema.hasOwnProperty(property)) {
if (spec.schema[property].type === "string" &&
spec.schema[property].format === "date-time") {
storage._key_schema.key_set[property] = {
read_from: property,
cast_to: "dateType"
};
if (storage._key_schema.cast_lookup.dateType === undefined) {
storage._key_schema.cast_lookup.dateType = dateType;
}
} else {
throw new jIO.util.jIOError(
"Wrong schema for property: " + property,
400
);
}
}
}
}
/**
* The jIO QueryStorage extension
*
......@@ -11,7 +38,8 @@
*/
function QueryStorage(spec) {
this._sub_storage = jIO.createJIO(spec.sub_storage);
this._key_schema = spec.key_schema;
this._key_schema = {key_set: {}, cast_lookup: {}};
initKeySchema(this, spec);
}
QueryStorage.prototype.get = function () {
......@@ -211,4 +239,4 @@
jIO.addStorage('query', QueryStorage);
}(jIO, RSVP));
}(jIO, RSVP, jiodate));
......@@ -44,8 +44,8 @@
* @param {String} [way="ascending"] 'ascending' or 'descending'
* @return {Function} The sort function
*/
function sortFunction(key, way) {
var result;
function sortFunction(key, way, key_schema) {
var result, cast_to;
if (way === 'descending') {
result = 1;
} else if (way === 'ascending') {
......@@ -54,6 +54,29 @@
throw new TypeError("Query.sortFunction(): " +
"Argument 2 must be 'ascending' or 'descending'");
}
if (key_schema !== undefined &&
key_schema.key_set !== undefined &&
key_schema.key_set[key] !== undefined &&
key_schema.key_set[key].cast_to !== undefined) {
if (typeof key_schema.key_set[key].cast_to === "string") {
cast_to = key_schema.cast_lookup[key_schema.key_set[key].cast_to];
} else {
cast_to = key_schema.key_set[key].cast_to;
}
return function (a, b) {
var f_a = cast_to(a[key]), f_b = cast_to(b[key]);
if (typeof f_b.cmp === 'function') {
return result * f_b.cmp(f_a);
}
if (f_a > f_b) {
return -result;
}
if (f_a < f_b) {
return result;
}
return 0;
};
}
return function (a, b) {
// this comparison is 5 times faster than json comparison
var i, l;
......@@ -78,6 +101,7 @@
};
}
/**
* Sort a list of items, according to keys and directions.
*
......@@ -85,7 +109,7 @@
* @param {Array} list The item list to sort
* @return {Array} The filtered list
*/
function sortOn(sort_on_option, list) {
function sortOn(sort_on_option, list, key_schema) {
var sort_index;
if (!Array.isArray(sort_on_option)) {
throw new TypeError("jioquery.sortOn(): " +
......@@ -95,7 +119,8 @@
sort_index -= 1) {
list.sort(sortFunction(
sort_on_option[sort_index][0],
sort_on_option[sort_index][1]
sort_on_option[sort_index][1],
key_schema
));
}
return list;
......@@ -158,6 +183,35 @@
return list;
}
function checkKeySchema(key_schema) {
var prop;
if (key_schema !== undefined) {
if (typeof key_schema !== 'object') {
throw new TypeError("Query().create(): " +
"key_schema is not of type 'object'");
}
// key_set is mandatory
if (key_schema.key_set === undefined) {
throw new TypeError("Query().create(): " +
"key_schema has no 'key_set' property");
}
for (prop in key_schema) {
if (key_schema.hasOwnProperty(prop)) {
switch (prop) {
case 'key_set':
case 'cast_lookup':
case 'match_lookup':
break;
default:
throw new TypeError("Query().create(): " +
"key_schema has unknown property '" + prop + "'");
}
}
}
}
}
/**
* The query to use to filter a list of objects.
* This is an abstract class.
......@@ -165,7 +219,10 @@
* @class Query
* @constructor
*/
function Query() {
function Query(key_schema) {
checkKeySchema(key_schema);
this._key_schema = key_schema || {};
/**
* Called before parsing the query. Must be overridden!
......@@ -238,7 +295,7 @@
}
if (option.sort_on) {
sortOn(option.sort_on, item_list);
sortOn(option.sort_on, item_list, this._key_schema);
}
if (option.limit) {
......@@ -577,7 +634,7 @@
*/
QueryFactory.create = function (object, key_schema) {
if (object === "") {
return new Query();
return new Query(key_schema);
}
if (typeof object === "string") {
object = parseStringToObject(object);
......@@ -609,35 +666,6 @@
throw new TypeError("This object is not a query");
}
function checkKeySchema(key_schema) {
var prop;
if (key_schema !== undefined) {
if (typeof key_schema !== 'object') {
throw new TypeError("SimpleQuery().create(): " +
"key_schema is not of type 'object'");
}
// key_set is mandatory
if (key_schema.key_set === undefined) {
throw new TypeError("SimpleQuery().create(): " +
"key_schema has no 'key_set' property");
}
for (prop in key_schema) {
if (key_schema.hasOwnProperty(prop)) {
switch (prop) {
case 'key_set':
case 'cast_lookup':
case 'match_lookup':
break;
default:
throw new TypeError("SimpleQuery().create(): " +
"key_schema has unknown property '" + prop + "'");
}
}
}
}
}
/**
* The SimpleQuery inherits from Query, and compares one metadata value
*
......@@ -649,11 +677,7 @@
* @param {String} spec.value The value of the metadata to compare
*/
function SimpleQuery(spec, key_schema) {
Query.call(this);
checkKeySchema(key_schema);
this._key_schema = key_schema || {};
Query.call(this, key_schema);
/**
* Operator to use to compare object values
......
......@@ -10,7 +10,6 @@
deepEqual = QUnit.deepEqual,
equal = QUnit.equal,
module = QUnit.module,
throws = QUnit.throws,
token = "sample_token";
/////////////////////////////////////////////////////////////////
......@@ -21,31 +20,10 @@
test("create storage", function () {
var jio = jIO.createJIO({
type: "dropbox",
access_token: token,
root : "sandbox"
access_token: token
});
equal(jio.__type, "dropbox");
deepEqual(jio.__storage._access_token, token);
deepEqual(jio.__storage._root, "sandbox");
});
test("reject invalid root", function () {
throws(
function () {
jIO.createJIO({
type: "dropbox",
access_token: token,
root : "foobar"
});
},
function (error) {
ok(error instanceof TypeError);
equal(error.message,
"root must be 'dropbox' or 'sandbox'");
return true;
}
);
});
/////////////////////////////////////////////////////////////////
......@@ -60,8 +38,7 @@
this.jio = jIO.createJIO({
type: "dropbox",
access_token: token,
root : "dropbox"
access_token: token
});
},
teardown: function () {
......@@ -71,8 +48,7 @@
});
test("put document", function () {
var url = "https://api.dropboxapi.com/1/fileops/create_folder?access_token="
+ token + "&root=dropbox&path=%2Fput1%2F",
var url = "https://api.dropboxapi.com/2/files/create_folder_v2",
server = this.server;
this.server.respondWith("POST", url, [201, {
"Content-Type": "text/xml"
......@@ -87,10 +63,14 @@
equal(server.requests[0].method, "POST");
equal(server.requests[0].url, url);
equal(server.requests[0].status, 201);
equal(server.requests[0].requestBody, undefined);
deepEqual(JSON.parse(server.requests[0].requestBody), {
"path": "/put1",
"autorename": false
});
equal(server.requests[0].responseText, "");
deepEqual(server.requests[0].requestHeaders, {
"Content-Type": "text/plain;charset=utf-8"
"Authorization": "Bearer sample_token",
"Content-Type": "application/json;charset=utf-8"
});
})
.fail(function (error) {
......@@ -101,18 +81,53 @@
});
});
test("don't throw error when putting existing directory", function () {
var url = "https://api.dropboxapi.com/1/fileops/create_folder?access_token="
+ token + "&root=dropbox&path=%2Fexisting%2F",
test("put sub document", function () {
var url = "https://api.dropboxapi.com/2/files/create_folder_v2",
server = this.server;
this.server.respondWith("POST", url, [405, {
this.server.respondWith("POST", url, [201, {
"Content-Type": "text/xml"
}, "POST" + url + "(Forbidden)"]);
}, ""]);
stop();
expect(7);
this.jio.put("/put1/put2/", {})
.then(function () {
equal(server.requests.length, 1);
equal(server.requests[0].method, "POST");
equal(server.requests[0].url, url);
equal(server.requests[0].status, 201);
deepEqual(JSON.parse(server.requests[0].requestBody), {
"path": "/put1/put2",
"autorename": false
});
equal(server.requests[0].responseText, "");
deepEqual(server.requests[0].requestHeaders, {
"Authorization": "Bearer sample_token",
"Content-Type": "application/json;charset=utf-8"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("don't throw error when putting existing directory", function () {
var url = "https://api.dropboxapi.com/2/files/create_folder_v2",
server = this.server;
this.server.respondWith("POST", url, [409, {
"Content-Type": "application/json"
}, JSON.stringify(
{error: {'.tag': 'path', 'path': {'.tag': 'conflict'}}}
)]);
stop();
expect(1);
this.jio.put("/existing/", {})
.then(function () {
equal(server.requests[0].status, 405);
equal(server.requests[0].status, 409);
})
.fail(function (error) {
ok(false, error);
......@@ -188,8 +203,7 @@
this.jio = jIO.createJIO({
type: "dropbox",
access_token: token,
root : "dropbox"
access_token: token
});
},
teardown: function () {
......@@ -197,9 +211,9 @@
delete this.server;
}
});
test("remove document", function () {
var url_delete = "https://api.dropboxapi.com/1/fileops/delete/?" +
"access_token=" + token + "&root=dropbox&path=%2Fremove1%2F",
var url_delete = "https://api.dropboxapi.com/2/files/delete_v2",
server = this.server;
this.server.respondWith("POST", url_delete, [204, {
"Content-Type": "text/xml"
......@@ -213,10 +227,13 @@
equal(server.requests[0].method, "POST");
equal(server.requests[0].url, url_delete);
equal(server.requests[0].status, 204);
equal(server.requests[0].requestBody, undefined);
equal(server.requests[0].responseText, '');
deepEqual(JSON.parse(server.requests[0].requestBody), {
"path": "/remove1"
});
equal(server.requests[0].responseText, "");
deepEqual(server.requests[0].requestHeaders, {
"Content-Type": "text/plain;charset=utf-8"
"Authorization": "Bearer sample_token",
"Content-Type": "application/json;charset=utf-8"
});
})
.fail(function (error) {
......@@ -275,8 +292,7 @@
this.jio = jIO.createJIO({
type: "dropbox",
access_token: token,
root : "dropbox"
access_token: token
});
},
teardown: function () {
......@@ -322,6 +338,13 @@
});
test("get inexistent document", function () {
var url = "https://api.dropboxapi.com/2/files/get_metadata";
this.server.respondWith("POST", url, [409, {
"Content-Type": "application/json"
}, JSON.stringify(
{error: {'.tag': 'path', 'path': {'.tag': 'not_found'}}}
)]);
stop();
expect(3);
......@@ -340,18 +363,25 @@
});
test("get directory", function () {
var url = "https://api.dropboxapi.com/1/metadata/dropbox" +
"/id1/?access_token=" + token;
this.server.respondWith("GET", url, [200, {
"Content-Type": "text/xml"
}, '{"is_dir": true, "contents": []}'
var url = "https://api.dropboxapi.com/2/files/get_metadata",
server = this.server;
this.server.respondWith("POST", url, [200, {
"Content-Type": "application/json"
}, '{".tag": "folder"}'
]);
stop();
expect(1);
expect(3);
this.jio.get("/id1/")
.then(function (result) {
deepEqual(result, {}, "Check document");
deepEqual(JSON.parse(server.requests[0].requestBody), {
"path": "/id1"
});
deepEqual(server.requests[0].requestHeaders, {
"Authorization": "Bearer sample_token",
"Content-Type": "application/json;charset=utf-8"
});
})
.fail(function (error) {
ok(false, error);
......@@ -362,11 +392,10 @@
});
test("get file", function () {
var url = "https://api.dropboxapi.com/1/metadata/dropbox" +
"/id1/?access_token=" + token;
this.server.respondWith("GET", url, [200, {
"Content-Type": "text/xml"
}, '{"is_dir": false, "contents": []}'
var url = "https://api.dropboxapi.com/2/files/get_metadata";
this.server.respondWith("POST", url, [200, {
"Content-Type": "application/json"
}, '{".tag": "file"}'
]);
stop();
expect(3);
......@@ -397,8 +426,7 @@
this.jio = jIO.createJIO({
type: "dropbox",
access_token: token,
root : "dropbox"
access_token: token
});
},
teardown: function () {
......@@ -444,12 +472,14 @@
});
test("get file", function () {
var url = "https://api.dropboxapi.com/1/metadata/dropbox" +
"/id1/?access_token=" + token;
this.server.respondWith("GET", url, [200, {
"Content-Type": "text/xml"
}, '{"is_dir": false, "contents": []}'
]);
var url = "https://api.dropboxapi.com/2/files/list_folder";
this.server.respondWith("POST", url, [409, {
"Content-Type": "application/json"
}, JSON.stringify(
{error: {'.tag': 'path', 'path': {'.tag': 'not_folder'}}}
)]);
stop();
expect(3);
......@@ -468,6 +498,14 @@
});
test("get inexistent document", function () {
var url = "https://api.dropboxapi.com/2/files/list_folder";
this.server.respondWith("POST", url, [409, {
"Content-Type": "application/json"
}, JSON.stringify(
{error: {'.tag': 'path', 'path': {'.tag': 'not_found'}}}
)]);
stop();
expect(3);
......@@ -486,18 +524,30 @@
});
test("get document without attachment", function () {
var url = "https://api.dropboxapi.com/1/metadata/dropbox" +
"/id1/?access_token=" + token;
this.server.respondWith("GET", url, [200, {
"Content-Type": "text/xml"
}, '{"is_dir": true, "contents": []}'
var url = "https://api.dropboxapi.com/2/files/list_folder",
server = this.server;
this.server.respondWith("POST", url, [200, {
"Content-Type": "application/json"
}, '{"entries": [], "has_more": false}'
]);
stop();
expect(1);
expect(3);
this.jio.allAttachments("/id1/")
.then(function (result) {
deepEqual(result, {}, "Check document");
deepEqual(JSON.parse(server.requests[0].requestBody), {
"include_deleted": false,
"include_has_explicit_shared_members": false,
"include_media_info": false,
"include_mounted_folders": true,
"path": "/id1",
"recursive": false
});
deepEqual(server.requests[0].requestHeaders, {
"Authorization": "Bearer sample_token",
"Content-Type": "application/json;charset=utf-8"
});
})
.fail(function (error) {
ok(false, error);
......@@ -508,28 +558,26 @@
});
test("get document with attachment", function () {
var url = "https://api.dropboxapi.com/1/metadata/dropbox" +
"/id1/?access_token=" + token;
this.server.respondWith("GET", url, [200, {
"Content-Type": "text/xml"
}, '{"is_dir": true, "path": "/id1", ' +
'"contents": ' +
'[{"rev": "143bb45509", ' +
'"thumb_exists": false, ' +
'"path": "/id1/attachment1", ' +
'"is_dir": false, "bytes": 151}, ' +
'{"rev": "153bb45509", ' +
'"thumb_exists": false, ' +
'"path": "/id1/attachment2", ' +
'"is_dir": false, "bytes": 11}, ' +
'{"rev": "173bb45509", ' +
'"thumb_exists": false, ' +
'"path": "/id1/fold1", ' +
'"is_dir": true, "bytes": 0}], ' +
'"icon": "folder"}'
]);
var url = "https://api.dropboxapi.com/2/files/list_folder",
server = this.server;
this.server.respondWith("POST", url, [200, {
"Content-Type": "application/json"
}, JSON.stringify({
"entries": [{
".tag": "file",
"name": "attachment1"
}, {
".tag": "file",
"name": "attachment2"
}, {
".tag": "folder",
"name": "fold1"
}],
"has_more": false
})]);
stop();
expect(1);
expect(3);
this.jio.allAttachments("/id1/")
.then(function (result) {
......@@ -537,6 +585,92 @@
attachment1: {},
attachment2: {}
}, "Check document");
deepEqual(JSON.parse(server.requests[0].requestBody), {
"include_deleted": false,
"include_has_explicit_shared_members": false,
"include_media_info": false,
"include_mounted_folders": true,
"path": "/id1",
"recursive": false
});
deepEqual(server.requests[0].requestHeaders, {
"Authorization": "Bearer sample_token",
"Content-Type": "application/json;charset=utf-8"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("get document with attachment and pagination", function () {
var url = "https://api.dropboxapi.com/2/files/list_folder",
paginate_url = "https://api.dropboxapi.com/2/files/list_folder/continue",
server = this.server,
cursor = "foocursor";
this.server.respondWith("POST", url, [200, {
"Content-Type": "application/json"
}, JSON.stringify({
"entries": [{
".tag": "file",
"name": "attachment1"
}, {
".tag": "folder",
"name": "fold1"
}],
"has_more": true,
"cursor": cursor
})]);
this.server.respondWith("POST", paginate_url, [200, {
"Content-Type": "application/json"
}, JSON.stringify({
"entries": [{
".tag": "file",
"name": "attachment2"
}, {
".tag": "folder",
"name": "fold2"
}],
"has_more": false
})]);
stop();
expect(7);
this.jio.allAttachments("/id1/")
.then(function (result) {
deepEqual(result, {
attachment1: {},
attachment2: {}
}, "Check document");
deepEqual(server.requests[0].url, url);
deepEqual(JSON.parse(server.requests[0].requestBody), {
"include_deleted": false,
"include_has_explicit_shared_members": false,
"include_media_info": false,
"include_mounted_folders": true,
"path": "/id1",
"recursive": false
});
deepEqual(server.requests[0].requestHeaders, {
"Authorization": "Bearer sample_token",
"Content-Type": "application/json;charset=utf-8"
});
deepEqual(server.requests[1].url, paginate_url);
deepEqual(JSON.parse(server.requests[1].requestBody), {
"cursor": cursor
});
deepEqual(server.requests[1].requestHeaders, {
"Authorization": "Bearer sample_token",
"Content-Type": "application/json;charset=utf-8"
});
})
.fail(function (error) {
ok(false, error);
......@@ -558,8 +692,7 @@
this.jio = jIO.createJIO({
type: "dropbox",
access_token: token,
root : "dropbox"
access_token: token
});
},
teardown: function () {
......@@ -635,13 +768,11 @@
});
test("putAttachment document", function () {
var blob = new Blob(["foo"]),
url_put_att = "https://content.dropboxapi.com/1/files_put/dropbox"
+ "/putAttachment1/"
+ "attachment1?access_token=" + token,
var blob = new Blob(["foo"], {"type": "xapplication/foo"}),
url_put_att = "https://content.dropboxapi.com/2/files/upload",
server = this.server;
this.server.respondWith("PUT", url_put_att, [204, {
this.server.respondWith("POST", url_put_att, [204, {
"Content-Type": "text/xml"
}, ""]);
......@@ -656,12 +787,16 @@
.then(function () {
equal(server.requests.length, 1);
equal(server.requests[0].method, "PUT");
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, {
"Content-Type": "text/plain;charset=utf-8"
"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);
})
......@@ -685,8 +820,7 @@
this.jio = jIO.createJIO({
type: "dropbox",
access_token: token,
root : "dropbox"
access_token: token
});
},
teardown: function () {
......@@ -759,9 +893,7 @@
});
test("removeAttachment document", function () {
var url_delete = "https://api.dropboxapi.com/1/fileops/delete/" +
"?access_token=" + token + "&root=dropbox" +
"&path=%2FremoveAttachment1%2Fattachment1",
var url_delete = "https://api.dropboxapi.com/2/files/delete_v2",
server = this.server;
this.server.respondWith("POST", url_delete, [204, {
......@@ -781,10 +913,13 @@
equal(server.requests[0].method, "POST");
equal(server.requests[0].url, url_delete);
equal(server.requests[0].status, 204);
equal(server.requests[0].requestBody, undefined);
deepEqual(JSON.parse(server.requests[0].requestBody), {
"path": "/removeAttachment1/attachment1"
});
equal(server.requests[0].responseText, "");
deepEqual(server.requests[0].requestHeaders, {
"Content-Type": "text/plain;charset=utf-8"
"Authorization": "Bearer sample_token",
"Content-Type": "application/json;charset=utf-8"
});
})
.fail(function (error) {
......@@ -796,6 +931,13 @@
});
test("remove inexistent attachment", function () {
var url_delete = "https://api.dropboxapi.com/2/files/delete_v2";
this.server.respondWith("POST", url_delete, [409, {
"Content-Type": "application/json"
}, JSON.stringify(
{error: {'.tag': 'path_lookup', 'path_lookup': {'.tag': 'not_found'}}}
)]);
stop();
expect(3);
......@@ -829,8 +971,7 @@
this.jio = jIO.createJIO({
type: "dropbox",
access_token: token,
root : "dropbox"
access_token: token
});
},
teardown: function () {
......@@ -903,15 +1044,14 @@
});
test("getAttachment document", function () {
var url = "https://content.dropboxapi.com/1/files/dropbox/" +
"%2FgetAttachment1%2Fattachment1?access_token=" + token,
var url = "https://content.dropboxapi.com/2/files/download",
server = this.server;
this.server.respondWith("GET", url, [200, {
"Content-Type": "text/plain"
this.server.respondWith("POST", url, [200, {
"Content-Type": "text/xplain"
}, "foo\nbaré"]);
stop();
expect(9);
expect(10);
this.jio.getAttachment(
"/getAttachment1/",
......@@ -919,14 +1059,19 @@
)
.then(function (result) {
equal(server.requests.length, 1);
equal(server.requests[0].method, "GET");
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, {
"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/plain", "Check mimetype");
deepEqual(result.type, "text/xplain", "Check mimetype");
return jIO.util.readBlobAsText(result);
})
.then(function (result) {
......@@ -942,6 +1087,14 @@
});
test("get inexistent attachment", function () {
var url = "https://content.dropboxapi.com/2/files/download";
this.server.respondWith("POST", url, [409, {
"Content-Type": "application/json"
}, JSON.stringify(
{error: {'.tag': 'path', 'path': {'.tag': 'not_found'}}}
)]);
stop();
expect(3);
......
/*jslint nomen: true*/
/*global Blob*/
/*global Blob, jiodate*/
(function (jIO, QUnit, Blob) {
"use strict";
var test = QUnit.test,
......@@ -24,9 +24,10 @@
// queryStorage.constructor
/////////////////////////////////////////////////////////////////
module("queryStorage.constructor");
test("create substorage", function () {
test("accept parameters", function () {
var jio = jIO.createJIO({
type: "query",
schema: {'date': {type: 'string', format: 'date-time'}},
sub_storage: {
type: "querystorage200"
}
......@@ -34,9 +35,37 @@
ok(jio.__storage._sub_storage instanceof jio.constructor);
equal(jio.__storage._sub_storage.__type, "querystorage200");
deepEqual(jio.__storage._key_schema.key_set, {
"date": {
"cast_to": "dateType",
"read_from": "date"
}
}, 'check key_schema');
ok(typeof jio.__storage._key_schema.cast_lookup.dateType === 'function');
});
test("failed on wrond schema", function () {
throws(
function () {
jIO.createJIO({
type: "query",
schema: {'date': {type: 'couscous'}},
sub_storage: {
type: "querystorage200"
}
});
},
function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.status_code, 400);
equal(error.message,
"Wrong schema for property: date");
return true;
}
);
});
/////////////////////////////////////////////////////////////////
// queryStorage.get
/////////////////////////////////////////////////////////////////
......@@ -823,6 +852,99 @@
});
});
test("manual query used and use schema", function () {
stop();
expect(4);
function StorageSchemaCapacity() {
return this;
}
StorageSchemaCapacity.prototype.get = function (id) {
var doc = {
title: id,
id: "ID " + id,
"another": "property"
};
if (id === "foo") {
equal(id, "foo", "Get foo");
doc.modification_date = "Fri, 08 Sep 2017 07:46:27 +0000";
} else {
equal(id, "bar", "Get bar");
doc.modification_date = "Thu, 07 Sep 2017 18:59:23 +0000";
}
return doc;
};
StorageSchemaCapacity.prototype.hasCapacity = function (capacity) {
if ((capacity === "list")) {
return true;
}
return false;
};
StorageSchemaCapacity.prototype.buildQuery = function (options) {
deepEqual(options, {}, "No query parameter");
var result2 = [{
id: "foo",
value: {}
}, {
id: "bar",
value: {}
}];
return result2;
};
jIO.addStorage(
'querystoragenoschemacapacity',
StorageSchemaCapacity
);
var jio = jIO.createJIO({
type: "query",
schema: {
"modification_date": {
"type": "string",
"format": "date-time"
}
},
sub_storage: {
type: "querystoragenoschemacapacity"
}
});
jio.allDocs({
sort_on: [["modification_date", "descending"]],
limit: [0, 5],
select_list: ['modification_date']
})
.then(function (result) {
deepEqual(result, {
data: {
rows: [
{
id: "foo",
doc: {},
value: {
modification_date: "Fri, 08 Sep 2017 07:46:27 +0000"
}
}, {
id: "bar",
doc: {},
value: {
modification_date: "Thu, 07 Sep 2017 18:59:23 +0000"
}
}
],
total_rows: 2
}
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
/////////////////////////////////////////////////////////////////
// queryStorage.repair
/////////////////////////////////////////////////////////////////
......
......@@ -54,7 +54,7 @@
} catch (e) {
equal(e.name, 'TypeError', 'wrong exception type');
equal(e.message,
"SimpleQuery().create(): key_schema is not of type 'object'",
"Query().create(): key_schema is not of type 'object'",
'wrong exception message');
}
......@@ -64,7 +64,7 @@
} catch (e) {
equal(e.name, 'TypeError', 'wrong exception type');
equal(e.message,
"SimpleQuery().create(): key_schema has no 'key_set' property",
"Query().create(): key_schema has no 'key_set' property",
'wrong exception message');
}
......@@ -76,7 +76,7 @@
} catch (e) {
equal(e.name, 'TypeError', 'wrong exception type');
equal(e.message,
"SimpleQuery().create(): key_schema has unknown property 'foobar'",
"Query().create(): key_schema has unknown property 'foobar'",
'wrong exception message');
}
......
......@@ -505,6 +505,78 @@
});
});
test('Query & sort_on option', function () {
var doc_list = [
{
idendifier: 'a',
date: "Fri, 08 Sep 2017 07:46:27 +0000"
},
{
identifier: 'c',
date: "Wed, 06 Sep 2017 00:27:13 +0000"
},
{
identifier: 'b',
date: "Thu, 07 Sep 2017 18:59:23 +0000"
}
];
stop();
expect(2);
jIO.QueryFactory.create("").exec(
doc_list,
{sort_on: [['date', 'descending']]}
).
then(function (list) {
var key_schema =
{
key_set: {
date: {
read_from: 'date',
cast_to: 'dateType'
}
},
cast_lookup: {
dateType: function (str) {
return window.jiodate.JIODate(new Date(str).toISOString());
}
}
};
deepEqual(list, [
{
identifier: 'c',
date: "Wed, 06 Sep 2017 00:27:13 +0000"
},
{
identifier: 'b',
date: "Thu, 07 Sep 2017 18:59:23 +0000"
},
{
idendifier: 'a',
date: "Fri, 08 Sep 2017 07:46:27 +0000"
}
], 'Document list is sorted');
return jIO.QueryFactory.create("", key_schema).exec(
doc_list,
{sort_on: [['date', 'ascending']]}
);
})
.then(function (list) {
deepEqual(list, [
{
identifier: 'c',
date: "Wed, 06 Sep 2017 00:27:13 +0000"
},
{
identifier: 'b',
date: "Thu, 07 Sep 2017 18:59:23 +0000"
},
{
idendifier: 'a',
date: "Fri, 08 Sep 2017 07:46:27 +0000"
}
], 'Document list is sorted with key_schema');
}).always(start);
});
// Asterisk wildcard is not supported yet.
/* test('Full text query with asterisk', function () {
var doc_list = [
......
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