Skip to content

Commit

Permalink
Merge pull request #6 from yakovkhalinsky/x-bz-info
Browse files Browse the repository at this point in the history
Add support for X-Bz-Info-* headers to uploadFile
  • Loading branch information
yakovkhalinsky committed Jan 2, 2016
2 parents 6193a8d + e27d468 commit 836097c
Show file tree
Hide file tree
Showing 9 changed files with 183 additions and 7 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
### v0.9.6 (December 18, 2015) - The information release

Features
- Add support for adding X-Bz-Info-* headers when uploading a file [link](https://github.com/yakovkhalinsky/backblaze-b2/pull/6)


### v0.9.5 (December 18, 2015) - The string encoding release

Features
Expand Down
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,13 @@ Always run `npm test` before you commit.
uploadUrl: 'uploadUrl',
uploadAuthToken: 'uploadAuthToken',
filename: 'filename',
data: 'data' // this is expecting a Buffer not an encoded string
data: 'data' // this is expecting a Buffer not an encoded string,
info: {
// optional info headers, prepended with X-Bz-Info- when sent, throws error if more than 10 keys set
// valid characters should be a-z, A-Z and '-', all other characters will cause an error to be thrown
key1: value
key2: value
}
}); // returns promise
// list file names
Expand Down
3 changes: 2 additions & 1 deletion conf.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
module.exports = {
API_AUTHORIZE__URL: 'https://api.backblaze.com/b2api/v1/b2_authorize_account',
API_VERSION_URL: '/b2api/v1'
API_VERSION_URL: '/b2api/v1',
MAX_INFO_HEADERS: 10 // maximum number of custom x-bz-info-* headers
};
3 changes: 3 additions & 0 deletions lib/actions/file.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ var sha1 = require('node-sha1');
var q = require('q');

var utils = require('./../utils');
var headers = require('../headers');
var request = require('../request');
var conf = require('../../conf');

Expand All @@ -10,6 +11,7 @@ exports.uploadFile = function(b2, args) {
var uploadAuthToken = args.uploadAuthToken;
var filename = encodeURIComponent(args.filename);
var data = args.data;
var info = args.info;

var options = {
url: uploadUrl,
Expand All @@ -22,6 +24,7 @@ exports.uploadFile = function(b2, args) {
},
body: data
};
headers.addInfoHeaders(options, info);
return request.sendRequest(options);
};

Expand Down
32 changes: 32 additions & 0 deletions lib/headers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
var conf = require('../conf');

exports.addInfoHeaders = function(options, info) {
var MAX_INFO_HEADERS = conf.MAX_INFO_HEADERS;
var invalidKeys = [];
if (info) {
var keys = Object.keys(info);

if (keys.length > MAX_INFO_HEADERS) {
throw new Error('Too many info headers: maximum of ' + MAX_INFO_HEADERS + ' allowed');
}

keys.forEach(addInfoHeader);

if (invalidKeys.length) {
throw new Error('Info header keys contain invalid characters: ' + invalidKeys.join(' '));
}
}

function isValidHeader(header) {
return /^[a-z0-9\-]+$/i.test(header);
}

function addInfoHeader(infoKey) {
if (isValidHeader(infoKey)) {
var key = 'X-Bz-Info-' + infoKey;
options.headers[key] = encodeURIComponent(info[infoKey]);
} else {
return invalidKeys.push(infoKey);
}
}
};
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "backblaze-b2",
"version": "0.9.5",
"version": "0.9.6",
"description": "Node.js Library for the Backblaze B2 Storage Service",
"main": "index.js",
"scripts": {
Expand Down
38 changes: 38 additions & 0 deletions test/unit/lib/actions/fileTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,44 @@ describe('actions/file', function() {
});
});

describe('with info headers', function() {

beforeEach(function(done) {
options = {
uploadUrl: 'https://uploadUrl',
uploadAuthToken: 'uploadauthtoken',
filename: 'foo.txt',
data: 'some text file content',
info: {
foo: 'bar',
unicorns: 'rainbows'
}
};

file.uploadFile(b2, options).then(function() {
done();
});
});

it('should properly add x-bz-info headers', function() {
expect(requestOptions).to.eql({
url: 'https://uploadUrl',
method: 'POST',
headers:
{
Authorization: 'uploadauthtoken',
'Content-Type': 'b2/x-auto',
'X-Bz-File-Name': 'foo.txt',
'X-Bz-Content-Sha1': '332e7f863695677895a406aff6d60acf7e84ea22',
'X-Bz-Info-foo': 'bar',
'X-Bz-Info-unicorns': 'rainbows'
},
body: 'some text file content'
});
});

});

});


Expand Down
78 changes: 78 additions & 0 deletions test/unit/lib/headersTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
var expect = require('expect.js');

var headers = require('../../../lib/headers');

describe('headers', function() {

var options;
var info;
var err;

beforeEach(function() {
options = {
headers: {}
};

info = null;

err = {
message: ''
};
});

describe('addInfoHeaders', function() {

it('should add the provided key/values to the headers properties of the options argument', function() {
info = {
foo: 'bar',
unicorns: 'rainbows'
};

headers.addInfoHeaders(options, info);

expect(options.headers).to.eql({
'X-Bz-Info-foo': 'bar',
'X-Bz-Info-unicorns': 'rainbows'
});
});

it('should throw an error when too many info headers are added', function() {
info = {
a: 'foo',
b: 'foo',
c: 'foo',
d: 'foo',
e: 'foo',
f: 'foo',
g: 'foo',
h: 'foo',
i: 'foo',
j: 'foo',
k: 'foo'
};

try {
headers.addInfoHeaders(options, info);
} catch (e) {
err = e;
}
expect(err.message).to.be('Too many info headers: maximum of 10 allowed');
});

it('should throw an error with a list of keys that have invalid characters', function() {
info = {
'abcsABC-!@#$_': 'woh bad key',
'defgDEF-)(*&^:"': 'very bad key'
};

try {
headers.addInfoHeaders(options, info);
} catch (e) {
err = e;
}
expect(err.message).to.be('Info header keys contain invalid characters: abcsABC-!@#$_ defgDEF-)(*&^:"');
});

});

});
20 changes: 16 additions & 4 deletions test/unit/lib/utilsTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ var utils = require('../../../lib/utils');

describe('utils', function() {

var err;

beforeEach(function() {
err = {
message: ''
};
});

describe('getAuthHeaderObject', function() {
it('Should get header object with Base64 encoded Authorization header', function() {
var accountId = 'unicorns';
Expand All @@ -18,8 +26,9 @@ describe('utils', function() {
try {
utils.getAuthHeaderObject(accountId, applicationKey);
} catch (e) {
expect(e.message).to.be('Invalid accountId');
err = e;
}
expect(err.message).to.be('Invalid accountId');
});

it('Should throw error given invalid applicationKey', function() {
Expand All @@ -28,8 +37,9 @@ describe('utils', function() {
try {
utils.getAuthHeaderObject(accountId, applicationKey);
} catch (e) {
expect(e.message).to.be('Invalid applicationKey');
err = e;
}
expect(err.message).to.be('Invalid applicationKey');
});
});

Expand All @@ -47,17 +57,19 @@ describe('utils', function() {
try {
utils.getAuthHeaderObjectWithToken(b2);
} catch (e) {
expect(e.message).to.be('Invalid B2 instance');
err = e;
}
expect(err.message).to.be('Invalid B2 instance');
});

it('Should throw error given b2 object has no valid authorizationToken', function() {
var b2 = {};
try {
utils.getAuthHeaderObjectWithToken(b2);
} catch (e) {
expect(e.message).to.be('Invalid authorizationToken');
err = e;
}
expect(err.message).to.be('Invalid authorizationToken');
});
});

Expand Down

0 comments on commit 836097c

Please sign in to comment.