From 2ae42bb4469db487480703737604c44de99ff22e Mon Sep 17 00:00:00 2001 From: Tristan Cavelier <tristan.cavelier@tiolive.com> Date: Fri, 5 Apr 2013 17:46:30 +0200 Subject: [PATCH] dav: post, put, putAttachment and get updated + test --- src/jio.storage/davstorage.js | 796 +++++++++++++++++++--------------- test/jiotests.js | 365 +++++++++++----- 2 files changed, 695 insertions(+), 466 deletions(-) diff --git a/src/jio.storage/davstorage.js b/src/jio.storage/davstorage.js index 0e23526..3f94cff 100644 --- a/src/jio.storage/davstorage.js +++ b/src/jio.storage/davstorage.js @@ -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 +}); diff --git a/test/jiotests.js b/test/jiotests.js index 0d77e9b..d63b9e0 100644 --- a/test/jiotests.js +++ b/test/jiotests.js @@ -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); -- 2.30.9