Commit 2ae42bb4 authored by Tristan Cavelier's avatar Tristan Cavelier

dav: post, put, putAttachment and get updated + test

parent 6e6a56d0
......@@ -3,147 +3,259 @@
* Released under the LGPL license.
* http://www.gnu.org/licenses/lgpl.html
*/
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global jIO: true, $: true, btoa: true */
jIO.addStorageType('dav', function (spec, my) {
spec = spec || {};
var that, priv, super_serialized;
that = my.basicStorage(spec, my);
priv = {};
super_serialized = that.serialized;
priv.secureDocId = function (string) {
var split = string.split('/'),
i;
if (split[0] === '') {
split = split.slice(1);
}
for (i = 0; i < split.length; i += 1) {
if (split[i] === '') {
return '';
// JIO Dav Storage Description :
// {
// type: "dav",
// url: {string}
// }
// {
// type: "dav",
// url: {string},
// auth_type: {string}, (optional)
// - "auto" (default) (not implemented)
// - "basic"
// - "digest" (not implemented)
// realm: {string}, (optional)
// - undefined (default) (not implemented)
// - "<string>" realm name (not implemented)
// username: {string},
// password: {string} (optional)
// }
// {
// type: "dav",
// url: {string},
// encoded_login: {string}
// }
// {
// type: "dav",
// url: {string},
// secured_login: {string} (not implemented)
// }
// NOTE: to get the authentication type ->
// curl --verbose -X OPTION http://domain/
// In the headers: "WWW-Authenticate: Basic realm="DAV-upload"
// URL Characters convertion:
// If I want to retrieve the file which id is -> http://100%.json
// http://domain/collection/http://100%.json cannot be applied
// - '/' is col separator,
// - '%' is special char
// - '.' document and attachment separator
// http://100%.json will become
// - http:%2F%2F100%25.json to avoid bad request ('/', '%' -> '%2F', '%25')
// - http:%2F%2F100%25_.json to avoid ids conflicts ('.' -> '_.')
// - http:%252F%252F100%2525_.json to avoid request bad interpretation
// ('%', '%25')
// The file will be saved as http:%2F%2F100%25_.json
jIO.addStorageType("dav", function (spec, my) {
var priv = {}, that = my.basicStorage(spec, my), dav = {};
// ATTRIBUTES //
priv.url = null;
priv.username = null;
priv.password = null;
priv.encoded_login = null;
// CONSTRUCTOR //
/**
* Init the dav storage connector thanks to the description
* @method __init__
* @param {object} description The description object
*/
priv.__init__ = function (description) {
priv.url = description.url || "";
priv.url = priv.removeSlashIfLast(priv.url);
// if (description.secured_login) {
// not implemented
// } else
if (description.encoded_login) {
priv.encoded_login = description.encoded_login;
} else if (description.auth_type) {
if (description.auth_type === "basic") {
priv.encoded_login = "Basic " +
btoa((description.username || "") + ":" +
(description.password || ""));
}
} else {
priv.encoded_login = "";
}
return split.join('%2F');
};
priv.convertSlashes = function (string) {
return string.split('/').join('%2F');
// OVERRIDES //
that.specToStore = function () {
// TODO: secured password
// The encoded_login can be seen by anyone, we must find a way to secure it!
// secured_login = encrypt(encoded_login)
// encoded_login = decrypt(secured_login)
return {
"url": priv.url,
"encoded_login": priv.encoded_login
};
};
that.validateState = function () {
if (typeof priv.url !== "string" || priv.url === "") {
return "The webDav server URL is not provided";
}
if (priv.encoded_login === null) {
return "Impossible to create the authorization";
}
return "";
};
priv.restoreSlashes = function (string) {
return string.split('%2F').join('/');
// TOOLS //
/**
* Generate a new uuid
* @method generateUuid
* @return {string} The new uuid
*/
priv.generateUuid = function () {
var S4 = function () {
/* 65536 */
var i, string = Math.floor(
Math.random() * 0x10000
).toString(16);
for (i = string.length; i < 4; i += 1) {
string = "0" + string;
}
return string;
};
return S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() +
S4() + S4();
};
// /**
// * Clones an object in deep
// * @method clone
// * @param {object} object The object to clone
// * @return {object} The cloned object
// */
// priv.clone = function (object) {
// var tmp = JSON.stringify(object);
// if (tmp === undefined) {
// return undefined;
// }
// return JSON.parse(tmp);
// };
/**
* Checks if an object has no enumerable keys
* @method objectIsEmpty
* @param {object} obj The object
* @return {boolean} true if empty, else false
* Replace substrings to another strings
* @method recursiveReplace
* @param {string} string The string to do replacement
* @param {array} list_of_replacement An array of couple
* ["substring to select", "selected substring replaced by this string"].
* @return {string} The replaced string
*/
priv.objectIsEmpty = function (obj) {
var k;
for (k in obj) {
if (obj.hasOwnProperty(k)) {
return false;
priv.recursiveReplace = function (string, list_of_replacement) {
var i, split_string = string.split(list_of_replacement[0][0]);
if (list_of_replacement[1]) {
for (i = 0; i < split_string.length; i += 1) {
split_string[i] = priv.recursiveReplace(
split_string[i],
list_of_replacement.slice(1)
);
}
}
return true;
return split_string.join(list_of_replacement[0][1]);
};
priv.username = spec.username || '';
priv.secured_username = priv.convertSlashes(priv.username);
priv.password = spec.password || '';
priv.url = spec.url || '';
that.serialized = function () {
var o = super_serialized();
o.username = priv.username;
o.url = priv.url;
o.password = priv.password;
return o;
/**
* Changes / to %2F, % to %25 and . to _.
* @method secureName
* @param {string} name The name to secure
* @return {string} The secured name
*/
priv.secureName = function (name) {
return priv.recursiveReplace(name, [["/", "%2F"], ["%", "%25"]]);
};
priv.newAsyncModule = function () {
var async = {};
async.call = function (obj, function_name, arglist) {
obj._wait = obj._wait || {};
if (obj._wait[function_name]) {
obj._wait[function_name] -= 1;
return function () {};
}
// ok if undef or 0
arglist = arglist || [];
return obj[function_name].apply(obj[function_name], arglist);
};
async.neverCall = function (obj, function_name) {
obj._wait = obj._wait || {};
obj._wait[function_name] = -1;
};
async.wait = function (obj, function_name, times) {
obj._wait = obj._wait || {};
obj._wait[function_name] = times;
};
async.end = function () {
async.call = function () {};
};
return async;
/**
* Restores the original name from a secured name
* @method restoreName
* @param {string} secured_name The secured name to restore
* @return {string} The original name
*/
priv.restoreName = function (secured_name) {
return priv.recursiveReplace(secured_name, [["%2F", "/"], ["%25", "%"]]);
};
/**
* Checks if a browser supports cors (cross domain ajax requests)
* @method checkCors
* @return {boolean} true if supported, else false
* Convert document id and attachment id to a file name
* @method idsToFileName
* @param {string} doc_id The document id
* @param {string} attachment_id The attachment id (optional)
* @return {string} The file name
*/
priv.checkCors = function () {
return $.support.cors;
priv.idsToFileName = function (doc_id, attachment_id) {
doc_id = priv.secureName(doc_id).split(".").join("_.");
if (typeof attachment_id === "string") {
attachment_id = priv.secureName(attachment_id).split(".").join("_.");
return doc_id + "." + attachment_id;
}
return doc_id;
};
/**
* Replaces last "." with "_." in document filenames
* @method underscoreFileExtenisons
* @param {string} url url to clean up
* @return {string} clean_url cleaned up URL
* Removes the last character if it is a "/". "/a/b/c/" become "/a/b/c"
* @method removeSlashIfLast
* @param {string} string The string to modify
* @return {string} The modified string
*/
priv.underscoreFileExtenisons = function (url) {
var clean_url = url.replace(/,\s(\w+)$/, "_.$1");
return clean_url;
priv.removeSlashIfLast = function (string) {
if (string[string.length - 1] === "/") {
return string.slice(0, -1);
}
return string;
};
/**
* Replaces "_." with "." in document filenames
* @method restoreDots
* @param {string} url url to clean up
* @return {string} clean_url cleaned up URL
* Modify an ajax object to add default values
* @method makeAjaxObject
* @param {string} file_name The file name to add to the url
* @param {object} ajax_object The ajax object to override
* @return {object} A new ajax object with default values
*/
priv.restoreDots = function (url) {
var clean_url = url.replace(/_\./g, '.');
return clean_url;
priv.makeAjaxObject = function (file_name, method, ajax_object) {
ajax_object.type = method || ajax_object.type || "GET";
ajax_object.url = priv.url + "/" + priv.secureName(file_name) +
"?_=" + Date.now();
ajax_object.async = ajax_object.async === false ? false : true;
ajax_object.crossdomain = ajax_object.crossdomain === false ? false : true;
ajax_object.headers = ajax_object.headers || {};
ajax_object.headers.Authorization = ajax_object.headers.Authorization ||
priv.encoded_login;
return ajax_object;
};
/**
* Runs all ajax requests for davStorage
* @method ajax
* @param {object} ajax_object The request parameters
* @param {string} doc_id The document id
* @param {string} attachment_id The attachment id, can be undefined
* @param {string} method The request method
* @param {object} ajax_object The request parameters (optional)
*/
priv.ajax = function (ajax_object) {
$.ajax({
url: ajax_object.url,
type: ajax_object.type,
async: true,
dataType: ajax_object.dataType || null,
data: ajax_object.data || null,
crossdomain : true,
headers : {
Authorization: 'Basic ' + btoa(
priv.username + ':' + priv.password
),
Depth: ajax_object.headers === undefined ? null :
ajax_object.headers.depth
},
// xhrFields: {withCredentials: 'true'},
success: ajax_object.success,
error: ajax_object.error
});
priv.ajax = function (doc_id, attachment_id, method, ajax_object) {
var new_ajax_object = JSON.parse(JSON.stringify(ajax_object) || "{}");
console.log(priv.makeAjaxObject(
priv.idsToFileName(doc_id, attachment_id),
method,
new_ajax_object
));
return $.ajax(priv.makeAjaxObject(
priv.idsToFileName(doc_id, attachment_id),
method,
new_ajax_object
));//.always(then || function () {});
};
/**
......@@ -153,57 +265,123 @@ jIO.addStorageType('dav', function (spec, my) {
* @return {object} error The error object
*/
priv.createError = function (status, message, reason) {
var error = {};
var error = {
"status": status,
"message": message,
"reason": reason
};
switch (status) {
case 404:
error.status = status;
error.statusText = "Not found";
error.error = "not_found";
error.message = message;
error.reason = reason;
break;
case 405:
error.status = status;
error.statusText = "Method Not Allowed";
error.error = "method_not_allowed";
error.message = message;
error.reason = reason;
break;
case 409:
error.status = status;
error.statusText = "Conflicts";
error.error = "conflicts";
error.message = message;
error.reason = reason;
break;
case 24:
error.statusText = "Broken Document";
break;
}
error.error = error.statusText.toLowerCase().split(" ").join("_");
return error;
};
/**
* Check if method can be run on browser
* @method support
* Converts ajax error object to a JIO error object
* @method ajaxErrorToJioError
* @param {object} ajax_error_object The ajax error object
* @param {string} message The error message
* @param {string} reason The error reason
* @return {object} The JIO error object
*/
priv.support = function (docid) {
// no docId
if (!(typeof docid === "string" && docid !== "")) {
that.error(priv.createError(405, "Can't create document without id",
"Document id is undefined"
));
return true;
}
// no cross domain ajax
if (priv.checkCors === false) {
that.error(priv.createError(405,
"Browser does not support cross domain ajax", "CORS is undefined"
));
return true;
}
priv.ajaxErrorToJioError = function (ajax_error_object, message, reason) {
var jio_error_object = {};
jio_error_object.status = ajax_error_object.status;
jio_error_object.statusText = ajax_error_object.statusText;
jio_error_object.error =
ajax_error_object.statusText.toLowerCase().split(" ").join("_");
jio_error_object.message = message;
jio_error_object.reason = reason;
return jio_error_object;
};
/**
* Function that create an object containing jQuery like callbacks
* @method makeJQLikeCallback
* @return {object} jQuery like callback methods
*/
priv.makeJQLikeCallback = function () {
var result = null, emptyFun = function () {}, jql = {
"respond": function () {
result = arguments;
},
"to_return": {
"always": function (func) {
if (result) {
func.apply(func, result);
jql.to_return.always = emptyFun;
} else {
jql.respond = func;
}
return jql.to_return;
},
"then": function (func) {
if (result) {
func(result[1]);
jql.to_return.then = emptyFun;
} else {
jql.respond = function (err, response) {
func(response);
};
}
return jql.to_return;
}
}
};
return jql;
};
// DAV REQUESTS //
/**
* Retrieve a document
* @method dav.getDocument
* @param {string} doc_id The document id
* @param {function} then The callback(err, response)
*/
dav.getDocument = function (doc_id) {
var doc, jql = priv.makeJQLikeCallback();
priv.ajax(doc_id, undefined, "GET").always(function (one, state, three) {
if (state !== "success") {
jql.respond(priv.ajaxErrorToJioError(
one,
"Cannot retrieve document",
"Unknown error"
), undefined);
} else {
try {
doc = JSON.parse(one);
} catch (e) {
return jql.respond(priv.createError(
24,
"Cannot parse document",
"Document is broken"
), undefined);
}
// document health is good
jql.respond(undefined, doc);
}
});
return jql.to_return;
};
dav.putDocument = function (doc) {
// TODO
};
// JIO COMMANDS //
// wedDav methods rfc4918 (short summary)
// COPY Reproduces single resources (files) and collections (directory
// trees). Will overwrite files (if specified by request) but will
......@@ -229,89 +407,54 @@ jIO.addStorageType('dav', function (spec, my) {
// adding custom headers triggers preflight OPTIONS request!
// http://remysharp.com/2011/04/21/getting-cors-working/
priv.putOrPost = function (command, type) {
var docid = command.getDocId(), secured_docid, url, ajax_object;
if (priv.support(docid)) {
return;
}
secured_docid = priv.secureDocId(command.getDocId());
url = priv.url + '/' + priv.underscoreFileExtenisons(secured_docid);
ajax_object = {
url: url + '?_=' + Date.now(),
type: "GET",
dataType: "text",
success: function () {
if (type === 'POST') {
// POST the document already exists
that.error(priv.createError(409,
"Cannot create a new document", "Document already exists"
));
return;
}
ajax_object = {
url: url,
type: type,
data: JSON.stringify(command.getDoc()),
success: function () {
that.success({
ok: true,
id: command.getDocId()
});
},
error: function () {
that.error(priv.createError(409, "Cannot modify document",
"Error writing to remote storage"
/**
* Creates a new document
* @method post
* @param {object} command The JIO command
*/
that.post = function (command) {
var doc_id = command.getDocId() || priv.generateUuid();
priv.ajax(doc_id, undefined, "GET").always(function (one, state, three) {
if (state !== "success") {
if (one.status === 404) {
// the document does not already exist
// updating document
priv.ajax(doc_id, undefined, "PUT", {
"dataType": "text",
"data": JSON.stringify(command.cloneDoc())
}).always(function (one, state, three) {
if (state !== "success") {
// an error occured
that.retry(priv.ajaxErrorToJioError(
one,
"An error occured when trying to PUT data",
"Unknown"
));
}
};
priv.ajax(ajax_object);
},
error: function (err) {
// Firefox returns 0 instead of 404 on CORS?
if (err.status === 404 || err.status === 0) {
ajax_object = {
url: url,
type: "PUT",
data: JSON.stringify(command.getDoc()),
success: function () {
} else {
// document updated
that.success({
ok: true,
id: command.getDocId()
"ok": true,
"id": doc_id
});
},
error: function () {
that.error(priv.createError(409,
"Cannot modify document", "Error writing to remote storage"
));
}
};
priv.ajax(ajax_object);
} else {
// error accessing remote storage
that.error({
"status": err.status,
"statusText": err.statusText,
"error": "error",
"message": err.message,
"reason": "Failed to access remote storage"
});
} else {
// an error occured
that.retry(priv.ajaxErrorToJioError(
one,
"An error occured when trying to GET data",
"Unknown"
));
}
} else {
// the document already exists
that.error(priv.createError(
405,
"Cannot create document",
"Document already exists"
));
}
};
priv.ajax(ajax_object);
};
/**
* Creates a new document
* @method post
* @param {object} command The JIO command
*/
that.post = function (command) {
priv.putOrPost(command, 'POST');
});
};
/**
......@@ -320,7 +463,26 @@ jIO.addStorageType('dav', function (spec, my) {
* @param {object} command The JIO command
*/
that.put = function (command) {
priv.putOrPost(command, 'PUT');
var doc_id = command.getDocId();
priv.ajax(doc_id, undefined, "PUT", {
"dataType": "text",
"data": JSON.stringify(command.cloneDoc())
}).always(function (one, state, three) {
if (state !== "success") {
// an error occured
that.retry(priv.ajaxErrorToJioError(
one,
"Cannot update document",
"Unknown error"
));
} else {
// document updated
that.success({
"ok": true,
"id": doc_id
});
}
});
};
/**
......@@ -329,149 +491,90 @@ jIO.addStorageType('dav', function (spec, my) {
* @param {object} command The JIO command
*/
that.putAttachment = function (command) {
var docid = command.getDocId(),
doc,
url,
secured_docid,
secured_attachmentid,
attachment_url,
ajax_object;
priv.support(docid);
secured_docid = priv.secureDocId(docid);
url = priv.url + '/' + priv.underscoreFileExtenisons(secured_docid);
ajax_object = {
url: url + '?_=' + Date.now(),
type: 'GET',
dataType: 'text',
success: function (response) {
doc = JSON.parse(response);
// the document exists - update document
var doc = null, doc_id = command.getDocId(), attachment_id, tmp;
attachment_id = command.getAttachmentId();
priv.ajax(doc_id, undefined, "GET").always(function (one, state, three) {
if (state !== "success") {
// document not found or error
tmp = that.retry;
if (one.status === 404) {
tmp = that.error;
}
tmp(priv.ajaxErrorToJioError(
one,
"Cannot update document",
"Unknown error"
));
} else {
try {
doc = JSON.parse(one);
} catch (e) {
return that.error(priv.createError(
24,
"Cannot upload attachment",
"Document is broken"
));
}
// document health is good
doc._attachments = doc._attachments || {};
doc._attachments[command.getAttachmentId()] = {
"content_type": command.getAttachmentMimeType(),
doc._attachments[attachment_id] = {
"length": command.getAttachmentLength(),
"digest": "md5-" + command.md5SumAttachmentData(),
"length": command.getAttachmentLength()
"content_type": command.getAttachmentMimeType()
};
// put updated document data
ajax_object = {
url: url + '?_=' + Date.now(),
type: 'PUT',
data: JSON.stringify(doc),
success: function () {
secured_attachmentid = priv.secureDocId(command.getAttachmentId());
attachment_url = url + '.' +
priv.underscoreFileExtenisons(secured_attachmentid);
ajax_object = {
url: attachment_url + '?_=' + Date.now(),
type: 'PUT',
data: JSON.stringify(command.getDoc()),
success: function () {
// put the attachment
priv.ajax(doc_id, attachment_id, "PUT", {
"dataType": "text",
"data": command.getAttachmentData()
}).always(function (one, state, three) {
if (state !== "success") {
// an error occured
that.retry(priv.ajaxErrorToJioError(
one,
"An error occured when trying to PUT data",
"Unknown"
));
} else {
// update the document
priv.ajax(doc_id, undefined, "PUT", {
"dataType": "text",
"data": JSON.stringify(doc)
}).always(function (one, state, three) {
if (state !== "success") {
that.retry(priv.ajaxErrorToJioError(
one,
"An error occured when trying to PUT data",
"Unknown"
));
} else {
that.success({
ok: true,
id: command.getDocId() + '/' + command.getAttachmentId()
"ok": true,
"id": doc_id,
"attachment": attachment_id
});
},
error: function () {
that.error(priv.createError(409,
"Cannot modify document", "Error when saving attachment"
));
return;
}
};
priv.ajax(ajax_object);
},
error: function () {
that.error(priv.createError(409,
"Cannot modify document", "Error writing to remote storage"
));
return;
});
}
};
priv.ajax(ajax_object);
},
error: function () {
// the document does not exist
that.error(priv.createError(404,
"Impossible to add attachment", "Document not found"
));
return;
});
}
};
// see if the underlying document exists
priv.ajax(ajax_object);
});
};
/**
* Get a document or attachment from distant storage
* Options:
* - {boolean} revs Add simple revision history (false by default).
* - {boolean} revs_info Add revs info (false by default).
* - {boolean} conflicts Add conflict object (false by default).
* Get a document
* @method get
* @param {object} command The JIO command
*/
that.get = function (command) {
var docid = command.getDocId(),
doc,
url,
secured_docid,
secured_attachmentid,
attachment_url,
ajax_object;
if (priv.support(docid)) {
return;
}
secured_docid = priv.secureDocId(command.getDocId());
url = priv.url + '/' + priv.underscoreFileExtenisons(secured_docid);
if (typeof command.getAttachmentId() === "string") {
secured_attachmentid = priv.secureDocId(command.getAttachmentId());
attachment_url = url + '.' + priv.underscoreFileExtenisons(
secured_attachmentid
);
// get attachment
ajax_object = {
url: attachment_url + '?_=' + Date.now(),
type: "GET",
dataType: "text",
success: function (response) {
doc = JSON.parse(response);
that.success(doc);
},
error: function () {
that.error(priv.createError(404,
"Cannot find the attachment", "Attachment does not exist"
));
dav.getDocument(command.getDocId()).always(function (err, response) {
if (err) {
if (err.status === 404) {
return that.error(err);
}
};
priv.ajax(ajax_object);
} else {
// get document
ajax_object = {
url: url + '?_=' + Date.now(),
type: "GET",
dataType: "text",
success: function (response) {
// metadata_only should not be handled by jIO, as it is a
// webDav only option, shouldn't it?
// ditto for content_only
doc = JSON.parse(response);
that.success(doc);
},
error: function () {
that.error(priv.createError(404,
"Cannot find the document", "Document does not exist"
));
}
};
priv.ajax(ajax_object);
}
return that.retry(err);
}
return that.success(response);
});
};
/**
......@@ -479,7 +582,7 @@ jIO.addStorageType('dav', function (spec, my) {
* @method remove
* @param {object} command The JIO command
*/
that.remove = function (command) {
that._remove = function (command) {
var docid = command.getDocId(), doc, url,
secured_docid, secured_attachmentid, attachment_url,
attachment_list = [], i, j, k = 1, deleteAttachment, ajax_object;
......@@ -666,7 +769,7 @@ jIO.addStorageType('dav', function (spec, my) {
// },{...}
// ]
//}
that.allDocs = function (command) {
that._allDocs = function (command) {
var rows = [], url,
am = priv.newAsyncModule(),
o = {},
......@@ -762,5 +865,6 @@ jIO.addStorageType('dav', function (spec, my) {
am.call(o, 'getDocumentList');
};
priv.__init__(spec);
return that;
});
\ No newline at end of file
});
......@@ -164,7 +164,6 @@ generateTools = function (test_namespace) {
var o = {};
o.t = test_namespace;
o.server = o.t.sandbox.useFakeServer();
o.clock = sinon.useFakeTimers();
o.clock.tick(base_tick);
o.spy = basicSpyFunction;
......@@ -3471,7 +3470,7 @@ module ("JIO Replicate Revision Storage");
}]
}, "2");
});
/*
module ("Jio DAVStorage");
test ("Post", function () {
......@@ -3479,32 +3478,97 @@ test ("Post", function () {
var o = generateTools(this);
o.jio = JIO.newJio({
"type": "dav",
"username": "davpost",
"password": "checkpwd",
"url": "https://ca-davstorage:8080"
"type": "dav",
"url": "https://ca-davstorage:8080",
"auth_type": "basic",
"username": "admin",
"password": "pwd"
});
// post without id
o.spy (o, "status", 405, "Post without id");
o.jio.post({}, o.f);
o.clock.tick(5000);
o.server = sinon.fakeServer.create();
o.server.respondWith(
"GET",
/https:\/\/ca-davstorage:8080\/[0-9a-fA-F]{4}/,
[
404,
{"Content-Type": "text/html"},
"<h1>Document not found</h1>"
]
);
o.server.respondWith(
"PUT",
/https:\/\/ca-davstorage:8080\/[0-9a-fA-F]{4}/,
[
200,
{"Content-Type": "text/html"},
"<h1>Document updated!</h1>"
]
);
o.spy(o, "jobstatus", "done", "Post without id");
o.jio.post({}, {"max_retry": 1}, function (err, response) {
o.f.apply(arguments);
if (response) {
ok(isUuid(response.id), "Uuid should look like " +
"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx : " + response.id);
}
});
o.clock.tick(1000);
o.server.respond();
o.tick(o);
o.server.restore();
// post non empty document
o.addFakeServerResponse("dav", "PUT", "myFile", 201, "HTML RESPONSE");
o.spy(o, "value", {"id": "myFile", "ok": true},
"Create = POST non empty document");
o.jio.post({"_id": "myFile", "title": "hello there"}, o.f);
o.clock.tick(5000);
// post document with id
o.server = sinon.fakeServer.create();
o.server.respondWith(
"GET",
/https:\/\/ca-davstorage:8080\/http:%252F%252F100%2525_\.json/,
[
404,
{"Content-Type": "text/html"},
"<h1>Document not found</h1>"
]
);
o.server.respondWith(
"PUT",
/https:\/\/ca-davstorage:8080\/http:%252F%252F100%2525_\.json/,
[
200,
{"Content-Type": "text/html"},
"<h1>Document updated!</h1>"
]
);
o.spy(o, "value", {"id": "http://100%.json", "ok": true},
"Create document with an id");
o.jio.post({
"_id": "http://100%.json",
"title": "Hello There"
}, {"max_retry": 1}, o.f);
o.clock.tick(1000);
o.server.respond();
o.tick(o);
o.server.restore();
// post but document already exists (post = error!, put = ok)
o.answer = JSON.stringify({"_id": "myFile", "title": "hello there"});
o.addFakeServerResponse("dav", "GET", "myFile", 200, o.answer);
o.spy (o, "status", 409, "Post but document already exists");
o.jio.post({"_id": "myFile", "title": "hello again"}, o.f);
o.clock.tick(5000);
// post already existant file
o.server = sinon.fakeServer.create();
o.server.respondWith(
"GET",
/https:\/\/ca-davstorage:8080\/http:%252F%252F100%2525_\.json/,
[
200,
{"Content-Type": "text/plain"},
'{"_id":"doc1","title":"Hello There"}'
]
);
o.spy(o, "status", 405, "Update document previous -> 405");
o.jio.post({
"_id": "http://100%.json",
"title": "Hello There Again"
}, {"max_retry": 1}, o.f);
o.clock.tick(1000);
o.server.respond();
o.tick(o);
o.server.restore();
o.jio.stop();
});
......@@ -3514,36 +3578,61 @@ test ("Put", function(){
var o = generateTools(this);
o.jio = JIO.newJio({
"type": "dav",
"username": "davput",
"password": "checkpwd",
"url": "https://ca-davstorage:8080"
"type": "dav",
"url": "https://ca-davstorage:8080",
"auth_type": "basic",
"username": "admin",
"password": "pwd"
});
// put without id => id required
o.spy (o, "status", 20, "Put without id");
// put without id => 20 Id Required
o.spy (o, "status", 20, "Put without id -> 20");
o.jio.put({}, o.f);
o.clock.tick(5000);
o.tick(o);
// put non empty document
o.addFakeServerResponse("dav", "PUT", "put1", 201, "HTML RESPONSE");
o.spy (o, "value", {"ok": true, "id": "put1"},
"Create = PUT non empty document");
o.jio.put({"_id": "put1", "title": "myPut1"}, o.f);
o.clock.tick(5000);
o.server = sinon.fakeServer.create();
o.server.respondWith(
"PUT",
/https:\/\/ca-davstorage:8080\/http:%252F%252F100%2525_\.json/,
[
200,
{"Content-Type": "text/html"},
"<h1>OK1</h1>"
]
);
o.spy (o, "value", {"ok": true, "id": "http://100%.json"},
"Create document");
o.jio.put({
"_id": "http://100%.json",
"title": "Hi There"
}, {"max_retry": 1}, o.f);
o.clock.tick(1000);
o.server.respond();
//console.log( o.server );
//console.log( o.server.requests[0].requestHeaders );
//console.log( o.server.requests[0].responseHeaders );
// put but document already exists = update
o.answer = JSON.stringify({"_id": "put1", "title": "myPut1"});
o.addFakeServerResponse("dav", "GET", "put1", 200, o.answer);
o.addFakeServerResponse("dav", "PUT", "put1", 201, "HTML RESPONSE");
o.spy (o, "value", {"ok": true, "id": "put1"}, "Updated the document");
o.jio.put({"_id": "put1", "title": "myPut2abcdedg"}, o.f);
o.clock.tick(5000);
o.tick(o);
o.server.restore();
// update document
o.server = sinon.fakeServer.create();
o.server.respondWith(
"PUT",
/https:\/\/ca-davstorage:8080\/http:%252F%252F100%2525_\.json/,
[
200,
{"Content-Type": "text/html"},
"<h1>OK!</h1>"
]
);
o.spy (o, "value", {"ok": true, "id": "http://100%.json"},
"Update document");
o.jio.put({
"_id": "http://100%.json",
"title": "Hi There Again"
}, {"max_retry": 1}, o.f);
o.clock.tick(1000);
o.server.respond();
o.tick(o);
o.server.restore();
o.jio.stop();
});
......@@ -3553,52 +3642,88 @@ test ("PutAttachment", function(){
var o = generateTools(this);
o.jio = JIO.newJio({
"type": "dav",
"username": "davputattm",
"password": "checkpwd",
"url": "https://ca-davstorage:8080"
"type": "dav",
"url": "https://ca-davstorage:8080",
"auth_type": "basic",
"username": "admin",
"password": "pwd"
});
// putAttachment without doc id => id required
o.spy(o, "status", 20, "PutAttachment without doc id");
o.jio.putAttachment({}, o.f);
o.clock.tick(5000);
// putAttachment without attachment id => attachment id required
o.spy(o, "status", 22, "PutAttachment without attachment id");
o.jio.putAttachment({"id": "putattmt1"}, o.f);
o.clock.tick(5000);
// putAttachment without document id => 20 Id Required
o.spy(o, "status", 20, "PutAttachment without doc id -> 20");
o.jio.putAttachment({"_attachment": "body.html"}, o.f);
o.tick(o);
// putAttachment without underlying document => not found
o.addFakeServerResponse("dav", "GET", "putattmtx", 22, "HTML RESPONSE");
o.spy(o, "status", 22, "PutAttachment without document");
o.jio.putAttachment({"id": "putattmtx.putattmt2"}, o.f);
o.clock.tick(5000);
o.server.respond();
// putAttachment without attachment id => 22 Attachment Id Required
o.spy(o, "status", 22, "PutAttachment without attachment id -> 22");
o.jio.putAttachment({"_id": "http://100%.json"}, o.f);
o.tick(o);
// putAttachment with document without data
o.answer = JSON.stringify({"_id": "putattmt1", "title": "myPutAttm1"});
o.addFakeServerResponse("dav", "GET", "putattmt1", 200, o.answer);
o.addFakeServerResponse("dav", "PUT", "putattmt1", 201, "HTML RESPONSE");
o.addFakeServerResponse("dav", "PUT", "putattmt1.putattmt2", 201,"HTML"+
+ "RESPONSE");
o.spy(o, "value", {"ok": true, "id": "putattmt1/putattmt2"},
"PutAttachment with document, without data");
o.jio.putAttachment({"id": "putattmt1/putattmt2"}, o.f);
o.clock.tick(5000);
// putAttachment without underlying document => 404 Not Found
o.server = sinon.fakeServer.create();
o.server.respondWith(
"GET",
/https:\/\/ca-davstorage:8080\/http:%252F%252F100%2525_\.json/,
[
404,
{"Content-Type": "text/html"},
"<h1>Not Found</h1>"
]
);
o.spy(o, "status", 404, "PutAttachment without document -> 404");
o.jio.putAttachment({
"_id": "http://100%.json",
"_attachment": "putattmt2"
}, {"max_retry": 1}, o.f);
o.clock.tick(1000);
o.server.respond();
o.tick(o);
o.server.restore();
// update attachment
o.answer = JSON.stringify({"_id": "putattmt1", "title": "myPutAttm1"});
o.addFakeServerResponse("dav", "GET", "putattmt1", 200, o.answer);
o.addFakeServerResponse("dav", "PUT", "putattmt1", 201, "HTML RESPONSE");
o.addFakeServerResponse("dav", "PUT", "putattmt1.putattmt2", 201,"HTML"+
"RESPONSE");
o.spy(o, "value", {"ok": true, "id": "putattmt1/putattmt2"},
"Update Attachment, with data");
o.jio.putAttachment({"id": "putattmt1/putattmt2", "data": "abc"}, o.f);
o.clock.tick(5000);
// upload attachment
o.server = sinon.fakeServer.create();
o.server.respondWith(
"GET",
/https:\/\/ca-davstorage:8080\/http:%252F%252F100%2525_\.json/,
[
200,
{"Content-Type": "text/plain"},
'{"_id":"http://100%.json","title":"Hi There!"}'
]
);
o.server.respondWith(
"PUT",
/https:\/\/ca-davstorage:8080\/http:%252F%252F100%2525_\.json.body_\.html/,
[
200,
{"Content-Type": "text/html"},
"<h1>OK!</h1>"
]
);
o.server.respondWith(
"PUT",
/https:\/\/ca-davstorage:8080\/http:%252F%252F100%2525_\.json/,
[
200,
{"Content-Type": "text/html"},
"<h1>OK!</h1>"
]
);
o.spy(o, "value", {
"ok": true,
"id": "http://100%.json",
"attachment": "body.html"
}, "Upload attachment");
o.jio.putAttachment({
"_id": "http://100%.json",
"_attachment": "body.html",
"_mimetype": "text/html",
"_data": "<h1>Hi There!!</h1><p>How are you?</p>"
}, {"max_retry": 1}, o.f);
o.clock.tick(1000);
o.server.respond();
o.tick(o);
o.server.restore();
o.jio.stop();
});
......@@ -3608,53 +3733,53 @@ test ("Get", function(){
var o = generateTools(this);
o.jio = JIO.newJio({
"type": "dav",
"username": "davget",
"password": "checkpwd",
"url": "https://ca-davstorage:8080"
"type": "dav",
"url": "https://ca-davstorage:8080",
"auth_type": "basic",
"username": "admin",
"password": "pwd"
});
// get inexistent document
o.addFakeServerResponse("dav", "GET", "get1", 404, "HTML RESPONSE");
o.spy(o, "status", 404, "Get non existing document");
o.jio.get("get1", o.f);
o.clock.tick(5000);
o.server.respond();
// get inexistent attachment
o.addFakeServerResponse("dav", "GET", "get1.get2", 404, "HTML RESPONSE");
o.spy(o, "status", 404, "Get non existing attachment");
o.jio.get("get1/get2", o.f);
o.clock.tick(5000);
o.server = sinon.fakeServer.create();
o.server.respondWith(
"GET",
/https:\/\/ca-davstorage:8080\/http:%252F%252F100%2525_\.json/,
[
404,
{"Content-Type": "text/html"},
"<h1>Not Found</h1>"
]
);
o.spy(o, "status", 404, "Get non existing document -> 404");
o.jio.get({"_id": "http://100%.json"}, {"max_retry": 1}, o.f);
o.clock.tick(1000);
o.server.respond();
o.tick(o);
o.server.restore();
// get document
o.answer = JSON.stringify({"_id": "get3", "title": "some title"});
o.addFakeServerResponse("dav", "GET", "get3", 200, o.answer);
o.spy(o, "value", {"_id": "get3", "title": "some title"}, "Get document");
o.jio.get("get3", o.f);
o.clock.tick(5000);
o.server.respond();
// get inexistent attachment (document exists)
o.addFakeServerResponse("dav", "GET", "get3.getx", 404, "HTML RESPONSE");
o.spy(o, "status", 404, "Get non existing attachment (doc exists)");
o.jio.get("get3/getx", o.f);
o.clock.tick(5000);
o.server.respond();
// get attachment
o.answer = JSON.stringify({"_id": "get4", "title": "some attachment"});
o.addFakeServerResponse("dav", "GET", "get3.get4", 200, o.answer);
o.spy(o, "value", {"_id": "get4", "title": "some attachment"},
"Get attachment");
o.jio.get("get3/get4", o.f);
o.clock.tick(5000);
o.server = sinon.fakeServer.create();
o.server.respondWith(
"GET",
/https:\/\/ca-davstorage:8080\/http:%252F%252F100%2525_\.json/,
[
200,
{"Content-Type": "text/html"},
'{"_id":"http://100%.json","title":"Hi There!"}'
]
);
o.spy(o, "value", {"_id": "http://100%.json", "title": "Hi There!"},
"Get document");
o.jio.get({"_id": "http://100%.json"}, {"max_retry": 1}, o.f);
o.clock.tick(1000);
o.server.respond();
o.tick(o);
o.server.restore();
o.jio.stop();
});
/*
test ("Remove", function(){
var o = generateTools(this);
......
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