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,
nomen: true
plusplus: true,
nomen: true,
regexp: true
*/
/*global
define: true,
exports: true,
require: true,
jIO: true,
jQuery: true,
XMLHttpRequest: true,
Blob: true,
FormData: true,
window: true,
setTimeout: true
XMLHttpRequest,
FormData
*/
/**
* JIO XWiki Storage. Type = 'xwiki'.
* XWiki Document/Attachment storage.
*/
(function () {
(function (dependencies, module) {
"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 || {};
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 = {};
priv.pathBasedMultiwiki = (spec.pathBasedMultiwiki !== undefined)
? spec.pathBasedMultiwiki : detectIsPathBasedMultiwiki(priv.xwikiurl);
/**
* Get the Space and Page components of a documkent ID.
......@@ -35,6 +114,7 @@
* @return a map of { 'space':<Space>, 'page':<Page> }
*/
priv.getParts = function (id) {
if (id.indexOf('/') === -1) {
return {
space: 'Main',
......@@ -103,37 +183,21 @@
+ priv.wiki + '/spaces/' + parts.space + '/pages/' + parts.page;
};
/**
* Make an HTML5 Blob object.
* Equivilant to the `new Blob()` constructor.
* Will fall back on deprecated BlobBuilder if necessary.
*/
priv.makeBlob = function (contentArray, options) {
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);
priv.getURL = function (action, space, page) {
var out = [priv.xwikiurl];
if (!priv.pathBasedMultiwiki) {
out.push('bin');
} else {
out.push('wiki', priv.wiki);
}
};
priv.isBlob = function (potentialBlob) {
return potentialBlob !== undefined &&
potentialBlob.toString() === "[object Blob]";
out.push(action, space, page);
return out.join('/');
};
/*
* Wrapper for the xwikistorage based on localstorage JiO store.
*/
xwikistorage = {
priv._storage = this._storage = {
/**
* Get content of an XWikiDocument.
*
......@@ -143,7 +207,60 @@
*/
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;
out = {};
try {
......@@ -165,7 +282,7 @@
out.content = $(this).text();
});
out._id = docId;
andThen(out, null);
getAttachments(out);
} catch (err) {
andThen(null, {
status: 500,
......@@ -175,7 +292,7 @@
reason: ""
});
}
};
}
$.ajax({
url: priv.getDocRestURL(docId),
......@@ -184,7 +301,7 @@
dataType: 'xml',
// 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) {
if (jqxhr.status === 404) {
andThen(null, null);
......@@ -217,14 +334,10 @@
// need to do this manually, jquery doesn't support returning blobs.
xhr = new XMLHttpRequest();
parts = priv.getParts(docId);
url = priv.xwikiurl + '/bin/download/' + parts.space +
"/" + parts.page + "/" + fileName + '?cb=' + Math.random();
url = priv.getURL('download', parts.space, parts.page) +
'/' + fileName + '?cb=' + Math.random();
xhr.open('GET', url, true);
if (priv.useBlobs) {
xhr.responseType = 'blob';
} else {
xhr.responseType = 'text';
}
xhr.responseType = 'blob';
xhr.onload = function () {
if (xhr.status === 200) {
......@@ -259,13 +372,12 @@
setItem: function (id, doc, andThen) {
priv.doWithFormToken(function (formToken, err) {
if (err) {
that.error(err);
andThen(err);
return;
}
var parts = priv.getParts(id);
$.ajax({
url: priv.xwikiurl + "/bin/preview/" +
parts.space + '/' + parts.page,
url: priv.getURL('preview', parts.space, parts.page),
type: "POST",
async: true,
dataType: 'text',
......@@ -308,26 +420,26 @@
* @param docId the ID of the document to attach to.
* @param fileName the attachment file name.
* @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.
*/
setAttachment: function (docId, fileName, mimeType, content, andThen) {
setAttachment: function (docId, fileName, blob, andThen) {
priv.doWithFormToken(function (formToken, err) {
var parts, blob, fd, xhr;
var parts, fd, xhr;
if (err) {
that.error(err);
andThen(err);
return;
}
parts = priv.getParts(docId);
blob = priv.isBlob(content)
? content
: priv.makeBlob([content], {type: mimeType});
fd = new FormData();
fd.append("filepath", blob, fileName);
fd.append("form_token", formToken);
xhr = new XMLHttpRequest();
xhr.open('POST', priv.xwikiurl + "/bin/upload/" +
parts.space + '/' + parts.page, true);
xhr.open(
'POST',
priv.getURL('upload', parts.space, parts.page),
true
);
xhr.onload = function () {
if (xhr.status === 302 || xhr.status === 200) {
andThen(null);
......@@ -349,13 +461,12 @@
removeItem: function (id, andThen) {
priv.doWithFormToken(function (formToken, err) {
if (err) {
that.error(err);
andThen(err);
return;
}
var parts = priv.getParts(id);
$.ajax({
url: priv.xwikiurl + "/bin/delete/" +
parts.space + '/' + parts.page,
url: priv.getURL('delete', parts.space, parts.page),
type: "POST",
async: true,
dataType: 'text',
......@@ -383,12 +494,12 @@
var parts = priv.getParts(docId);
priv.doWithFormToken(function (formToken, err) {
if (err) {
that.error(err);
andThen(err);
return;
}
$.ajax({
url: priv.xwikiurl + "/bin/delattachment/" + parts.space + '/' +
parts.page + '/' + fileName,
url: priv.getURL('delattachment', parts.space, parts.page) +
'/' + fileName,
type: "POST",
async: true,
dataType: 'text',
......@@ -411,322 +522,594 @@
}
});
});
}
};
},
// ==================== Tools ====================
/**
* Update [doc] the document object and remove [doc] keys
* which are not in [new_doc]. It only changes [doc] keys not starting
* with an underscore.
* ex: doc: {key:value1,_key:value2} with
* new_doc: {key:value3,_key:value4} updates
* doc: {key:value3,_key:value2}.
* @param {object} doc The original document object.
* @param {object} new_doc The new document object
*/
priv.documentObjectUpdate = function (doc, new_doc) {
var k;
for (k in doc) {
if (doc.hasOwnProperty(k)) {
if (k[0] !== '_') {
delete doc[k];
/**
* Gets a document list from the xwiki storage.
* It will retreive an array containing files meta data owned by
* the user.
* @method allDocs
*/
allDocs: function (includeDocs, andThen) {
var getData = function (callback) {
$.ajax({
url: priv.xwikiurl + '/rest/wikis/xwiki/pages?cb=' + Date.now(),
type: "GET",
async: true,
dataType: 'xml',
success: function (xmlData) {
var data = [];
$(xmlData).find('fullName').each(function () {
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 (k in new_doc) {
if (new_doc.hasOwnProperty(k)) {
if (k[0] !== '_') {
doc[k] = new_doc[k];
for (i = 0; i < rows.length; i++) {
rows[i] = {
id: rows[i],
key: rows[i],
value: {}
};
}
}
if (includeDocs) {
next(0);
} else {
andThen(rows);
}
});
}
};
}
/**
* Checks if an object has no enumerable keys
* @method objectIsEmpty
* @param {object} obj The object
* @return {boolean} true if empty, else false
*/
priv.objectIsEmpty = function (obj) {
var k;
for (k in obj) {
if (obj.hasOwnProperty(k)) {
return false;
}
/**
* Create a document in local storage.
*
* @method post
* @param {Object} command The JIO command
* @param {Object} metadata The metadata to store
* @param {Object} options The command options
*/
XWikiStorage.prototype.post = function (command, metadata) {
var doc_id = metadata._id, that = this;
if (!doc_id) {
doc_id = jIO.util.generateUuid();
}
that._storage.getItem(doc_id, function (doc, err) {
if (err) {
command.error(err);
return;
}
return true;
};
// ==================== attributes ====================
// the wiki to store stuff in
priv.wiki = spec.wiki || 'xwiki';
// unused
priv.username = spec.username;
priv.language = spec.language;
// URL location of the wiki, unused since
// XWiki doesn't currently allow cross-domain requests.
priv.xwikiurl = spec.xwikiurl ||
window.location.href.replace(/\/xwiki\/bin\//, '/xwiki\n')
.split('\n')[0];
// should be: s@/xwiki/bin/.*$@/xwiki@
// but jslint gets in the way.
// Which URL to load for getting the Anti-CSRF form token, used for testing.
priv.formTokenPath = spec.formTokenPath || priv.xwikiurl;
// If true then Blob objects will be returned by
// getAttachment() rather than strings.
priv.useBlobs = spec.useBlobs || false;
if (doc === null) {
// the document does not exist
doc = jIO.util.deepClone(metadata);
doc._id = doc_id;
delete doc._attachments;
that._storage.setItem(doc_id, doc, function (err) {
if (err) {
command.error(
"failed",
"failed to upload document",
String(err)
);
} else {
command.success({"id": doc_id});
}
});
} else {
// the document already exists
command.error(
"conflict",
"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;
/**
* Create or update a document in local storage.
*
* @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 () {
return {
"username": priv.username,
"language": priv.language,
"xwikiurl": priv.xwikiurl
};
};
// the document already exists
// download data
if ((doc._attachments || {})[param._attachment]) {
status = "no_content";
}
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 () {
return '';
};
/**
* Get a document
*
* @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 ====================
/**
* Create a document in local storage.
* @method post
* @param {object} command The JIO command
*/
that.post = function (command) {
var docId = command.getDocId();
if (!(typeof docId === "string" && docId !== "")) {
setTimeout(function () {
that.error({
"status": 405,
"statusText": "Method Not Allowed",
"error": "method_not_allowed",
"message": "Cannot create document which id is undefined",
"reason": "Document id is undefined"
/**
* Get an attachment
*
* @method getAttachment
* @param {Object} command The JIO command
* @param {Object} param The given parameters
* @param {Object} options The command options
*/
XWikiStorage.prototype.getAttachment = function (command, param) {
var that = this;
that._storage.getItem(param._id, function (doc, err) {
if (err) {
return command.error(err);
}
if (doc === null) {
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.
* @method put
* @param {object} command The JIO command
*/
that.put = function (command) {
xwikistorage.getItem(command.getDocId(), function (doc, err) {
if (err) {
that.error(err);
} else if (doc === null) {
doc = command.cloneDoc();
} else {
priv.documentObjectUpdate(doc, command.cloneDoc());
}
// write
xwikistorage.setItem(command.getDocId(), doc, function (err) {
/**
* Remove an attachment
*
* @method removeAttachment
* @param {Object} command The JIO command
* @param {Object} param The given parameters
* @param {Object} options The command options
*/
XWikiStorage.prototype.removeAttachment = function (command, param) {
var that = this;
that._storage.getItem(param._id, function (doc, err) {
if (err) {
return command.error(err);
}
if (typeof doc !== 'object' || doc === null) {
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) {
that.error(err);
command.error(err);
} else {
that.success({
"ok": true,
"id": command.getDocId()
});
command.success();
}
});
});
};
/**
* 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}});
});
};
/**
* Add an attachment to a document
* @method putAttachment
* @param {object} command The JIO command
*/
that.putAttachment = function (command) {
xwikistorage.getItem(command.getDocId(), function (doc, err) {
} else {
that._storage.allDocs(true, function (rows, err) {
if (err) {
that.error(err);
} else if (doc === null) {
// the document does not exist
that.error({
"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()
});
}
});
return command.error(err);
}
for (i = 0; i < rows.length; i++) {
document_list.push(rows[i].doc);
}
});
};
/**
* Get a document or attachment
* @method get
* @param {object} command The JIO command
*/
that.get = that.getAttachment = function (command) {
if (typeof command.getAttachmentId() === "string") {
// seeking for an attachment
xwikistorage.getAttachment(command.getDocId(),
command.getAttachmentId(),
function (attach, err) {
if (err) {
that.error(err);
} else if (attach !== null) {
that.success(attach);
} else {
that.error({
"status": 404,
"statusText": "Not Found",
"error": "not_found",
"message": "Cannot find the attachment",
"reason": "Attachment does not exist"
});
options.select_list = options.select_list || [];
if (options.select_list.indexOf("_id") === -1) {
options.select_list.push("_id");
delete_id = true;
}
if (options.include_docs === true) {
document_object = {};
document_list.forEach(function (meta) {
document_object[meta._id] = meta;
});
}
jIO.QueryFactory.create(options.query || "", this._key_schema).
exec(document_list, options).then(function () {
document_list = document_list.map(function (value) {
var o = {
"id": value._id,
"key": value._id
};
if (options.include_docs === true) {
o.doc = document_object[value._id];
delete document_object[value._id];
}
if (delete_id) {
delete value._id;
}
o.value = value;
return o;
});
} else {
// seeking for a document
xwikistorage.getItem(command.getDocId(), function (doc, err) {
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"
});
}
command.success({"data": {
"total_rows": document_list.length,
"rows": document_list
}});
});
}
};
/**
* 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;
}
/**
* Remove a document or attachment
* @method remove
* @param {object} command The JIO command
*/
that.remove = that.removeAttachment = function (command) {
var objId, complete;
// notFoundError = function (word) {
// that.error({
// "status": 404,
// "statusText": "Not Found",
// "error": "not_found",
// "message": word + " not found",
// "reason": "missing"
// });
// };
objId = command.getDocId();
complete = function (err) {
if (err) {
that.error(err);
} else {
that.success({
"ok": true,
"id": objId
});
function attachmentFound(param, attachment) {
if (param.referenced_attachments.indexOf(attachment) !== -1) {
return;
}
if (param.unreferenced_attachments.indexOf(attachment) !== -1) {
return;
}
param.unreferenced_attachments[param.unreferenced_attachments.length] =
attachment;
}
function repairOne(param, repair) {
var i, doc, modified;
doc = that._storage.getItem(param._id);
if (doc === null) {
return; // OK
}
// check document type
if (typeof doc !== 'object' || doc === null) {
// wrong document
if (!repair) {
return {"error": true, "answers": [
"conflict",
"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 {
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
}
/**
* Get all filenames belonging to a user from the document index
* @method allDocs
* @param {object} command The JIO command
*/
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"
});
});
};
function repairAll(param, repair) {
var i, result;
for (i in that._database) {
if (that._database.hasOwnProperty(i)) {
// browsing every entry
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) {
define(['jquery', 'jio'], function (jquery, jIO) {
$ = jquery;
jIO.addStorageType('xwiki', store);
});
} else {
jIO.addStorageType('xwiki', store);
$ = jQuery;
}
jIO.addStorage('xwiki', XWikiStorage);
}());
}));
/*jslint indent: 2, maxlen: 80, nomen: true */
/*global define, jIO, jio_tests, test, ok, sinon, module */
/*jslint
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);
(function (dependencies, module) {
......@@ -7,34 +25,103 @@
if (typeof define === 'function' && define.amd) {
return define(dependencies, module);
}
module(jIO, jio_tests);
}(['jio', 'jio_tests', 'xwikistorage'], function (jIO, util) {
module(jIO);
}(['jio', 'xwikistorage'], function (jIO) {
"use strict";
function generateTools() {
var o = {
clock: sinon.useFakeTimers(),
spy: util.ospy,
tick: util.otick
function nThen(next) {
var funcs = [],
timeouts = [],
calls = 0,
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) {
/*jslint unparam: true */
o.server.respondWith(method, new RegExp(path), [
status,
{"Content-Type": 'application/xml'},
response
]);
}
o.addFakeServerResponse = addFakeServerResponse;
return o;
waitFor.abort = function () {
timeouts.forEach(clearTimeout);
abort = 1;
};
ret = {
nThen: function (next) {
if (!abort) {
if (!calls) {
next(waitFor);
} 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');
function setUp(that) {
var o = generateTools(that);
o.server = sinon.fakeServer.create();
o.jio = jIO.newJio({type: 'xwiki', formTokenPath: 'form_token'});
function setUp() {
var o = {
sinon: sinon.sandbox.create()
};
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,
'<meta name="form_token" content="OMGHAX"/>');
o._addFakeServerResponse = o.addFakeServerResponse;
......@@ -46,11 +133,11 @@
o.assertReqs = function (count, message) {
var i, j, req, expected, ex;
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 [" +
(o.server.requests.length - (o.requests - count)) + "]]");
(o.sinon.server.requests.length - (o.requests - count)) + "]]");
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) {
break;
}
......@@ -70,242 +157,588 @@
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 () {
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
o.spy(o, "status", 405, "Post without id");
o.jio.post({}, o.f);
o.clock.tick(5000);
o.assertReqs(0, "no id -> no request");
// post non empty document
o.addFakeServerResponse("xwiki", "POST", "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);
o.server.respond();
o.assertReqs(
3,
"put -> 1 request to get csrf token, 1 to get doc and 1 to post data"
);
test("Post2", function () {
var o = setUp(this);
nThenTest(o, nThen(function (waitFor) {
// 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", "pages/myFile2", 200, o.answer);
o.jio.post({"_id": "myFile2", "title": "hello again"}).
then(function () {
ok(0);
}, waitFor(function (err) {
deepEqual({
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);
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
o.spy(o, "status", 20, "Put without id");
o.jio.put({}, o.f);
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");
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.respond();
o.assertReqs(
3,
"put normal doc -> 1 req to get doc, 1 for csrf token, 1 to post"
);
test("Put", function () {
var o = setUp(this);
nThenTest(o, nThen(function (waitFor) {
// put non empty document
o.addFakeServerResponse("xwiki", "POST", "put1", 201, "HTML RESPONSE");
o.jio.put({"_id": "put1", "title": "myPut1"}).
then(waitFor(function (res) {
deepEqual({
"id": "put1",
"method": "put",
"result": "success",
"status": 201,
"statusText": "Created"
}, res);
}));
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
o.answer = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
'<page xmlns="http://www.xwiki.org"><title>mtPut1</title></page>';
o.addFakeServerResponse("xwiki", "GET", "put2", 200, o.answer);
o.addFakeServerResponse("xwiki", "POST", "put2", 201, "HTML RESPONSE");
o.spy(o, "value", {"ok": true, "id": "put2"}, "Updated the document");
o.jio.put({"_id": "put2", "title": "myPut2abcdedg"}, o.f);
o.clock.tick(5000);
o.server.respond();
o.assertReqs(
3,
"put update doc -> 1 req to get doc, 1 for csrf token, 1 to post"
);
test("PutUpdateDoc", function () {
var o = setUp(this);
nThenTest(o, nThen(function (waitFor) {
// put but document already exists = update
o.answer = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
'<page xmlns="http://www.xwiki.org"><title>mtPut1</title></page>';
o.addFakeServerResponse("xwiki", "GET", "put2", 200, o.answer);
o.addFakeServerResponse("xwiki", "POST", "put2", 201, "HTML RESPONSE");
o.jio.put({"_id": "put2", "title": "myPut2abcdedg"}).
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);
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
o.spy(o, "status", 20, "PutAttachment without doc id");
o.jio.putAttachment({}, o.f);
o.clock.tick(5000);
o.assertReqs(0, "put attach w/o doc id -> 0 requests");
// 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);
o.assertReqs(0, "put attach w/o attach id -> 0 requests");
// putAttachment without underlying document => not found
o.addFakeServerResponse("xwiki", "GET", "putattmtx", 404, "HTML RESPONSE");
o.spy(o, "status", 404, "PutAttachment without document");
o.jio.putAttachment({"_id": "putattmtx", "_attachment": "putattmt2"}, o.f);
o.clock.tick(5000);
o.server.respond();
o.assertReqs(1, "put attach w/o existing document -> 1 request to get doc");
// putAttachment with document without data
o.answer = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
'<page xmlns="http://www.xwiki.org"><title>myPutAttm</title></page>';
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("GetNonexistantDoc", function () {
var o = setUp(this);
nThenTest(o, nThen(function (waitFor) {
// get inexistent document
o.jio.get({_id: "get_nonexistant_doc"}, function () {
ok(0);
}, waitFor(function (err) {
deepEqual({
"error": "not_found",
"id": "get_nonexistant_doc",
"message": "Cannot find document",
"method": "get",
"reason": "missing",
"result": "error",
"status": 404,
"statusText": "Not Found"
}, err);
}));
o.respond();
}).nThen(function () {
o.assertReqs(1, "try to get nonexistent doc -> 1 request");
}));
});
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);
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
o.spy(o, "status", 404, "Get non existing document");
o.jio.get("get1", o.f);
o.clock.tick(5000);
o.server.respond();
o.assertReqs(1, "try to get nonexistent doc -> 1 request");
// get inexistent attachment
o.spy(o, "status", 404, "Get non existing attachment");
o.jio.get("get1/get2", o.f);
o.clock.tick(5000);
o.server.respond();
o.assertReqs(1, "try to get nonexistent attach -> 1 request");
// get document
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", "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();
o.assertReqs(1, "get document -> 1 request");
// get inexistent attachment (document exists)
o.spy(o, "status", 404, "Get non existing attachment (doc exists)");
o.jio.get({"_id": "get3", "_attachment": "getx"}, o.f);
o.clock.tick(5000);
o.server.respond();
o.assertReqs(1, "get nonexistant attachment -> 1 request");
// get attachment
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.jio.get({"_id": "get3", "_attachment": "get4"}, o.f);
o.clock.tick(5000);
o.server.respond();
o.assertReqs(1, "get attachment -> 1 request");
util.closeAndcleanUpJio(o.jio);
test("GetNonexistantAttach", function () {
var o = setUp(this);
nThenTest(o, nThen(function (waitFor) {
// get inexistent attachment (document exists)
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_nonexistant_attach",
200,
o.answer
);
o.jio.getAttachment(
{"_id": "get_nonexistant_attach", "_attachment": "nxattach"},
function () {
ok(0);
},
waitFor(function (err) {
deepEqual({
"attachment": "nxattach",
"error": "not_found",
"id": "get_nonexistant_attach",
"message": "Cannot find attachment",
"method": "getAttachment",
"reason": "missing attachment",
"result": "error",
"status": 404,
"statusText": "Not Found"
}, err);
})
);
o.respond();
}).nThen(function () {
o.assertReqs(2, "get nonexistant attachment -> 2 request to get doc");
}));
});
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);
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
o.addFakeServerResponse("xwiki", "GET", "remove1/remove2", 404, "HTML" +
"RESPONSE");
o.spy(o, "status", 404, "Remove inexistent document/attachment");
o.jio.removeAttachment({"_id": "remove1", "_attachment": "remove2"}, o.f);
o.clock.tick(5000);
o.server.respond();
o.assertReqs(
2,
"remove nonexistant attach -> 1 request for csrf and 1 for doc"
);
test("RemoveNonexistantAttachment", function () {
var o = setUp(this);
nThenTest(o, nThen(function (waitFor) {
// remove inexistent document/attachment
o.addFakeServerResponse("xwiki", "GET", "remove1/remove2", 404, "HTML" +
"RESPONSE");
o.jio.removeAttachment(
{"_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({
"_id": "remove4",
"title": "some doc",
"_attachments": {
"remove5": {
"length": 4,
"digest": "md5-d41d8cd98f00b204e9800998ecf8427e"
}
}
});
// remove attachment
o.addFakeServerResponse(
"xwiki",
"POST",
"delattachment/Main/remove4/remove5",
200,
"HTML RESPONSE"
);
o.spy(o, "value", {"ok": true, "id": "remove4/remove5"},
"Remove attachment");
o.jio.removeAttachment({"_id": "remove4", "_attachment": "remove5"}, o.f);
o.clock.tick(5000);
o.server.respond();
o.assertReqs(2, "remove attach -> 1 request for csrf and 1 for deletion");
util.closeAndcleanUpJio(o.jio);
test("RemoveDocument", function () {
var o = setUp(this);
nThenTest(o, nThen(function (waitFor) {
o.addFakeServerResponse("xwiki", "POST", "delete/Main/remove3",
200, "HTML RESPONSE");
o.jio.remove({"_id": "remove3"}).then(waitFor(function (ret) {
deepEqual({
"id": "remove3",
"method": "remove",
"result": "success",
"status": 204,
"statusText": "No Content"
}, ret);
}));
o.respond();
}).nThen(function () {
o.assertReqs(
2,
"remove document -> 1 request for csrf and 1 for deleting doc"
);
}));
});
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 @@
<script src="queries/key-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="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