From 90844331aee5d062fb03a0625d9d87013cc03c28 Mon Sep 17 00:00:00 2001 From: Amit-A Date: Tue, 19 Feb 2019 15:12:09 -0500 Subject: [PATCH 1/7] using let and const instead of var (es6) --- .eslintrc.js | 1 + lib/actions/auth.js | 10 ++--- lib/actions/bucket.js | 16 ++++---- lib/actions/file.js | 90 +++++++++++++++++++++---------------------- lib/headers.js | 6 +-- lib/request.js | 6 +-- 6 files changed, 65 insertions(+), 64 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 18681cc..99ab558 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -18,6 +18,7 @@ module.exports = { ] }, "env": { + es6: true, "node": true }, "extends": "eslint:recommended" diff --git a/lib/actions/auth.js b/lib/actions/auth.js index 1a0e16a..7bb0054 100644 --- a/lib/actions/auth.js +++ b/lib/actions/auth.js @@ -1,11 +1,11 @@ -var conf = require('../../conf'); -var request = require('./../request'); -var utils = require('./../utils'); +const conf = require('../../conf'); +const request = require('./../request'); +const utils = require('./../utils'); exports.authorize = function(b2) { - var options = getRequestOptions(b2.accountId, b2.applicationKey); + const options = getRequestOptions(b2.accountId, b2.applicationKey); - var axiosInstance = request.getInstance(); + const axiosInstance = request.getInstance(); return axiosInstance(options).then(function(res) { utils.saveAuthContext(b2, res.data); return res; // For testing and/or Promise chaining diff --git a/lib/actions/bucket.js b/lib/actions/bucket.js index 9cd7bbf..9877c9f 100644 --- a/lib/actions/bucket.js +++ b/lib/actions/bucket.js @@ -1,6 +1,6 @@ -var utils = require('./../utils'); -var request = require('../request'); -var conf = require('../../conf'); +const utils = require('./../utils'); +const request = require('../request'); +const conf = require('../../conf'); exports.TYPES = { ALL_PUBLIC: 'allPublic', @@ -8,7 +8,7 @@ exports.TYPES = { }; exports.create = function(b2, bucketName, bucketType) { - var options = { + const options = { url: getCreateUrl(b2, bucketName, bucketType), method: 'POST', headers: utils.getAuthHeaderObjectWithToken(b2), @@ -22,7 +22,7 @@ exports.create = function(b2, bucketName, bucketType) { }; exports.delete = function(b2, bucketId) { - var options = { + const options = { url: getDeleteUrl(b2), method: 'POST', data: { @@ -35,7 +35,7 @@ exports.delete = function(b2, bucketId) { }; exports.list = function(b2) { - var options = { + const options = { url: getListUrl(b2), method: 'POST', data: { @@ -70,7 +70,7 @@ exports.get = function(b2, args) { }; exports.update = function(b2, bucketId, bucketType) { - var options = { + const options = { url: getUpdateUrl(b2), method: 'POST', data: { @@ -84,7 +84,7 @@ exports.update = function(b2, bucketId, bucketType) { }; exports.getUploadUrl = function(b2, bucketId) { - var options = { + const options = { url: getGetUploadUrl(b2), method: 'POST', data: { diff --git a/lib/actions/file.js b/lib/actions/file.js index a22feb0..66cfa34 100644 --- a/lib/actions/file.js +++ b/lib/actions/file.js @@ -1,20 +1,20 @@ -var utils = require('./../utils'); -var headersUtil = require('../headers'); -var request = require('../request'); -var conf = require('../../conf'); +const utils = require('./../utils'); +const headersUtil = require('../headers'); +const request = require('../request'); +const conf = require('../../conf'); const sha1 = (value) => require('crypto').createHash('sha1').update(value).digest('hex'); exports.uploadFile = function(b2, args) { - var uploadUrl = args.uploadUrl; - var uploadAuthToken = args.uploadAuthToken; + const uploadUrl = args.uploadUrl; + const uploadAuthToken = args.uploadAuthToken; // Previous versions used filename (lowercase), so support that here - var fileName = utils.getUrlEncodedFileName(args.fileName || args.filename); - var data = args.data; - var hash = args.hash; - var info = args.info; - var mime = args.mime; + const fileName = utils.getUrlEncodedFileName(args.fileName || args.filename); + const data = args.data; + const hash = args.hash; + const info = args.info; + const mime = args.mime; - var options = { + const options = { url: uploadUrl, method: 'POST', headers: { @@ -33,7 +33,7 @@ exports.uploadFile = function(b2, args) { }; exports.startLargeFile = function(b2, args) { - var options = { + const options = { url: getStartLargeFileUrl(b2), method: 'POST', headers: utils.getAuthHeaderObjectWithToken(b2), @@ -47,7 +47,7 @@ exports.startLargeFile = function(b2, args) { }; exports.getUploadPartUrl = function(b2, args) { - var options = { + const options = { url: getGetUploadPartUrl(b2), method: 'POST', headers: utils.getAuthHeaderObjectWithToken(b2), @@ -59,7 +59,7 @@ exports.getUploadPartUrl = function(b2, args) { }; exports.uploadPart = function(b2, args) { - var options = { + const options = { url: args.uploadUrl, method: 'POST', headers: { @@ -76,7 +76,7 @@ exports.uploadPart = function(b2, args) { }; exports.finishLargeFile = function(b2, args) { - var options = { + const options = { url: getFinishLargeFileUrl(b2), method: 'POST', headers: utils.getAuthHeaderObjectWithToken(b2), @@ -89,7 +89,7 @@ exports.finishLargeFile = function(b2, args) { }; exports.cancelLargeFile = function(b2, args) { - var options = { + const options = { url: getCancelLargeFileUrl(b2), method: 'POST', headers: utils.getAuthHeaderObjectWithToken(b2), @@ -101,13 +101,13 @@ exports.cancelLargeFile = function(b2, args) { }; exports.listFileNames = function(b2, args) { - var bucketId = args.bucketId; - var startFileName = args.startFileName; - var maxFileCount = args.maxFileCount; - var prefix = args.prefix; - var delimiter = args.delimiter; + const bucketId = args.bucketId; + const startFileName = args.startFileName; + const maxFileCount = args.maxFileCount; + const prefix = args.prefix; + const delimiter = args.delimiter; - var options = { + const options = { url: getListFilesUrl(b2), method: 'POST', headers: utils.getAuthHeaderObjectWithToken(b2), @@ -123,11 +123,11 @@ exports.listFileNames = function(b2, args) { }; exports.listFileVersions = function(b2, args) { - var bucketId = args.bucketId; - var startFileName = args.startFileName; - var maxFileCount = args.maxFileCount; + const bucketId = args.bucketId; + const startFileName = args.startFileName; + const maxFileCount = args.maxFileCount; - var options = { + const options = { url: getListFileVersionsUrl(b2), method: 'POST', headers: utils.getAuthHeaderObjectWithToken(b2), @@ -141,10 +141,10 @@ exports.listFileVersions = function(b2, args) { }; exports.hideFile = function(b2, args) { - var bucketId = args.bucketId; - var fileName = args.fileName; + const bucketId = args.bucketId; + const fileName = args.fileName; - var options = { + const options = { url: getHideFileUrl(b2), method: 'POST', headers: utils.getAuthHeaderObjectWithToken(b2), @@ -157,7 +157,7 @@ exports.hideFile = function(b2, args) { }; exports.getFileInfo = function(b2, fileId) { - var options = { + const options = { url: getFileInfoUrl(b2), method: 'POST', headers: utils.getAuthHeaderObjectWithToken(b2), @@ -169,12 +169,12 @@ exports.getFileInfo = function(b2, fileId) { }; exports.getDownloadAuthorization = function (b2, args) { - var bucketId = args.bucketId; - var fileNamePrefix = args.fileNamePrefix; - var validDurationInSeconds = args.validDurationInSeconds; - var b2ContentDisposition = args.b2ContentDisposition; + const bucketId = args.bucketId; + const fileNamePrefix = args.fileNamePrefix; + const validDurationInSeconds = args.validDurationInSeconds; + const b2ContentDisposition = args.b2ContentDisposition; - var options = { + const options = { url: getDownloadAuthorizationUrl(b2), method: 'POST', headers: utils.getAuthHeaderObjectWithToken(b2), @@ -190,10 +190,10 @@ exports.getDownloadAuthorization = function (b2, args) { }; exports.downloadFileByName = function(b2, args) { - var bucketName = args.bucketName; - var fileName = utils.getUrlEncodedFileName(args.fileName); + const bucketName = args.bucketName; + const fileName = utils.getUrlEncodedFileName(args.fileName); - var options = { + const options = { url: getDownloadFileByNameUrl(b2, bucketName, fileName), headers: utils.getAuthHeaderObjectWithToken(b2), responseType: args.responseType || null, @@ -202,12 +202,12 @@ exports.downloadFileByName = function(b2, args) { onDownloadProgress: args.onDownloadProgress || null }; - var requestInstance = request.getInstance(); + const requestInstance = request.getInstance(); return requestInstance(options, utils.getProcessFileSuccess()); }; exports.downloadFileById = function(b2, args) { - var options = { + const options = { url: getDownloadFileByIdUrl(b2, args.fileId), headers: utils.getAuthHeaderObjectWithToken(b2), responseType: args.responseType || null, @@ -216,15 +216,15 @@ exports.downloadFileById = function(b2, args) { onDownloadProgress: args.onDownloadProgress || null }; - var requestInstance = request.getInstance(); + const requestInstance = request.getInstance(); return requestInstance(options, utils.getProcessFileSuccess()); }; exports.deleteFileVersion = function(b2, args) { - var fileId = args.fileId; - var fileName = args.fileName; + const fileId = args.fileId; + const fileName = args.fileName; - var options = { + const options = { url: getDeleteFileVersionUrl(b2), method: 'POST', headers: utils.getAuthHeaderObjectWithToken(b2), diff --git a/lib/headers.js b/lib/headers.js index e5eae96..a17c881 100644 --- a/lib/headers.js +++ b/lib/headers.js @@ -23,7 +23,7 @@ exports.addInfoHeaders = function(options, info) { function addInfoHeader(infoKey) { if (isValidHeader(infoKey)) { - var key = 'X-Bz-Info-' + infoKey; + const key = 'X-Bz-Info-' + infoKey; options.headers[key] = encodeURIComponent(info[infoKey]); } else { return invalidKeys.push(infoKey); @@ -32,7 +32,7 @@ exports.addInfoHeaders = function(options, info) { }; exports.addBzHeaders = function(headers, targetObj) { - var keys = Object.keys(headers); + const keys = Object.keys(headers); return keys.filter(isBzHeader) .map(getKeyObj) @@ -43,7 +43,7 @@ exports.addBzHeaders = function(headers, targetObj) { } function getKeyObj(header) { - var replacement = /^X-Bz-Info-/i.test(header) ? /X-Bz-Info-/i : /X-Bz-/i; + const replacement = /^X-Bz-Info-/i.test(header) ? /X-Bz-Info-/i : /X-Bz-/i; return { original: header, header: camelCase(header.replace(replacement, '')) diff --git a/lib/request.js b/lib/request.js index de3bd0f..434f44d 100644 --- a/lib/request.js +++ b/lib/request.js @@ -1,12 +1,12 @@ -var utils = require('./utils.js'); -var REQUEST; +const utils = require('./utils.js'); +let REQUEST; exports.setup = function(requestObject) { REQUEST = requestObject; }; exports.sendRequest = function(options) { - var requestInstance = exports.getInstance(); + const requestInstance = exports.getInstance(); return requestInstance(options, utils.processResponseGeneric()); }; From b515da0ba80a446d1d4854c1d704ee6a477f4840 Mon Sep 17 00:00:00 2001 From: Amit-A Date: Tue, 19 Feb 2019 15:44:31 -0500 Subject: [PATCH 2/7] standardizing function arguments, now: function(b2, args) Also added lodash which will be necessary for the next commit (using lodash merge). This won't add overhead since it looks like lodash is already in use indirectly by a dependency (I see it in the package-lock.json file) --- lib/actions/auth.js | 2 +- lib/actions/bucket.js | 35 ++++++++++++++++++++++++++++++----- lib/actions/file.js | 8 +++++++- lib/b2.js | 34 ++++++++++++++++++---------------- package-lock.json | 3 +-- package.json | 3 ++- 6 files changed, 59 insertions(+), 26 deletions(-) diff --git a/lib/actions/auth.js b/lib/actions/auth.js index 7bb0054..d2c6681 100644 --- a/lib/actions/auth.js +++ b/lib/actions/auth.js @@ -2,7 +2,7 @@ const conf = require('../../conf'); const request = require('./../request'); const utils = require('./../utils'); -exports.authorize = function(b2) { +exports.authorize = function(b2, args) { const options = getRequestOptions(b2.accountId, b2.applicationKey); const axiosInstance = request.getInstance(); diff --git a/lib/actions/bucket.js b/lib/actions/bucket.js index 9877c9f..f6c6714 100644 --- a/lib/actions/bucket.js +++ b/lib/actions/bucket.js @@ -1,3 +1,4 @@ +const _ = require('lodash'); const utils = require('./../utils'); const request = require('../request'); const conf = require('../../conf'); @@ -7,7 +8,14 @@ exports.TYPES = { ALL_PRIVATE: 'allPrivate' }; -exports.create = function(b2, bucketName, bucketType) { +exports.create = function(b2, argsOrBucketName, undefOrBucketType) { + // we're allowing an args object OR bucketName and bucketType for backwards compatibility + let bucketName = argsOrBucketName; + let bucketType = undefOrBucketType; + if (!_.isString(argsOrBucketName)) { + bucketName = _.get(argsOrBucketName, 'bucketName'); + bucketType = _.get(argsOrBucketName, 'bucketType'); + } const options = { url: getCreateUrl(b2, bucketName, bucketType), method: 'POST', @@ -21,7 +29,12 @@ exports.create = function(b2, bucketName, bucketType) { return request.sendRequest(options); }; -exports.delete = function(b2, bucketId) { +exports.delete = function(b2, argsOrBucketId) { + // we're allowing an args object OR bucketId for backwards compatibility + let bucketId = argsOrBucketId; + if (!_.isString(argsOrBucketId)) { + bucketId = _.get(argsOrBucketId, 'bucketId'); + } const options = { url: getDeleteUrl(b2), method: 'POST', @@ -34,7 +47,7 @@ exports.delete = function(b2, bucketId) { return request.sendRequest(options); }; -exports.list = function(b2) { +exports.list = function(b2, args) { const options = { url: getListUrl(b2), method: 'POST', @@ -69,7 +82,14 @@ exports.get = function(b2, args) { return request.sendRequest(options); }; -exports.update = function(b2, bucketId, bucketType) { +exports.update = function(b2, argsOrBucketId, undefOrBucketType) { + // we're allowing an args object OR bucketId and bucketType for backwards compatibility + let bucketId = argsOrBucketId; + let bucketType = undefOrBucketType; + if (!_.isString(argsOrBucketId)) { + bucketId = _.get(argsOrBucketId, 'bucketId'); + bucketType = _.get(argsOrBucketId, 'bucketType'); + } const options = { url: getUpdateUrl(b2), method: 'POST', @@ -83,7 +103,12 @@ exports.update = function(b2, bucketId, bucketType) { return request.sendRequest(options); }; -exports.getUploadUrl = function(b2, bucketId) { +exports.getUploadUrl = function(b2, argsOrBucketId) { + // we're allowing an args object OR bucketId for backwards compatibility + let bucketId = argsOrBucketId; + if (!_.isString(argsOrBucketId)) { + bucketId = _.get(argsOrBucketId, 'bucketId'); + } const options = { url: getGetUploadUrl(b2), method: 'POST', diff --git a/lib/actions/file.js b/lib/actions/file.js index 66cfa34..b84e077 100644 --- a/lib/actions/file.js +++ b/lib/actions/file.js @@ -1,3 +1,4 @@ +const _ = require('lodash'); const utils = require('./../utils'); const headersUtil = require('../headers'); const request = require('../request'); @@ -156,7 +157,12 @@ exports.hideFile = function(b2, args) { return request.sendRequest(options); }; -exports.getFileInfo = function(b2, fileId) { +exports.getFileInfo = function(b2, argsOrFileId) { + /* we're allowing an args object OR fileId for backwards compatibility */ + let fileId = argsOrFileId; + if (!_.isString(argsOrFileId)) { + fileId = _.get(argsOrFileId, 'fileId'); + } const options = { url: getFileInfoUrl(b2), method: 'POST', diff --git a/lib/b2.js b/lib/b2.js index d25fbc2..2d4c209 100644 --- a/lib/b2.js +++ b/lib/b2.js @@ -26,20 +26,20 @@ function B2(options) { B2.prototype.BUCKET_TYPES = actions.bucket.TYPES; -B2.prototype.authorize = function() { - return actions.auth.authorize(this, this.accountId, this.applicationKey); +B2.prototype.authorize = function(args) { + return actions.auth.authorize(this, args); }; -B2.prototype.createBucket = function(bucketName, bucketType) { - return actions.bucket.create(this, bucketName, bucketType); +B2.prototype.createBucket = function(argsOrBucketName, undefOrBucketType) { + return actions.bucket.create(this, argsOrBucketName, undefOrBucketType); }; -B2.prototype.deleteBucket = function(bucketId) { - return actions.bucket.delete(this, bucketId); +B2.prototype.deleteBucket = function(argsOrBucketId) { + return actions.bucket.delete(this, argsOrBucketId); }; -B2.prototype.listBuckets = function() { - return actions.bucket.list(this); +B2.prototype.listBuckets = function(args) { + return actions.bucket.list(this, args); }; // args: @@ -49,12 +49,12 @@ B2.prototype.getBucket = function(args) { return actions.bucket.get(this, args); }; -B2.prototype.updateBucket = function(bucketId, bucketType) { - return actions.bucket.update(this, bucketId, bucketType); +B2.prototype.updateBucket = function(argsOrBucketId, undefOrBucketType) { + return actions.bucket.update(this, argsOrBucketId, undefOrBucketType); }; -B2.prototype.getUploadUrl = function(bucketId) { - return actions.bucket.getUploadUrl(this, bucketId); +B2.prototype.getUploadUrl = function(argsOrBucketId) { + return actions.bucket.getUploadUrl(this, argsOrBucketId); }; // args: @@ -90,8 +90,10 @@ B2.prototype.hideFile = function(args) { return actions.file.hideFile(this, args); }; -B2.prototype.getFileInfo = function(fileId) { - return actions.file.getFileInfo(this, fileId); +// args: +// - fileId +B2.prototype.getFileInfo = function(argsOrFileId) { + return actions.file.getFileInfo(this, argsOrFileId); }; // args: @@ -110,8 +112,8 @@ B2.prototype.downloadFileByName = function(args) { return actions.file.downloadFileByName(this, args); }; -B2.prototype.downloadFileById = function(fileId) { - return actions.file.downloadFileById(this, fileId); +B2.prototype.downloadFileById = function(args) { + return actions.file.downloadFileById(this, args); }; // args: diff --git a/package-lock.json b/package-lock.json index a1207c8..99582a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -662,8 +662,7 @@ "lodash": { "version": "4.17.11", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", - "dev": true + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" }, "mimic-fn": { "version": "1.2.0", diff --git a/package.json b/package.json index dd9fcb7..b29dd44 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,8 @@ }, "dependencies": { "axios": "^0.18.0", - "axios-retry": "^3.1.2" + "axios-retry": "^3.1.2", + "lodash": "^4.17.11" }, "engines": { "node": ">=10.0" From 9d61af9564cefcecc1c3a3567b601d93eb6179d0 Mon Sep 17 00:00:00 2001 From: Amit-A Date: Tue, 19 Feb 2019 16:01:12 -0500 Subject: [PATCH 3/7] adding the axios and axiosOverride arguments to every action this allows users to control axios arguments at the action/request level (e.g. specify timeouts) --- lib/actions/auth.js | 9 +++- lib/actions/bucket.js | 42 +++++++++++++--- lib/actions/file.js | 111 ++++++++++++++++++++++++++++++++++++------ 3 files changed, 140 insertions(+), 22 deletions(-) diff --git a/lib/actions/auth.js b/lib/actions/auth.js index d2c6681..4c7b1ac 100644 --- a/lib/actions/auth.js +++ b/lib/actions/auth.js @@ -1,10 +1,15 @@ +const _ = require('lodash'); const conf = require('../../conf'); const request = require('./../request'); const utils = require('./../utils'); exports.authorize = function(b2, args) { - const options = getRequestOptions(b2.accountId, b2.applicationKey); - + // merge order matters here: later objects override earlier objects + const options = _.merge({}, + _.get(args, 'axios', {}), + getRequestOptions(b2.accountId, b2.applicationKey), + _.get(args, 'axiosOverride', {}) + ); const axiosInstance = request.getInstance(); return axiosInstance(options).then(function(res) { utils.saveAuthContext(b2, res.data); diff --git a/lib/actions/bucket.js b/lib/actions/bucket.js index f6c6714..8970816 100644 --- a/lib/actions/bucket.js +++ b/lib/actions/bucket.js @@ -26,7 +26,12 @@ exports.create = function(b2, argsOrBucketName, undefOrBucketType) { bucketType: bucketType } }; - return request.sendRequest(options); + // merge order matters here: later objects override earlier objects + return request.sendRequest(_.merge({}, + _.get(argsOrBucketName, 'axios', {}), + options, + _.get(argsOrBucketName, 'axiosOverride', {}) + )); }; exports.delete = function(b2, argsOrBucketId) { @@ -44,7 +49,12 @@ exports.delete = function(b2, argsOrBucketId) { }, headers: utils.getAuthHeaderObjectWithToken(b2) }; - return request.sendRequest(options); + // merge order matters here: later objects override earlier objects + return request.sendRequest(_.merge({}, + _.get(argsOrBucketId, 'axios', {}), + options, + _.get(argsOrBucketId, 'axiosOverride', {}) + )); }; exports.list = function(b2, args) { @@ -57,7 +67,12 @@ exports.list = function(b2, args) { headers: utils.getAuthHeaderObjectWithToken(b2) }; - return request.sendRequest(options); + // merge order matters here: later objects override earlier objects + return request.sendRequest(_.merge({}, + _.get(args, 'axios', {}), + options, + _.get(args, 'axiosOverride', {}) + )); }; // https://www.backblaze.com/b2/docs/b2_list_buckets.html @@ -79,7 +94,12 @@ exports.get = function(b2, args) { data, headers: utils.getAuthHeaderObjectWithToken(b2) }; - return request.sendRequest(options); + // merge order matters here: later objects override earlier objects + return request.sendRequest(_.merge({}, + _.get(args, 'axios', {}), + options, + _.get(args, 'axiosOverride', {}) + )); }; exports.update = function(b2, argsOrBucketId, undefOrBucketType) { @@ -100,7 +120,12 @@ exports.update = function(b2, argsOrBucketId, undefOrBucketType) { }, headers: utils.getAuthHeaderObjectWithToken(b2) }; - return request.sendRequest(options); + // merge order matters here: later objects override earlier objects + return request.sendRequest(_.merge({}, + _.get(argsOrBucketId, 'axios', {}), + options, + _.get(argsOrBucketId, 'axiosOverride', {}) + )); }; exports.getUploadUrl = function(b2, argsOrBucketId) { @@ -117,7 +142,12 @@ exports.getUploadUrl = function(b2, argsOrBucketId) { }, headers: utils.getAuthHeaderObjectWithToken(b2) }; - return request.sendRequest(options); + // merge order matters here: later objects override earlier objects + return request.sendRequest(_.merge({}, + _.get(argsOrBucketId, 'axios', {}), + options, + _.get(argsOrBucketId, 'axiosOverride', {}) + )); }; function getCreateUrl(b2) { diff --git a/lib/actions/file.js b/lib/actions/file.js index b84e077..1abd3de 100644 --- a/lib/actions/file.js +++ b/lib/actions/file.js @@ -29,8 +29,14 @@ exports.uploadFile = function(b2, args) { maxRedirects: 0, onUploadProgress: args.onUploadProgress || null }; + headersUtil.addInfoHeaders(options, info); - return request.sendRequest(options); + // merge order matters here: later objects override earlier objects + return request.sendRequest(_.merge({}, + _.get(args, 'axios', {}), + options, + _.get(args, 'axiosOverride', {}) + )); }; exports.startLargeFile = function(b2, args) { @@ -44,7 +50,13 @@ exports.startLargeFile = function(b2, args) { contentType: args.contentType || 'b2/x-auto' } }; - return request.sendRequest(options); + + // merge order matters here: later objects override earlier objects + return request.sendRequest(_.merge({}, + _.get(args, 'axios', {}), + options, + _.get(args, 'axiosOverride', {}) + )); }; exports.getUploadPartUrl = function(b2, args) { @@ -56,7 +68,13 @@ exports.getUploadPartUrl = function(b2, args) { fileId: args.fileId } }; - return request.sendRequest(options); + + // merge order matters here: later objects override earlier objects + return request.sendRequest(_.merge({}, + _.get(args, 'axios', {}), + options, + _.get(args, 'axiosOverride', {}) + )); }; exports.uploadPart = function(b2, args) { @@ -73,7 +91,13 @@ exports.uploadPart = function(b2, args) { onUploadProgress: args.onUploadProgress || null, maxRedirects: 0 }; - return request.sendRequest(options); + + // merge order matters here: later objects override earlier objects + return request.sendRequest(_.merge({}, + _.get(args, 'axios', {}), + options, + _.get(args, 'axiosOverride', {}) + )); }; exports.finishLargeFile = function(b2, args) { @@ -86,7 +110,13 @@ exports.finishLargeFile = function(b2, args) { partSha1Array: args.partSha1Array } }; - return request.sendRequest(options); + + // merge order matters here: later objects override earlier objects + return request.sendRequest(_.merge({}, + _.get(args, 'axios', {}), + options, + _.get(args, 'axiosOverride', {}) + )); }; exports.cancelLargeFile = function(b2, args) { @@ -98,7 +128,13 @@ exports.cancelLargeFile = function(b2, args) { fileId: args.fileId } }; - return request.sendRequest(options); + + // merge order matters here: later objects override earlier objects + return request.sendRequest(_.merge({}, + _.get(args, 'axios', {}), + options, + _.get(args, 'axiosOverride', {}) + )); }; exports.listFileNames = function(b2, args) { @@ -120,7 +156,13 @@ exports.listFileNames = function(b2, args) { delimiter: delimiter || null } }; - return request.sendRequest(options, utils.getProcessFileSuccess(options)); + + // merge order matters here: later objects override earlier objects + return request.sendRequest(_.merge({}, + _.get(args, 'axios', {}), + options, + _.get(args, 'axiosOverride', {}) + ), utils.getProcessFileSuccess(options)); }; exports.listFileVersions = function(b2, args) { @@ -138,7 +180,13 @@ exports.listFileVersions = function(b2, args) { maxFileCount: maxFileCount || 100 } }; - return request.sendRequest(options); + + // merge order matters here: later objects override earlier objects + return request.sendRequest(_.merge({}, + _.get(args, 'axios', {}), + options, + _.get(args, 'axiosOverride', {}) + )); }; exports.hideFile = function(b2, args) { @@ -154,7 +202,13 @@ exports.hideFile = function(b2, args) { fileName: fileName } }; - return request.sendRequest(options); + + // merge order matters here: later objects override earlier objects + return request.sendRequest(_.merge({}, + _.get(args, 'axios', {}), + options, + _.get(args, 'axiosOverride', {}) + )); }; exports.getFileInfo = function(b2, argsOrFileId) { @@ -163,6 +217,7 @@ exports.getFileInfo = function(b2, argsOrFileId) { if (!_.isString(argsOrFileId)) { fileId = _.get(argsOrFileId, 'fileId'); } + const options = { url: getFileInfoUrl(b2), method: 'POST', @@ -171,7 +226,13 @@ exports.getFileInfo = function(b2, argsOrFileId) { fileId: fileId } }; - return request.sendRequest(options); + + // merge order matters here: later objects override earlier objects + return request.sendRequest(_.merge({}, + _.get(argsOrFileId, 'axios', {}), + options, + _.get(argsOrFileId, 'axiosOverride', {}) + )); }; exports.getDownloadAuthorization = function (b2, args) { @@ -192,7 +253,12 @@ exports.getDownloadAuthorization = function (b2, args) { } }; - return request.sendRequest(options); + // merge order matters here: later objects override earlier objects + return request.sendRequest(_.merge({}, + _.get(args, 'axios', {}), + options, + _.get(args, 'axiosOverride', {}) + )); }; exports.downloadFileByName = function(b2, args) { @@ -209,7 +275,14 @@ exports.downloadFileByName = function(b2, args) { }; const requestInstance = request.getInstance(); - return requestInstance(options, utils.getProcessFileSuccess()); + // merge order matters here: later objects override earlier objects + return requestInstance(_.merge({}, + _.get(args, 'axios', {}), + options, + _.get(args, 'axiosOverride', {}) + ), utils.getProcessFileSuccess()); + + }; exports.downloadFileById = function(b2, args) { @@ -223,7 +296,12 @@ exports.downloadFileById = function(b2, args) { }; const requestInstance = request.getInstance(); - return requestInstance(options, utils.getProcessFileSuccess()); + // merge order matters here: later objects override earlier objects + return requestInstance(_.merge({}, + _.get(args, 'axios', {}), + options, + _.get(args, 'axiosOverride', {}) + ), utils.getProcessFileSuccess()); }; exports.deleteFileVersion = function(b2, args) { @@ -240,7 +318,12 @@ exports.deleteFileVersion = function(b2, args) { } }; - return request.sendRequest(options); + // merge order matters here: later objects override earlier objects + return request.sendRequest(_.merge({}, + _.get(args, 'axios', {}), + options, + _.get(args, 'axiosOverride', {}) + )); }; function getListFilesUrl(b2) { From bdf42239c44f96b1c5de50b90c9224a1102c2870 Mon Sep 17 00:00:00 2001 From: Amit-A Date: Tue, 19 Feb 2019 16:35:00 -0500 Subject: [PATCH 4/7] Moving api endpoint generation into one file (endpoints.js) --- lib/actions/bucket.js | 38 ++++---------------- lib/actions/file.js | 82 ++++++++----------------------------------- lib/endpoints.js | 30 ++++++++++++++++ 3 files changed, 52 insertions(+), 98 deletions(-) create mode 100644 lib/endpoints.js diff --git a/lib/actions/bucket.js b/lib/actions/bucket.js index 8970816..707f977 100644 --- a/lib/actions/bucket.js +++ b/lib/actions/bucket.js @@ -1,7 +1,7 @@ const _ = require('lodash'); const utils = require('./../utils'); const request = require('../request'); -const conf = require('../../conf'); +const endpoints = require('../endpoints'); exports.TYPES = { ALL_PUBLIC: 'allPublic', @@ -17,7 +17,7 @@ exports.create = function(b2, argsOrBucketName, undefOrBucketType) { bucketType = _.get(argsOrBucketName, 'bucketType'); } const options = { - url: getCreateUrl(b2, bucketName, bucketType), + url: endpoints(b2).createBucketUrl, method: 'POST', headers: utils.getAuthHeaderObjectWithToken(b2), data: { @@ -41,7 +41,7 @@ exports.delete = function(b2, argsOrBucketId) { bucketId = _.get(argsOrBucketId, 'bucketId'); } const options = { - url: getDeleteUrl(b2), + url: endpoints(b2).deleteBucketUrl, method: 'POST', data: { accountId: b2.accountId, @@ -59,7 +59,7 @@ exports.delete = function(b2, argsOrBucketId) { exports.list = function(b2, args) { const options = { - url: getListUrl(b2), + url: endpoints(b2).listBucketUrl, method: 'POST', data: { accountId: b2.accountId @@ -89,7 +89,7 @@ exports.get = function(b2, args) { } const options = { - url: getListUrl(b2), + url: endpoints(b2).listBucketUrl, method: 'POST', data, headers: utils.getAuthHeaderObjectWithToken(b2) @@ -111,7 +111,7 @@ exports.update = function(b2, argsOrBucketId, undefOrBucketType) { bucketType = _.get(argsOrBucketId, 'bucketType'); } const options = { - url: getUpdateUrl(b2), + url: endpoints(b2).updateBucketUrl, method: 'POST', data: { accountId: b2.accountId, @@ -135,7 +135,7 @@ exports.getUploadUrl = function(b2, argsOrBucketId) { bucketId = _.get(argsOrBucketId, 'bucketId'); } const options = { - url: getGetUploadUrl(b2), + url: endpoints(b2).getBucketUploadUrl, method: 'POST', data: { bucketId: bucketId @@ -149,27 +149,3 @@ exports.getUploadUrl = function(b2, argsOrBucketId) { _.get(argsOrBucketId, 'axiosOverride', {}) )); }; - -function getCreateUrl(b2) { - return getApiUrl(b2) + '/b2_create_bucket'; -} - -function getDeleteUrl(b2) { - return getApiUrl(b2) + '/b2_delete_bucket'; -} - -function getListUrl(b2) { - return getApiUrl(b2) + '/b2_list_buckets'; -} - -function getUpdateUrl(b2) { - return getApiUrl(b2) + '/b2_update_bucket'; -} - -function getGetUploadUrl(b2) { - return getApiUrl(b2) + '/b2_get_upload_url'; -} - -function getApiUrl(b2) { - return b2.apiUrl + conf.API_VERSION_URL; -} diff --git a/lib/actions/file.js b/lib/actions/file.js index 1abd3de..5378de1 100644 --- a/lib/actions/file.js +++ b/lib/actions/file.js @@ -2,7 +2,7 @@ const _ = require('lodash'); const utils = require('./../utils'); const headersUtil = require('../headers'); const request = require('../request'); -const conf = require('../../conf'); +const endpoints = require('../endpoints'); const sha1 = (value) => require('crypto').createHash('sha1').update(value).digest('hex'); exports.uploadFile = function(b2, args) { @@ -29,7 +29,7 @@ exports.uploadFile = function(b2, args) { maxRedirects: 0, onUploadProgress: args.onUploadProgress || null }; - + headersUtil.addInfoHeaders(options, info); // merge order matters here: later objects override earlier objects return request.sendRequest(_.merge({}, @@ -41,7 +41,7 @@ exports.uploadFile = function(b2, args) { exports.startLargeFile = function(b2, args) { const options = { - url: getStartLargeFileUrl(b2), + url: endpoints(b2).startLargeFileUrl, method: 'POST', headers: utils.getAuthHeaderObjectWithToken(b2), data: { @@ -61,7 +61,7 @@ exports.startLargeFile = function(b2, args) { exports.getUploadPartUrl = function(b2, args) { const options = { - url: getGetUploadPartUrl(b2), + url: endpoints(b2).getUploadPartUrl, method: 'POST', headers: utils.getAuthHeaderObjectWithToken(b2), data: { @@ -102,7 +102,7 @@ exports.uploadPart = function(b2, args) { exports.finishLargeFile = function(b2, args) { const options = { - url: getFinishLargeFileUrl(b2), + url: endpoints(b2).finishLargeFileUrl, method: 'POST', headers: utils.getAuthHeaderObjectWithToken(b2), data: { @@ -121,7 +121,7 @@ exports.finishLargeFile = function(b2, args) { exports.cancelLargeFile = function(b2, args) { const options = { - url: getCancelLargeFileUrl(b2), + url: endpoints(b2).cancelLargeFileUrl, method: 'POST', headers: utils.getAuthHeaderObjectWithToken(b2), data: { @@ -145,7 +145,7 @@ exports.listFileNames = function(b2, args) { const delimiter = args.delimiter; const options = { - url: getListFilesUrl(b2), + url: endpoints(b2).listFilesUrl, method: 'POST', headers: utils.getAuthHeaderObjectWithToken(b2), data: { @@ -171,7 +171,7 @@ exports.listFileVersions = function(b2, args) { const maxFileCount = args.maxFileCount; const options = { - url: getListFileVersionsUrl(b2), + url: endpoints(b2).listFileVersionsUrl, method: 'POST', headers: utils.getAuthHeaderObjectWithToken(b2), data: { @@ -194,7 +194,7 @@ exports.hideFile = function(b2, args) { const fileName = args.fileName; const options = { - url: getHideFileUrl(b2), + url: endpoints(b2).hideFileUrl, method: 'POST', headers: utils.getAuthHeaderObjectWithToken(b2), data: { @@ -219,7 +219,7 @@ exports.getFileInfo = function(b2, argsOrFileId) { } const options = { - url: getFileInfoUrl(b2), + url: endpoints(b2).fileInfoUrl, method: 'POST', headers: utils.getAuthHeaderObjectWithToken(b2), data: { @@ -242,7 +242,7 @@ exports.getDownloadAuthorization = function (b2, args) { const b2ContentDisposition = args.b2ContentDisposition; const options = { - url: getDownloadAuthorizationUrl(b2), + url: endpoints(b2).downloadAuthorizationUrl, method: 'POST', headers: utils.getAuthHeaderObjectWithToken(b2), data: { @@ -266,7 +266,7 @@ exports.downloadFileByName = function(b2, args) { const fileName = utils.getUrlEncodedFileName(args.fileName); const options = { - url: getDownloadFileByNameUrl(b2, bucketName, fileName), + url: endpoints(b2).downloadFileByNameUrl(bucketName, fileName), headers: utils.getAuthHeaderObjectWithToken(b2), responseType: args.responseType || null, encoding: null, @@ -287,7 +287,7 @@ exports.downloadFileByName = function(b2, args) { exports.downloadFileById = function(b2, args) { const options = { - url: getDownloadFileByIdUrl(b2, args.fileId), + url: endpoints(b2).downloadFileByIdUrl(args.fileId), headers: utils.getAuthHeaderObjectWithToken(b2), responseType: args.responseType || null, encoding: null, @@ -309,7 +309,7 @@ exports.deleteFileVersion = function(b2, args) { const fileName = args.fileName; const options = { - url: getDeleteFileVersionUrl(b2), + url: endpoints(b2).deleteFileVersionUrl, method: 'POST', headers: utils.getAuthHeaderObjectWithToken(b2), data: { @@ -324,56 +324,4 @@ exports.deleteFileVersion = function(b2, args) { options, _.get(args, 'axiosOverride', {}) )); -}; - -function getListFilesUrl(b2) { - return getApiUrl(b2) + '/b2_list_file_names'; -} - -function getListFileVersionsUrl(b2) { - return getApiUrl(b2) + '/b2_list_file_versions'; -} - -function getHideFileUrl(b2) { - return getApiUrl(b2) + '/b2_hide_file'; -} - -function getFileInfoUrl(b2) { - return getApiUrl(b2) + '/b2_get_file_info'; -} - -function getDownloadAuthorizationUrl(b2) { - return getApiUrl(b2) + '/b2_get_download_authorization'; -} - -function getDownloadFileByNameUrl(b2, bucketName, fileName) { - return b2.downloadUrl + '/file/' + bucketName + '/' + fileName; -} - -function getDownloadFileByIdUrl(b2, fileId) { - return b2.downloadUrl + conf.API_VERSION_URL + '/b2_download_file_by_id?fileId=' + fileId; -} - -function getDeleteFileVersionUrl(b2) { - return getApiUrl(b2) + '/b2_delete_file_version'; -} - -function getApiUrl(b2) { - return b2.apiUrl + conf.API_VERSION_URL; -} - -function getStartLargeFileUrl(b2) { - return getApiUrl(b2) + '/b2_start_large_file'; -} - -function getGetUploadPartUrl(b2) { - return getApiUrl(b2) + '/b2_get_upload_part_url'; -} - -function getFinishLargeFileUrl(b2) { - return getApiUrl(b2) + '/b2_finish_large_file'; -} - -function getCancelLargeFileUrl(b2) { - return getApiUrl(b2) + '/b2_cancel_large_file'; -} +}; \ No newline at end of file diff --git a/lib/endpoints.js b/lib/endpoints.js new file mode 100644 index 0000000..8dd8814 --- /dev/null +++ b/lib/endpoints.js @@ -0,0 +1,30 @@ +const conf = require('../conf'); + +module.exports = function(b2) { + const apiUrl = `${b2.apiUrl}${conf.API_VERSION_URL}`; + return { + // bucket actions + createBucketUrl: `${apiUrl}/b2_create_bucket`, + deleteBucketUrl: `${apiUrl}/b2_delete_bucket`, + listBucketUrl: `${apiUrl}/b2_list_buckets`, + updateBucketUrl: `${apiUrl}/b2_update_bucket`, + getBucketUploadUrl: `${apiUrl}/b2_get_upload_url`, + // file actions + listFilesUrl: `${apiUrl}/b2_list_file_names`, + listFileVersionsUrl: `${apiUrl}/b2_list_file_versions`, + hideFileUrl: `${apiUrl}/b2_hide_file`, + fileInfoUrl: `${apiUrl}/b2_get_file_info`, + downloadAuthorizationUrl: `${apiUrl}/b2_get_download_authorization`, + downloadFileByNameUrl: function(bucketName, fileName) { + return `${b2.downloadUrl}/file/${bucketName}/${fileName}`; + }, + downloadFileByIdUrl: function(fileId) { + return `${b2.downloadUrl}${conf.API_VERSION_URL}/b2_download_file_by_id?fileId=${fileId}`; + }, + deleteFileVersionUrl: `${apiUrl}/b2_delete_file_version`, + startLargeFileUrl: `${apiUrl}/b2_start_large_file`, + getUploadPartUrl: `${apiUrl}/b2_get_upload_part_url`, + finishLargeFileUrl: `${apiUrl}/b2_finish_large_file`, + cancelLargeFileUrl: `${apiUrl}/b2_cancel_large_file` + }; +}; \ No newline at end of file From 99065f4a27c2db131beddf1819dc2929d9d1ac1c Mon Sep 17 00:00:00 2001 From: Amit-A Date: Tue, 19 Feb 2019 19:42:32 -0500 Subject: [PATCH 5/7] Update README.md --- README.md | 204 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 135 insertions(+), 69 deletions(-) diff --git a/README.md b/README.md index ec5d1bf..032ed5e 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,16 @@ -### Backblaze B2 Node.js Library +# Backblaze B2 Node.js Library + [![npm version](https://badge.fury.io/js/backblaze-b2.svg)](https://badge.fury.io/js/backblaze-b2) [![Build Status](https://travis-ci.org/yakovkhalinsky/backblaze-b2.svg?branch=master)](https://travis-ci.org/yakovkhalinsky/backblaze-b2) +A customizable B2 client for Node.js: + +* Uses [axios](https://github.com/axios/axios). You can control the axios instance at the request level (see `axios` and `axiosOverride` config arguments) and at the global level (see `axios` config argument at instantiation) so you can use any axios feature. +* Automatically retries on request failure. You can control retry behaviour using the `retries` argument at instantiation. + +## Usage + This library uses promises, so all actions on a `B2` instance return a promise in the following pattern: + ``` javascript b2.instanceFunction(arg1, arg2).then( successFn(response) { ... }, @@ -9,31 +18,6 @@ b2.instanceFunction(arg1, arg2).then( ); ``` -### Status of project - -See the [CHANGELOG](https://github.com/yakovkhalinsky/backblaze-b2/blob/master/CHANGELOG.md) for a history of updates. - -### Contributing - -Contributions, suggestions, and questions are welcome. Please review the [contributing guidelines](CONTRIBUTING.md) for details. - -### Upgrading from 0.9.x to 1.0.x - -For this update, we've switched the back end HTTP request library from `request` to `axios` as it has better Promise and progress support built in. However, there are a couple changes that will break your code and ruin your day. Here are the changes: -* The Promise resolution has a different data structure. Where previously, the request response data was the root object in the promise resolution (`res`), this data now resides in `res.data`. -* In v0.9.12, we added request progress reporting via the third parameter to `then()`. Because we are no longer using the same promise library, this functionality has been removed. However, progress reporting is still available by passing a callback function into the `b2.method()` that you're calling. See the documentation below for details. -* In v0.9.x, `b2.downloadFileById()` accepted a `fileId` parameter as a String or Number. As of 1.0.0, the first parameter is now expected to be a plain Object of arguments. - -### Response Object - -Each request returns an object with: -- `status` - int, html error Status -- `statusText` -- `headers` -- `config` -- `request` -- `data` - actual returned data from backblaze, https://www.backblaze.com/b2/docs/calling.html - ### Basic Example ```javascript @@ -47,7 +31,7 @@ const b2 = new B2({ async function GetBucket() { try { await b2.authorize(); // must authorize first - let response = await b2.getBucket({bucketName: 'my-bucket'}); + let response = await b2.getBucket({ bucketName: 'my-bucket' }); console.log(response.data); } catch (err) { console.log('Error getting bucket:', err); @@ -55,41 +39,26 @@ async function GetBucket() { } ``` -### Uploading Large Files - -To upload large files, you need to split the file into parts (between 5MB and 5GB) and upload each part seperately. +### Response Object -First, you initiate the large file upload to get the fileId: -```javascript -let response = await b2.startLargeFile({bucketId, fileName}); -let fileId = response.data.fileId; -``` +Each request returns an object with: -Then for each part you request an `uploadUrl`, and use the response to upload the part: -```javascript -let response = await b2.getUploadPartUrl({fileId}); +* `status` - int, html error Status +* `statusText` +* `headers` +* `config` +* `request` +* `data` - actual returned data from backblaze, https://www.backblaze.com/b2/docs/calling.html -let uploadURL = response.data.uploadUrl; -let authToken = response.data.authorizationToken; +### How it works -response = await b2.uploadPart({ - partNumber: parNum, - uploadUrl: uploadURL, - uploadAuthToken: authToken, - data: buf -}); -// status checks etc. -``` +Each action (see reference below) takes arguments and constructs an axios request. You can add additional axios options at the request level using: -Then finish the uploadUrl: -```javascript -let response = await b2.finishLargeFile({ - fileId, - partSha1Array: parts.map(buf => sha1(buf)) -}) -``` +* The `axios` argument (object): each property in this object is added to the axios request object *only if it does not conflict* with an existing property. +* The `axiosOverride` argument (object): each property in this object is added to the axios request object by *overriding* conflicting properties, if any. Don't use this unless you know what you're doing! +* Both `axios` and `axiosOverride` work by recursively merging properties, so if you pass ```axios: { headers: { 'your-custom-header': 'header-value' } }```, the entire headers object will not be overridden - each header property (`your-custom-header`) will be compared. -### Usage +### Reference ```javascript const B2 = require('backblaze-b2'); @@ -103,7 +72,7 @@ const b2 = new B2({ applicationKey: 'applicationKey', // or masterApplicationKey // optional: axios: { - // overrides the axios instance default config + // overrides the axios instance default config, see https://github.com/axios/axios }, retry: { retries: 3 // this is the default @@ -111,32 +80,59 @@ const b2 = new B2({ } }); +// common arguments - you can use these in any of the functions below +const common_args = { + // axios request level config, see https://github.com/axios/axios#request-config + axios: { + timeout: 30000 // (example) + }, + axiosOverride: { + /* Don't use me unless you know what you're doing! */ + } +} + // authorize with provided credentials -b2.authorize(); // returns promise +b2.authorize({ + // ...common arguments (optional) +}); // returns promise // create bucket -b2.createBucket( - bucketName, - bucketType // one of `allPublic`, `allPrivate` -); // returns promise +b2.createBucket({ + bucketName: 'bucketName', + bucketType: 'bucketType' // one of `allPublic`, `allPrivate` + // ...common arguments (optional) +}); // returns promise // delete bucket -b2.deleteBucket(bucketId); // returns promise +b2.deleteBucket({ + bucketId: 'bucketId' + // ...common arguments (optional) +}); // returns promise // list buckets -b2.listBuckets(); // returns promise +b2.listBuckets({ + // ...common arguments (optional) +}); // returns promise // get the bucket b2.getBucket({ - bucketName, - bucketId // optional + bucketName: 'bucketName', + bucketId: 'bucketId' // optional + // ...common arguments (optional) }); // returns promise // update bucket -b2.updateBucket(bucketId, bucketType); // returns promise +b2.updateBucket({ + bucketId: 'bucketId', + bucketType: 'bucketType' + // ...common arguments (optional) +}); // returns promise // get upload url -b2.getUploadUrl(bucketId); // returns promise +b2.getUploadUrl({ + bucketId: 'bucketId' + // ...common arguments (optional) +}); // returns promise // upload file b2.uploadFile({ @@ -153,6 +149,7 @@ b2.uploadFile({ key2: 'value' }, onUploadProgress: (event) => {} || null // progress monitoring + // ...common arguments (optional) }); // returns promise // list file names @@ -162,6 +159,7 @@ b2.listFileNames({ maxFileCount: 100, delimiter: '', prefix: '' + // ...common arguments (optional) }); // returns promise // list file versions @@ -169,16 +167,21 @@ b2.listFileVersions({ bucketId: 'bucketId', startFileName: 'startFileName', maxFileCount: 100 + // ...common arguments (optional) }); // returns promise // hide file b2.hideFile({ bucketId: 'bucketId', fileName: 'fileName' + // ...common arguments (optional) }); // returns promise // get file info -b2.getFileInfo(fileId); // returns promise +b2.getFileInfo({ + fileId: 'fileId' + // ...common arguments (optional) +}); // returns promise // get download authorization b2.getDownloadAuthorization({ @@ -186,6 +189,7 @@ b2.getDownloadAuthorization({ fileNamePrefix: 'fileNamePrefix', validDurationInSeconds: 'validDurationInSeconds', // a number from 0 to 604800 b2ContentDisposition: 'b2ContentDisposition' + // ...common arguments (optional) }); // returns promise // download file by name @@ -194,6 +198,7 @@ b2.downloadFileByName({ fileName: 'fileName', responseType: 'arraybuffer', // options are as in axios: 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream' onDownloadProgress: (event) => {} || null // progress monitoring + // ...common arguments (optional) }); // returns promise // download file by fileId @@ -201,23 +206,27 @@ b2.downloadFileById({ fileId: 'fileId', responseType: 'arraybuffer', // options are as in axios: 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream' onDownloadProgress: (event) => {} || null // progress monitoring + // ...common arguments (optional) }); // returns promise // delete file version b2.deleteFileVersion({ fileId: 'fileId', fileName: 'fileName' + // ...common arguments (optional) }); // returns promise // start large file b2.startLargeFile({ bucketId: 'bucketId', fileName: 'fileName' + // ...common arguments (optional) }); // returns promise // get upload part url b2.getUploadPartUrl({ fileId: 'fileId' + // ...common arguments (optional) }); // returns promise // get upload part @@ -228,22 +237,79 @@ b2.uploadPart({ data: Buffer // this is expecting a Buffer not an encoded string, hash: 'sha1-hash', // optional data hash, will use sha1(data) if not provided onUploadProgress: (event) => {} || null // progress monitoring + // ...common arguments (optional) }); // returns promise // finish large file b2.finishLargeFile({ fileId: 'fileId', partSha1Array: [partSha1Array] // array of sha1 for each part + // ...common arguments (optional) }); // returns promise // cancel large file b2.cancelLargeFile({ fileId: 'fileId' + // ...common arguments (optional) }); // returns promise ``` +### Uploading Large Files Example + +To upload large files, you should split the file into parts (between 5MB and 5GB) and upload each part seperately. + +First, you initiate the large file upload to get the fileId: + +```javascript +let response = await b2.startLargeFile({ bucketId, fileName }); +let fileId = response.data.fileId; +``` + +Then for each part you request an `uploadUrl`, and use the response to upload the part: + +```javascript +let response = await b2.getUploadPartUrl({ fileId }); + +let uploadURL = response.data.uploadUrl; +let authToken = response.data.authorizationToken; + +response = await b2.uploadPart({ + partNumber: parNum, + uploadUrl: uploadURL, + uploadAuthToken: authToken, + data: buf +}); +// status checks etc. +``` + +Then finish the uploadUrl: + +```javascript +let response = await b2.finishLargeFile({ + fileId, + partSha1Array: parts.map(buf => sha1(buf)) +}) +``` + +## Changes + +See the [CHANGELOG](https://github.com/yakovkhalinsky/backblaze-b2/blob/master/CHANGELOG.md) for a history of updates. + +### Upgrading from 0.9.x to 1.0.x + +For this update, we've switched the back end HTTP request library from `request` to `axios` as it has better Promise and progress support built in. However, there are a couple changes that will break your code and ruin your day. Here are the changes: + +* The Promise resolution has a different data structure. Where previously, the request response data was the root object in the promise resolution (`res`), this data now resides in `res.data`. +* In v0.9.12, we added request progress reporting via the third parameter to `then()`. Because we are no longer using the same promise library, this functionality has been removed. However, progress reporting is still available by passing a callback function into the `b2.method()` that you're calling. See the documentation below for details. +* In v0.9.x, `b2.downloadFileById()` accepted a `fileId` parameter as a String or Number. As of 1.0.0, the first parameter is now expected to be a plain Object of arguments. + +## Contributing + +Contributions, suggestions, and questions are welcome. Please review the [contributing guidelines](CONTRIBUTING.md) for details. + ### Authors * Yakov Khalinsky (@yakovkhalinsky) * Ivan Kalinin (@IvanKalinin) at Isolary * Brandon Patton (@crazyscience) at Isolary +* Amit (@Amit-A) From f0cc038ba29b046798f2740168c1e86c72867994 Mon Sep 17 00:00:00 2001 From: Amit-A Date: Tue, 19 Feb 2019 20:08:27 -0500 Subject: [PATCH 6/7] Update README.md --- README.md | 209 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 139 insertions(+), 70 deletions(-) diff --git a/README.md b/README.md index ec5d1bf..08bf644 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,16 @@ -### Backblaze B2 Node.js Library +# Backblaze B2 Node.js Library + [![npm version](https://badge.fury.io/js/backblaze-b2.svg)](https://badge.fury.io/js/backblaze-b2) [![Build Status](https://travis-ci.org/yakovkhalinsky/backblaze-b2.svg?branch=master)](https://travis-ci.org/yakovkhalinsky/backblaze-b2) +A customizable B2 client for Node.js: + +* Uses [axios](https://github.com/axios/axios). You can control the axios instance at the request level (see `axios` and `axiosOverride` config arguments) and at the global level (see `axios` config argument at instantiation) so you can use any axios feature. +* Automatically retries on request failure. You can control retry behaviour using the `retries` argument at instantiation. + +## Usage + This library uses promises, so all actions on a `B2` instance return a promise in the following pattern: + ``` javascript b2.instanceFunction(arg1, arg2).then( successFn(response) { ... }, @@ -9,31 +18,6 @@ b2.instanceFunction(arg1, arg2).then( ); ``` -### Status of project - -See the [CHANGELOG](https://github.com/yakovkhalinsky/backblaze-b2/blob/master/CHANGELOG.md) for a history of updates. - -### Contributing - -Contributions, suggestions, and questions are welcome. Please review the [contributing guidelines](CONTRIBUTING.md) for details. - -### Upgrading from 0.9.x to 1.0.x - -For this update, we've switched the back end HTTP request library from `request` to `axios` as it has better Promise and progress support built in. However, there are a couple changes that will break your code and ruin your day. Here are the changes: -* The Promise resolution has a different data structure. Where previously, the request response data was the root object in the promise resolution (`res`), this data now resides in `res.data`. -* In v0.9.12, we added request progress reporting via the third parameter to `then()`. Because we are no longer using the same promise library, this functionality has been removed. However, progress reporting is still available by passing a callback function into the `b2.method()` that you're calling. See the documentation below for details. -* In v0.9.x, `b2.downloadFileById()` accepted a `fileId` parameter as a String or Number. As of 1.0.0, the first parameter is now expected to be a plain Object of arguments. - -### Response Object - -Each request returns an object with: -- `status` - int, html error Status -- `statusText` -- `headers` -- `config` -- `request` -- `data` - actual returned data from backblaze, https://www.backblaze.com/b2/docs/calling.html - ### Basic Example ```javascript @@ -47,7 +31,7 @@ const b2 = new B2({ async function GetBucket() { try { await b2.authorize(); // must authorize first - let response = await b2.getBucket({bucketName: 'my-bucket'}); + let response = await b2.getBucket({ bucketName: 'my-bucket' }); console.log(response.data); } catch (err) { console.log('Error getting bucket:', err); @@ -55,41 +39,26 @@ async function GetBucket() { } ``` -### Uploading Large Files - -To upload large files, you need to split the file into parts (between 5MB and 5GB) and upload each part seperately. +### Response Object -First, you initiate the large file upload to get the fileId: -```javascript -let response = await b2.startLargeFile({bucketId, fileName}); -let fileId = response.data.fileId; -``` +Each request returns an object with: -Then for each part you request an `uploadUrl`, and use the response to upload the part: -```javascript -let response = await b2.getUploadPartUrl({fileId}); +* `status` - int, html error Status +* `statusText` +* `headers` +* `config` +* `request` +* `data` - actual returned data from backblaze, https://www.backblaze.com/b2/docs/calling.html -let uploadURL = response.data.uploadUrl; -let authToken = response.data.authorizationToken; +### How it works -response = await b2.uploadPart({ - partNumber: parNum, - uploadUrl: uploadURL, - uploadAuthToken: authToken, - data: buf -}); -// status checks etc. -``` +Each action (see reference below) takes arguments and constructs an axios request. You can add additional axios options at the request level using: -Then finish the uploadUrl: -```javascript -let response = await b2.finishLargeFile({ - fileId, - partSha1Array: parts.map(buf => sha1(buf)) -}) -``` +* The `axios` argument (object): each property in this object is added to the axios request object *only if it does not conflict* with an existing property. +* The `axiosOverride` argument (object): each property in this object is added to the axios request object by *overriding* conflicting properties, if any. Don't use this unless you know what you're doing! +* Both `axios` and `axiosOverride` work by recursively merging properties, so if you pass ```axios: { headers: { 'your-custom-header': 'header-value' } }```, the entire headers object will not be overridden - each header property (`your-custom-header`) will be compared. -### Usage +### Reference ```javascript const B2 = require('backblaze-b2'); @@ -103,7 +72,7 @@ const b2 = new B2({ applicationKey: 'applicationKey', // or masterApplicationKey // optional: axios: { - // overrides the axios instance default config + // overrides the axios instance default config, see https://github.com/axios/axios }, retry: { retries: 3 // this is the default @@ -111,32 +80,59 @@ const b2 = new B2({ } }); +// common arguments - you can use these in any of the functions below +const common_args = { + // axios request level config, see https://github.com/axios/axios#request-config + axios: { + timeout: 30000 // (example) + }, + axiosOverride: { + /* Don't use me unless you know what you're doing! */ + } +} + // authorize with provided credentials -b2.authorize(); // returns promise +b2.authorize({ + // ...common arguments (optional) +}); // returns promise // create bucket -b2.createBucket( - bucketName, - bucketType // one of `allPublic`, `allPrivate` -); // returns promise +b2.createBucket({ + bucketName: 'bucketName', + bucketType: 'bucketType' // one of `allPublic`, `allPrivate` + // ...common arguments (optional) +}); // returns promise // delete bucket -b2.deleteBucket(bucketId); // returns promise +b2.deleteBucket({ + bucketId: 'bucketId' + // ...common arguments (optional) +}); // returns promise // list buckets -b2.listBuckets(); // returns promise +b2.listBuckets({ + // ...common arguments (optional) +}); // returns promise // get the bucket b2.getBucket({ - bucketName, - bucketId // optional + bucketName: 'bucketName', + bucketId: 'bucketId' // optional + // ...common arguments (optional) }); // returns promise // update bucket -b2.updateBucket(bucketId, bucketType); // returns promise +b2.updateBucket({ + bucketId: 'bucketId', + bucketType: 'bucketType' + // ...common arguments (optional) +}); // returns promise // get upload url -b2.getUploadUrl(bucketId); // returns promise +b2.getUploadUrl({ + bucketId: 'bucketId' + // ...common arguments (optional) +}); // returns promise // upload file b2.uploadFile({ @@ -153,6 +149,7 @@ b2.uploadFile({ key2: 'value' }, onUploadProgress: (event) => {} || null // progress monitoring + // ...common arguments (optional) }); // returns promise // list file names @@ -162,6 +159,7 @@ b2.listFileNames({ maxFileCount: 100, delimiter: '', prefix: '' + // ...common arguments (optional) }); // returns promise // list file versions @@ -169,16 +167,21 @@ b2.listFileVersions({ bucketId: 'bucketId', startFileName: 'startFileName', maxFileCount: 100 + // ...common arguments (optional) }); // returns promise // hide file b2.hideFile({ bucketId: 'bucketId', fileName: 'fileName' + // ...common arguments (optional) }); // returns promise // get file info -b2.getFileInfo(fileId); // returns promise +b2.getFileInfo({ + fileId: 'fileId' + // ...common arguments (optional) +}); // returns promise // get download authorization b2.getDownloadAuthorization({ @@ -186,6 +189,7 @@ b2.getDownloadAuthorization({ fileNamePrefix: 'fileNamePrefix', validDurationInSeconds: 'validDurationInSeconds', // a number from 0 to 604800 b2ContentDisposition: 'b2ContentDisposition' + // ...common arguments (optional) }); // returns promise // download file by name @@ -194,6 +198,7 @@ b2.downloadFileByName({ fileName: 'fileName', responseType: 'arraybuffer', // options are as in axios: 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream' onDownloadProgress: (event) => {} || null // progress monitoring + // ...common arguments (optional) }); // returns promise // download file by fileId @@ -201,23 +206,27 @@ b2.downloadFileById({ fileId: 'fileId', responseType: 'arraybuffer', // options are as in axios: 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream' onDownloadProgress: (event) => {} || null // progress monitoring + // ...common arguments (optional) }); // returns promise // delete file version b2.deleteFileVersion({ fileId: 'fileId', fileName: 'fileName' + // ...common arguments (optional) }); // returns promise // start large file b2.startLargeFile({ bucketId: 'bucketId', fileName: 'fileName' + // ...common arguments (optional) }); // returns promise // get upload part url b2.getUploadPartUrl({ fileId: 'fileId' + // ...common arguments (optional) }); // returns promise // get upload part @@ -228,22 +237,82 @@ b2.uploadPart({ data: Buffer // this is expecting a Buffer not an encoded string, hash: 'sha1-hash', // optional data hash, will use sha1(data) if not provided onUploadProgress: (event) => {} || null // progress monitoring + // ...common arguments (optional) }); // returns promise // finish large file b2.finishLargeFile({ fileId: 'fileId', partSha1Array: [partSha1Array] // array of sha1 for each part + // ...common arguments (optional) }); // returns promise // cancel large file b2.cancelLargeFile({ fileId: 'fileId' + // ...common arguments (optional) }); // returns promise ``` -### Authors +### Uploading Large Files Example + +To upload large files, you should split the file into parts (between 5MB and 5GB) and upload each part seperately. + +First, you initiate the large file upload to get the fileId: + +```javascript +let response = await b2.startLargeFile({ bucketId, fileName }); +let fileId = response.data.fileId; +``` + +Then for each part you request an `uploadUrl`, and use the response to upload the part: + +```javascript +let response = await b2.getUploadPartUrl({ fileId }); + +let uploadURL = response.data.uploadUrl; +let authToken = response.data.authorizationToken; + +response = await b2.uploadPart({ + partNumber: parNum, + uploadUrl: uploadURL, + uploadAuthToken: authToken, + data: buf +}); +// status checks etc. +``` + +Then finish the uploadUrl: + +```javascript +let response = await b2.finishLargeFile({ + fileId, + partSha1Array: parts.map(buf => sha1(buf)) +}) +``` + +## Changes + +See the [CHANGELOG](https://github.com/yakovkhalinsky/backblaze-b2/blob/master/CHANGELOG.md) for a history of updates. + +### Upgrading from 0.9.x to 1.0.x + +For this update, we've switched the back end HTTP request library from `request` to `axios` as it has better Promise and progress support built in. However, there are a couple changes that will break your code and ruin your day. Here are the changes: + +* The Promise resolution has a different data structure. Where previously, the request response data was the root object in the promise resolution (`res`), this data now resides in `res.data`. +* In v0.9.12, we added request progress reporting via the third parameter to `then()`. Because we are no longer using the same promise library, this functionality has been removed. However, progress reporting is still available by passing a callback function into the `b2.method()` that you're calling. See the documentation below for details. +* In v0.9.x, `b2.downloadFileById()` accepted a `fileId` parameter as a String or Number. As of 1.0.0, the first parameter is now expected to be a plain Object of arguments. + +## Contributing + +Contributions, suggestions, and questions are welcome. Please review the [contributing guidelines](CONTRIBUTING.md) for details. + +### Authors and Contributors * Yakov Khalinsky (@yakovkhalinsky) * Ivan Kalinin (@IvanKalinin) at Isolary * Brandon Patton (@crazyscience) at Isolary +* C. Bess (@cbess) +* Amit (@Amit-A) +* Zsombor Paróczi (@realhidden) +* Oden (@odensc) From 1883ccbf2d0b7bd3ebfb0eb9a8132ff3dd6100cb Mon Sep 17 00:00:00 2001 From: Amit-A Date: Tue, 19 Feb 2019 20:19:05 -0500 Subject: [PATCH 7/7] Update README.md --- README.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/README.md b/README.md index ae5866a..08bf644 100644 --- a/README.md +++ b/README.md @@ -307,20 +307,12 @@ For this update, we've switched the back end HTTP request library from `request` Contributions, suggestions, and questions are welcome. Please review the [contributing guidelines](CONTRIBUTING.md) for details. -<<<<<<< HEAD ### Authors and Contributors -======= -### Authors ->>>>>>> 99065f4a27c2db131beddf1819dc2929d9d1ac1c * Yakov Khalinsky (@yakovkhalinsky) * Ivan Kalinin (@IvanKalinin) at Isolary * Brandon Patton (@crazyscience) at Isolary -<<<<<<< HEAD * C. Bess (@cbess) * Amit (@Amit-A) * Zsombor Paróczi (@realhidden) * Oden (@odensc) -======= -* Amit (@Amit-A) ->>>>>>> 99065f4a27c2db131beddf1819dc2929d9d1ac1c