Commit 176f9543 authored by Caleb James DeLisle's avatar Caleb James DeLisle Committed by Tristan Cavelier

Updated XWiki storage and tests to fit new version of JIO

parent e79f94f9
/*jslint indent: 2, /*jslint
indent: 2,
maxlen: 80, maxlen: 80,
nomen: true plusplus: true,
nomen: true,
regexp: true
*/ */
/*global /*global
define: true, define: true,
exports: true,
require: true,
jIO: true, jIO: true,
jQuery: true, jQuery: true,
XMLHttpRequest: true,
Blob: true,
FormData: true,
window: true, window: true,
setTimeout: true XMLHttpRequest,
FormData
*/ */
/** /**
* JIO XWiki Storage. Type = 'xwiki'. * JIO XWiki Storage. Type = 'xwiki'.
* XWiki Document/Attachment storage. * XWiki Document/Attachment storage.
*/ */
(function () { (function (dependencies, module) {
"use strict"; "use strict";
if (typeof define === 'function' && define.amd) {
return define(dependencies, module);
}
if (typeof exports === 'object') {
return module(require('jio'), require('jquery'));
}
module(jIO, jQuery);
}([
'jio',
'jquery'
], function (jIO, $) {
"use strict";
function detectWiki() {
// try first the meta tag, then look for js,
// then finally fail over to 'xwiki'...
return $('meta[name="wiki"]').attr('content') ||
(window.XWiki || {}).currentWiki ||
'xwiki';
}
function detectXWikiURL(wiki) {
var loc, action, idx;
loc = window.location.href;
action = (window.XWiki || {}).contextAction || 'view';
idx = loc.indexOf('/wiki/' + wiki + '/' + action + '/');
if (idx !== -1) {
return loc.substring(0, idx);
}
idx = loc.indexOf('/bin/' + action + '/');
if (idx !== -1) {
// single wiki host:port/xwiki/bin/view/Main/WebHome
return loc.substring(0, idx);
}
throw new Error("Unable to detect XWiki URL");
}
var $, store; /**
store = function (spec, my) { * Checks if an object has no enumerable keys
*
* @param {Object} obj The object
* @return {Boolean} true if empty, else false
*/
function objectIsEmpty(obj) {
var k;
for (k in obj) {
if (obj.hasOwnProperty(k)) {
return false;
}
}
return true;
}
function detectIsPathBasedMultiwiki(xwikiurl) {
var loc = window.location.href;
if (loc.indexOf(xwikiurl + '/wiki/') === 0) { return true; }
if (loc.indexOf(xwikiurl + '/bin/') === 0) { return false; }
// warn the user that we're unusure?
return false;
}
/**
* The JIO XWikiStorage extension
*
* @class XWikiStorage
* @constructor
*/
function XWikiStorage(spec) {
spec = spec || {}; spec = spec || {};
var that, priv, xwikistorage; var priv = {};
// the wiki to store stuff in
priv.wiki = spec.wiki || detectWiki();
// URL location of the wiki
// XWiki doesn't currently allow cross-domain requests.
priv.xwikiurl = (spec.xwikiurl !== undefined)
? spec.xwikiurl : detectXWikiURL(priv.wiki);
// Which URL to load for getting the Anti-CSRF form token, used for testing.
priv.formTokenPath = spec.formTokenPath || priv.xwikiurl;
that = my.basicStorage(spec, my); priv.pathBasedMultiwiki = (spec.pathBasedMultiwiki !== undefined)
priv = {}; ? spec.pathBasedMultiwiki : detectIsPathBasedMultiwiki(priv.xwikiurl);
/** /**
* Get the Space and Page components of a documkent ID. * Get the Space and Page components of a documkent ID.
...@@ -35,6 +114,7 @@ ...@@ -35,6 +114,7 @@
* @return a map of { 'space':<Space>, 'page':<Page> } * @return a map of { 'space':<Space>, 'page':<Page> }
*/ */
priv.getParts = function (id) { priv.getParts = function (id) {
if (id.indexOf('/') === -1) { if (id.indexOf('/') === -1) {
return { return {
space: 'Main', space: 'Main',
...@@ -103,37 +183,21 @@ ...@@ -103,37 +183,21 @@
+ priv.wiki + '/spaces/' + parts.space + '/pages/' + parts.page; + priv.wiki + '/spaces/' + parts.space + '/pages/' + parts.page;
}; };
/** priv.getURL = function (action, space, page) {
* Make an HTML5 Blob object. var out = [priv.xwikiurl];
* Equivilant to the `new Blob()` constructor. if (!priv.pathBasedMultiwiki) {
* Will fall back on deprecated BlobBuilder if necessary. out.push('bin');
*/ } else {
priv.makeBlob = function (contentArray, options) { out.push('wiki', priv.wiki);
var i, bb, BB;
try {
// use the constructor if possible.
return new Blob(contentArray, options);
} catch (err) {
// fall back on the blob builder.
BB = (window.MozBlobBuilder || window.WebKitBlobBuilder
|| window.BlobBuilder);
bb = new BB();
for (i = 0; i < contentArray.length; i += 1) {
bb.append(contentArray[i]);
}
return bb.getBlob(options ? options.type : undefined);
} }
}; out.push(action, space, page);
return out.join('/');
priv.isBlob = function (potentialBlob) {
return potentialBlob !== undefined &&
potentialBlob.toString() === "[object Blob]";
}; };
/* /*
* Wrapper for the xwikistorage based on localstorage JiO store. * Wrapper for the xwikistorage based on localstorage JiO store.
*/ */
xwikistorage = { priv._storage = this._storage = {
/** /**
* Get content of an XWikiDocument. * Get content of an XWikiDocument.
* *
...@@ -143,7 +207,60 @@ ...@@ -143,7 +207,60 @@
*/ */
getItem: function (docId, andThen) { getItem: function (docId, andThen) {
var success = function (jqxhr) { function attachSuccess(doc, jqxhr) {
var out = {}, xd;
if (jqxhr.status !== 200) {
andThen(null, {
"status": jqxhr.status,
"statusText": jqxhr.statusText,
"error": "",
"message": "Failed to get attachments for document [" +
docId + "]",
"reason": ""
});
return;
}
try {
xd = $(jqxhr.responseText);
xd.find('attachment').each(function () {
var attach = {}, attachXML = $(this);
attachXML.find('mimeType').each(function () {
attach.content_type = $(this).text();
});
attachXML.find('size').each(function () {
attach.length = Number($(this).text());
});
attach.digest = "unknown-0";
attachXML.find('name').each(function () {
out[$(this).text()] = attach;
});
});
doc._attachments = out;
andThen(doc, null);
} catch (err) {
andThen(null, {
status: 500,
statusText: "internal error",
error: err,
message: err.message,
reason: ""
});
}
}
function getAttachments(doc) {
$.ajax({
url: priv.getDocRestURL(docId) + '/attachments',
type: "GET",
async: true,
dataType: 'xml',
complete: function (jqxhr) {
attachSuccess(doc, jqxhr);
}
});
}
function success(jqxhr) {
var out, xd; var out, xd;
out = {}; out = {};
try { try {
...@@ -165,7 +282,7 @@ ...@@ -165,7 +282,7 @@
out.content = $(this).text(); out.content = $(this).text();
}); });
out._id = docId; out._id = docId;
andThen(out, null); getAttachments(out);
} catch (err) { } catch (err) {
andThen(null, { andThen(null, {
status: 500, status: 500,
...@@ -175,7 +292,7 @@ ...@@ -175,7 +292,7 @@
reason: "" reason: ""
}); });
} }
}; }
$.ajax({ $.ajax({
url: priv.getDocRestURL(docId), url: priv.getDocRestURL(docId),
...@@ -184,7 +301,7 @@ ...@@ -184,7 +301,7 @@
dataType: 'xml', dataType: 'xml',
// Use complete instead of success and error because phantomjs // Use complete instead of success and error because phantomjs
// sometimes causes error to be called with html return code 200. // sometimes causes error to be called with http return code 200.
complete: function (jqxhr) { complete: function (jqxhr) {
if (jqxhr.status === 404) { if (jqxhr.status === 404) {
andThen(null, null); andThen(null, null);
...@@ -217,14 +334,10 @@ ...@@ -217,14 +334,10 @@
// need to do this manually, jquery doesn't support returning blobs. // need to do this manually, jquery doesn't support returning blobs.
xhr = new XMLHttpRequest(); xhr = new XMLHttpRequest();
parts = priv.getParts(docId); parts = priv.getParts(docId);
url = priv.xwikiurl + '/bin/download/' + parts.space + url = priv.getURL('download', parts.space, parts.page) +
"/" + parts.page + "/" + fileName + '?cb=' + Math.random(); '/' + fileName + '?cb=' + Math.random();
xhr.open('GET', url, true); xhr.open('GET', url, true);
if (priv.useBlobs) { xhr.responseType = 'blob';
xhr.responseType = 'blob';
} else {
xhr.responseType = 'text';
}
xhr.onload = function () { xhr.onload = function () {
if (xhr.status === 200) { if (xhr.status === 200) {
...@@ -259,13 +372,12 @@ ...@@ -259,13 +372,12 @@
setItem: function (id, doc, andThen) { setItem: function (id, doc, andThen) {
priv.doWithFormToken(function (formToken, err) { priv.doWithFormToken(function (formToken, err) {
if (err) { if (err) {
that.error(err); andThen(err);
return; return;
} }
var parts = priv.getParts(id); var parts = priv.getParts(id);
$.ajax({ $.ajax({
url: priv.xwikiurl + "/bin/preview/" + url: priv.getURL('preview', parts.space, parts.page),
parts.space + '/' + parts.page,
type: "POST", type: "POST",
async: true, async: true,
dataType: 'text', dataType: 'text',
...@@ -308,26 +420,26 @@ ...@@ -308,26 +420,26 @@
* @param docId the ID of the document to attach to. * @param docId the ID of the document to attach to.
* @param fileName the attachment file name. * @param fileName the attachment file name.
* @param mimeType the MIME type of the attachment content. * @param mimeType the MIME type of the attachment content.
* @param content the attachment content. * @param blob the attachment content.
* @param andThen a callback taking one parameter, the error if any. * @param andThen a callback taking one parameter, the error if any.
*/ */
setAttachment: function (docId, fileName, mimeType, content, andThen) { setAttachment: function (docId, fileName, blob, andThen) {
priv.doWithFormToken(function (formToken, err) { priv.doWithFormToken(function (formToken, err) {
var parts, blob, fd, xhr; var parts, fd, xhr;
if (err) { if (err) {
that.error(err); andThen(err);
return; return;
} }
parts = priv.getParts(docId); parts = priv.getParts(docId);
blob = priv.isBlob(content)
? content
: priv.makeBlob([content], {type: mimeType});
fd = new FormData(); fd = new FormData();
fd.append("filepath", blob, fileName); fd.append("filepath", blob, fileName);
fd.append("form_token", formToken); fd.append("form_token", formToken);
xhr = new XMLHttpRequest(); xhr = new XMLHttpRequest();
xhr.open('POST', priv.xwikiurl + "/bin/upload/" + xhr.open(
parts.space + '/' + parts.page, true); 'POST',
priv.getURL('upload', parts.space, parts.page),
true
);
xhr.onload = function () { xhr.onload = function () {
if (xhr.status === 302 || xhr.status === 200) { if (xhr.status === 302 || xhr.status === 200) {
andThen(null); andThen(null);
...@@ -349,13 +461,12 @@ ...@@ -349,13 +461,12 @@
removeItem: function (id, andThen) { removeItem: function (id, andThen) {
priv.doWithFormToken(function (formToken, err) { priv.doWithFormToken(function (formToken, err) {
if (err) { if (err) {
that.error(err); andThen(err);
return; return;
} }
var parts = priv.getParts(id); var parts = priv.getParts(id);
$.ajax({ $.ajax({
url: priv.xwikiurl + "/bin/delete/" + url: priv.getURL('delete', parts.space, parts.page),
parts.space + '/' + parts.page,
type: "POST", type: "POST",
async: true, async: true,
dataType: 'text', dataType: 'text',
...@@ -383,12 +494,12 @@ ...@@ -383,12 +494,12 @@
var parts = priv.getParts(docId); var parts = priv.getParts(docId);
priv.doWithFormToken(function (formToken, err) { priv.doWithFormToken(function (formToken, err) {
if (err) { if (err) {
that.error(err); andThen(err);
return; return;
} }
$.ajax({ $.ajax({
url: priv.xwikiurl + "/bin/delattachment/" + parts.space + '/' + url: priv.getURL('delattachment', parts.space, parts.page) +
parts.page + '/' + fileName, '/' + fileName,
type: "POST", type: "POST",
async: true, async: true,
dataType: 'text', dataType: 'text',
...@@ -411,322 +522,594 @@ ...@@ -411,322 +522,594 @@
} }
}); });
}); });
} },
};
// ==================== Tools ==================== /**
/** * Gets a document list from the xwiki storage.
* Update [doc] the document object and remove [doc] keys * It will retreive an array containing files meta data owned by
* which are not in [new_doc]. It only changes [doc] keys not starting * the user.
* with an underscore. * @method allDocs
* ex: doc: {key:value1,_key:value2} with */
* new_doc: {key:value3,_key:value4} updates allDocs: function (includeDocs, andThen) {
* doc: {key:value3,_key:value2}. var getData = function (callback) {
* @param {object} doc The original document object. $.ajax({
* @param {object} new_doc The new document object url: priv.xwikiurl + '/rest/wikis/xwiki/pages?cb=' + Date.now(),
*/ type: "GET",
priv.documentObjectUpdate = function (doc, new_doc) { async: true,
var k; dataType: 'xml',
for (k in doc) { success: function (xmlData) {
if (doc.hasOwnProperty(k)) { var data = [];
if (k[0] !== '_') { $(xmlData).find('fullName').each(function () {
delete doc[k]; data[data.length] = $(this).text();
});
callback(data);
},
error: function (error) {
andThen(null, error);
}
});
};
getData(function (rows, err) {
var i, next;
next = function (i) {
priv._storage.getItem(rows[i].id, function (doc, err) {
if (err) {
andThen(null, err);
return;
}
rows[i].doc = doc;
if (i < rows.length) {
next(i + 1);
} else {
andThen(rows);
}
});
};
if (err) {
return andThen(null, err);
} }
} for (i = 0; i < rows.length; i++) {
} rows[i] = {
for (k in new_doc) { id: rows[i],
if (new_doc.hasOwnProperty(k)) { key: rows[i],
if (k[0] !== '_') { value: {}
doc[k] = new_doc[k]; };
} }
} if (includeDocs) {
next(0);
} else {
andThen(rows);
}
});
} }
}; };
}
/** /**
* Checks if an object has no enumerable keys * Create a document in local storage.
* @method objectIsEmpty *
* @param {object} obj The object * @method post
* @return {boolean} true if empty, else false * @param {Object} command The JIO command
*/ * @param {Object} metadata The metadata to store
priv.objectIsEmpty = function (obj) { * @param {Object} options The command options
var k; */
for (k in obj) { XWikiStorage.prototype.post = function (command, metadata) {
if (obj.hasOwnProperty(k)) { var doc_id = metadata._id, that = this;
return false; if (!doc_id) {
} doc_id = jIO.util.generateUuid();
}
that._storage.getItem(doc_id, function (doc, err) {
if (err) {
command.error(err);
return;
} }
return true; if (doc === null) {
}; // the document does not exist
doc = jIO.util.deepClone(metadata);
// ==================== attributes ==================== doc._id = doc_id;
// the wiki to store stuff in delete doc._attachments;
priv.wiki = spec.wiki || 'xwiki'; that._storage.setItem(doc_id, doc, function (err) {
if (err) {
// unused command.error(
priv.username = spec.username; "failed",
priv.language = spec.language; "failed to upload document",
String(err)
// URL location of the wiki, unused since );
// XWiki doesn't currently allow cross-domain requests. } else {
priv.xwikiurl = spec.xwikiurl || command.success({"id": doc_id});
window.location.href.replace(/\/xwiki\/bin\//, '/xwiki\n') }
.split('\n')[0]; });
// should be: s@/xwiki/bin/.*$@/xwiki@ } else {
// but jslint gets in the way. // the document already exists
command.error(
// Which URL to load for getting the Anti-CSRF form token, used for testing. "conflict",
priv.formTokenPath = spec.formTokenPath || priv.xwikiurl; "document exists",
"Cannot create a new document"
// If true then Blob objects will be returned by );
// getAttachment() rather than strings. }
priv.useBlobs = spec.useBlobs || false; });
};
// If true then Blob objects will be returned by /**
// getAttachment() rather than strings. * Create or update a document in local storage.
priv.useBlobs = spec.useBlobs || false; *
* @method put
* @param {Object} command The JIO command
* @param {Object} metadata The metadata to store
* @param {Object} options The command options
*/
XWikiStorage.prototype.put = function (command, metadata) {
var tmp, status, that = this;
that._storage.getItem(metadata._id, function (doc, err) {
if (err) {
command.error(err);
return;
}
if (doc === null || doc === undefined) {
// the document does not exist
doc = jIO.util.deepClone(metadata);
delete doc._attachments;
status = "created";
} else {
// the document already exists
tmp = jIO.util.deepClone(metadata);
tmp._attachments = doc._attachments;
doc = tmp;
status = "no_content";
}
// write
that._storage.setItem(metadata._id, doc, function (err) {
if (err) { command.error(err); return; }
command.success(status);
});
});
};
/**
* Add an attachment to a document
*
* @method putAttachment
* @param {Object} command The JIO command
* @param {Object} param The given parameters
* @param {Object} options The command options
*/
XWikiStorage.prototype.putAttachment = function (command, param) {
var that = this, status = "created";
that._storage.getItem(param._id, function (doc, err) {
if (err) {
return command.error(err);
}
if (doc === null) {
// the document does not exist
return command.error(
"not_found",
"missing",
"Impossible to add attachment"
);
}
that.specToStore = function () { // the document already exists
return { // download data
"username": priv.username, if ((doc._attachments || {})[param._attachment]) {
"language": priv.language, status = "no_content";
"xwikiurl": priv.xwikiurl }
}; that._storage.setAttachment(param._id,
}; param._attachment,
param._blob,
function (err) {
if (err) {
command.error(err);
} else {
// XWiki doesn't do digests of attachments
// so we'll calculate it on the client side.
jIO.util.readBlobAsBinaryString(param._blob).then(function (e) {
command.success(status,
{"digest": jIO.util.makeBinaryStringDigest(e.target.result)}
);
});
}
});
});
};
// can't fo wrong since no parameters are required. /**
that.validateState = function () { * Get a document
return ''; *
}; * @method get
* @param {Object} command The JIO command
* @param {Object} param The given parameters
* @param {Object} options The command options
*/
XWikiStorage.prototype.get = function (command, param) {
this._storage.getItem(param._id, function (ret, err) {
if (err) { command.error(err); return; }
if (ret === null) {
command.error(
"not_found",
"missing",
"Cannot find document"
);
} else {
command.success({"data": ret});
}
});
};
// ==================== commands ==================== /**
/** * Get an attachment
* Create a document in local storage. *
* @method post * @method getAttachment
* @param {object} command The JIO command * @param {Object} command The JIO command
*/ * @param {Object} param The given parameters
that.post = function (command) { * @param {Object} options The command options
var docId = command.getDocId(); */
if (!(typeof docId === "string" && docId !== "")) { XWikiStorage.prototype.getAttachment = function (command, param) {
setTimeout(function () { var that = this;
that.error({ that._storage.getItem(param._id, function (doc, err) {
"status": 405, if (err) {
"statusText": "Method Not Allowed", return command.error(err);
"error": "method_not_allowed", }
"message": "Cannot create document which id is undefined", if (doc === null) {
"reason": "Document id is undefined" return command.error(
"not_found",
"missing document",
"Cannot find document"
);
}
if (typeof doc._attachments !== 'object' ||
typeof doc._attachments[param._attachment] !== 'object') {
return command.error(
"not_found",
"missing attachment",
"Cannot find attachment"
);
}
that._storage.getAttachment(param._id, param._attachment,
function (blob, err) {
var attach = doc._attachments[param._attachment];
if (err) {
return command.error(err);
}
if (blob.size !== attach.length) {
return command.error(
"incomplete",
"attachment size incorrect",
"expected [" + attach.size + "] bytes, got [" + blob.size + "]"
);
}
command.success({
"data": blob,
"digest": attach.digest || "",
"content_type": attach.content_type || ""
}); });
}); });
return; });
};
/**
* Remove a document
*
* @method remove
* @param {Object} command The JIO command
* @param {Object} param The given parameters
* @param {Object} options The command options
*/
XWikiStorage.prototype.remove = function (command, param) {
this._storage.removeItem(param._id, function (err) {
if (err) {
command.error(err);
} else {
command.success();
} }
xwikistorage.getItem(docId, function (doc, err) { });
if (err) { };
that.error(err);
} else if (doc === null) {
// the document does not exist
xwikistorage.setItem(command.getDocId(),
command.cloneDoc(),
function (err) {
if (err) {
that.error(err);
} else {
that.success({
"ok": true,
"id": command.getDocId()
});
}
});
} else {
// the document already exists
that.error({
"status": 409,
"statusText": "Conflicts",
"error": "conflicts",
"message": "Cannot create a new document",
"reason": "Document already exists (use 'put' to modify it)"
});
}
});
};
/** /**
* Create or update a document in local storage. * Remove an attachment
* @method put *
* @param {object} command The JIO command * @method removeAttachment
*/ * @param {Object} command The JIO command
that.put = function (command) { * @param {Object} param The given parameters
xwikistorage.getItem(command.getDocId(), function (doc, err) { * @param {Object} options The command options
if (err) { */
that.error(err); XWikiStorage.prototype.removeAttachment = function (command, param) {
} else if (doc === null) { var that = this;
doc = command.cloneDoc(); that._storage.getItem(param._id, function (doc, err) {
} else { if (err) {
priv.documentObjectUpdate(doc, command.cloneDoc()); return command.error(err);
} }
// write if (typeof doc !== 'object' || doc === null) {
xwikistorage.setItem(command.getDocId(), doc, function (err) { return command.error(
"not_found",
"missing document",
"Document not found"
);
}
if (typeof doc._attachments !== "object" ||
typeof doc._attachments[param._attachment] !== "object") {
return command.error(
"not_found",
"missing attachment",
"Attachment not found"
);
}
that._storage.removeAttachment(param._id, param._attachment,
function (err) {
if (err) { if (err) {
that.error(err); command.error(err);
} else { } else {
that.success({ command.success();
"ok": true,
"id": command.getDocId()
});
} }
}); });
});
};
/**
* Get all filenames belonging to a user from the document index
*
* @method allDocs
* @param {Object} command The JIO command
* @param {Object} param The given parameters
* @param {Object} options The command options
*/
XWikiStorage.prototype.allDocs = function (command, param, options) {
var i, document_list, document_object, delete_id, that = this;
param.unused = true;
document_list = [];
if (options.query === undefined && options.sort_on === undefined &&
options.select_list === undefined &&
options.include_docs === undefined) {
that._storage.allDocs(options.include_docs, function (rows, err) {
if (err) {
return command.error(err);
}
command.success({"data": {"rows": rows, "total_rows": rows.length}});
}); });
};
/** } else {
* Add an attachment to a document
* @method putAttachment that._storage.allDocs(true, function (rows, err) {
* @param {object} command The JIO command
*/
that.putAttachment = function (command) {
xwikistorage.getItem(command.getDocId(), function (doc, err) {
if (err) { if (err) {
that.error(err); return command.error(err);
} else if (doc === null) { }
// the document does not exist for (i = 0; i < rows.length; i++) {
that.error({ document_list.push(rows[i].doc);
"status": 404,
"statusText": "Not Found",
"error": "not_found",
"message": "Impossible to add attachment",
"reason": "Document not found"
});
} else {
// Document exists, upload attachment.
xwikistorage.setAttachment(command.getDocId(),
command.getAttachmentId(),
command.getAttachmentMimeType(),
command.getAttachmentData(),
function (err) {
if (err) {
that.error(err);
} else {
that.success({
"ok": true,
"id": command.getDocId() + "/" + command.getAttachmentId()
});
}
});
} }
}); });
};
/** options.select_list = options.select_list || [];
* Get a document or attachment if (options.select_list.indexOf("_id") === -1) {
* @method get options.select_list.push("_id");
* @param {object} command The JIO command delete_id = true;
*/ }
that.get = that.getAttachment = function (command) { if (options.include_docs === true) {
if (typeof command.getAttachmentId() === "string") { document_object = {};
// seeking for an attachment document_list.forEach(function (meta) {
xwikistorage.getAttachment(command.getDocId(), document_object[meta._id] = meta;
command.getAttachmentId(), });
function (attach, err) { }
if (err) { jIO.QueryFactory.create(options.query || "", this._key_schema).
that.error(err); exec(document_list, options).then(function () {
} else if (attach !== null) { document_list = document_list.map(function (value) {
that.success(attach); var o = {
} else { "id": value._id,
that.error({ "key": value._id
"status": 404, };
"statusText": "Not Found", if (options.include_docs === true) {
"error": "not_found", o.doc = document_object[value._id];
"message": "Cannot find the attachment", delete document_object[value._id];
"reason": "Attachment does not exist"
});
} }
if (delete_id) {
delete value._id;
}
o.value = value;
return o;
}); });
} else { command.success({"data": {
// seeking for a document "total_rows": document_list.length,
xwikistorage.getItem(command.getDocId(), function (doc, err) { "rows": document_list
if (err) { }});
that.error(err);
} else if (doc !== null) {
that.success(doc);
} else {
that.error({
"status": 404,
"statusText": "Not Found",
"error": "not_found",
"message": "Cannot find the document",
"reason": "Document does not exist"
});
}
}); });
}
};
/**
* Check the storage or a specific document
*
* @method check
* @param {Object} command The JIO command
* @param {Object} param The command parameters
* @param {Object} options The command options
*/
XWikiStorage.prototype.check = function (command, param) {
this.genericRepair(command, param, false);
};
/**
* Repair the storage or a specific document
*
* @method repair
* @param {Object} command The JIO command
* @param {Object} param The command parameters
* @param {Object} options The command options
*/
XWikiStorage.prototype.repair = function (command, param) {
this.genericRepair(command, param, true);
};
/**
* A generic method that manage check or repair command
*
* @method genericRepair
* @param {Object} command The JIO command
* @param {Object} param The command parameters
* @param {Boolean} repair If true then repair else just check
*/
XWikiStorage.prototype.genericRepair = function (command, param, repair) {
var that = this, final_result;
function referenceAttachment(param, attachment) {
if (param.referenced_attachments.indexOf(attachment) !== -1) {
return;
} }
}; var i = param.unreferenced_attachments.indexOf(attachment);
if (i !== -1) {
param.unreferenced_attachments.splice(i, 1);
}
param.referenced_attachments[param.referenced_attachments.length] =
attachment;
}
/** function attachmentFound(param, attachment) {
* Remove a document or attachment if (param.referenced_attachments.indexOf(attachment) !== -1) {
* @method remove return;
* @param {object} command The JIO command }
*/ if (param.unreferenced_attachments.indexOf(attachment) !== -1) {
that.remove = that.removeAttachment = function (command) { return;
var objId, complete; }
// notFoundError = function (word) { param.unreferenced_attachments[param.unreferenced_attachments.length] =
// that.error({ attachment;
// "status": 404, }
// "statusText": "Not Found",
// "error": "not_found", function repairOne(param, repair) {
// "message": word + " not found", var i, doc, modified;
// "reason": "missing" doc = that._storage.getItem(param._id);
// }); if (doc === null) {
// }; return; // OK
}
objId = command.getDocId();
complete = function (err) { // check document type
if (err) { if (typeof doc !== 'object' || doc === null) {
that.error(err); // wrong document
} else { if (!repair) {
that.success({ return {"error": true, "answers": [
"ok": true, "conflict",
"id": objId "corrupted",
}); "Document is unrecoverable"
]};
}
// delete the document
that._storage.removeItem(param._id);
return; // OK
}
// good document type
// repair json document
if (!repair) {
if (!(new jIO.Metadata(doc).check())) {
return {"error": true, "answers": [
"conflict",
"corrupted",
"Some metadata might be lost"
]};
} }
};
if (typeof command.getAttachmentId() === "string") {
objId += '/' + command.getAttachmentId();
xwikistorage.removeAttachment(command.getDocId(),
command.getAttachmentId(),
complete);
} else { } else {
xwikistorage.removeItem(objId, complete); modified = jIO.util.uniqueJSONStringify(doc) !==
jIO.util.uniqueJSONStringify(new jIO.Metadata(doc).format()._dict);
} }
}; if (doc._attachments !== undefined) {
if (typeof doc._attachments !== 'object') {
if (!repair) {
return {"error": true, "answers": [
"conflict",
"corrupted",
"Attachments are unrecoverable"
]};
}
delete doc._attachments;
that._storage.setItem(param._id, doc);
return; // OK
}
for (i in doc._attachments) {
if (doc._attachments.hasOwnProperty(i)) {
// check attachment existence
if (that._storage.getItem(param._id + "/" + i) !== 'string') {
if (!repair) {
return {"error": true, "answers": [
"conflict",
"missing attachment",
"Attachment \"" + i + "\" of \"" + param._id + "\" is missing"
]};
}
delete doc._attachments[i];
if (objectIsEmpty(doc._attachments)) {
delete doc._attachments;
}
modified = true;
} else {
// attachment exists
// check attachment metadata
// check length
referenceAttachment(param, param._id + "/" + doc._attachments[i]);
if (doc._attachments[i].length !== undefined &&
typeof doc._attachments[i].length !== 'number') {
if (!repair) {
return {"error": true, "answers": [
"conflict",
"corrupted",
"Attachment metadata length corrupted"
]};
}
// It could take a long time to get the length, no repair.
// length can be omited
delete doc._attachments[i].length;
}
// It could take a long time to regenerate the hash, no check.
// Impossible to discover the attachment content type.
}
}
}
}
if (modified) {
that._storage.setItem(param._id, doc);
}
// OK
}
/** function repairAll(param, repair) {
* Get all filenames belonging to a user from the document index var i, result;
* @method allDocs for (i in that._database) {
* @param {object} command The JIO command if (that._database.hasOwnProperty(i)) {
*/ // browsing every entry
that.allDocs = function () {
setTimeout(function () {
that.error({
"status": 405,
"statusText": "Method Not Allowed",
"error": "method_not_allowed",
"message": "Your are not allowed to use this command",
"reason": "xwikistorage forbids AllDocs command executions"
});
});
};
return that; // is part of the user space
if (/^[^\/]+\/[^\/]+$/.test(i)) {
// this is an attachment
attachmentFound(param, i);
} else if (/^[^\/]+$/.test(i)) {
// this is a document
param._id = i;
result = repairOne(param, repair);
if (result) {
return result;
}
} else {
// this is pollution
that._storage.removeItem(i);
}
}
}
// remove unreferenced attachments
for (i = 0; i < param.unreferenced_attachments.length; i += 1) {
that._storage.removeItem(param.unreferenced_attachments[i]);
}
}
param.referenced_attachments = [];
param.unreferenced_attachments = [];
if (typeof param._id === 'string') {
final_result = repairOne(param, repair) || {};
} else {
final_result = repairAll(param, repair) || {};
}
if (final_result.error) {
return command.error.apply(command, final_result.answers || []);
}
command.success.apply(command, final_result.answers || []);
}; };
if (typeof define === 'function' && define.amd) { jIO.addStorage('xwiki', XWikiStorage);
define(['jquery', 'jio'], function (jquery, jIO) {
$ = jquery;
jIO.addStorageType('xwiki', store);
});
} else {
jIO.addStorageType('xwiki', store);
$ = jQuery;
}
}()); }));
/*jslint indent: 2, maxlen: 80, nomen: true */ /*jslint
/*global define, jIO, jio_tests, test, ok, sinon, module */ indent: 2,
maxlen: 80,
plusplus: true,
nomen: true,
regexp: true
*/
/*global
define,
jIO,
test,
ok,
sinon,
module,
clearTimeout,
setTimeout,
start,
stop,
deepEqual
*/
// define([module_name], [dependencies], module); // define([module_name], [dependencies], module);
(function (dependencies, module) { (function (dependencies, module) {
...@@ -7,34 +25,103 @@ ...@@ -7,34 +25,103 @@
if (typeof define === 'function' && define.amd) { if (typeof define === 'function' && define.amd) {
return define(dependencies, module); return define(dependencies, module);
} }
module(jIO, jio_tests); module(jIO);
}(['jio', 'jio_tests', 'xwikistorage'], function (jIO, util) { }(['jio', 'xwikistorage'], function (jIO) {
"use strict"; "use strict";
function generateTools() { function nThen(next) {
var o = { var funcs = [],
clock: sinon.useFakeTimers(), timeouts = [],
spy: util.ospy, calls = 0,
tick: util.otick abort,
ret,
waitFor;
waitFor = function (func) {
calls++;
return function () {
if (func) {
func.apply(null, arguments);
}
calls = (calls || 1) - 1;
while (!calls && funcs.length && !abort) {
funcs.shift()(waitFor);
}
};
}; };
function addFakeServerResponse(type, method, path, status, response) { waitFor.abort = function () {
/*jslint unparam: true */ timeouts.forEach(clearTimeout);
o.server.respondWith(method, new RegExp(path), [ abort = 1;
status, };
{"Content-Type": 'application/xml'}, ret = {
response nThen: function (next) {
]); if (!abort) {
} if (!calls) {
o.addFakeServerResponse = addFakeServerResponse; next(waitFor);
return o; } else {
funcs.push(next);
}
}
return ret;
},
orTimeout: function (func, milliseconds) {
var cto, timeout;
if (abort) { return ret; }
if (!milliseconds) {
throw new Error("Must specify milliseconds to orTimeout()");
}
timeout = setTimeout(function () {
var f;
while (f !== cto) { f = funcs.shift(); }
func(waitFor);
calls = (calls || 1) - 1;
while (!calls && funcs.length) { funcs.shift()(waitFor); }
}, milliseconds);
cto = function () {
var i;
for (i = 0; i < timeouts.length; i++) {
if (timeouts[i] === timeout) {
timeouts.splice(i, 1);
clearTimeout(timeout);
return;
}
}
throw new Error('timeout not listed in array');
};
funcs.push(cto);
timeouts.push(timeout);
return ret;
}
};
return ret.nThen(next);
} }
module('XWikiStorage'); module('XWikiStorage');
function setUp(that) { function setUp() {
var o = generateTools(that); var o = {
o.server = sinon.fakeServer.create(); sinon: sinon.sandbox.create()
o.jio = jIO.newJio({type: 'xwiki', formTokenPath: 'form_token'}); };
o.addFakeServerResponse =
function (type, method, path, status, response, t) {
t = t || 'application/xml';
/*jslint unparam: true */
o.sinon.server.respondWith(method, new RegExp(path), [
status,
{"Content-Type": t},
response
]);
};
o.sinon.useFakeTimers();
o.sinon.useFakeServer();
o.respond = function () {
o.sinon.clock.tick(5000);
o.sinon.server.respond();
};
o.jio = jIO.createJIO(
{type: 'xwiki', formTokenPath: 'form_token', xwikiurl: ''}
);
o.addFakeServerResponse("xwiki", "GET", "form_token", 200, o.addFakeServerResponse("xwiki", "GET", "form_token", 200,
'<meta name="form_token" content="OMGHAX"/>'); '<meta name="form_token" content="OMGHAX"/>');
o._addFakeServerResponse = o.addFakeServerResponse; o._addFakeServerResponse = o.addFakeServerResponse;
...@@ -46,11 +133,11 @@ ...@@ -46,11 +133,11 @@
o.assertReqs = function (count, message) { o.assertReqs = function (count, message) {
var i, j, req, expected, ex; var i, j, req, expected, ex;
o.requests = (o.requests || 0) + count; o.requests = (o.requests || 0) + count;
ok(o.server.requests.length === o.requests, ok(o.sinon.server.requests.length === o.requests,
message + "[expected [" + count + "] got [" + message + "[expected [" + count + "] got [" +
(o.server.requests.length - (o.requests - count)) + "]]"); (o.sinon.server.requests.length - (o.requests - count)) + "]]");
for (i = 1; i <= count; i += 1) { for (i = 1; i <= count; i += 1) {
req = o.server.requests[o.server.requests.length - i]; req = o.sinon.server.requests[o.sinon.server.requests.length - i];
if (!req) { if (!req) {
break; break;
} }
...@@ -70,242 +157,588 @@ ...@@ -70,242 +157,588 @@
return o; return o;
} }
function nThenTest(o, nt) {
stop();
nt.nThen(function () {
o.sinon.restore();
start();
}).orTimeout(function () {
o.sinon.restore();
ok(0);
start();
}, 1000);
}
test("Post", function () { test("Post", function () {
var o = setUp(this); var o = setUp(this);
nThenTest(o, nThen(function (waitFor) {
// post non empty document
o.addFakeServerResponse("xwiki", "POST", "myFile1", 201, "HTML RESPONSE");
o.jio.post({"_id": "myFile1", "title": "hello there"}).
then(waitFor(function (ret) {
deepEqual({
id: "myFile1",
method: "post",
result: "success",
status: 201,
statusText: "Created"
}, ret);
}));
o.respond();
}).nThen(function () {
o.assertReqs(
3,
"post -> 1 request to get csrf token, 1 to get doc and 1 to post data"
);
}));
});
// post without id test("Post2", function () {
o.spy(o, "status", 405, "Post without id");
o.jio.post({}, o.f); var o = setUp(this);
o.clock.tick(5000); nThenTest(o, nThen(function (waitFor) {
o.assertReqs(0, "no id -> no request");
// post but document already exists (post = error!, put = ok)
// post non empty document o.answer = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
o.addFakeServerResponse("xwiki", "POST", "myFile", 201, "HTML RESPONSE"); '<page xmlns="http://www.xwiki.org"><title>hello there</title></page>';
o.spy(o, "value", {"id": "myFile", "ok": true}, o.addFakeServerResponse("xwiki", "GET", "pages/myFile2", 200, o.answer);
"Create = POST non empty document");
o.jio.post({"_id": "myFile", "title": "hello there"}, o.f); o.jio.post({"_id": "myFile2", "title": "hello again"}).
o.clock.tick(5000); then(function () {
o.server.respond(); ok(0);
o.assertReqs( }, waitFor(function (err) {
3, deepEqual({
"put -> 1 request to get csrf token, 1 to get doc and 1 to post data" error: "conflict",
); id: "myFile2",
message: "Cannot create a new document",
method: "post",
reason: "document exists",
result: "error",
status: 409,
statusText: "Conflict"
}, err);
}));
o.respond();
}).nThen(function () {
o.assertReqs(2, "post w/ existing doc -> 2 request to get doc then fail");
}));
// post but document already exists (post = error!, put = ok)
o.answer = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
'<page xmlns="http://www.xwiki.org"><title>hello there</title></page>';
o.addFakeServerResponse("xwiki", "GET", "myFile2", 200, o.answer);
o.spy(o, "status", 409, "Post but document already exists");
o.jio.post({"_id": "myFile2", "title": "hello again"}, o.f);
o.clock.tick(5000);
o.server.respond();
o.assertReqs(1, "post w/ existing doc -> 1 request to get doc then fail");
util.closeAndcleanUpJio(o.jio);
}); });
test("Put", function () { test("PutNoId", function () {
var o = setUp(this); var o = setUp(this);
nThenTest(o, nThen(function (waitFor) {
// put without id => id required
o.jio.put({}).then(function () {
ok(0);
}, waitFor(function (err) {
deepEqual({
"error": "bad_request",
"message": "Document id must be a non empty string.",
"method": "put",
"reason": "wrong document id",
"result": "error",
"status": 400,
"statusText": "Bad Request"
}, err);
}));
}).nThen(function () {
o.assertReqs(0, "put w/o id -> 0 requests");
}));
});
// put without id => id required test("Put", function () {
o.spy(o, "status", 20, "Put without id"); var o = setUp(this);
o.jio.put({}, o.f); nThenTest(o, nThen(function (waitFor) {
o.clock.tick(5000);
o.assertReqs(0, "put w/o id -> 0 requests"); // put non empty document
o.addFakeServerResponse("xwiki", "POST", "put1", 201, "HTML RESPONSE");
// put non empty document o.jio.put({"_id": "put1", "title": "myPut1"}).
o.addFakeServerResponse("xwiki", "POST", "put1", 201, "HTML RESPONSE"); then(waitFor(function (res) {
o.spy(o, "value", {"ok": true, "id": "put1"}, deepEqual({
"Create = PUT non empty document"); "id": "put1",
o.jio.put({"_id": "put1", "title": "myPut1"}, o.f); "method": "put",
o.clock.tick(5000); "result": "success",
o.server.respond(); "status": 201,
o.assertReqs( "statusText": "Created"
3, }, res);
"put normal doc -> 1 req to get doc, 1 for csrf token, 1 to post" }));
);
o.respond();
}).nThen(function () {
o.assertReqs(
3,
"put normal doc -> 1 req to get doc (404), 1 for csrf token, 1 to post"
);
}));
});
// put but document already exists = update test("PutUpdateDoc", function () {
o.answer = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + var o = setUp(this);
'<page xmlns="http://www.xwiki.org"><title>mtPut1</title></page>'; nThenTest(o, nThen(function (waitFor) {
o.addFakeServerResponse("xwiki", "GET", "put2", 200, o.answer);
o.addFakeServerResponse("xwiki", "POST", "put2", 201, "HTML RESPONSE"); // put but document already exists = update
o.spy(o, "value", {"ok": true, "id": "put2"}, "Updated the document"); o.answer = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
o.jio.put({"_id": "put2", "title": "myPut2abcdedg"}, o.f); '<page xmlns="http://www.xwiki.org"><title>mtPut1</title></page>';
o.clock.tick(5000); o.addFakeServerResponse("xwiki", "GET", "put2", 200, o.answer);
o.server.respond(); o.addFakeServerResponse("xwiki", "POST", "put2", 201, "HTML RESPONSE");
o.assertReqs(
3, o.jio.put({"_id": "put2", "title": "myPut2abcdedg"}).
"put update doc -> 1 req to get doc, 1 for csrf token, 1 to post" then(waitFor(function (ret) {
); deepEqual({
"id": "put2",
"method": "put",
"result": "success",
"status": 204,
"statusText": "No Content"
}, ret);
}));
o.respond();
}).nThen(function () {
o.assertReqs(
4,
"put update doc -> 2 req to get doc, 1 for csrf token, 1 to post"
);
}));
});
util.closeAndcleanUpJio(o.jio); test("PutAttachmentNoDocId", function () {
var o = setUp(this);
nThenTest(o, nThen(function (waitFor) {
// putAttachment without doc id => id required
o.jio.putAttachment({}, function () {
ok(0);
}, waitFor(function (err) {
deepEqual({
"attachment": undefined,
"error": "bad_request",
"message": "Document id must be a non empty string.",
"method": "putAttachment",
"reason": "wrong document id",
"result": "error",
"status": 400,
"statusText": "Bad Request"
}, err);
}));
o.respond();
}).nThen(function () {
o.assertReqs(0, "put attach w/o doc id -> 0 requests");
}));
}); });
test("PutAttachment", function () { test("PutAttachmentNoAttachId", function () {
var o = setUp(this);
nThenTest(o, nThen(function (waitFor) {
// putAttachment without attachment id => attachment id required
o.jio.putAttachment({"_id": "putattmt1"}, function () {
ok(0);
}, waitFor(function (err) {
deepEqual({
"attachment": undefined,
"error": "bad_request",
"id": "putattmt1",
"message": "Attachment id must be a non empty string.",
"method": "putAttachment",
"reason": "wrong attachment id",
"result": "error",
"status": 400,
"statusText": "Bad Request"
}, err);
}));
o.respond();
}).nThen(function () {
o.assertReqs(0, "put attach w/o attach id -> 0 requests");
}));
});
test("PutAttachmentUnderlyingDocumentNonexistant", function () {
var o = setUp(this); var o = setUp(this);
nThenTest(o, nThen(function (waitFor) {
// putAttachment without underlying document => not found
o.jio.putAttachment(
{"_id": "putattmtx", "_attachment": "putattmt2", "_data": ""},
function () {
ok(0);
},
waitFor(function (err) {
deepEqual({
"attachment": "putattmt2",
"error": "not_found",
"id": "putattmtx",
"message": "Impossible to add attachment",
"method": "putAttachment",
"reason": "missing",
"result": "error",
"status": 404,
"statusText": "Not Found"
}, err);
})
);
o.respond();
}).nThen(function () {
o.assertReqs(
1,
"put attach w/o existing document -> 1 request to get doc"
);
}));
});
// putAttachment without doc id => id required test("GetNonexistantDoc", function () {
o.spy(o, "status", 20, "PutAttachment without doc id"); var o = setUp(this);
o.jio.putAttachment({}, o.f); nThenTest(o, nThen(function (waitFor) {
o.clock.tick(5000); // get inexistent document
o.assertReqs(0, "put attach w/o doc id -> 0 requests"); o.jio.get({_id: "get_nonexistant_doc"}, function () {
ok(0);
// putAttachment without attachment id => attachment id required }, waitFor(function (err) {
o.spy(o, "status", 22, "PutAttachment without attachment id"); deepEqual({
o.jio.putAttachment({"_id": "putattmt1"}, o.f); "error": "not_found",
o.clock.tick(5000); "id": "get_nonexistant_doc",
o.assertReqs(0, "put attach w/o attach id -> 0 requests"); "message": "Cannot find document",
"method": "get",
// putAttachment without underlying document => not found "reason": "missing",
o.addFakeServerResponse("xwiki", "GET", "putattmtx", 404, "HTML RESPONSE"); "result": "error",
o.spy(o, "status", 404, "PutAttachment without document"); "status": 404,
o.jio.putAttachment({"_id": "putattmtx", "_attachment": "putattmt2"}, o.f); "statusText": "Not Found"
o.clock.tick(5000); }, err);
o.server.respond(); }));
o.assertReqs(1, "put attach w/o existing document -> 1 request to get doc");
o.respond();
// putAttachment with document without data
o.answer = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + }).nThen(function () {
'<page xmlns="http://www.xwiki.org"><title>myPutAttm</title></page>'; o.assertReqs(1, "try to get nonexistent doc -> 1 request");
o.addFakeServerResponse("xwiki", "GET", "putattmt1", 200, o.answer); }));
o.addFakeServerResponse(
"xwiki",
"POST",
"putattmt1/putattmt2",
201,
"HTML RESPONSE"
);
o.spy(o, "value", {"ok": true, "id": "putattmt1/putattmt2"},
"PutAttachment with document, without data");
o.jio.putAttachment({"_id": "putattmt1", "_attachment": "putattmt2"}, o.f);
o.clock.tick(5000);
o.server.respond();
o.assertReqs(3, "put attach -> 1 request to get document, 1 to put " +
"attach, 1 to get csrf token");
util.closeAndcleanUpJio(o.jio);
}); });
test("Get", function () { test("GetAttachInNonexistantDoc", function () {
var o = setUp(this);
nThenTest(o, nThen(function (waitFor) {
o.jio.getAttachment(
{"_id": "noSuchDoc", "_attachment": "its_attachment"},
function () {
ok(0);
},
waitFor(function (err) {
deepEqual({
"attachment": "its_attachment",
"error": "not_found",
"id": "noSuchDoc",
"message": "Cannot find document",
"method": "getAttachment",
"reason": "missing document",
"result": "error",
"status": 404,
"statusText": "Not Found"
}, err);
})
);
o.respond();
}).nThen(function () {
o.assertReqs(1, "try to get nonexistent attach -> 1 request");
}));
});
test("GetDoc", function () {
var o = setUp(this); var o = setUp(this);
nThenTest(o, nThen(function (waitFor) {
o.answer = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
'<page xmlns="http://www.xwiki.org"><title>some title</title></page>';
o.addFakeServerResponse("xwiki", "GET", "get_doc", 200, o.answer);
o.jio.get({_id: "get_doc"}).then(waitFor(function (ret) {
deepEqual({
"data": {
"_attachments": {},
"_id": "get_doc",
"title": "some title"
},
"id": "get_doc",
"method": "get",
"result": "success",
"status": 200,
"statusText": "Ok"
}, ret);
}));
o.respond();
}).nThen(function () {
o.assertReqs(2, "get document -> 2 request");
}));
});
// get inexistent document test("GetNonexistantAttach", function () {
o.spy(o, "status", 404, "Get non existing document"); var o = setUp(this);
o.jio.get("get1", o.f); nThenTest(o, nThen(function (waitFor) {
o.clock.tick(5000);
o.server.respond(); // get inexistent attachment (document exists)
o.assertReqs(1, "try to get nonexistent doc -> 1 request"); o.answer = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
'<page xmlns="http://www.xwiki.org"><title>some title</title></page>';
// get inexistent attachment o.addFakeServerResponse(
o.spy(o, "status", 404, "Get non existing attachment"); "xwiki",
o.jio.get("get1/get2", o.f); "GET",
o.clock.tick(5000); "get_nonexistant_attach",
o.server.respond(); 200,
o.assertReqs(1, "try to get nonexistent attach -> 1 request"); o.answer
);
// get document
o.answer = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + o.jio.getAttachment(
'<page xmlns="http://www.xwiki.org"><title>some title</title></page>'; {"_id": "get_nonexistant_attach", "_attachment": "nxattach"},
o.addFakeServerResponse("xwiki", "GET", "get3", 200, o.answer); function () {
o.spy(o, "value", {"_id": "get3", "title": "some title"}, "Get document"); ok(0);
o.jio.get("get3", o.f); },
o.clock.tick(5000); waitFor(function (err) {
o.server.respond(); deepEqual({
o.assertReqs(1, "get document -> 1 request"); "attachment": "nxattach",
"error": "not_found",
// get inexistent attachment (document exists) "id": "get_nonexistant_attach",
o.spy(o, "status", 404, "Get non existing attachment (doc exists)"); "message": "Cannot find attachment",
o.jio.get({"_id": "get3", "_attachment": "getx"}, o.f); "method": "getAttachment",
o.clock.tick(5000); "reason": "missing attachment",
o.server.respond(); "result": "error",
o.assertReqs(1, "get nonexistant attachment -> 1 request"); "status": 404,
"statusText": "Not Found"
// get attachment }, err);
o.answer = JSON.stringify({"_id": "get4", "title": "some attachment"}); })
o.addFakeServerResponse("xwiki", "GET", "get3/get4", 200, o.answer); );
o.spy(o, "value", {"_id": "get4", "title": "some attachment"},
"Get attachment"); o.respond();
o.jio.get({"_id": "get3", "_attachment": "get4"}, o.f);
o.clock.tick(5000); }).nThen(function () {
o.server.respond(); o.assertReqs(2, "get nonexistant attachment -> 2 request to get doc");
o.assertReqs(1, "get attachment -> 1 request"); }));
});
util.closeAndcleanUpJio(o.jio);
test("GetAttachHappyPath", function () {
var o = setUp(this);
nThenTest(o, nThen(function (waitFor) {
// get attachment
o.addFakeServerResponse(
"xwiki",
"GET",
"spaces/Main/pages/get_attachment$",
200,
'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
'<page xmlns="http://www.xwiki.org"><title>some title</title></page>'
);
o.addFakeServerResponse(
"xwiki",
"GET",
"spaces/Main/pages/get_attachment/attachments",
200,
'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
'<attachments xmlns="http://www.xwiki.org">' +
'<attachment><name>attach_name</name></attachment>' +
'</attachments>'
);
// We can't fully test this
// because sinon doesn't support HTML5 blob responses.
//o.addFakeServerResponse(
// "xwiki", "GET", "download/Main/get_attachment/attach_name", 200,
// "content", "application/octet-stream");
o.jio.getAttachment(
{"_id": "get_attachment", "_attachment": "attach_name"},
waitFor(function (ret) {
deepEqual({
"attachment": "attach_name",
"error": "err_network_error",
"id": "get_attachment",
"message": "Failed to get attachment [get_attachment/attach_name]",
"method": "getAttachment",
"reason": "Error getting data from network",
"result": "error",
"status": 404,
"statusText": "Not Found"
}, ret);
})
);
o.respond();
}).nThen(function () {
o.assertReqs(3, "get attachment -> 2 requests to get doc, 1 for attach");
}));
}); });
test("Remove", function () {
test("RemoveNonexistantDocument", function () {
var o = setUp(this); var o = setUp(this);
nThenTest(o, nThen(function (waitFor) {
// remove inexistent document
o.addFakeServerResponse("xwiki", "GET", "remove1", 404, "HTML RESPONSE");
o.jio.remove({"_id": "remove1"}, waitFor(function (ret) {
deepEqual({
"error": "error",
"id": "remove1",
"message": "Failed to delete document [remove1]",
"method": "remove",
"reason": "Not Found",
"result": "error",
"status": 404,
"statusText": "Not Found"
}, ret);
}));
o.respond();
}).nThen(function () {
o.assertReqs(
2,
"remove nonexistent doc -> 1 request for csrf and 1 for doc"
);
}));
});
// remove inexistent document
o.addFakeServerResponse("xwiki", "GET", "remove1", 404, "HTML RESPONSE");
o.spy(o, "status", 404, "Remove non existening document");
o.jio.remove({"_id": "remove1"}, o.f);
o.clock.tick(5000);
o.server.respond();
o.assertReqs(
2,
"remove nonexistent doc -> 1 request for csrf and 1 for doc"
);
// remove inexistent document/attachment test("RemoveNonexistantAttachment", function () {
o.addFakeServerResponse("xwiki", "GET", "remove1/remove2", 404, "HTML" + var o = setUp(this);
"RESPONSE"); nThenTest(o, nThen(function (waitFor) {
o.spy(o, "status", 404, "Remove inexistent document/attachment");
o.jio.removeAttachment({"_id": "remove1", "_attachment": "remove2"}, o.f); // remove inexistent document/attachment
o.clock.tick(5000); o.addFakeServerResponse("xwiki", "GET", "remove1/remove2", 404, "HTML" +
o.server.respond(); "RESPONSE");
o.assertReqs(
2, o.jio.removeAttachment(
"remove nonexistant attach -> 1 request for csrf and 1 for doc" {"_id": "remove1", "_attachment": "remove2"},
); function () {
ok(0);
},
waitFor(function (err) {
deepEqual({
"attachment": "remove2",
"error": "not_found",
"id": "remove1",
"message": "Document not found",
"method": "removeAttachment",
"reason": "missing document",
"result": "error",
"status": 404,
"statusText": "Not Found"
}, err);
})
);
o.respond();
}).nThen(function () {
o.assertReqs(1, "remove nonexistant attach -> 1 request for doc");
}));
});
// remove document
//o.answer = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
// '<page xmlns="http://www.xwiki.org"><title>some doc</title></page>';
//o.addFakeServerResponse("xwiki", "GET", "remove3", 200, o.answer);
o.addFakeServerResponse("xwiki", "POST", "bin/delete/Main/remove3",
200, "HTML RESPONSE");
o.spy(o, "value", {"ok": true, "id": "remove3"}, "Remove document");
o.jio.remove({"_id": "remove3"}, o.f);
o.clock.tick(5000);
o.server.respond();
o.assertReqs(
2,
"remove document -> 1 request for csrf and 1 for deleting doc"
);
o.answer = JSON.stringify({ test("RemoveDocument", function () {
"_id": "remove4", var o = setUp(this);
"title": "some doc", nThenTest(o, nThen(function (waitFor) {
"_attachments": {
"remove5": { o.addFakeServerResponse("xwiki", "POST", "delete/Main/remove3",
"length": 4, 200, "HTML RESPONSE");
"digest": "md5-d41d8cd98f00b204e9800998ecf8427e"
} o.jio.remove({"_id": "remove3"}).then(waitFor(function (ret) {
} deepEqual({
}); "id": "remove3",
// remove attachment "method": "remove",
o.addFakeServerResponse( "result": "success",
"xwiki", "status": 204,
"POST", "statusText": "No Content"
"delattachment/Main/remove4/remove5", }, ret);
200, }));
"HTML RESPONSE"
); o.respond();
o.spy(o, "value", {"ok": true, "id": "remove4/remove5"},
"Remove attachment"); }).nThen(function () {
o.jio.removeAttachment({"_id": "remove4", "_attachment": "remove5"}, o.f); o.assertReqs(
o.clock.tick(5000); 2,
o.server.respond(); "remove document -> 1 request for csrf and 1 for deleting doc"
o.assertReqs(2, "remove attach -> 1 request for csrf and 1 for deletion"); );
}));
util.closeAndcleanUpJio(o.jio); });
test("RemoveAttachment", function () {
var o = setUp(this);
nThenTest(o, nThen(function (waitFor) {
// remove attachment
o.addFakeServerResponse(
"xwiki",
"GET",
"spaces/Main/pages/remove4$",
200,
'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
'<page xmlns="http://www.xwiki.org"><title>some title</title></page>'
);
o.addFakeServerResponse(
"xwiki",
"GET",
"spaces/Main/pages/remove4/attachments",
200,
'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
'<attachments xmlns="http://www.xwiki.org">' +
'<attachment><name>remove5</name></attachment>' +
'</attachments>'
);
o.addFakeServerResponse(
"xwiki",
"POST",
"delattachment/Main/remove4/remove5",
200,
"HTML RESPONSE"
);
o.jio.removeAttachment(
{"_id": "remove4", "_attachment": "remove5"}
).always(waitFor(function (ret) {
deepEqual({
"attachment": "remove5",
"id": "remove4",
"method": "removeAttachment",
"result": "success",
"status": 204,
"statusText": "No Content"
}, ret);
}));
o.respond();
}).nThen(function () {
o.assertReqs(
4,
"remove attach -> get doc, get attachments, get csrf, remove attach"
);
}));
}); });
})); }));
...@@ -32,6 +32,10 @@ ...@@ -32,6 +32,10 @@
<script src="queries/key-localstorage.tests.js"></script> <script src="queries/key-localstorage.tests.js"></script>
<script src="jio.storage/localstorage.tests.js"></script> <script src="jio.storage/localstorage.tests.js"></script>
<script src="../lib/jquery/jquery.min.js"></script>
<script src="../src/jio.storage/xwikistorage.js"></script>
<script src="jio.storage/xwikistorage.tests.js"></script>
<script src="../src/jio.storage/davstorage.js"></script> <script src="../src/jio.storage/davstorage.js"></script>
<script src="jio.storage/davstorage.tests.js"></script> <script src="jio.storage/davstorage.tests.js"></script>
......
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