-
Notifications
You must be signed in to change notification settings - Fork 768
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Stream support for FileUpload #471
Changes from 12 commits
a58cb35
5dc9afd
d99d077
54a79a6
27c0121
87328dd
f109d07
8764b7d
183d644
05a84c4
5acf777
c039eeb
aa90cec
a1ebecc
d963055
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -212,6 +212,20 @@ StripeResource.prototype = { | |
} | ||
}, | ||
|
||
_errorHandlerStream: function(callback) { | ||
var self = this; | ||
return function(error) { | ||
callback.call( | ||
self, | ||
new Error.StreamProcessingError({ | ||
message: 'An error occurred while attempting to process the file for upload.', | ||
detail: error, | ||
}), | ||
null | ||
); | ||
} | ||
}, | ||
|
||
_defaultHeaders: function(auth, contentLength, apiVersion) { | ||
var userAgentString = 'Stripe/v1 NodeBindings/' + this._stripe.getConstant('PACKAGE_VERSION'); | ||
|
||
|
@@ -241,28 +255,37 @@ StripeResource.prototype = { | |
var self = this; | ||
var requestData; | ||
|
||
if (self.requestDataProcessor) { | ||
requestData = self.requestDataProcessor(method, data, options.headers); | ||
} else { | ||
requestData = utils.stringifyRequestData(data || {}); | ||
} | ||
function makeRequestWithData(error, data) { | ||
var apiVersion; | ||
var headers; | ||
|
||
var apiVersion = this._stripe.getApiField('version'); | ||
if (error) { | ||
var handleError = self._errorHandlerStream(callback); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd rather see this FileUpload-specific error generation/handling pushed to
At this point in the code, we don't (or shouldn't) really care what kind of error this is, we should just expose it in the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. updated |
||
return handleError(error); | ||
} | ||
|
||
var headers = self._defaultHeaders(auth, requestData.length, apiVersion); | ||
apiVersion = self._stripe.getApiField('version'); | ||
requestData = data; | ||
headers = self._defaultHeaders(auth, requestData.length, apiVersion); | ||
|
||
// Grab client-user-agent before making the request: | ||
this._stripe.getClientUserAgent(function(cua) { | ||
headers['X-Stripe-Client-User-Agent'] = cua; | ||
self._stripe.getClientUserAgent(function(cua) { | ||
headers['X-Stripe-Client-User-Agent'] = cua; | ||
|
||
if (options.headers) { | ||
Object.assign(headers, options.headers); | ||
} | ||
if (options.headers) { | ||
Object.assign(headers, options.headers); | ||
} | ||
|
||
makeRequest(); | ||
}); | ||
makeRequest(apiVersion, headers); | ||
}); | ||
} | ||
|
||
if (self.requestDataProcessor) { | ||
self.requestDataProcessor(method, data, options.headers, makeRequestWithData); | ||
} else { | ||
makeRequestWithData(null, utils.stringifyRequestData(data || {})); | ||
} | ||
|
||
function makeRequest() { | ||
function makeRequest(apiVersion, headers) { | ||
var timeout = self._stripe.getApiField('timeout'); | ||
var isInsecureConnection = self._stripe.getApiField('protocol') == 'http'; | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
'use strict'; | ||
|
||
var Buffer = require('safe-buffer').Buffer; | ||
var utils = require('../utils'); | ||
var StripeResource = require('../StripeResource'); | ||
var stripeMethod = StripeResource.method; | ||
|
@@ -9,14 +10,38 @@ module.exports = StripeResource.extend({ | |
|
||
overrideHost: 'uploads.stripe.com', | ||
|
||
requestDataProcessor: function(method, data, headers) { | ||
requestDataProcessor: function(method, data, headers, callback) { | ||
data = data || {}; | ||
|
||
if (method === 'POST') { | ||
return multipartDataGenerator(method, data, headers); | ||
return getProcessorForSourceType(data); | ||
} else { | ||
return utils.stringifyRequestData(data); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This needs to make use of the callback flow here, so it's probably There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh wow, great catch - my fault - I totally missed that! Thanks |
||
} | ||
|
||
function getProcessorForSourceType(data) { | ||
var isStream = utils.checkForStream(data); | ||
if (isStream) { | ||
return streamProcessor(multipartDataGenerator); | ||
} else { | ||
var buffer = multipartDataGenerator(method, data, headers); | ||
return callback(null, buffer); | ||
} | ||
} | ||
|
||
function streamProcessor (fn) { | ||
var bufferArray = []; | ||
data.file.data.on('data', function(line) { | ||
bufferArray.push(line); | ||
}).on('end', function() { | ||
var bufferData = Object.assign({}, data); | ||
bufferData.file.data = Buffer.concat(bufferArray); | ||
var buffer = fn(method, bufferData, headers); | ||
callback(null, buffer); | ||
}).on('error', function(err) { | ||
callback(err, null); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Per comments above, this is probably the right place to new up the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. updated - thanks! |
||
}); | ||
} | ||
}, | ||
|
||
path: 'files', | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,7 +6,8 @@ var stripe = require('../lib/stripe')( | |
testUtils.getUserStripeKey(), | ||
'latest' | ||
); | ||
|
||
var fs = require('fs'); | ||
var path = require('path'); | ||
var expect = chai.expect; | ||
|
||
var CUSTOMER_DETAILS = { | ||
|
@@ -522,4 +523,24 @@ describe('Flows', function() { | |
}).type).to.equal('StripeInvalidRequestError'); | ||
}); | ||
}); | ||
|
||
describe('FileUpload', function() { | ||
it('Allows you to upload a file as a stream', function() { | ||
var testFilename = path.join(__dirname, 'resources/data/minimal.pdf'); | ||
var f = fs.createReadStream(testFilename); | ||
|
||
return expect( | ||
stripe.fileUploads.create({ | ||
purpose: 'dispute_evidence', | ||
file: { | ||
data: f, | ||
name: 'minimal.pdf', | ||
type: 'application/octet-stream', | ||
}, | ||
}).then(null, function(error) { | ||
return error; | ||
}) | ||
).to.eventually.have.nested.property('size', 739); | ||
}); | ||
}); | ||
}); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't actually test that the error comes back as expected; this should do it:
We should also test that you can still provide a (non-Stream) file, which is just the same as what you've already got except with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (I tried doing this in a promisey way but There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. added both tests (for synchronous file uploads and testing for the stream error to return properly) - thanks! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Per comment further down, I think the
StreamProcessingError
should be created (and wrap the incoming error) inFileUploads.js
, since it's so specific to FileUploads.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Apparently I meant 'further up' 😂)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good point! - removed and updated
StreamProcessingError
to FileUploads.