Commit 884c0304 authored by Romain Courteaud's avatar Romain Courteaud

Add draft version of a http://qiniu.com/ storage.

It only support for now adding, modifying and getting file.
indexstorage is needed to get the list of documents.
parent 17b42149
/*
CryptoJS v3.1.2
code.google.com/p/crypto-js
(c) 2009-2013 by Jeff Mott. All rights reserved.
code.google.com/p/crypto-js/wiki/License
*/
var CryptoJS=CryptoJS||function(g,l){var e={},d=e.lib={},m=function(){},k=d.Base={extend:function(a){m.prototype=this;var c=new m;a&&c.mixIn(a);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var c in a)a.hasOwnProperty(c)&&(this[c]=a[c]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}},
p=d.WordArray=k.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=l?c:4*a.length},toString:function(a){return(a||n).stringify(this)},concat:function(a){var c=this.words,q=a.words,f=this.sigBytes;a=a.sigBytes;this.clamp();if(f%4)for(var b=0;b<a;b++)c[f+b>>>2]|=(q[b>>>2]>>>24-8*(b%4)&255)<<24-8*((f+b)%4);else if(65535<q.length)for(b=0;b<a;b+=4)c[f+b>>>2]=q[b>>>2];else c.push.apply(c,q);this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<<
32-8*(c%4);a.length=g.ceil(c/4)},clone:function(){var a=k.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],b=0;b<a;b+=4)c.push(4294967296*g.random()|0);return new p.init(c,a)}}),b=e.enc={},n=b.Hex={stringify:function(a){var c=a.words;a=a.sigBytes;for(var b=[],f=0;f<a;f++){var d=c[f>>>2]>>>24-8*(f%4)&255;b.push((d>>>4).toString(16));b.push((d&15).toString(16))}return b.join("")},parse:function(a){for(var c=a.length,b=[],f=0;f<c;f+=2)b[f>>>3]|=parseInt(a.substr(f,
2),16)<<24-4*(f%8);return new p.init(b,c/2)}},j=b.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var b=[],f=0;f<a;f++)b.push(String.fromCharCode(c[f>>>2]>>>24-8*(f%4)&255));return b.join("")},parse:function(a){for(var c=a.length,b=[],f=0;f<c;f++)b[f>>>2]|=(a.charCodeAt(f)&255)<<24-8*(f%4);return new p.init(b,c)}},h=b.Utf8={stringify:function(a){try{return decodeURIComponent(escape(j.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(a){return j.parse(unescape(encodeURIComponent(a)))}},
r=d.BufferedBlockAlgorithm=k.extend({reset:function(){this._data=new p.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=h.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,b=c.words,f=c.sigBytes,d=this.blockSize,e=f/(4*d),e=a?g.ceil(e):g.max((e|0)-this._minBufferSize,0);a=e*d;f=g.min(4*a,f);if(a){for(var k=0;k<a;k+=d)this._doProcessBlock(b,k);k=b.splice(0,a);c.sigBytes-=f}return new p.init(k,f)},clone:function(){var a=k.clone.call(this);
a._data=this._data.clone();return a},_minBufferSize:0});d.Hasher=r.extend({cfg:k.extend(),init:function(a){this.cfg=this.cfg.extend(a);this.reset()},reset:function(){r.reset.call(this);this._doReset()},update:function(a){this._append(a);this._process();return this},finalize:function(a){a&&this._append(a);return this._doFinalize()},blockSize:16,_createHelper:function(a){return function(b,d){return(new a.init(d)).finalize(b)}},_createHmacHelper:function(a){return function(b,d){return(new s.HMAC.init(a,
d)).finalize(b)}}});var s=e.algo={};return e}(Math);
(function(){var g=CryptoJS,l=g.lib,e=l.WordArray,d=l.Hasher,m=[],l=g.algo.SHA1=d.extend({_doReset:function(){this._hash=new e.init([1732584193,4023233417,2562383102,271733878,3285377520])},_doProcessBlock:function(d,e){for(var b=this._hash.words,n=b[0],j=b[1],h=b[2],g=b[3],l=b[4],a=0;80>a;a++){if(16>a)m[a]=d[e+a]|0;else{var c=m[a-3]^m[a-8]^m[a-14]^m[a-16];m[a]=c<<1|c>>>31}c=(n<<5|n>>>27)+l+m[a];c=20>a?c+((j&h|~j&g)+1518500249):40>a?c+((j^h^g)+1859775393):60>a?c+((j&h|j&g|h&g)-1894007588):c+((j^h^
g)-899497514);l=g;g=h;h=j<<30|j>>>2;j=n;n=c}b[0]=b[0]+n|0;b[1]=b[1]+j|0;b[2]=b[2]+h|0;b[3]=b[3]+g|0;b[4]=b[4]+l|0},_doFinalize:function(){var d=this._data,e=d.words,b=8*this._nDataBytes,g=8*d.sigBytes;e[g>>>5]|=128<<24-g%32;e[(g+64>>>9<<4)+14]=Math.floor(b/4294967296);e[(g+64>>>9<<4)+15]=b;d.sigBytes=4*e.length;this._process();return this._hash},clone:function(){var e=d.clone.call(this);e._hash=this._hash.clone();return e}});g.SHA1=d._createHelper(l);g.HmacSHA1=d._createHmacHelper(l)})();
(function(){var g=CryptoJS,l=g.enc.Utf8;g.algo.HMAC=g.lib.Base.extend({init:function(e,d){e=this._hasher=new e.init;"string"==typeof d&&(d=l.parse(d));var g=e.blockSize,k=4*g;d.sigBytes>k&&(d=e.finalize(d));d.clamp();for(var p=this._oKey=d.clone(),b=this._iKey=d.clone(),n=p.words,j=b.words,h=0;h<g;h++)n[h]^=1549556828,j[h]^=909522486;p.sigBytes=b.sigBytes=k;this.reset()},reset:function(){var e=this._hasher;e.reset();e.update(this._iKey)},update:function(e){this._hasher.update(e);return this},finalize:function(e){var d=
this._hasher;e=d.finalize(e);d.reset();return d.finalize(this._oKey.clone().concat(e))}})})();
/*
* Copyright 2013, Nexedi SA
* Released under the LGPL license.
* http://www.gnu.org/licenses/lgpl.html
*/
/**
* JIO Qiniu Storage. Type = "qiniu".
* Qiniu "database" storage.
*/
/*global FormData, btoa, Blob, CryptoJS */
/*jslint nomen: true, unparam: true, bitwise: true */
(function (dependencies, module) {
"use strict";
if (typeof define === 'function' && define.amd) {
return define(dependencies, module);
}
// if (typeof exports === 'object') {
// return module(exports, require('jio'), require('complex_queries'));
// }
module(jIO);
}([
'jio'
], function (jIO) {
"use strict";
var b64_hmac_sha1 = function (secret_key, message) {
// https://parse.com/questions/hmac-sha1-byte-order
// Not sure why we have to do this, but we need to swap
// the bytes inside each of the five
// words that make up the encoded signature from b0, b1,
// b2, b3 to b3, b2, b1, b0.
var encodedArray = [],
i,
encoded = CryptoJS.HmacSHA1(message, secret_key),
encodedString;
for (i = 0; i < 5; i = i + 1) {
encodedArray[(i * 4)] = ((encoded.words[i] & 0xff000000) >>> 24);
encodedArray[(i * 4) + 1] = ((encoded.words[i] & 0x00ff0000) >>> 16);
encodedArray[(i * 4) + 2] = ((encoded.words[i] & 0x0000ff00) >>> 8);
encodedArray[(i * 4) + 3] = ((encoded.words[i] & 0x000000ff) >>> 0);
}
// Make string from our array of bytes that we just ordered.
encodedString = String.fromCharCode.apply(null, encodedArray);
return btoa(encodedString)
.replace(/\+/g, '-') // Convert '+' to '-'
.replace(/\//g, '_'); // Convert '/' to '_'
// .replace(/=+$/, ''); // Remove ending '='
},
UPLOAD_URL = "http://up.qiniu.com/",
// DEADLINE = 1451491200;
DEADLINE = 2451491200;
/**
* The JIO QiniuStorage extension
*
* @class QiniuStorage
* @constructor
*/
function QiniuStorage(spec) {
if (typeof spec.bucket !== 'string' && !spec.bucket) {
throw new TypeError("Qiniu 'bucket' must be a string " +
"which contains more than one character.");
}
if (typeof spec.access_key !== 'string' && !spec.access_key) {
throw new TypeError("Qiniu 'access_key' must be a string " +
"which contains more than one character.");
}
if (typeof spec.secret_key !== 'string' && !spec.secret_key) {
throw new TypeError("Qiniu 'secret_key' must be a string " +
"which contains more than one character.");
}
this._bucket = spec.bucket;
this._access_key = spec.access_key;
this._secret_key = spec.secret_key;
}
QiniuStorage.prototype._put = function (key, blob, update) {
var data,
put_policy,
encoded,
encode_signed,
upload_token;
data = new FormData();
if (update === true) {
put_policy = JSON.stringify({
"scope": this._bucket + ':' + key,
"deadline": DEADLINE
});
} else {
put_policy = JSON.stringify({
"scope": this._bucket,
"deadline": DEADLINE
});
}
encoded = btoa(put_policy);
encode_signed = b64_hmac_sha1(this._secret_key, encoded);
upload_token = this._access_key + ":" + encode_signed + ":" + encoded;
data.append("key", key);
data.append("token", upload_token);
data.append(
"file",
// new Blob([JSON.stringify(doc)], {type: "application/json"}),
// new Blob([doc], {type: "application/json"}),
blob,
// new Blob([], {type: "application/octet-stream"}),
key
);
return jIO.util.ajax({
"type": "POST",
"url": UPLOAD_URL,
"data": data
// }).then(function (doc) {
// if (doc !== null) {
// command.success({"data": doc});
// } else {
// command.error(
// "not_found",
// "missing",
// "Cannot find document"
// );
// }
});
};
/**
* Create a document.
*
* @method post
* @param {Object} command The JIO command
* @param {Object} metadata The metadata to store
*/
QiniuStorage.prototype.post = function (command, metadata) {
var doc = jIO.util.deepClone(metadata),
doc_id = metadata._id;
if (!doc_id) {
doc_id = jIO.util.generateUuid();
doc._id = doc_id;
}
return this._put(
doc_id,
new Blob([JSON.stringify(doc)], {type: "application/json"})
).then(function (doc) {
if (doc !== null) {
command.success({"id": doc_id});
} else {
command.error(
"not_found",
"missing",
"Cannot find document"
);
}
}, function (event) {
command.error(
event.target.status,
event.target.statusText,
"Unable to post doc"
);
});
};
/**
* Update/create a document.
*
* @method put
* @param {Object} command The JIO command
* @param {Object} metadata The metadata to store
*/
QiniuStorage.prototype.put = function (command, metadata) {
return this._put(
metadata._id,
new Blob([JSON.stringify(metadata)], {type: "application/json"}),
true
).then(function (doc) {
if (doc !== null) {
command.success({"data": doc});
} else {
command.error(
"not_found",
"missing",
"Cannot find document"
);
}
}, function (event) {
command.error(
event.target.status,
event.target.statusText,
"Unable to put doc"
);
});
};
QiniuStorage.prototype._get = function (key) {
var download_url = 'http://' + this._bucket + '.u.qiniudn.com/' + key
+ '?e=' + DEADLINE,
token = b64_hmac_sha1(this._secret_key, download_url);
return jIO.util.ajax({
"type": "GET",
"url": download_url + "&token=" + this._access_key + ':' + token
// "dataType": "blob"
});
};
/**
* Get a document or attachment
* @method get
* @param {object} command The JIO command
**/
QiniuStorage.prototype.get = function (command, param) {
return this._get(param._id)
.then(function (doc) {
if (doc.target.responseText !== undefined) {
command.success({"data": JSON.parse(doc.target.responseText)});
} else {
command.error(
"not_found",
"missing",
"Cannot find document"
);
}
}, function (event) {
command.error(
event.target.status,
event.target.statusText,
"Unable to get doc"
);
});
};
/**
* Get an attachment
*
* @method getAttachment
* @param {Object} command The JIO command
* @param {Object} param The given parameters
* @param {Object} options The command options
*/
QiniuStorage.prototype.getAttachment = function (command, param) {
return this._get(param._id + "/" + param._attachment)
.then(function (doc) {
if (doc.target.response) {
command.success({"data": doc.target.response});
} else {
// XXX Handle error
command.error(
"not_found",
"missing",
"Cannot find document"
);
}
}, function (event) {
command.error(
event.target.status,
event.target.statusText,
"Unable to get attachment"
);
});
};
/**
* 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
*/
QiniuStorage.prototype.putAttachment = function (command, param) {
return this._put(
param._id + "/" + param._attachment,
param._blob,
true
).then(function (doc) {
if (doc !== null) {
command.success({"data": doc});
} else {
command.error(
"not_found",
"missing",
"Cannot find document"
);
}
}, function (event) {
command.error(
event.target.status,
event.target.statusText,
"Unable to put attachment"
);
});
};
jIO.addStorage('qiniu', QiniuStorage);
}));
/*global define, module, test_util, RSVP, jIO, test, ok,
deepEqual, sinon, expect, stop, start, Blob, equal */
(function (dependencies, module) {
"use strict";
if (typeof define === 'function' && define.amd) {
return define(dependencies, module);
}
module(test_util, RSVP, jIO);
}([
'test_util',
'rsvp',
'jio',
'hmacsha1',
'qiniustorage',
'qunit'
// ], function (util, RSVP, jIO) {
], function () {
"use strict";
var qiniu_spec = {
"type": "qiniu",
"bucket": "uth6nied",
"access_key": "Imh9CFmpVZ5L1TE04Pjt-UmR_Ccr2cW9-KjSmvSA",
"secret_key": "vFkNUlI2U4B7G1sz8UL_Z25kYHozfz82z4vMWPgo"
};
module("QiniuStorage", {
setup: function () {
this.server = sinon.fakeServer.create();
this.server.autoRespond = true;
this.server.autoRespondAfter = 5;
this.jio_storage = jIO.createJIO(qiniu_spec);
},
teardown: function () {
this.server.restore();
delete this.server;
delete this.jio_storage;
}
});
test('get', function () {
var key = "foobar12345",
server = this.server,
download_url = 'http://uth6nied.u.qiniudn.com/foobar12345?' +
'e=2451491200&token=Imh9CFmpVZ5L1TE04Pjt-UmR_Ccr2cW9-KjSmvSA:' +
'hISFzrC4dQvdOR8A_MozNsB5cME=',
data = {
"_id": key,
"foo": "bar"
};
server.respondWith("GET", download_url, [200, {
"Content-Type": "application/json"
}, JSON.stringify(data)]);
stop();
this.jio_storage.get({"_id": key})
.then(function (result) {
deepEqual(result, {
"data": data,
"id": key,
"method": "get",
"result": "success",
"status": 200,
"statusText": "Ok"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test('getAttachment', function () {
var key = "foobar12345",
attachment = "barfoo54321",
server = this.server,
download_url = 'http://uth6nied.u.qiniudn.com/foobar12345/barfoo54321' +
'?e=2451491200&token=Imh9CFmpVZ5L1TE04Pjt-UmR_Ccr2cW9-KjSmvSA:' +
'L88mHkZkfjr11DqPUqb5gsDjHFY=',
data = {
"_id": key,
"foo": "bar",
"nut": "nut"
};
server.respondWith("GET", download_url, [200, {
"Content-Type": "application/octet-stream"
}, JSON.stringify(data)]);
stop();
this.jio_storage.getAttachment({"_id": key, "_attachment": attachment})
.then(function (result) {
return jIO.util.readBlobAsText(result.data).then(function (e) {
return {
"result": result,
"text": e.target.result
};
});
}).then(function (result) {
result.result.data = result.text;
deepEqual(result.result, {
"data": JSON.stringify(data),
"id": key,
"attachment": attachment,
"method": "getAttachment",
"result": "success",
"status": 200,
"statusText": "Ok"
});
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test('post', function () {
var key = "foobar12345",
server = this.server,
upload_url = 'http://up.qiniu.com/',
data = {"ok": "excellent"};
server.respondWith("POST", upload_url, [200, {
"Content-Type": "application/json"
}, JSON.stringify(data)]);
stop();
this.jio_storage.post({"_id": key})
.then(function (result) {
deepEqual(result, {
"id": key,
"method": "post",
"result": "success",
"status": 201,
"statusText": "Created"
});
return jIO.util.readBlobAsBinaryString(server.requests[0].requestBody);
})
.then(function (e) {
equal(e.target.result, "XXX test FormData verification");
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test('put', function () {
var key = "foobar12345",
server = this.server,
upload_url = 'http://up.qiniu.com/',
data = {"ok": "excellent"};
server.respondWith("POST", upload_url, [200, {
"Content-Type": "application/json"
}, JSON.stringify(data)]);
stop();
this.jio_storage.put({"_id": key})
.then(function () {
throw new Error("Not implemented");
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test('putAttachment', function () {
var key = "foobar12345",
attachment = "barfoo54321",
server = this.server,
upload_url = 'http://up.qiniu.com/',
data = {"ok": "excellent"};
server.respondWith("POST", upload_url, [200, {
"Content-Type": "application/json"
}, JSON.stringify(data)]);
stop();
this.jio_storage.putAttachment({
"_id": key,
"_attachment": attachment,
"_blob": "bar"
})
.then(function () {
throw new Error("Not implemented");
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
}));
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