diff --git a/src/jio.storage/s3storage.js b/src/jio.storage/s3storage.js index 402d949e0302e3778e874ca7366c36acdc859ad5..5acd2740cc20d87a6fe63cc083ff076aa2eecd3f 100644 --- a/src/jio.storage/s3storage.js +++ b/src/jio.storage/s3storage.js @@ -604,7 +604,7 @@ that.XHRwrapper(command, docId, '', 'GET', mime, '', isJIO, false); }; - that.getAttachment = function (command,param,option) { + that.getAttachment = function (command,param,option) { var docId, attachId, isJIO, mime; function getTheAttachment(){ @@ -848,7 +848,8 @@ function putDocument() { var data, doc; - data = JSON.parse(my_document); + //data = JSON.parse(my_document); + data = my_document; doc = priv.updateMeta(data, docId, attachId, "remove", ''); that.XHRwrapper(command, docId, '', 'PUT', mime, doc, false, false, function (reponse) { @@ -860,18 +861,35 @@ function getDocument() { that.XHRwrapper(command, docId, '', 'GET', mime, '', false, false, function (response) { - my_document = response; - if (JSON.parse(my_document)._attachments[attachId] !== undefined){ - putDocument(); - } else { + if (response == '404'){ return command.error( - 404, - "missing attachment", - "This Attachment does not exist" - ); + 404, + "missing document", + "This Document does not exist" + ); + } else { + my_document = JSON.parse(response); + if (my_document._attachments == undefined){ + return command.error( + 404, + "missing attachment", + "This Document has no attachments" + ); + } + else { + if (my_document._attachments[attachId] !== undefined){ + putDocument(); + } else { + return command.error( + 404, + "missing attachment", + "This Attachment does not exist" + ); + } + } } } - ); + ); } getDocument(); }; @@ -882,7 +900,7 @@ * @param {object} command The JIO command **/ - that.allDocs = function (command) { + that.allDocs = function (command, param, options) { var my_document, mime; my_document = null; mime = 'text/plain; charset=UTF-8'; @@ -946,11 +964,6 @@ errCallback = function (err) { if (err.status === 404) { - //status - //statustext "Not Found" - //error - //reason "reason" - //message "did not work" err.error = "not_found"; command.error(err); } else { @@ -960,81 +973,60 @@ i = resultTable.length - 1; - - - + ///* JONR : what is the replacement for command.getOption ? */ // if (command.getOption("include_docs") === true) { - - // for (i; i >= 0; i -= 1) { - // keyId = resultTable[i]; - // Signature = that.encodeAuthorization(keyId); - // callURL = 'http://' + priv.server + '.s3.amazonaws.com/' + keyId; - // requestUTC = new Date().toUTCString(); - // parse = true; - - // allDocResponse.rows[i] = { - // "id": priv.fileNameToIds(keyId).join(), - // "key": keyId, - // "value": {} - // }; - // checkCounter = i; - - // $.ajax({ - // contentType : '', - // crossdomain : true, - // url : callURL, - // type : 'GET', - // headers : { - // 'Authorization' : "AWS" - // + " " - // + priv.AWSIdentifier - // + ":" - // + Signature, - // 'x-amz-date' : requestUTC, - // 'Content-Type' : 'application/json' - // //'Content-MD5' : '' - // //'Content-Length' : , - // //'Expect' : , - // //'x-amz-security-token' : , - // }, - // success : dealCallback(i, countB, allDocResponse), - // error : errCallback(command.error) - // }); - // countB += 1; - // } - // } - //else { + ///*TRISTANC : + //allDocs = function (command, param, options) { + //if (options.include_docs) { + if (options.include_docs) { + for (i; i >= 0; i -= 1) { + keyId = resultTable[i]; + Signature = that.encodeAuthorization(keyId); + callURL = 'http://' + priv.server + '.s3.amazonaws.com/' + keyId; + requestUTC = new Date().toUTCString(); + parse = true; + allDocResponse.rows[i] = { + "id": priv.fileNameToIds(keyId).join(), + "key": keyId, + "value": {} + }; + checkCounter = i; + $.ajax({ + contentType : '', + crossdomain : true, + url : callURL, + type : 'GET', + headers : { + 'Authorization' : "AWS" + + " " + + priv.AWSIdentifier + + ":" + + Signature, + 'x-amz-date' : requestUTC, + 'Content-Type' : 'application/json' + //'Content-MD5' : '' + //'Content-Length' : , + //'Expect' : , + //'x-amz-security-token' : , + }, + success : dealCallback(i, countB, allDocResponse), + error : errCallback(command.error) + }); + countB += 1; + } + } + else { for (i; i >= 0; i -= 1) { keyId = resultTable[i]; allDocResponse.rows[i] = { "id": priv.fileNameToIds(keyId).join(), + "key": priv.fileNameToIds(keyId).join(), "value": {} }; } - - - // allDocResponse = { - // "data": { - // "total_rows": 2, - // "rows": [{ - // "id": "lol", - // "value": {} - // }, { - // "id": "b", - // "value": {} - // }] - // }, - // "method": "allDocs", - // "result": "success", - // "status": 200, - // "statusText": "Ok" - // }; - - - allDocResponse = {"data":allDocResponse}; + allDocResponse = {"data":allDocResponse}; command.success(allDocResponse); - - //} + } } function getXML() { diff --git a/src/jio.storage/splitstorage.js b/src/jio.storage/splitstorage.js new file mode 100644 index 0000000000000000000000000000000000000000..89af669e7568221840280ba940c89be15314388c --- /dev/null +++ b/src/jio.storage/splitstorage.js @@ -0,0 +1,556 @@ +/* + * Copyright 2013, Nexedi SA + * Released under the LGPL license. + * http://www.gnu.org/licenses/lgpl.html + */ + +/*jslint indent:2, maxlen: 80, nomen: true */ +/*global jIO, define, Blob */ + +/** + * Provides a split storage for JIO. This storage splits data + * and store them in the sub storages defined on the description. + * + * { + * "type": "split", + * "storage_list": [<storage description>, ...] + * } + */ +// define([dependencies], module); +(function (dependencies, module) { + "use strict"; + if (typeof define === 'function' && define.amd) { + return define(dependencies, module); + } + module(jIO); +}(['jio'], function (jIO) { + "use strict"; + + /** + * Generate a new uuid + * + * @method generateUuid + * @private + * @return {String} The new uuid + */ + function generateUuid() { + function S4() { + /* 65536 */ + var i, string = Math.floor( + Math.random() * 0x10000 + ).toString(16); + for (i = string.length; i < 4; i += 1) { + string = '0' + string; + } + return string; + } + return S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + + S4() + S4(); + } + + + /** + * Class to merge allDocs responses from several sub storages. + * + * @class AllDocsResponseMerger + * @constructor + */ + function AllDocsResponseMerger() { + + /** + * A list of allDocs response. + * + * @attribute response_list + * @type {Array} Contains allDocs responses + * @default [] + */ + this.response_list = []; + } + AllDocsResponseMerger.prototype.constructor = AllDocsResponseMerger; + + /** + * Add an allDocs response to the response list. + * + * @method addResponse + * @param {Object} response The allDocs response. + * @return {AllDocsResponseMerger} This + */ + AllDocsResponseMerger.prototype.addResponse = function (response) { + this.response_list.push(response); + return this; + }; + + /** + * Add several allDocs responses to the response list. + * + * @method addResponseList + * @param {Array} response_list An array of allDocs responses. + * @return {AllDocsResponseMerger} This + */ + AllDocsResponseMerger.prototype.addResponseList = function (response_list) { + var i; + for (i = 0; i < response_list.length; i += 1) { + this.response_list.push(response_list[i]); + } + return this; + }; + + /** + * Merge the response_list to one allDocs response. + * + * The merger will find rows with the same id in order to merge them, thanks + * to the onRowToMerge method. If no row correspond to an id, rows with the + * same id will be ignored. + * + * @method merge + * @param {Object} [option={}] The merge options + * @param {Boolean} [option.include_docs=false] Tell the merger to also + * merge metadata if true. + * @return {Object} The merged allDocs response. + */ + AllDocsResponseMerger.prototype.merge = function (option) { + var result = [], row, to_merge = [], tmp, i; + if (this.response_list.length === 0) { + return []; + } + /*jslint ass: true */ + while ((row = this.response_list[0].data.rows.shift()) !== undefined) { + to_merge[0] = row; + for (i = 1; i < this.response_list.length; i += 1) { + to_merge[i] = AllDocsResponseMerger.listPopFromRowId( + this.response_list[i].data.rows, + row.id + ); + if (to_merge[i] === undefined) { + break; + } + } + tmp = this.onRowToMerge(to_merge, option || {}); + if (tmp !== undefined) { + result[result.length] = tmp; + } + } + this.response_list = []; + return {"total_rows": result.length, "rows": result}; + }; + + /** + * This method is called when the merger want to merge several rows with the + * same id. + * + * @method onRowToMerge + * @param {Array} row_list An array of rows. + * @param {Object} [option={}] The merge option. + * @param {Boolean} [option.include_docs=false] Also merge the metadata if + * true + * @return {Object} The merged row + */ + AllDocsResponseMerger.prototype.onRowToMerge = function (row_list, option) { + var i, k, new_row = {"value": {}}, data = ""; + option = option || {}; + for (i = 0; i < row_list.length; i += 1) { + new_row.id = row_list[i].id; + if (row_list[i].key) { + new_row.key = row_list[i].key; + } + if (option.include_docs) { + new_row.doc = new_row.doc || {}; + for (k in row_list[i].doc) { + if (row_list[i].doc.hasOwnProperty(k)) { + if (k[0] === "_") { + new_row.doc[k] = row_list[i].doc[k]; + } + } + } + data += row_list[i].doc.data; + } + } + if (option.include_docs) { + try { + data = JSON.parse(data); + } catch (e) { return undefined; } + for (k in data) { + if (data.hasOwnProperty(k)) { + new_row.doc[k] = data[k]; + } + } + } + return new_row; + }; + + /** + * Search for a specific row and pop it. During the search operation, all + * parsed rows are stored on a dictionnary in order to be found instantly + * later. + * + * @method listPopFromRowId + * @param {Array} rows The row list + * @param {String} doc_id The document/row id + * @return {Object/undefined} The poped row + */ + AllDocsResponseMerger.listPopFromRowId = function (rows, doc_id) { + var row; + if (!rows.dict) { + rows.dict = {}; + } + if (rows.dict[doc_id]) { + row = rows.dict[doc_id]; + delete rows.dict[doc_id]; + return row; + } + /*jslint ass: true*/ + while ((row = rows.shift()) !== undefined) { + if (row.id === doc_id) { + return row; + } + rows.dict[row.id] = row; + } + }; + + + /** + * The split storage class used by JIO. + * + * A split storage instance is able to i/o on several sub storages with + * split documents. + * + * @class SplitStorage + */ + function SplitStorage(spec) { + var that = this, priv = {}; + + /** + * The list of sub storages we want to use to store part of documents. + * + * @attribute storage_list + * @private + * @type {Array} Array of storage descriptions + */ + priv.storage_list = spec.storage_list; + + ////////////////////////////////////////////////////////////////////// + // Tools + + /** + * Send a command to all sub storages. All the response are returned + * in a list. The index of the response correspond to the storage_list + * index. If an error occurs during operation, the callback is called with + * `callback(err, undefined)`. The response is given with + * `callback(undefined, response_list)`. + * + * `doc` is the document informations but can also be a list of dedicated + * document informations. In this case, each document is associated to one + * sub storage. + * + * @method send + * @private + * @param {String} method The command method + * @param {Object,Array} doc The document information to send to each sub + * storages or a list of dedicated document + * @param {Object} option The command option + * @param {Function} callback Called at the end + */ + priv.send = function (command, method, doc, option, callback) { + var i, answer_list = [], failed = false; + function onEnd() { + i += 1; + if (i === priv.storage_list.length) { + callback(undefined, answer_list); + } + } + function onSuccess(i) { + return function (response) { + if (!failed) { + answer_list[i] = response; + } + onEnd(); + }; + } + function onError(i) { + return function (err) { + if (!failed) { + failed = true; + err.index = i; + callback(err, undefined); + } + }; + } + if (!Array.isArray(doc)) { + for (i = 0; i < priv.storage_list.length; i += 1) { + if (method === 'allDocs') { + command.storage(priv.storage_list[i])[method](option). + then(onSuccess(i), onError(i)); + } else { + command.storage(priv.storage_list[i])[method](doc, option). + then(onSuccess(i), onError(i)); + } + } + } else { + for (i = 0; i < priv.storage_list.length; i += 1) { + // we assume that alldocs is not called if the there is several docs + command.storage(priv.storage_list[i])[method](doc[i], option). + then(onSuccess(i), onError(i)); + } + } + i = 0; + }; + + /** + * Split document metadata then store them to the sub storages. + * + * @method postOrPut + * @private + * @param {Object} doc A serialized document object + * @param {Object} option Command option properties + * @param {String} method The command method ('post' or 'put') + */ + priv.postOrPut = function (command, doc, option, method) { + var i, data, doc_list = [], doc_underscores = {}; + if (!doc._id) { + doc._id = generateUuid(); // XXX should let gidstorage guess uid + } + for (i in doc) { + if (doc.hasOwnProperty(i)) { + if (i[0] === "_") { + doc_underscores[i] = doc[i]; + delete doc[i]; + } + } + } + data = JSON.stringify(doc); + for (i = 0; i < priv.storage_list.length; i += 1) { + doc_list[i] = JSON.parse(JSON.stringify(doc_underscores)); + doc_list[i].data = data.slice( + (data.length / priv.storage_list.length) * i, + (data.length / priv.storage_list.length) * (i + 1) + ); + } + priv.send(command, method, doc_list, option, function (err) { + if (err) { + err.message = "Unable to " + method + " document"; + delete err.index; + return command.error(err); + } + command.success({"id": doc_underscores._id}); + }); + }; + + ////////////////////////////////////////////////////////////////////// + // JIO commands + + /** + * Split document metadata then store them to the sub storages. + * + * @method post + * @param {Object} command The JIO command + */ + that.post = function (command, metadata, option) { + priv.postOrPut(command, metadata, option, 'post'); + }; + + /** + * Split document metadata then store them to the sub storages. + * + * @method put + * @param {Object} command The JIO command + */ + that.put = function (command, metadata, option) { + priv.postOrPut(command, metadata, option, 'put'); + }; + + /** + * Puts an attachment to the sub storages. + * + * @method putAttachment + * @param {Object} command The JIO command + */ + that.putAttachment = function (command, param, option) { + var i, attachment_list = [], data = param._blob; + for (i = 0; i < priv.storage_list.length; i += 1) { + attachment_list[i] = jIO.util.deepClone(param); + attachment_list[i]._blob = data.slice( + data.size * i / priv.storage_list.length, + data.size * (i + 1) / priv.storage_list.length, + data.type + ); + } + priv.send( + command, + 'putAttachment', + attachment_list, + option, + function (err) { + if (err) { + err.message = "Unable to put attachment"; + delete err.index; + return command.error(err); + } + command.success(); + } + ); + }; + + /** + * Gets splited document metadata then returns real document. + * + * @method get + * @param {Object} command The JIO command + */ + that.get = function (command, param, option) { + var doc = param; + priv.send(command, 'get', doc, option, function (err, response) { + var i, k; + if (err) { + err.message = "Unable to get document"; + delete err.index; + return command.error(err); + } + doc = ''; + for (i = 0; i < response.length; i += 1) { + response[i] = response[i].data; + doc += response[i].data; + } + doc = JSON.parse(doc); + for (i = 0; i < response.length; i += 1) { + for (k in response[i]) { + if (response[i].hasOwnProperty(k)) { + if (k[0] === "_") { + doc[k] = response[i][k]; + } + } + } + } + delete doc._attachments; + for (i = 0; i < response.length; i += 1) { + if (response[i]._attachments) { + for (k in response[i]._attachments) { + if (response[i]._attachments.hasOwnProperty(k)) { + doc._attachments = doc._attachments || {}; + doc._attachments[k] = doc._attachments[k] || { + "length": 0, + "content_type": "" + }; + doc._attachments[k].length += response[i]._attachments[k]. + length; + // if (response[i]._attachments[k].digest) { + // if (doc._attachments[k].digest) { + // doc._attachments[k].digest += " " + response[i]. + // _attachments[k].digest; + // } else { + // doc._attachments[k].digest = response[i]. + // _attachments[k].digest; + // } + // } + doc._attachments[k].content_type = response[i]._attachments[k]. + content_type; + } + } + } + } + command.success({"data": doc}); + }); + }; + + /** + * Gets splited document attachment then returns real attachment data. + * + * @method getAttachment + * @param {Object} command The JIO command + */ + that.getAttachment = function (command, param, option) { + priv.send(command, 'getAttachment', param, option, function ( + err, + response + ) { + if (err) { + err.message = "Unable to get attachment"; + delete err.index; + return command.error(err); + } + + command.success({"data": new Blob(response.map(function (answer) { + return answer.data; + }), {"type": response[0].data.type})}); + }); + }; + + /** + * Removes a document from the sub storages. + * + * @method remove + * @param {Object} command The JIO command + */ + that.remove = function (command, param, option) { + priv.send( + command, + 'remove', + param, + option, + function (err) { + if (err) { + err.message = "Unable to remove document"; + delete err.index; + return command.error(err); + } + command.success(); + } + ); + }; + + /** + * Removes an attachment from the sub storages. + * + * @method removeAttachment + * @param {Object} command The JIO command + */ + that.removeAttachment = function (command, param, option) { + priv.send( + command, + 'removeAttachment', + param, + option, + function (err) { + if (err) { + err.message = "Unable to remove attachment"; + delete err.index; + return command.error(err); + } + command.success(); + } + ); + }; + + /** + * Retreive a list of all document in the sub storages. + * + * If include_docs option is false, then it returns the document list from + * the first sub storage. Else, it will merge results and return. + * + * @method allDocs + * @param {Object} command The JIO command + */ + that.allDocs = function (command, param, option) { + option = {"include_docs": option.include_docs}; + priv.send( + command, + 'allDocs', + param, + option, + function (err, response_list) { + var all_docs_merger; + if (err) { + err.message = "Unable to retrieve document list"; + delete err.index; + return command.error(err); + } + all_docs_merger = new AllDocsResponseMerger(); + all_docs_merger.addResponseList(response_list); + return command.success({"data": all_docs_merger.merge(option)}); + } + ); + }; + + } // end of splitStorage + + jIO.addStorage('split', SplitStorage); +})); diff --git a/src/jio.storage/splitstorage.tests.js b/src/jio.storage/splitstorage.tests.js new file mode 100644 index 0000000000000000000000000000000000000000..e1d1f481501137b58276a716f3a64c411e9c871a --- /dev/null +++ b/src/jio.storage/splitstorage.tests.js @@ -0,0 +1,964 @@ +/*jslint indent: 2, maxlen: 80, nomen: true */ +/*global define, jIO, test_util, RSVP, test, ok, deepEqual, module, stop, + start, hex_sha256 */ + +// define([module_name], [dependencies], module); +(function (dependencies, module) { + "use strict"; + if (typeof define === 'function' && define.amd) { + return define(dependencies, module); + } + module(jIO, test_util, RSVP); +}([ + 'jio', + 'test_util', + 'rsvp', + 'localstorage', + 'splitstorage' +], function (jIO, util, RSVP) { + "use strict"; + + var tool = { + "readBlobAsBinaryString": jIO.util.readBlobAsBinaryString + }; + + function reverse(promise) { + return new RSVP.Promise(function (resolve, reject, notify) { + promise.then(reject, resolve, notify); + }, function () { + promise.cancel(); + }); + } + + /** + * sequence(thens): Promise + * + * Executes a sequence of *then* callbacks. It acts like + * `smth().then(callback).then(callback)...`. The first callback is called + * with no parameter. + * + * Elements of `thens` array can be a function or an array contaning at most + * three *then* callbacks: *onFulfilled*, *onRejected*, *onNotified*. + * + * When `cancel()` is executed, each then promises are cancelled at the same + * time. + * + * @param {Array} thens An array of *then* callbacks + * @return {Promise} A new promise + */ + function sequence(thens) { + var promises = []; + return new RSVP.Promise(function (resolve, reject, notify) { + var i; + promises[0] = new RSVP.Promise(function (resolve) { + resolve(); + }); + for (i = 0; i < thens.length; i += 1) { + if (Array.isArray(thens[i])) { + promises[i + 1] = promises[i]. + then(thens[i][0], thens[i][1], thens[i][2]); + } else { + promises[i + 1] = promises[i].then(thens[i]); + } + } + promises[i].then(resolve, reject, notify); + }, function () { + var i; + for (i = 0; i < promises.length; i += 1) { + promises[i].cancel(); + } + }); + } + + function unexpectedError(error) { + if (error instanceof Error) { + deepEqual([ + error.name + ": " + error.message, + error + ], "UNEXPECTED ERROR", "Unexpected error"); + } else { + deepEqual(error, "UNEXPECTED ERROR", "Unexpected error"); + } + } + + module("SplitStorage + LocalStorage"); + + test("Post", function () { + var shared = {}, jio, jio_local_list = []; + + shared.workspace = {}; + shared.local_storage_description1 = { + "type": "local", + "username": "splitstorage", + "application_name": "post1", + "mode": "memory" + }; + shared.local_storage_description2 = { + "type": "local", + "username": "splitstorage", + "application_name": "post2", + "mode": "memory" + }; + jio = jIO.createJIO({ + "type": "split", + "storage_list": [ + shared.local_storage_description1, + shared.local_storage_description2 + ] + }, {"workspace": shared.workspace}); + jio_local_list[0] = jIO.createJIO(shared.local_storage_description1, { + "workspace": shared.workspace + }); + jio_local_list[1] = jIO.createJIO(shared.local_storage_description2, { + "workspace": shared.workspace + }); + jio_local_list.run = function (method, argument) { + var i, promises = []; + for (i = 0; i < this.length; i += 1) { + promises[i] = this[i][method].apply(this[i], argument); + } + return RSVP.all(promises); + }; + jio_local_list.get = function () { + return this.run("get", arguments); + }; + + stop(); + // post without id + jio.post({ + "_underscored_meta": "uvalue", + "meta": "data" + }).then(function (answer) { + shared.uuid = answer.id; + answer.id = "<uuid>"; + ok(util.isUuid(shared.uuid), "Uuid should look like " + + "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx : " + shared.uuid); + deepEqual(answer, { + "id": "<uuid>", + "method": "post", + "result": "success", + "status": 201, + "statusText": "Created" + }, "Post document without id"); + + // check uploaded documents + return jio_local_list.get({"_id": shared.uuid}); + + }).then(function (answers) { + var i; + for (i = 0; i < answers.length; i += 1) { + deepEqual(answers[i].data, { + "_id": shared.uuid, + "_underscored_meta": "uvalue", + "data": i === 0 ? "{\"meta\"" : ":\"data\"}" + }, "Check uploaded document in sub storage " + (i + 1)); + } + + // post with id + return jio.post({ + "_id": "one", + "_underscored_meta": "uvalue", + "meta": "data", + "hello": "world" + }); + + }).then(function (answer) { + + deepEqual(answer, { + "id": "one", + "method": "post", + "result": "success", + "status": 201, + "statusText": "Created" + }, "Post document with id"); + + // check uploaded documents + return jio_local_list.get({"_id": "one"}); + + }).then(function (answers) { + + deepEqual(answers[0].data, { + "_id": "one", + "_underscored_meta": "uvalue", + "data": "{\"meta\":\"data\"," + }, "Check uploaded document in sub storage 1"); + + deepEqual(answers[1].data, { + "_id": "one", + "_underscored_meta": "uvalue", + "data": "\"hello\":\"world\"}" + }, "Check uploaded document in sub storage 2"); + + // post with id + return reverse(jio.post({ + "_id": "one", + "_underscored_meta": "uvalue", + "meta": "data", + "hello": "world" + })); + + }).then(function (answer) { + + deepEqual(answer, { + "error": "conflict", + "id": "one", + "message": "Unable to post document", + "method": "post", + "reason": "document exists", + "result": "error", + "status": 409, + "statusText": "Conflict" + }, "Post document with same id -> 409 Conflict"); + + }).fail(unexpectedError).always(start); + + }); + + test("PutAttachment", function () { + var shared = {}, jio, jio_local_list = []; + + shared.workspace = {}; + shared.local_storage_description1 = { + "type": "local", + "username": "splitstorage", + "application_name": "putAttachment1", + "mode": "memory" + }; + shared.local_storage_description2 = { + "type": "local", + "username": "splitstorage", + "application_name": "putAttachment2", + "mode": "memory" + }; + jio = jIO.createJIO({ + "type": "split", + "storage_list": [ + shared.local_storage_description1, + shared.local_storage_description2 + ] + }, {"workspace": shared.workspace}); + jio_local_list[0] = jIO.createJIO(shared.local_storage_description1, { + "workspace": shared.workspace + }); + jio_local_list[1] = jIO.createJIO(shared.local_storage_description2, { + "workspace": shared.workspace + }); + jio_local_list.run = function (method, argument) { + var i, promises = []; + for (i = 0; i < this.length; i += 1) { + promises[i] = this[i][method].apply(this[i], argument); + } + return RSVP.all(promises); + }; + jio_local_list.get = function () { + return this.run("get", arguments); + }; + jio_local_list.getAttachmentAsBinaryString = function () { + return this.run("getAttachment", arguments).then(function (answers) { + var i, promises = []; + for (i = 0; i < answers.length; i += 1) { + promises[i] = tool.readBlobAsBinaryString(answers[i].data); + } + return RSVP.all(promises); + }); + }; + + stop(); + + return reverse(jio.putAttachment({ + "_id": "one", + "_attachment": "my_attachment", + "_data": "My Data", + "_content_type": "text/plain" + })).then(function (answer) { + + deepEqual(answer, { + "attachment": "my_attachment", + "error": "not_found", + "id": "one", + "message": "Unable to put attachment", + "method": "putAttachment", + "reason": "missing", + "result": "error", + "status": 404, + "statusText": "Not Found" + }, "Put attachment on a inexistent document -> 404 Not Found"); + + return jio.post({ + "_id": "one", + "_underscored_meta": "uvalue", + "meta": "data" + }); + + }).then(function () { + + return jio.putAttachment({ + "_id": "one", + "_attachment": "my_attachment", + "_data": "My Data", + "_mimetype": "text/plain" + }); + + }).then(function (answer) { + + deepEqual(answer, { + "attachment": "my_attachment", + "id": "one", + "method": "putAttachment", + "result": "success", + "status": 204, + "statusText": "No Content" + }, "Put attachment on a document"); + + // check uploaded documents + return jio_local_list.get({"_id": "one"}); + + }).then(function (answers) { + + deepEqual(answers[0].data, { + "_attachments": { + "my_attachment": { + "content_type": "text/plain", + "digest": "sha256-ebf2d770a6a2dfa135f6c81431f22fc3cbcde9ae" + + "e52759ca9e520d4d964c1322", // sha256("My ") + "length": 3 + } + }, + "_id": "one", + "_underscored_meta": "uvalue", + "data": "{\"meta\"" + }, "Check uploaded document in sub storage 1"); + + deepEqual(answers[1].data, { + "_attachments": { + "my_attachment": { + "content_type": "text/plain", + "digest": "sha256-cec3a9b89b2e391393d0f68e4bc12a9fa6cf358b" + + "3cdf79496dc442d52b8dd528", // sha256("Data") + "length": 4 + } + }, + "_id": "one", + "_underscored_meta": "uvalue", + "data": ":\"data\"}" + }, "Check uploaded document in sub storage 2"); + + return jio_local_list.getAttachmentAsBinaryString({ + "_id": "one", + "_attachment": "my_attachment" + }); + + }).then(function (events) { + + deepEqual(events[0].target.result, "My ", + "Check uploaded document in sub storage 1"); + deepEqual(events[1].target.result, "Data", + "Check uploaded document in sub storage 1"); + + }).fail(unexpectedError).always(start); + + }); + + test("Get", function () { + var shared = {}, jio; + + shared.workspace = {}; + shared.local_storage_description1 = { + "type": "local", + "username": "splitstorage", + "application_name": "get1", + "mode": "memory" + }; + shared.local_storage_description2 = { + "type": "local", + "username": "splitstorage", + "application_name": "get2", + "mode": "memory" + }; + jio = jIO.createJIO({ + "type": "split", + "storage_list": [ + shared.local_storage_description1, + shared.local_storage_description2 + ] + }, {"workspace": shared.workspace}); + + stop(); + + reverse(jio.get({"_id": "one"})).then(function (answer) { + + deepEqual(answer, { + "error": "not_found", + "id": "one", + "message": "Unable to get document", + "method": "get", + "reason": "missing", + "result": "error", + "status": 404, + "statusText": "Not Found" + }, "Get missing document"); + + return jio.post({ + "_id": "one", + "_underscored_meta": "uvalue", + "meta": "data" + }); + + }).then(function () { + + return jio.get({"_id": "one"}); + + }).then(function (answer) { + + deepEqual(answer.data, { + "_id": "one", + "_underscored_meta": "uvalue", + "meta": "data" + }, "Get posted document"); + + return jio.putAttachment({ + "_id": "one", + "_attachment": "my_attachment", + "_data": "My Data", + "_content_type": "text/plain" + }); + + }).then(function () { + + return jio.get({"_id": "one"}); + + }).then(function (answer) { + + deepEqual(answer.data, { + "_id": "one", + "_underscored_meta": "uvalue", + "meta": "data", + "_attachments": { + "my_attachment": { + "length": 7, + "content_type": "text/plain" + } + } + }, "Get document with attachment informations"); + + }).fail(unexpectedError).always(start); + + }); + + test("GetAttachment", function () { + var shared = {}, jio; + + shared.workspace = {}; + shared.local_storage_description1 = { + "type": "local", + "username": "splitstorage", + "application_name": "getAttachment1", + "mode": "memory" + }; + shared.local_storage_description2 = { + "type": "local", + "username": "splitstorage", + "application_name": "getAttachment2", + "mode": "memory" + }; + jio = jIO.createJIO({ + "type": "split", + "storage_list": [ + shared.local_storage_description1, + shared.local_storage_description2 + ] + }, {"workspace": shared.workspace}); + + stop(); + + reverse(jio.getAttachment({ + "_id": "one", + "_attachment": "my_attachment" + })).then(function (answer) { + + deepEqual(answer, { + "attachment": "my_attachment", + "error": "not_found", + "id": "one", + "message": "Unable to get attachment", + "method": "getAttachment", + "reason": "missing document", + "result": "error", + "status": 404, + "statusText": "Not Found" + }, "Get attachment from missing document -> 404 Not Found"); + + return jio.post({ + "_id": "one", + "_underscored_meta": "uvalue", + "meta": "data" + }); + + }).then(function () { + + return reverse(jio.getAttachment({ + "_id": "one", + "_attachment": "my_attachment" + })); + + }).then(function (answer) { + + deepEqual(answer, { + "attachment": "my_attachment", + "error": "not_found", + "id": "one", + "message": "Unable to get attachment", + "method": "getAttachment", + "reason": "missing attachment", + "result": "error", + "status": 404, + "statusText": "Not Found" + }, "Get missing attachment from document"); + + return jio.putAttachment({ + "_id": "one", + "_attachment": "my_attachment", + "_data": "My Data", + "_mimetype": "text/plain" + }); + + }).then(function () { + + return jio.getAttachment({"_id": "one", "_attachment": "my_attachment"}); + + }).then(function (answer) { + return tool.readBlobAsBinaryString(answer.data); + }).then(function (event) { + + deepEqual(event.target.result, "My Data", "Get attachment"); + + }).fail(unexpectedError).always(start); + + }); + + test("RemoveAttachment", function () { + var shared = {}, jio; + + shared.workspace = {}; + shared.local_storage_description1 = { + "type": "local", + "username": "splitstorage", + "application_name": "removeAttachment1", + "mode": "memory" + }; + shared.local_storage_description2 = { + "type": "local", + "username": "splitstorage", + "application_name": "removeAttachment2", + "mode": "memory" + }; + jio = jIO.createJIO({ + "type": "split", + "storage_list": [ + shared.local_storage_description1, + shared.local_storage_description2 + ] + }, {"workspace": shared.workspace}); + + stop(); + + reverse(jio.removeAttachment({ + "_id": "one", + "_attachment": "my_attachment" + })).then(function (answer) { + + deepEqual(answer, { + "attachment": "my_attachment", + "error": "not_found", + "id": "one", + "message": "Unable to remove attachment", + "method": "removeAttachment", + "reason": "missing document", + "result": "error", + "status": 404, + "statusText": "Not Found" + }, "Remove attachment from inexistent document -> 404 Not Found"); + + return jio.post({ + "_id": "one", + "_underscored_meta": "uvalue", + "meta": "data" + }); + + }).then(function () { + + return reverse(jio.removeAttachment({ + "_id": "one", + "_attachment": "my_attachment" + })); + + }).then(function (answer) { + + deepEqual(answer, { + "attachment": "my_attachment", + "error": "not_found", + "id": "one", + "message": "Unable to remove attachment", + "method": "removeAttachment", + "reason": "missing attachment", + "result": "error", + "status": 404, + "statusText": "Not Found" + }, "Remove inexistent attachment -> 404 Not Found"); + + return jio.putAttachment({ + "_id": "one", + "_attachment": "my_attachment", + "_data": "My Data", + "_mimetype": "text/plain" + }); + + }).then(function () { + + return jio.removeAttachment({ + "_id": "one", + "_attachment": "my_attachment" + }); + + }).then(function (answer) { + + deepEqual(answer, { + "attachment": "my_attachment", + "id": "one", + "method": "removeAttachment", + "result": "success", + "status": 204, + "statusText": "No Content" + }, "Remove attachment"); + + return jio.get({"_id": "one"}); + + }).then(function (answer) { + + deepEqual(answer.data, { + "_id": "one", + "_underscored_meta": "uvalue", + "meta": "data" + }, "Check document"); + + return reverse(jio.getAttachment({ + "_id": "one", + "_attachment": "my_attachment" + })); + + }).then(function (answer) { + + deepEqual(answer, { + "attachment": "my_attachment", + "error": "not_found", + "id": "one", + "message": "Unable to get attachment", + "method": "getAttachment", + "reason": "missing attachment", + "result": "error", + "status": 404, + "statusText": "Not Found" + }, "Check attachment -> 404 Not Found"); + + }).fail(unexpectedError).always(start); + + }); + + test("Remove", function () { + var shared = {}, jio; + + shared.workspace = {}; + shared.local_storage_description1 = { + "type": "local", + "username": "splitstorage", + "application_name": "remove1", + "mode": "memory" + }; + shared.local_storage_description2 = { + "type": "local", + "username": "splitstorage", + "application_name": "remove2", + "mode": "memory" + }; + jio = jIO.createJIO({ + "type": "split", + "storage_list": [ + shared.local_storage_description1, + shared.local_storage_description2 + ] + }, {"workspace": shared.workspace}); + + stop(); + + reverse(jio.remove({"_id": "one"})).then(function (answer) { + + deepEqual(answer, { + "error": "not_found", + "id": "one", + "message": "Unable to remove document", + "method": "remove", + "reason": "missing", + "result": "error", + "status": 404, + "statusText": "Not Found" + }, "Remove missing document -> 404 Not Found"); + + return jio.post({ + "_id": "one", + "_underscored_meta": "uvalue", + "meta": "data" + }); + + }).then(function () { + + return jio.putAttachment({ + "_id": "one", + "_attachment": "my_attachment", + "_data": "My Data", + "_mimetype": "text/plain" + }); + + }).then(function () { + + return jio.remove({"_id": "one"}); + + }).then(function (answer) { + + deepEqual(answer, { + "id": "one", + "method": "remove", + "result": "success", + "status": 204, + "statusText": "No Content" + }, "Remove document"); + + return reverse(jio.getAttachment({ + "_id": "one", + "_attachment": "my_attachment" + })); + + }).then(function (answer) { + + deepEqual(answer, { + "attachment": "my_attachment", + "error": "not_found", + "id": "one", + "message": "Unable to get attachment", + "method": "getAttachment", + "reason": "missing document", + "result": "error", + "status": 404, + "statusText": "Not Found" + }, "Check attachment -> 404 Not Found"); + + return reverse(jio.get({"_id": "one"})); + + }).then(function (answer) { + + deepEqual(answer, { + "error": "not_found", + "id": "one", + "message": "Unable to get document", + "method": "get", + "reason": "missing", + "result": "error", + "status": 404, + "statusText": "Not Found" + }, "Check document -> 404 Not Found"); + + }).fail(unexpectedError).always(start); + + }); + + test("Put", function () { + var shared = {}, jio; + + shared.workspace = {}; + shared.local_storage_description1 = { + "type": "local", + "username": "splitstorage", + "application_name": "put1", + "mode": "memory" + }; + shared.local_storage_description2 = { + "type": "local", + "username": "splitstorage", + "application_name": "put2", + "mode": "memory" + }; + jio = jIO.createJIO({ + "type": "split", + "storage_list": [ + shared.local_storage_description1, + shared.local_storage_description2 + ] + }, {"workspace": shared.workspace}); + + stop(); + + jio.put({ + "_id": "one", + "_underscored_meta": "uvalue", + "meta": "data" + }).then(function (answer) { + + deepEqual(answer, { + "id": "one", + "method": "put", + "result": "success", + "status": 204, + "statusText": "No Content" + }, "Put new document"); + + return jio.get({"_id": "one"}); + + }).then(function (answer) { + + deepEqual(answer.data, { + "_id": "one", + "_underscored_meta": "uvalue", + "meta": "data" + }, "Check document"); + + return jio.put({ + "_id": "one", + "_underscored_meta": "uvalue", + "meow": "dog" + }); + + }).then(function (answer) { + + deepEqual(answer, { + "id": "one", + "method": "put", + "result": "success", + "status": 204, + "statusText": "No Content" + }, "Put same document again"); + + return jio.get({"_id": "one"}); + + }).then(function (answer) { + + deepEqual(answer.data, { + "_id": "one", + "_underscored_meta": "uvalue", + "meow": "dog" + }, "Get document for check"); + + }).fail(unexpectedError).always(start); + + }); + + test("AllDocs", function () { + var shared = {}, jio; + + shared.workspace = {}; + shared.local_storage_description1 = { + "type": "local", + "username": "splitstorage", + "application_name": "alldocs1", + "mode": "memory" + }; + shared.local_storage_description2 = { + "type": "local", + "username": "splitstorage", + "application_name": "alldocs2", + "mode": "memory" + }; + jio = jIO.createJIO({ + "type": "split", + "storage_list": [ + shared.local_storage_description1, + shared.local_storage_description2 + ] + }, {"workspace": shared.workspace}); + + stop(); + + function prepareDatabase() { + var i, do_list = []; + function post(i) { + return function () { + return jio.post({ + "_id": "doc" + i, + "_underscored_meta": "uvalue" + i, + "meta": "data" + i + }); + }; + } + function putAttachment(i) { + return function () { + return jio.putAttachment({ + "_id": "doc" + i, + "_attachment": "my_attachment" + i, + "_data": "My Data" + i, + "_content_type": "text/plain" + }); + }; + } + for (i = 0; i < 5; i += 1) { + do_list.push(post(i)); + } + for (i = 0; i < 2; i += 1) { + do_list.push(putAttachment(i)); + } + return sequence(do_list); + } + + prepareDatabase().then(function () { + + return jio.get({"_id": "doc1"}); + + }).then(function (answer) { + + deepEqual(answer.data, { + "_id": "doc1", + "_underscored_meta": "uvalue1", + "meta": "data1", + "_attachments": { + "my_attachment1": { + "length": 8, + "content_type": "text/plain" + } + } + }, "Check document"); + + return jio.allDocs(); + + }).then(function (answer) { + + answer.data.rows.sort(function (a, b) { + return a.id < b.id ? -1 : a.id > b.id ? 1 : 0; + }); + + deepEqual(answer.data, { + "total_rows": 5, + "rows": [{ + "id": "doc0", + "key": "doc0", + "value": {} + }, { + "id": "doc1", + "key": "doc1", + "value": {} + }, { + "id": "doc2", + "key": "doc2", + "value": {} + }, { + "id": "doc3", + "key": "doc3", + "value": {} + }, { + "id": "doc4", + "key": "doc4", + "value": {} + }] + }, "AllDocs with document ids only"); + + }).fail(unexpectedError).always(start); + + }); + +})); diff --git a/src/jio/core/JobQueue.js b/src/jio/core/JobQueue.js index 451ae57956cba246fd19c755e2f450a1173de52a..6f7328c789ff8362d2b9d8f2f9711bf253ed0592 100644 --- a/src/jio/core/JobQueue.js +++ b/src/jio/core/JobQueue.js @@ -98,6 +98,8 @@ JobQueue.prototype.repair = function () { * @return {Number} The generated id */ JobQueue.prototype.post = function (job) { + console.log('jio core'); + console.log(job); var i, next = 1; // get next id for (i = 0; i < this._array.length; i += 1) { diff --git a/test/jio.storage/s3storage.livetests.html b/test/jio.storage/s3storage.livetests.html index 19469275e7ec8febaf7495e81a1197fc758ade55..07da17a944682d32e65b28a591dfc6921385c1a5 100644 --- a/test/jio.storage/s3storage.livetests.html +++ b/test/jio.storage/s3storage.livetests.html @@ -13,6 +13,7 @@ <script src="../../complex_queries.js"></script> <script src="../../src/sha1.amd.js"></script> <script src="../../src/jio.storage/s3storage.js"></script> + <script src="../../src/jio.storage/splitstorage.js"></script> <script src="../jio/util.js"></script> </head> <body> @@ -35,6 +36,7 @@ location.href.split('?')[1].split('&').forEach(function (item) { </form> <br /> <div id="qunit"></div> - <script src="s3storage.tests.js"></script> + <!--<script src="s3storage.tests.js"></script>--> + <script src="split.s3storage.tests.js"></script> </body> </html> diff --git a/test/jio.storage/s3storage.tests.js b/test/jio.storage/s3storage.tests.js index bafa99a956304b4b319eaabc716b9e28ea3bef30..18163cc50286efb2ad04da5f0da30d817068cc31 100644 --- a/test/jio.storage/s3storage.tests.js +++ b/test/jio.storage/s3storage.tests.js @@ -1,6 +1,6 @@ /*jslint indent: 2, maxlen: 80, nomen: true */ /*global module, test, stop, start, expect, ok, deepEqual, location, sinon, - davstorage_spec, RSVP, jIO, test_util, dav_storage, btoa */ + davstorage_spec, RSVP, jIO, test_util, dav_storage, btoa, s3storage_spec */ (function () { "use strict"; @@ -9,11 +9,6 @@ if (typeof s3storage_spec === 'object') { use_fake_server = false; spec = s3storage_spec; - } else { - spec = dav_storage.createDescription( - "http://localhost", - "none" - ); } module("S3 Storage"); @@ -198,35 +193,7 @@ responses.push([ 200, {"Content-Type": "text/xml"}, - '<?xml version="1.0" encoding="utf-8"?>' + - '<D:multistatus xmlns:D="DAV:">' + - '<D:response xmlns:lp2="http://apache.org/dav/props/" ' + - 'xmlns:lp1="DAV:">' + - '<D:href>/uploads/</D:href>' + - '<D:propstat>' + - '<D:prop>' + - '<lp1:resourcetype><D:collection/></lp1:resourcetype>' + - '<lp1:creationdate>2013-09-19T11:54:43Z</lp1:creationdate>' + - '<lp1:getlastmodified>Thu, 19 Sep 2013 11:54:43 GMT' + - '</lp1:getlastmodified>' + - '<lp1:getetag>"240be-1000-4e6bb3840a9ac"</lp1:getetag>' + - '<D:supportedlock>' + - '<D:lockentry>' + - '<D:lockscope><D:exclusive/></D:lockscope>' + - '<D:locktype><D:write/></D:locktype>' + - '</D:lockentry>' + - '<D:lockentry>' + - '<D:lockscope><D:shared/></D:lockscope>' + - '<D:locktype><D:write/></D:locktype>' + - '</D:lockentry>' + - '</D:supportedlock>' + - '<D:lockdiscovery/>' + - '<D:getcontenttype>httpd/unix-directory</D:getcontenttype>' + - '</D:prop>' + - '<D:status>HTTP/1.1 200 OK</D:status>' + - '</D:propstat>' + - '</D:response>' + - '</D:multistatus>' + '' ]); // PROPFIND return jio.allDocs(); } @@ -899,4 +866,7 @@ }); + module("SplitStorage + S3 Storage"); + + }()); diff --git a/test/jio.storage/split.s3storage.tests.js b/test/jio.storage/split.s3storage.tests.js new file mode 100644 index 0000000000000000000000000000000000000000..47f5995d43761cc30e1453923fa9192239ed0a63 --- /dev/null +++ b/test/jio.storage/split.s3storage.tests.js @@ -0,0 +1,967 @@ +/*jslint indent: 2, maxlen: 80, nomen: true */ +/*global define, jIO, test_util, RSVP, test, ok, deepEqual, module, stop, + start, hex_sha256 */ + +// define([module_name], [dependencies], module); +(function (dependencies, module) { + "use strict"; + if (typeof define === 'function' && define.amd) { + return define(dependencies, module); + } + module(jIO, test_util, RSVP); +}([ + 'jio', + 'test_util', + 'rsvp', + 's3storage', + 'splitstorage' +], function (jIO, util, RSVP) { + "use strict"; + + var tool = { + "readBlobAsBinaryString": jIO.util.readBlobAsBinaryString + }; + + function reverse(promise) { + return new RSVP.Promise(function (resolve, reject, notify) { + promise.then(reject, resolve, notify); + }, function () { + promise.cancel(); + }); + } + + /** + * sequence(thens): Promise + * + * Executes a sequence of *then* callbacks. It acts like + * `smth().then(callback).then(callback)...`. The first callback is called + * with no parameter. + * + * Elements of `thens` array can be a function or an array contaning at most + * three *then* callbacks: *onFulfilled*, *onRejected*, *onNotified*. + * + * When `cancel()` is executed, each then promises are cancelled at the same + * time. + * + * @param {Array} thens An array of *then* callbacks + * @return {Promise} A new promise + */ + function sequence(thens) { + var promises = []; + return new RSVP.Promise(function (resolve, reject, notify) { + var i; + promises[0] = new RSVP.Promise(function (resolve) { + resolve(); + }); + for (i = 0; i < thens.length; i += 1) { + if (Array.isArray(thens[i])) { + promises[i + 1] = promises[i]. + then(thens[i][0], thens[i][1], thens[i][2]); + } else { + promises[i + 1] = promises[i].then(thens[i]); + } + } + promises[i].then(resolve, reject, notify); + }, function () { + var i; + for (i = 0; i < promises.length; i += 1) { + promises[i].cancel(); + } + }); + } + + function unexpectedError(error) { + if (error instanceof Error) { + deepEqual([ + error.name + ": " + error.message, + error + ], "UNEXPECTED ERROR", "Unexpected error"); + } else { + deepEqual(error, "UNEXPECTED ERROR", "Unexpected error"); + } + } + + module("SplitStorage + S3 Storage"); + + test("Post", function () { + var shared = {}, jio, jio_s3_list = []; + + shared.workspace = {}; + shared.s3_storage_description1 = { + "type": "s3", + "server": "jiobucketsplit", + "AWSIdentifier": "AKIAJLNYGVLTV66RHPEQ", + "password": "/YHoa5r2X6EUHfvP31jdYx6t75h81pAjIZ4Mt94y" + }; + shared.s3_storage_description2 = { + "type": "s3", + "server": "jiobucketsplit_bis", + "AWSIdentifier": "AKIAJLNYGVLTV66RHPEQ", + "password": "/YHoa5r2X6EUHfvP31jdYx6t75h81pAjIZ4Mt94y" + }; + jio = jIO.createJIO({ + "type": "split", + "storage_list": [ + shared.s3_storage_description1, + shared.s3_storage_description2 + ] + }, {"workspace": shared.workspace}); + jio_s3_list[0] = jIO.createJIO(shared.s3_storage_description1, { + "workspace": shared.workspace + }); + jio_s3_list[1] = jIO.createJIO(shared.s3_storage_description2, { + "workspace": shared.workspace + }); + jio_s3_list.run = function (method, argument) { + var i, promises = []; + for (i = 0; i < this.length; i += 1) { + promises[i] = this[i][method].apply(this[i], argument); + } + return RSVP.all(promises); + }; + jio_s3_list.get = function () { + return this.run("get", arguments); + }; + + stop(); + // post without id + jio.post({ + "_underscored_meta": "uvalue", + "meta": "data" + }) + .then(function (answer) { + shared.uuid = answer.id; + answer.id = "<uuid>"; + ok(util.isUuid(shared.uuid), "Uuid should look like " + + "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx : " + shared.uuid); + deepEqual(answer, { + "id": "<uuid>", + "method": "post", + "result": "success", + "status": 201, + "statusText": "Created" + }, "Post document without id"); + // check uploaded documents + return jio_s3_list.get({"_id": shared.uuid}); + }) + + .then(function (answers) { + var i; + for (i = 0; i < answers.length; i += 1) { + deepEqual(answers[i].data, { + "_id": shared.uuid, + "_underscored_meta": "uvalue", + "data": i === 0 ? "{\"meta\"" : ":\"data\"}" + }, "Check uploaded document in sub storage " + (i + 1)); + } + // post with id + return jio.post({ + "_id": "one", + "_underscored_meta": "uvalue", + "meta": "data", + "hello": "world" + }); + }) + .then(function (answer) { + deepEqual(answer, { + "id": "one", + "method": "post", + "result": "success", + "status": 201, + "statusText": "Created" + }, "Post document with id"); + // check uploaded documents + return jio_s3_list.get({"_id": "one"}); + }) + + .then(function (answers) { + deepEqual(answers[0].data, { + "_id": "one", + "_underscored_meta": "uvalue", + "data": "{\"meta\":\"data\"," + }, "Check uploaded document in sub storage 1"); + deepEqual(answers[1].data, { + "_id": "one", + "_underscored_meta": "uvalue", + "data": "\"hello\":\"world\"}" + }, "Check uploaded document in sub storage 2"); + // post with id + return reverse(jio.post({ + "_id": "one", + "_underscored_meta": "uvalue", + "meta": "data", + "hello": "world" + })); + }) + .then(function (answer) { + deepEqual(answer, { + "error": "conflict", + "id": "one", + "message": "Unable to post document", + "method": "post", + "reason": "Document already exists", + "result": "error", + "status": 409, + "statusText": "Conflict" + }, "Post document with same id -> 409 Conflict"); + }) + .fail(unexpectedError).always(start); + }); + + test("PutAttachment", function () { + var shared = {}, jio, jio_s3_list = []; + + shared.workspace = {}; + shared.s3_storage_description1 = { + "type": "s3", + "server": "jiobucketsplit", + "AWSIdentifier": "AKIAJLNYGVLTV66RHPEQ", + "password": "/YHoa5r2X6EUHfvP31jdYx6t75h81pAjIZ4Mt94y", + "application_name": "putAttachment1", + "mode": "memory" + }; + shared.s3_storage_description2 = { + "type": "s3", + "server": "jiobucketsplit_bis", + "AWSIdentifier": "AKIAJLNYGVLTV66RHPEQ", + "password": "/YHoa5r2X6EUHfvP31jdYx6t75h81pAjIZ4Mt94y", + "application_name": "putAttachment2", + "mode": "memory" + }; + jio = jIO.createJIO({ + "type": "split", + "storage_list": [ + shared.s3_storage_description1, + shared.s3_storage_description2 + ] + }, {"workspace": shared.workspace}); + jio_s3_list[0] = jIO.createJIO(shared.s3_storage_description1, { + "workspace": shared.workspace + }); + jio_s3_list[1] = jIO.createJIO(shared.s3_storage_description2, { + "workspace": shared.workspace + }); + jio_s3_list.run = function (method, argument) { + var i, promises = []; + for (i = 0; i < this.length; i += 1) { + promises[i] = this[i][method].apply(this[i], argument); + } + return RSVP.all(promises); + }; + jio_s3_list.get = function () { + return this.run("get", arguments); + }; + jio_s3_list.getAttachmentAsBinaryString = function () { + return this.run("getAttachment", arguments).then(function (answers) { + var i, promises = []; + for (i = 0; i < answers.length; i += 1) { + promises[i] = tool.readBlobAsBinaryString(answers[i].data); + } + return RSVP.all(promises); + }); + }; + + stop(); + + return reverse(jio.putAttachment({ + "_id": "two", + "_attachment": "my_attachment", + "_data": "My Data", + "_content_type": "text/plain" + })).then(function (answer) { + + deepEqual(answer, { + "attachment": "my_attachment", + "error": "not_found", + "id": "two", + "message": "Unable to put attachment", + "method": "putAttachment", + "reason": "Document does not exist", + "result": "error", + "status": 404, + "statusText": "Not Found" + }, "Put attachment on a inexistent document -> 404 Not Found"); + + return jio.post({ + "_id": "two", + "_underscored_meta": "uvalue", + "meta": "data" + }); + + }).then(function () { + + return jio.putAttachment({ + "_id": "two", + "_attachment": "my_attachment", + "_data": "My Data", + "_mimetype": "text/plain" + }); + + }).then(function (answer) { + + deepEqual(answer, { + "attachment": "my_attachment", + "id": "two", + "method": "putAttachment", + "result": "success", + "status": 204, + "statusText": "No Content" + }, "Put attachment on a document"); + + // check uploaded documents + return jio_s3_list.get({"_id": "two"}); + + }).then(function (answers) { + + deepEqual(answers[0].data, { + "_attachments": { + "my_attachment": { + "content_type": "text/plain", + "digest": "sha256-ebf2d770a6a2dfa135f6c81431f22fc3cbcde9ae" + + "e52759ca9e520d4d964c1322", // sha256("My ") + "length": 3 + } + }, + "_id": "two", + "_underscored_meta": "uvalue", + "data": "{\"meta\"" + }, "Check uploaded document in sub storage 1"); + + deepEqual(answers[1].data, { + "_attachments": { + "my_attachment": { + "content_type": "text/plain", + "digest": "sha256-cec3a9b89b2e391393d0f68e4bc12a9fa6cf358b" + + "3cdf79496dc442d52b8dd528", // sha256("Data") + "length": 4 + } + }, + "_id": "two", + "_underscored_meta": "uvalue", + "data": ":\"data\"}" + }, "Check uploaded document in sub storage 2"); + + return jio_s3_list.getAttachmentAsBinaryString({ + "_id": "two", + "_attachment": "my_attachment" + }); + + }).then(function (events) { + + deepEqual(events[0].target.result, "My ", + "Check uploaded document in sub storage 1"); + deepEqual(events[1].target.result, "Data", + "Check uploaded document in sub storage 1"); + + }).fail(unexpectedError).always(start); + + }); + + test("Get", function () { + var shared = {}, jio; + + shared.workspace = {}; + shared.s3_storage_description1 = { + "type": "s3", + "server": "jiobucketsplit", + "AWSIdentifier": "AKIAJLNYGVLTV66RHPEQ", + "password": "/YHoa5r2X6EUHfvP31jdYx6t75h81pAjIZ4Mt94y", + "application_name": "get1", + "mode": "memory" + }; + shared.s3_storage_description2 = { + "type": "s3", + "server": "jiobucketsplit_bis", + "AWSIdentifier": "AKIAJLNYGVLTV66RHPEQ", + "password": "/YHoa5r2X6EUHfvP31jdYx6t75h81pAjIZ4Mt94y", + "application_name": "get2", + "mode": "memory" + }; + jio = jIO.createJIO({ + "type": "split", + "storage_list": [ + shared.s3_storage_description1, + shared.s3_storage_description2 + ] + }, {"workspace": shared.workspace}); + + stop(); + + reverse(jio.get({"_id": "three"})).then(function (answer) { + + deepEqual(answer, { + "error": "not_found", + "id": "three", + "message": "Unable to get document", + "method": "get", + "reason": "Not Found", + "result": "error", + "status": 404, + "statusText": "Not Found" + }, "Get missing document"); + + return jio.post({ + "_id": "three", + "_underscored_meta": "uvalue", + "meta": "data" + }); + + }).then(function () { + + return jio.get({"_id": "three"}); + + }).then(function (answer) { + + deepEqual(answer.data, { + "_id": "three", + "_underscored_meta": "uvalue", + "meta": "data" + }, "Get posted document"); + + return jio.putAttachment({ + "_id": "three", + "_attachment": "my_attachment", + "_data": "My Data", + "_content_type": "text/plain" + }); + + }).then(function () { + + return jio.get({"_id": "three"}); + + }).then(function (answer) { + + deepEqual(answer.data, { + "_id": "three", + "_underscored_meta": "uvalue", + "meta": "data", + "_attachments": { + "my_attachment": { + "length": 7, + "content_type": "text/plain" + } + } + }, "Get document with attachment informations"); + + }).fail(unexpectedError).always(start); + + }); + + + test("GetAttachment", function () { + var shared = {}, jio; + + shared.workspace = {}; + shared.s3_storage_description1 = { + "type": "s3", + "server": "jiobucketsplit", + "AWSIdentifier": "AKIAJLNYGVLTV66RHPEQ", + "password": "/YHoa5r2X6EUHfvP31jdYx6t75h81pAjIZ4Mt94y", + "application_name": "getAttachment", + "mode": "memory" + }; + shared.s3_storage_description2 = { + "type": "s3", + "server": "jiobucketsplit_bis", + "AWSIdentifier": "AKIAJLNYGVLTV66RHPEQ", + "password": "/YHoa5r2X6EUHfvP31jdYx6t75h81pAjIZ4Mt94y", + "application_name": "getAttachment2", + "mode": "memory" + }; + jio = jIO.createJIO({ + "type": "split", + "storage_list": [ + shared.s3_storage_description1, + shared.s3_storage_description2 + ] + }, {"workspace": shared.workspace}); + + stop(); + + reverse(jio.getAttachment({ + "_id": "four", + "_attachment": "my_attachment" + })) + + .then(function (answer) { + deepEqual(answer, { + "attachment": "my_attachment", + "error": "not_found", + "id": "four", + "message": "Unable to get attachment", + "method": "getAttachment", + "reason": "Not Found", + "result": "error", + "status": 404, + "statusText": "Not Found" + }, "Get attachment from missing document -> 404 Not Found"); + return jio.post({ + "_id": "four", + "_underscored_meta": "uvalue", + "meta": "data" + }); + }) + .then(function () { + return reverse(jio.getAttachment({ + "_id": "four", + "_attachment": "my_attachment" + })); + }).then(function (answer) { + deepEqual(answer, { + "attachment": "my_attachment", + "error": "not_found", + "id": "four", + "message": "Unable to get attachment", + "method": "getAttachment", + "reason": "Not Found", + "result": "error", + "status": 404, + "statusText": "Not Found" + }, "Get missing attachment from document"); + return jio.putAttachment({ + "_id": "four", + "_attachment": "my_attachment", + "_data": "My Data", + "_mimetype": "text/plain" + }); + }) + .then(function () { + return jio.getAttachment({ + "_id": "four", + "_attachment": "my_attachment" + }); + }).then(function (answer) { + return tool.readBlobAsBinaryString(answer.data); + }).then(function (event) { + deepEqual(event.target.result, "My Data", "Get attachment"); + }) + .fail(unexpectedError).always(start); + }); + + + test("RemoveAttachment", function () { + var shared = {}, jio; + + shared.workspace = {}; + shared.s3_storage_description1 = { + "type": "s3", + "server": "jiobucketsplit", + "AWSIdentifier": "AKIAJLNYGVLTV66RHPEQ", + "password": "/YHoa5r2X6EUHfvP31jdYx6t75h81pAjIZ4Mt94y", + "mode": "memory" + }; + shared.s3_storage_description2 = { + "type": "s3", + "server": "jiobucketsplit_bis", + "AWSIdentifier": "AKIAJLNYGVLTV66RHPEQ", + "password": "/YHoa5r2X6EUHfvP31jdYx6t75h81pAjIZ4Mt94y", + "mode": "memory" + }; + jio = jIO.createJIO({ + "type": "split", + "storage_list": [ + shared.s3_storage_description1, + shared.s3_storage_description2 + ] + }, {"workspace": shared.workspace}); + + stop(); + + reverse(jio.removeAttachment({ + "_id": "five", + "_attachment": "my_attachment" + })).then(function (answer) { + + deepEqual(answer, { + "attachment": "my_attachment", + "error": "not_found", + "id": "five", + "message": "Unable to remove attachment", + "method": "removeAttachment", + "reason": "missing document", + "result": "error", + "status": 404, + "statusText": "Not Found" + }, "Remove attachment from inexistent document -> 404 Not Found"); + + return jio.post({ + "_id": "five", + "_underscored_meta": "uvalue", + "meta": "data" + }); + + }).then(function () { + + return reverse(jio.removeAttachment({ + "_id": "five", + "_attachment": "my_attachment" + })); + + }).then(function (answer) { + + deepEqual(answer, { + "attachment": "my_attachment", + "error": "not_found", + "id": "five", + "message": "Unable to remove attachment", + "method": "removeAttachment", + "reason": "missing attachment", + "result": "error", + "status": 404, + "statusText": "Not Found" + }, "Remove inexistent attachment -> 404 Not Found"); + + return jio.putAttachment({ + "_id": "five", + "_attachment": "my_attachment", + "_data": "My Data", + "_mimetype": "text/plain" + }); + + }) + .then(function () { + return jio.removeAttachment({ + "_id": "five", + "_attachment": "my_attachment" + }); + }) + .then(function (answer) { + deepEqual(answer, { + "attachment": "my_attachment", + "id": "five", + "method": "removeAttachment", + "result": "success", + "status": 204, + "statusText": "No Content" + }, "Remove attachment"); + return jio.get({"_id": "five"}); + }).then(function (answer) { + deepEqual(answer.data, { + "_id": "five", + "_underscored_meta": "uvalue", + "meta": "data" + }, "Check document"); + return reverse(jio.getAttachment({ + "_id": "five", + "_attachment": "my_attachment" + })); + }).then(function (answer) { + deepEqual(answer, { + "attachment": "my_attachment", + "error": "not_found", + "id": "five", + "message": "Unable to get attachment", + "method": "getAttachment", + "reason": "Not Found", + "result": "error", + "status": 404, + "statusText": "Not Found" + }, "Check attachment -> 404 Not Found"); + }) + .fail(unexpectedError).always(start); + + }); + + test("Remove", function () { + var shared = {}, jio; + + shared.workspace = {}; + shared.s3_storage_description1 = { + "type": "s3", + "server": "jiobucketsplit", + "AWSIdentifier": "AKIAJLNYGVLTV66RHPEQ", + "password": "/YHoa5r2X6EUHfvP31jdYx6t75h81pAjIZ4Mt94y" + }; + shared.s3_storage_description2 = { + "type": "s3", + "server": "jiobucketsplit_bis", + "AWSIdentifier": "AKIAJLNYGVLTV66RHPEQ", + "password": "/YHoa5r2X6EUHfvP31jdYx6t75h81pAjIZ4Mt94y" + }; + jio = jIO.createJIO({ + "type": "split", + "storage_list": [ + shared.s3_storage_description1, + shared.s3_storage_description2 + ] + }, {"workspace": shared.workspace}); + + stop(); + + reverse(jio.remove({"_id": "six"})).then(function (answer) { + + deepEqual(answer, { + "error": "not_found", + "id": "six", + "message": "Unable to remove document", + "method": "remove", + "reason": "Not Found", + "result": "error", + "status": 404, + "statusText": "Not Found" + }, "Remove missing document -> 404 Not Found"); + + return jio.post({ + "_id": "six", + "_underscored_meta": "uvalue", + "meta": "data" + }); + + }).then(function () { + + return jio.putAttachment({ + "_id": "six", + "_attachment": "my_attachment", + "_data": "My Data", + "_mimetype": "text/plain" + }); + + }).then(function () { + + return jio.remove({"_id": "six"}); + + }).then(function (answer) { + + deepEqual(answer, { + "id": "six", + "method": "remove", + "result": "success", + "status": 204, + "statusText": "No Content" + }, "Remove document"); + + return reverse(jio.getAttachment({ + "_id": "six", + "_attachment": "my_attachment" + })); + + }).then(function (answer) { + + deepEqual(answer, { + "attachment": "my_attachment", + "error": "not_found", + "id": "six", + "message": "Unable to get attachment", + "method": "getAttachment", + "reason": "Not Found", + "result": "error", + "status": 404, + "statusText": "Not Found" + }, "Check attachment -> 404 Not Found"); + + return reverse(jio.get({"_id": "six"})); + + }).then(function (answer) { + + deepEqual(answer, { + "error": "not_found", + "id": "six", + "message": "Unable to get document", + "method": "get", + "reason": "Not Found", + "result": "error", + "status": 404, + "statusText": "Not Found" + }, "Check document -> 404 Not Found"); + + }).fail(unexpectedError).always(start); + + }); + + test("Put", function () { + var shared = {}, jio; + + shared.workspace = {}; + shared.s3_storage_description1 = { + "type": "s3", + "server": "jiobucketsplit", + "AWSIdentifier": "AKIAJLNYGVLTV66RHPEQ", + "password": "/YHoa5r2X6EUHfvP31jdYx6t75h81pAjIZ4Mt94y" + }; + shared.s3_storage_description2 = { + "type": "s3", + "server": "jiobucketsplit_bis", + "AWSIdentifier": "AKIAJLNYGVLTV66RHPEQ", + "password": "/YHoa5r2X6EUHfvP31jdYx6t75h81pAjIZ4Mt94y" + }; + jio = jIO.createJIO({ + "type": "split", + "storage_list": [ + shared.s3_storage_description1, + shared.s3_storage_description2 + ] + }, {"workspace": shared.workspace}); + + stop(); + + jio.put({ + "_id": "seven", + "_underscored_meta": "uvalue", + "meta": "data" + }).then(function (answer) { + + deepEqual(answer, { + "id": "seven", + "method": "put", + "result": "success", + "status": 204, + "statusText": "No Content" + }, "Put new document"); + + return jio.get({"_id": "seven"}); + + }).then(function (answer) { + + deepEqual(answer.data, { + "_id": "seven", + "_underscored_meta": "uvalue", + "meta": "data" + }, "Check document"); + + return jio.put({ + "_id": "seven", + "_underscored_meta": "uvalue", + "meow": "dog" + }); + + }).then(function (answer) { + + deepEqual(answer, { + "id": "seven", + "method": "put", + "result": "success", + "status": 204, + "statusText": "No Content" + }, "Put same document again"); + + return jio.get({"_id": "seven"}); + + }).then(function (answer) { + + deepEqual(answer.data, { + "_id": "seven", + "_underscored_meta": "uvalue", + "meow": "dog" + }, "Get document for check"); + + }).fail(unexpectedError).always(start); + + }); + + test("AllDocs", function () { + var shared = {}, jio; + + shared.workspace = {}; + shared.s3_storage_description1 = { + "type": "s3", + "server": "jiobucket_alldocs", + "AWSIdentifier": "AKIAJLNYGVLTV66RHPEQ", + "password": "/YHoa5r2X6EUHfvP31jdYx6t75h81pAjIZ4Mt94y" + }; + shared.s3_storage_description2 = { + "type": "s3", + "server": "jiobucket_alldocs_bis", + "AWSIdentifier": "AKIAJLNYGVLTV66RHPEQ", + "password": "/YHoa5r2X6EUHfvP31jdYx6t75h81pAjIZ4Mt94y" + }; + jio = jIO.createJIO({ + "type": "split", + "storage_list": [ + shared.s3_storage_description1, + shared.s3_storage_description2 + ] + }, {"workspace": shared.workspace}); + + stop(); + + function prepareDatabase() { + var i, do_list = []; + function post(i) { + return function () { + return jio.post({ + "_id": "doc" + i, + "_underscored_meta": "uvalue" + i, + "meta": "data" + i + }); + }; + } + function putAttachment(i) { + return function () { + return jio.putAttachment({ + "_id": "doc" + i, + "_attachment": "my_attachment" + i, + "_data": "My Data" + i, + "_content_type": "text/plain" + }); + }; + } + for (i = 0; i < 5; i += 1) { + do_list.push(post(i)); + } + for (i = 0; i < 2; i += 1) { + do_list.push(putAttachment(i)); + } + return sequence(do_list); + } + + prepareDatabase().then(function () { + + return jio.get({"_id": "doc1"}); + + }).then(function (answer) { + + deepEqual(answer.data, { + "_id": "doc1", + "_underscored_meta": "uvalue1", + "meta": "data1", + "_attachments": { + "my_attachment1": { + "length": 8, + "content_type": "text/plain" + } + } + }, "Check document"); + + return jio.allDocs(); + + }).then(function (answer) { + + answer.data.rows.sort(function (a, b) { + return a.id < b.id ? -1 : a.id > b.id ? 1 : 0; + }); + + deepEqual(answer.data, { + "total_rows": 5, + "rows": [ + { + "id": "doc0", + "key": "doc0", + "value": {} + }, + { + "id": "doc1", + "key": "doc1", + "value": {} + }, + { + "id": "doc2", + "key": "doc2", + "value": {} + }, + { + "id": "doc3", + "key": "doc3", + "value": {} + }, + { + "id": "doc4", + "key": "doc4", + "value": {} + }] + }, "AllDocs with document ids only"); + + }).fail(unexpectedError).always(start); + + }); + +}));